vivth 1.2.3 → 1.3.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.
Files changed (71) hide show
  1. package/README.md +609 -586
  2. package/README.src.md +5 -1
  3. package/bun.lock +6 -0
  4. package/index.mjs +10 -6
  5. package/package.json +3 -1
  6. package/src/bundler/CompileJS.mjs +11 -8
  7. package/src/bundler/EsBundler.mjs +4 -2
  8. package/src/bundler/FSInline.mjs +3 -0
  9. package/src/bundler/FSInlineAnalyzer.mjs +31 -7
  10. package/src/bundler/FSInlineBundled.mjs +5 -1
  11. package/src/bundler/adds/ToBundledJSPlugin.mjs +3 -4
  12. package/src/class/Console.mjs +12 -4
  13. package/src/class/Derived.mjs +5 -1
  14. package/src/class/Effect.mjs +6 -6
  15. package/src/class/EnvSignal.mjs +1 -8
  16. package/src/class/EventSignal.mjs +8 -7
  17. package/src/class/FileSafe.mjs +1 -1
  18. package/src/class/ListSignal.mjs +18 -10
  19. package/src/class/LitExp.mjs +241 -204
  20. package/src/class/Paths.mjs +10 -8
  21. package/src/class/QChannel.mjs +14 -7
  22. package/src/class/SafeExit.mjs +16 -7
  23. package/src/class/Signal.mjs +8 -7
  24. package/src/class/WorkerMainThread.mjs +45 -24
  25. package/src/class/WorkerMainThreadBundled.mjs +42 -24
  26. package/src/class/WorkerThread.mjs +21 -7
  27. package/src/common/Base64URL.mjs +2 -2
  28. package/src/common/Base64URLFromFile.mjs +1 -1
  29. package/src/doc/JSautoDOC.mjs +101 -62
  30. package/src/doc/correctBeforeParse.mjs +139 -0
  31. package/src/doc/parsedFile.mjs +127 -76
  32. package/src/function/CreateImmutable.mjs +12 -7
  33. package/src/function/GetRuntime.mjs +8 -3
  34. package/src/function/LazyFactory.mjs +10 -4
  35. package/src/function/Try.mjs +4 -1
  36. package/src/function/TryAsync.mjs +2 -1
  37. package/src/function/TrySync.mjs +3 -1
  38. package/src/function/TsToMjs.mjs +7 -4
  39. package/src/types/AnyButUndefined.mjs +1 -0
  40. package/src/types/LitExpResultType.mjs +7 -0
  41. package/src/types/Runtime.mjs +1 -1
  42. package/tsconfig.json +27 -5
  43. package/types/dev/workerThreadClass.d.mts +1 -1
  44. package/types/index.d.mts +7 -6
  45. package/types/src/bundler/CompileJS.d.mts +14 -9
  46. package/types/src/bundler/EsBundler.d.mts +1 -1
  47. package/types/src/class/Derived.d.mts +4 -64
  48. package/types/src/class/Effect.d.mts +8 -8
  49. package/types/src/class/EnvSignal.d.mts +0 -1
  50. package/types/src/class/EventSignal.d.mts +5 -5
  51. package/types/src/class/FileSafe.d.mts +2 -2
  52. package/types/src/class/ListSignal.d.mts +1 -1
  53. package/types/src/class/LitExp.d.mts +49 -53
  54. package/types/src/class/Paths.d.mts +4 -4
  55. package/types/src/class/QChannel.d.mts +1 -1
  56. package/types/src/class/SafeExit.d.mts +3 -3
  57. package/types/src/class/Signal.d.mts +3 -3
  58. package/types/src/class/WorkerMainThread.d.mts +13 -8
  59. package/types/src/class/WorkerMainThreadBundled.d.mts +13 -8
  60. package/types/src/class/WorkerThread.d.mts +4 -4
  61. package/types/src/common/Base64URL.d.mts +2 -2
  62. package/types/src/common/Base64URLFromFile.d.mts +2 -2
  63. package/types/src/doc/JSautoDOC.d.mts +34 -12
  64. package/types/src/doc/correctBeforeParse.d.mts +2 -0
  65. package/types/src/doc/parsedFile.d.mts +7 -10
  66. package/types/src/function/CreateImmutable.d.mts +2 -2
  67. package/types/src/function/Try.d.mts +2 -2
  68. package/types/src/function/TryAsync.d.mts +2 -2
  69. package/types/src/function/TrySync.d.mts +3 -2
  70. package/types/src/function/TsToMjs.d.mts +2 -2
  71. package/types/src/types/LitExpResultType.d.mts +7 -0
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
 
3
3
  import { extname, join } from 'node:path';
4
- import { readFile, stat } from 'node:fs/promises';
4
+ import { readFile } from 'node:fs/promises';
5
5
 
6
6
  import chokidar from 'chokidar';
7
7
  import { EventSignal } from '../class/EventSignal.mjs';
@@ -15,6 +15,7 @@ import { TryAsync } from '../function/TryAsync.mjs';
15
15
  import { Console } from '../class/Console.mjs';
16
16
  import { TsToMjs } from '../function/TsToMjs.mjs';
17
17
  import { FileSafe } from '../class/FileSafe.mjs';
18
+ import { correctBeforeParse } from './correctBeforeParse.mjs';
18
19
 
19
20
  /**
20
21
  * @typedef {import('fs').Stats} Stats
@@ -46,21 +47,25 @@ const acceptableExt = new Set(['.mjs', '.mts', '.ts']);
46
47
  * >>- first `"at"${string}` after `"at"description` until `"at"example` will be treated as `javascript` comment block on the `markdown`;
47
48
  * >>- `"at"example` are treated as `javascript` block on the `markdown` file, and should be placed last on the same comment block;
48
49
  * >>- you can always look at `vivth/src` files to check how the source, and the `README.md` and `index.mjs` documentation/generation results;
50
+ * >6) this types of arrow functions will be converted to regullar function, for concise type emition:
51
+ * >>- validly exported function;
52
+ * >>- static/instance method(s) with generic template;
53
+ * >7) transpile `.ts` and `.mts` to `.mjs` same name and directory;
49
54
  */
50
55
  export class JSautoDOC {
51
56
  /**
52
- * @type {JSautoDOC}
57
+ * @type {JSautoDOC|undefined}
53
58
  */
54
- static #instance = undefined;
59
+ static #instance;
55
60
  /**
56
61
  * @description
57
62
  * @param {Object} [options]
58
63
  * @param {Object} [options.paths]
59
- * @param {string} [options.paths.file]
64
+ * @param {string} options.paths.file
60
65
  * - entry point;
61
- * @param {string} [options.paths.readMe]
66
+ * @param {string} options.paths.readMe
62
67
  * - readme target;
63
- * @param {string} [options.paths.dir]
68
+ * @param {string} options.paths.dir
64
69
  * - source directory;
65
70
  * @param {string} [options.copyright]
66
71
  * @param {string} [options.tableOfContentTitle]
@@ -90,23 +95,27 @@ export class JSautoDOC {
90
95
  this.#paths = paths;
91
96
  this.#copyright = copyright;
92
97
  const rootPath = Paths.root;
98
+ if (rootPath === undefined) {
99
+ return;
100
+ }
93
101
  const watchpath = join(rootPath, this.#paths.dir);
94
102
  const watcher = chokidar.watch(watchpath, option);
95
103
  const watcherReadme = chokidar.watch(join(rootPath, readmesrcname), option);
96
104
  /**
97
- * @type {(eventName: 'add'|'change'|'unlink', path: string, stats?: import('fs').Stats) => void}
105
+ * @type {(eventName: import('chokidar/handler.js').EventName, path: string, stats?: import('fs').Stats) => void}
98
106
  */
99
107
  const listener = (eventName, path, stats) => {
100
108
  const ext = extname(path);
101
109
  if (
102
- !acceptableExt.has(
110
+ acceptableExt.has(
103
111
  // @ts-expect-error
104
112
  ext
105
- )
113
+ ) === false
106
114
  ) {
107
115
  return;
108
116
  }
109
117
  if (ext !== '.mjs') {
118
+ // no need to be awaited
110
119
  TsToMjs(path, {
111
120
  encoding,
112
121
  });
@@ -124,44 +133,47 @@ export class JSautoDOC {
124
133
  };
125
134
  watcher.on('all', listener);
126
135
  watcherReadme.on('all', this.#readMeListener);
127
- SafeExit.instance.addCallback(async () => {
128
- watcher.close();
129
- watcherReadme.close();
130
- watcher.removeAllListeners();
131
- watcherReadme.removeAllListeners();
132
- watcher.removeListener('all', listener);
133
- watcherReadme.removeAllListeners(this.#readMeListener);
134
- watcherReadme.removeListener('all', this.#readMeListener);
135
- });
136
+ if (SafeExit.instance) {
137
+ SafeExit.instance.addCallback(async () => {
138
+ watcher.close();
139
+ watcherReadme.close();
140
+ watcher.removeAllListeners();
141
+ watcherReadme.removeAllListeners();
142
+ watcher.removeListener('all', listener);
143
+ watcherReadme.removeAllListeners(this.#readMeListener);
144
+ watcherReadme.removeListener('all', this.#readMeListener);
145
+ });
146
+ }
136
147
  }
137
148
  /**
138
- * @type {string}
149
+ * @type {string|undefined}
139
150
  */
140
- #copyright = undefined;
151
+ #copyright;
141
152
  /**
142
153
  * @type {{
143
- * file?: string;
144
- * readMe?: string;
145
- * dir?: string;
146
- * }}
154
+ * file: string;
155
+ * readMe: string;
156
+ * dir: string;
157
+ * }|undefined}
147
158
  */
148
159
  #paths;
149
160
  /**
150
- * @type {string}
161
+ * @type {string|undefined}
151
162
  */
152
163
  #tableOfContentTitle;
153
164
  /**
154
- * @param {string} _eventName
165
+ * @param {import('chokidar/handler.js').EventName} _eventName
155
166
  * @param {string} path_
156
- * @param {Stats} stats
157
- * @returns {Promise<void>}
167
+ * @param {Stats|undefined} stats
168
+ * @returns {void}
158
169
  */
159
- #readMeListener = async (_eventName, path_, stats) => {
160
- if (!stats.isFile()) {
170
+ #readMeListener = (_eventName, path_, stats) => {
171
+ if (stats && stats.isFile() === false) {
161
172
  return;
162
173
  }
163
- const content = await readFile(path_, { encoding });
164
- this.#readMESRCContent.value = content;
174
+ readFile(path_, { encoding }).then((content) => {
175
+ this.#readMESRCContent.value = content;
176
+ });
165
177
  };
166
178
  /**
167
179
  * @type {Signal<Set<string>>}
@@ -170,32 +182,37 @@ export class JSautoDOC {
170
182
  /**
171
183
  * @type {Signal<string>}
172
184
  */
173
- #readMESRCContent = LazyFactory(() => new Signal(undefined));
185
+ #readMESRCContent = LazyFactory(() => new Signal(''));
174
186
  #generatedREADME_md = new Effect(async ({ subscribe, isLastCalled }) => {
175
187
  const contentSRC = subscribe(this.#readMESRCContent).value;
176
188
  const filepaths = subscribe(this.#filePaths).value;
177
- if (!(await isLastCalled(100)) || !contentSRC || !filepaths) {
189
+ if ((await isLastCalled(100)) === false || !contentSRC || !filepaths) {
178
190
  return;
179
191
  }
180
- const { readme, mjsFile } = await this.#generateFromSRC(contentSRC, filepaths);
181
- if (!(await isLastCalled())) {
192
+ const res = await this.#generateFromSRC(contentSRC, filepaths);
193
+ if ((await isLastCalled()) === false || res === undefined) {
182
194
  return;
183
195
  }
184
- const readmePath = join(Paths.root, this.#paths.readMe);
185
- const mjsFilePath = join(Paths.root, this.#paths.file);
196
+ const { readme, mjsFile } = res;
197
+ const rootPath = Paths.root;
198
+ if (rootPath === undefined || this.#paths === undefined) {
199
+ return;
200
+ }
201
+ const readmePath = join(rootPath, this.#paths.readMe);
202
+ const mjsFilePath = join(rootPath, this.#paths.file);
186
203
  const [[, errorWriteReadme], [, errorWriteMjsFile]] = await Promise.all([
187
204
  FileSafe.write(readmePath, readme, { encoding }),
188
205
  FileSafe.write(mjsFilePath, mjsFile, { encoding }),
189
206
  ]);
190
- if (!errorWriteReadme) {
191
- Console.info({ message: `successfully generate: '${readmePath}'` });
207
+ if (errorWriteReadme === undefined) {
208
+ Console.info({ vivthJSautoDOC: `successfully generate '${readmePath}'` });
192
209
  } else {
193
- Console.error({ message: `unable to generate: '${readmePath}';`, errorWriteReadme });
210
+ Console.error({ vivthJSautoDOC: `unable to generate '${readmePath}'`, errorWriteReadme });
194
211
  }
195
- if (!errorWriteMjsFile) {
196
- Console.info({ message: `successfully generate: '${mjsFilePath}'` });
212
+ if (errorWriteMjsFile === undefined) {
213
+ Console.info({ vivthJSautoDOC: `successfully generate '${mjsFilePath}'` });
197
214
  } else {
198
- Console.error({ message: `unable to generate: '${mjsFilePath}';`, errorWriteMjsFile });
215
+ Console.error({ vivthJSautoDOC: `unable to generate '${mjsFilePath}'`, errorWriteMjsFile });
199
216
  }
200
217
  });
201
218
  /**
@@ -216,9 +233,12 @@ export class JSautoDOC {
216
233
  /**
217
234
  * @param {string} contentSRC
218
235
  * @param {Set<string>} filepaths
219
- * @returns {Promise<generatedFromSRC>}
236
+ * @returns {Promise<generatedFromSRC|undefined>}
220
237
  */
221
238
  #generateFromSRC = async (contentSRC, filepaths) => {
239
+ if (this.#tableOfContentTitle === undefined || this.#copyright === undefined) {
240
+ return;
241
+ }
222
242
  const tableID = this.#tableOfContentTitle.replace(/\s+/g, '-').toLowerCase();
223
243
  const tableOfContent = [];
224
244
  const apiDocuments = [];
@@ -232,7 +252,12 @@ export class JSautoDOC {
232
252
  path: { relative: relativePath },
233
253
  baseName: { noExt },
234
254
  } = (await this.#parsedFilesRef.get(path_)).value;
235
- const hasNoAutoDoc = /\/\*\*[\s\*]*?@noautodoc[\s\*]*?\*\//.test(await content.string());
255
+ const trueContent = await content.string();
256
+ if (trueContent === undefined) {
257
+ return;
258
+ }
259
+
260
+ const hasNoAutoDoc = /\/\*\*[\s\*]*?@noautodoc[\s\*]*?\*\//.test(trueContent);
236
261
  if (hasValidExportObject) {
237
262
  mjsMain.push(
238
263
  `export { ${noExt} } from '${
@@ -240,6 +265,9 @@ export class JSautoDOC {
240
265
  }';`
241
266
  );
242
267
  }
268
+ /**
269
+ * @type {string[]}
270
+ */
243
271
  const currentDescription = [];
244
272
  const { readme, typedef } = documented;
245
273
  const [typedefString, error] = await TryAsync(async () => {
@@ -249,9 +277,9 @@ export class JSautoDOC {
249
277
  const result = await typedef();
250
278
  return result;
251
279
  });
252
- if (!error && typedefString) {
280
+ if (error === undefined && typedefString) {
253
281
  mjsTypes.push(typedefString.module);
254
- if (!hasNoAutoDoc) {
282
+ if (hasNoAutoDoc === false) {
255
283
  const nameVarID = noExt.toLowerCase();
256
284
  tableOfContent.push(`[${noExt}](#${nameVarID})`);
257
285
  apiDocuments.push(
@@ -261,7 +289,7 @@ export class JSautoDOC {
261
289
  );
262
290
  }
263
291
  }
264
- if (!hasNoAutoDoc && hasValidExportObject) {
292
+ if (hasNoAutoDoc === false && hasValidExportObject) {
265
293
  readme.forEach((ref) => {
266
294
  const {
267
295
  // fullDescription,
@@ -325,19 +353,30 @@ export class JSautoDOC {
325
353
  */
326
354
  #addHandler = (eventName, path__, _stats) => {
327
355
  TryAsync(async () => {
328
- const dispatch = await this.#parsedFilesRef.get(path__);
329
- if (!(await stat(path__)).isFile()) {
356
+ if (
357
+ //
358
+ !_stats?.isFile()
359
+ ) {
330
360
  return;
331
361
  }
332
- dispatch.value = new parsedFile(path__, encoding);
333
- await dispatch.value.parse();
334
- dispatch.subscribers.notify();
335
- this.#filePaths.subscribers.notify(async ({ signalInstance }) => {
336
- Console.info({ [eventName]: path__ });
337
- signalInstance.value.add(path__);
338
- });
362
+ const res = await correctBeforeParse(path__, 'utf-8');
363
+ switch (res) {
364
+ case 'shouldProceedNextCheck':
365
+ const dispatch = await this.#parsedFilesRef.get(path__);
366
+ dispatch.value = new parsedFile(path__, _stats, encoding);
367
+ this.#filePaths.subscribers.notify(async ({ signalInstance }) => {
368
+ await dispatch.value.parse();
369
+ dispatch.subscribers.notify();
370
+ Console.info({ vivthJSautoDOC: `${eventName} '${path__}' to export and doc` });
371
+ signalInstance.value.add(path__);
372
+ });
373
+ break;
374
+ case 'waitForRewrite':
375
+ case 'doNotProcess':
376
+ break;
377
+ }
339
378
  }).then(([, error]) => {
340
- if (!error) {
379
+ if (error === undefined) {
341
380
  return;
342
381
  }
343
382
  Console.error(error);
@@ -348,12 +387,12 @@ export class JSautoDOC {
348
387
  */
349
388
  #removeHandler = (eventName, path__, _stats) => {
350
389
  TryAsync(async () => {
351
- if (!(await stat(path__)).isFile()) {
390
+ if (_stats?.isFile()) {
352
391
  return true;
353
392
  }
354
393
  return false;
355
- }).then(([res, error]) => {
356
- if (res && !error) {
394
+ }).then(([, error]) => {
395
+ if (error === undefined) {
357
396
  return;
358
397
  }
359
398
  this.#filePaths.subscribers.notify(async ({ signalInstance }) => {
@@ -0,0 +1,139 @@
1
+ // @ts-check
2
+
3
+ import { basename } from 'node:path';
4
+
5
+ import { TryAsync } from '../function/TryAsync.mjs';
6
+ import { readFile, writeFile } from 'node:fs/promises';
7
+ import { Console } from '../class/Console.mjs';
8
+ import { Timeout } from '../function/Timeout.mjs';
9
+ import { LitExp } from '../class/LitExp.mjs';
10
+
11
+ /**
12
+ * @typedef {'shouldProceedNextCheck'|'waitForRewrite'|'doNotProcess'} RetType
13
+ */
14
+
15
+ /**
16
+ * @param {string} path
17
+ * @param {BufferEncoding} [encoding]
18
+ * @returns {Promise<RetType>}
19
+ */
20
+ export const correctBeforeParse = async (path, encoding = 'utf-8') => {
21
+ const validExportName = basename(path).split('.')[0] ?? '';
22
+ const firstLetter = validExportName[0];
23
+ const isStartWithCapital = firstLetter?.toUpperCase() === firstLetter;
24
+ if (validExportName === '' || isStartWithCapital === false) {
25
+ return 'doNotProcess';
26
+ }
27
+ const content = (await readFile(path, { encoding: 'utf-8' })).toString();
28
+ const [resFunctionCheck, errorFunctionCheck] = await checkIsFunction(
29
+ content,
30
+ validExportName,
31
+ path,
32
+ encoding
33
+ );
34
+ if (errorFunctionCheck) {
35
+ return 'doNotProcess';
36
+ }
37
+ if (resFunctionCheck === 'waitForRewrite') {
38
+ return 'waitForRewrite';
39
+ }
40
+ const [resClassCheck, errorClassCheck] = await checkIsClass(
41
+ content,
42
+ validExportName,
43
+ path,
44
+ encoding
45
+ );
46
+ if (errorClassCheck) {
47
+ return 'doNotProcess';
48
+ }
49
+ return resClassCheck;
50
+ };
51
+
52
+ /**
53
+ * @type {(content:string,
54
+ * validExportName:string,
55
+ * ...options:Parameters<correctBeforeParse>)=>
56
+ * ReturnType<typeof TryAsync<RetType>>}
57
+ */
58
+ const checkIsFunction = async (content, validExportName, path, encoding) => {
59
+ return await TryAsync(async () => {
60
+ const regexConst = new RegExp(
61
+ `export\\s+const\\s+${validExportName}\\s+\\=\\s*?(?:async|)\\s*?\\(([\\s\\S]*?)\\)\\s*?\=\>`,
62
+ 'g'
63
+ );
64
+ const matches = content.matchAll(regexConst).toArray()[0];
65
+ const useConst = regexConst.test(content);
66
+ if (useConst && matches) {
67
+ const [fullString, parameters] = matches;
68
+ const declaratorReplaceMent = `export function ${validExportName}(${parameters})`;
69
+ const newContent = content.replace(fullString, declaratorReplaceMent);
70
+ await Timeout(100); // to wait for pretify on autoSave;
71
+ await writeFile(path, newContent, { encoding });
72
+ Console.info({
73
+ vivthJSAutoDoc: `successfully modify '${path}' exported function to regullar function declaration, for correct type emition;`,
74
+ });
75
+ return 'waitForRewrite';
76
+ }
77
+ return 'shouldProceedNextCheck';
78
+ });
79
+ };
80
+
81
+ /**
82
+ * @type {(content:string,
83
+ * validExportName:string,
84
+ * ...options:Parameters<correctBeforeParse>)=>
85
+ * ReturnType<typeof TryAsync<RetType>>}
86
+ */
87
+ const checkIsClass = async (content, validExportName, path, encoding) => {
88
+ return await TryAsync(
89
+ /**
90
+ * @returns {Promise<RetType>}
91
+ */
92
+ async () => {
93
+ const isAClassRegex = new RegExp(`export\\s+class\\s+${validExportName}`, 'g');
94
+ if (isAClassRegex.test(content) === false) {
95
+ return 'shouldProceedNextCheck';
96
+ }
97
+ let rewrite = false;
98
+ const jsdocCommentBlockRegex = /\/\*\*[\s\S]*?\*\//g;
99
+ content
100
+ .match(jsdocCommentBlockRegex)
101
+ ?.filter((val) => {
102
+ return val.includes('@template');
103
+ })
104
+ .forEach((val) => {
105
+ const check = new RegExp(
106
+ `(?<opening>${LitExp.escape(val).replace(
107
+ /\s+/g,
108
+ `\\\s*`
109
+ )}\\\s*?(?:static\\\s+|))(?<funcname>(?:\#|)[a-zA-Z0-9]*)\\\s*?\\\=\\\s*?(?<async_>async\\\s*?|)\\\((?<parameters>[\\\s\\\S]*?)\\\)\\\s*?=>`,
110
+ 'g'
111
+ );
112
+ const [checkContent] = content.matchAll(check).toArray();
113
+ if (checkContent === undefined) {
114
+ return;
115
+ }
116
+ const fullCaptured = checkContent[0];
117
+ const grouped = checkContent.groups;
118
+ if (grouped === undefined) {
119
+ return;
120
+ }
121
+ const { opening, funcname, async_, parameters } = grouped;
122
+ rewrite = true;
123
+ const modifiedFuncDeclaration = `${opening}${
124
+ async_ ? 'async ' : ''
125
+ }${funcname}(${parameters})`;
126
+ content = content.replace(fullCaptured, modifiedFuncDeclaration);
127
+ });
128
+ if (rewrite) {
129
+ await Timeout(100); // to wait for pretify on autoSave;
130
+ await writeFile(path, content, { encoding });
131
+ Console.info({
132
+ vivthJSAutoDoc: `successfully modify '${path}' class/instance method(s), that has generic template, for correct type emition;`,
133
+ });
134
+ return 'waitForRewrite';
135
+ }
136
+ return 'shouldProceedNextCheck';
137
+ }
138
+ );
139
+ };