vivth 1.2.2 → 1.3.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.
Files changed (71) hide show
  1. package/README.md +225 -199
  2. package/README.src.md +5 -1
  3. package/bun.lock +6 -0
  4. package/index.mjs +5 -1
  5. package/package.json +3 -1
  6. package/src/bundler/CompileJS.mjs +21 -11
  7. package/src/bundler/EsBundler.mjs +4 -3
  8. package/src/bundler/FSInline.mjs +3 -0
  9. package/src/bundler/FSInlineAnalyzer.mjs +31 -9
  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 +99 -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 +2 -1
  45. package/types/src/bundler/CompileJS.d.mts +22 -11
  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 +33 -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,24 @@ 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;
49
53
  */
50
54
  export class JSautoDOC {
51
55
  /**
52
- * @type {JSautoDOC}
56
+ * @type {JSautoDOC|undefined}
53
57
  */
54
- static #instance = undefined;
58
+ static #instance;
55
59
  /**
56
60
  * @description
57
61
  * @param {Object} [options]
58
62
  * @param {Object} [options.paths]
59
- * @param {string} [options.paths.file]
63
+ * @param {string} options.paths.file
60
64
  * - entry point;
61
- * @param {string} [options.paths.readMe]
65
+ * @param {string} options.paths.readMe
62
66
  * - readme target;
63
- * @param {string} [options.paths.dir]
67
+ * @param {string} options.paths.dir
64
68
  * - source directory;
65
69
  * @param {string} [options.copyright]
66
70
  * @param {string} [options.tableOfContentTitle]
@@ -90,19 +94,22 @@ export class JSautoDOC {
90
94
  this.#paths = paths;
91
95
  this.#copyright = copyright;
92
96
  const rootPath = Paths.root;
97
+ if (rootPath === undefined) {
98
+ return;
99
+ }
93
100
  const watchpath = join(rootPath, this.#paths.dir);
94
101
  const watcher = chokidar.watch(watchpath, option);
95
102
  const watcherReadme = chokidar.watch(join(rootPath, readmesrcname), option);
96
103
  /**
97
- * @type {(eventName: 'add'|'change'|'unlink', path: string, stats?: import('fs').Stats) => void}
104
+ * @type {(eventName: import('chokidar/handler.js').EventName, path: string, stats?: import('fs').Stats) => void}
98
105
  */
99
106
  const listener = (eventName, path, stats) => {
100
107
  const ext = extname(path);
101
108
  if (
102
- !acceptableExt.has(
109
+ acceptableExt.has(
103
110
  // @ts-expect-error
104
111
  ext
105
- )
112
+ ) === false
106
113
  ) {
107
114
  return;
108
115
  }
@@ -124,44 +131,47 @@ export class JSautoDOC {
124
131
  };
125
132
  watcher.on('all', listener);
126
133
  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
- });
134
+ if (SafeExit.instance) {
135
+ SafeExit.instance.addCallback(async () => {
136
+ watcher.close();
137
+ watcherReadme.close();
138
+ watcher.removeAllListeners();
139
+ watcherReadme.removeAllListeners();
140
+ watcher.removeListener('all', listener);
141
+ watcherReadme.removeAllListeners(this.#readMeListener);
142
+ watcherReadme.removeListener('all', this.#readMeListener);
143
+ });
144
+ }
136
145
  }
137
146
  /**
138
- * @type {string}
147
+ * @type {string|undefined}
139
148
  */
140
- #copyright = undefined;
149
+ #copyright;
141
150
  /**
142
151
  * @type {{
143
- * file?: string;
144
- * readMe?: string;
145
- * dir?: string;
146
- * }}
152
+ * file: string;
153
+ * readMe: string;
154
+ * dir: string;
155
+ * }|undefined}
147
156
  */
148
157
  #paths;
149
158
  /**
150
- * @type {string}
159
+ * @type {string|undefined}
151
160
  */
152
161
  #tableOfContentTitle;
153
162
  /**
154
- * @param {string} _eventName
163
+ * @param {import('chokidar/handler.js').EventName} _eventName
155
164
  * @param {string} path_
156
- * @param {Stats} stats
157
- * @returns {Promise<void>}
165
+ * @param {Stats|undefined} stats
166
+ * @returns {void}
158
167
  */
159
- #readMeListener = async (_eventName, path_, stats) => {
160
- if (!stats.isFile()) {
168
+ #readMeListener = (_eventName, path_, stats) => {
169
+ if (stats && stats.isFile() === false) {
161
170
  return;
162
171
  }
163
- const content = await readFile(path_, { encoding });
164
- this.#readMESRCContent.value = content;
172
+ readFile(path_, { encoding }).then((content) => {
173
+ this.#readMESRCContent.value = content;
174
+ });
165
175
  };
166
176
  /**
167
177
  * @type {Signal<Set<string>>}
@@ -170,32 +180,37 @@ export class JSautoDOC {
170
180
  /**
171
181
  * @type {Signal<string>}
172
182
  */
173
- #readMESRCContent = LazyFactory(() => new Signal(undefined));
183
+ #readMESRCContent = LazyFactory(() => new Signal(''));
174
184
  #generatedREADME_md = new Effect(async ({ subscribe, isLastCalled }) => {
175
185
  const contentSRC = subscribe(this.#readMESRCContent).value;
176
186
  const filepaths = subscribe(this.#filePaths).value;
177
- if (!(await isLastCalled(100)) || !contentSRC || !filepaths) {
187
+ if ((await isLastCalled(100)) === false || !contentSRC || !filepaths) {
178
188
  return;
179
189
  }
180
- const { readme, mjsFile } = await this.#generateFromSRC(contentSRC, filepaths);
181
- if (!(await isLastCalled())) {
190
+ const res = await this.#generateFromSRC(contentSRC, filepaths);
191
+ if ((await isLastCalled()) === false || res === undefined) {
182
192
  return;
183
193
  }
184
- const readmePath = join(Paths.root, this.#paths.readMe);
185
- const mjsFilePath = join(Paths.root, this.#paths.file);
194
+ const { readme, mjsFile } = res;
195
+ const rootPath = Paths.root;
196
+ if (rootPath === undefined || this.#paths === undefined) {
197
+ return;
198
+ }
199
+ const readmePath = join(rootPath, this.#paths.readMe);
200
+ const mjsFilePath = join(rootPath, this.#paths.file);
186
201
  const [[, errorWriteReadme], [, errorWriteMjsFile]] = await Promise.all([
187
202
  FileSafe.write(readmePath, readme, { encoding }),
188
203
  FileSafe.write(mjsFilePath, mjsFile, { encoding }),
189
204
  ]);
190
- if (!errorWriteReadme) {
191
- Console.info({ message: `successfully generate: '${readmePath}'` });
205
+ if (errorWriteReadme === undefined) {
206
+ Console.info({ vivthJSautoDOC: `successfully generate '${readmePath}'` });
192
207
  } else {
193
- Console.error({ message: `unable to generate: '${readmePath}';`, errorWriteReadme });
208
+ Console.error({ vivthJSautoDOC: `unable to generate '${readmePath}'`, errorWriteReadme });
194
209
  }
195
- if (!errorWriteMjsFile) {
196
- Console.info({ message: `successfully generate: '${mjsFilePath}'` });
210
+ if (errorWriteMjsFile === undefined) {
211
+ Console.info({ vivthJSautoDOC: `successfully generate '${mjsFilePath}'` });
197
212
  } else {
198
- Console.error({ message: `unable to generate: '${mjsFilePath}';`, errorWriteMjsFile });
213
+ Console.error({ vivthJSautoDOC: `unable to generate '${mjsFilePath}'`, errorWriteMjsFile });
199
214
  }
200
215
  });
201
216
  /**
@@ -216,9 +231,12 @@ export class JSautoDOC {
216
231
  /**
217
232
  * @param {string} contentSRC
218
233
  * @param {Set<string>} filepaths
219
- * @returns {Promise<generatedFromSRC>}
234
+ * @returns {Promise<generatedFromSRC|undefined>}
220
235
  */
221
236
  #generateFromSRC = async (contentSRC, filepaths) => {
237
+ if (this.#tableOfContentTitle === undefined || this.#copyright === undefined) {
238
+ return;
239
+ }
222
240
  const tableID = this.#tableOfContentTitle.replace(/\s+/g, '-').toLowerCase();
223
241
  const tableOfContent = [];
224
242
  const apiDocuments = [];
@@ -232,7 +250,12 @@ export class JSautoDOC {
232
250
  path: { relative: relativePath },
233
251
  baseName: { noExt },
234
252
  } = (await this.#parsedFilesRef.get(path_)).value;
235
- const hasNoAutoDoc = /\/\*\*[\s\*]*?@noautodoc[\s\*]*?\*\//.test(await content.string());
253
+ const trueContent = await content.string();
254
+ if (trueContent === undefined) {
255
+ return;
256
+ }
257
+
258
+ const hasNoAutoDoc = /\/\*\*[\s\*]*?@noautodoc[\s\*]*?\*\//.test(trueContent);
236
259
  if (hasValidExportObject) {
237
260
  mjsMain.push(
238
261
  `export { ${noExt} } from '${
@@ -240,6 +263,9 @@ export class JSautoDOC {
240
263
  }';`
241
264
  );
242
265
  }
266
+ /**
267
+ * @type {string[]}
268
+ */
243
269
  const currentDescription = [];
244
270
  const { readme, typedef } = documented;
245
271
  const [typedefString, error] = await TryAsync(async () => {
@@ -249,9 +275,9 @@ export class JSautoDOC {
249
275
  const result = await typedef();
250
276
  return result;
251
277
  });
252
- if (!error && typedefString) {
278
+ if (error === undefined && typedefString) {
253
279
  mjsTypes.push(typedefString.module);
254
- if (!hasNoAutoDoc) {
280
+ if (hasNoAutoDoc === false) {
255
281
  const nameVarID = noExt.toLowerCase();
256
282
  tableOfContent.push(`[${noExt}](#${nameVarID})`);
257
283
  apiDocuments.push(
@@ -261,7 +287,7 @@ export class JSautoDOC {
261
287
  );
262
288
  }
263
289
  }
264
- if (!hasNoAutoDoc && hasValidExportObject) {
290
+ if (hasNoAutoDoc === false && hasValidExportObject) {
265
291
  readme.forEach((ref) => {
266
292
  const {
267
293
  // fullDescription,
@@ -325,19 +351,30 @@ export class JSautoDOC {
325
351
  */
326
352
  #addHandler = (eventName, path__, _stats) => {
327
353
  TryAsync(async () => {
328
- const dispatch = await this.#parsedFilesRef.get(path__);
329
- if (!(await stat(path__)).isFile()) {
354
+ if (
355
+ //
356
+ !_stats?.isFile()
357
+ ) {
330
358
  return;
331
359
  }
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
- });
360
+ const res = await correctBeforeParse(path__, 'utf-8');
361
+ switch (res) {
362
+ case 'shouldProceedNextCheck':
363
+ const dispatch = await this.#parsedFilesRef.get(path__);
364
+ dispatch.value = new parsedFile(path__, _stats, encoding);
365
+ this.#filePaths.subscribers.notify(async ({ signalInstance }) => {
366
+ await dispatch.value.parse();
367
+ dispatch.subscribers.notify();
368
+ Console.info({ vivthJSautoDOC: `${eventName} '${path__}' to export and doc` });
369
+ signalInstance.value.add(path__);
370
+ });
371
+ break;
372
+ case 'waitForRewrite':
373
+ case 'doNotProcess':
374
+ break;
375
+ }
339
376
  }).then(([, error]) => {
340
- if (!error) {
377
+ if (error === undefined) {
341
378
  return;
342
379
  }
343
380
  Console.error(error);
@@ -348,12 +385,12 @@ export class JSautoDOC {
348
385
  */
349
386
  #removeHandler = (eventName, path__, _stats) => {
350
387
  TryAsync(async () => {
351
- if (!(await stat(path__)).isFile()) {
388
+ if (_stats?.isFile()) {
352
389
  return true;
353
390
  }
354
391
  return false;
355
- }).then(([res, error]) => {
356
- if (res && !error) {
392
+ }).then(([, error]) => {
393
+ if (error === undefined) {
357
394
  return;
358
395
  }
359
396
  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
+ };