Compare commits
151 Commits
Author | SHA1 | Date | |
---|---|---|---|
21b3870a7d | |||
023e1cdf29 | |||
493a235065 | |||
e4e48dc9db | |||
7de647daa1 | |||
34bc92137b | |||
954df5a0ee | |||
b9c15b11ad | |||
55e6e81957 | |||
f37956270c | |||
0c985b9c00 | |||
85ff487809 | |||
eb65c4e859 | |||
a1d6c37f18 | |||
915ad00801 | |||
910671bfc6 | |||
ae8835d430 | |||
d08cc0f350 | |||
1311039127 | |||
c267d2f226 | |||
5a9e1b5798 | |||
b1ec86ee2d | |||
a1353170f6 | |||
3ff6de201d | |||
f5c106b2ca | |||
d3c26d0d46 | |||
9935fe2d3c | |||
3b05aab39b | |||
53be2eb59d | |||
c92a0dddbd | |||
27403a73b5 | |||
b925e5e662 | |||
98a5d2c94d | |||
0e735cba20 | |||
f815457801 | |||
f7e47ae354 | |||
684e893801 | |||
d4b381d33d | |||
291a11aa60 | |||
ca592afec9 | |||
8b07197224 | |||
b60fd15ec6 | |||
853eccc780 | |||
c26aff85b5 | |||
321e4d9dea | |||
3d2789857c | |||
07b88a078d | |||
6fee0028d8 | |||
629c52f9bc | |||
fd056c29e9 | |||
36c456b509 | |||
16f8c25557 | |||
219e070ba2 | |||
ee97e1d88b | |||
279db74568 | |||
b84c504f11 | |||
7b3194cc13 | |||
e1e821efec | |||
6b613d1b8a | |||
70f1c58a82 | |||
5df76ca94b | |||
32cfda3c90 | |||
dd521398ea | |||
038e6cc33d | |||
2fc37d6892 | |||
3c1eb1ab70 | |||
5296e8859b | |||
160e0ae451 | |||
373c6538ae | |||
7a1476e106 | |||
b2a2035f00 | |||
03e4f03035 | |||
d74bbb2b12 | |||
22cfe1f5cb | |||
5fc2c4586d | |||
6ab81fb323 | |||
58ec27a1a0 | |||
88811646b7 | |||
412bb52eee | |||
b04750ecbd | |||
0c99475888 | |||
86317def88 | |||
0a5af0ba96 | |||
cf73ff4a54 | |||
788897e765 | |||
7fa3894f6e | |||
afdd654664 | |||
8277e0ca6d | |||
0892c87a68 | |||
841ba2e14d | |||
5b2953bf02 | |||
d3457fd65b | |||
3bf1eafe6b | |||
04a9b992d7 | |||
6206b55deb | |||
6c05bf6ae3 | |||
371074afc1 | |||
e0e665fe6d | |||
5483202972 | |||
24b3458888 | |||
cffa47ac3d | |||
9dadf3f78f | |||
b35a671fe9 | |||
0ca38c109e | |||
4e2321e1ee | |||
a640ab3d7b | |||
37d6d56287 | |||
443a026502 | |||
9644c5b7e3 | |||
25faa8c697 | |||
982387aaa3 | |||
4a11f50efe | |||
0ddec29392 | |||
df484d54e8 | |||
f637c20241 | |||
ed0c1a9181 | |||
0e22999f69 | |||
38f001ab23 | |||
d1429c5a41 | |||
403a0f4fae | |||
91ade8a4d4 | |||
1a571bba90 | |||
bcb66b2ccf | |||
0000984e4b | |||
b391e54083 | |||
b32c06aef2 | |||
27f60f6719 | |||
b2482ab8a5 | |||
9c614bd2f3 | |||
b12de7aed4 | |||
d6eb54d21c | |||
6646f173c6 | |||
f20657ff71 | |||
7f3f2da702 | |||
1e60ab375b | |||
98fa607c43 | |||
12b1672e58 | |||
9fca2c3aa4 | |||
5b7d822cfc | |||
ce57478e39 | |||
292c2699ea | |||
afb7fa8e89 | |||
e2c3005a08 | |||
53e595a023 | |||
93702dca78 | |||
0a95ae6284 | |||
de09f85c34 | |||
b9fefc7c95 | |||
76eae15f7d | |||
2e457f6011 | |||
e83c63fd2a |
66
.gitea/workflows/default_nottags.yaml
Normal file
66
.gitea/workflows/default_nottags.yaml
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
name: Default (not tags)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags-ignore:
|
||||||
|
- '**'
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||||
|
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
||||||
|
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||||
|
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||||
|
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||||
|
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
security:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install pnpm and npmci
|
||||||
|
run: |
|
||||||
|
pnpm install -g pnpm
|
||||||
|
pnpm install -g @shipzone/npmci
|
||||||
|
|
||||||
|
- name: Run npm prepare
|
||||||
|
run: npmci npm prepare
|
||||||
|
|
||||||
|
- name: Audit production dependencies
|
||||||
|
run: |
|
||||||
|
npmci command npm config set registry https://registry.npmjs.org
|
||||||
|
npmci command pnpm audit --audit-level=high --prod
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Audit development dependencies
|
||||||
|
run: |
|
||||||
|
npmci command npm config set registry https://registry.npmjs.org
|
||||||
|
npmci command pnpm audit --audit-level=high --dev
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
test:
|
||||||
|
if: ${{ always() }}
|
||||||
|
needs: security
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Test stable
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm install
|
||||||
|
npmci npm test
|
||||||
|
|
||||||
|
- name: Test build
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm install
|
||||||
|
npmci npm build
|
124
.gitea/workflows/default_tags.yaml
Normal file
124
.gitea/workflows/default_tags.yaml
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
name: Default (tags)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||||
|
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
|
||||||
|
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||||
|
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||||
|
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||||
|
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
security:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
pnpm install -g pnpm
|
||||||
|
pnpm install -g @shipzone/npmci
|
||||||
|
npmci npm prepare
|
||||||
|
|
||||||
|
- name: Audit production dependencies
|
||||||
|
run: |
|
||||||
|
npmci command npm config set registry https://registry.npmjs.org
|
||||||
|
npmci command pnpm audit --audit-level=high --prod
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Audit development dependencies
|
||||||
|
run: |
|
||||||
|
npmci command npm config set registry https://registry.npmjs.org
|
||||||
|
npmci command pnpm audit --audit-level=high --dev
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
test:
|
||||||
|
if: ${{ always() }}
|
||||||
|
needs: security
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
pnpm install -g pnpm
|
||||||
|
pnpm install -g @shipzone/npmci
|
||||||
|
npmci npm prepare
|
||||||
|
|
||||||
|
- name: Test stable
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm install
|
||||||
|
npmci npm test
|
||||||
|
|
||||||
|
- name: Test build
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm install
|
||||||
|
npmci npm build
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: test
|
||||||
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
pnpm install -g pnpm
|
||||||
|
pnpm install -g @shipzone/npmci
|
||||||
|
npmci npm prepare
|
||||||
|
|
||||||
|
- name: Release
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm publish
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
needs: test
|
||||||
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ${{ env.IMAGE }}
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
pnpm install -g pnpm
|
||||||
|
pnpm install -g @shipzone/npmci
|
||||||
|
npmci npm prepare
|
||||||
|
|
||||||
|
- name: Code quality
|
||||||
|
run: |
|
||||||
|
npmci command npm install -g typescript
|
||||||
|
npmci npm install
|
||||||
|
|
||||||
|
- name: Trigger
|
||||||
|
run: npmci trigger
|
||||||
|
|
||||||
|
- name: Build docs and upload artifacts
|
||||||
|
run: |
|
||||||
|
npmci node install stable
|
||||||
|
npmci npm install
|
||||||
|
pnpm install -g @git.zone/tsdoc
|
||||||
|
npmci command tsdoc
|
||||||
|
continue-on-error: true
|
137
.gitlab-ci.yml
137
.gitlab-ci.yml
@ -1,137 +0,0 @@
|
|||||||
# gitzone ci_default
|
|
||||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
|
||||||
|
|
||||||
cache:
|
|
||||||
paths:
|
|
||||||
- .npmci_cache/
|
|
||||||
key: '$CI_BUILD_STAGE'
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- security
|
|
||||||
- test
|
|
||||||
- release
|
|
||||||
- metadata
|
|
||||||
|
|
||||||
# ====================
|
|
||||||
# security stage
|
|
||||||
# ====================
|
|
||||||
mirror:
|
|
||||||
stage: security
|
|
||||||
script:
|
|
||||||
- npmci git mirror
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
- notpriv
|
|
||||||
|
|
||||||
auditProductionDependencies:
|
|
||||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
|
||||||
stage: security
|
|
||||||
script:
|
|
||||||
- npmci npm prepare
|
|
||||||
- npmci command npm install --production --ignore-scripts
|
|
||||||
- npmci command npm config set registry https://registry.npmjs.org
|
|
||||||
- npmci command npm audit --audit-level=high --only=prod --production
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
auditDevDependencies:
|
|
||||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
|
||||||
stage: security
|
|
||||||
script:
|
|
||||||
- npmci npm prepare
|
|
||||||
- npmci command npm install --ignore-scripts
|
|
||||||
- npmci command npm config set registry https://registry.npmjs.org
|
|
||||||
- npmci command npm audit --audit-level=high --only=dev
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
allow_failure: true
|
|
||||||
|
|
||||||
# ====================
|
|
||||||
# test stage
|
|
||||||
# ====================
|
|
||||||
|
|
||||||
testStable:
|
|
||||||
stage: test
|
|
||||||
script:
|
|
||||||
- npmci npm prepare
|
|
||||||
- npmci node install stable
|
|
||||||
- npmci npm install
|
|
||||||
- npmci npm test
|
|
||||||
coverage: /\d+.?\d+?\%\s*coverage/
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
testBuild:
|
|
||||||
stage: test
|
|
||||||
script:
|
|
||||||
- npmci npm prepare
|
|
||||||
- npmci node install stable
|
|
||||||
- npmci npm install
|
|
||||||
- npmci command npm run build
|
|
||||||
coverage: /\d+.?\d+?\%\s*coverage/
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
release:
|
|
||||||
stage: release
|
|
||||||
script:
|
|
||||||
- npmci node install stable
|
|
||||||
- npmci npm publish
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
- notpriv
|
|
||||||
|
|
||||||
# ====================
|
|
||||||
# metadata stage
|
|
||||||
# ====================
|
|
||||||
codequality:
|
|
||||||
stage: metadata
|
|
||||||
allow_failure: true
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
script:
|
|
||||||
- npmci command npm install -g tslint typescript
|
|
||||||
- npmci npm prepare
|
|
||||||
- npmci npm install
|
|
||||||
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
- priv
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
stage: metadata
|
|
||||||
script:
|
|
||||||
- npmci trigger
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
- notpriv
|
|
||||||
|
|
||||||
pages:
|
|
||||||
stage: metadata
|
|
||||||
script:
|
|
||||||
- npmci node install lts
|
|
||||||
- npmci command npm install -g @gitzone/tsdoc
|
|
||||||
- npmci npm prepare
|
|
||||||
- npmci npm install
|
|
||||||
- npmci command tsdoc
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
- notpriv
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
artifacts:
|
|
||||||
expire_in: 1 week
|
|
||||||
paths:
|
|
||||||
- public
|
|
||||||
allow_failure: true
|
|
4
.snyk
4
.snyk
@ -1,4 +0,0 @@
|
|||||||
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
|
|
||||||
version: v1.13.1
|
|
||||||
ignore: {}
|
|
||||||
patch: {}
|
|
24
.vscode/launch.json
vendored
24
.vscode/launch.json
vendored
@ -2,28 +2,10 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "current file",
|
"command": "npm test",
|
||||||
"type": "node",
|
"name": "Run npm test",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"args": [
|
"type": "node-terminal"
|
||||||
"${relativeFile}"
|
|
||||||
],
|
|
||||||
"runtimeArgs": ["-r", "@gitzone/tsrun"],
|
|
||||||
"cwd": "${workspaceRoot}",
|
|
||||||
"protocol": "inspector",
|
|
||||||
"internalConsoleOptions": "openOnSessionStart"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "test.ts",
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"args": [
|
|
||||||
"test/test.ts"
|
|
||||||
],
|
|
||||||
"runtimeArgs": ["-r", "@gitzone/tsrun"],
|
|
||||||
"cwd": "${workspaceRoot}",
|
|
||||||
"protocol": "inspector",
|
|
||||||
"internalConsoleOptions": "openOnSessionStart"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
{
|
{
|
||||||
"npmts": {
|
|
||||||
"mode": "default",
|
|
||||||
"coverageTreshold": 70
|
|
||||||
},
|
|
||||||
"npmdocker": {},
|
"npmdocker": {},
|
||||||
"npmci": {
|
"npmci": {
|
||||||
"npmGlobalTools": [],
|
"npmGlobalTools": [],
|
||||||
@ -11,12 +7,36 @@
|
|||||||
"gitzone": {
|
"gitzone": {
|
||||||
"projectType": "npm",
|
"projectType": "npm",
|
||||||
"module": {
|
"module": {
|
||||||
"githost": "gitlab.com",
|
"githost": "code.foss.global",
|
||||||
"gitscope": "pushrocks",
|
"gitscope": "push.rocks",
|
||||||
"gitrepo": "smartfile",
|
"gitrepo": "smartfile",
|
||||||
"shortDescription": "smart ways to work with files in nodejs",
|
"description": "provides a robust suite of tools for managing files in Node.js using TypeScript.",
|
||||||
"npmPackagename": "@pushrocks/smartfile",
|
"npmPackagename": "@push.rocks/smartfile",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"files management",
|
||||||
|
"TypeScript",
|
||||||
|
"Node.js",
|
||||||
|
"read files",
|
||||||
|
"write files",
|
||||||
|
"copy files",
|
||||||
|
"file streaming",
|
||||||
|
"directories manipulation",
|
||||||
|
"virtual file system",
|
||||||
|
"filesystem utilities",
|
||||||
|
"ESM syntax",
|
||||||
|
"memory-efficient streaming"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"tsdoc": {
|
||||||
|
"classes": [
|
||||||
|
"SmartFile",
|
||||||
|
"StreamFile"
|
||||||
|
],
|
||||||
|
"descriptions": [
|
||||||
|
"the purpose of the StreamFile class is to provide a hybrid interface between streaming files and simple handling when writing and reading those files multiple times."
|
||||||
|
],
|
||||||
|
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
10446
package-lock.json
generated
10446
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
70
package.json
70
package.json
@ -1,50 +1,64 @@
|
|||||||
{
|
{
|
||||||
"name": "@pushrocks/smartfile",
|
"name": "@push.rocks/smartfile",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "8.0.2",
|
"version": "11.0.14",
|
||||||
"description": "offers smart ways to work with files in nodejs",
|
"description": "provides a robust suite of tools for managing files in Node.js using TypeScript.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
"typings": "dist_ts/index.d.ts",
|
"typings": "dist_ts/index.d.ts",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "(tstest test/)",
|
"test": "(tstest test/)",
|
||||||
"build": "(tsbuild --web)"
|
"build": "(tsbuild --web --allowimplicitany)",
|
||||||
|
"buildDocs": "tsdoc"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitlab.com/pushrocks/smartfile.git"
|
"url": "git+https://gitlab.com/push.rocks/smartfile.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"filesystem",
|
"files management",
|
||||||
"json"
|
"TypeScript",
|
||||||
|
"Node.js",
|
||||||
|
"read files",
|
||||||
|
"write files",
|
||||||
|
"copy files",
|
||||||
|
"file streaming",
|
||||||
|
"directories manipulation",
|
||||||
|
"virtual file system",
|
||||||
|
"filesystem utilities",
|
||||||
|
"ESM syntax",
|
||||||
|
"memory-efficient streaming"
|
||||||
],
|
],
|
||||||
"author": "Lossless GmbH <hello@lossless.com> (https://lossless.com)",
|
"author": "Lossless GmbH <hello@lossless.com> (https://lossless.com)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://gitlab.com/pushrocks/smartfile/issues"
|
"url": "https://gitlab.com/push.rocks/smartfile/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://gitlab.com/pushrocks/smartfile",
|
"homepage": "https://gitlab.com/push.rocks/smartfile#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pushrocks/smarthash": "^2.1.6",
|
"@push.rocks/lik": "^6.0.14",
|
||||||
"@pushrocks/smartmime": "^1.0.3",
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
"@pushrocks/smartpath": "^4.0.3",
|
"@push.rocks/smartfile-interfaces": "^1.0.7",
|
||||||
"@pushrocks/smartpromise": "^3.0.6",
|
"@push.rocks/smarthash": "^3.0.4",
|
||||||
"@pushrocks/smartrequest": "^1.1.51",
|
"@push.rocks/smartjson": "^5.0.16",
|
||||||
"@types/fs-extra": "^9.0.1",
|
"@push.rocks/smartmime": "^1.0.5",
|
||||||
"@types/glob": "^7.1.3",
|
"@push.rocks/smartpath": "^5.0.13",
|
||||||
"@types/js-yaml": "^3.12.5",
|
"@push.rocks/smartpromise": "^4.0.2",
|
||||||
"fs-extra": "^9.0.1",
|
"@push.rocks/smartrequest": "^2.0.21",
|
||||||
"glob": "^7.1.6",
|
"@push.rocks/smartstream": "^3.0.34",
|
||||||
"js-yaml": "^3.14.0"
|
"@types/fs-extra": "^11.0.4",
|
||||||
|
"@types/glob": "^8.1.0",
|
||||||
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
"fs-extra": "^11.2.0",
|
||||||
|
"glob": "^10.3.12",
|
||||||
|
"js-yaml": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@gitzone/tsbuild": "^2.1.25",
|
"@git.zone/tsbuild": "^2.1.72",
|
||||||
"@gitzone/tsrun": "^1.2.12",
|
"@git.zone/tsrun": "^1.2.46",
|
||||||
"@gitzone/tstest": "^1.0.52",
|
"@git.zone/tstest": "^1.0.88",
|
||||||
"@pushrocks/tapbundle": "^3.2.9",
|
"@push.rocks/tapbundle": "^5.0.22",
|
||||||
"@types/node": "^14.11.2",
|
"@types/node": "^20.12.7"
|
||||||
"gulp-function": "^2.2.14",
|
|
||||||
"tslint": "^6.1.3",
|
|
||||||
"tslint-config-prettier": "^1.18.0"
|
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"ts/**/*",
|
"ts/**/*",
|
||||||
|
4808
pnpm-lock.yaml
generated
Normal file
4808
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
302
readme.md
302
readme.md
@ -1,51 +1,279 @@
|
|||||||
# @pushrocks/smartfile
|
# @push.rocks/smartfile
|
||||||
smart ways to work with files in nodejs
|
|
||||||
|
|
||||||
## Availabililty and Links
|
> Provides a robust suite of tools for managing files in Node.js using TypeScript.
|
||||||
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartfile)
|
|
||||||
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartfile)
|
|
||||||
* [github.com (source mirror)](https://github.com/pushrocks/smartfile)
|
|
||||||
* [docs (typedoc)](https://pushrocks.gitlab.io/smartfile/)
|
|
||||||
|
|
||||||
## Status for master
|
## Install
|
||||||
|
|
||||||
Status Category | Status Badge
|
To integrate `@push.rocks/smartfile` into your project, run:
|
||||||
-- | --
|
|
||||||
GitLab Pipelines | [](https://lossless.cloud)
|
```bash
|
||||||
GitLab Pipline Test Coverage | [](https://lossless.cloud)
|
npm install @push.rocks/smartfile
|
||||||
npm | [](https://lossless.cloud)
|
```
|
||||||
Snyk | [](https://lossless.cloud)
|
|
||||||
TypeScript Support | [](https://lossless.cloud)
|
|
||||||
node Support | [](https://nodejs.org/dist/latest-v10.x/docs/api/)
|
|
||||||
Code Style | [](https://lossless.cloud)
|
|
||||||
PackagePhobia (total standalone install weight) | [](https://lossless.cloud)
|
|
||||||
PackagePhobia (package size on registry) | [](https://lossless.cloud)
|
|
||||||
BundlePhobia (total size when bundled) | [](https://lossless.cloud)
|
|
||||||
Platform support | [](https://lossless.cloud) [](https://lossless.cloud)
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
smartfile is an approach of being one tool to handle files in diverse environments.
|
`@push.rocks/smartfile` offers a comprehensive set of utilities for handling files in Node.js projects using modern TypeScript and ESM syntax. It simplifies various file operations such as reading, writing, copying, and streaming files, as well as working with directories and virtual file systems.
|
||||||
|
|
||||||
### Smartfile Sections
|
### Key Features and Classes
|
||||||
|
|
||||||
smartfile thinks in sections:
|
- **`SmartFile`**: Facilitates reading from and writing to individual files, managing metadata.
|
||||||
|
- **`StreamFile`**: Optimizes memory usage by enabling efficient file streaming.
|
||||||
|
- **`VirtualDirectory`**: Allows manipulation of a group of files or directories as a virtual file system.
|
||||||
|
|
||||||
| section | description |
|
### Getting Started with ESM and TypeScript
|
||||||
| ----------- | ---------------------------------------------------------------------------- |
|
|
||||||
| fs | (object) gets data from fs to somewhere |
|
|
||||||
| memory | gets data from memory to somewhere |
|
|
||||||
| remote | gets data from remote locations to somewhere |
|
|
||||||
| interpreter | (object) handles yaml and json |
|
|
||||||
| smartfile | (class) a virtual representation of a file, alternative to vinyl file format |
|
|
||||||
|
|
||||||
## Contribution
|
First, ensure your project supports ESM syntax and TypeScript. Then, begin by importing the desired features from `@push.rocks/smartfile`:
|
||||||
|
|
||||||
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
|
```typescript
|
||||||
|
import { SmartFile, StreamFile, VirtualDirectory, memory, fs as smartFs } from '@push.rocks/smartfile';
|
||||||
|
```
|
||||||
|
|
||||||
For further information read the linked docs at the top of this readme.
|
### Reading and Writing Files
|
||||||
|
|
||||||
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
|
#### Reading Files
|
||||||
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
|
|
||||||
|
|
||||||
[](https://maintainedby.lossless.com)
|
Reading a JSON file:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const myJsonFile: SmartFile = await SmartFile.fromFilePath('./data.json');
|
||||||
|
const jsonData = JSON.parse(myJsonFile.contents.toString());
|
||||||
|
console.log(jsonData);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Writing Files
|
||||||
|
|
||||||
|
Writing content to a file:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const filePath: string = './output.txt';
|
||||||
|
const content: string = 'Hello, SmartFile!';
|
||||||
|
await memory.toFs(content, filePath);
|
||||||
|
console.log('File saved successfully.');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Streaming Large Files
|
||||||
|
|
||||||
|
For large files, `StreamFile` provides a memory-efficient streaming solution:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { createReadStream } from 'fs';
|
||||||
|
const sourceStream = createReadStream('./large-video.mp4');
|
||||||
|
const myStreamFile = await StreamFile.fromStream(sourceStream, 'large-video.mp4');
|
||||||
|
await myStreamFile.writeToDir('./storage');
|
||||||
|
console.log('Large file streamed to disk successfully.');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with Virtual Directories
|
||||||
|
|
||||||
|
`VirtualDirectory` abstracts a collection of files allowing operations to be performed as if they were on disk:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const virtualDir = new VirtualDirectory();
|
||||||
|
virtualDir.addSmartfiles([smartFile1, smartFile2]); // Assuming these are SmartFile instances
|
||||||
|
await virtualDir.saveToDisk('./virtual-output');
|
||||||
|
console.log('Virtual directory saved to disk.');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced File Operations
|
||||||
|
|
||||||
|
`@push.rocks/smartfile` simplifies complex file operations, including:
|
||||||
|
- Copying directories and files
|
||||||
|
- Removing files or directories
|
||||||
|
- Listing files and directories with filters
|
||||||
|
- Reading file content directly into JavaScript objects
|
||||||
|
|
||||||
|
### Web File Handling
|
||||||
|
|
||||||
|
Handling files from HTTP requests:
|
||||||
|
`@push.rocks/smartfile` offers utilities to work with files from web sources, making it simpler to manage downloads and uploads.
|
||||||
|
|
||||||
|
### Comprehensive File Management
|
||||||
|
|
||||||
|
Whether you're dealing with local files, directories, or files over the internet, `@push.rocks/smartfile` provides a comprehensive set of tools to streamline your workflow and reduce the complexity of file management in your Node.js projects.
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### `SmartFile` Class
|
||||||
|
|
||||||
|
#### Static Methods
|
||||||
|
|
||||||
|
- `SmartFile.fromFilePath(filePath: string, baseArg?: string): Promise<SmartFile>`
|
||||||
|
- Creates a `SmartFile` instance from a file path.
|
||||||
|
- `SmartFile.fromBuffer(filePath: string, contentBufferArg: Buffer, baseArg?: string): Promise<SmartFile>`
|
||||||
|
- Creates a `SmartFile` instance from a `Buffer`.
|
||||||
|
- `SmartFile.fromString(filePath: string, contentStringArg: string, encodingArg: 'utf8' | 'binary', baseArg?: string): Promise<SmartFile>`
|
||||||
|
- Creates a `SmartFile` instance from a string.
|
||||||
|
- `SmartFile.fromFoldedJson(foldedJsonArg: string): Promise<SmartFile>`
|
||||||
|
- Creates a `SmartFile` instance from a folded JSON string.
|
||||||
|
- `SmartFile.fromStream(stream: Readable, filePath: string, baseArg?: string): Promise<SmartFile>`
|
||||||
|
- Creates a `SmartFile` instance from a `Readable` stream.
|
||||||
|
- `SmartFile.fromUrl(urlArg: string): Promise<SmartFile>`
|
||||||
|
- Creates a `SmartFile` instance from a URL.
|
||||||
|
|
||||||
|
#### Instance Properties
|
||||||
|
|
||||||
|
- `path: string`
|
||||||
|
- The relative path of the file.
|
||||||
|
- `parsedPath: path.ParsedPath`
|
||||||
|
- A parsed path object.
|
||||||
|
- `absolutePath: string`
|
||||||
|
- The absolute path of the file.
|
||||||
|
- `absoluteParsedPath: path.ParsedPath`
|
||||||
|
- A parsed absolute path object.
|
||||||
|
- `contentBuffer: Buffer`
|
||||||
|
- The content of the file as a `Buffer`.
|
||||||
|
- `base: string`
|
||||||
|
- The current working directory of the file.
|
||||||
|
- `sync: boolean`
|
||||||
|
- Indicates whether the file is synced with the disk.
|
||||||
|
|
||||||
|
#### Instance Methods
|
||||||
|
|
||||||
|
- `setContentsFromString(contentString: string, encodingArg?: 'utf8' | 'binary'): void`
|
||||||
|
- Sets the contents of the file from a string.
|
||||||
|
- `write(): Promise<void>`
|
||||||
|
- Writes the file to disk at its original location.
|
||||||
|
- `writeToDiskAtPath(filePathArg: string): Promise<void>`
|
||||||
|
- Writes the file to the specified path on disk.
|
||||||
|
- `writeToDir(dirPathArg: string): Promise<string>`
|
||||||
|
- Writes the file to a directory combined with the relative path portion.
|
||||||
|
- `read(): Promise<void>`
|
||||||
|
- Reads the file from disk.
|
||||||
|
- `delete(): Promise<void>`
|
||||||
|
- Deletes the file from disk at its original location.
|
||||||
|
- `getHash(typeArg?: 'path' | 'content' | 'all'): Promise<string>`
|
||||||
|
- Returns a hash of the file based on the specified type.
|
||||||
|
- `updateFileName(fileNameArg: string): void`
|
||||||
|
- Updates the file name of the `SmartFile` instance.
|
||||||
|
- `editContentAsString(editFuncArg: (fileStringArg: string) => Promise<string>): Promise<void>`
|
||||||
|
- Edits the content of the file as a string using the provided edit function.
|
||||||
|
- `getStream(): Readable`
|
||||||
|
- Returns a `Readable` stream from the file's content buffer.
|
||||||
|
|
||||||
|
### `StreamFile` Class
|
||||||
|
|
||||||
|
#### Static Methods
|
||||||
|
|
||||||
|
- `StreamFile.fromPath(filePath: string): Promise<StreamFile>`
|
||||||
|
- Creates a `StreamFile` instance from a file path.
|
||||||
|
- `StreamFile.fromUrl(url: string): Promise<StreamFile>`
|
||||||
|
- Creates a `StreamFile` instance from a URL.
|
||||||
|
- `StreamFile.fromBuffer(buffer: Buffer, relativeFilePath?: string): StreamFile`
|
||||||
|
- Creates a `StreamFile` instance from a `Buffer`.
|
||||||
|
- `StreamFile.fromStream(stream: Readable, relativeFilePath?: string, multiUse?: boolean): StreamFile`
|
||||||
|
- Creates a `StreamFile` instance from a `Readable` stream.
|
||||||
|
|
||||||
|
#### Instance Properties
|
||||||
|
|
||||||
|
- `relativeFilePath?: string`
|
||||||
|
- The relative file path of the `StreamFile`.
|
||||||
|
- `multiUse: boolean`
|
||||||
|
- Indicates whether the `StreamFile` can be used multiple times.
|
||||||
|
- `used: boolean`
|
||||||
|
- Indicates whether the `StreamFile` has been used.
|
||||||
|
|
||||||
|
#### Instance Methods
|
||||||
|
|
||||||
|
- `createReadStream(): Promise<Readable>`
|
||||||
|
- Creates a new `Readable` stream from the source.
|
||||||
|
- `writeToDisk(filePathArg: string): Promise<void>`
|
||||||
|
- Writes the stream to the disk at the specified path.
|
||||||
|
- `writeToDir(dirPathArg: string): Promise<void>`
|
||||||
|
- Writes the stream to a directory combined with the relative path portion.
|
||||||
|
- `getContentAsBuffer(): Promise<Buffer>`
|
||||||
|
- Returns the content of the `StreamFile` as a `Buffer`.
|
||||||
|
- `getContentAsString(formatArg?: 'utf8' | 'binary'): Promise<string>`
|
||||||
|
- Returns the content of the `StreamFile` as a string.
|
||||||
|
|
||||||
|
### `VirtualDirectory` Class
|
||||||
|
|
||||||
|
#### Static Methods
|
||||||
|
|
||||||
|
- `VirtualDirectory.fromFsDirPath(pathArg: string): Promise<VirtualDirectory>`
|
||||||
|
- Creates a `VirtualDirectory` instance from a file system directory path.
|
||||||
|
- `VirtualDirectory.fromVirtualDirTransferableObject(virtualDirTransferableObjectArg: VirtualDirTransferableObject): Promise<VirtualDirectory>`
|
||||||
|
- Creates a `VirtualDirectory` instance from a `VirtualDirTransferableObject`.
|
||||||
|
|
||||||
|
#### Instance Properties
|
||||||
|
|
||||||
|
- `smartfileArray: SmartFile[]`
|
||||||
|
- An array of `SmartFile` instances in the `VirtualDirectory`.
|
||||||
|
|
||||||
|
#### Instance Methods
|
||||||
|
|
||||||
|
- `addSmartfiles(smartfileArrayArg: SmartFile[]): void`
|
||||||
|
- Adds an array of `SmartFile` instances to the `VirtualDirectory`.
|
||||||
|
- `getFileByPath(pathArg: string): Promise<SmartFile | undefined>`
|
||||||
|
- Retrieves a `SmartFile` instance from the `VirtualDirectory` by its path.
|
||||||
|
- `toVirtualDirTransferableObject(): Promise<VirtualDirTransferableObject>`
|
||||||
|
- Converts the `VirtualDirectory` to a `VirtualDirTransferableObject`.
|
||||||
|
- `saveToDisk(dirArg: string): Promise<void>`
|
||||||
|
- Saves the `VirtualDirectory` to the disk at the specified directory.
|
||||||
|
- `shiftToSubdirectory(subDir: string): Promise<VirtualDirectory>`
|
||||||
|
- Shifts the `VirtualDirectory` to a subdirectory.
|
||||||
|
- `addVirtualDirectory(virtualDir: VirtualDirectory, newRoot: string): Promise<void>`
|
||||||
|
- Adds another `VirtualDirectory` to the current `VirtualDirectory` with a new root path.
|
||||||
|
|
||||||
|
### `fs` Module
|
||||||
|
|
||||||
|
The `fs` module provides various file system utility functions. Some notable functions include:
|
||||||
|
|
||||||
|
- `fileExistsSync(filePath: string): boolean`
|
||||||
|
- Checks if a file exists synchronously.
|
||||||
|
- `fileExists(filePath: string): Promise<boolean>`
|
||||||
|
- Checks if a file exists asynchronously.
|
||||||
|
- `listFoldersSync(pathArg: string, regexFilter?: RegExp): string[]`
|
||||||
|
- Lists folders in a directory synchronously.
|
||||||
|
- `listFolders(pathArg: string, regexFilter?: RegExp): Promise<string[]>`
|
||||||
|
- Lists folders in a directory asynchronously.
|
||||||
|
- `listFilesSync(pathArg: string, regexFilter?: RegExp): string[]`
|
||||||
|
- Lists files in a directory synchronously.
|
||||||
|
- `listFiles(pathArg: string, regexFilter?: RegExp): Promise<string[]>`
|
||||||
|
- Lists files in a directory asynchronously.
|
||||||
|
- `listFileTree(dirPathArg: string, miniMatchFilter: string, absolutePathsBool?: boolean): Promise<string[]>`
|
||||||
|
- Lists a file tree using a minimatch filter.
|
||||||
|
- `waitForFileToBeReady(filePathArg: string): Promise<void>`
|
||||||
|
- Waits for a file to be ready (exists and is not empty).
|
||||||
|
- `toFs(filePathArg: string, fileContentArg: string | Buffer | SmartFile | StreamFile, optionsArg?: { respectRelative?: boolean }): Promise<void>`
|
||||||
|
- Writes a string, `Buffer`, `SmartFile`, or `StreamFile` to the file system.
|
||||||
|
|
||||||
|
### `memory` Module
|
||||||
|
|
||||||
|
The `memory` module provides utility functions for working with files in memory. Some notable functions include:
|
||||||
|
|
||||||
|
- `toObject(fileStringArg: string, fileTypeArg: string): any`
|
||||||
|
- Converts a file string to an object based on the file type.
|
||||||
|
- `toFs(fileContentArg: string | Buffer | SmartFile | StreamFile, filePathArg: string, optionsArg?: IToFsOptions): Promise<void>`
|
||||||
|
- Writes a string, `Buffer`, `SmartFile`, or `StreamFile` to the file system.
|
||||||
|
- `toFsSync(fileArg: string, filePathArg: string): void`
|
||||||
|
- Writes a string to the file system synchronously.
|
||||||
|
- `smartfileArrayToFs(smartfileArrayArg: SmartFile[], dirArg: string): Promise<void>`
|
||||||
|
- Writes an array of `SmartFile` instances to the file system.
|
||||||
|
|
||||||
|
### `interpreter` Module
|
||||||
|
|
||||||
|
The `interpreter` module provides utility functions for interpreting file types and contents. Some notable functions include:
|
||||||
|
|
||||||
|
- `filetype(pathArg: string): string`
|
||||||
|
- Determines the file type based on the file path.
|
||||||
|
- `objectFile(fileStringArg: string, fileTypeArg: any): any`
|
||||||
|
- Converts a file string to an object based on the file type.
|
||||||
|
|
||||||
|
## License and Legal Information
|
||||||
|
|
||||||
|
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||||
|
|
||||||
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
### Trademarks
|
||||||
|
|
||||||
|
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
||||||
|
|
||||||
|
### Company Information
|
||||||
|
|
||||||
|
Task Venture Capital GmbH
|
||||||
|
Registered at District court Bremen HRB 35230 HB, Germany
|
||||||
|
|
||||||
|
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
||||||
|
|
||||||
|
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
67
test/test.streamfile.ts
Normal file
67
test/test.streamfile.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import { expect, tap } from '@push.rocks/tapbundle';
|
||||||
|
import * as smartfile from '../ts/index.js'; // adjust the import path as needed
|
||||||
|
|
||||||
|
// Test assets path
|
||||||
|
const testAssetsPath = './test/testassets/';
|
||||||
|
|
||||||
|
// ---------------------------
|
||||||
|
// StreamFile tests
|
||||||
|
// ---------------------------
|
||||||
|
|
||||||
|
tap.test('StreamFile.fromPath should create a StreamFile from a file path', async () => {
|
||||||
|
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
|
||||||
|
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
|
||||||
|
const contentBuffer = await streamFile.getContentAsBuffer();
|
||||||
|
expect(contentBuffer).toBeInstanceOf(Buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('StreamFile.fromUrl should create a StreamFile from a URL', async () => {
|
||||||
|
const streamFile = await smartfile.StreamFile.fromUrl('http://example.com/somefile.json');
|
||||||
|
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('StreamFile.fromBuffer should create a StreamFile from a Buffer', async () => {
|
||||||
|
const buffer = Buffer.from('Some content');
|
||||||
|
const streamFile = smartfile.StreamFile.fromBuffer(buffer, 'bufferfile.txt');
|
||||||
|
expect(streamFile).toBeInstanceOf(smartfile.StreamFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('StreamFile should write the stream to disk', async () => {
|
||||||
|
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
|
||||||
|
await streamFile.writeToDisk(path.join(testAssetsPath, 'temp', 'mytest.json'));
|
||||||
|
// Verify the file was written
|
||||||
|
expect(
|
||||||
|
// We'll use the fileExists method from your smartfile library
|
||||||
|
// Replace with the actual method you use to check file existence
|
||||||
|
await smartfile.fs.fileExists(path.join(testAssetsPath, 'temp', 'mytest.json'))
|
||||||
|
).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('StreamFile should write to a directory', async () => {
|
||||||
|
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
|
||||||
|
await streamFile.writeToDir(path.join(testAssetsPath, 'temp'));
|
||||||
|
// Verify the file was written
|
||||||
|
expect(
|
||||||
|
await smartfile.fs.fileExists(path.join(testAssetsPath, 'temp', 'mytest.json'))
|
||||||
|
).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('StreamFile should return content as a buffer', async () => {
|
||||||
|
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
|
||||||
|
const contentBuffer = await streamFile.getContentAsBuffer();
|
||||||
|
expect(contentBuffer).toBeInstanceOf(Buffer);
|
||||||
|
// Further checks on the content can be added here if necessary
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('StreamFile should return content as a string', async () => {
|
||||||
|
const streamFile = await smartfile.StreamFile.fromPath(path.join(testAssetsPath, 'mytest.json'));
|
||||||
|
const contentString = await streamFile.getContentAsString();
|
||||||
|
expect(typeof contentString).toBeTypeofString();
|
||||||
|
// Verify the content matches what's expected
|
||||||
|
// This assumes the file contains a JSON object with a key 'key1' with value 'this works'
|
||||||
|
expect(JSON.parse(contentString).key1).toEqual('this works');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the test sequence
|
||||||
|
tap.start();
|
117
test/test.ts
117
test/test.ts
@ -1,53 +1,53 @@
|
|||||||
import * as smartfile from '../ts/index';
|
import * as smartfile from '../ts/index.js';
|
||||||
import path = require('path');
|
import * as path from 'path';
|
||||||
|
|
||||||
import { expect, tap } from '@pushrocks/tapbundle';
|
import { expect, tap } from '@push.rocks/tapbundle';
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// smartfile.fs
|
// smartfile.fs
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
|
|
||||||
tap.test('.fs.fileExistsSync -> should return an accurate boolean', async () => {
|
tap.test('.fs.fileExistsSync -> should return an accurate boolean', async () => {
|
||||||
expect(smartfile.fs.fileExistsSync('./test/testassets/mytest.json')).to.be.true;
|
// tslint:disable-next-line: no-unused-expression
|
||||||
expect(smartfile.fs.fileExistsSync('./test/testassets/notthere.json')).be.false;
|
expect(smartfile.fs.fileExistsSync('./test/testassets/mytest.json')).toBeTrue();
|
||||||
|
// tslint:disable-next-line: no-unused-expression
|
||||||
|
expect(smartfile.fs.fileExistsSync('./test/testassets/notthere.json')).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.fileExists -> should resolve or reject a promise', async () => {
|
tap.test('.fs.fileExists -> should resolve or reject a promise', async () => {
|
||||||
expect(smartfile.fs.fileExists('./test/testassets/mytest.json')).to.be.instanceof(Promise);
|
expect(smartfile.fs.fileExists('./test/testassets/mytest.json')).toBeInstanceOf(Promise);
|
||||||
await smartfile.fs.fileExists('./test/testassets/mytest.json');
|
await smartfile.fs.fileExists('./test/testassets/mytest.json');
|
||||||
await smartfile.fs.fileExists('./test/testassets/notthere.json').catch((err) => {
|
await smartfile.fs.fileExists('./test/testassets/notthere.json').catch((err) => {
|
||||||
return expect(err.message).to.equal(
|
return expect(err.message).toEqual(
|
||||||
"ENOENT: no such file or directory, access './test/testassets/notthere.json'"
|
"ENOENT: no such file or directory, access './test/testassets/notthere.json'"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.listFoldersSync() -> should get the file type from a string', async () => {
|
tap.test('.fs.listFoldersSync() -> should get the file type from a string', async () => {
|
||||||
expect(smartfile.fs.listFoldersSync('./test/testassets/')).to.include('testfolder');
|
expect(smartfile.fs.listFoldersSync('./test/testassets/')).toContain('testfolder');
|
||||||
expect(smartfile.fs.listFoldersSync('./test/testassets/')).to.not.include('notExistentFolder');
|
expect(smartfile.fs.listFoldersSync('./test/testassets/')).not.toContain('notExistentFolder');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.listFolders() -> should get the file type from a string', async () => {
|
tap.test('.fs.listFolders() -> should get the file type from a string', async () => {
|
||||||
const folderArrayArg = await smartfile.fs.listFolders('./test/testassets/');
|
const folderArrayArg = await smartfile.fs.listFolders('./test/testassets/');
|
||||||
expect(folderArrayArg).to.include('testfolder');
|
expect(folderArrayArg).toContain('testfolder');
|
||||||
expect(folderArrayArg).to.not.include('notExistentFolder');
|
expect(folderArrayArg).not.toContain('notExistentFolder');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.listFilesSync() -> should get the file type from a string', async () => {
|
tap.test('.fs.listFilesSync() -> should get the file type from a string', async () => {
|
||||||
expect(smartfile.fs.listFilesSync('./test/testassets/')).to.include('mytest.json');
|
expect(smartfile.fs.listFilesSync('./test/testassets/')).toContain('mytest.json');
|
||||||
expect(smartfile.fs.listFilesSync('./test/testassets/')).to.not.include('notExistentFile');
|
expect(smartfile.fs.listFilesSync('./test/testassets/')).not.toContain('notExistentFile');
|
||||||
expect(smartfile.fs.listFilesSync('./test/testassets/', /mytest\.json/)).to.include(
|
expect(smartfile.fs.listFilesSync('./test/testassets/', /mytest\.json/)).toContain('mytest.json');
|
||||||
'mytest.json'
|
expect(smartfile.fs.listFilesSync('./test/testassets/', /mytests.json/)).not.toContain(
|
||||||
);
|
|
||||||
expect(smartfile.fs.listFilesSync('./test/testassets/', /mytests.json/)).to.not.include(
|
|
||||||
'mytest.json'
|
'mytest.json'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.listFiles() -> should get the file type from a string', async () => {
|
tap.test('.fs.listFiles() -> should get the file type from a string', async () => {
|
||||||
const folderArrayArg = await smartfile.fs.listFiles('./test/testassets/');
|
const folderArrayArg = await smartfile.fs.listFiles('./test/testassets/');
|
||||||
expect(folderArrayArg).to.include('mytest.json');
|
expect(folderArrayArg).toContain('mytest.json');
|
||||||
expect(folderArrayArg).to.not.include('notExistentFile');
|
expect(folderArrayArg).not.toContain('notExistentFile');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.listFileTree() -> should get a file tree', async () => {
|
tap.test('.fs.listFileTree() -> should get a file tree', async () => {
|
||||||
@ -55,8 +55,8 @@ tap.test('.fs.listFileTree() -> should get a file tree', async () => {
|
|||||||
path.resolve('./test/testassets/'),
|
path.resolve('./test/testassets/'),
|
||||||
'**/*.txt'
|
'**/*.txt'
|
||||||
);
|
);
|
||||||
expect(folderArrayArg).to.include('testfolder/testfile1.txt');
|
expect(folderArrayArg).toContain('testfolder/testfile1.txt');
|
||||||
expect(folderArrayArg).to.not.include('mytest.json');
|
expect(folderArrayArg).not.toContain('mytest.json');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.fileTreeToObject -> should read a file tree into an Object', async () => {
|
tap.test('.fs.fileTreeToObject -> should read a file tree into an Object', async () => {
|
||||||
@ -64,8 +64,8 @@ tap.test('.fs.fileTreeToObject -> should read a file tree into an Object', async
|
|||||||
path.resolve('./test/testassets/'),
|
path.resolve('./test/testassets/'),
|
||||||
'**/*.txt'
|
'**/*.txt'
|
||||||
);
|
);
|
||||||
expect(fileArrayArg[0]).to.be.instanceof(smartfile.Smartfile);
|
expect(fileArrayArg[0]).toBeInstanceOf(smartfile.SmartFile);
|
||||||
expect(fileArrayArg[0].contents.toString()).to.equal(fileArrayArg[0].contentBuffer.toString());
|
expect(fileArrayArg[0].contents.toString()).toEqual(fileArrayArg[0].contentBuffer.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.copy() -> should copy a directory', async () => {
|
tap.test('.fs.copy() -> should copy a directory', async () => {
|
||||||
@ -91,15 +91,15 @@ tap.test('.fs.remove -> should remove single files', async () => {
|
|||||||
|
|
||||||
tap.test('.fs.removeSync -> should remove single files synchronouly', async () => {
|
tap.test('.fs.removeSync -> should remove single files synchronouly', async () => {
|
||||||
smartfile.fs.removeSync('./test/testassets/temp/testfile1.txt');
|
smartfile.fs.removeSync('./test/testassets/temp/testfile1.txt');
|
||||||
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt')).to.be.false;
|
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt')).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.removeMany -> should remove and array of files', async () => {
|
tap.test('.fs.removeMany -> should remove and array of files', async () => {
|
||||||
smartfile.fs
|
smartfile.fs
|
||||||
.removeMany(['./test/testassets/temp/testfile1.txt', './test/testassets/temp/testfile2.txt'])
|
.removeMany(['./test/testassets/temp/testfile1.txt', './test/testassets/temp/testfile2.txt'])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt')).to.be.false;
|
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt')).toBeFalse();
|
||||||
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile2.txt')).to.be.false;
|
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile2.txt')).toBeFalse();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -108,14 +108,14 @@ tap.test('.fs.removeManySync -> should remove and array of single files synchron
|
|||||||
'./test/testassets/temp/testfile1.txt',
|
'./test/testassets/temp/testfile1.txt',
|
||||||
'./test/testassets/temp/testfile2.txt',
|
'./test/testassets/temp/testfile2.txt',
|
||||||
]);
|
]);
|
||||||
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt')).to.be.false;
|
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile1.txt')).toBeFalse();
|
||||||
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile2.txt')).to.be.false;
|
expect(smartfile.fs.fileExistsSync('./test/testassets/temp/testfile2.txt')).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.toObjectSync() -> should read an ' + '.yaml' + ' file to an object', async () => {
|
tap.test('.fs.toObjectSync() -> should read an .yaml file to an object', async () => {
|
||||||
const testData = smartfile.fs.toObjectSync('./test/testassets/mytest.yaml');
|
const testData = smartfile.fs.toObjectSync('./test/testassets/mytest.yaml');
|
||||||
expect(testData).to.include({ key1: 'this works' });
|
expect(testData.key1).toEqual('this works');
|
||||||
expect(testData).to.include({ key2: 'this works too' });
|
expect(testData.key2).toEqual('this works too');
|
||||||
});
|
});
|
||||||
tap.test(
|
tap.test(
|
||||||
'.fs.toObjectSync() -> should state unknown file type for unknown file types',
|
'.fs.toObjectSync() -> should state unknown file type for unknown file types',
|
||||||
@ -124,16 +124,14 @@ tap.test(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
tap.test('.fs.toObjectSync() -> should read an ' + '.json' + ' file to an object', async () => {
|
tap.test('.fs.toObjectSync() -> should read an .json file to an object', async () => {
|
||||||
const testData = smartfile.fs.toObjectSync('./test/testassets/mytest.json');
|
const testData = smartfile.fs.toObjectSync('./test/testassets/mytest.json');
|
||||||
expect(testData).to.include({ key1: 'this works' });
|
expect(testData.key1).toEqual('this works');
|
||||||
expect(testData).to.include({ key2: 'this works too' });
|
expect(testData.key2).toEqual('this works too');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('.fs.toStringSync() -> should read a file to a string', async () => {
|
tap.test('.fs.toStringSync() -> should read a file to a string', async () => {
|
||||||
expect(smartfile.fs.toStringSync('./test/testassets/mytest.txt')).to.equal(
|
expect(smartfile.fs.toStringSync('./test/testassets/mytest.txt')).toEqual('Some TestString &&%$');
|
||||||
'Some TestString &&%$'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@ -141,7 +139,7 @@ tap.test('.fs.toStringSync() -> should read a file to a string', async () => {
|
|||||||
// ---------------------------
|
// ---------------------------
|
||||||
|
|
||||||
tap.test('.interpreter.filetype() -> should get the file type from a string', async () => {
|
tap.test('.interpreter.filetype() -> should get the file type from a string', async () => {
|
||||||
expect(smartfile.interpreter.filetype('./somefolder/data.json')).equal('json');
|
expect(smartfile.interpreter.filetype('./somefolder/data.json')).toEqual('json');
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@ -167,21 +165,6 @@ tap.test(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
tap.test('.remote.toString() -> should load a remote file to a variable', async () => {
|
|
||||||
const responseString = await smartfile.remote.toString(
|
|
||||||
'https://raw.githubusercontent.com/pushrocks/smartfile/master/test/testassets/mytest.txt'
|
|
||||||
);
|
|
||||||
expect(responseString).to.equal('Some TestString &&%$');
|
|
||||||
});
|
|
||||||
|
|
||||||
tap.test('.remote.toString() -> should reject a Promise when the link is false', async (tools) => {
|
|
||||||
await smartfile.remote.toString('https://push.rocks/doesnotexist.txt').catch((err) => {
|
|
||||||
return expect(err.message).to.equal(
|
|
||||||
'could not get remote file from https://push.rocks/doesnotexist.txt'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// smartfile.Smartfile
|
// smartfile.Smartfile
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@ -192,14 +175,14 @@ tap.test('.Smartfile -> should produce vinyl compatible files', async () => {
|
|||||||
'./test/testassets/testfolder/**/*'
|
'./test/testassets/testfolder/**/*'
|
||||||
);
|
);
|
||||||
const localSmartfile = smartfileArray[0];
|
const localSmartfile = smartfileArray[0];
|
||||||
expect(localSmartfile).to.be.instanceof(smartfile.Smartfile);
|
expect(localSmartfile).toBeInstanceOf(smartfile.SmartFile);
|
||||||
expect(localSmartfile.contents).to.be.instanceof(Buffer);
|
expect(localSmartfile.contents).toBeInstanceOf(Buffer);
|
||||||
// tslint:disable-next-line:no-unused-expression
|
// tslint:disable-next-line:no-unused-expression
|
||||||
expect(localSmartfile.isBuffer()).to.be.true;
|
expect(localSmartfile.isBuffer()).toBeTrue();
|
||||||
// tslint:disable-next-line:no-unused-expression
|
// tslint:disable-next-line:no-unused-expression
|
||||||
expect(localSmartfile.isDirectory()).to.be.false;
|
expect(localSmartfile.isDirectory()).toBeFalse();
|
||||||
// tslint:disable-next-line:no-unused-expression
|
// tslint:disable-next-line:no-unused-expression
|
||||||
expect(localSmartfile.isNull()).to.be.false;
|
expect(localSmartfile.isNull()).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('should output a smartfile array to disk', async () => {
|
tap.test('should output a smartfile array to disk', async () => {
|
||||||
@ -219,11 +202,23 @@ tap.test('should output a smartfile array to disk', async () => {
|
|||||||
tap.test('should create, store and retrieve valid smartfiles', async () => {
|
tap.test('should create, store and retrieve valid smartfiles', async () => {
|
||||||
const fileString = 'hi there';
|
const fileString = 'hi there';
|
||||||
const filePath = './test/testassets/utf8.txt';
|
const filePath = './test/testassets/utf8.txt';
|
||||||
const smartfileInstance = await smartfile.Smartfile.fromString(filePath, fileString, 'utf8');
|
const smartfileInstance = await smartfile.SmartFile.fromString(filePath, fileString, 'utf8');
|
||||||
smartfileInstance.write();
|
smartfileInstance.write();
|
||||||
const smartfileInstance2 = await smartfile.Smartfile.fromFilePath(filePath);
|
const smartfileInstance2 = await smartfile.SmartFile.fromFilePath(filePath);
|
||||||
const retrievedString = smartfileInstance.contents.toString();
|
const retrievedString = smartfileInstance.contents.toString();
|
||||||
expect(retrievedString).to.equal(fileString);
|
expect(retrievedString).toEqual(fileString);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should get a hash', async () => {
|
||||||
|
const fileString = 'hi there';
|
||||||
|
const filePath = './test/testassets/utf8.txt';
|
||||||
|
const smartfileInstance = await smartfile.SmartFile.fromString(filePath, fileString, 'utf8');
|
||||||
|
const hash = await smartfileInstance.getHash();
|
||||||
|
console.log(hash);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should wait for file to be ready', async () => {
|
||||||
|
await smartfile.fs.waitForFileToBeReady('./test/testassets/mytest.json');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.start();
|
tap.start();
|
||||||
|
15
test/test.virtualdirectory.ts
Normal file
15
test/test.virtualdirectory.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { tap, expect } from '@push.rocks/tapbundle';
|
||||||
|
|
||||||
|
import * as smartfile from '../ts/index.js';
|
||||||
|
|
||||||
|
tap.test('should create a virtualdirectory', async () => {
|
||||||
|
const virtualDir = await smartfile.VirtualDirectory.fromFsDirPath('./test/testassets/testfolder');
|
||||||
|
expect(virtualDir.smartfileArray.length).toEqual(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should write to a directory', async () => {
|
||||||
|
const virtualDir = await smartfile.VirtualDirectory.fromFsDirPath('./test/testassets/testfolder');
|
||||||
|
virtualDir.saveToDisk('./test/testassets/test');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.start();
|
8
test/testassets/temp/mytest.json
Normal file
8
test/testassets/temp/mytest.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"key1": "this works",
|
||||||
|
"key2": "this works too",
|
||||||
|
"key3": {
|
||||||
|
"nestedkey1": "hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
8
test/testassets/temp/test/testassets/mytest.json
Normal file
8
test/testassets/temp/test/testassets/mytest.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"key1": "this works",
|
||||||
|
"key2": "this works too",
|
||||||
|
"key3": {
|
||||||
|
"nestedkey1": "hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
okidoks
|
1
test/testassets/test/subfolder/subtestfile.txt
Normal file
1
test/testassets/test/subfolder/subtestfile.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
hi
|
0
test/testassets/test/testfile1.txt
Normal file
0
test/testassets/test/testfile1.txt
Normal file
0
test/testassets/test/testfile2.txt
Normal file
0
test/testassets/test/testfile2.txt
Normal file
8
ts/00_commitinfo_data.ts
Normal file
8
ts/00_commitinfo_data.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* autocreated commitinfo by @pushrocks/commitinfo
|
||||||
|
*/
|
||||||
|
export const commitinfo = {
|
||||||
|
name: '@push.rocks/smartfile',
|
||||||
|
version: '11.0.14',
|
||||||
|
description: 'provides a robust suite of tools for managing files in Node.js using TypeScript.'
|
||||||
|
}
|
309
ts/classes.smartfile.ts
Normal file
309
ts/classes.smartfile.ts
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
import * as plugins from './plugins.js';
|
||||||
|
import * as fs from './fs.js';
|
||||||
|
import * as memory from './memory.js';
|
||||||
|
|
||||||
|
export interface ISmartfileConstructorOptions {
|
||||||
|
path: string;
|
||||||
|
contentBuffer: Buffer;
|
||||||
|
base: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* an vinyl file compatible in memory file class
|
||||||
|
*/
|
||||||
|
export class SmartFile extends plugins.smartjson.Smartjson {
|
||||||
|
// ======
|
||||||
|
// STATIC
|
||||||
|
// ======
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates a Smartfile from a filePath
|
||||||
|
* @param filePath
|
||||||
|
*/
|
||||||
|
public static async fromFilePath(filePath: string, baseArg: string = process.cwd()) {
|
||||||
|
filePath = plugins.path.resolve(filePath);
|
||||||
|
const fileBuffer = fs.toBufferSync(filePath);
|
||||||
|
const smartfile = new SmartFile({
|
||||||
|
contentBuffer: fileBuffer,
|
||||||
|
base: baseArg,
|
||||||
|
path: plugins.path.relative(baseArg, filePath),
|
||||||
|
});
|
||||||
|
return smartfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async fromBuffer(
|
||||||
|
filePath: string,
|
||||||
|
contentBufferArg: Buffer,
|
||||||
|
baseArg: string = process.cwd()
|
||||||
|
) {
|
||||||
|
const smartfile = new SmartFile({
|
||||||
|
contentBuffer: contentBufferArg,
|
||||||
|
base: baseArg,
|
||||||
|
path: plugins.path.relative(baseArg, filePath),
|
||||||
|
});
|
||||||
|
|
||||||
|
return smartfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async fromString(
|
||||||
|
filePath: string,
|
||||||
|
contentStringArg: string,
|
||||||
|
encodingArg: 'utf8' | 'binary',
|
||||||
|
baseArg = process.cwd()
|
||||||
|
) {
|
||||||
|
const smartfile = new SmartFile({
|
||||||
|
contentBuffer: Buffer.from(contentStringArg, encodingArg),
|
||||||
|
base: baseArg,
|
||||||
|
path: plugins.path.relative(baseArg, filePath),
|
||||||
|
});
|
||||||
|
|
||||||
|
return smartfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async fromFoldedJson(foldedJsonArg: string) {
|
||||||
|
return new SmartFile(plugins.smartjson.parse(foldedJsonArg));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates a Smartfile from a ReadableStream
|
||||||
|
* @param stream a readable stream that provides file content
|
||||||
|
* @param filePath the file path to associate with the content
|
||||||
|
* @param baseArg the base path to use for the file
|
||||||
|
*/
|
||||||
|
public static async fromStream(
|
||||||
|
stream: plugins.stream.Readable,
|
||||||
|
filePath: string,
|
||||||
|
baseArg: string = process.cwd()
|
||||||
|
): Promise<SmartFile> {
|
||||||
|
return new Promise<SmartFile>((resolve, reject) => {
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
|
||||||
|
stream.on('error', (error) => reject(error));
|
||||||
|
stream.on('end', () => {
|
||||||
|
const contentBuffer = Buffer.concat(chunks);
|
||||||
|
const smartfile = new SmartFile({
|
||||||
|
contentBuffer: contentBuffer,
|
||||||
|
base: baseArg,
|
||||||
|
path: plugins.path.relative(baseArg, filePath),
|
||||||
|
});
|
||||||
|
resolve(smartfile);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async fromUrl (urlArg: string) {
|
||||||
|
const response = await plugins.smartrequest.getBinary(urlArg);
|
||||||
|
const smartfile = await SmartFile.fromBuffer(urlArg, response.body);
|
||||||
|
return smartfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========
|
||||||
|
// INSTANCE
|
||||||
|
// ========
|
||||||
|
/**
|
||||||
|
* the relative path of the file
|
||||||
|
*/
|
||||||
|
@plugins.smartjson.foldDec()
|
||||||
|
public path: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a parsed path
|
||||||
|
*/
|
||||||
|
public get parsedPath(): plugins.path.ParsedPath {
|
||||||
|
return plugins.path.parse(this.path);
|
||||||
|
}
|
||||||
|
public get absolutePath() {
|
||||||
|
return plugins.path.join(this.base, this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get absoluteParsedPath() {
|
||||||
|
return plugins.path.parse(this.absolutePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the content of the file as Buffer
|
||||||
|
*/
|
||||||
|
@plugins.smartjson.foldDec()
|
||||||
|
public contentBuffer: Buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current working directory of the file
|
||||||
|
* Note:this is similar to gulp and different from native node path base
|
||||||
|
*/
|
||||||
|
@plugins.smartjson.foldDec()
|
||||||
|
public base: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sync the file with disk
|
||||||
|
*/
|
||||||
|
@plugins.smartjson.foldDec()
|
||||||
|
public sync: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the constructor of Smartfile
|
||||||
|
* @param optionsArg
|
||||||
|
*/
|
||||||
|
|
||||||
|
constructor(optionsArg: ISmartfileConstructorOptions) {
|
||||||
|
super();
|
||||||
|
if (optionsArg.contentBuffer) {
|
||||||
|
this.contentBuffer = optionsArg.contentBuffer;
|
||||||
|
} else {
|
||||||
|
console.log('created empty Smartfile?');
|
||||||
|
}
|
||||||
|
this.path = optionsArg.path;
|
||||||
|
this.base = optionsArg.base;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set contents from string
|
||||||
|
* @param contentString
|
||||||
|
*/
|
||||||
|
public setContentsFromString(contentString: string, encodingArg: 'utf8' | 'binary' = 'utf8') {
|
||||||
|
this.contents = Buffer.from(contentString, encodingArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* write file to disk at its original location
|
||||||
|
* Behaviours:
|
||||||
|
* - no argument write to exactly where the file was picked up
|
||||||
|
*/
|
||||||
|
public async write() {
|
||||||
|
let writePath = plugins.smartpath.transform.makeAbsolute(this.path, this.base);
|
||||||
|
console.log(`writing to ${writePath}`);
|
||||||
|
await memory.toFs(this.contentBuffer, writePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes the file to path given as argument
|
||||||
|
* note: if the path is not absolute, takes process.cwd() as base
|
||||||
|
* @param filePathArg
|
||||||
|
*/
|
||||||
|
public async writeToDiskAtPath(filePathArg: string) {
|
||||||
|
if (!plugins.path.isAbsolute(filePathArg)) {
|
||||||
|
filePathArg = plugins.path.join(process.cwd(), filePathArg);
|
||||||
|
}
|
||||||
|
await memory.toFs(this.contentBuffer, filePathArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes the file to a directory combined with the relative path portion
|
||||||
|
* @param dirPathArg
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async writeToDir(dirPathArg: string) {
|
||||||
|
dirPathArg = plugins.smartpath.transform.toAbsolute(dirPathArg) as string;
|
||||||
|
const filePath = plugins.path.join(dirPathArg, this.path);
|
||||||
|
await memory.toFs(this.contentBuffer, filePath);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read file from disk
|
||||||
|
*/
|
||||||
|
public async read() {
|
||||||
|
this.contentBuffer = await fs.toBuffer(plugins.path.join(this.base, this.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* deletes the file from disk at its original location
|
||||||
|
*/
|
||||||
|
public async delete() {
|
||||||
|
await fs.remove(plugins.path.join(this.base, this.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------
|
||||||
|
// vinyl compatibility
|
||||||
|
// -----------------------------------------------
|
||||||
|
/**
|
||||||
|
* vinyl-compatibility: alias of this.contentBuffer
|
||||||
|
*/
|
||||||
|
get contents(): Buffer {
|
||||||
|
return this.contentBuffer;
|
||||||
|
}
|
||||||
|
set contents(contentsArg) {
|
||||||
|
this.contentBuffer = contentsArg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vinyl-compatibility
|
||||||
|
*/
|
||||||
|
public get cwd() {
|
||||||
|
return process.cwd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return relative path of file
|
||||||
|
*/
|
||||||
|
public get relative(): string {
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return truw when the file has content
|
||||||
|
*/
|
||||||
|
public isNull(): boolean {
|
||||||
|
if (!this.contentBuffer) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return true if contents are Buffer
|
||||||
|
*/
|
||||||
|
public isBuffer(): boolean {
|
||||||
|
if (this.contents instanceof Buffer) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isDirectory() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isStream() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isSymbolic() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getHash(typeArg: 'path' | 'content' | 'all' = 'all') {
|
||||||
|
const pathHash = await plugins.smarthash.sha256FromString(this.path);
|
||||||
|
const contentHash = await plugins.smarthash.sha256FromBuffer(this.contentBuffer);
|
||||||
|
const combinedHash = await plugins.smarthash.sha256FromString(pathHash + contentHash);
|
||||||
|
switch (typeArg) {
|
||||||
|
case 'path':
|
||||||
|
return pathHash;
|
||||||
|
case 'content':
|
||||||
|
return contentHash;
|
||||||
|
case 'all':
|
||||||
|
default:
|
||||||
|
return combinedHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update things
|
||||||
|
public updateFileName(fileNameArg: string) {
|
||||||
|
const oldFileName = this.parsedPath.base;
|
||||||
|
this.path = this.path.replace(new RegExp(oldFileName + '$'), fileNameArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async editContentAsString(editFuncArg: (fileStringArg: string) => Promise<string>) {
|
||||||
|
const newFileString = await editFuncArg(this.contentBuffer.toString());
|
||||||
|
this.contentBuffer = Buffer.from(newFileString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a ReadableStream from the file's content buffer
|
||||||
|
*/
|
||||||
|
public getStream(): plugins.stream.Readable {
|
||||||
|
const stream = new plugins.stream.Readable();
|
||||||
|
stream.push(this.contentBuffer); // Push the content buffer to the stream
|
||||||
|
stream.push(null); // Push null to signify the end of the stream (EOF)
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
}
|
152
ts/classes.streamfile.ts
Normal file
152
ts/classes.streamfile.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import * as plugins from './plugins.js';
|
||||||
|
import * as smartfileFs from './fs.js';
|
||||||
|
import * as smartfileFsStream from './fsstream.js';
|
||||||
|
import { Readable } from 'stream';
|
||||||
|
|
||||||
|
type TStreamSource = (streamFile: StreamFile) => Promise<Readable>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The StreamFile class represents a file as a stream.
|
||||||
|
* It allows creating streams from a file path, a URL, or a buffer.
|
||||||
|
*/
|
||||||
|
export class StreamFile {
|
||||||
|
// INSTANCE
|
||||||
|
relativeFilePath?: string;
|
||||||
|
private streamSource: TStreamSource;
|
||||||
|
|
||||||
|
// enable stream based multi use
|
||||||
|
private cachedStreamBuffer?: Buffer;
|
||||||
|
public multiUse: boolean;
|
||||||
|
public used: boolean = false;
|
||||||
|
|
||||||
|
private constructor(streamSource: TStreamSource, relativeFilePath?: string) {
|
||||||
|
this.streamSource = streamSource;
|
||||||
|
this.relativeFilePath = relativeFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STATIC
|
||||||
|
|
||||||
|
public static async fromPath(filePath: string): Promise<StreamFile> {
|
||||||
|
const streamSource: TStreamSource = async (stremFileArg) => smartfileFsStream.createReadStream(filePath);
|
||||||
|
const streamFile = new StreamFile(streamSource, filePath);
|
||||||
|
streamFile.multiUse = true;
|
||||||
|
return streamFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async fromUrl(url: string): Promise<StreamFile> {
|
||||||
|
const streamSource: TStreamSource = async (streamFileArg) => plugins.smartrequest.getStream(url); // Replace with actual plugin method
|
||||||
|
const streamFile = new StreamFile(streamSource);
|
||||||
|
streamFile.multiUse = true;
|
||||||
|
return streamFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromBuffer(buffer: Buffer, relativeFilePath?: string): StreamFile {
|
||||||
|
const streamSource: TStreamSource = async (streamFileArg) => {
|
||||||
|
const stream = new Readable();
|
||||||
|
stream.push(buffer);
|
||||||
|
stream.push(null); // End of stream
|
||||||
|
return stream;
|
||||||
|
};
|
||||||
|
const streamFile = new StreamFile(streamSource, relativeFilePath);
|
||||||
|
streamFile.multiUse = true;
|
||||||
|
return streamFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a StreamFile from an existing Readable stream with an option for multiple uses.
|
||||||
|
* @param stream A Node.js Readable stream.
|
||||||
|
* @param relativeFilePath Optional file path for the stream.
|
||||||
|
* @param multiUse If true, the stream can be read multiple times, caching its content.
|
||||||
|
* @returns A StreamFile instance.
|
||||||
|
*/
|
||||||
|
public static fromStream(stream: Readable, relativeFilePath?: string, multiUse: boolean = false): StreamFile {
|
||||||
|
const streamSource: TStreamSource = (streamFileArg) => {
|
||||||
|
if (streamFileArg.multiUse) {
|
||||||
|
// If multi-use is enabled and we have cached content, create a new readable stream from the buffer
|
||||||
|
const bufferedStream = new Readable();
|
||||||
|
bufferedStream.push(streamFileArg.cachedStreamBuffer);
|
||||||
|
bufferedStream.push(null); // No more data to push
|
||||||
|
return Promise.resolve(bufferedStream);
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(stream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const streamFile = new StreamFile(streamSource, relativeFilePath);
|
||||||
|
streamFile.multiUse = multiUse;
|
||||||
|
|
||||||
|
// If multi-use is enabled, cache the stream when it's first read
|
||||||
|
if (multiUse) {
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
|
||||||
|
stream.on('end', () => {
|
||||||
|
streamFile.cachedStreamBuffer = Buffer.concat(chunks);
|
||||||
|
});
|
||||||
|
// It's important to handle errors that may occur during streaming
|
||||||
|
stream.on('error', (err) => {
|
||||||
|
console.error('Error while caching stream:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return streamFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
|
||||||
|
private checkMultiUse() {
|
||||||
|
if (!this.multiUse && this.used) {
|
||||||
|
throw new Error('This stream can only be used once.');
|
||||||
|
}
|
||||||
|
this.used = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new readable stream from the source.
|
||||||
|
*/
|
||||||
|
public async createReadStream(): Promise<Readable> {
|
||||||
|
return this.streamSource(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the stream to the disk at the specified path.
|
||||||
|
* @param filePathArg The file path where the stream should be written.
|
||||||
|
*/
|
||||||
|
public async writeToDisk(filePathArg: string): Promise<void> {
|
||||||
|
this.checkMultiUse();
|
||||||
|
const readStream = await this.createReadStream();
|
||||||
|
const writeStream = smartfileFsStream.createWriteStream(filePathArg);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
readStream.pipe(writeStream);
|
||||||
|
readStream.on('error', reject);
|
||||||
|
writeStream.on('error', reject);
|
||||||
|
writeStream.on('finish', resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async writeToDir(dirPathArg: string) {
|
||||||
|
this.checkMultiUse();
|
||||||
|
const filePath = plugins.path.join(dirPathArg, this.relativeFilePath);
|
||||||
|
await smartfileFs.ensureDir(plugins.path.parse(filePath).dir);
|
||||||
|
return this.writeToDisk(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getContentAsBuffer() {
|
||||||
|
this.checkMultiUse();
|
||||||
|
const done = plugins.smartpromise.defer<Buffer>();
|
||||||
|
const readStream = await this.createReadStream();
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
readStream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
|
||||||
|
readStream.on('error', done.reject);
|
||||||
|
readStream.on('end', () => {
|
||||||
|
const contentBuffer = Buffer.concat(chunks);
|
||||||
|
done.resolve(contentBuffer);
|
||||||
|
});
|
||||||
|
return done.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getContentAsString(formatArg: 'utf8' | 'binary' = 'utf8') {
|
||||||
|
const contentBuffer = await this.getContentAsBuffer();
|
||||||
|
return contentBuffer.toString(formatArg);
|
||||||
|
}
|
||||||
|
}
|
87
ts/classes.virtualdirectory.ts
Normal file
87
ts/classes.virtualdirectory.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { SmartFile } from './classes.smartfile.js';
|
||||||
|
import * as plugins from './plugins.js';
|
||||||
|
import * as fs from './fs.js';
|
||||||
|
|
||||||
|
|
||||||
|
export interface IVirtualDirectoryConstructorOptions {
|
||||||
|
mode: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a virtual directory exposes a fs api
|
||||||
|
*/
|
||||||
|
export class VirtualDirectory {
|
||||||
|
|
||||||
|
consstructor(options = {}) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// STATIC
|
||||||
|
public static async fromFsDirPath(pathArg: string): Promise<VirtualDirectory> {
|
||||||
|
const newVirtualDir = new VirtualDirectory();
|
||||||
|
newVirtualDir.addSmartfiles(await fs.fileTreeToObject(pathArg, '**/*'));
|
||||||
|
return newVirtualDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async fromVirtualDirTransferableObject(
|
||||||
|
virtualDirTransferableObjectArg: plugins.smartfileInterfaces.VirtualDirTransferableObject
|
||||||
|
): Promise<VirtualDirectory> {
|
||||||
|
const newVirtualDir = new VirtualDirectory();
|
||||||
|
for (const fileArg of virtualDirTransferableObjectArg.files) {
|
||||||
|
newVirtualDir.addSmartfiles([SmartFile.enfoldFromJson(fileArg) as SmartFile]);
|
||||||
|
}
|
||||||
|
return newVirtualDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// INSTANCE
|
||||||
|
public smartfileArray: SmartFile[] = [];
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
public addSmartfiles(smartfileArrayArg: SmartFile[]) {
|
||||||
|
this.smartfileArray = this.smartfileArray.concat(smartfileArrayArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getFileByPath(pathArg: string) {
|
||||||
|
for (const smartfile of this.smartfileArray) {
|
||||||
|
if (smartfile.path === pathArg) {
|
||||||
|
return smartfile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async toVirtualDirTransferableObject(): Promise<plugins.smartfileInterfaces.VirtualDirTransferableObject> {
|
||||||
|
return {
|
||||||
|
files: this.smartfileArray.map((smartfileArg) => smartfileArg.foldToJson()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async saveToDisk(dirArg: string) {
|
||||||
|
console.log(`writing VirtualDirectory with ${this.smartfileArray.length} files to directory:
|
||||||
|
--> ${dirArg}`);
|
||||||
|
for (const smartfileArg of this.smartfileArray) {
|
||||||
|
const filePath = await smartfileArg.writeToDir(dirArg);
|
||||||
|
console.log(`wrote ${smartfileArg.relative} to
|
||||||
|
--> ${filePath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async shiftToSubdirectory(subDir: string): Promise<VirtualDirectory> {
|
||||||
|
const newVirtualDir = new VirtualDirectory();
|
||||||
|
for (const file of this.smartfileArray) {
|
||||||
|
if (file.path.startsWith(subDir)) {
|
||||||
|
const adjustedFilePath = plugins.path.relative(subDir, file.path);
|
||||||
|
file.path = adjustedFilePath;
|
||||||
|
newVirtualDir.addSmartfiles([file]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newVirtualDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addVirtualDirectory(virtualDir: VirtualDirectory, newRoot: string): Promise<void> {
|
||||||
|
for (const file of virtualDir.smartfileArray) {
|
||||||
|
file.path = plugins.path.join(newRoot, file.path);
|
||||||
|
}
|
||||||
|
this.addSmartfiles(virtualDir.smartfileArray);
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
import plugins = require('./smartfile.plugins');
|
import * as plugins from './plugins.js';
|
||||||
import SmartfileInterpreter = require('./smartfile.interpreter');
|
import * as interpreter from './interpreter.js';
|
||||||
|
|
||||||
import { Smartfile } from './smartfile.classes.smartfile';
|
import { SmartFile } from './classes.smartfile.js';
|
||||||
|
|
||||||
import * as memory from './smartfile.memory';
|
import * as memory from './memory.js';
|
||||||
|
import type { StreamFile } from './classes.streamfile.js';
|
||||||
/*===============================================================
|
/*===============================================================
|
||||||
============================ Checks =============================
|
============================ Checks =============================
|
||||||
===============================================================*/
|
===============================================================*/
|
||||||
@ -40,7 +41,18 @@ export const fileExists = async (filePath): Promise<boolean> => {
|
|||||||
/**
|
/**
|
||||||
* Checks if given path points to an existing directory
|
* Checks if given path points to an existing directory
|
||||||
*/
|
*/
|
||||||
export const isDirectory = (pathArg): boolean => {
|
export const isDirectory = (pathArg: string): boolean => {
|
||||||
|
try {
|
||||||
|
return plugins.fsExtra.statSync(pathArg).isDirectory();
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if given path points to an existing directory
|
||||||
|
*/
|
||||||
|
export const isDirectorySync = (pathArg: string): boolean => {
|
||||||
try {
|
try {
|
||||||
return plugins.fsExtra.statSync(pathArg).isDirectory();
|
return plugins.fsExtra.statSync(pathArg).isDirectory();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -186,8 +198,8 @@ export const removeManySync = (filePathArrayArg: string[]): void => {
|
|||||||
export const toObjectSync = (filePathArg, fileTypeArg?) => {
|
export const toObjectSync = (filePathArg, fileTypeArg?) => {
|
||||||
const fileString = plugins.fsExtra.readFileSync(filePathArg, 'utf8');
|
const fileString = plugins.fsExtra.readFileSync(filePathArg, 'utf8');
|
||||||
let fileType;
|
let fileType;
|
||||||
fileTypeArg ? (fileType = fileTypeArg) : (fileType = SmartfileInterpreter.filetype(filePathArg));
|
fileTypeArg ? (fileType = fileTypeArg) : (fileType = interpreter.filetype(filePathArg));
|
||||||
return SmartfileInterpreter.objectFile(fileString, fileType);
|
return interpreter.objectFile(fileString, fileType);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,19 +207,38 @@ export const toObjectSync = (filePathArg, fileTypeArg?) => {
|
|||||||
*/
|
*/
|
||||||
export const toStringSync = (filePath: string): string => {
|
export const toStringSync = (filePath: string): string => {
|
||||||
const encoding = plugins.smartmime.getEncoding(filePath);
|
const encoding = plugins.smartmime.getEncoding(filePath);
|
||||||
const fileString: string = plugins.fsExtra.readFileSync(filePath, encoding);
|
let fileString: string | Buffer = plugins.fsExtra.readFileSync(filePath, encoding);
|
||||||
|
if (Buffer.isBuffer(fileString)) {
|
||||||
|
fileString = fileString.toString('binary');
|
||||||
|
}
|
||||||
return fileString;
|
return fileString;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const toBuffer = async (filePath: string): Promise<Buffer> => {
|
||||||
|
return plugins.fsExtra.readFile(filePath);
|
||||||
|
};
|
||||||
|
|
||||||
export const toBufferSync = (filePath: string): Buffer => {
|
export const toBufferSync = (filePath: string): Buffer => {
|
||||||
return plugins.fsExtra.readFileSync(filePath);
|
return plugins.fsExtra.readFileSync(filePath);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Readable Stream from a file path.
|
||||||
|
* @param filePath The path to the file.
|
||||||
|
* @returns {fs.ReadStream}
|
||||||
|
*/
|
||||||
|
export const toReadStream = (filePath: string): plugins.fs.ReadStream => {
|
||||||
|
if (!fileExistsSync(filePath)) {
|
||||||
|
throw new Error(`File does not exist at path: ${filePath}`);
|
||||||
|
}
|
||||||
|
return plugins.fsExtra.createReadStream(filePath);
|
||||||
|
};
|
||||||
|
|
||||||
export const fileTreeToHash = async (dirPathArg: string, miniMatchFilter: string) => {
|
export const fileTreeToHash = async (dirPathArg: string, miniMatchFilter: string) => {
|
||||||
const fileTreeObject = await fileTreeToObject(dirPathArg, miniMatchFilter);
|
const fileTreeObject = await fileTreeToObject(dirPathArg, miniMatchFilter);
|
||||||
let combinedString = '';
|
let combinedString = '';
|
||||||
for (const smartfile of fileTreeObject) {
|
for (const smartfile of fileTreeObject) {
|
||||||
combinedString += smartfile.contentBuffer.toString();
|
combinedString += await smartfile.getHash();
|
||||||
}
|
}
|
||||||
const hash = await plugins.smarthash.sha256FromString(combinedString);
|
const hash = await plugins.smarthash.sha256FromString(combinedString);
|
||||||
return hash;
|
return hash;
|
||||||
@ -224,11 +255,11 @@ export const fileTreeToObject = async (dirPathArg: string, miniMatchFilter: stri
|
|||||||
if (plugins.path.isAbsolute(miniMatchFilter)) {
|
if (plugins.path.isAbsolute(miniMatchFilter)) {
|
||||||
dirPath = '/';
|
dirPath = '/';
|
||||||
} else {
|
} else {
|
||||||
dirPath = dirPathArg;
|
dirPath = plugins.smartpath.transform.toAbsolute(dirPathArg) as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileTree = await listFileTree(dirPath, miniMatchFilter);
|
const fileTree = await listFileTree(dirPath, miniMatchFilter);
|
||||||
const smartfileArray: Smartfile[] = [];
|
const smartfileArray: SmartFile[] = [];
|
||||||
for (const filePath of fileTree) {
|
for (const filePath of fileTree) {
|
||||||
const readPath = ((): string => {
|
const readPath = ((): string => {
|
||||||
if (!plugins.path.isAbsolute(filePath)) {
|
if (!plugins.path.isAbsolute(filePath)) {
|
||||||
@ -237,12 +268,12 @@ export const fileTreeToObject = async (dirPathArg: string, miniMatchFilter: stri
|
|||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
const fileContentString = toStringSync(readPath);
|
const fileBuffer = plugins.fs.readFileSync(readPath);
|
||||||
|
|
||||||
// push a read file as Smartfile
|
// push a read file as Smartfile
|
||||||
smartfileArray.push(
|
smartfileArray.push(
|
||||||
new Smartfile({
|
new SmartFile({
|
||||||
contentBuffer: Buffer.from(fileContentString),
|
contentBuffer: fileBuffer,
|
||||||
base: dirPath,
|
base: dirPath,
|
||||||
path: filePath,
|
path: filePath,
|
||||||
})
|
})
|
||||||
@ -334,8 +365,6 @@ export const listFileTree = async (
|
|||||||
miniMatchFilter: string,
|
miniMatchFilter: string,
|
||||||
absolutePathsBool: boolean = false
|
absolutePathsBool: boolean = false
|
||||||
): Promise<string[]> => {
|
): Promise<string[]> => {
|
||||||
const done = plugins.smartpromise.defer<string[]>();
|
|
||||||
|
|
||||||
// handle absolute miniMatchFilter
|
// handle absolute miniMatchFilter
|
||||||
let dirPath: string;
|
let dirPath: string;
|
||||||
if (plugins.path.isAbsolute(miniMatchFilter)) {
|
if (plugins.path.isAbsolute(miniMatchFilter)) {
|
||||||
@ -349,15 +378,8 @@ export const listFileTree = async (
|
|||||||
nodir: true,
|
nodir: true,
|
||||||
dot: true,
|
dot: true,
|
||||||
};
|
};
|
||||||
plugins.glob(miniMatchFilter, options, (err, files: string[]) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
done.reject(err);
|
|
||||||
}
|
|
||||||
done.resolve(files);
|
|
||||||
});
|
|
||||||
|
|
||||||
let fileList = await done.promise;
|
let fileList = await plugins.glob.glob(miniMatchFilter, options);
|
||||||
if (absolutePathsBool) {
|
if (absolutePathsBool) {
|
||||||
fileList = fileList.map((filePath) => {
|
fileList = fileList.map((filePath) => {
|
||||||
return plugins.path.resolve(plugins.path.join(dirPath, filePath));
|
return plugins.path.resolve(plugins.path.join(dirPath, filePath));
|
||||||
@ -366,3 +388,104 @@ export const listFileTree = async (
|
|||||||
|
|
||||||
return fileList;
|
return fileList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watches for file stability before resolving the promise.
|
||||||
|
*/
|
||||||
|
export const waitForFileToBeReady = (filePathArg: string): Promise<void> => {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
let lastFileSize = -1;
|
||||||
|
let fileIsStable = false;
|
||||||
|
|
||||||
|
const checkFileStability = async () => {
|
||||||
|
let currentFileSize: number;
|
||||||
|
const deferred = plugins.smartpromise.defer();
|
||||||
|
plugins.fs.stat(filePathArg, (err, stats) => {
|
||||||
|
if (err) {
|
||||||
|
fileIsStable = true;
|
||||||
|
watcher.close();
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentFileSize = stats.size;
|
||||||
|
deferred.resolve();
|
||||||
|
});
|
||||||
|
await deferred.promise;
|
||||||
|
if (currentFileSize === lastFileSize) {
|
||||||
|
fileIsStable = true;
|
||||||
|
await plugins.smartdelay.delayFor(100);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
lastFileSize = currentFileSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
const watcher = plugins.fs.watch(filePathArg, (eventType, filename) => {
|
||||||
|
if (eventType === 'change') {
|
||||||
|
checkFileStability();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watcher.on('error', (error) => {
|
||||||
|
watcher.close();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
while (!fileIsStable) {
|
||||||
|
await checkFileStability();
|
||||||
|
if (!fileIsStable) {
|
||||||
|
await plugins.smartdelay.delayFor(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watcher.close();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes string or Smartfile to disk.
|
||||||
|
* @param fileArg
|
||||||
|
* @param fileNameArg
|
||||||
|
* @param fileBaseArg
|
||||||
|
*/
|
||||||
|
export let toFs = async (
|
||||||
|
fileContentArg: string | Buffer | SmartFile | StreamFile,
|
||||||
|
filePathArg: string,
|
||||||
|
optionsArg: {
|
||||||
|
respectRelative?: boolean;
|
||||||
|
} = {}
|
||||||
|
) => {
|
||||||
|
const done = plugins.smartpromise.defer();
|
||||||
|
|
||||||
|
// check args
|
||||||
|
if (!fileContentArg || !filePathArg) {
|
||||||
|
throw new Error('expected valid arguments');
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare actual write action
|
||||||
|
let fileContent: string | Buffer;
|
||||||
|
let fileEncoding: 'utf8' | 'binary' = 'utf8';
|
||||||
|
let filePath: string = filePathArg;
|
||||||
|
|
||||||
|
// handle Smartfile
|
||||||
|
if (fileContentArg instanceof SmartFile) {
|
||||||
|
fileContent = fileContentArg.contentBuffer;
|
||||||
|
// handle options
|
||||||
|
if (optionsArg.respectRelative) {
|
||||||
|
filePath = plugins.path.join(filePath, fileContentArg.path);
|
||||||
|
}
|
||||||
|
} else if (Buffer.isBuffer(fileContentArg)) {
|
||||||
|
fileContent = fileContentArg;
|
||||||
|
fileEncoding = 'binary';
|
||||||
|
} else if (typeof fileContentArg === 'string') {
|
||||||
|
fileContent = fileContentArg;
|
||||||
|
} else {
|
||||||
|
throw new Error('fileContent is neither string nor Smartfile');
|
||||||
|
}
|
||||||
|
await ensureDir(plugins.path.parse(filePath).dir);
|
||||||
|
plugins.fsExtra.writeFile(filePath, fileContent, { encoding: fileEncoding }, done.resolve);
|
||||||
|
return await done.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
195
ts/fsstream.ts
Normal file
195
ts/fsstream.ts
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
This file contains logic for streaming things from and to the filesystem
|
||||||
|
*/
|
||||||
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
|
export const createReadStream = (pathArg: string) => {
|
||||||
|
return plugins.fs.createReadStream(pathArg);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createWriteStream = (pathArg: string) => {
|
||||||
|
return plugins.fs.createWriteStream(pathArg);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const processFile = async (
|
||||||
|
filePath: string,
|
||||||
|
asyncFunc: (fileStream: plugins.stream.Readable) => Promise<void>
|
||||||
|
): Promise<void> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const fileStream = createReadStream(filePath);
|
||||||
|
asyncFunc(fileStream).then(resolve).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const processDirectory = async (
|
||||||
|
directoryPath: string,
|
||||||
|
asyncFunc: (fileStream: plugins.stream.Readable) => Promise<void>
|
||||||
|
): Promise<void> => {
|
||||||
|
const files = plugins.fs.readdirSync(directoryPath, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const fullPath = plugins.path.join(directoryPath, file.name);
|
||||||
|
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
await processDirectory(fullPath, asyncFunc); // Recursively call processDirectory for directories
|
||||||
|
} else if (file.isFile()) {
|
||||||
|
await processFile(fullPath, asyncFunc); // Call async function with the file stream and wait for it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a file is ready to be streamed (exists and is not empty).
|
||||||
|
*/
|
||||||
|
export const isFileReadyForStreaming = async (filePathArg: string): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
const stats = await plugins.fs.promises.stat(filePathArg);
|
||||||
|
return stats.size > 0;
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'ENOENT') { // File does not exist
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw error; // Rethrow other unexpected errors
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for a file to be ready for streaming (exists and is not empty).
|
||||||
|
*/
|
||||||
|
export const waitForFileToBeReadyForStreaming = (filePathArg: string): Promise<void> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Normalize and resolve the file path
|
||||||
|
const filePath = plugins.path.resolve(filePathArg);
|
||||||
|
|
||||||
|
// Function to check file stats
|
||||||
|
const checkFile = (resolve: () => void, reject: (reason: any) => void) => {
|
||||||
|
plugins.fs.stat(filePath, (err, stats) => {
|
||||||
|
if (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
// File not found, wait and try again
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Some other error occurred
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
if (stats.size > 0) {
|
||||||
|
// File exists and is not empty, resolve the promise
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set up file watcher
|
||||||
|
const watcher = plugins.fs.watch(filePath, { persistent: false }, (eventType) => {
|
||||||
|
if (eventType === 'change' || eventType === 'rename') {
|
||||||
|
checkFile(resolve, reject);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check file immediately in case it's already ready
|
||||||
|
checkFile(resolve, reject);
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
watcher.on('error', (error) => {
|
||||||
|
watcher.close();
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export class SmartReadStream extends plugins.stream.Readable {
|
||||||
|
private watcher: plugins.fs.FSWatcher | null = null;
|
||||||
|
private lastReadSize: number = 0;
|
||||||
|
private endTimeout: NodeJS.Timeout | null = null;
|
||||||
|
private filePath: string;
|
||||||
|
private endDelay: number;
|
||||||
|
private reading: boolean = false;
|
||||||
|
|
||||||
|
constructor(filePath: string, endDelay = 60000, opts?: plugins.stream.ReadableOptions) {
|
||||||
|
super(opts);
|
||||||
|
this.filePath = filePath;
|
||||||
|
this.endDelay = endDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
private startWatching(): void {
|
||||||
|
this.watcher = plugins.fs.watch(this.filePath, (eventType) => {
|
||||||
|
if (eventType === 'change') {
|
||||||
|
this.resetEndTimeout();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.watcher.on('error', (error) => {
|
||||||
|
this.cleanup();
|
||||||
|
this.emit('error', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private resetEndTimeout(): void {
|
||||||
|
if (this.endTimeout) clearTimeout(this.endTimeout);
|
||||||
|
this.endTimeout = setTimeout(() => this.checkForEnd(), this.endDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkForEnd(): void {
|
||||||
|
plugins.fs.stat(this.filePath, (err, stats) => {
|
||||||
|
if (err) {
|
||||||
|
this.emit('error', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.lastReadSize === stats.size) {
|
||||||
|
this.push(null); // Signal the end of the stream
|
||||||
|
this.cleanup();
|
||||||
|
} else {
|
||||||
|
this.lastReadSize = stats.size;
|
||||||
|
this.resetEndTimeout();
|
||||||
|
if (!this.reading) {
|
||||||
|
// We only want to continue reading if we were previously waiting for more data
|
||||||
|
this.reading = true;
|
||||||
|
this._read(10000); // Try to read more data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private cleanup(): void {
|
||||||
|
if (this.endTimeout) clearTimeout(this.endTimeout);
|
||||||
|
if (this.watcher) this.watcher.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
_read(size: number): void {
|
||||||
|
this.reading = true;
|
||||||
|
const chunkSize = Math.min(size, 16384); // Read in chunks of 16KB
|
||||||
|
const buffer = Buffer.alloc(chunkSize);
|
||||||
|
plugins.fs.open(this.filePath, 'r', (err, fd) => {
|
||||||
|
if (err) {
|
||||||
|
this.emit('error', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
plugins.fs.read(fd, buffer, 0, chunkSize, this.lastReadSize, (err, bytesRead, buffer) => {
|
||||||
|
if (err) {
|
||||||
|
this.emit('error', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesRead > 0) {
|
||||||
|
this.lastReadSize += bytesRead;
|
||||||
|
this.push(buffer.slice(0, bytesRead)); // Push the data onto the stream
|
||||||
|
} else {
|
||||||
|
this.reading = false; // No more data to read for now
|
||||||
|
this.resetEndTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.fs.close(fd, (err) => {
|
||||||
|
if (err) {
|
||||||
|
this.emit('error', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_destroy(error: Error | null, callback: (error: Error | null) => void): void {
|
||||||
|
this.cleanup();
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
}
|
22
ts/index.ts
22
ts/index.ts
@ -1,12 +1,14 @@
|
|||||||
import * as plugins from './smartfile.plugins';
|
import * as plugins from './plugins.js';
|
||||||
import * as SmartfileFs from './smartfile.fs';
|
import * as fsMod from './fs.js';
|
||||||
import * as SmartfileInterpreter from './smartfile.interpreter';
|
import * as fsStreamMod from './fsstream.js';
|
||||||
import * as SmartfileMemory from './smartfile.memory';
|
import * as interpreterMod from './interpreter.js';
|
||||||
import * as SmartfileRemote from './smartfile.remote';
|
import * as memoryMod from './memory.js';
|
||||||
|
|
||||||
export { Smartfile } from './smartfile.classes.smartfile';
|
export * from './classes.smartfile.js';
|
||||||
|
export * from './classes.streamfile.js';
|
||||||
|
export * from './classes.virtualdirectory.js';
|
||||||
|
|
||||||
export let fs = SmartfileFs;
|
export const fs = fsMod;
|
||||||
export let interpreter = SmartfileInterpreter;
|
export const fsStream = fsStreamMod;
|
||||||
export let memory = SmartfileMemory;
|
export const interpreter = interpreterMod;
|
||||||
export let remote = SmartfileRemote;
|
export const memory = memoryMod;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import plugins = require('./smartfile.plugins');
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
export let filetype = (pathArg: string): string => {
|
export let filetype = (pathArg: string): string => {
|
||||||
const extName = plugins.path.extname(pathArg);
|
const extName = plugins.path.extname(pathArg);
|
||||||
@ -10,7 +10,7 @@ export let objectFile = (fileStringArg: string, fileTypeArg) => {
|
|||||||
switch (fileTypeArg) {
|
switch (fileTypeArg) {
|
||||||
case 'yml':
|
case 'yml':
|
||||||
case 'yaml':
|
case 'yaml':
|
||||||
return plugins.yaml.safeLoad(fileStringArg);
|
return plugins.yaml.load(fileStringArg);
|
||||||
case 'json':
|
case 'json':
|
||||||
return JSON.parse(fileStringArg);
|
return JSON.parse(fileStringArg);
|
||||||
default:
|
default:
|
@ -1,8 +1,8 @@
|
|||||||
import plugins = require('./smartfile.plugins');
|
import * as plugins from './plugins.js';
|
||||||
import { Smartfile } from './smartfile.classes.smartfile';
|
import { SmartFile } from './classes.smartfile.js';
|
||||||
import * as smartfileFs from './smartfile.fs';
|
import * as smartfileFs from './fs.js';
|
||||||
|
import * as interpreter from './interpreter.js';
|
||||||
import SmartfileInterpreter = require('./smartfile.interpreter');
|
import type { StreamFile } from './classes.streamfile.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* converts file to Object
|
* converts file to Object
|
||||||
@ -10,8 +10,8 @@ import SmartfileInterpreter = require('./smartfile.interpreter');
|
|||||||
* @param fileTypeArg
|
* @param fileTypeArg
|
||||||
* @returns {any|any}
|
* @returns {any|any}
|
||||||
*/
|
*/
|
||||||
export let toObject = function (fileStringArg: string, fileTypeArg: string) {
|
export let toObject = (fileStringArg: string, fileTypeArg: string) => {
|
||||||
return SmartfileInterpreter.objectFile(fileStringArg, fileTypeArg);
|
return interpreter.objectFile(fileStringArg, fileTypeArg);
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IToFsOptions {
|
export interface IToFsOptions {
|
||||||
@ -25,7 +25,7 @@ export interface IToFsOptions {
|
|||||||
* @param fileBaseArg
|
* @param fileBaseArg
|
||||||
*/
|
*/
|
||||||
export let toFs = async (
|
export let toFs = async (
|
||||||
fileContentArg: string | Buffer | Smartfile,
|
fileContentArg: string | Buffer | SmartFile | StreamFile,
|
||||||
filePathArg: string,
|
filePathArg: string,
|
||||||
optionsArg: IToFsOptions = {}
|
optionsArg: IToFsOptions = {}
|
||||||
) => {
|
) => {
|
||||||
@ -42,7 +42,7 @@ export let toFs = async (
|
|||||||
let filePath: string = filePathArg;
|
let filePath: string = filePathArg;
|
||||||
|
|
||||||
// handle Smartfile
|
// handle Smartfile
|
||||||
if (fileContentArg instanceof Smartfile) {
|
if (fileContentArg instanceof SmartFile) {
|
||||||
fileContent = fileContentArg.contentBuffer;
|
fileContent = fileContentArg.contentBuffer;
|
||||||
// handle options
|
// handle options
|
||||||
if (optionsArg.respectRelative) {
|
if (optionsArg.respectRelative) {
|
||||||
@ -84,7 +84,7 @@ export const toFsSync = (fileArg: string, filePathArg: string) => {
|
|||||||
plugins.fsExtra.writeFileSync(filePath, fileString, { encoding: 'utf8' });
|
plugins.fsExtra.writeFileSync(filePath, fileString, { encoding: 'utf8' });
|
||||||
};
|
};
|
||||||
|
|
||||||
export let smartfileArrayToFs = async (smartfileArrayArg: Smartfile[], dirArg: string) => {
|
export let smartfileArrayToFs = async (smartfileArrayArg: SmartFile[], dirArg: string) => {
|
||||||
await smartfileFs.ensureDir(dirArg);
|
await smartfileFs.ensureDir(dirArg);
|
||||||
for (const smartfile of smartfileArrayArg) {
|
for (const smartfile of smartfileArrayArg) {
|
||||||
await toFs(smartfile, dirArg, {
|
await toFs(smartfile, dirArg, {
|
38
ts/plugins.ts
Normal file
38
ts/plugins.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// node native scope
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as stream from 'stream';
|
||||||
|
|
||||||
|
export { fs, path, stream };
|
||||||
|
|
||||||
|
// @pushrocks scope
|
||||||
|
import * as lik from '@push.rocks/lik';
|
||||||
|
import * as smartfileInterfaces from '@push.rocks/smartfile-interfaces';
|
||||||
|
import * as smartdelay from '@push.rocks/smartdelay';
|
||||||
|
import * as smarthash from '@push.rocks/smarthash';
|
||||||
|
import * as smartjson from '@push.rocks/smartjson';
|
||||||
|
import * as smartmime from '@push.rocks/smartmime';
|
||||||
|
import * as smartpath from '@push.rocks/smartpath';
|
||||||
|
import * as smartpromise from '@push.rocks/smartpromise';
|
||||||
|
import * as smartrequest from '@push.rocks/smartrequest';
|
||||||
|
import * as smartstream from '@push.rocks/smartstream';
|
||||||
|
|
||||||
|
export {
|
||||||
|
lik,
|
||||||
|
smartfileInterfaces,
|
||||||
|
smartdelay,
|
||||||
|
smarthash,
|
||||||
|
smartjson,
|
||||||
|
smartmime,
|
||||||
|
smartpath,
|
||||||
|
smartpromise,
|
||||||
|
smartrequest,
|
||||||
|
smartstream,
|
||||||
|
};
|
||||||
|
|
||||||
|
// third party scope
|
||||||
|
import fsExtra from 'fs-extra';
|
||||||
|
import * as glob from 'glob';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
|
export { fsExtra, glob, yaml };
|
@ -1,188 +0,0 @@
|
|||||||
import * as plugins from './smartfile.plugins';
|
|
||||||
import * as fs from './smartfile.fs';
|
|
||||||
import * as memory from './smartfile.memory';
|
|
||||||
|
|
||||||
export interface ISmartfileConstructorOptions {
|
|
||||||
path?: string;
|
|
||||||
contentBuffer?: Buffer;
|
|
||||||
base?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* class Smartfile
|
|
||||||
* -> is vinyl file compatible
|
|
||||||
*/
|
|
||||||
export class Smartfile {
|
|
||||||
// ======
|
|
||||||
// STATIC
|
|
||||||
// ======
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates a Smartfile from a filePath
|
|
||||||
* @param filePath
|
|
||||||
*/
|
|
||||||
public static async fromFilePath(filePath: string) {
|
|
||||||
filePath = plugins.path.resolve(filePath);
|
|
||||||
const fileBuffer = fs.toBufferSync(filePath);
|
|
||||||
const smartfile = new Smartfile({
|
|
||||||
path: filePath,
|
|
||||||
contentBuffer: fileBuffer,
|
|
||||||
});
|
|
||||||
return smartfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async fromBuffer(filePath: string, contentBufferArg: Buffer) {
|
|
||||||
const smartfile = new Smartfile({
|
|
||||||
contentBuffer: contentBufferArg,
|
|
||||||
path: filePath,
|
|
||||||
});
|
|
||||||
|
|
||||||
return smartfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async fromString(
|
|
||||||
filePath: string,
|
|
||||||
contentStringArg: string,
|
|
||||||
encodingArg: 'utf8' | 'binary'
|
|
||||||
) {
|
|
||||||
const smartfile = new Smartfile({
|
|
||||||
contentBuffer: Buffer.from(contentStringArg, encodingArg),
|
|
||||||
path: filePath,
|
|
||||||
});
|
|
||||||
|
|
||||||
return smartfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========
|
|
||||||
// INSTANCE
|
|
||||||
// ========
|
|
||||||
/**
|
|
||||||
* the full path of the file on disk
|
|
||||||
*/
|
|
||||||
public path: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public parsedPath: plugins.path.ParsedPath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the content of the file as Buffer
|
|
||||||
*/
|
|
||||||
public contentBuffer: Buffer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current working directory of the file
|
|
||||||
* Note:this is similar to gulp and different from native node path base
|
|
||||||
*/
|
|
||||||
public base: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sync the file with disk
|
|
||||||
*/
|
|
||||||
public sync: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the constructor of Smartfile
|
|
||||||
* @param optionsArg
|
|
||||||
*/
|
|
||||||
|
|
||||||
constructor(optionsArg: ISmartfileConstructorOptions) {
|
|
||||||
if (optionsArg.contentBuffer) {
|
|
||||||
this.contentBuffer = optionsArg.contentBuffer;
|
|
||||||
} else {
|
|
||||||
console.log('created empty Smartfile?');
|
|
||||||
}
|
|
||||||
this.path = optionsArg.path;
|
|
||||||
this.parsedPath = plugins.path.parse(this.path);
|
|
||||||
this.base = optionsArg.base;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set contents from string
|
|
||||||
* @param contentString
|
|
||||||
*/
|
|
||||||
public setContentsFromString(contentString: string, encodingArg: 'utf8' | 'binary' = 'utf8') {
|
|
||||||
this.contents = new Buffer(contentString, encodingArg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* write file to disk
|
|
||||||
* Behaviours:
|
|
||||||
* - no argument write to exactly where the file was picked up
|
|
||||||
*/
|
|
||||||
public async write(pathArg?: string) {
|
|
||||||
const stringToWrite = this.contentBuffer.toString();
|
|
||||||
await memory.toFs(stringToWrite, this.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* read file from disk
|
|
||||||
*/
|
|
||||||
public async read() {}
|
|
||||||
|
|
||||||
// -----------------------------------------------
|
|
||||||
// vinyl compatibility
|
|
||||||
// -----------------------------------------------
|
|
||||||
/**
|
|
||||||
* vinyl-compatibility: alias of this.contentBuffer
|
|
||||||
*/
|
|
||||||
get contents(): Buffer {
|
|
||||||
return this.contentBuffer;
|
|
||||||
}
|
|
||||||
set contents(contentsArg) {
|
|
||||||
this.contentBuffer = contentsArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* vinyl-compatibility
|
|
||||||
*/
|
|
||||||
public get cwd() {
|
|
||||||
return process.cwd();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return relative path of file
|
|
||||||
*/
|
|
||||||
public get relative(): string {
|
|
||||||
return plugins.path.relative(this.base, this.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return truw when the file has content
|
|
||||||
*/
|
|
||||||
public isNull(): boolean {
|
|
||||||
if (!this.contentBuffer) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return true if contents are Buffer
|
|
||||||
*/
|
|
||||||
public isBuffer(): boolean {
|
|
||||||
if (this.contents instanceof Buffer) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isDirectory() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isStream() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isSymbolic() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update things
|
|
||||||
public updateFileName(fileNameArg: string) {
|
|
||||||
const oldFileName = this.parsedPath.base;
|
|
||||||
this.path = this.path.replace(new RegExp(oldFileName + '$'), fileNameArg);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import { Smartfile } from './smartfile.classes.smartfile';
|
|
||||||
import * as plugins from './smartfile.plugins';
|
|
||||||
import * as fs from './smartfile.fs';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* a virtual directory exposes a fs api
|
|
||||||
*/
|
|
||||||
export class VirtualDirectory {
|
|
||||||
private fileArray: Smartfile[] = [];
|
|
||||||
public static async fromFsDirPath(pathArg: string) {
|
|
||||||
const newVirtualDir = new VirtualDirectory();
|
|
||||||
newVirtualDir.addSmartfiles(await fs.fileTreeToObject(pathArg, '**/*'));
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
public addSmartfiles(smartfileArrayArg: Smartfile[]) {
|
|
||||||
this.fileArray = this.fileArray.concat(smartfileArrayArg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getFileByPath(pathArg: string) {
|
|
||||||
for (const smartfile of this.fileArray) {
|
|
||||||
if (smartfile.path === pathArg) {
|
|
||||||
return smartfile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO implement root shifting to get subdirectories as new virtual directories
|
|
||||||
// TODO implement root shifting to combine VirtualDirecotries in a parent virtual directory
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
// node native scope
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
export { fs, path };
|
|
||||||
|
|
||||||
// @pushrocks scope
|
|
||||||
import * as smarthash from '@pushrocks/smarthash';
|
|
||||||
import * as smartmime from '@pushrocks/smartmime';
|
|
||||||
import * as smartpath from '@pushrocks/smartpath';
|
|
||||||
import * as smartpromise from '@pushrocks/smartpromise';
|
|
||||||
import * as smartrequest from '@pushrocks/smartrequest';
|
|
||||||
|
|
||||||
export { smarthash, smartmime, smartpath, smartpromise, smartrequest };
|
|
||||||
|
|
||||||
// third party scope
|
|
||||||
import * as fsExtra from 'fs-extra';
|
|
||||||
import glob from 'glob';
|
|
||||||
import yaml from 'js-yaml';
|
|
||||||
|
|
||||||
export { fsExtra, glob, yaml };
|
|
@ -1,51 +0,0 @@
|
|||||||
import plugins = require('./smartfile.plugins');
|
|
||||||
import SmartfileInterpreter = require('./smartfile.interpreter');
|
|
||||||
|
|
||||||
/* export let toFs = function (from: string, toPath: string) {
|
|
||||||
let done = plugins.q.defer()
|
|
||||||
let stream = plugins.smartrequest(from).pipe(plugins.fsExtra.createWriteStream(toPath))
|
|
||||||
stream.on('finish', function () {
|
|
||||||
done.resolve(toPath)
|
|
||||||
})
|
|
||||||
return done.promise
|
|
||||||
} */
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param fromArg
|
|
||||||
* @returns {any}
|
|
||||||
*/
|
|
||||||
export let toObject = (fromArg: string) => {
|
|
||||||
const done = plugins.smartpromise.defer();
|
|
||||||
plugins.smartrequest
|
|
||||||
.request(fromArg, {
|
|
||||||
method: 'get',
|
|
||||||
})
|
|
||||||
.then((res: any) => {
|
|
||||||
if (res.statusCode === 200) {
|
|
||||||
done.resolve(res.body);
|
|
||||||
} else {
|
|
||||||
console.log('could not get remote file from ' + fromArg);
|
|
||||||
done.reject(new Error('could not get remote file from ' + fromArg));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return done.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param fromArg
|
|
||||||
* @returns {any}
|
|
||||||
*/
|
|
||||||
export let toString = (fromArg: string): Promise<string> => {
|
|
||||||
const done = plugins.smartpromise.defer<string>();
|
|
||||||
plugins.smartrequest.getBinary(fromArg).then((res: any) => {
|
|
||||||
if (res.statusCode === 200) {
|
|
||||||
const encoding = plugins.smartmime.getEncoding(fromArg);
|
|
||||||
done.resolve(res.body.toString(encoding));
|
|
||||||
} else {
|
|
||||||
done.reject(new Error('could not get remote file from ' + fromArg));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return done.promise;
|
|
||||||
};
|
|
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"verbatimModuleSyntax": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"dist_*/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
17
tslint.json
17
tslint.json
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["tslint:latest", "tslint-config-prettier"],
|
|
||||||
"rules": {
|
|
||||||
"semicolon": [true, "always"],
|
|
||||||
"no-console": false,
|
|
||||||
"ordered-imports": false,
|
|
||||||
"object-literal-sort-keys": false,
|
|
||||||
"member-ordering": {
|
|
||||||
"options":{
|
|
||||||
"order": [
|
|
||||||
"static-method"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultSeverity": "warning"
|
|
||||||
}
|
|
Reference in New Issue
Block a user