@dimina/compiler 1.0.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.
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # 星河小程序编译工具
2
+
3
+ ## compiler 编译工具
4
+
5
+ 星河小程序编译工具(dmcc)用于将小程序源码编译为星河小程序运行时所需的文件格式。
6
+
7
+ ### 安装
8
+
9
+ ```sh
10
+ npm install @dimina/compiler -g
11
+ ```
12
+
13
+ 或者在项目中本地安装:
14
+
15
+ ```sh
16
+ npm install @dimina/compiler --save-dev
17
+ ```
18
+
19
+ ### 使用 npx 执行
20
+
21
+ 如果您不想全局安装,也可以使用 npx 直接执行:
22
+
23
+ ```sh
24
+ # 使用 npx 执行编译命令
25
+ npx @dimina/compiler build [选项]
26
+ ```
27
+
28
+ 对于项目中已安装的情况,可以在 package.json 的 scripts 中添加:
29
+
30
+ ```json
31
+ {
32
+ "scripts": {
33
+ "build": "dmcc build",
34
+ "dev": "dmcc build -w"
35
+ }
36
+ }
37
+ ```
38
+
39
+ 然后通过 npm 执行:
40
+
41
+ ```sh
42
+ # 编译项目
43
+ npm run build
44
+
45
+ # 开发模式(监听文件变化)
46
+ npm run dev
47
+ ```
48
+
49
+ ### 使用方法
50
+
51
+ #### 基本命令
52
+
53
+ ```sh
54
+ # 查看版本
55
+ dmcc --version
56
+
57
+ # 编译小程序
58
+ dmcc build [选项]
59
+ ```
60
+
61
+ #### build 命令选项
62
+
63
+ ```sh
64
+ Usage: dmcc build [选项]
65
+
66
+ 选项:
67
+ -c, --work-path <path> 编译文件所在目录(默认为当前目录)
68
+ -s, --target-path <path> 编译产物存放路径(默认为当前目录)
69
+ -w, --watch 启用改动监听(实时编译)
70
+ -h, --help 显示帮助信息
71
+ ```
72
+
73
+ #### 示例
74
+
75
+ ```sh
76
+ # 编译当前目录下的小程序项目
77
+ dmcc build
78
+
79
+ # 编译指定目录的小程序项目,并将产物输出到指定目录
80
+ dmcc build -c ./src -s ./dist
81
+
82
+ # 监听文件变化,实时编译
83
+ dmcc build -w
84
+
85
+ # 完整示例:编译指定目录,输出到指定目录,并启用监听
86
+ dmcc build -c ./src -s ./dist -w
87
+ ```
88
+
89
+ ### 编译流程说明
90
+
91
+ 编译工具会将小程序源码转换为星河小程序运行时可识别的格式:
92
+
93
+ ```txt
94
+ app.js, index.js -> logic.js (逻辑文件)
95
+ index.wxml -> view.js (视图文件)
96
+ app.wxss, index.wxss -> style.css (样式文件)
97
+ app.json, index.json -> config.json (配置文件)
98
+ ```
99
+
100
+ ### 编译产物目录结构
101
+
102
+ 编译后,会在目标目录生成以小程序 id 命名的文件夹,项目结构如下:
103
+
104
+ ```txt
105
+ dist/
106
+ ├── logic.js # 小程序逻辑代码
107
+ ├── view.js # 小程序视图代码
108
+ ├── style.css # 小程序样式
109
+ └── config.json # 小程序配置
110
+ ```
111
+
112
+ ### 常见问题
113
+
114
+ 如果编译过程中遇到问题,可以尝试以下解决方法:
115
+
116
+ 1. 确保小程序项目结构正确
117
+ 2. 检查 app.json 配置是否有效
118
+ 3. 启用监听模式 (-w) 可以实时查看编译错误
119
+ 4. 确保依赖安装完整
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ const process = require("node:process");
4
+ const path = require("node:path");
5
+ const commander = require("commander");
6
+ const chokidar = require("chokidar");
7
+ const index = require("../index.cjs");
8
+ const version = "1.0.1";
9
+ const pack = {
10
+ version
11
+ };
12
+ commander.program.command("build").option("-c, --work-path <path>", "编译工作目录").option("-s, --target-path <path>", "编译产物存放路径").option("-w, --watch", "启用监听文件改动").action(async (options) => {
13
+ const workPath = options.workPath ? path.resolve(options.workPath) : process.cwd();
14
+ const targetPath = options.targetPath ? path.resolve(options.targetPath) : process.cwd();
15
+ await index(targetPath, workPath);
16
+ const watch = options.watch;
17
+ if (watch) {
18
+ chokidar.watch(workPath, {
19
+ persistent: true,
20
+ // 持续监听
21
+ ignoreInitial: true
22
+ // 忽略初始的 add/addDir 事件
23
+ }).on("all", async (event, path2) => {
24
+ if (event === "change") {
25
+ console.log(`${path2} 改动,重新编译`);
26
+ await index(targetPath, workPath);
27
+ }
28
+ });
29
+ }
30
+ });
31
+ commander.program.name("dmcc").version(pack.version);
32
+ commander.program.parse(process.argv);
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ import process from "node:process";
3
+ import path from "node:path";
4
+ import { program } from "commander";
5
+ import chokidar from "chokidar";
6
+ import build from "../index.js";
7
+ const version = "1.0.1";
8
+ const pack = {
9
+ version
10
+ };
11
+ program.command("build").option("-c, --work-path <path>", "编译工作目录").option("-s, --target-path <path>", "编译产物存放路径").option("-w, --watch", "启用监听文件改动").action(async (options) => {
12
+ const workPath = options.workPath ? path.resolve(options.workPath) : process.cwd();
13
+ const targetPath = options.targetPath ? path.resolve(options.targetPath) : process.cwd();
14
+ await build(targetPath, workPath);
15
+ const watch = options.watch;
16
+ if (watch) {
17
+ chokidar.watch(workPath, {
18
+ persistent: true,
19
+ // 持续监听
20
+ ignoreInitial: true
21
+ // 忽略初始的 add/addDir 事件
22
+ }).on("all", async (event, path2) => {
23
+ if (event === "change") {
24
+ console.log(`${path2} 改动,重新编译`);
25
+ await build(targetPath, workPath);
26
+ }
27
+ });
28
+ }
29
+ });
30
+ program.name("dmcc").version(pack.version);
31
+ program.parse(process.argv);
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ const fs = require("node:fs");
3
+ const path = require("node:path");
4
+ const node_worker_threads = require("node:worker_threads");
5
+ const babel = require("@babel/core");
6
+ const types = require("@babel/types");
7
+ const _traverse = require("@babel/traverse");
8
+ const esbuild = require("esbuild");
9
+ const env = require("../env-Chow6VXH.cjs");
10
+ const traverse = _traverse.default ? _traverse.default : _traverse;
11
+ const processedModules = /* @__PURE__ */ new Set();
12
+ if (!node_worker_threads.isMainThread) {
13
+ node_worker_threads.parentPort.on("message", async ({ pages, storeInfo }) => {
14
+ try {
15
+ env.resetStoreInfo(storeInfo);
16
+ const progress = {
17
+ _completedTasks: 0,
18
+ get completedTasks() {
19
+ return this._completedTasks;
20
+ },
21
+ set completedTasks(value) {
22
+ this._completedTasks = value;
23
+ node_worker_threads.parentPort.postMessage({ completedTasks: this.completedTasks });
24
+ }
25
+ };
26
+ const mainCompileRes = await compileJS(pages.mainPages, null, null, progress);
27
+ for (const [root, subPages] of Object.entries(pages.subPages)) {
28
+ const subCompileRes = await compileJS(
29
+ subPages.info,
30
+ root,
31
+ subPages.independent ? [] : mainCompileRes,
32
+ progress
33
+ );
34
+ await writeCompileRes(subCompileRes, root);
35
+ }
36
+ await writeCompileRes(mainCompileRes, null);
37
+ node_worker_threads.parentPort.postMessage({ success: true });
38
+ } catch (error) {
39
+ node_worker_threads.parentPort.postMessage({ success: false, error: error.message });
40
+ }
41
+ });
42
+ }
43
+ async function writeCompileRes(compileRes, root) {
44
+ let mergeCode = "";
45
+ for (const module2 of compileRes) {
46
+ const amdFormat = `modDefine('${module2.path}', function(require, module, exports) {
47
+ ${module2.code}
48
+ });`;
49
+ const { code: minifiedCode } = await esbuild.transform(amdFormat, {
50
+ minify: true,
51
+ target: ["es2023"],
52
+ // quickjs 支持版本
53
+ platform: "neutral"
54
+ });
55
+ mergeCode += minifiedCode;
56
+ }
57
+ if (root) {
58
+ const subDir = `${env.getTargetPath()}/${root}`;
59
+ if (!fs.existsSync(subDir)) {
60
+ fs.mkdirSync(subDir, { recursive: true });
61
+ }
62
+ fs.writeFileSync(`${subDir}/logic.js`, mergeCode);
63
+ } else {
64
+ const mainDir = `${env.getTargetPath()}/main`;
65
+ if (!fs.existsSync(mainDir)) {
66
+ fs.mkdirSync(mainDir);
67
+ }
68
+ fs.writeFileSync(`${mainDir}/logic.js`, mergeCode);
69
+ }
70
+ }
71
+ async function compileJS(pages, root, mainCompileRes, progress) {
72
+ const compileRes = [];
73
+ if (!root) {
74
+ buildJSByPath(root, { path: "app" }, compileRes, mainCompileRes, false);
75
+ }
76
+ pages.forEach((page) => {
77
+ buildJSByPath(root, page, compileRes, mainCompileRes, true);
78
+ progress.completedTasks++;
79
+ });
80
+ return compileRes;
81
+ }
82
+ function buildJSByPath(root, module2, compileRes, mainCompileRes, addExtra, depthChain = [], putMain = false) {
83
+ const currentPath = module2.path;
84
+ if (depthChain.includes(currentPath)) {
85
+ console.warn(`检测到循环依赖: ${[...depthChain, currentPath].join(" -> ")}`);
86
+ }
87
+ if (depthChain.length > 100) {
88
+ console.warn(`检测到深度依赖: ${[...depthChain, currentPath].join(" -> ")}`);
89
+ }
90
+ depthChain = [...depthChain, currentPath];
91
+ if (!module2.path) {
92
+ return;
93
+ }
94
+ if (env.hasCompileInfo(module2.path, compileRes, mainCompileRes)) {
95
+ return;
96
+ }
97
+ const compileInfo = {
98
+ path: module2.path,
99
+ code: ""
100
+ };
101
+ const src = module2.path.startsWith("/") ? module2.path : `/${module2.path}`;
102
+ const modulePath = `${env.getWorkPath()}${src}.js`;
103
+ const js = env.getContentByPath(modulePath);
104
+ const ast = babel.parseSync(js);
105
+ const addedArgs = types.objectExpression([
106
+ types.objectProperty(types.identifier("path"), types.stringLiteral(module2.path))
107
+ ]);
108
+ if (module2.component) {
109
+ const component = types.objectProperty(types.identifier("component"), types.booleanLiteral(true));
110
+ addedArgs.properties.push(component);
111
+ }
112
+ if (module2.usingComponents) {
113
+ const components = types.objectProperty(types.identifier("usingComponents"), types.objectExpression([]));
114
+ const allSubPackages = env.getAppConfigInfo().subPackages;
115
+ for (const [name, path2] of Object.entries(module2.usingComponents)) {
116
+ let toMainSubPackage = true;
117
+ if (root) {
118
+ const rootPackageName = root.split("_")[1];
119
+ const normalizedPath = path2.startsWith("/") ? path2.substring(1) : path2;
120
+ for (const subPackage of allSubPackages) {
121
+ if (normalizedPath.startsWith(`${subPackage.root}/`)) {
122
+ toMainSubPackage = false;
123
+ break;
124
+ }
125
+ }
126
+ if (!toMainSubPackage) {
127
+ if (!normalizedPath.startsWith(`${rootPackageName}/`)) {
128
+ continue;
129
+ }
130
+ }
131
+ } else {
132
+ toMainSubPackage = false;
133
+ }
134
+ const componentModule = env.getComponent(path2);
135
+ if (!componentModule) {
136
+ continue;
137
+ }
138
+ buildJSByPath(root, componentModule, compileRes, mainCompileRes, true, depthChain, toMainSubPackage);
139
+ const props = types.objectProperty(types.identifier(`'${name}'`), types.stringLiteral(path2));
140
+ components.value.properties.push(props);
141
+ }
142
+ addedArgs.properties.push(components);
143
+ }
144
+ if (addExtra) {
145
+ ast.program.body.splice(0, 0, getExtraInfoStatement("this", addedArgs));
146
+ }
147
+ if (putMain) {
148
+ mainCompileRes.push(compileInfo);
149
+ } else {
150
+ compileRes.push(compileInfo);
151
+ }
152
+ traverse(ast, {
153
+ CallExpression(ap) {
154
+ if (ap.node.callee.name === "require" || ap.node.callee.object?.name === "require") {
155
+ const requirePath = ap.node.arguments[0].value;
156
+ if (requirePath) {
157
+ const requireFullPath = path.resolve(modulePath, `../${requirePath}`);
158
+ const id = requireFullPath.split(`${env.getWorkPath()}/`)[1].split(".js")[0];
159
+ ap.node.arguments[0] = types.stringLiteral(id);
160
+ if (!processedModules.has(id)) {
161
+ buildJSByPath(root, { path: id }, compileRes, mainCompileRes, false, depthChain);
162
+ }
163
+ }
164
+ }
165
+ }
166
+ });
167
+ const { code } = babel.transformFromAstSync(ast, "", {
168
+ comments: false
169
+ });
170
+ compileInfo.code = code;
171
+ processedModules.add(currentPath);
172
+ }
173
+ function getExtraInfoStatement(type, addedArgs) {
174
+ const propertyAssignment = types.objectProperty(types.identifier("__extraInfo"), addedArgs);
175
+ const propertyAssignmentStatement = types.expressionStatement(
176
+ types.assignmentExpression(
177
+ "=",
178
+ types.memberExpression(types.identifier(type), propertyAssignment.key),
179
+ propertyAssignment.value
180
+ )
181
+ );
182
+ return propertyAssignmentStatement;
183
+ }
184
+ module.exports = compileJS;
@@ -0,0 +1,185 @@
1
+ import fs from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { isMainThread, parentPort } from "node:worker_threads";
4
+ import babel from "@babel/core";
5
+ import types from "@babel/types";
6
+ import _traverse from "@babel/traverse";
7
+ import { transform } from "esbuild";
8
+ import { r as resetStoreInfo, g as getTargetPath, i as hasCompileInfo, f as getWorkPath, b as getContentByPath, j as getAppConfigInfo, a as getComponent } from "../env-CezfCSQz.js";
9
+ const traverse = _traverse.default ? _traverse.default : _traverse;
10
+ const processedModules = /* @__PURE__ */ new Set();
11
+ if (!isMainThread) {
12
+ parentPort.on("message", async ({ pages, storeInfo }) => {
13
+ try {
14
+ resetStoreInfo(storeInfo);
15
+ const progress = {
16
+ _completedTasks: 0,
17
+ get completedTasks() {
18
+ return this._completedTasks;
19
+ },
20
+ set completedTasks(value) {
21
+ this._completedTasks = value;
22
+ parentPort.postMessage({ completedTasks: this.completedTasks });
23
+ }
24
+ };
25
+ const mainCompileRes = await compileJS(pages.mainPages, null, null, progress);
26
+ for (const [root, subPages] of Object.entries(pages.subPages)) {
27
+ const subCompileRes = await compileJS(
28
+ subPages.info,
29
+ root,
30
+ subPages.independent ? [] : mainCompileRes,
31
+ progress
32
+ );
33
+ await writeCompileRes(subCompileRes, root);
34
+ }
35
+ await writeCompileRes(mainCompileRes, null);
36
+ parentPort.postMessage({ success: true });
37
+ } catch (error) {
38
+ parentPort.postMessage({ success: false, error: error.message });
39
+ }
40
+ });
41
+ }
42
+ async function writeCompileRes(compileRes, root) {
43
+ let mergeCode = "";
44
+ for (const module of compileRes) {
45
+ const amdFormat = `modDefine('${module.path}', function(require, module, exports) {
46
+ ${module.code}
47
+ });`;
48
+ const { code: minifiedCode } = await transform(amdFormat, {
49
+ minify: true,
50
+ target: ["es2023"],
51
+ // quickjs 支持版本
52
+ platform: "neutral"
53
+ });
54
+ mergeCode += minifiedCode;
55
+ }
56
+ if (root) {
57
+ const subDir = `${getTargetPath()}/${root}`;
58
+ if (!fs.existsSync(subDir)) {
59
+ fs.mkdirSync(subDir, { recursive: true });
60
+ }
61
+ fs.writeFileSync(`${subDir}/logic.js`, mergeCode);
62
+ } else {
63
+ const mainDir = `${getTargetPath()}/main`;
64
+ if (!fs.existsSync(mainDir)) {
65
+ fs.mkdirSync(mainDir);
66
+ }
67
+ fs.writeFileSync(`${mainDir}/logic.js`, mergeCode);
68
+ }
69
+ }
70
+ async function compileJS(pages, root, mainCompileRes, progress) {
71
+ const compileRes = [];
72
+ if (!root) {
73
+ buildJSByPath(root, { path: "app" }, compileRes, mainCompileRes, false);
74
+ }
75
+ pages.forEach((page) => {
76
+ buildJSByPath(root, page, compileRes, mainCompileRes, true);
77
+ progress.completedTasks++;
78
+ });
79
+ return compileRes;
80
+ }
81
+ function buildJSByPath(root, module, compileRes, mainCompileRes, addExtra, depthChain = [], putMain = false) {
82
+ const currentPath = module.path;
83
+ if (depthChain.includes(currentPath)) {
84
+ console.warn(`检测到循环依赖: ${[...depthChain, currentPath].join(" -> ")}`);
85
+ }
86
+ if (depthChain.length > 100) {
87
+ console.warn(`检测到深度依赖: ${[...depthChain, currentPath].join(" -> ")}`);
88
+ }
89
+ depthChain = [...depthChain, currentPath];
90
+ if (!module.path) {
91
+ return;
92
+ }
93
+ if (hasCompileInfo(module.path, compileRes, mainCompileRes)) {
94
+ return;
95
+ }
96
+ const compileInfo = {
97
+ path: module.path,
98
+ code: ""
99
+ };
100
+ const src = module.path.startsWith("/") ? module.path : `/${module.path}`;
101
+ const modulePath = `${getWorkPath()}${src}.js`;
102
+ const js = getContentByPath(modulePath);
103
+ const ast = babel.parseSync(js);
104
+ const addedArgs = types.objectExpression([
105
+ types.objectProperty(types.identifier("path"), types.stringLiteral(module.path))
106
+ ]);
107
+ if (module.component) {
108
+ const component = types.objectProperty(types.identifier("component"), types.booleanLiteral(true));
109
+ addedArgs.properties.push(component);
110
+ }
111
+ if (module.usingComponents) {
112
+ const components = types.objectProperty(types.identifier("usingComponents"), types.objectExpression([]));
113
+ const allSubPackages = getAppConfigInfo().subPackages;
114
+ for (const [name, path] of Object.entries(module.usingComponents)) {
115
+ let toMainSubPackage = true;
116
+ if (root) {
117
+ const rootPackageName = root.split("_")[1];
118
+ const normalizedPath = path.startsWith("/") ? path.substring(1) : path;
119
+ for (const subPackage of allSubPackages) {
120
+ if (normalizedPath.startsWith(`${subPackage.root}/`)) {
121
+ toMainSubPackage = false;
122
+ break;
123
+ }
124
+ }
125
+ if (!toMainSubPackage) {
126
+ if (!normalizedPath.startsWith(`${rootPackageName}/`)) {
127
+ continue;
128
+ }
129
+ }
130
+ } else {
131
+ toMainSubPackage = false;
132
+ }
133
+ const componentModule = getComponent(path);
134
+ if (!componentModule) {
135
+ continue;
136
+ }
137
+ buildJSByPath(root, componentModule, compileRes, mainCompileRes, true, depthChain, toMainSubPackage);
138
+ const props = types.objectProperty(types.identifier(`'${name}'`), types.stringLiteral(path));
139
+ components.value.properties.push(props);
140
+ }
141
+ addedArgs.properties.push(components);
142
+ }
143
+ if (addExtra) {
144
+ ast.program.body.splice(0, 0, getExtraInfoStatement("this", addedArgs));
145
+ }
146
+ if (putMain) {
147
+ mainCompileRes.push(compileInfo);
148
+ } else {
149
+ compileRes.push(compileInfo);
150
+ }
151
+ traverse(ast, {
152
+ CallExpression(ap) {
153
+ if (ap.node.callee.name === "require" || ap.node.callee.object?.name === "require") {
154
+ const requirePath = ap.node.arguments[0].value;
155
+ if (requirePath) {
156
+ const requireFullPath = resolve(modulePath, `../${requirePath}`);
157
+ const id = requireFullPath.split(`${getWorkPath()}/`)[1].split(".js")[0];
158
+ ap.node.arguments[0] = types.stringLiteral(id);
159
+ if (!processedModules.has(id)) {
160
+ buildJSByPath(root, { path: id }, compileRes, mainCompileRes, false, depthChain);
161
+ }
162
+ }
163
+ }
164
+ }
165
+ });
166
+ const { code } = babel.transformFromAstSync(ast, "", {
167
+ comments: false
168
+ });
169
+ compileInfo.code = code;
170
+ processedModules.add(currentPath);
171
+ }
172
+ function getExtraInfoStatement(type, addedArgs) {
173
+ const propertyAssignment = types.objectProperty(types.identifier("__extraInfo"), addedArgs);
174
+ const propertyAssignmentStatement = types.expressionStatement(
175
+ types.assignmentExpression(
176
+ "=",
177
+ types.memberExpression(types.identifier(type), propertyAssignment.key),
178
+ propertyAssignment.value
179
+ )
180
+ );
181
+ return propertyAssignmentStatement;
182
+ }
183
+ export {
184
+ compileJS as default
185
+ };