@yannick-z/modulo 0.2.0 → 0.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 (55) hide show
  1. package/.vscode/extensions.json +3 -0
  2. package/README.md +57 -60
  3. package/bin/modulo.js +29 -19
  4. package/biome.json +34 -0
  5. package/package.json +36 -35
  6. package/rslib.config.ts +19 -19
  7. package/src/args/get-framework-name.ts +7 -7
  8. package/src/args/index.ts +16 -74
  9. package/src/args/preset.ts +16 -16
  10. package/src/cli/init.ts +10 -6
  11. package/src/cli/pack-code.ts +20 -9
  12. package/src/config/example/example-config.ts +50 -39
  13. package/src/config/example/example-externals.ts +21 -37
  14. package/src/config/index.ts +186 -16
  15. package/src/config/presets.ts +100 -0
  16. package/src/config/type.ts +34 -12
  17. package/src/index.ts +104 -10
  18. package/src/initiator/create-config-file.ts +52 -36
  19. package/src/initiator/create-project.ts +249 -0
  20. package/src/initiator/modify-scripts.ts +42 -42
  21. package/src/packer/auto-external-plugin.ts +205 -0
  22. package/src/packer/collect-modules.ts +58 -69
  23. package/src/packer/get-externals-and-tags.ts +82 -55
  24. package/src/packer/lib.ts +78 -70
  25. package/src/packer/page.ts +105 -82
  26. package/src/packer/prepare.ts +63 -57
  27. package/src/tools/cli.ts +21 -0
  28. package/src/tools/file.ts +84 -14
  29. package/src/tools/find-path-root.ts +52 -25
  30. package/src/tools/get-framework-name.ts +7 -7
  31. package/src/tools/get-ui-plugin.ts +27 -13
  32. package/src/tools/json.ts +63 -9
  33. package/src/tools/log.ts +58 -0
  34. package/src/tools/merge-user-config.ts +17 -17
  35. package/src/tools/omit-root-path.ts +17 -7
  36. package/src/tools/panic.ts +12 -8
  37. package/src/tools/string.ts +13 -2
  38. package/src/type/guard.ts +22 -3
  39. package/tsconfig.json +9 -9
  40. package/dist/index.js +0 -773
  41. package/src/args/cmd.ts +0 -16
  42. package/src/args/mode.ts +0 -38
  43. package/src/args/node_env.ts +0 -15
  44. package/src/args/target.ts +0 -44
  45. package/src/config/externals.ts +0 -70
  46. package/src/config/generate_config.ts +0 -105
  47. package/src/config/preset/alias.ts +0 -3
  48. package/src/config/preset/dev-server.ts +0 -6
  49. package/src/config/preset/dirs.ts +0 -12
  50. package/src/config/preset/html.ts +0 -19
  51. package/src/config/preset/index.ts +0 -23
  52. package/src/config/preset/libs.ts +0 -5
  53. package/src/config/preset/minify.ts +0 -24
  54. package/src/config/preset/url.ts +0 -4
  55. package/src/tools/debug-log.ts +0 -37
package/src/tools/json.ts CHANGED
@@ -1,11 +1,65 @@
1
- import picocolors from 'picocolors';
1
+ import picocolors from "picocolors";
2
+ import { readFileSync, writeFileSync } from "node:fs";
2
3
 
3
- export function jsonparse<T>(input: string) {
4
- try {
5
- if (input) {
6
- return JSON.parse(input) as T;
7
- }
8
- } catch (e) {
9
- console.error(picocolors.red(`JSON.parse failed\n${e}`));
10
- }
4
+ /**
5
+ * 解析 JSON 字符串
6
+ * @param input JSON 字符串
7
+ * @param defaultValue 解析失败或输入为空时的默认值(可选)
8
+ * @returns 解析后的对象或默认值
9
+ */
10
+ export function jsonparse<T>(input: string, defaultValue?: T): T | undefined {
11
+ try {
12
+ if (input) {
13
+ return JSON.parse(input) as T;
14
+ }
15
+ return defaultValue;
16
+ } catch (e) {
17
+ console.error(picocolors.red(`JSON.parse failed\n${e}`));
18
+ return defaultValue;
19
+ }
20
+ }
21
+
22
+ /**
23
+ * 更新 JSON 文件
24
+ * @param path 文件路径
25
+ * @param updater 更新函数,接收当前数据并返回新数据
26
+ * @param createIfNotExist 如果文件不存在是否创建(默认 false)
27
+ * @returns 是否更新成功
28
+ */
29
+ export function update_json_file<T = any>(
30
+ path: string,
31
+ updater: (data: T) => T,
32
+ createIfNotExist = false,
33
+ ): boolean {
34
+ try {
35
+ let data: T;
36
+ try {
37
+ const content = readFileSync(path, "utf-8");
38
+ const parsed = jsonparse<T>(content);
39
+ if (!parsed) {
40
+ if (createIfNotExist) {
41
+ data = {} as T;
42
+ } else {
43
+ console.error(picocolors.red(`Failed to parse JSON file: ${path}`));
44
+ return false;
45
+ }
46
+ } else {
47
+ data = parsed;
48
+ }
49
+ } catch (error: any) {
50
+ if (error.code === "ENOENT" && createIfNotExist) {
51
+ data = {} as T;
52
+ } else {
53
+ console.error(picocolors.red(`Failed to read file: ${path}`));
54
+ return false;
55
+ }
56
+ }
57
+
58
+ const newData = updater(data);
59
+ writeFileSync(path, JSON.stringify(newData, null, 2) + "\n");
60
+ return true;
61
+ } catch (e) {
62
+ console.error(picocolors.red(`Failed to update JSON file: ${path}\n${e}`));
63
+ return false;
64
+ }
11
65
  }
@@ -0,0 +1,58 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import picocolors from "picocolors";
4
+
5
+ const logFile = path.join(process.cwd(), "modulo.debug.log");
6
+ let index = 0;
7
+
8
+ /**
9
+ * 调试日志函数
10
+ *
11
+ * 仅在 process.env.DEBUG 开启或 CLI 参数包含 --debug/--verbose 时输出。
12
+ * - --verbose: 直接输出到控制台
13
+ * - --debug: 输出日志文件到当前目录的 modulo.debug.log,并在控制台打印序号
14
+ *
15
+ * @param hint 日志标题或提示信息
16
+ * @param params 日志内容(可以是任意对象)
17
+ */
18
+ export function debug_log(hint: string, ...params: unknown[]) {
19
+ const argv_debug = process.env.DEBUG || process.argv.includes("--debug");
20
+ const argv_verbose =
21
+ process.argv.includes("--verbose") || process.argv.includes("-v");
22
+
23
+ if (!argv_debug && !argv_verbose) {
24
+ return;
25
+ }
26
+
27
+ const timestamp = new Date().toISOString();
28
+ const sn = String(index++).padStart(3, "0");
29
+ const logEntry = `--------------\n${sn} [${timestamp}] ${hint}\n${params
30
+ .map((p: unknown) =>
31
+ typeof p === "object" ? JSON.stringify(p, null, 2) : String(p),
32
+ )
33
+ .join("\n")}\n---------------\n\n`;
34
+
35
+ if (argv_verbose) {
36
+ console.log(logEntry);
37
+ }
38
+
39
+ if (argv_debug) {
40
+ // 打印序列号方便debug
41
+ console.log(picocolors.blue(`\ndebug log ${sn}`));
42
+
43
+ fs.appendFileSync(logFile, logEntry);
44
+ }
45
+ }
46
+
47
+ // Logger utility
48
+ export const logger = {
49
+ info: (msg: string) => console.log(picocolors.cyan(msg)),
50
+ success: (msg: string) => console.log(picocolors.green(msg)),
51
+ warn: (msg: string) => console.log(picocolors.yellow(msg)),
52
+ error: (msg: string) => console.log(picocolors.red(msg)),
53
+ debug: (msg: string) => {
54
+ if (process.env.DEBUG) {
55
+ console.log(picocolors.gray(`[DEBUG] ${msg}`));
56
+ }
57
+ },
58
+ };
@@ -1,21 +1,21 @@
1
1
  import { PANIC_IF } from "./panic.ts";
2
2
 
3
3
  export function merge_user_config(target: any, input: any) {
4
- for (const key in input) {
5
- const from = input[key];
6
- const to = target[key];
7
- if (typeof from !== typeof to || !(key in target)) {
8
- target[key] = from;
9
- continue;
10
- } else {
11
- if (Array.isArray(to)) {
12
- PANIC_IF(!Array.isArray(from));
13
- target[key] = [...to, ...from];
14
- } else if (typeof to === "object") {
15
- merge_user_config(to, from);
16
- } else {
17
- target[key] = from;
18
- }
19
- }
20
- }
4
+ for (const key in input) {
5
+ const from = input[key];
6
+ const to = target[key];
7
+ if (typeof from !== typeof to || !(key in target)) {
8
+ target[key] = from;
9
+ continue;
10
+ } else {
11
+ if (Array.isArray(to)) {
12
+ PANIC_IF(!Array.isArray(from));
13
+ target[key] = [...to, ...from];
14
+ } else if (typeof to === "object") {
15
+ merge_user_config(to, from);
16
+ } else {
17
+ target[key] = from;
18
+ }
19
+ }
20
+ }
21
21
  }
@@ -1,11 +1,21 @@
1
- const root_path = process.cwd();
2
- export function omit_root_path(path: string) {
3
- // return relative path
4
- return path.replace(root_path, "");
1
+ import { relative } from "node:path";
2
+
3
+ /**
4
+ * 获取相对于根目录的路径
5
+ * @param path 绝对路径
6
+ * @returns 相对路径(以 / 开头)
7
+ */
8
+ export function omit_root_path(path: string): string {
9
+ const rel = relative(process.cwd(), path);
10
+ // 保持输出格式一致,添加前导 /
11
+ return rel.startsWith("/") ? rel : `/${rel}`;
5
12
  }
6
13
 
14
+ /**
15
+ * 批量处理入口文件路径
16
+ */
7
17
  export function omit_root_path_for_entries(entries: Record<string, string>) {
8
- return Object.fromEntries(
9
- Object.entries(entries).map(([key, value]) => [key, omit_root_path(value)])
10
- );
18
+ return Object.fromEntries(
19
+ Object.entries(entries).map(([key, value]) => [key, omit_root_path(value)]),
20
+ );
11
21
  }
@@ -1,11 +1,15 @@
1
- import { exit } from 'node:process';
2
- import pc from 'picocolors';
1
+ import { exit } from "node:process";
2
+ import pc from "picocolors";
3
3
 
4
- const alert = '! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !';
4
+ const alert = "! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !";
5
5
 
6
- export function PANIC_IF(status = false, msg = `SOMETHING'S WRONG`, halt = true): asserts status is false {
7
- if (status) {
8
- console.log(pc.bgRed(pc.white(`\n${alert}\n\n${msg}\n\n${alert}`)), '\n');
9
- halt && exit(1);
10
- }
6
+ export function PANIC_IF(
7
+ status = false,
8
+ msg = `SOMETHING'S WRONG`,
9
+ halt = true,
10
+ ): asserts status is false {
11
+ if (status) {
12
+ console.log(pc.bgRed(pc.white(`\n${alert}\n\n${msg}\n\n${alert}`)), "\n");
13
+ halt && exit(1);
14
+ }
11
15
  }
@@ -1,3 +1,14 @@
1
- export function first_letter_upper(str: string) {
2
- return `${str[0].toUpperCase()}${str.slice(1).toLowerCase()}`;
1
+ /**
2
+ * 首字母大写,其余小写
3
+ * @param str 输入字符串
4
+ * @returns 格式化后的字符串
5
+ */
6
+ export function capitalize(str: string): string {
7
+ if (!str) return "";
8
+ return `${str.charAt(0).toUpperCase()}${str.slice(1).toLowerCase()}`;
3
9
  }
10
+
11
+ /**
12
+ * @deprecated Use capitalize instead
13
+ */
14
+ export const first_letter_upper = capitalize;
package/src/type/guard.ts CHANGED
@@ -1,11 +1,30 @@
1
+ import {
2
+ type EnvExternalUrl,
3
+ type ConfigExternalUrl,
4
+ type ImportExternal,
5
+ } from "../config/type.ts";
6
+
1
7
  export function is_string(data: unknown): data is string {
2
- return typeof data === "string";
8
+ return typeof data === "string";
3
9
  }
4
10
 
5
11
  export function is_true_string(data: unknown): data is string {
6
- return typeof data === "string" && !!data;
12
+ return typeof data === "string" && !!data;
7
13
  }
8
14
 
9
15
  export function is_record(data: unknown): data is Record<string, unknown> {
10
- return !!data && typeof data === "object";
16
+ return !!data && typeof data === "object";
17
+ }
18
+
19
+ export function is_env_external(data: unknown): data is EnvExternalUrl {
20
+ return is_record(data) && is_string(data.dev) && is_string(data.prd);
21
+ }
22
+
23
+ export function is_url_config(data: unknown): data is ConfigExternalUrl {
24
+ return is_env_external(data) || is_string(data);
25
+ }
26
+
27
+ export function is_import_external(data: unknown): data is ImportExternal {
28
+ // 没有global的一概作为import的依赖
29
+ return is_record(data) && is_url_config(data.url) && !is_string(data.global);
11
30
  }
package/tsconfig.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
- "compilerOptions": {
3
- "allowImportingTsExtensions": true,
4
- "allowJs": true,
5
- "noEmit": true,
6
- "outDir": "./dist",
7
- "rootDir": "./"
8
- },
9
- "extends": "../../tsconfig.base.json",
10
- "include": ["./src/**/*", "./rslib.config.ts", "./types/*"]
2
+ "compilerOptions": {
3
+ "allowImportingTsExtensions": true,
4
+ "allowJs": true,
5
+ "noEmit": true,
6
+ "outDir": "./dist",
7
+ "rootDir": "./"
8
+ },
9
+ "extends": "../../tsconfig.base.json",
10
+ "include": ["./src/**/*", "./rslib.config.ts", "./types/*"]
11
11
  }