@impactor/nodejs 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Dibo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ Nodejs utils
2
+
3
+ # FS operations
4
+
5
+ Working with files, directories, and paths.
6
+ all functions are available as promise or sync, for example:
7
+
8
+ - `read()` -> the promise version
9
+ - `readSync()` -> the sync version
10
+
11
+ The following are just a choosen examples, to see the full API check our docs.
12
+
13
+ ### Reading a JSON file
14
+
15
+ ```ts
16
+ import { read } from "@impactor/nodejs-utils";
17
+
18
+ let file = await read("./package.json");
19
+ ```
20
+
21
+ The content automatically pared as JSON.
22
+
23
+ ### Reading a cache file if only it is not expired
24
+
25
+ ```ts
26
+ import { read } from "@impactor/nodejs-utils";
27
+
28
+ let cache = await read("./cache.json", { maxAge: 10000 });
29
+ ```
30
+
31
+ ### Writing a JSON data
32
+
33
+ ```ts
34
+ import { write } from "@impactor/nodejs-utils";
35
+
36
+ write("./data/reports/report.json", { status: "ok" });
37
+ ```
38
+
39
+ The data will be stringified automatically.
40
+ also, you don't need to manually create the directory, because it is created recursively.
41
+ for instance if `./data` or `./data/reports` doesn't exist, it'll be created.
42
+
43
+ ### copy or move a file or folder, you can filter the files to be copied.
44
+
45
+ ```ts
46
+ // copy static non-typescript files
47
+ copy("src", "dist/assets", (file) => !file.endsWith(".ts"));
48
+
49
+ // move flat-structure project to use "src"
50
+ move(".", "./src", (file) => file.endsWith(".ts"));
51
+
52
+ // remove log files
53
+ remove("./data", (file) => file.endsWith(".log"));
54
+ ```
55
+
56
+ ## Parsing a path
57
+
58
+ ```ts
59
+ let info = parsePath("./data/report.txt");
60
+
61
+ console.log(`type: ${info.type}`); // file or dir
62
+ console.log(`file extension: ${info.extension}`); // txt
63
+ console.log(`the containing dir: ${info.dir}`); // data
64
+ console.log(`file name: ${info.name}`); // report
65
+ ```
66
+
67
+ ### Get the total size of a file or directory
68
+
69
+ ```ts
70
+ getSize("./data");
71
+ ```
72
+
73
+ ### Create the full path directory recursively
74
+
75
+ ```ts
76
+ mkdir("./data/reports/summaries");
77
+ ```
78
+
79
+ if one portion of the specified path doesn't exist, the full path will be created.
80
+ for example, if `./data` doesn't exist, it'll be created first, then `reports` then `summaries`.
81
+ It also accepts files, the containing directory will be created.
82
+
83
+ ## get all or some files or directory.
84
+
85
+ ```ts
86
+ // list first-level directories under "apps"
87
+ getEntries("apps", "dirs", 1);
88
+
89
+ // list all files under "reports" and its children
90
+ getEntries("reports", "files");
91
+
92
+ // list all JSON files under reports
93
+ getEntries("reports", "files", (entry) => entry.endsWith(".json"));
94
+
95
+ // list all summary log files under "reports"
96
+ // i.e. files that follow this pattern "summary-{number}.log"
97
+ getEntries("reports", "files", /summary-\d\.log/);
98
+ ```
99
+
100
+ ### Apply an arbitrary action recursively
101
+
102
+ ```ts
103
+ // remove all "*.log" files under "reports" and its subdirectories, recursively.
104
+ recursive(
105
+ "reports", // the starting point
106
+ (entry) => unlinkSync(entry), // the action to be applied
107
+ (entry) => entry.endsWith(".log"), // the filter
108
+ );
109
+ ```
110
+
111
+ # Cache
112
+
113
+ Manage caches.
114
+ built on top of [@impactor/cache](https://www.npmjs.com/package/@impactor/cache), using file-system caching.
115
+
116
+ ```ts
117
+ let cache = await cacheFS(
118
+ // cache entries
119
+ ["./report-today.json", "./report-yesterday.json"],
120
+ // data source
121
+ () => fetch("https://my-server.com/getReport"),
122
+ // cache options
123
+ {
124
+ maxAge: 1000,
125
+ maxStale: 2000,
126
+ // you don't need to define `read()` and `write()` here.
127
+ },
128
+ );
129
+ ```
package/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './src/cache-fs';
2
+ export * from './src/fs';
3
+ export * from './src/fs-sync';
4
+ export * from './src/https';
5
+ export * from './src/process';
6
+ export { default as cacheFs } from './src/cache-fs';
7
+ export { default as https } from './src/https';
package/index.js ADDED
@@ -0,0 +1,11 @@
1
+ export * from "./src/cache-fs";
2
+ export * from "./src/fs";
3
+ export * from "./src/fs-sync";
4
+ export * from "./src/https";
5
+ export * from "./src/process";
6
+ import { default as default2 } from "./src/cache-fs";
7
+ import { default as default3 } from "./src/https";
8
+ export {
9
+ default2 as cacheFs,
10
+ default3 as https
11
+ };
package/nx.json ADDED
@@ -0,0 +1,190 @@
1
+ {
2
+ "$schema": "./node_modules/nx/schemas/nx-schema.json",
3
+ "targetDefaults": {
4
+ "build": {
5
+ "executor": "@impactor/nx-manager:universal-builder",
6
+ "dependsOn": ["typecheck", "prebuild", "^build"],
7
+ "cache": true,
8
+ "inputs": ["default", "^default"],
9
+ "outputs": ["{projectRoot}/dist"],
10
+ "options": {
11
+ "forceRnDefaultPostBuild": true
12
+ }
13
+ },
14
+ "serve": {
15
+ "executor": "nx:run-commands",
16
+ "dependsOn": ["build", "preserve"],
17
+ "continuous": true,
18
+ "options": {
19
+ "cwd": "{projectRoot}",
20
+ "command": "NODE_ENV=development pnpm tsx --env-file-if-exists=.env --env-file-if-exists=.env.local dist/main.js"
21
+ },
22
+ "configurations": {
23
+ "production": {
24
+ "command": "NODE_ENV=production pnpm tsx --env-file-if-exists=.env --env-file-if-exists=.env.local --env-file-if-exists=.env.production --env-file-if-exists=.env.production.local dist/main.js"
25
+ }
26
+ }
27
+ },
28
+ "nx-release-publish": {
29
+ "options": {
30
+ "packageRoot": "{projectRoot}/dist"
31
+ }
32
+ },
33
+ "semantic-release": {
34
+ "executor": "nx:run-commands",
35
+ "dependsOn": ["build", "prerelease"],
36
+ "options": {
37
+ "cwd": "{projectRoot}",
38
+ "command": "pnpm tsx node_modules/semantic-release/bin/semantic-release.js"
39
+ },
40
+ "configurations": {
41
+ "local": {
42
+ "command": "DOTENV_CONFIG_PATH=../../.env pnpm run release --no-ci"
43
+ },
44
+ "dry": {
45
+ "command": "DOTENV_CONFIG_PATH=../../.env pnpm run release --no-ci --dry-run"
46
+ }
47
+ }
48
+ },
49
+ "test": {
50
+ "inputs": ["test"],
51
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
52
+ "dependsOn": ["^build", "pretest"],
53
+ "configurations": {
54
+ "unit": {
55
+ "args": [
56
+ "--testPathIgnorePatterns '.*\\.e2e\\.spec\\.ts$'",
57
+ "--testPathIgnorePatterns '/.*-e2e/'",
58
+ "--testPathIgnorePatterns '/.*-e2e/'"
59
+ ]
60
+ },
61
+ "e2e": {
62
+ "args": [
63
+ "--testMatch '**/*-e2e/**/*.{spec,test}.[mc]?[jt]s?(x)'",
64
+ "--testMatch '**/e2e/**/*.{spec,test}.[mc]?[jt]s?(x)'",
65
+ "--testMatch '**/*.e2e.(spec|test).?([mc])[jt]s?(x)'"
66
+ ]
67
+ }
68
+ }
69
+ },
70
+ "@angular/build:application": {
71
+ "cache": true,
72
+ "dependsOn": ["prebuild", "^build"],
73
+ "inputs": ["default", "^default"]
74
+ },
75
+ "@nx/angular:package": {
76
+ "cache": true,
77
+ "dependsOn": ["^build"],
78
+ "inputs": ["default", "^default"],
79
+ "outputs": ["{projectRoot}/dist"],
80
+ "options": {
81
+ "project": "{projectRoot}/ng-package.json",
82
+ "tsConfig": "{projectRoot}/tsconfig.lib.json"
83
+ }
84
+ },
85
+ "@nx/js:tsc": {
86
+ "cache": true,
87
+ "dependsOn": ["^build"],
88
+ "inputs": ["default", "^default"]
89
+ }
90
+ },
91
+ "namedInputs": {
92
+ "default": [
93
+ "{projectRoot}/src/**/*",
94
+ "!{projectRoot}/**/*.(spec|test)(.e2e)?.[tj]sx?",
95
+ "!{projectRoot}/**/e2e/**/*",
96
+ "{projectRoot}/.env(.*)?",
97
+ "{workspaceRoot}/tasks/(post-)?build.ts",
98
+ "{projectRoot}/(project|package|tsconfig)(.+)?.json",
99
+ "{workspaceRoot}/tsconfig.base.json",
100
+ {
101
+ "runtime": "node --version"
102
+ },
103
+ {
104
+ "env": "NODE_ENV"
105
+ }
106
+ ],
107
+ "test": [
108
+ "default",
109
+ "{projectRoot}/tsconfig.spec.json",
110
+ "{projectRoot}/jest.config.[jt]s",
111
+ "{workspaceRoot}/jest.config.[jt]s",
112
+ "{projectRoot}/**/*.(spec|test)(.e2e)?.[jt]sx?",
113
+ "{projectRoot}/**/e2e/**/*.[jt]sx?",
114
+ {
115
+ "externalDependencies": ["jest"]
116
+ }
117
+ ]
118
+ },
119
+ "plugins": [
120
+ {
121
+ "plugin": "@nx/js/typescript",
122
+ "options": {
123
+ "typecheck": {
124
+ "targetName": "typecheck"
125
+ },
126
+ "build": false
127
+ }
128
+ },
129
+ {
130
+ "plugin": "@nx/jest/plugin",
131
+ "options": {
132
+ "targetName": "test",
133
+ "ciTargetName": "test-ci"
134
+ }
135
+ }
136
+ ],
137
+ "tasksRunnerOptions": {
138
+ "default": {
139
+ "options": {
140
+ "useDaemonProcess": false
141
+ }
142
+ }
143
+ },
144
+ "generators": {
145
+ "@nx/angular:library": {
146
+ "linter": "eslint",
147
+ "unitTestRunner": "jest"
148
+ },
149
+ "@nx/angular:component": {
150
+ "style": "scss"
151
+ }
152
+ },
153
+ "release": {
154
+ "groups": {
155
+ "libs": {
156
+ "projects": ["libs/*"]
157
+ },
158
+ "apps": {
159
+ "projects": ["apps/*"],
160
+ "changelog": {
161
+ "projectChangelogs": {
162
+ "createRelease": "github"
163
+ }
164
+ }
165
+ }
166
+ },
167
+ "projectsRelationship": "independent",
168
+ "version": {
169
+ "conventionalCommits": true,
170
+ "updateDependents": "always",
171
+ "preVersionCommand": "pnpm nx run-many -t build",
172
+ "manifestRootsToUpdate": [
173
+ "{projectRoot}",
174
+ {
175
+ "path": "{projectRoot}/dist",
176
+ "preserveLocalDependencyProtocols": false
177
+ }
178
+ ]
179
+ },
180
+ "releaseTag": {
181
+ "pattern": "{projectName}@{version}"
182
+ },
183
+ "changelog": {
184
+ "projectChangelogs": true
185
+ }
186
+ },
187
+ "sync": {
188
+ "applyChanges": false
189
+ }
190
+ }
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@impactor/nodejs",
3
+ "version": "3.0.1",
4
+ "type": "module",
5
+ "description": "nodejs utils",
6
+ "private": false,
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "keywords": [
11
+ "nodejs",
12
+ "javascript",
13
+ "js"
14
+ ],
15
+ "nx": {
16
+ "projectType": "library",
17
+ "targets": {
18
+ "build": {},
19
+ "semantic-release": {}
20
+ }
21
+ },
22
+ "imports": {
23
+ "#*": "./src/*.js"
24
+ },
25
+ "exports": {
26
+ ".": {
27
+ "types": "./index.d.js",
28
+ "default": "./index.js"
29
+ },
30
+ "./package.json": {
31
+ "default": "./package.json"
32
+ },
33
+ "./*": "./src/*"
34
+ },
35
+ "dependencies": {
36
+ "content-type": "^1.0.5",
37
+ "@impactor/cache": "3.0.1",
38
+ "@impactor/javascript": "3.0.1"
39
+ },
40
+ "devDependencies": {
41
+ "@types/content-type": "^1.1.9",
42
+ "semantic-release": "^25.0.2",
43
+ "semantic-release-monorepo": "^8.0.2",
44
+ "tsx": "^4.21.0"
45
+ },
46
+ "engines": {
47
+ "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
48
+ },
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/its-dibo/dibo.git"
52
+ },
53
+ "author": "Sherif Eldeeb <sh.eldeeb.2010+github@gmail.com> (https://github.com/its-dibo)",
54
+ "homepage": "https://github.com/its-dibo/dibo#readme",
55
+ "bugs": {
56
+ "url": "https://github.com/its-dibo/dibo/issues",
57
+ "email": "sh.eldeeb.2010+github@gmail.com"
58
+ },
59
+ "license": "MIT",
60
+ "contributors": [],
61
+ "funding": [
62
+ {
63
+ "type": "paypal",
64
+ "url": "https://paypal.me/group99001"
65
+ },
66
+ {
67
+ "type": "patreon",
68
+ "url": "https://www.patreon.com/GoogleDev"
69
+ }
70
+ ],
71
+ "scripts": {}
72
+ }
@@ -0,0 +1,8 @@
1
+ import { type CacheOptions } from '@impactor/cache';
2
+ import { type PathLike } from 'node:fs';
3
+ import { type ReadOptions } from './fs-sync';
4
+ export interface CacheOpts extends Omit<CacheOptions, 'read' | 'write'> {
5
+ }
6
+ export interface CacheFSOptions extends CacheOptions, ReadOptions {
7
+ }
8
+ export default function cacheFS(files: PathLike | PathLike[], dataSource?: () => unknown, options?: CacheOpts): Promise<unknown>;
@@ -0,0 +1,32 @@
1
+ import { cache } from "@impactor/cache";
2
+ import { readSync, resolve } from "./fs-sync";
3
+ import { write } from "./fs";
4
+ function getCache(entries, options) {
5
+ for (let filePath of entries) {
6
+ try {
7
+ let data = readSync(filePath, options);
8
+ if (data !== void 0) {
9
+ return data;
10
+ }
11
+ } catch {
12
+ }
13
+ }
14
+ throw "no valid cache found";
15
+ }
16
+ function setCache(entry, data, _options) {
17
+ return write(entry, data);
18
+ }
19
+ function cacheFS(files, dataSource, options) {
20
+ let opts = {
21
+ read: (entries, options2) => getCache(entries, options2),
22
+ write: (entry, data, options2) => setCache(entry, data, options2),
23
+ ...options
24
+ };
25
+ let cacheEntries = (Array.isArray(files) ? files : [files]).map(
26
+ (filePath) => resolve(filePath)
27
+ );
28
+ return cache(cacheEntries, dataSource, opts);
29
+ }
30
+ export {
31
+ cacheFS as default
32
+ };
@@ -0,0 +1,38 @@
1
+ import { afterAll, beforeEach, expect, test } from "@jest/globals";
2
+ import cache from "./cache-fs";
3
+ import { remove, write } from "./fs";
4
+ import { resolve } from "./fs-sync";
5
+ let dir = resolve(import.meta.dirname, "./test!!/cache");
6
+ beforeEach(() => remove(`${dir}/file.txt`));
7
+ afterAll(() => remove(dir));
8
+ test("create a new cache", () => cache(`${dir}/new-file.txt`, () => "content").then(
9
+ (value) => expect(value).toEqual("content")
10
+ ));
11
+ test("read from an existing cached file", () => write(`${dir}/file-uguihgkuhkgj.txt`, "content#1").then(() => cache(`${dir}/file-uguihgkuhkgj.txt`, () => "content#2")).then((value) => expect(value).toEqual("content#1")));
12
+ test("read from multiple cache paths, one of them exists - case1", () => write(`${dir}/multiple-case1/file.txt`, "content#1").then(
13
+ () => cache(
14
+ [
15
+ `${dir}/multiple-case1/file.txt`,
16
+ `${dir}/none1.txt`,
17
+ `${dir}/none2.txt`
18
+ ],
19
+ () => "content#3"
20
+ )
21
+ ).then((value) => expect(value).toEqual("content#1")));
22
+ test("read from multiple cache paths, one of them exists - case2", () => write(`${dir}/multiple-case2/file.txt`, "content#1").then(
23
+ () => cache(
24
+ [
25
+ `${dir}/none1.txt`,
26
+ `${dir}/multiple-case2/file.txt`,
27
+ `${dir}/none2.txt`
28
+ ],
29
+ () => "content#3"
30
+ )
31
+ ).then((value) => expect(value).toEqual("content#1")));
32
+ test("read from multiple cache paths, non exists", () => cache([`${dir}/none3.txt`, `${dir}/none4.txt`], () => "content").then(
33
+ (value) => expect(value).toEqual("content")
34
+ ));
35
+ test("read from .json file", () => write(`${dir}/file.json`, { x: 1 }).then(() => cache(`${dir}/file.json`, () => "nothing")).then((value) => expect(value).toEqual({ x: 1 })));
36
+ test("read from a Promise dataSource", () => cache(`${dir}/promise.txt`, () => {
37
+ return new Promise((r) => r("content"));
38
+ }).then((value) => expect(value).toEqual("content")));
@@ -0,0 +1,41 @@
1
+ import { type Obj } from '@impactor/javascript';
2
+ import { type PathLike, type WriteFileOptions } from 'node:fs';
3
+ export declare function resolve(...paths: Array<PathLike | URL>): string;
4
+ export interface ParsePath {
5
+ type: 'dir' | 'file';
6
+ dir: string;
7
+ name: string;
8
+ extension: string | undefined;
9
+ }
10
+ export declare function parsePath(path: PathLike): ParsePath;
11
+ export declare function getExtensionSync(file: PathLike): string;
12
+ export declare function getSizeSync(path: PathLike | PathLike[], unit?: 'b' | 'kb' | 'mb' | 'gb', filter?: Filter): number;
13
+ export declare function isDirSync(path: PathLike): boolean;
14
+ export declare function getModifiedTimeSync(path: PathLike): number;
15
+ export declare function mkdirSync(path: PathLike | PathLike[], mode?: number | string): void;
16
+ export declare enum MoveOptionsExisting {
17
+ 'replace' = 0,
18
+ 'rename' = 1,
19
+ 'skip' = 2,
20
+ 'stop' = 3,
21
+ 'throw' = 4
22
+ }
23
+ export interface MoveOptions {
24
+ existing: MoveOptionsExisting | string | ((path: string) => string);
25
+ }
26
+ export declare function moveSync(path: PathLike, newPath: PathLike, _options?: MoveOptions): void;
27
+ export declare function removeSync(path: PathLike | PathLike[], filter?: Filter, keepDir?: boolean): void;
28
+ export declare function copySync(source: PathLike, destination: string, filter?: Filter): any;
29
+ export declare function writeSync(path: PathLike, data: any, options?: WriteFileOptions): void;
30
+ export interface ReadOptions {
31
+ encoding?: BufferEncoding | null;
32
+ flag?: 'a' | 'ax' | 'a+' | 'ax+' | 'as' | 'as+' | 'r' | 'r+' | 'rs+' | 'w' | 'wx' | 'w+' | 'wx+';
33
+ maxAge?: number;
34
+ }
35
+ export declare function readSync<R extends Buffer | string | Array<unknown> | Obj>(path: PathLike | URL, options?: ReadOptions | BufferEncoding): R;
36
+ export declare function stripComments(content: string): string;
37
+ export declare function getEntriesSync(dir?: string | string[], filter?: ((entry: string) => boolean) | RegExp | 'files' | 'dirs', depth?: number, skip?: ((entry: string) => boolean) | RegExp): Array<string>;
38
+ export type Filter = (path: string, type?: 'dir' | 'file') => boolean;
39
+ export declare function recursiveSync(path: PathLike | PathLike[], apply: (path: string, type: 'dir' | 'file') => void, filter?: Filter): any | any[];
40
+ export declare function toPosix(path: PathLike, removeDriverLetter?: boolean): string;
41
+ export declare function validFilePath(path: PathLike): string;