vivth 1.1.1 → 1.2.0
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/.vivth-temp/README.src.md +35 -0
- package/README.md +1243 -441
- package/README.src.md +5 -2
- package/bun.lock +228 -0
- package/index.mjs +22 -9
- package/package.json +6 -3
- package/src/bundler/CompileJS.mjs +258 -0
- package/src/bundler/CreateESPlugin.mjs +24 -0
- package/src/bundler/EsBundler.mjs +27 -13
- package/src/bundler/FSInline.mjs +57 -0
- package/src/bundler/FSInlineAnalyzer.mjs +197 -0
- package/src/bundler/FSInlineBundled.mjs +34 -0
- package/src/bundler/adds/ToBundledJSPlugin.mjs +77 -0
- package/src/bundler/adds/externals.mjs +8 -0
- package/src/bundler/adds/pluginVivthBundle.mjs +5 -0
- package/src/class/Console.mjs +48 -27
- package/src/class/Derived.mjs +55 -7
- package/src/class/Effect.mjs +100 -39
- package/src/class/EnvSignal.mjs +5 -5
- package/src/class/EventSignal.mjs +55 -5
- package/src/class/FileSafe.mjs +124 -0
- package/src/class/ListDerived.mjs +6 -3
- package/src/class/ListSignal.mjs +11 -11
- package/src/class/LitExp.mjs +405 -0
- package/src/class/Paths.mjs +39 -4
- package/src/class/QChannel.mjs +79 -28
- package/src/class/SafeExit.mjs +31 -11
- package/src/class/Setup.mjs +12 -7
- package/src/class/Signal.mjs +26 -24
- package/src/class/WorkerMainThread.mjs +108 -135
- package/src/class/WorkerMainThreadBundled.mjs +216 -0
- package/src/class/WorkerThread.mjs +40 -31
- package/src/common/Base64URL.mjs +10 -5
- package/src/common/Base64URLFromFile.mjs +24 -0
- package/src/common/keys.mjs +3 -0
- package/src/doc/JSautoDOC.mjs +32 -56
- package/src/doc/parsedFile.mjs +37 -36
- package/src/function/CreateImmutable.mjs +9 -9
- package/src/function/EventCheck.mjs +2 -2
- package/src/function/EventObject.mjs +5 -5
- package/src/function/GetRuntime.mjs +38 -0
- package/src/function/IsAsync.mjs +2 -2
- package/src/function/LazyFactory.mjs +13 -13
- package/src/function/Timeout.mjs +2 -2
- package/src/function/Try.mjs +17 -12
- package/src/function/TryAsync.mjs +5 -5
- package/src/function/TrySync.mjs +5 -5
- package/src/function/TsToMjs.mjs +5 -4
- package/src/types/LitExpKeyType.mjs +5 -0
- package/src/types/QCBReturn.mjs +1 -1
- package/src/types/Runtime.mjs +7 -0
- package/types/dev/fsInline.d.mts +1 -0
- package/types/dev/test.d.mts +1 -0
- package/types/dev/testWorker.d.mts +7 -0
- package/types/dev/testbundle.d.mts +1 -0
- package/types/dev/workerThreadClass.d.mts +13 -0
- package/types/index.d.mts +18 -9
- package/types/src/bundler/CompileJS.d.mts +78 -0
- package/types/src/bundler/CreateESPlugin.d.mts +17 -0
- package/types/src/bundler/EsBundler.d.mts +33 -4
- package/types/src/bundler/FSInline.d.mts +43 -0
- package/types/src/bundler/FSInlineAnalyzer.d.mts +36 -0
- package/types/src/bundler/FSInlineBundled.d.mts +22 -0
- package/types/src/bundler/adds/ToBundledJSPlugin.d.mts +16 -0
- package/types/src/bundler/adds/externals.d.mts +1 -0
- package/types/src/bundler/adds/pluginVivthBundle.d.mts +1 -0
- package/types/src/class/Console.d.mts +36 -5
- package/types/src/class/Derived.d.mts +132 -5
- package/types/src/class/Effect.d.mts +106 -7
- package/types/src/class/EnvSignal.d.mts +8 -8
- package/types/src/class/EventSignal.d.mts +151 -7
- package/types/src/class/FileSafe.d.mts +90 -0
- package/types/src/class/ListDerived.d.mts +8 -5
- package/types/src/class/ListSignal.d.mts +123 -18
- package/types/src/class/LitExp.d.mts +361 -0
- package/types/src/class/Paths.d.mts +30 -4
- package/types/src/class/QChannel.d.mts +69 -22
- package/types/src/class/SafeExit.d.mts +24 -9
- package/types/src/class/Setup.d.mts +13 -10
- package/types/src/class/Signal.d.mts +73 -17
- package/types/src/class/WorkerMainThread.d.mts +47 -39
- package/types/src/class/WorkerMainThreadBundled.d.mts +85 -0
- package/types/src/class/WorkerThread.d.mts +34 -26
- package/types/src/common/Base64URL.d.mts +22 -1
- package/types/src/common/Base64URLFromFile.d.mts +16 -0
- package/types/src/common/keys.d.mts +1 -0
- package/types/src/doc/JSautoDOC.d.mts +3 -19
- package/types/src/doc/parsedFile.d.mts +72 -13
- package/types/src/function/CreateImmutable.d.mts +27 -2
- package/types/src/function/EventCheck.d.mts +15 -0
- package/types/src/function/EventObject.d.mts +17 -2
- package/types/src/function/GetRuntime.d.mts +2 -0
- package/types/src/function/IsAsync.d.mts +18 -0
- package/types/src/function/LazyFactory.d.mts +35 -2
- package/types/src/function/Timeout.d.mts +16 -0
- package/types/src/function/Try.d.mts +52 -1
- package/types/src/function/TryAsync.d.mts +22 -1
- package/types/src/function/TrySync.d.mts +16 -1
- package/types/src/function/TsToMjs.d.mts +19 -0
- package/types/src/types/LitExpKeyType.d.mts +1 -0
- package/types/src/types/QCBReturn.d.mts +1 -1
- package/types/src/types/Runtime.d.mts +1 -0
- package/dev/index.mjs +0 -28
- package/src/bundler/CompileMJS.mjs +0 -110
- package/src/function/WriteFileSafe.mjs +0 -37
- package/types/src/bundler/A.d.mts +0 -1
- package/types/src/bundler/CompileMJS.d.mts +0 -8
- package/types/src/function/WriteFileSafe.d.mts +0 -2
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
4
|
+
import { join, extname, basename, dirname } from 'node:path';
|
|
5
|
+
import { platform } from 'node:os';
|
|
6
|
+
|
|
7
|
+
import { exec } from 'pkg';
|
|
8
|
+
|
|
9
|
+
import { EsBundler } from './EsBundler.mjs';
|
|
10
|
+
import { FileSafe } from '../class/FileSafe.mjs';
|
|
11
|
+
import { TryAsync } from '../function/TryAsync.mjs';
|
|
12
|
+
import { FSInlineAnalyzer } from './FSInlineAnalyzer.mjs';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {'win32' | 'linux' | 'darwin' | string} PlatformKey
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
let binaryExtension = undefined;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {Record<string, string[]|string>} compilerOptions
|
|
22
|
+
* @returns {string[]}
|
|
23
|
+
*/
|
|
24
|
+
const generateFlagsValue = (compilerOptions) => {
|
|
25
|
+
const options = [];
|
|
26
|
+
for (const flag in compilerOptions) {
|
|
27
|
+
const value = compilerOptions[flag];
|
|
28
|
+
options.push(`--${flag}`);
|
|
29
|
+
if (!Array.isArray(value)) {
|
|
30
|
+
if (value) {
|
|
31
|
+
options.push(value);
|
|
32
|
+
}
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
options.push(value.filter((val) => !!val).join(','));
|
|
36
|
+
}
|
|
37
|
+
return options;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Maps Node.js platform to binary file extension.
|
|
41
|
+
*
|
|
42
|
+
* @returns {string} extension including dot (e.g. '.exe', '')
|
|
43
|
+
*/
|
|
44
|
+
const getBinaryExtension = () => {
|
|
45
|
+
if (!binaryExtension) {
|
|
46
|
+
switch (platform()) {
|
|
47
|
+
case 'win32':
|
|
48
|
+
binaryExtension = '.exe';
|
|
49
|
+
break;
|
|
50
|
+
case 'linux':
|
|
51
|
+
binaryExtension = ''; // Linux binaries typically have no extension
|
|
52
|
+
break;
|
|
53
|
+
case 'darwin':
|
|
54
|
+
binaryExtension = ''; // macOS binaries typically have no extension
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
binaryExtension = ''; // fallback for unknown platforms
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return binaryExtension;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* @description
|
|
65
|
+
* - function to compile `.ts`|`.mts`|`.mjs` file, into a single executable;
|
|
66
|
+
* - also generate js representation;
|
|
67
|
+
* - uses [pkg](https://www.npmjs.com/package/pkg), [bun](https://bun.com/docs/bundler/executables), and [deno](https://docs.deno.com/runtime/reference/cli/compile/) compiler under the hood;
|
|
68
|
+
* >- they are used only as packaging agent, and doesn't necessarily supports their advanced feature, such as, assets bundling(use [FSInline](#fsinline) instead);
|
|
69
|
+
* >- `WorkerThread` will be converted to inline using `FSInline` too;
|
|
70
|
+
*
|
|
71
|
+
* !!!WARNING!!!
|
|
72
|
+
* !!!WARNING!!!
|
|
73
|
+
* !!!WARNING!!!
|
|
74
|
+
*
|
|
75
|
+
* - This function does not obfuscate and will not prevent decompilation. Do not embed environment variables or sensitive information inside `options.entryPoint`;
|
|
76
|
+
* - It is designed for quick binarization, allowing execution on machines without `Node.js`, `Bun`, or `Deno` installed;
|
|
77
|
+
* - The resulting binary will contain `FSInline` and `WorkerMainThread` target paths Buffers, which are loaded into memory at runtime. If your logic depends on the file system, use `node:fs` or `node:fs/promises` APIs and ship external files alongside the binary (not compiled);
|
|
78
|
+
*
|
|
79
|
+
* !!!WARNING!!!
|
|
80
|
+
* !!!WARNING!!!
|
|
81
|
+
* !!!WARNING!!!
|
|
82
|
+
*
|
|
83
|
+
* @param {Object} options
|
|
84
|
+
* @param {string} options.entryPoint
|
|
85
|
+
* - need to be manually prefixed;
|
|
86
|
+
* @param {BufferEncoding} [options.encoding]
|
|
87
|
+
* - write and read encoding for the sources;
|
|
88
|
+
* - default: `utf-8`;
|
|
89
|
+
* @param {boolean} options.minifyFirst
|
|
90
|
+
* - minify the bundle before compilation;
|
|
91
|
+
* @param {string} options.outDir
|
|
92
|
+
* - need manual prefix;
|
|
93
|
+
* @param {'pkg'|'bun'|'deno'} [options.compiler]
|
|
94
|
+
* - default: no comilation, just bundling;
|
|
95
|
+
* - `bun` and `pkg` is checked, if there's bug on `deno`, please report on github for issues;
|
|
96
|
+
* @param {Record<string, string>} [options.compilerArguments]
|
|
97
|
+
* - `key` are to used as `--keyName`;
|
|
98
|
+
* - value are the following value of the key;
|
|
99
|
+
* - no need to add the output/outdir, as it use the `options.outDir`;
|
|
100
|
+
* @return {ReturnType<TryAsync<{compileResult:Promise<any>,
|
|
101
|
+
* commandCalled: string;
|
|
102
|
+
* compiledBinFile: string;
|
|
103
|
+
* bundledJSFile:string
|
|
104
|
+
* }>>}
|
|
105
|
+
* @example
|
|
106
|
+
* import { join } from 'node:path';
|
|
107
|
+
*
|
|
108
|
+
* import { CompileJS, Paths } from 'vivth';
|
|
109
|
+
*
|
|
110
|
+
* const [[resultPkg, errorPkg], [resultBun, errorBun]] = await Promise.all([
|
|
111
|
+
* CompileJS({
|
|
112
|
+
* entryPoint: join(Paths.root, '/dev'),
|
|
113
|
+
* minifyFirst: true,
|
|
114
|
+
* outDir: join(Paths.root, '/dev-pkg'),
|
|
115
|
+
* compiler: 'pkg',
|
|
116
|
+
* compilerArguments: {
|
|
117
|
+
* target: ['node18-win-x64'],
|
|
118
|
+
* }
|
|
119
|
+
* }),
|
|
120
|
+
* CompileJS({
|
|
121
|
+
* entryPoint: join(Paths.root, '/dev'),
|
|
122
|
+
* minifyFirst: true,
|
|
123
|
+
* outDir: join(Paths.root, '/dev-pkg'),
|
|
124
|
+
* compiler: 'bun',
|
|
125
|
+
* compilerArguments: {
|
|
126
|
+
* target: ['bun-win-x64'],
|
|
127
|
+
* }
|
|
128
|
+
* }),
|
|
129
|
+
* ])
|
|
130
|
+
*/
|
|
131
|
+
export async function CompileJS({
|
|
132
|
+
entryPoint,
|
|
133
|
+
minifyFirst,
|
|
134
|
+
encoding = 'utf-8',
|
|
135
|
+
outDir,
|
|
136
|
+
compiler = undefined,
|
|
137
|
+
compilerArguments = undefined,
|
|
138
|
+
}) {
|
|
139
|
+
return await TryAsync(async () => {
|
|
140
|
+
/**
|
|
141
|
+
* @type {'cjs'|'esm'|'iife'}
|
|
142
|
+
*/
|
|
143
|
+
let format;
|
|
144
|
+
switch (compiler) {
|
|
145
|
+
case 'pkg':
|
|
146
|
+
format = 'cjs';
|
|
147
|
+
break;
|
|
148
|
+
case 'bun':
|
|
149
|
+
case 'deno':
|
|
150
|
+
format = 'esm';
|
|
151
|
+
break;
|
|
152
|
+
default:
|
|
153
|
+
minifyFirst = true;
|
|
154
|
+
format = 'esm';
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
const extOfSource = extname(entryPoint);
|
|
158
|
+
switch (extOfSource) {
|
|
159
|
+
case '.mts':
|
|
160
|
+
case '.ts':
|
|
161
|
+
case '.mjs':
|
|
162
|
+
break;
|
|
163
|
+
default:
|
|
164
|
+
throw new Error(
|
|
165
|
+
`extention mismatch: "${extOfSource}", should be one of:".mjs"|".mts"|".ts"`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
const sourceText = await readFile(entryPoint, { encoding });
|
|
169
|
+
const [bundledPrep, errorPrep] = await EsBundler(
|
|
170
|
+
{
|
|
171
|
+
content: sourceText,
|
|
172
|
+
extension: extOfSource,
|
|
173
|
+
root: dirname(entryPoint),
|
|
174
|
+
withBinHeader: compiler ? true : false,
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
minify: minifyFirst,
|
|
178
|
+
format,
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
if (errorPrep) {
|
|
182
|
+
throw errorPrep;
|
|
183
|
+
}
|
|
184
|
+
const [analyzedBundled, errorAnalyze] = await FSInlineAnalyzer.finalContent(
|
|
185
|
+
bundledPrep,
|
|
186
|
+
format
|
|
187
|
+
);
|
|
188
|
+
if (errorAnalyze) {
|
|
189
|
+
throw errorAnalyze;
|
|
190
|
+
}
|
|
191
|
+
const outputBaseNameNoExt = join(outDir, basename(entryPoint).replace(extOfSource, ''));
|
|
192
|
+
const bundledJSFile = `${outputBaseNameNoExt}${format === 'cjs' ? '.cjs' : '.mjs'}`;
|
|
193
|
+
await FileSafe.write(bundledJSFile, analyzedBundled, { encoding });
|
|
194
|
+
const compiledBinFile = `${outputBaseNameNoExt}${getBinaryExtension()}`;
|
|
195
|
+
switch (compiler) {
|
|
196
|
+
case 'pkg': {
|
|
197
|
+
const commandCalled = [
|
|
198
|
+
bundledJSFile,
|
|
199
|
+
...generateFlagsValue(compilerArguments),
|
|
200
|
+
'--output',
|
|
201
|
+
compiledBinFile,
|
|
202
|
+
];
|
|
203
|
+
return {
|
|
204
|
+
compileResult: await exec(commandCalled),
|
|
205
|
+
commandCalled: `pkg ${commandCalled.join(' ')}`,
|
|
206
|
+
compiledBinFile,
|
|
207
|
+
bundledJSFile,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
case 'bun': {
|
|
211
|
+
const commandCalled = [
|
|
212
|
+
'build',
|
|
213
|
+
bundledJSFile,
|
|
214
|
+
'--compile',
|
|
215
|
+
...generateFlagsValue(compilerArguments),
|
|
216
|
+
'--outfile',
|
|
217
|
+
compiledBinFile,
|
|
218
|
+
];
|
|
219
|
+
return {
|
|
220
|
+
compileResult: await Bun.spawn(['bun', ...commandCalled]),
|
|
221
|
+
commandCalled: `bun ${commandCalled.join(' ')}`,
|
|
222
|
+
compiledBinFile,
|
|
223
|
+
bundledJSFile,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
case 'deno': {
|
|
227
|
+
const commandCalled = [
|
|
228
|
+
'compile',
|
|
229
|
+
...generateFlagsValue(compilerArguments),
|
|
230
|
+
'--output',
|
|
231
|
+
compiledBinFile,
|
|
232
|
+
bundledJSFile,
|
|
233
|
+
'--verbose',
|
|
234
|
+
];
|
|
235
|
+
// @ts-expect-error
|
|
236
|
+
const command = new Deno.Command('deno', {
|
|
237
|
+
args: commandCalled,
|
|
238
|
+
stdout: 'piped',
|
|
239
|
+
stderr: 'piped',
|
|
240
|
+
});
|
|
241
|
+
return {
|
|
242
|
+
compileResult: await command.output(),
|
|
243
|
+
commandCalled: `deno ${commandCalled.join(' ')}`,
|
|
244
|
+
compiledBinFile,
|
|
245
|
+
bundledJSFile,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
default: {
|
|
249
|
+
return {
|
|
250
|
+
compileResult: undefined,
|
|
251
|
+
commandCalled: undefined,
|
|
252
|
+
compiledBinFile: undefined,
|
|
253
|
+
bundledJSFile,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @description
|
|
5
|
+
* - typed esbuild Plugin generator;
|
|
6
|
+
* @param {string} name
|
|
7
|
+
* @param {import('esbuild').Plugin["setup"]} setup
|
|
8
|
+
* @returns {import('esbuild').Plugin}
|
|
9
|
+
* @example
|
|
10
|
+
* import { CreateESPlugin } from 'vivth';
|
|
11
|
+
*
|
|
12
|
+
* export const pluginAddCopyRight = CreateESPlugin(
|
|
13
|
+
* 'MyCopyrightDeclaration',
|
|
14
|
+
* async (build) => {
|
|
15
|
+
* // build script;
|
|
16
|
+
* }
|
|
17
|
+
* );
|
|
18
|
+
*/
|
|
19
|
+
export function CreateESPlugin(name, setup) {
|
|
20
|
+
return {
|
|
21
|
+
name: JSON.stringify({ 'vivth:CustomESBuildPlugin': name }),
|
|
22
|
+
setup,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -5,6 +5,8 @@ import { build } from 'esbuild';
|
|
|
5
5
|
import { Console } from '../class/Console.mjs';
|
|
6
6
|
import { Paths } from '../class/Paths.mjs';
|
|
7
7
|
import { TryAsync } from '../function/TryAsync.mjs';
|
|
8
|
+
import { pluginVivthBundle } from './adds/pluginVivthBundle.mjs';
|
|
9
|
+
import { externals } from './adds/externals.mjs';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* @description
|
|
@@ -13,15 +15,18 @@ import { TryAsync } from '../function/TryAsync.mjs';
|
|
|
13
15
|
* @param {Object} options
|
|
14
16
|
* @param {string} options.content
|
|
15
17
|
* - the code can also uses composites from the result from multiple readFiles;
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* @param {
|
|
19
|
-
* @param {
|
|
20
|
-
* @
|
|
18
|
+
* @param {string} options.root
|
|
19
|
+
* - use dirname of said fileString path;
|
|
20
|
+
* @param {'.mts'|'.ts'|'.mjs'} options.extension
|
|
21
|
+
* @param {boolean} [options.withBinHeader]
|
|
22
|
+
* @param {Omit<Parameters<build>[0],
|
|
23
|
+
* 'entryPoints'|'bundle'|'write'|'sourcemap'>
|
|
24
|
+
* } [esbuildOptions]
|
|
25
|
+
* @returns {ReturnType<typeof TryAsync<string>>}
|
|
21
26
|
* @example
|
|
22
27
|
* import { EsBundler } from 'vivth';
|
|
23
28
|
*
|
|
24
|
-
* const bundledString = EsBundler(
|
|
29
|
+
* const bundledString = EsBundler(
|
|
25
30
|
* {
|
|
26
31
|
* content: ``,
|
|
27
32
|
* extension: '.mts',
|
|
@@ -31,7 +36,10 @@ import { TryAsync } from '../function/TryAsync.mjs';
|
|
|
31
36
|
* ...esbuildOptions,
|
|
32
37
|
* });
|
|
33
38
|
*/
|
|
34
|
-
export
|
|
39
|
+
export async function EsBundler(
|
|
40
|
+
{ content, extension, root, withBinHeader = false },
|
|
41
|
+
esbuildOptions = {}
|
|
42
|
+
) {
|
|
35
43
|
return await TryAsync(async () => {
|
|
36
44
|
/** @type {Parameters<build>[0]['stdin']['loader']} */
|
|
37
45
|
let loader;
|
|
@@ -52,28 +60,34 @@ export const EsBundler = async ({ content, extension, asBinary = false }, esbuil
|
|
|
52
60
|
Console.error(error);
|
|
53
61
|
throw new Error(JSON.stringify(error));
|
|
54
62
|
}
|
|
63
|
+
esbuildOptions.external = [...(esbuildOptions?.external ?? []), ...externals];
|
|
64
|
+
esbuildOptions.plugins = [...(esbuildOptions?.plugins ?? []), pluginVivthBundle];
|
|
55
65
|
const result = await build({
|
|
56
66
|
target: 'esnext',
|
|
57
67
|
platform: 'node',
|
|
68
|
+
format: 'esm',
|
|
58
69
|
...esbuildOptions,
|
|
59
70
|
stdin: {
|
|
60
71
|
contents: content,
|
|
61
72
|
loader,
|
|
62
|
-
resolveDir: Paths.root,
|
|
73
|
+
resolveDir: root ?? Paths.root,
|
|
74
|
+
...esbuildOptions.banner,
|
|
63
75
|
},
|
|
64
|
-
format: 'esm',
|
|
65
76
|
bundle: true,
|
|
66
77
|
write: false,
|
|
67
78
|
sourcemap: false,
|
|
68
|
-
external: [],
|
|
69
79
|
banner: {
|
|
70
|
-
js:
|
|
80
|
+
js: withBinHeader ? '#!/usr/bin/env node' : '',
|
|
81
|
+
...esbuildOptions.banner,
|
|
71
82
|
},
|
|
72
83
|
});
|
|
73
84
|
if (result.warnings?.length) {
|
|
74
85
|
Console.warn(result.warnings.map((w) => w.text).join('\n'));
|
|
75
86
|
}
|
|
76
87
|
const resString = result.outputFiles?.[0]?.text;
|
|
77
|
-
|
|
88
|
+
if (!resString) {
|
|
89
|
+
return '';
|
|
90
|
+
}
|
|
91
|
+
return resString;
|
|
78
92
|
});
|
|
79
|
-
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
|
|
6
|
+
import { Paths } from '../class/Paths.mjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @description
|
|
10
|
+
* - class helper to inline file;
|
|
11
|
+
* - use only if you are planning to use [CompileJS](#compilejs);
|
|
12
|
+
*/
|
|
13
|
+
export class FSInline {
|
|
14
|
+
/**
|
|
15
|
+
* @description
|
|
16
|
+
* - declare entrypoint of file inlining;
|
|
17
|
+
* >- on the dev time, it's just regullar `readFile` from `node:fs/promises`;
|
|
18
|
+
* >- on the compiled, it will read file from `FSInline.vivthFSInlinelists`
|
|
19
|
+
* @param {string} filePathFromProject
|
|
20
|
+
* - doesn't require prefix;
|
|
21
|
+
* @returns {Promise<Buffer<ArrayBuffer>>}
|
|
22
|
+
* @example
|
|
23
|
+
* import { FSInline } from 'vivth';
|
|
24
|
+
*
|
|
25
|
+
* (await FSInline.vivthFSInlineFile('/assets/text.txt')).toString('utf-8');
|
|
26
|
+
*/
|
|
27
|
+
static vivthFSInlineFile = async (filePathFromProject) => {
|
|
28
|
+
filePathFromProject = Paths.normalizesForRoot(filePathFromProject);
|
|
29
|
+
const fullAbsolutePath = join(Paths.root, filePathFromProject);
|
|
30
|
+
return Buffer.from(await readFile(fullAbsolutePath));
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* @description
|
|
34
|
+
* - declare entrypoint of file inlining, include all files on `dir` and `subdir` that match the `fileRule`;
|
|
35
|
+
* @param {string} dirPathFromProject
|
|
36
|
+
* - doesn't require prefix;
|
|
37
|
+
* @param {RegExp} fileRule
|
|
38
|
+
* @returns {Promise<typeof FSInline["vivthFSInlineFile"]>}
|
|
39
|
+
* @example
|
|
40
|
+
* import { FSInline } from 'vivth';
|
|
41
|
+
*
|
|
42
|
+
* export const pngAssets = await FSInline.vivthFSInlineDir('/assets', /.png$/g);
|
|
43
|
+
*/
|
|
44
|
+
static vivthFSInlineDir = async (dirPathFromProject, fileRule) => {
|
|
45
|
+
dirPathFromProject = Paths.normalizesForRoot(dirPathFromProject);
|
|
46
|
+
return (path_) => FSInline.vivthFSInlineFile(join(dirPathFromProject, path_));
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* @description
|
|
50
|
+
* - placeholder for FSInline;
|
|
51
|
+
* - it's remain publicly accessible so it doesn't mess with regex analyze on bundle;
|
|
52
|
+
* - shouldn't be manually accessed;
|
|
53
|
+
* >- access via `FSInline.vivthFSInlineFile` or `FSInline.vivthFSInlineDir`;
|
|
54
|
+
* @type {Record<string, Buffer<ArrayBuffer>>}
|
|
55
|
+
*/
|
|
56
|
+
static vivthFSInlinelists = {};
|
|
57
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { dirname, extname, join, relative } from 'node:path';
|
|
4
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
5
|
+
|
|
6
|
+
import { Paths } from '../class/Paths.mjs';
|
|
7
|
+
import { LitExp } from '../class/LitExp.mjs';
|
|
8
|
+
import { TryAsync } from '../function/TryAsync.mjs';
|
|
9
|
+
import { FSInline } from './FSInline.mjs';
|
|
10
|
+
import { EsBundler } from './EsBundler.mjs';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} str
|
|
14
|
+
* @returns {RegExp}
|
|
15
|
+
*/
|
|
16
|
+
const hydrateRegex = (str) => {
|
|
17
|
+
const match = str.match(/^\/(.*)\/([a-z]*)$/);
|
|
18
|
+
if (!match) throw new Error('Invalid regex string format');
|
|
19
|
+
|
|
20
|
+
const [, pattern, flags] = match;
|
|
21
|
+
return new RegExp(pattern, flags);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @description
|
|
26
|
+
* - collections of static method to analyze content for `FSInline`;
|
|
27
|
+
*/
|
|
28
|
+
export class FSInlineAnalyzer {
|
|
29
|
+
/**
|
|
30
|
+
* @description
|
|
31
|
+
* - to be used on bundled content;
|
|
32
|
+
* @param {string} content
|
|
33
|
+
* @param {'cjs'|'esm'} format
|
|
34
|
+
* @returns {ReturnType<typeof TryAsync<string>>}
|
|
35
|
+
* @example
|
|
36
|
+
* import { readFile } from 'node:fs/promises';
|
|
37
|
+
*
|
|
38
|
+
* import { FSInlineAnalyzer } from 'vivth';
|
|
39
|
+
*
|
|
40
|
+
* const [resultFinalContent, errorFinalContent] = await FSInlineAnalyzer.finalContent(
|
|
41
|
+
* await readFile('./resultESBunlded.mjs', {encoding: 'utf-8'}),
|
|
42
|
+
* 'esm'
|
|
43
|
+
* );
|
|
44
|
+
*/
|
|
45
|
+
static finalContent = async (content, format) => {
|
|
46
|
+
return await TryAsync(async () => {
|
|
47
|
+
const [literalFile, errorPrepareFile] = LitExp.prepare({
|
|
48
|
+
FSInline: /\w*/,
|
|
49
|
+
method: /\.vivthFSInlineFile\s*?\(\s*?['"]/,
|
|
50
|
+
path: false,
|
|
51
|
+
closing: /['"]\s*?\)(?:;|)/,
|
|
52
|
+
});
|
|
53
|
+
const [literalWorker, errorPreparWorker] = LitExp.prepare({
|
|
54
|
+
ref: /\w*/,
|
|
55
|
+
method: /\.newVivthWorker\s*?\(\s*?['"]/,
|
|
56
|
+
path: false,
|
|
57
|
+
closing: /['"]/,
|
|
58
|
+
});
|
|
59
|
+
const [literalDir, errorPrepareDir] = LitExp.prepare({
|
|
60
|
+
FSInline: /\w*/,
|
|
61
|
+
method: /\.vivthFSInlineDir\s*?\(\s*?['"]/,
|
|
62
|
+
path: false,
|
|
63
|
+
methodClosing: /['"]\s*?,/,
|
|
64
|
+
rule: false,
|
|
65
|
+
functionClosing: /\)(?:;|)/,
|
|
66
|
+
});
|
|
67
|
+
if (errorPrepareFile || errorPrepareDir || errorPreparWorker) {
|
|
68
|
+
throw { errorPrepareFile, errorPrepareDir, errorPreparWorker };
|
|
69
|
+
}
|
|
70
|
+
const templateFile = literalFile`${'FSInline'}${'method'}${'path'}${'closing'}`;
|
|
71
|
+
const templateWorker = literalWorker`${'ref'}${'method'}${'path'}${'closing'}`;
|
|
72
|
+
const templateDir = literalDir`${'FSInline'}${'method'}${'path'}${'methodClosing'}${'rule'}${'functionClosing'}`;
|
|
73
|
+
const [resultMatchingFile, errorMatchingFile] = templateFile.evaluate.matchedAllAndGrouped(
|
|
74
|
+
content,
|
|
75
|
+
{
|
|
76
|
+
flags: 'gm',
|
|
77
|
+
whiteSpaceSensitive: false,
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
const [resultMatchingWorker, errorMatchingWorker] =
|
|
81
|
+
templateWorker.evaluate.matchedAllAndGrouped(content, {
|
|
82
|
+
flags: 'gm',
|
|
83
|
+
whiteSpaceSensitive: false,
|
|
84
|
+
});
|
|
85
|
+
const [resultMatchingDir, errorMatchingDir] = templateDir.evaluate.matchedAllAndGrouped(
|
|
86
|
+
content,
|
|
87
|
+
{
|
|
88
|
+
flags: 'gm',
|
|
89
|
+
whiteSpaceSensitive: false,
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
if (errorMatchingFile || errorMatchingDir || errorMatchingWorker) {
|
|
93
|
+
throw { errorMatchingFile, errorMatchingDir, errorMatchingWorker };
|
|
94
|
+
}
|
|
95
|
+
const {
|
|
96
|
+
result: { named: namedFile },
|
|
97
|
+
} = resultMatchingFile;
|
|
98
|
+
for (let i = 0; i < namedFile.length; i++) {
|
|
99
|
+
const { path } = namedFile[i];
|
|
100
|
+
FSInline.vivthFSInlinelists[path] = Buffer.from(await readFile(join(Paths.root, path)));
|
|
101
|
+
}
|
|
102
|
+
const {
|
|
103
|
+
result: { named: namedWorker },
|
|
104
|
+
regexp,
|
|
105
|
+
} = resultMatchingWorker;
|
|
106
|
+
|
|
107
|
+
for (let i = 0; i < namedWorker.length; i++) {
|
|
108
|
+
const { path } = namedWorker[i];
|
|
109
|
+
const fullPath = join(Paths.root, path);
|
|
110
|
+
const content = await readFile(fullPath, { encoding: 'utf-8' });
|
|
111
|
+
const [contentBundled, errorBundled] = await EsBundler(
|
|
112
|
+
{
|
|
113
|
+
content,
|
|
114
|
+
// @ts-expect-error
|
|
115
|
+
extension: extname(path),
|
|
116
|
+
root: dirname(fullPath),
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
minify: true,
|
|
120
|
+
format,
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
if (errorBundled) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const [trueContent, errorFinal] = await FSInlineAnalyzer.finalContent(
|
|
127
|
+
contentBundled,
|
|
128
|
+
format
|
|
129
|
+
);
|
|
130
|
+
if (errorFinal) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
FSInline.vivthFSInlinelists[path] = Buffer.from(trueContent);
|
|
134
|
+
}
|
|
135
|
+
const {
|
|
136
|
+
result: { named: namedDir },
|
|
137
|
+
} = resultMatchingDir;
|
|
138
|
+
for (let i = 0; i < namedDir.length; i++) {
|
|
139
|
+
let { path, rule } = namedDir[i];
|
|
140
|
+
rule = rule.trim();
|
|
141
|
+
const results = await FSInlineAnalyzer.#dir(join(Paths.root, path), hydrateRegex(rule));
|
|
142
|
+
for (let j = 0; j < results.length; j++) {
|
|
143
|
+
const { path, buffer } = results[j];
|
|
144
|
+
if (path in FSInline.vivthFSInlinelists) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
FSInline.vivthFSInlinelists[path] = buffer;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return content.replace(
|
|
151
|
+
/static\s*vivthFSInlinelists(?:;|)/,
|
|
152
|
+
`static vivthFSInlinelists=${JSON.stringify(FSInline.vivthFSInlinelists)}`
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* @typedef {{path:string, buffer:Buffer<ArrayBuffer>}[]} _dirReturn
|
|
158
|
+
*/
|
|
159
|
+
/**
|
|
160
|
+
* @param {string} dirName
|
|
161
|
+
* @param {RegExp} ruleForFileFullPath
|
|
162
|
+
* @returns {Promise<_dirReturn>}
|
|
163
|
+
*/
|
|
164
|
+
static #dir = async (dirName, ruleForFileFullPath) => {
|
|
165
|
+
/**
|
|
166
|
+
* @type {_dirReturn}
|
|
167
|
+
*/
|
|
168
|
+
const result = [];
|
|
169
|
+
/**
|
|
170
|
+
* @param {string} current
|
|
171
|
+
* @returns {Promise<void>}
|
|
172
|
+
*/
|
|
173
|
+
const walk = async (current) => {
|
|
174
|
+
const entries = await readdir(current, {
|
|
175
|
+
recursive: false,
|
|
176
|
+
withFileTypes: true,
|
|
177
|
+
encoding: 'utf-8',
|
|
178
|
+
});
|
|
179
|
+
for (const entry of entries) {
|
|
180
|
+
const fullPath = Paths.normalize(join(current, entry.name));
|
|
181
|
+
const relativePath = Paths.normalizesForRoot(relative(Paths.root, fullPath));
|
|
182
|
+
if (entry.isDirectory()) {
|
|
183
|
+
await walk(fullPath);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (entry.isFile() && ruleForFileFullPath.test(fullPath)) {
|
|
187
|
+
result.push({
|
|
188
|
+
path: relativePath,
|
|
189
|
+
buffer: Buffer.from(await readFile(fullPath)),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
await walk(dirName);
|
|
195
|
+
return result;
|
|
196
|
+
};
|
|
197
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { Paths } from '../class/Paths.mjs';
|
|
6
|
+
|
|
7
|
+
export class FSInline {
|
|
8
|
+
static prefix = '';
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} filePathFromProject
|
|
11
|
+
* @returns {Promise<Buffer<ArrayBuffer>>}
|
|
12
|
+
*/
|
|
13
|
+
static vivthFSInlineFile = async (filePathFromProject) => {
|
|
14
|
+
filePathFromProject = Paths.normalizesForRoot(filePathFromProject);
|
|
15
|
+
return Buffer.from(FSInline.vivthFSInlinelists[filePathFromProject]);
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} dirPathFromProject
|
|
19
|
+
* @param {Object} regexRule
|
|
20
|
+
* @param {RegExp} regexRule.dir
|
|
21
|
+
* @param {RegExp} regexRule.file
|
|
22
|
+
* @returns {Promise<typeof FSInline["vivthFSInlineFile"]>}
|
|
23
|
+
* - relative to the `dirPathFromProject`
|
|
24
|
+
*/
|
|
25
|
+
static vivthFSInlineDir = async (dirPathFromProject) => {
|
|
26
|
+
dirPathFromProject = Paths.normalizesForRoot(dirPathFromProject);
|
|
27
|
+
return (path_) => FSInline.vivthFSInlineFile(join(dirPathFromProject, path_));
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* - to be used as embed placeholder on `bundled` and `compiled`;
|
|
31
|
+
* @type {Record<string, Buffer<ArrayBufferLike>>}
|
|
32
|
+
*/
|
|
33
|
+
static vivthFSInlinelists;
|
|
34
|
+
}
|