@open-wxa/wxa-runner-core 0.9.0 → 0.10.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.
- package/dist/index.d.mts +34 -2
- package/dist/index.d.ts +34 -2
- package/dist/index.js +373 -166
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +373 -166
- package/dist/index.mjs.map +1 -1
- package/dist/runtime/frame.js +90 -77
- package/dist/runtime/frame.js.map +1 -1
- package/dist/runtime/host.js +63 -44
- package/dist/runtime/host.js.map +1 -1
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -49,6 +49,47 @@ var FileStore = class {
|
|
|
49
49
|
this.etagPrefix = (0, import_crypto.randomUUID)();
|
|
50
50
|
this.version = 0;
|
|
51
51
|
this.nodes = /* @__PURE__ */ new Map([["/", { dir: true, data: Buffer.alloc(0), mtime: Date.now(), etag: "" }]]);
|
|
52
|
+
this.operations = {
|
|
53
|
+
readFile: ({ filePath, encoding }) => {
|
|
54
|
+
const data = this.read(filePath);
|
|
55
|
+
return encoding === void 0 ? { data: data.toString("base64"), binary: true } : { data: data.toString(encoding) };
|
|
56
|
+
},
|
|
57
|
+
writeFile: (args) => this.writeEncoded(args, false),
|
|
58
|
+
appendFile: (args) => this.writeEncoded(args, true),
|
|
59
|
+
copyFile: ({ srcPath, destPath }) => {
|
|
60
|
+
this.write(destPath, this.read(srcPath));
|
|
61
|
+
return {};
|
|
62
|
+
},
|
|
63
|
+
access: ({ path: path7 }) => {
|
|
64
|
+
this.existing(path7);
|
|
65
|
+
return {};
|
|
66
|
+
},
|
|
67
|
+
stat: ({ path: path7 }) => {
|
|
68
|
+
const node = this.existing(path7);
|
|
69
|
+
const stats = {
|
|
70
|
+
mode: node.dir ? 16877 : 33188,
|
|
71
|
+
size: node.data.length,
|
|
72
|
+
lastAccessedTime: Math.floor(node.mtime / 1e3),
|
|
73
|
+
lastModifiedTime: Math.floor(node.mtime / 1e3)
|
|
74
|
+
};
|
|
75
|
+
return { stats, ...stats };
|
|
76
|
+
},
|
|
77
|
+
getFileInfo: ({ filePath }) => ({ size: this.read(filePath).length }),
|
|
78
|
+
mkdir: ({ dirPath, recursive }) => {
|
|
79
|
+
this.mkdir(dirPath, !!recursive);
|
|
80
|
+
return {};
|
|
81
|
+
},
|
|
82
|
+
readdir: ({ dirPath }) => ({ files: this.readdir(dirPath) }),
|
|
83
|
+
unlink: ({ filePath }) => {
|
|
84
|
+
this.remove(filePath, { dir: false });
|
|
85
|
+
return {};
|
|
86
|
+
},
|
|
87
|
+
rmdir: ({ dirPath, recursive }) => {
|
|
88
|
+
this.assertNotRoot(dirPath);
|
|
89
|
+
this.remove(dirPath, { dir: true, recursive: !!recursive });
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
};
|
|
52
93
|
this.roots = roots.map((root) => this.normalize(root));
|
|
53
94
|
for (const root of this.roots) this.mkdir(root, true);
|
|
54
95
|
}
|
|
@@ -109,66 +150,37 @@ var FileStore = class {
|
|
|
109
150
|
}
|
|
110
151
|
this.nodes.delete(target);
|
|
111
152
|
}
|
|
153
|
+
/** 文件 API 经开发服务器调用的操作,参数和返回值的字段名与基础库一致 */
|
|
112
154
|
execute(operation, args) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (!node) throw new Error(`no such file or directory ${args.path}`);
|
|
133
|
-
const stats = {
|
|
134
|
-
mode: node.dir ? 16877 : 33188,
|
|
135
|
-
size: node.data.length,
|
|
136
|
-
lastAccessedTime: Math.floor(node.mtime / 1e3),
|
|
137
|
-
lastModifiedTime: Math.floor(node.mtime / 1e3)
|
|
138
|
-
};
|
|
139
|
-
return { stats, ...stats };
|
|
140
|
-
}
|
|
141
|
-
case "getFileInfo":
|
|
142
|
-
return { size: this.read(args.filePath).length };
|
|
143
|
-
case "mkdir":
|
|
144
|
-
this.mkdir(args.dirPath, !!args.recursive);
|
|
145
|
-
return {};
|
|
146
|
-
case "readdir":
|
|
147
|
-
return { files: this.readdir(args.dirPath) };
|
|
148
|
-
case "unlink":
|
|
149
|
-
this.remove(args.filePath, { dir: false });
|
|
150
|
-
return {};
|
|
151
|
-
case "rmdir": {
|
|
152
|
-
const target = this.normalize(args.dirPath);
|
|
153
|
-
const prefix = target === "/" ? "/" : target + "/";
|
|
154
|
-
if (this.roots.some((root) => root === target || root.startsWith(prefix))) {
|
|
155
|
-
throw new Error(`operation not permitted ${args.dirPath}`);
|
|
156
|
-
}
|
|
157
|
-
this.remove(args.dirPath, { dir: true, recursive: !!args.recursive });
|
|
158
|
-
return {};
|
|
159
|
-
}
|
|
160
|
-
default:
|
|
161
|
-
throw new Error(`unsupported filesystem operation ${operation}`);
|
|
155
|
+
if (!Object.prototype.hasOwnProperty.call(this.operations, operation)) {
|
|
156
|
+
throw new Error(`unsupported filesystem operation ${operation}`);
|
|
157
|
+
}
|
|
158
|
+
return this.operations[operation](args);
|
|
159
|
+
}
|
|
160
|
+
writeEncoded(args, append) {
|
|
161
|
+
this.write(args.filePath, Buffer.from(args.data ?? "", args.binary ? "base64" : args.encoding || "utf8"), append);
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
existing(filePath) {
|
|
165
|
+
const node = this.stat(filePath);
|
|
166
|
+
if (!node) throw new Error(`no such file or directory ${filePath}`);
|
|
167
|
+
return node;
|
|
168
|
+
}
|
|
169
|
+
assertNotRoot(dirPath) {
|
|
170
|
+
const target = this.normalize(dirPath);
|
|
171
|
+
const prefix = target === "/" ? "/" : target + "/";
|
|
172
|
+
if (this.roots.some((root) => root === target || root.startsWith(prefix))) {
|
|
173
|
+
throw new Error(`operation not permitted ${dirPath}`);
|
|
162
174
|
}
|
|
163
175
|
}
|
|
164
176
|
};
|
|
165
177
|
|
|
166
178
|
// src/server/DevServer.ts
|
|
167
|
-
var
|
|
179
|
+
var import_fs6 = __toESM(require("fs"));
|
|
168
180
|
var import_http = __toESM(require("http"));
|
|
169
181
|
var import_net = __toESM(require("net"));
|
|
170
|
-
var
|
|
171
|
-
var
|
|
182
|
+
var import_os3 = __toESM(require("os"));
|
|
183
|
+
var import_path6 = __toESM(require("path"));
|
|
172
184
|
var import_express2 = __toESM(require("express"));
|
|
173
185
|
var import_ws2 = require("ws");
|
|
174
186
|
var import_wxa_runner_extractor5 = require("@open-wxa/wxa-runner-extractor");
|
|
@@ -177,6 +189,164 @@ var import_wxa_runner_extractor5 = require("@open-wxa/wxa-runner-extractor");
|
|
|
177
189
|
var import_wxa_runner_compiler = require("@open-wxa/wxa-runner-compiler");
|
|
178
190
|
var import_esbuild = require("esbuild");
|
|
179
191
|
|
|
192
|
+
// src/project/StyleMirror.ts
|
|
193
|
+
var import_fs = __toESM(require("fs"));
|
|
194
|
+
var import_module = require("module");
|
|
195
|
+
var import_os = __toESM(require("os"));
|
|
196
|
+
var import_path = __toESM(require("path"));
|
|
197
|
+
var import_url = require("url");
|
|
198
|
+
var PLUGIN_EXTENSIONS = {
|
|
199
|
+
less: [".less"],
|
|
200
|
+
sass: [".sass", ".scss"]
|
|
201
|
+
};
|
|
202
|
+
var IMPORT_WXSS = /(?:^|\s)?(?:@import)(?:\s*)?(["'])([^"']+.wxss)\1(?:\s*);/g;
|
|
203
|
+
function readStyleSetting(setting) {
|
|
204
|
+
const plugins = [];
|
|
205
|
+
let lessGlobalVars = {};
|
|
206
|
+
for (const item of Array.isArray(setting?.useCompilerPlugins) ? setting.useCompilerPlugins : []) {
|
|
207
|
+
const [name, options] = Array.isArray(item) ? item : [item, void 0];
|
|
208
|
+
if (name !== "less" && name !== "sass" || plugins.includes(name)) continue;
|
|
209
|
+
plugins.push(name);
|
|
210
|
+
if (name === "less" && options?.globalVars) lessGlobalVars = options.globalVars;
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
plugins,
|
|
214
|
+
lessGlobalVars,
|
|
215
|
+
lessCommonFile: setting?.lessSetting?.commonUseFilePath || "app.less",
|
|
216
|
+
sassCommonFile: setting?.sassSetting?.sassCommonUseFilePath || "global.sass",
|
|
217
|
+
scssCommonFile: setting?.sassSetting?.scssCommonUseFilePath || "global.scss"
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function ignoredStyleSources(project, setting) {
|
|
221
|
+
return Object.keys(PLUGIN_EXTENSIONS).filter((plugin) => !setting.plugins.includes(plugin)).map((plugin) => ({ plugin, files: project.listFiles(PLUGIN_EXTENSIONS[plugin]) })).filter(({ files }) => files.length > 0);
|
|
222
|
+
}
|
|
223
|
+
var StyleMirror = class {
|
|
224
|
+
constructor(setting) {
|
|
225
|
+
this.setting = setting;
|
|
226
|
+
this.dir = null;
|
|
227
|
+
/** 已写入的 .wxss 与写入的内容 */
|
|
228
|
+
this.files = /* @__PURE__ */ new Map();
|
|
229
|
+
this.compilers = /* @__PURE__ */ new Map();
|
|
230
|
+
}
|
|
231
|
+
/** 镜像目录;还没同步过时为 null */
|
|
232
|
+
get directory() {
|
|
233
|
+
return this.dir;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 编译全部预处理文件,与原有的 .wxss 一起写进镜像目录。
|
|
237
|
+
* @returns 镜像目录,以及其中全部 .wxss 的相对路径
|
|
238
|
+
*/
|
|
239
|
+
async sync(project) {
|
|
240
|
+
const sources = this.pickSources(project);
|
|
241
|
+
const outputs = await Promise.all(
|
|
242
|
+
[...sources].map(async ([target, source]) => [target, await this.compile(project, source)])
|
|
243
|
+
);
|
|
244
|
+
const dir = this.dir ??= import_fs.default.mkdtempSync(import_path.default.join(import_os.default.tmpdir(), "wxa-runner-wxss-"));
|
|
245
|
+
for (const rel of [...this.files.keys()]) {
|
|
246
|
+
if (sources.has(rel)) continue;
|
|
247
|
+
import_fs.default.rmSync(import_path.default.join(dir, rel), { force: true });
|
|
248
|
+
this.files.delete(rel);
|
|
249
|
+
}
|
|
250
|
+
for (const [rel, css] of outputs) {
|
|
251
|
+
if (this.files.get(rel) === css) continue;
|
|
252
|
+
const target = import_path.default.join(dir, rel);
|
|
253
|
+
import_fs.default.mkdirSync(import_path.default.dirname(target), { recursive: true });
|
|
254
|
+
import_fs.default.writeFileSync(target, css);
|
|
255
|
+
this.files.set(rel, css);
|
|
256
|
+
}
|
|
257
|
+
return { cwd: dir, files: [...sources.keys()] };
|
|
258
|
+
}
|
|
259
|
+
dispose() {
|
|
260
|
+
if (this.dir) import_fs.default.rmSync(this.dir, { recursive: true, force: true });
|
|
261
|
+
this.dir = null;
|
|
262
|
+
this.files.clear();
|
|
263
|
+
}
|
|
264
|
+
/** 每个 .wxss 由哪个源文件生成:键为 .wxss 的相对路径 */
|
|
265
|
+
pickSources(project) {
|
|
266
|
+
const extensions = [".wxss", ...this.setting.plugins.flatMap((plugin) => PLUGIN_EXTENSIONS[plugin])];
|
|
267
|
+
const sources = /* @__PURE__ */ new Map();
|
|
268
|
+
for (const file of project.listFiles(extensions)) {
|
|
269
|
+
const ext = import_path.default.extname(file);
|
|
270
|
+
const target = file.slice(0, -ext.length) + ".wxss";
|
|
271
|
+
const current = sources.get(target);
|
|
272
|
+
if (!current || extensions.indexOf(ext) > extensions.indexOf(import_path.default.extname(current))) sources.set(target, file);
|
|
273
|
+
}
|
|
274
|
+
return sources;
|
|
275
|
+
}
|
|
276
|
+
async compile(project, file) {
|
|
277
|
+
const ext = import_path.default.extname(file);
|
|
278
|
+
if (ext === ".wxss") return project.readText(file);
|
|
279
|
+
const compiler = await this.loadCompiler(ext === ".less" ? "less" : "sass", project);
|
|
280
|
+
try {
|
|
281
|
+
return ext === ".less" ? await this.compileLess(compiler, project, file) : this.compileSass(compiler, project, file);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
throw new Error(`${file} \u7F16\u8BD1\u5931\u8D25\uFF1A${describeError(project, error)}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async compileLess(less, project, file) {
|
|
287
|
+
const common = this.setting.lessCommonFile;
|
|
288
|
+
const prefix = Object.entries(this.setting.lessGlobalVars).map(([name, value]) => `@${name.replace(/^@/, "")}: ${value};`);
|
|
289
|
+
if (file !== common && project.exists(common)) prefix.push(`@import (optional, reference) '${toRoot(file)}/${common}';`);
|
|
290
|
+
let code = prefix.map((statement) => statement + " ").join("") + project.readText(file);
|
|
291
|
+
code = code.replace(IMPORT_WXSS, (statement, _quote, target) => statement.replace(target, `${target}?css`));
|
|
292
|
+
const { css } = await less.render(code, {
|
|
293
|
+
filename: import_path.default.join(project.root, file),
|
|
294
|
+
paths: [project.root]
|
|
295
|
+
});
|
|
296
|
+
return css.replace(/(@import\s*["'][^"']+\.wxss)\?css(["'])/g, "$1$2");
|
|
297
|
+
}
|
|
298
|
+
compileSass(sass, project, file) {
|
|
299
|
+
const indented = file.endsWith(".sass");
|
|
300
|
+
const preferred = indented ? this.setting.sassCommonFile : this.setting.scssCommonFile;
|
|
301
|
+
const fallback = indented ? this.setting.scssCommonFile : this.setting.sassCommonFile;
|
|
302
|
+
const common = project.exists(preferred) ? preferred : project.exists(fallback) ? fallback : null;
|
|
303
|
+
let code = project.readText(file);
|
|
304
|
+
if (common && file !== preferred && file !== fallback) {
|
|
305
|
+
code = `@use '${toRoot(file)}/${common}'${indented ? "\n" : "; "}${code}`;
|
|
306
|
+
}
|
|
307
|
+
const imported = /* @__PURE__ */ new Set();
|
|
308
|
+
code = code.replace(IMPORT_WXSS, (statement, _quote, target) => {
|
|
309
|
+
imported.add(target);
|
|
310
|
+
return statement.replace(target, `${target.slice(0, -".wxss".length)}.css`);
|
|
311
|
+
});
|
|
312
|
+
const css = typeof sass.compileString === "function" ? sass.compileString(code, { url: (0, import_url.pathToFileURL)(import_path.default.join(project.root, file)), syntax: indented ? "indented" : "scss", loadPaths: [project.root] }).css : sass.renderSync({ data: code, file: import_path.default.join(project.root, file), indentedSyntax: indented, includePaths: [project.root] }).css.toString();
|
|
313
|
+
return css.replace(
|
|
314
|
+
/(@import\s*["'])([^"']+)\.css(["'])/g,
|
|
315
|
+
(statement, head, target, tail) => imported.has(`${target}.wxss`) ? `${head}${target}.wxss${tail}` : statement
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
/** 用到时才加载:先从小程序根目录和项目目录向上查找 node_modules,找不到时用 wxa-runner 自带的 */
|
|
319
|
+
loadCompiler(name, project) {
|
|
320
|
+
let loading = this.compilers.get(name);
|
|
321
|
+
if (!loading) {
|
|
322
|
+
loading = importCompiler(name, project);
|
|
323
|
+
this.compilers.set(name, loading);
|
|
324
|
+
}
|
|
325
|
+
return loading;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
async function importCompiler(name, project) {
|
|
329
|
+
let resolved = null;
|
|
330
|
+
try {
|
|
331
|
+
resolved = (0, import_module.createRequire)(import_path.default.join(project.root, "package.json")).resolve(name, { paths: [project.root, project.projectPath] });
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
const module2 = resolved ? await import((0, import_url.pathToFileURL)(resolved).href) : name === "less" ? await import("less") : await import("sass");
|
|
335
|
+
const entry = name === "less" ? "render" : "renderSync";
|
|
336
|
+
return typeof module2[entry] === "function" ? module2 : module2.default;
|
|
337
|
+
}
|
|
338
|
+
function toRoot(file) {
|
|
339
|
+
const dir = import_path.default.posix.dirname(file);
|
|
340
|
+
return dir === "." ? "." : dir.split("/").map(() => "..").join("/");
|
|
341
|
+
}
|
|
342
|
+
function describeError(project, error) {
|
|
343
|
+
if (typeof error?.line === "number" && error.filename) {
|
|
344
|
+
const file = import_path.default.relative(project.root, error.filename).split(import_path.default.sep).join("/");
|
|
345
|
+
return `${file}:${error.line}:${(error.column ?? 0) + 1} ${error.message}`;
|
|
346
|
+
}
|
|
347
|
+
return error?.message ?? String(error);
|
|
348
|
+
}
|
|
349
|
+
|
|
180
350
|
// src/project/wxConfig.ts
|
|
181
351
|
var DEFAULT_WINDOW = {
|
|
182
352
|
navigationBarBackgroundColor: "#000000",
|
|
@@ -234,9 +404,9 @@ function createWxConfig(input) {
|
|
|
234
404
|
}
|
|
235
405
|
|
|
236
406
|
// src/project/WxmlMirror.ts
|
|
237
|
-
var
|
|
238
|
-
var
|
|
239
|
-
var
|
|
407
|
+
var import_fs2 = __toESM(require("fs"));
|
|
408
|
+
var import_os2 = __toESM(require("os"));
|
|
409
|
+
var import_path2 = __toESM(require("path"));
|
|
240
410
|
var WxmlMirror = class {
|
|
241
411
|
constructor(transform) {
|
|
242
412
|
this.transform = transform;
|
|
@@ -253,25 +423,25 @@ var WxmlMirror = class {
|
|
|
253
423
|
* 只重写源内容变化的文件;项目里已删除的文件同时从镜像目录删除,被引用时 wcc 照常报错
|
|
254
424
|
*/
|
|
255
425
|
sync(project, relPaths) {
|
|
256
|
-
const dir = this.dir ??=
|
|
426
|
+
const dir = this.dir ??= import_fs2.default.mkdtempSync(import_path2.default.join(import_os2.default.tmpdir(), "wxa-runner-wxml-"));
|
|
257
427
|
const current = new Set(relPaths);
|
|
258
428
|
for (const rel of [...this.files.keys()]) {
|
|
259
429
|
if (current.has(rel)) continue;
|
|
260
|
-
|
|
430
|
+
import_fs2.default.rmSync(import_path2.default.join(dir, rel), { force: true });
|
|
261
431
|
this.files.delete(rel);
|
|
262
432
|
}
|
|
263
433
|
for (const rel of relPaths) {
|
|
264
434
|
const source = project.readText(rel);
|
|
265
435
|
if (this.files.get(rel) === source) continue;
|
|
266
|
-
const target =
|
|
267
|
-
|
|
268
|
-
|
|
436
|
+
const target = import_path2.default.join(dir, rel);
|
|
437
|
+
import_fs2.default.mkdirSync(import_path2.default.dirname(target), { recursive: true });
|
|
438
|
+
import_fs2.default.writeFileSync(target, rel.endsWith(".wxml") ? this.transform(source, rel) : source);
|
|
269
439
|
this.files.set(rel, source);
|
|
270
440
|
}
|
|
271
441
|
return dir;
|
|
272
442
|
}
|
|
273
443
|
dispose() {
|
|
274
|
-
if (this.dir)
|
|
444
|
+
if (this.dir) import_fs2.default.rmSync(this.dir, { recursive: true, force: true });
|
|
275
445
|
this.dir = null;
|
|
276
446
|
this.files.clear();
|
|
277
447
|
}
|
|
@@ -313,15 +483,18 @@ var AppBuilder = class {
|
|
|
313
483
|
this.dirty = /* @__PURE__ */ new Set(["config", "wxml", "wxss", "js"]);
|
|
314
484
|
this.wxmlCache = null;
|
|
315
485
|
this.wxssCache = null;
|
|
486
|
+
/** 开启了 less / sass 编译插件时才有;插件设置变了就换一个 */
|
|
487
|
+
this.styleMirror = null;
|
|
316
488
|
this.wxmlMirror = options.wxmlTransform ? new WxmlMirror(options.wxmlTransform) : null;
|
|
317
489
|
}
|
|
318
490
|
/** wxmlTransform 的镜像目录;没有设置转换或还没编译过时为 null */
|
|
319
491
|
get wxmlMirrorDir() {
|
|
320
492
|
return this.wxmlMirror?.directory ?? null;
|
|
321
493
|
}
|
|
322
|
-
/** 删除 wxmlTransform
|
|
494
|
+
/** 删除 wxmlTransform 和样式预处理的镜像目录,开发服务器关闭时调用 */
|
|
323
495
|
dispose() {
|
|
324
496
|
this.wxmlMirror?.dispose();
|
|
497
|
+
this.styleMirror?.mirror.dispose();
|
|
325
498
|
}
|
|
326
499
|
/** 根据变化的文件标记需要重新构建的部分;返回 true 表示需要刷新 */
|
|
327
500
|
invalidate(changedFiles = []) {
|
|
@@ -455,14 +628,26 @@ ${pageStyles}`,
|
|
|
455
628
|
}
|
|
456
629
|
/** 与开发者工具一致,项目里的 wxss 全部交给 wcsc:被 @import 的文件(如 uni-app 的 common/main.wxss)也要在列表里 */
|
|
457
630
|
async compileStyles(units) {
|
|
458
|
-
const
|
|
459
|
-
const
|
|
460
|
-
const
|
|
631
|
+
const mirror = this.currentStyleMirror();
|
|
632
|
+
const { cwd, files } = mirror ? await mirror.sync(this.project) : { cwd: this.project.root, files: this.project.listFiles([".wxss"]) };
|
|
633
|
+
const styles = new Set(files);
|
|
634
|
+
const pageFiles = units.filter((id) => styles.has(id + ".wxss")).map((id) => `./${id}.wxss`);
|
|
635
|
+
const commonFiles = files.map((file) => `./${file}`).filter((file) => !pageFiles.includes(file));
|
|
636
|
+
const { comm, pages } = await (0, import_wxa_runner_compiler.compileWxss)({ cwd, pageFiles, commonFiles });
|
|
461
637
|
return {
|
|
462
638
|
comm: rewriteSafeAreaInsets(comm),
|
|
463
639
|
pages: Object.fromEntries(Object.entries(pages).map(([file, css]) => [file, rewriteSafeAreaInsets(css)]))
|
|
464
640
|
};
|
|
465
641
|
}
|
|
642
|
+
currentStyleMirror() {
|
|
643
|
+
const setting = this.project.styleSetting;
|
|
644
|
+
const key = JSON.stringify(setting);
|
|
645
|
+
if (this.styleMirror?.setting !== key) {
|
|
646
|
+
this.styleMirror?.mirror.dispose();
|
|
647
|
+
this.styleMirror = setting.plugins.length > 0 ? { setting: key, mirror: new StyleMirror(setting) } : null;
|
|
648
|
+
}
|
|
649
|
+
return this.styleMirror?.mirror ?? null;
|
|
650
|
+
}
|
|
466
651
|
/** $gwx 加上各页面、组件的 json / wxml 注册;逻辑层用裁剪版 $gwx,渲染层用完整版 */
|
|
467
652
|
createTemplates(app, units, unitJson, gwx) {
|
|
468
653
|
const lines = [gwx, `__wxAppCode__["app.json"] = ${toJs(app)};`];
|
|
@@ -507,28 +692,32 @@ function toJs(value) {
|
|
|
507
692
|
function classifyFile(file) {
|
|
508
693
|
if (file.endsWith(".json")) return "config";
|
|
509
694
|
if (file.endsWith(".wxml") || file.endsWith(".wxs")) return "wxml";
|
|
510
|
-
if (
|
|
695
|
+
if (/\.(wxss|less|scss|sass|css)$/.test(file)) return "wxss";
|
|
511
696
|
if (file.endsWith(".js")) return "js";
|
|
512
697
|
return null;
|
|
513
698
|
}
|
|
514
699
|
|
|
515
700
|
// src/project/MiniProgram.ts
|
|
516
|
-
var
|
|
517
|
-
var
|
|
701
|
+
var import_fs3 = __toESM(require("fs"));
|
|
702
|
+
var import_path3 = __toESM(require("path"));
|
|
518
703
|
var IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".idea", ".vscode"]);
|
|
519
704
|
var PROJECT_FILES = /* @__PURE__ */ new Set(["project.config.json", "project.private.config.json", "sitemap.json", "app.json", "wxa.setup.js"]);
|
|
520
705
|
var MiniProgram = class {
|
|
521
706
|
constructor(projectPath) {
|
|
522
707
|
this.projectPath = projectPath;
|
|
523
|
-
const projectConfig = readJsonFile(
|
|
524
|
-
this.root =
|
|
708
|
+
const projectConfig = readJsonFile(import_path3.default.join(projectPath, "project.config.json")) || {};
|
|
709
|
+
this.root = import_path3.default.resolve(projectPath, projectConfig.miniprogramRoot || "");
|
|
525
710
|
this.appid = projectConfig.appid || "touristappid";
|
|
526
|
-
if (!
|
|
711
|
+
if (!import_fs3.default.existsSync(import_path3.default.join(this.root, "app.json"))) {
|
|
527
712
|
throw new Error(`${this.root} \u4E0B\u6CA1\u6709 app.json\uFF0C\u4E0D\u662F\u6709\u6548\u7684\u5C0F\u7A0B\u5E8F\u76EE\u5F55`);
|
|
528
713
|
}
|
|
529
714
|
}
|
|
715
|
+
/** project.config.json 里与 less / sass 编译插件有关的设置;每次重新读取,开发中修改了也能生效 */
|
|
716
|
+
get styleSetting() {
|
|
717
|
+
return readStyleSetting(readJsonFile(import_path3.default.join(this.projectPath, "project.config.json"))?.setting);
|
|
718
|
+
}
|
|
530
719
|
readAppJson() {
|
|
531
|
-
const app = readJsonFile(
|
|
720
|
+
const app = readJsonFile(import_path3.default.join(this.root, "app.json"));
|
|
532
721
|
if (!app || !Array.isArray(app.pages) || app.pages.length === 0) {
|
|
533
722
|
throw new Error("app.json \u7F3A\u5C11 pages \u914D\u7F6E");
|
|
534
723
|
}
|
|
@@ -540,14 +729,14 @@ var MiniProgram = class {
|
|
|
540
729
|
/** Worker 代码所在目录,不带首尾的 /;没有配置 workers 时为 null */
|
|
541
730
|
workersDir(app) {
|
|
542
731
|
const dir = typeof app.workers === "string" ? app.workers : app.workers?.path;
|
|
543
|
-
return dir ?
|
|
732
|
+
return dir ? import_path3.default.posix.normalize(dir).replace(/^\/+|\/+$/g, "") : null;
|
|
544
733
|
}
|
|
545
734
|
/** 主包页面 + 分包页面(带 root 前缀) */
|
|
546
735
|
allPages(app) {
|
|
547
736
|
const pages = [...app.pages];
|
|
548
737
|
for (const pkg of this.subPackages(app)) {
|
|
549
738
|
for (const page of pkg.pages) {
|
|
550
|
-
const full =
|
|
739
|
+
const full = import_path3.default.posix.join(pkg.root, page);
|
|
551
740
|
if (!pages.includes(full)) pages.push(full);
|
|
552
741
|
}
|
|
553
742
|
}
|
|
@@ -559,7 +748,7 @@ var MiniProgram = class {
|
|
|
559
748
|
* 所以能解析到的引用都换成组件的绝对路径
|
|
560
749
|
*/
|
|
561
750
|
readUnitJson(id, app) {
|
|
562
|
-
const json = { ...readJsonFile(
|
|
751
|
+
const json = { ...readJsonFile(import_path3.default.join(this.root, id + ".json")) };
|
|
563
752
|
const resolve = (fromId, ref) => {
|
|
564
753
|
const resolved = this.resolveComponent(fromId, ref);
|
|
565
754
|
return resolved ? "/" + resolved : null;
|
|
@@ -580,7 +769,7 @@ var MiniProgram = class {
|
|
|
580
769
|
if (resolved || ref.startsWith("/") || /^plugin(-private)?:\/\//.test(ref)) {
|
|
581
770
|
json.usingComponents[tag] = resolved ?? ref;
|
|
582
771
|
} else {
|
|
583
|
-
const relative =
|
|
772
|
+
const relative = import_path3.default.posix.relative(import_path3.default.posix.dirname(id), ref);
|
|
584
773
|
json.usingComponents[tag] = relative.startsWith(".") ? relative : "./" + relative;
|
|
585
774
|
}
|
|
586
775
|
}
|
|
@@ -593,25 +782,25 @@ var MiniProgram = class {
|
|
|
593
782
|
*/
|
|
594
783
|
resolveComponent(fromId, ref) {
|
|
595
784
|
if (!ref || ref.startsWith("plugin://") || ref.startsWith("plugin-private://")) return null;
|
|
596
|
-
const fromDir =
|
|
785
|
+
const fromDir = import_path3.default.posix.dirname(fromId);
|
|
597
786
|
const candidates = [];
|
|
598
787
|
if (ref.startsWith("/")) {
|
|
599
788
|
candidates.push(ref.slice(1));
|
|
600
789
|
} else {
|
|
601
|
-
candidates.push(
|
|
790
|
+
candidates.push(import_path3.default.posix.join(fromDir, ref));
|
|
602
791
|
if (!ref.startsWith(".")) {
|
|
603
792
|
let dir = fromDir;
|
|
604
793
|
for (; ; ) {
|
|
605
|
-
candidates.push(
|
|
794
|
+
candidates.push(import_path3.default.posix.join(dir === "." ? "" : dir, "miniprogram_npm", ref));
|
|
606
795
|
if (dir === "." || dir === "") break;
|
|
607
|
-
dir =
|
|
796
|
+
dir = import_path3.default.posix.dirname(dir);
|
|
608
797
|
}
|
|
609
798
|
}
|
|
610
799
|
}
|
|
611
800
|
for (const candidate of candidates) {
|
|
612
|
-
const normalized =
|
|
613
|
-
for (const id of [normalized,
|
|
614
|
-
if (
|
|
801
|
+
const normalized = import_path3.default.posix.normalize(candidate);
|
|
802
|
+
for (const id of [normalized, import_path3.default.posix.join(normalized, "index")]) {
|
|
803
|
+
if (import_fs3.default.existsSync(import_path3.default.join(this.root, id + ".json"))) return id;
|
|
615
804
|
}
|
|
616
805
|
}
|
|
617
806
|
return null;
|
|
@@ -643,11 +832,11 @@ var MiniProgram = class {
|
|
|
643
832
|
listFiles(extensions) {
|
|
644
833
|
const result = [];
|
|
645
834
|
const walk = (dir, rel) => {
|
|
646
|
-
for (const entry of
|
|
835
|
+
for (const entry of import_fs3.default.readdirSync(dir, { withFileTypes: true })) {
|
|
647
836
|
if (entry.name.startsWith(".") || IGNORED_DIRS.has(entry.name)) continue;
|
|
648
837
|
const childRel = rel ? `${rel}/${entry.name}` : entry.name;
|
|
649
838
|
if (entry.isDirectory()) {
|
|
650
|
-
walk(
|
|
839
|
+
walk(import_path3.default.join(dir, entry.name), childRel);
|
|
651
840
|
} else if (extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
652
841
|
if (!rel && PROJECT_FILES.has(entry.name)) continue;
|
|
653
842
|
result.push(childRel);
|
|
@@ -658,18 +847,18 @@ var MiniProgram = class {
|
|
|
658
847
|
return result.sort();
|
|
659
848
|
}
|
|
660
849
|
exists(relPath) {
|
|
661
|
-
return
|
|
850
|
+
return import_fs3.default.existsSync(import_path3.default.join(this.root, relPath));
|
|
662
851
|
}
|
|
663
852
|
readText(relPath) {
|
|
664
|
-
return
|
|
853
|
+
return import_fs3.default.readFileSync(import_path3.default.join(this.root, relPath), "utf8");
|
|
665
854
|
}
|
|
666
855
|
isInIndependentSubPackage(id, app) {
|
|
667
856
|
return this.subPackages(app).some((pkg) => pkg.independent && id.startsWith(pkg.root.replace(/\/?$/, "/")));
|
|
668
857
|
}
|
|
669
858
|
};
|
|
670
859
|
function readJsonFile(file) {
|
|
671
|
-
if (!
|
|
672
|
-
const text =
|
|
860
|
+
if (!import_fs3.default.existsSync(file)) return null;
|
|
861
|
+
const text = import_fs3.default.readFileSync(file, "utf8");
|
|
673
862
|
try {
|
|
674
863
|
return JSON.parse(text);
|
|
675
864
|
} catch (error) {
|
|
@@ -735,9 +924,9 @@ var WechatDevClient = class {
|
|
|
735
924
|
*
|
|
736
925
|
* @throws 没有可用的登录态、超时、连不上、HTTP 状态不是 2xx 时,消息是给用户看的原因
|
|
737
926
|
*/
|
|
738
|
-
async post(
|
|
927
|
+
async post(path7, body, timeout = DEFAULT_TIMEOUT) {
|
|
739
928
|
const query = new URLSearchParams({ appid: this.appid, newticket: requireUsableTicket(await this.findTicket()), clientversion: CLIENT_VERSION });
|
|
740
|
-
const response = await fetch(`${BASE_URL}/${
|
|
929
|
+
const response = await fetch(`${BASE_URL}/${path7}?${query}`, {
|
|
741
930
|
method: "POST",
|
|
742
931
|
headers: { "Content-Type": "application/json" },
|
|
743
932
|
body: JSON.stringify(body),
|
|
@@ -749,8 +938,8 @@ var WechatDevClient = class {
|
|
|
749
938
|
return response;
|
|
750
939
|
}
|
|
751
940
|
/** 同 post,返回解析后的 JSON */
|
|
752
|
-
async postJson(
|
|
753
|
-
return (await this.post(
|
|
941
|
+
async postJson(path7, body) {
|
|
942
|
+
return (await this.post(path7, body)).json();
|
|
754
943
|
}
|
|
755
944
|
};
|
|
756
945
|
|
|
@@ -1112,28 +1301,9 @@ var DROPPED_RESPONSE_HEADERS = /* @__PURE__ */ new Set(["content-encoding", "con
|
|
|
1112
1301
|
async function handleProxy(req, res) {
|
|
1113
1302
|
const target = fromServerPath(PROXY_PREFIX, req.originalUrl);
|
|
1114
1303
|
if (!target) {
|
|
1115
|
-
res
|
|
1304
|
+
sendProxyError(res, 400, `invalid url "${req.originalUrl}"`);
|
|
1116
1305
|
return;
|
|
1117
1306
|
}
|
|
1118
|
-
let requestHeaders = {};
|
|
1119
|
-
try {
|
|
1120
|
-
requestHeaders = JSON.parse(req.get("x-wxa-headers") || "{}");
|
|
1121
|
-
} catch {
|
|
1122
|
-
}
|
|
1123
|
-
const headers = {};
|
|
1124
|
-
for (const [key, value] of Object.entries(requestHeaders)) {
|
|
1125
|
-
if (!DROPPED_REQUEST_HEADERS.has(key.toLowerCase()) && value !== void 0 && value !== null) {
|
|
1126
|
-
headers[key] = String(value);
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
const browserContentType = req.get("content-type") || "";
|
|
1130
|
-
if (browserContentType.startsWith("multipart/form-data")) {
|
|
1131
|
-
for (const key of Object.keys(headers)) {
|
|
1132
|
-
if (key.toLowerCase() === "content-type") delete headers[key];
|
|
1133
|
-
}
|
|
1134
|
-
headers["content-type"] = browserContentType;
|
|
1135
|
-
}
|
|
1136
|
-
const hasBody = !["GET", "HEAD"].includes(req.method) && Buffer.isBuffer(req.body) && req.body.length > 0;
|
|
1137
1307
|
const controller = new AbortController();
|
|
1138
1308
|
const cancel = () => controller.abort();
|
|
1139
1309
|
res.once("close", cancel);
|
|
@@ -1142,48 +1312,77 @@ async function handleProxy(req, res) {
|
|
|
1142
1312
|
try {
|
|
1143
1313
|
upstream = await fetch(target, {
|
|
1144
1314
|
method: req.method,
|
|
1145
|
-
headers,
|
|
1146
|
-
body:
|
|
1315
|
+
headers: forwardedRequestHeaders(req.get("x-wxa-headers"), req.get("content-type")),
|
|
1316
|
+
body: requestBody(req),
|
|
1147
1317
|
redirect: "follow",
|
|
1148
1318
|
signal: controller.signal
|
|
1149
1319
|
});
|
|
1150
1320
|
} catch (error) {
|
|
1151
1321
|
if (res.destroyed || controller.signal.aborted) return;
|
|
1152
|
-
|
|
1153
|
-
res.status(502).set("x-wxa-proxy-error", "1").send(cause || error.message);
|
|
1322
|
+
sendProxyError(res, 502, fetchErrorMessage(error));
|
|
1154
1323
|
return;
|
|
1155
1324
|
}
|
|
1156
|
-
|
|
1157
|
-
upstream.headers.forEach((value, key) => {
|
|
1158
|
-
responseHeaders[key] = value;
|
|
1159
|
-
});
|
|
1160
|
-
const setCookies = typeof upstream.headers.getSetCookie === "function" ? upstream.headers.getSetCookie() : [];
|
|
1161
|
-
res.status(upstream.status);
|
|
1162
|
-
upstream.headers.forEach((value, key) => {
|
|
1163
|
-
if (!DROPPED_RESPONSE_HEADERS.has(key)) res.setHeader(key, value);
|
|
1164
|
-
});
|
|
1165
|
-
res.setHeader("x-wxa-headers", encodeURIComponent(JSON.stringify(responseHeaders)));
|
|
1166
|
-
res.setHeader("x-wxa-set-cookie", encodeURIComponent(JSON.stringify(setCookies)));
|
|
1167
|
-
res.flushHeaders();
|
|
1168
|
-
if (!upstream.body) {
|
|
1169
|
-
res.end();
|
|
1170
|
-
return;
|
|
1171
|
-
}
|
|
1172
|
-
await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(upstream.body), res);
|
|
1325
|
+
await relayResponse(upstream, res);
|
|
1173
1326
|
} catch (error) {
|
|
1174
1327
|
if (!res.destroyed) res.destroy(error);
|
|
1175
1328
|
} finally {
|
|
1176
1329
|
res.off("close", cancel);
|
|
1177
1330
|
}
|
|
1178
1331
|
}
|
|
1332
|
+
function requestBody(req) {
|
|
1333
|
+
const hasBody = !["GET", "HEAD"].includes(req.method) && Buffer.isBuffer(req.body) && req.body.length > 0;
|
|
1334
|
+
return hasBody ? req.body : void 0;
|
|
1335
|
+
}
|
|
1336
|
+
function fetchErrorMessage(error) {
|
|
1337
|
+
return error?.cause?.message || error.message;
|
|
1338
|
+
}
|
|
1339
|
+
function sendProxyError(res, status, message) {
|
|
1340
|
+
res.status(status).set("x-wxa-proxy-error", "1").send(message);
|
|
1341
|
+
}
|
|
1342
|
+
function forwardedRequestHeaders(encoded, browserContentType = "") {
|
|
1343
|
+
const multipart = browserContentType.startsWith("multipart/form-data");
|
|
1344
|
+
const headers = {};
|
|
1345
|
+
for (const [key, value] of Object.entries(parseHeaders(encoded))) {
|
|
1346
|
+
const name = key.toLowerCase();
|
|
1347
|
+
if (DROPPED_REQUEST_HEADERS.has(name) || multipart && name === "content-type" || value === void 0 || value === null) continue;
|
|
1348
|
+
headers[key] = String(value);
|
|
1349
|
+
}
|
|
1350
|
+
if (multipart) headers["content-type"] = browserContentType;
|
|
1351
|
+
return headers;
|
|
1352
|
+
}
|
|
1353
|
+
function parseHeaders(encoded = "{}") {
|
|
1354
|
+
try {
|
|
1355
|
+
return JSON.parse(encoded);
|
|
1356
|
+
} catch {
|
|
1357
|
+
return {};
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
async function relayResponse(upstream, res) {
|
|
1361
|
+
const responseHeaders = {};
|
|
1362
|
+
upstream.headers.forEach((value, key) => {
|
|
1363
|
+
responseHeaders[key] = value;
|
|
1364
|
+
if (!DROPPED_RESPONSE_HEADERS.has(key)) res.setHeader(key, value);
|
|
1365
|
+
});
|
|
1366
|
+
res.status(upstream.status);
|
|
1367
|
+
res.setHeader("x-wxa-headers", encodeURIComponent(JSON.stringify(responseHeaders)));
|
|
1368
|
+
const setCookies = typeof upstream.headers.getSetCookie === "function" ? upstream.headers.getSetCookie() : [];
|
|
1369
|
+
res.setHeader("x-wxa-set-cookie", encodeURIComponent(JSON.stringify(setCookies)));
|
|
1370
|
+
res.flushHeaders();
|
|
1371
|
+
if (!upstream.body) {
|
|
1372
|
+
res.end();
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
await (0, import_promises.pipeline)(import_node_stream.Readable.fromWeb(upstream.body), res);
|
|
1376
|
+
}
|
|
1179
1377
|
|
|
1180
1378
|
// src/server/hmr.ts
|
|
1379
|
+
var STYLE_FILE = /\.(wxss|less|scss|sass|css)$/;
|
|
1181
1380
|
function planHotUpdate(files, units) {
|
|
1182
1381
|
const owners = /* @__PURE__ */ new Set([...units.pages, ...units.components]);
|
|
1183
1382
|
const plan = { templates: [], scripts: [], styles: [] };
|
|
1184
1383
|
for (const file of files) {
|
|
1185
|
-
const id = file.replace(/\.(wxml|js|wxss)$/, "");
|
|
1186
|
-
const target =
|
|
1384
|
+
const id = file.replace(/\.(wxml|js|wxss|less|scss|sass|css)$/, "");
|
|
1385
|
+
const target = STYLE_FILE.test(file) ? plan.styles : !owners.has(id) ? null : file.endsWith(".wxml") ? plan.templates : file.endsWith(".js") ? plan.scripts : null;
|
|
1187
1386
|
if (!target) return null;
|
|
1188
1387
|
if (!target.includes(id)) target.push(id);
|
|
1189
1388
|
}
|
|
@@ -1387,16 +1586,16 @@ function respond(action) {
|
|
|
1387
1586
|
}
|
|
1388
1587
|
|
|
1389
1588
|
// src/server/setup.ts
|
|
1390
|
-
var
|
|
1391
|
-
var
|
|
1589
|
+
var import_fs4 = __toESM(require("fs"));
|
|
1590
|
+
var import_path4 = __toESM(require("path"));
|
|
1392
1591
|
var import_esbuild2 = require("esbuild");
|
|
1393
1592
|
var SETUP_FILE_NAMES = ["wxa.setup.ts", "wxa.setup.js", "wxa.setup.mjs"];
|
|
1394
1593
|
var SETUP_MODULE = "@open-wxa/wxa-runner-core/setup";
|
|
1395
1594
|
var ENV_PREFIX = "WXA_";
|
|
1396
1595
|
function findSetupFile(projectPath) {
|
|
1397
1596
|
for (const name of SETUP_FILE_NAMES) {
|
|
1398
|
-
const file =
|
|
1399
|
-
if (
|
|
1597
|
+
const file = import_path4.default.join(projectPath, name);
|
|
1598
|
+
if (import_fs4.default.existsSync(file)) return file;
|
|
1400
1599
|
}
|
|
1401
1600
|
return null;
|
|
1402
1601
|
}
|
|
@@ -1408,7 +1607,7 @@ function collectSetupEnv(env) {
|
|
|
1408
1607
|
async function bundleSetup(file) {
|
|
1409
1608
|
const result = await (0, import_esbuild2.build)({
|
|
1410
1609
|
entryPoints: [file],
|
|
1411
|
-
absWorkingDir:
|
|
1610
|
+
absWorkingDir: import_path4.default.dirname(file),
|
|
1412
1611
|
bundle: true,
|
|
1413
1612
|
format: "esm",
|
|
1414
1613
|
platform: "browser",
|
|
@@ -1436,12 +1635,12 @@ function builtinSetupModule() {
|
|
|
1436
1635
|
|
|
1437
1636
|
// src/server/watcher.ts
|
|
1438
1637
|
var import_crypto3 = require("crypto");
|
|
1439
|
-
var
|
|
1440
|
-
var
|
|
1638
|
+
var import_fs5 = __toESM(require("fs"));
|
|
1639
|
+
var import_path5 = __toESM(require("path"));
|
|
1441
1640
|
var import_chokidar = __toESM(require("chokidar"));
|
|
1442
1641
|
var DEBOUNCE = 120;
|
|
1443
1642
|
var IGNORED = /(^|[/\\])(node_modules|\.git)([/\\]|$)/;
|
|
1444
|
-
var FINGERPRINTED = /\.(js|mjs|ts|json|wxml|wxss|wxs)$/;
|
|
1643
|
+
var FINGERPRINTED = /\.(js|mjs|ts|json|wxml|wxss|wxs|less|scss|sass|css)$/;
|
|
1445
1644
|
function watchProject(root, paths, onChange) {
|
|
1446
1645
|
let changed = /* @__PURE__ */ new Set();
|
|
1447
1646
|
let structural = false;
|
|
@@ -1465,7 +1664,7 @@ function watchProject(root, paths, onChange) {
|
|
|
1465
1664
|
const unchanged = updateFingerprint(fingerprints, file, event);
|
|
1466
1665
|
if (!scanned || unchanged) return;
|
|
1467
1666
|
if (event !== "change") structural = true;
|
|
1468
|
-
changed.add(
|
|
1667
|
+
changed.add(import_path5.default.relative(root, file).split(import_path5.default.sep).join("/"));
|
|
1469
1668
|
if (timer) clearTimeout(timer);
|
|
1470
1669
|
timer = setTimeout(flush, DEBOUNCE);
|
|
1471
1670
|
});
|
|
@@ -1479,7 +1678,7 @@ function updateFingerprint(fingerprints, file, event) {
|
|
|
1479
1678
|
if (event !== "add" && event !== "change" || !FINGERPRINTED.test(file)) return false;
|
|
1480
1679
|
let next;
|
|
1481
1680
|
try {
|
|
1482
|
-
next = (0, import_crypto3.createHash)("sha1").update(
|
|
1681
|
+
next = (0, import_crypto3.createHash)("sha1").update(import_fs5.default.readFileSync(file)).digest("hex");
|
|
1483
1682
|
} catch {
|
|
1484
1683
|
return false;
|
|
1485
1684
|
}
|
|
@@ -1533,7 +1732,12 @@ var DevServer = class {
|
|
|
1533
1732
|
/** 运行时生成的临时文件(canvas 导出、拍照等),随服务器进程存在 */
|
|
1534
1733
|
this.fileSessions = /* @__PURE__ */ new Map();
|
|
1535
1734
|
this.logger = options.logger || console;
|
|
1536
|
-
this.project = new MiniProgram(
|
|
1735
|
+
this.project = new MiniProgram(import_path6.default.resolve(options.projectPath));
|
|
1736
|
+
for (const { plugin, files } of ignoredStyleSources(this.project, this.project.styleSetting)) {
|
|
1737
|
+
this.logger.warn(
|
|
1738
|
+
`[wxa-runner] ${files.length} \u4E2A ${plugin} \u6837\u5F0F\u6587\u4EF6\u4E0D\u4F1A\u751F\u6548\uFF08\u5982 ${files[0]}\uFF09\uFF1Aproject.config.json \u7684 setting.useCompilerPlugins \u6CA1\u6709\u5F00\u542F ${plugin}\uFF0C\u5F00\u53D1\u8005\u5DE5\u5177\u540C\u6837\u4E0D\u7F16\u8BD1\u5B83\u4EEC`
|
|
1739
|
+
);
|
|
1740
|
+
}
|
|
1537
1741
|
const extended = applyExtensions(options, options.extensions ?? [], { projectPath: this.project.projectPath, root: this.project.root });
|
|
1538
1742
|
extended.middlewares.forEach(({ path: mountPath }) => assertMountPath(mountPath));
|
|
1539
1743
|
this.webviewScripts = extended.webviewScripts;
|
|
@@ -1563,7 +1767,7 @@ var DevServer = class {
|
|
|
1563
1767
|
get networkUrls() {
|
|
1564
1768
|
if (!this.lanAccess) return [];
|
|
1565
1769
|
const { port } = new URL(this.address);
|
|
1566
|
-
return Object.values(
|
|
1770
|
+
return Object.values(import_os3.default.networkInterfaces()).flat().filter((net2) => net2 && net2.family === "IPv4" && !net2.internal).map((net2) => `http://${net2.address}:${port}`);
|
|
1567
1771
|
}
|
|
1568
1772
|
async start() {
|
|
1569
1773
|
try {
|
|
@@ -1681,7 +1885,7 @@ ${error.message}`);
|
|
|
1681
1885
|
app.get(/^\/__plugin__\/([^/]+)(\/.+)$/, this.withBuild((build2, req, res, next) => {
|
|
1682
1886
|
const file = build2.plugins.find((plugin) => plugin.provider === req.params[0])?.files.get(decodeURIComponent(req.params[1]));
|
|
1683
1887
|
if (!file) return next();
|
|
1684
|
-
res.type(
|
|
1888
|
+
res.type(import_path6.default.extname(req.params[1]) || "application/octet-stream").send(file);
|
|
1685
1889
|
}));
|
|
1686
1890
|
app.get(/^\/__wxa__\/module\/(.+\.js)$/, this.withBuild((build2, req, res) => {
|
|
1687
1891
|
const id = decodeURIComponent(req.params[0]);
|
|
@@ -1692,7 +1896,7 @@ ${error.message}`);
|
|
|
1692
1896
|
res.type("js").send(this.builder.getModuleScript(id));
|
|
1693
1897
|
}));
|
|
1694
1898
|
app.get("/__wxa__/worker.js", this.withBuild((build2, _req, res) => {
|
|
1695
|
-
const hasWorker =
|
|
1899
|
+
const hasWorker = import_fs6.default.existsSync(import_path6.default.join(this.baseLib.dir, "WAWorker.js"));
|
|
1696
1900
|
res.type("js").send(workerScript(build2, { version: this.baseLib.version, hasWorker }));
|
|
1697
1901
|
}));
|
|
1698
1902
|
app.get("/__wxa__/service-bootstrap.js", this.withBuild((build2, _req, res) => {
|
|
@@ -1711,28 +1915,28 @@ ${error.message}`);
|
|
|
1711
1915
|
res.type("js").send(build2.pageFrame);
|
|
1712
1916
|
}));
|
|
1713
1917
|
app.get("/__wxa__/runtime/frame.js.map", (_req, res, next) => {
|
|
1714
|
-
const file =
|
|
1715
|
-
if (!
|
|
1716
|
-
res.type("json").send(ignoreConsoleHook(
|
|
1918
|
+
const file = import_path6.default.join(this.runtimeDir(), "frame.js.map");
|
|
1919
|
+
if (!import_fs6.default.existsSync(file)) return next();
|
|
1920
|
+
res.type("json").send(ignoreConsoleHook(import_fs6.default.readFileSync(file, "utf8")));
|
|
1717
1921
|
});
|
|
1718
1922
|
app.use("/__wxa__/runtime", import_express2.default.static(this.runtimeDir(), {
|
|
1719
1923
|
cacheControl: false,
|
|
1720
1924
|
// Service Worker 默认只能接管自己所在目录下的页面,宿主页面在根路径
|
|
1721
1925
|
setHeaders: (res, file) => {
|
|
1722
|
-
if (
|
|
1926
|
+
if (import_path6.default.basename(file) === "sw.js") res.setHeader("Service-Worker-Allowed", "/");
|
|
1723
1927
|
}
|
|
1724
1928
|
}));
|
|
1725
1929
|
const sourceMaps = /* @__PURE__ */ new Map();
|
|
1726
1930
|
app.get(/^\/__baselib__\/(.+\.js)\.map$/, (req, res, next) => {
|
|
1727
|
-
const file =
|
|
1728
|
-
if (!file.startsWith(this.baseLib.dir +
|
|
1729
|
-
if (!sourceMaps.has(file)) sourceMaps.set(file, ignoreListSourceMap(
|
|
1931
|
+
const file = import_path6.default.resolve(this.baseLib.dir, req.params[0]);
|
|
1932
|
+
if (!file.startsWith(this.baseLib.dir + import_path6.default.sep) || !import_fs6.default.existsSync(file)) return next();
|
|
1933
|
+
if (!sourceMaps.has(file)) sourceMaps.set(file, ignoreListSourceMap(import_fs6.default.readFileSync(file, "utf8"), `${import_path6.default.basename(file)}?original`));
|
|
1730
1934
|
res.type("json").send(sourceMaps.get(file));
|
|
1731
1935
|
});
|
|
1732
1936
|
app.use("/__baselib__", import_express2.default.static(this.baseLib.dir, {
|
|
1733
1937
|
maxAge: "1d",
|
|
1734
1938
|
setHeaders: (res, file) => {
|
|
1735
|
-
if (file.endsWith(".js")) res.setHeader("SourceMap", `${
|
|
1939
|
+
if (file.endsWith(".js")) res.setHeader("SourceMap", `${import_path6.default.basename(file)}.map`);
|
|
1736
1940
|
}
|
|
1737
1941
|
}));
|
|
1738
1942
|
app.use(PROXY_PREFIX, import_express2.default.raw({ type: () => true, limit: "100mb" }), handleProxy);
|
|
@@ -1792,7 +1996,7 @@ ${error.message}`);
|
|
|
1792
1996
|
return;
|
|
1793
1997
|
}
|
|
1794
1998
|
const data = node.data;
|
|
1795
|
-
res.type(
|
|
1999
|
+
res.type(import_path6.default.extname(req.params[0]));
|
|
1796
2000
|
res.setHeader("ETag", `"${node.etag}"`);
|
|
1797
2001
|
res.setHeader("Accept-Ranges", "bytes");
|
|
1798
2002
|
const ranges = req.range(data.length);
|
|
@@ -1862,11 +2066,11 @@ ${error.message}`);
|
|
|
1862
2066
|
runtimeDir() {
|
|
1863
2067
|
const candidates = [
|
|
1864
2068
|
this.options.runtimeDir,
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2069
|
+
import_path6.default.join(__dirname, "runtime"),
|
|
2070
|
+
import_path6.default.join(__dirname, "../runtime"),
|
|
2071
|
+
import_path6.default.join(__dirname, "../../dist/runtime")
|
|
1868
2072
|
].filter((dir) => !!dir);
|
|
1869
|
-
const found = candidates.find((dir) =>
|
|
2073
|
+
const found = candidates.find((dir) => import_fs6.default.existsSync(import_path6.default.join(dir, "host.js")));
|
|
1870
2074
|
if (!found) {
|
|
1871
2075
|
throw new Error("\u627E\u4E0D\u5230\u6D4F\u89C8\u5668\u7AEF\u8FD0\u884C\u65F6\uFF08host.js\uFF09\uFF0C\u8BF7\u5148\u6267\u884C pnpm build");
|
|
1872
2076
|
}
|
|
@@ -1879,7 +2083,10 @@ ${error.message}`);
|
|
|
1879
2083
|
startWatching() {
|
|
1880
2084
|
let lastBuildFailed = false;
|
|
1881
2085
|
const watched = [this.project.root];
|
|
1882
|
-
|
|
2086
|
+
const outsideRoot = (file) => import_path6.default.relative(this.project.root, file).startsWith("..");
|
|
2087
|
+
if (this.setupFile && outsideRoot(this.setupFile)) watched.push(this.setupFile);
|
|
2088
|
+
const projectConfig = import_path6.default.join(this.project.projectPath, "project.config.json");
|
|
2089
|
+
if (outsideRoot(projectConfig) && import_fs6.default.existsSync(projectConfig)) watched.push(projectConfig);
|
|
1883
2090
|
this.watcher = watchProject(this.project.root, watched, async ({ files, structural }) => {
|
|
1884
2091
|
const reloadOnly = structural || lastBuildFailed;
|
|
1885
2092
|
this.builder.invalidate(files);
|
|
@@ -1951,8 +2158,8 @@ function assertMountPath(mountPath) {
|
|
|
1951
2158
|
function resolveSetupFile(option, projectPath) {
|
|
1952
2159
|
if (option === false) return null;
|
|
1953
2160
|
if (!option) return findSetupFile(projectPath);
|
|
1954
|
-
const file =
|
|
1955
|
-
if (!
|
|
2161
|
+
const file = import_path6.default.resolve(option);
|
|
2162
|
+
if (!import_fs6.default.existsSync(file)) throw new Error(`\u627E\u4E0D\u5230 setup \u6587\u4EF6\uFF1A${file}`);
|
|
1956
2163
|
return file;
|
|
1957
2164
|
}
|
|
1958
2165
|
// Annotate the CommonJS export names for ESM import in node:
|