@yannick-z/modulo 0.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.
Files changed (49) hide show
  1. package/README.md +311 -0
  2. package/bin/modulo.js +22 -0
  3. package/dist/index.js +773 -0
  4. package/package.json +36 -0
  5. package/rslib.config.ts +22 -0
  6. package/src/args/cmd.ts +16 -0
  7. package/src/args/get-framework-name.ts +19 -0
  8. package/src/args/index.ts +80 -0
  9. package/src/args/mode.ts +38 -0
  10. package/src/args/node_env.ts +15 -0
  11. package/src/args/preset.ts +26 -0
  12. package/src/args/target.ts +44 -0
  13. package/src/cli/init.ts +12 -0
  14. package/src/cli/pack-code.ts +17 -0
  15. package/src/config/example/example-config.ts +46 -0
  16. package/src/config/example/example-externals.ts +47 -0
  17. package/src/config/externals.ts +70 -0
  18. package/src/config/generate_config.ts +105 -0
  19. package/src/config/index.ts +27 -0
  20. package/src/config/preset/alias.ts +3 -0
  21. package/src/config/preset/dev-server.ts +6 -0
  22. package/src/config/preset/dirs.ts +12 -0
  23. package/src/config/preset/html.ts +19 -0
  24. package/src/config/preset/index.ts +23 -0
  25. package/src/config/preset/libs.ts +5 -0
  26. package/src/config/preset/minify.ts +24 -0
  27. package/src/config/preset/url.ts +4 -0
  28. package/src/config/type.ts +17 -0
  29. package/src/index.ts +13 -0
  30. package/src/initiator/create-config-file.ts +46 -0
  31. package/src/initiator/modify-scripts.ts +52 -0
  32. package/src/packer/collect-modules.ts +77 -0
  33. package/src/packer/get-externals-and-tags.ts +65 -0
  34. package/src/packer/lib.ts +84 -0
  35. package/src/packer/page.ts +98 -0
  36. package/src/packer/prepare.ts +76 -0
  37. package/src/tools/debug-log.ts +37 -0
  38. package/src/tools/file.ts +19 -0
  39. package/src/tools/find-path-root.ts +29 -0
  40. package/src/tools/get-framework-name.ts +14 -0
  41. package/src/tools/get-ui-plugin.ts +23 -0
  42. package/src/tools/json.ts +11 -0
  43. package/src/tools/merge-user-config.ts +21 -0
  44. package/src/tools/omit-root-path.ts +11 -0
  45. package/src/tools/panic.ts +11 -0
  46. package/src/tools/string.ts +3 -0
  47. package/src/type/guard.ts +11 -0
  48. package/template/index.html +12 -0
  49. package/tsconfig.json +11 -0
@@ -0,0 +1,23 @@
1
+ import type { OutputConfig } from "@rsbuild/core";
2
+ import { preset_alias } from "./alias.ts";
3
+ import { preset_dev_server_config } from "./dev-server.ts";
4
+ import { preset_input_dirs, preset_output_dirs } from "./dirs.ts";
5
+ import { default_html_config } from "./html.ts";
6
+ import { preset_ui_libs } from "./libs.ts";
7
+ import { preset_minify_config } from "./minify.ts";
8
+ import { preset_url_config } from "./url.ts";
9
+ import type { ExternalLibs } from "../externals.ts";
10
+
11
+ export const preset_config = {
12
+ analyze: false, // 是否执行bundleAnalyze
13
+ define: {} as Record<string, string | boolean | number>, // 配置全局变量,用于打包时替换代码中的全局变量
14
+ dev_server: preset_dev_server_config,
15
+ externals: {} as ExternalLibs,
16
+ html: default_html_config,
17
+ input: preset_input_dirs,
18
+ minify: preset_minify_config as OutputConfig["minify"], // 是否压缩产物,同时进行mangle
19
+ output: preset_output_dirs,
20
+ ui_lib: preset_ui_libs,
21
+ url: preset_url_config,
22
+ alias: preset_alias,
23
+ };
@@ -0,0 +1,5 @@
1
+ export const preset_ui_libs = {
2
+ // 对ui库做严格的版本限制,以提高统一程度
3
+ react: '17.0.2',
4
+ vue: '2.7.16',
5
+ };
@@ -0,0 +1,24 @@
1
+ import type { OutputConfig } from "@rsbuild/core";
2
+
3
+ export const preset_minify_config: OutputConfig["minify"] = {
4
+ js: true,
5
+ jsOptions: {
6
+ minimizerOptions: {
7
+ compress: {
8
+ dead_code: true,
9
+ defaults: false,
10
+ toplevel: true,
11
+ unused: true,
12
+ },
13
+ format: {
14
+ comments: "some",
15
+ ecma: 2015,
16
+ preserve_annotations: true,
17
+ safari10: true,
18
+ semicolons: false,
19
+ },
20
+ mangle: true,
21
+ minify: true,
22
+ },
23
+ },
24
+ };
@@ -0,0 +1,4 @@
1
+ export const preset_url_config = {
2
+ base: '/', // 前缀路径
3
+ cdn: '', // 可以将除了html资源以外的资源都部署到cdn上,比如https://cdn.host.com/,或者https://cdn.host.com/cdn-path/
4
+ };
@@ -0,0 +1,17 @@
1
+ import type { preset_config } from "./preset/index.ts";
2
+
3
+ export type GLOBAL_CONFIG = typeof preset_config;
4
+
5
+ export interface USER_CONFIG {
6
+ analyze?: boolean;
7
+ define?: GLOBAL_CONFIG["define"];
8
+ dev_server?: Partial<GLOBAL_CONFIG["dev_server"]>;
9
+ externals?: GLOBAL_CONFIG["externals"];
10
+ html?: Partial<GLOBAL_CONFIG["html"]>;
11
+ input?: Partial<GLOBAL_CONFIG["input"]>;
12
+ minify?: Partial<GLOBAL_CONFIG["minify"]> | boolean;
13
+ output?: Partial<GLOBAL_CONFIG["output"]>;
14
+ ui_lib?: Partial<GLOBAL_CONFIG["ui_lib"]>;
15
+ alias?: GLOBAL_CONFIG["alias"];
16
+ url?: Partial<GLOBAL_CONFIG["url"]>;
17
+ }
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { get_args } from "./args/index.ts";
2
+ import { init_tool } from "./cli/init.ts";
3
+ import { pack_code } from "./cli/pack-code.ts";
4
+
5
+ export function exec() {
6
+ const args = get_args();
7
+
8
+ if (args.cmd === "init") {
9
+ init_tool(args);
10
+ } else if (args.cmd === "build" || args.cmd === "dev") {
11
+ pack_code(args);
12
+ }
13
+ }
@@ -0,0 +1,46 @@
1
+ // 在当前目录创建一份json配置文件
2
+ import { existsSync, writeFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import readline from "node:readline";
5
+ import picocolors from "picocolors";
6
+ import type { ModuloArgs_Init } from "../args/index.ts";
7
+ import {
8
+ default_config_file_name,
9
+ get_example_config,
10
+ } from "../config/example/example-config.ts";
11
+
12
+ export async function create_config_file(args: ModuloArgs_Init) {
13
+ const path = args.init.path || default_config_file_name;
14
+ console.log(picocolors.blue("即将创建配置文件"), path);
15
+
16
+ const filepath = resolve(process.cwd(), path);
17
+
18
+ if (existsSync(filepath)) {
19
+ if (args.init.force) {
20
+ console.log(picocolors.bgRed(picocolors.white("配置文件已存在,将覆盖")));
21
+ } else {
22
+ console.log(picocolors.red("配置文件已存在,是否覆盖?"));
23
+ const rl = readline.createInterface({
24
+ input: process.stdin,
25
+ output: process.stdout,
26
+ });
27
+ const answer = await new Promise<string>((resolve) => {
28
+ rl.question("\n请输入(Y/N) ", (answer) => {
29
+ rl.close();
30
+ resolve(answer);
31
+ });
32
+ });
33
+ if (answer.toLowerCase() !== "y") {
34
+ console.log("取消创建");
35
+ return;
36
+ }
37
+ }
38
+ }
39
+
40
+ writeFileSync(
41
+ filepath,
42
+ JSON.stringify(get_example_config(args.init.preset), null, 2)
43
+ );
44
+
45
+ console.log(picocolors.green("创建成功"), filepath);
46
+ }
@@ -0,0 +1,52 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import readline from "node:readline";
4
+ import picocolors from "picocolors";
5
+ import { get_packagejson } from "../config/index.ts";
6
+
7
+ export const star_line = "**********************";
8
+
9
+ export async function modify_scripts() {
10
+ const packagejson = get_packagejson();
11
+ const new_scripts = {
12
+ ...(packagejson.scripts || {}),
13
+ "build:page": "modulo build page",
14
+ "build:module": "modulo build module",
15
+ "build:all": "modulo build all",
16
+ build: "modulo build all",
17
+ "dev:page": "modulo dev page",
18
+ "dev:module": "modulo dev module",
19
+ "watch:page": "modulo build page --watch=true",
20
+ "watch:module": "modulo build module --watch=true",
21
+ };
22
+ console.log(
23
+ picocolors.magentaBright(
24
+ `\n${star_line}修改package.json中的scripts\n新的内容修改后如下:\n${JSON.stringify(
25
+ new_scripts,
26
+ null,
27
+ 2
28
+ )}\n${star_line}`
29
+ )
30
+ );
31
+
32
+ // 确定是否修改package.json
33
+ const rl = readline.createInterface({
34
+ input: process.stdin,
35
+ output: process.stdout,
36
+ });
37
+ const answer = await new Promise<string>((resolve) => {
38
+ rl.question("\n确定修改吗?请输入(Y/N) ", (answer) => {
39
+ rl.close();
40
+ resolve(answer);
41
+ });
42
+ });
43
+ if (answer.toLowerCase() !== "y") {
44
+ console.log("取消修改");
45
+ return;
46
+ }
47
+
48
+ packagejson.scripts = new_scripts;
49
+ const new_packagejson = JSON.stringify(packagejson, null, 2);
50
+ writeFileSync(resolve(process.cwd(), "package.json"), new_packagejson);
51
+ console.log(picocolors.green(`\npackage.json修改成功`));
52
+ }
@@ -0,0 +1,77 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import picocolors from "picocolors";
4
+ import { get_global_config } from "../config/index.ts";
5
+ import { debug_log } from "../tools/debug-log.ts";
6
+ import { get_framework_name } from "../tools/get-framework-name.ts";
7
+ import type { ModuloArgs_Pack } from "../args/index.ts";
8
+
9
+ export function collect_modules(
10
+ args: ModuloArgs_Pack,
11
+ kind: "page" | "module"
12
+ ) {
13
+ const global_config = get_global_config(args);
14
+ const framework_name = get_framework_name();
15
+ const module_path = global_config.input[`${kind}s`];
16
+ const exist = existsSync(module_path);
17
+ debug_log(
18
+ picocolors.blue("check module_path"),
19
+ module_path,
20
+ exist ? "exists" : "NOT exists"
21
+ );
22
+ // 解析目录下的所有模块
23
+ const module_entries = !exist
24
+ ? []
25
+ : readdirSync(module_path, { withFileTypes: true })
26
+ .filter((item) => {
27
+ debug_log(
28
+ "checking module is directory",
29
+ item.name,
30
+ item.isDirectory()
31
+ );
32
+ return item.isDirectory();
33
+ })
34
+ // 每个模块都要求以index或者main或者文件夹同名命名
35
+ .map((dirent) => {
36
+ const dir_path = resolve(module_path, dirent.name);
37
+ const entry_file_path = [
38
+ resolve(dir_path, "index.ts"),
39
+ resolve(dir_path, "index.tsx"),
40
+ resolve(dir_path, "index.js"),
41
+ resolve(dir_path, "index.jsx"),
42
+ resolve(dir_path, "main.ts"),
43
+ resolve(dir_path, "main.tsx"),
44
+ resolve(dir_path, "main.js"),
45
+ resolve(dir_path, "main.jsx"),
46
+ // vue组件常常用vue文件作为入口,因此优先级高于同名ts和tsx文件
47
+ ...(framework_name === "vue"
48
+ ? [
49
+ resolve(dir_path, "index.vue"),
50
+ resolve(dir_path, "main.vue"),
51
+ resolve(dir_path, `${dirent.name}.vue`),
52
+ ]
53
+ : []),
54
+ resolve(dir_path, `${dirent.name}.ts`),
55
+ resolve(dir_path, `${dirent.name}.tsx`),
56
+ resolve(dir_path, `${dirent.name}.js`),
57
+ resolve(dir_path, `${dirent.name}.jsx`),
58
+ ].find((path) => {
59
+ const exists = existsSync(path);
60
+ debug_log(
61
+ "checking entry candidate",
62
+ `${path} ${picocolors[exists ? "green" : "red"](
63
+ `exists - ${exists}`
64
+ )}`
65
+ );
66
+ return exists;
67
+ });
68
+ debug_log("found entry", entry_file_path);
69
+ return [dirent.name, entry_file_path];
70
+ })
71
+ // 没有找到入口的就不管
72
+ .filter((entry): entry is [string, string] => !!entry[1]);
73
+
74
+ return module_entries.length > 0
75
+ ? Object.fromEntries(module_entries)
76
+ : undefined;
77
+ }
@@ -0,0 +1,65 @@
1
+ import type { ModuloArgs_Pack } from "../args/index.ts";
2
+ import {
3
+ is_env_external,
4
+ type ExternalLibs,
5
+ type ImportExternal,
6
+ is_module_typed_external_url,
7
+ type ModuleTypedExternalUrl,
8
+ type ConfigExternalUrl,
9
+ } from "../config/externals.ts";
10
+ import type { Tag } from "../config/preset/html.ts";
11
+ import { is_string } from "../type/guard.ts";
12
+
13
+ function get_external_url(args: ModuloArgs_Pack, url: ConfigExternalUrl) {
14
+ let _url = url;
15
+ while (!is_string(_url)) {
16
+ if (is_env_external(_url)) {
17
+ _url = _url[args.pack.env];
18
+ } else {
19
+ const mode = args.pack.esm ? "esm" : "umd";
20
+ _url = mode in _url ? (_url as any)[mode] : undefined;
21
+ }
22
+ }
23
+ return _url;
24
+ }
25
+
26
+ export function get_externals_importmaps(
27
+ args: ModuloArgs_Pack,
28
+ external_list: ExternalLibs
29
+ ) {
30
+ return Object.entries(external_list).reduce(
31
+ ({ externals, importmaps }, [lib_name, data]) => {
32
+ // 归一化为GlobalExternal或者ImportExternal
33
+ // 此处类型推导有问题,因此手动断言
34
+ const _data = is_env_external(data) ? data[args.pack.env] : data;
35
+ const external_lib =
36
+ typeof _data === "string"
37
+ ? { url: { esm: _data, umd: _data } }
38
+ : is_module_typed_external_url(_data)
39
+ ? { url: _data }
40
+ : _data;
41
+
42
+ // 合并externals,以剔除打包内容
43
+ const _importName = external_lib.importName || lib_name;
44
+ (Array.isArray(_importName) ? _importName : [_importName]).forEach(
45
+ (name) => (externals[name] = lib_name)
46
+ );
47
+
48
+ const url = get_external_url(args, external_lib.url);
49
+ // 如果有external的url
50
+ if (url) {
51
+ // 如果是import的,加入到importmaps
52
+ importmaps[lib_name] = url;
53
+ }
54
+
55
+ return {
56
+ externals,
57
+ importmaps,
58
+ };
59
+ },
60
+ {
61
+ externals: {} as Record<string, string>,
62
+ importmaps: {} as Record<string, string>,
63
+ }
64
+ );
65
+ }
@@ -0,0 +1,84 @@
1
+ import { pluginLess } from "@rsbuild/plugin-less";
2
+ import { build, defineConfig } from "@rslib/core";
3
+ import picocolors from "picocolors";
4
+ import type { ModuloArgs_Pack } from "../args/index.ts";
5
+ import { get_global_config, get_packagejson } from "../config/index.ts";
6
+ import { framework_plugin } from "../tools/get-ui-plugin.ts";
7
+ import { prepare_config } from "./prepare.ts";
8
+
9
+ export async function lib_pack(args: ModuloArgs_Pack) {
10
+ const config = get_global_config(args);
11
+ const packagejson = get_packagejson();
12
+
13
+ const { entries, externals } = prepare_config(args, "module", config);
14
+
15
+ if (!entries) {
16
+ return;
17
+ }
18
+
19
+ const rslibConfig = defineConfig({
20
+ source: {
21
+ define: config.define,
22
+ entry: entries,
23
+ },
24
+ plugins: [framework_plugin(args), pluginLess()],
25
+ resolve: {
26
+ alias: config.alias,
27
+ },
28
+ lib: [
29
+ {
30
+ format: "esm",
31
+ syntax: "esnext",
32
+ dts: false,
33
+ output: {
34
+ assetPrefix: `${config.url.base}/modules`,
35
+ externals,
36
+ distPath: {
37
+ root: config.output.modules,
38
+ js: "esm",
39
+ jsAsync: "esm",
40
+ css: "esm",
41
+ },
42
+ minify: config.minify,
43
+ },
44
+ },
45
+ {
46
+ format: "umd",
47
+ output: {
48
+ assetPrefix: `${config.url.base}/modules`,
49
+ externals,
50
+ distPath: {
51
+ root: config.output.modules,
52
+ js: "umd",
53
+ jsAsync: "umd",
54
+ css: "umd",
55
+ },
56
+ minify: config.minify,
57
+ },
58
+ syntax: "es6",
59
+ umdName: `${packagejson.name}-modules-[name]`,
60
+ },
61
+ ],
62
+ output: {
63
+ legalComments: "none",
64
+ target: "web",
65
+ },
66
+ performance: {
67
+ bundleAnalyze: config.analyze
68
+ ? {
69
+ analyzerMode: "disabled",
70
+ generateStatsFile: true,
71
+ }
72
+ : undefined,
73
+ chunkSplit: {
74
+ strategy: "all-in-one",
75
+ },
76
+ },
77
+ });
78
+
79
+ await build(rslibConfig, { watch: args.cmd === "build" && args.pack.watch });
80
+
81
+ if (args.cmd === "build") {
82
+ console.log(picocolors.green("\n**** 构建【module】完成 ****\n"));
83
+ }
84
+ }
@@ -0,0 +1,98 @@
1
+ import { resolve } from "node:path";
2
+ import { createRsbuild, defineConfig } from "@rsbuild/core";
3
+ import { pluginLess } from "@rsbuild/plugin-less";
4
+ import picocolors from "picocolors";
5
+ import type { ModuloArgs_Pack } from "../args/index.ts";
6
+ import { get_global_config } from "../config/index.ts";
7
+ import { get_package_root } from "../tools/find-path-root.ts";
8
+ import { framework_plugin } from "../tools/get-ui-plugin.ts";
9
+ import { pluginUmd } from "@rsbuild/plugin-umd";
10
+ import { prepare_config } from "./prepare.ts";
11
+
12
+ export async function page_pack(args: ModuloArgs_Pack) {
13
+ const config = get_global_config(args);
14
+
15
+ const { entries, externals, importmaps_tag } = prepare_config(
16
+ args,
17
+ "page",
18
+ config
19
+ );
20
+
21
+ if (!entries) {
22
+ return;
23
+ }
24
+
25
+ const rsbuildConfig = defineConfig({
26
+ source: {
27
+ define: config.define,
28
+ entry: entries,
29
+ },
30
+ plugins: [
31
+ framework_plugin(args),
32
+ pluginLess(),
33
+ pluginUmd({
34
+ name: "modulo-page",
35
+ }),
36
+ ],
37
+ tools: {
38
+ rspack: {
39
+ experiments: {
40
+ outputModule: args.pack.esm,
41
+ },
42
+ },
43
+ htmlPlugin: true,
44
+ },
45
+ output: {
46
+ assetPrefix: config.url.cdn || config.url.base,
47
+ distPath: {
48
+ root: config.output.dist,
49
+ },
50
+ externals,
51
+ filenameHash: config.output.filenameHash,
52
+ legalComments: "none",
53
+ minify: config.minify,
54
+ },
55
+ html: {
56
+ meta: config.html.meta,
57
+ mountId: config.html.root,
58
+ scriptLoading: args.pack.esm ? "module" : "defer",
59
+ tags: [importmaps_tag, ...config.html.tags],
60
+ template:
61
+ config.html.template ||
62
+ resolve(get_package_root(), "template/index.html"),
63
+ templateParameters: {
64
+ base_prefix: config.url.base,
65
+ },
66
+ title: config.html.title,
67
+ },
68
+ resolve: {
69
+ alias: config.alias,
70
+ },
71
+ server: {
72
+ base: config.url.base,
73
+ open: config.dev_server.open
74
+ ? config.dev_server.open.map(
75
+ (name: string) =>
76
+ config.url.base +
77
+ (name.endsWith("html") ? `/${name}` : `/${name}.html`)
78
+ )
79
+ : false,
80
+ port: config.dev_server.port,
81
+ proxy: config.dev_server.proxy,
82
+ },
83
+ performance: {
84
+ chunkSplit: {
85
+ strategy: "split-by-experience",
86
+ },
87
+ },
88
+ });
89
+
90
+ const rsbuild = await createRsbuild({ rsbuildConfig });
91
+ await rsbuild[args.cmd === "dev" ? "startDevServer" : "build"]({
92
+ watch: args.cmd === "build" && args.pack.watch,
93
+ });
94
+
95
+ if (args.cmd === "build") {
96
+ console.log(picocolors.green("\n**** 构建【page】完成 ****"));
97
+ }
98
+ }
@@ -0,0 +1,76 @@
1
+ import picocolors from "picocolors";
2
+ import type { ModuloArgs_Pack } from "../args/index.ts";
3
+ import { collect_modules } from "./collect-modules.ts";
4
+ import { omit_root_path_for_entries } from "../tools/omit-root-path.ts";
5
+ import { get_externals_importmaps } from "./get-externals-and-tags.ts";
6
+ import type { GLOBAL_CONFIG } from "../config/type.ts";
7
+
8
+ let printed = false;
9
+
10
+ export function prepare_config(
11
+ args: ModuloArgs_Pack,
12
+ kind: "page" | "module",
13
+ config: GLOBAL_CONFIG
14
+ ) {
15
+ const { externals, importmaps } = get_externals_importmaps(
16
+ args,
17
+ config.externals
18
+ );
19
+
20
+ !printed &&
21
+ console.log(
22
+ `${picocolors.blue("\nexternals:")}\n${JSON.stringify(
23
+ externals,
24
+ null,
25
+ 2
26
+ )}\n`
27
+ );
28
+
29
+ if (kind === "page") {
30
+ console.log(
31
+ `${picocolors.blue("html_tags:")}\n${JSON.stringify(
32
+ config.html.tags,
33
+ null,
34
+ 2
35
+ )}\n`
36
+ );
37
+ }
38
+
39
+ const importmaps_tag = {
40
+ append: false,
41
+ head: true,
42
+ tag: "script",
43
+ attrs: { type: args.pack.esm ? "importmap" : "systemjs-importmap" },
44
+ children: `{
45
+ "imports": ${JSON.stringify(importmaps, null, 2)}
46
+ }`,
47
+ };
48
+
49
+ !printed &&
50
+ console.log(
51
+ `${picocolors.blue("\nimportmaps:")}\n${JSON.stringify(
52
+ importmaps,
53
+ null,
54
+ 2
55
+ )}\n`
56
+ );
57
+
58
+ printed = true;
59
+
60
+ console.log(picocolors.blueBright(`\n**** 开始构建 【${kind}】 ****`));
61
+ const entries = collect_modules(args, kind);
62
+
63
+ if (!entries) {
64
+ console.log(picocolors.red(`\n没有要构建的${kind},跳过\n`));
65
+ } else {
66
+ console.log(
67
+ `${picocolors.blue(`\n${kind} entries:`)}\n${JSON.stringify(
68
+ omit_root_path_for_entries(entries),
69
+ null,
70
+ 2
71
+ )}\n`
72
+ );
73
+ }
74
+
75
+ return { entries, externals, importmaps_tag };
76
+ }
@@ -0,0 +1,37 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import picocolors from "picocolors";
4
+ import { argv_debug, argv_verbose } from "../args/index.ts";
5
+
6
+ const logFile = path.join(process.cwd(), "modulo.debug.log");
7
+ let index = 0;
8
+
9
+ let debug_log_function: (hint: string, ...params: any) => void;
10
+
11
+ export function debug_log(_hint: string, ..._params: any) {
12
+ if (!debug_log_function) {
13
+ debug_log_function = (hint: string, ...params: any) => {
14
+ if (argv_debug || argv_verbose) {
15
+ const timestamp = new Date().toISOString();
16
+ const sn = String(index++).padStart(3, "0");
17
+ const logEntry = `--------------\n${sn} [${timestamp}] ${hint}\n${params
18
+ .map((p: unknown) =>
19
+ typeof p === "object" ? JSON.stringify(p, null, 2) : String(p)
20
+ )
21
+ .join("\n")}\n---------------\n\n`;
22
+
23
+ if (argv_verbose) {
24
+ console.log(logEntry);
25
+ }
26
+
27
+ if (argv_debug) {
28
+ // 打印序列号方便debug
29
+ console.log(picocolors.blue(`\ndebug log ${sn}`));
30
+
31
+ fs.appendFileSync(logFile, logEntry);
32
+ }
33
+ }
34
+ };
35
+ }
36
+ debug_log_function(_hint, ..._params);
37
+ }
@@ -0,0 +1,19 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import picocolors from 'picocolors';
4
+ import { debug_log } from './debug-log.ts';
5
+
6
+ export function read_file(path: string, error_msg?: string) {
7
+ try {
8
+ return readFileSync(path, 'utf8');
9
+ } catch {
10
+ console.log(picocolors.red(error_msg || `文件无法访问或者不存在: ${path}`));
11
+ return '';
12
+ }
13
+ }
14
+
15
+ export function resolve_and_read(root: string, name: string) {
16
+ const fullpath = resolve(root, name);
17
+ debug_log(`resolve file: ${name}`, 'result is:', fullpath);
18
+ return read_file(fullpath);
19
+ }
@@ -0,0 +1,29 @@
1
+ import fs from 'node:fs';
2
+ import path, { dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { debug_log } from './debug-log.ts';
5
+
6
+ let packageRoot = '';
7
+
8
+ export function get_package_root() {
9
+ if (!packageRoot) {
10
+ const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
11
+ const __dirname = dirname(__filename); // get the name of the directory
12
+
13
+ let currentDir = path.resolve(__dirname);
14
+ const root = path.parse(currentDir).root;
15
+
16
+ while (currentDir !== root) {
17
+ const potentialPkgJson = path.join(currentDir, 'package.json');
18
+ if (fs.existsSync(potentialPkgJson)) {
19
+ break;
20
+ }
21
+ currentDir = path.dirname(currentDir);
22
+ }
23
+
24
+ debug_log('packageRoot', currentDir);
25
+ packageRoot = currentDir;
26
+ }
27
+
28
+ return packageRoot;
29
+ }