@open-wxa/wxa-runner-core 0.8.2 → 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 +119 -3
- package/dist/index.d.ts +119 -3
- package/dist/index.js +478 -170
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +478 -170
- 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",
|
|
@@ -233,6 +403,50 @@ function createWxConfig(input) {
|
|
|
233
403
|
};
|
|
234
404
|
}
|
|
235
405
|
|
|
406
|
+
// src/project/WxmlMirror.ts
|
|
407
|
+
var import_fs2 = __toESM(require("fs"));
|
|
408
|
+
var import_os2 = __toESM(require("os"));
|
|
409
|
+
var import_path2 = __toESM(require("path"));
|
|
410
|
+
var WxmlMirror = class {
|
|
411
|
+
constructor(transform) {
|
|
412
|
+
this.transform = transform;
|
|
413
|
+
this.dir = null;
|
|
414
|
+
/** 已写入的文件:源内容(判断要不要重新转换)与写入的内容 */
|
|
415
|
+
this.files = /* @__PURE__ */ new Map();
|
|
416
|
+
}
|
|
417
|
+
/** 镜像目录;还没同步过时为 null */
|
|
418
|
+
get directory() {
|
|
419
|
+
return this.dir;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* 让镜像目录与项目里的 wxml / wxs 一致,返回镜像目录。
|
|
423
|
+
* 只重写源内容变化的文件;项目里已删除的文件同时从镜像目录删除,被引用时 wcc 照常报错
|
|
424
|
+
*/
|
|
425
|
+
sync(project, relPaths) {
|
|
426
|
+
const dir = this.dir ??= import_fs2.default.mkdtempSync(import_path2.default.join(import_os2.default.tmpdir(), "wxa-runner-wxml-"));
|
|
427
|
+
const current = new Set(relPaths);
|
|
428
|
+
for (const rel of [...this.files.keys()]) {
|
|
429
|
+
if (current.has(rel)) continue;
|
|
430
|
+
import_fs2.default.rmSync(import_path2.default.join(dir, rel), { force: true });
|
|
431
|
+
this.files.delete(rel);
|
|
432
|
+
}
|
|
433
|
+
for (const rel of relPaths) {
|
|
434
|
+
const source = project.readText(rel);
|
|
435
|
+
if (this.files.get(rel) === source) continue;
|
|
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);
|
|
439
|
+
this.files.set(rel, source);
|
|
440
|
+
}
|
|
441
|
+
return dir;
|
|
442
|
+
}
|
|
443
|
+
dispose() {
|
|
444
|
+
if (this.dir) import_fs2.default.rmSync(this.dir, { recursive: true, force: true });
|
|
445
|
+
this.dir = null;
|
|
446
|
+
this.files.clear();
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
|
|
236
450
|
// src/project/AppBuilder.ts
|
|
237
451
|
var MODULE_PARAMS = [
|
|
238
452
|
"require",
|
|
@@ -259,7 +473,7 @@ var MODULE_PARAMS = [
|
|
|
259
473
|
].join(", ");
|
|
260
474
|
var ESM_SYNTAX = /(?:^|[;}])\s*(?:import\s*[{*'"]|import\s+[\w$]|export\s*[{*]|export\s+(?:default|const|let|var|function|class|async)\b)/m;
|
|
261
475
|
var AppBuilder = class {
|
|
262
|
-
constructor(project) {
|
|
476
|
+
constructor(project, options = {}) {
|
|
263
477
|
this.project = project;
|
|
264
478
|
this.version = 0;
|
|
265
479
|
/** invalidate 标记过的变化次数;每次构建记下开始时覆盖到第几次 */
|
|
@@ -269,6 +483,18 @@ var AppBuilder = class {
|
|
|
269
483
|
this.dirty = /* @__PURE__ */ new Set(["config", "wxml", "wxss", "js"]);
|
|
270
484
|
this.wxmlCache = null;
|
|
271
485
|
this.wxssCache = null;
|
|
486
|
+
/** 开启了 less / sass 编译插件时才有;插件设置变了就换一个 */
|
|
487
|
+
this.styleMirror = null;
|
|
488
|
+
this.wxmlMirror = options.wxmlTransform ? new WxmlMirror(options.wxmlTransform) : null;
|
|
489
|
+
}
|
|
490
|
+
/** wxmlTransform 的镜像目录;没有设置转换或还没编译过时为 null */
|
|
491
|
+
get wxmlMirrorDir() {
|
|
492
|
+
return this.wxmlMirror?.directory ?? null;
|
|
493
|
+
}
|
|
494
|
+
/** 删除 wxmlTransform 和样式预处理的镜像目录,开发服务器关闭时调用 */
|
|
495
|
+
dispose() {
|
|
496
|
+
this.wxmlMirror?.dispose();
|
|
497
|
+
this.styleMirror?.mirror.dispose();
|
|
272
498
|
}
|
|
273
499
|
/** 根据变化的文件标记需要重新构建的部分;返回 true 表示需要刷新 */
|
|
274
500
|
invalidate(changedFiles = []) {
|
|
@@ -393,7 +619,7 @@ ${pageStyles}`,
|
|
|
393
619
|
const json = unitJson.get(id);
|
|
394
620
|
return { path: id, tags: Object.keys({ ...json.usingComponents, ...json.componentGenerics }) };
|
|
395
621
|
});
|
|
396
|
-
const cwd = this.project.root;
|
|
622
|
+
const cwd = this.wxmlMirror ? this.wxmlMirror.sync(this.project, files.map((file) => file.slice(2))) : this.project.root;
|
|
397
623
|
const [full, cut] = await Promise.all([
|
|
398
624
|
(0, import_wxa_runner_compiler.compileWxml)({ cwd, files, components, cut: false }),
|
|
399
625
|
(0, import_wxa_runner_compiler.compileWxml)({ cwd, files, components, cut: true })
|
|
@@ -402,14 +628,26 @@ ${pageStyles}`,
|
|
|
402
628
|
}
|
|
403
629
|
/** 与开发者工具一致,项目里的 wxss 全部交给 wcsc:被 @import 的文件(如 uni-app 的 common/main.wxss)也要在列表里 */
|
|
404
630
|
async compileStyles(units) {
|
|
405
|
-
const
|
|
406
|
-
const
|
|
407
|
-
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 });
|
|
408
637
|
return {
|
|
409
638
|
comm: rewriteSafeAreaInsets(comm),
|
|
410
639
|
pages: Object.fromEntries(Object.entries(pages).map(([file, css]) => [file, rewriteSafeAreaInsets(css)]))
|
|
411
640
|
};
|
|
412
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
|
+
}
|
|
413
651
|
/** $gwx 加上各页面、组件的 json / wxml 注册;逻辑层用裁剪版 $gwx,渲染层用完整版 */
|
|
414
652
|
createTemplates(app, units, unitJson, gwx) {
|
|
415
653
|
const lines = [gwx, `__wxAppCode__["app.json"] = ${toJs(app)};`];
|
|
@@ -454,28 +692,32 @@ function toJs(value) {
|
|
|
454
692
|
function classifyFile(file) {
|
|
455
693
|
if (file.endsWith(".json")) return "config";
|
|
456
694
|
if (file.endsWith(".wxml") || file.endsWith(".wxs")) return "wxml";
|
|
457
|
-
if (
|
|
695
|
+
if (/\.(wxss|less|scss|sass|css)$/.test(file)) return "wxss";
|
|
458
696
|
if (file.endsWith(".js")) return "js";
|
|
459
697
|
return null;
|
|
460
698
|
}
|
|
461
699
|
|
|
462
700
|
// src/project/MiniProgram.ts
|
|
463
|
-
var
|
|
464
|
-
var
|
|
701
|
+
var import_fs3 = __toESM(require("fs"));
|
|
702
|
+
var import_path3 = __toESM(require("path"));
|
|
465
703
|
var IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".idea", ".vscode"]);
|
|
466
704
|
var PROJECT_FILES = /* @__PURE__ */ new Set(["project.config.json", "project.private.config.json", "sitemap.json", "app.json", "wxa.setup.js"]);
|
|
467
705
|
var MiniProgram = class {
|
|
468
706
|
constructor(projectPath) {
|
|
469
707
|
this.projectPath = projectPath;
|
|
470
|
-
const projectConfig = readJsonFile(
|
|
471
|
-
this.root =
|
|
708
|
+
const projectConfig = readJsonFile(import_path3.default.join(projectPath, "project.config.json")) || {};
|
|
709
|
+
this.root = import_path3.default.resolve(projectPath, projectConfig.miniprogramRoot || "");
|
|
472
710
|
this.appid = projectConfig.appid || "touristappid";
|
|
473
|
-
if (!
|
|
711
|
+
if (!import_fs3.default.existsSync(import_path3.default.join(this.root, "app.json"))) {
|
|
474
712
|
throw new Error(`${this.root} \u4E0B\u6CA1\u6709 app.json\uFF0C\u4E0D\u662F\u6709\u6548\u7684\u5C0F\u7A0B\u5E8F\u76EE\u5F55`);
|
|
475
713
|
}
|
|
476
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
|
+
}
|
|
477
719
|
readAppJson() {
|
|
478
|
-
const app = readJsonFile(
|
|
720
|
+
const app = readJsonFile(import_path3.default.join(this.root, "app.json"));
|
|
479
721
|
if (!app || !Array.isArray(app.pages) || app.pages.length === 0) {
|
|
480
722
|
throw new Error("app.json \u7F3A\u5C11 pages \u914D\u7F6E");
|
|
481
723
|
}
|
|
@@ -487,14 +729,14 @@ var MiniProgram = class {
|
|
|
487
729
|
/** Worker 代码所在目录,不带首尾的 /;没有配置 workers 时为 null */
|
|
488
730
|
workersDir(app) {
|
|
489
731
|
const dir = typeof app.workers === "string" ? app.workers : app.workers?.path;
|
|
490
|
-
return dir ?
|
|
732
|
+
return dir ? import_path3.default.posix.normalize(dir).replace(/^\/+|\/+$/g, "") : null;
|
|
491
733
|
}
|
|
492
734
|
/** 主包页面 + 分包页面(带 root 前缀) */
|
|
493
735
|
allPages(app) {
|
|
494
736
|
const pages = [...app.pages];
|
|
495
737
|
for (const pkg of this.subPackages(app)) {
|
|
496
738
|
for (const page of pkg.pages) {
|
|
497
|
-
const full =
|
|
739
|
+
const full = import_path3.default.posix.join(pkg.root, page);
|
|
498
740
|
if (!pages.includes(full)) pages.push(full);
|
|
499
741
|
}
|
|
500
742
|
}
|
|
@@ -506,7 +748,7 @@ var MiniProgram = class {
|
|
|
506
748
|
* 所以能解析到的引用都换成组件的绝对路径
|
|
507
749
|
*/
|
|
508
750
|
readUnitJson(id, app) {
|
|
509
|
-
const json = { ...readJsonFile(
|
|
751
|
+
const json = { ...readJsonFile(import_path3.default.join(this.root, id + ".json")) };
|
|
510
752
|
const resolve = (fromId, ref) => {
|
|
511
753
|
const resolved = this.resolveComponent(fromId, ref);
|
|
512
754
|
return resolved ? "/" + resolved : null;
|
|
@@ -527,7 +769,7 @@ var MiniProgram = class {
|
|
|
527
769
|
if (resolved || ref.startsWith("/") || /^plugin(-private)?:\/\//.test(ref)) {
|
|
528
770
|
json.usingComponents[tag] = resolved ?? ref;
|
|
529
771
|
} else {
|
|
530
|
-
const relative =
|
|
772
|
+
const relative = import_path3.default.posix.relative(import_path3.default.posix.dirname(id), ref);
|
|
531
773
|
json.usingComponents[tag] = relative.startsWith(".") ? relative : "./" + relative;
|
|
532
774
|
}
|
|
533
775
|
}
|
|
@@ -540,25 +782,25 @@ var MiniProgram = class {
|
|
|
540
782
|
*/
|
|
541
783
|
resolveComponent(fromId, ref) {
|
|
542
784
|
if (!ref || ref.startsWith("plugin://") || ref.startsWith("plugin-private://")) return null;
|
|
543
|
-
const fromDir =
|
|
785
|
+
const fromDir = import_path3.default.posix.dirname(fromId);
|
|
544
786
|
const candidates = [];
|
|
545
787
|
if (ref.startsWith("/")) {
|
|
546
788
|
candidates.push(ref.slice(1));
|
|
547
789
|
} else {
|
|
548
|
-
candidates.push(
|
|
790
|
+
candidates.push(import_path3.default.posix.join(fromDir, ref));
|
|
549
791
|
if (!ref.startsWith(".")) {
|
|
550
792
|
let dir = fromDir;
|
|
551
793
|
for (; ; ) {
|
|
552
|
-
candidates.push(
|
|
794
|
+
candidates.push(import_path3.default.posix.join(dir === "." ? "" : dir, "miniprogram_npm", ref));
|
|
553
795
|
if (dir === "." || dir === "") break;
|
|
554
|
-
dir =
|
|
796
|
+
dir = import_path3.default.posix.dirname(dir);
|
|
555
797
|
}
|
|
556
798
|
}
|
|
557
799
|
}
|
|
558
800
|
for (const candidate of candidates) {
|
|
559
|
-
const normalized =
|
|
560
|
-
for (const id of [normalized,
|
|
561
|
-
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;
|
|
562
804
|
}
|
|
563
805
|
}
|
|
564
806
|
return null;
|
|
@@ -590,11 +832,11 @@ var MiniProgram = class {
|
|
|
590
832
|
listFiles(extensions) {
|
|
591
833
|
const result = [];
|
|
592
834
|
const walk = (dir, rel) => {
|
|
593
|
-
for (const entry of
|
|
835
|
+
for (const entry of import_fs3.default.readdirSync(dir, { withFileTypes: true })) {
|
|
594
836
|
if (entry.name.startsWith(".") || IGNORED_DIRS.has(entry.name)) continue;
|
|
595
837
|
const childRel = rel ? `${rel}/${entry.name}` : entry.name;
|
|
596
838
|
if (entry.isDirectory()) {
|
|
597
|
-
walk(
|
|
839
|
+
walk(import_path3.default.join(dir, entry.name), childRel);
|
|
598
840
|
} else if (extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
599
841
|
if (!rel && PROJECT_FILES.has(entry.name)) continue;
|
|
600
842
|
result.push(childRel);
|
|
@@ -605,18 +847,18 @@ var MiniProgram = class {
|
|
|
605
847
|
return result.sort();
|
|
606
848
|
}
|
|
607
849
|
exists(relPath) {
|
|
608
|
-
return
|
|
850
|
+
return import_fs3.default.existsSync(import_path3.default.join(this.root, relPath));
|
|
609
851
|
}
|
|
610
852
|
readText(relPath) {
|
|
611
|
-
return
|
|
853
|
+
return import_fs3.default.readFileSync(import_path3.default.join(this.root, relPath), "utf8");
|
|
612
854
|
}
|
|
613
855
|
isInIndependentSubPackage(id, app) {
|
|
614
856
|
return this.subPackages(app).some((pkg) => pkg.independent && id.startsWith(pkg.root.replace(/\/?$/, "/")));
|
|
615
857
|
}
|
|
616
858
|
};
|
|
617
859
|
function readJsonFile(file) {
|
|
618
|
-
if (!
|
|
619
|
-
const text =
|
|
860
|
+
if (!import_fs3.default.existsSync(file)) return null;
|
|
861
|
+
const text = import_fs3.default.readFileSync(file, "utf8");
|
|
620
862
|
try {
|
|
621
863
|
return JSON.parse(text);
|
|
622
864
|
} catch (error) {
|
|
@@ -682,9 +924,9 @@ var WechatDevClient = class {
|
|
|
682
924
|
*
|
|
683
925
|
* @throws 没有可用的登录态、超时、连不上、HTTP 状态不是 2xx 时,消息是给用户看的原因
|
|
684
926
|
*/
|
|
685
|
-
async post(
|
|
927
|
+
async post(path7, body, timeout = DEFAULT_TIMEOUT) {
|
|
686
928
|
const query = new URLSearchParams({ appid: this.appid, newticket: requireUsableTicket(await this.findTicket()), clientversion: CLIENT_VERSION });
|
|
687
|
-
const response = await fetch(`${BASE_URL}/${
|
|
929
|
+
const response = await fetch(`${BASE_URL}/${path7}?${query}`, {
|
|
688
930
|
method: "POST",
|
|
689
931
|
headers: { "Content-Type": "application/json" },
|
|
690
932
|
body: JSON.stringify(body),
|
|
@@ -696,8 +938,8 @@ var WechatDevClient = class {
|
|
|
696
938
|
return response;
|
|
697
939
|
}
|
|
698
940
|
/** 同 post,返回解析后的 JSON */
|
|
699
|
-
async postJson(
|
|
700
|
-
return (await this.post(
|
|
941
|
+
async postJson(path7, body) {
|
|
942
|
+
return (await this.post(path7, body)).json();
|
|
701
943
|
}
|
|
702
944
|
};
|
|
703
945
|
|
|
@@ -893,12 +1135,12 @@ function webviewHtml(build2, pagePath, webviewId, options) {
|
|
|
893
1135
|
return `<!DOCTYPE html>
|
|
894
1136
|
<html>
|
|
895
1137
|
<head>
|
|
896
|
-
${webviewHead(build2, pagePath, id)}
|
|
1138
|
+
${webviewHead(build2, pagePath, id, options.scripts)}
|
|
897
1139
|
${enableHotUpdate(options.hot)}<script src="/__wxa__/page-frame.js?v=${build2.version}"></script>
|
|
898
1140
|
${pageWindow(build2, pagePath).disableScroll ? "<style>body{overflow-y:hidden}</style>" : ""}
|
|
899
1141
|
</head>
|
|
900
1142
|
<body>
|
|
901
|
-
<script>
|
|
1143
|
+
${scriptTags(options.scripts, "body")}<script>
|
|
902
1144
|
(function () {
|
|
903
1145
|
var path = ${toJs(pagePath)};
|
|
904
1146
|
if (typeof __wxAppCode__[path + '.wxss'] === 'function') __wxAppCode__[path + '.wxss']();
|
|
@@ -931,18 +1173,18 @@ self.console = __wxaConsole;
|
|
|
931
1173
|
importScripts(${modules.map((url) => toJs(url)).join(", ")});
|
|
932
1174
|
`;
|
|
933
1175
|
}
|
|
934
|
-
function pluginWebviewHtml(build2, plugin, innerPath, pagePath, webviewId) {
|
|
1176
|
+
function pluginWebviewHtml(build2, plugin, innerPath, pagePath, webviewId, options = {}) {
|
|
935
1177
|
const id = Number(webviewId) || 0;
|
|
936
1178
|
const page = plugin.files.get(`/${innerPath}.html`)?.toString("utf8") ?? `<script>console.error(${JSON.stringify(`[wxa-runner] \u63D2\u4EF6 ${plugin.alias} \u91CC\u6CA1\u6709\u9875\u9762 ${innerPath}`)})</script>`;
|
|
937
1179
|
return `<!DOCTYPE html>
|
|
938
1180
|
<html>
|
|
939
1181
|
<head>
|
|
940
|
-
${webviewHead(build2, pagePath, id)}
|
|
1182
|
+
${webviewHead(build2, pagePath, id, options.scripts)}
|
|
941
1183
|
<script src="${pluginScriptUrl(plugin, "pageframe.js")}"></script>
|
|
942
1184
|
${pageWindow(build2, pagePath).disableScroll ? "<style>body{overflow-y:hidden}</style>" : ""}
|
|
943
1185
|
</head>
|
|
944
1186
|
<body>
|
|
945
|
-
${page}
|
|
1187
|
+
${scriptTags(options.scripts, "body")}${page}
|
|
946
1188
|
<script>parent.__wxaHost.frameLoaded('webview', ${id});</script>
|
|
947
1189
|
</body>
|
|
948
1190
|
</html>`;
|
|
@@ -950,7 +1192,7 @@ ${page}
|
|
|
950
1192
|
function pageWindow(build2, pagePath) {
|
|
951
1193
|
return { ...build2.wxConfig.global?.window, ...build2.wxConfig.page?.[pagePath + ".html"]?.window };
|
|
952
1194
|
}
|
|
953
|
-
function webviewHead(build2, pagePath, id) {
|
|
1195
|
+
function webviewHead(build2, pagePath, id, scripts) {
|
|
954
1196
|
const wxConfig = { ...build2.wxConfig, window: pageWindow(build2, pagePath) };
|
|
955
1197
|
return `<meta charset="utf-8">
|
|
956
1198
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
@@ -965,7 +1207,12 @@ function webviewHead(build2, pagePath, id) {
|
|
|
965
1207
|
<style>.wxa-service-canvas{position:absolute!important;left:0!important;top:0!important;width:100%!important;height:100%!important;visibility:inherit!important;pointer-events:none}</style>
|
|
966
1208
|
<script>var __wxConfig = ${toJs(wxConfig)};</script>
|
|
967
1209
|
<script src="/__wxa__/runtime/frame.js" data-kind="webview" data-webview-id="${id}"></script>
|
|
968
|
-
<script src="/__baselib__/WAWebview.js"></script>`;
|
|
1210
|
+
${scriptTags(scripts, "head")}<script src="/__baselib__/WAWebview.js"></script>`;
|
|
1211
|
+
}
|
|
1212
|
+
function scriptTags(scripts, position) {
|
|
1213
|
+
return (scripts ?? []).filter((script) => (script.position ?? "body") === position).map((script) => script.src !== void 0 ? `<script src="${escapeHtml(script.src)}"></script>
|
|
1214
|
+
` : `<script>${script.content.replace(/<\/script/gi, "<\\/script")}</script>
|
|
1215
|
+
`).join("");
|
|
969
1216
|
}
|
|
970
1217
|
function escapeHtml(text) {
|
|
971
1218
|
return text.replace(/[&<>"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[ch]);
|
|
@@ -1054,28 +1301,9 @@ var DROPPED_RESPONSE_HEADERS = /* @__PURE__ */ new Set(["content-encoding", "con
|
|
|
1054
1301
|
async function handleProxy(req, res) {
|
|
1055
1302
|
const target = fromServerPath(PROXY_PREFIX, req.originalUrl);
|
|
1056
1303
|
if (!target) {
|
|
1057
|
-
res
|
|
1304
|
+
sendProxyError(res, 400, `invalid url "${req.originalUrl}"`);
|
|
1058
1305
|
return;
|
|
1059
1306
|
}
|
|
1060
|
-
let requestHeaders = {};
|
|
1061
|
-
try {
|
|
1062
|
-
requestHeaders = JSON.parse(req.get("x-wxa-headers") || "{}");
|
|
1063
|
-
} catch {
|
|
1064
|
-
}
|
|
1065
|
-
const headers = {};
|
|
1066
|
-
for (const [key, value] of Object.entries(requestHeaders)) {
|
|
1067
|
-
if (!DROPPED_REQUEST_HEADERS.has(key.toLowerCase()) && value !== void 0 && value !== null) {
|
|
1068
|
-
headers[key] = String(value);
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
const browserContentType = req.get("content-type") || "";
|
|
1072
|
-
if (browserContentType.startsWith("multipart/form-data")) {
|
|
1073
|
-
for (const key of Object.keys(headers)) {
|
|
1074
|
-
if (key.toLowerCase() === "content-type") delete headers[key];
|
|
1075
|
-
}
|
|
1076
|
-
headers["content-type"] = browserContentType;
|
|
1077
|
-
}
|
|
1078
|
-
const hasBody = !["GET", "HEAD"].includes(req.method) && Buffer.isBuffer(req.body) && req.body.length > 0;
|
|
1079
1307
|
const controller = new AbortController();
|
|
1080
1308
|
const cancel = () => controller.abort();
|
|
1081
1309
|
res.once("close", cancel);
|
|
@@ -1084,48 +1312,77 @@ async function handleProxy(req, res) {
|
|
|
1084
1312
|
try {
|
|
1085
1313
|
upstream = await fetch(target, {
|
|
1086
1314
|
method: req.method,
|
|
1087
|
-
headers,
|
|
1088
|
-
body:
|
|
1315
|
+
headers: forwardedRequestHeaders(req.get("x-wxa-headers"), req.get("content-type")),
|
|
1316
|
+
body: requestBody(req),
|
|
1089
1317
|
redirect: "follow",
|
|
1090
1318
|
signal: controller.signal
|
|
1091
1319
|
});
|
|
1092
1320
|
} catch (error) {
|
|
1093
1321
|
if (res.destroyed || controller.signal.aborted) return;
|
|
1094
|
-
|
|
1095
|
-
res.status(502).set("x-wxa-proxy-error", "1").send(cause || error.message);
|
|
1096
|
-
return;
|
|
1097
|
-
}
|
|
1098
|
-
const responseHeaders = {};
|
|
1099
|
-
upstream.headers.forEach((value, key) => {
|
|
1100
|
-
responseHeaders[key] = value;
|
|
1101
|
-
});
|
|
1102
|
-
const setCookies = typeof upstream.headers.getSetCookie === "function" ? upstream.headers.getSetCookie() : [];
|
|
1103
|
-
res.status(upstream.status);
|
|
1104
|
-
upstream.headers.forEach((value, key) => {
|
|
1105
|
-
if (!DROPPED_RESPONSE_HEADERS.has(key)) res.setHeader(key, value);
|
|
1106
|
-
});
|
|
1107
|
-
res.setHeader("x-wxa-headers", encodeURIComponent(JSON.stringify(responseHeaders)));
|
|
1108
|
-
res.setHeader("x-wxa-set-cookie", encodeURIComponent(JSON.stringify(setCookies)));
|
|
1109
|
-
res.flushHeaders();
|
|
1110
|
-
if (!upstream.body) {
|
|
1111
|
-
res.end();
|
|
1322
|
+
sendProxyError(res, 502, fetchErrorMessage(error));
|
|
1112
1323
|
return;
|
|
1113
1324
|
}
|
|
1114
|
-
await (
|
|
1325
|
+
await relayResponse(upstream, res);
|
|
1115
1326
|
} catch (error) {
|
|
1116
1327
|
if (!res.destroyed) res.destroy(error);
|
|
1117
1328
|
} finally {
|
|
1118
1329
|
res.off("close", cancel);
|
|
1119
1330
|
}
|
|
1120
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
|
+
}
|
|
1121
1377
|
|
|
1122
1378
|
// src/server/hmr.ts
|
|
1379
|
+
var STYLE_FILE = /\.(wxss|less|scss|sass|css)$/;
|
|
1123
1380
|
function planHotUpdate(files, units) {
|
|
1124
1381
|
const owners = /* @__PURE__ */ new Set([...units.pages, ...units.components]);
|
|
1125
1382
|
const plan = { templates: [], scripts: [], styles: [] };
|
|
1126
1383
|
for (const file of files) {
|
|
1127
|
-
const id = file.replace(/\.(wxml|js|wxss)$/, "");
|
|
1128
|
-
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;
|
|
1129
1386
|
if (!target) return null;
|
|
1130
1387
|
if (!target.includes(id)) target.push(id);
|
|
1131
1388
|
}
|
|
@@ -1329,16 +1586,16 @@ function respond(action) {
|
|
|
1329
1586
|
}
|
|
1330
1587
|
|
|
1331
1588
|
// src/server/setup.ts
|
|
1332
|
-
var
|
|
1333
|
-
var
|
|
1589
|
+
var import_fs4 = __toESM(require("fs"));
|
|
1590
|
+
var import_path4 = __toESM(require("path"));
|
|
1334
1591
|
var import_esbuild2 = require("esbuild");
|
|
1335
1592
|
var SETUP_FILE_NAMES = ["wxa.setup.ts", "wxa.setup.js", "wxa.setup.mjs"];
|
|
1336
1593
|
var SETUP_MODULE = "@open-wxa/wxa-runner-core/setup";
|
|
1337
1594
|
var ENV_PREFIX = "WXA_";
|
|
1338
1595
|
function findSetupFile(projectPath) {
|
|
1339
1596
|
for (const name of SETUP_FILE_NAMES) {
|
|
1340
|
-
const file =
|
|
1341
|
-
if (
|
|
1597
|
+
const file = import_path4.default.join(projectPath, name);
|
|
1598
|
+
if (import_fs4.default.existsSync(file)) return file;
|
|
1342
1599
|
}
|
|
1343
1600
|
return null;
|
|
1344
1601
|
}
|
|
@@ -1350,7 +1607,7 @@ function collectSetupEnv(env) {
|
|
|
1350
1607
|
async function bundleSetup(file) {
|
|
1351
1608
|
const result = await (0, import_esbuild2.build)({
|
|
1352
1609
|
entryPoints: [file],
|
|
1353
|
-
absWorkingDir:
|
|
1610
|
+
absWorkingDir: import_path4.default.dirname(file),
|
|
1354
1611
|
bundle: true,
|
|
1355
1612
|
format: "esm",
|
|
1356
1613
|
platform: "browser",
|
|
@@ -1378,12 +1635,12 @@ function builtinSetupModule() {
|
|
|
1378
1635
|
|
|
1379
1636
|
// src/server/watcher.ts
|
|
1380
1637
|
var import_crypto3 = require("crypto");
|
|
1381
|
-
var
|
|
1382
|
-
var
|
|
1638
|
+
var import_fs5 = __toESM(require("fs"));
|
|
1639
|
+
var import_path5 = __toESM(require("path"));
|
|
1383
1640
|
var import_chokidar = __toESM(require("chokidar"));
|
|
1384
1641
|
var DEBOUNCE = 120;
|
|
1385
1642
|
var IGNORED = /(^|[/\\])(node_modules|\.git)([/\\]|$)/;
|
|
1386
|
-
var FINGERPRINTED = /\.(js|mjs|ts|json|wxml|wxss|wxs)$/;
|
|
1643
|
+
var FINGERPRINTED = /\.(js|mjs|ts|json|wxml|wxss|wxs|less|scss|sass|css)$/;
|
|
1387
1644
|
function watchProject(root, paths, onChange) {
|
|
1388
1645
|
let changed = /* @__PURE__ */ new Set();
|
|
1389
1646
|
let structural = false;
|
|
@@ -1407,7 +1664,7 @@ function watchProject(root, paths, onChange) {
|
|
|
1407
1664
|
const unchanged = updateFingerprint(fingerprints, file, event);
|
|
1408
1665
|
if (!scanned || unchanged) return;
|
|
1409
1666
|
if (event !== "change") structural = true;
|
|
1410
|
-
changed.add(
|
|
1667
|
+
changed.add(import_path5.default.relative(root, file).split(import_path5.default.sep).join("/"));
|
|
1411
1668
|
if (timer) clearTimeout(timer);
|
|
1412
1669
|
timer = setTimeout(flush, DEBOUNCE);
|
|
1413
1670
|
});
|
|
@@ -1421,7 +1678,7 @@ function updateFingerprint(fingerprints, file, event) {
|
|
|
1421
1678
|
if (event !== "add" && event !== "change" || !FINGERPRINTED.test(file)) return false;
|
|
1422
1679
|
let next;
|
|
1423
1680
|
try {
|
|
1424
|
-
next = (0, import_crypto3.createHash)("sha1").update(
|
|
1681
|
+
next = (0, import_crypto3.createHash)("sha1").update(import_fs5.default.readFileSync(file)).digest("hex");
|
|
1425
1682
|
} catch {
|
|
1426
1683
|
return false;
|
|
1427
1684
|
}
|
|
@@ -1430,7 +1687,30 @@ function updateFingerprint(fingerprints, file, event) {
|
|
|
1430
1687
|
return previous === next;
|
|
1431
1688
|
}
|
|
1432
1689
|
|
|
1690
|
+
// src/server/extensions.ts
|
|
1691
|
+
function applyExtensions(base, extensions, context) {
|
|
1692
|
+
const webviewScripts = [...base.webviewScripts ?? []];
|
|
1693
|
+
const middlewares = [...base.middlewares ?? []];
|
|
1694
|
+
const transforms = base.wxmlTransform ? [base.wxmlTransform] : [];
|
|
1695
|
+
const entries = [];
|
|
1696
|
+
for (const extension of extensions) {
|
|
1697
|
+
let setup;
|
|
1698
|
+
try {
|
|
1699
|
+
setup = extension.setup(context);
|
|
1700
|
+
} catch (error) {
|
|
1701
|
+
throw new Error(`\u6269\u5C55 ${extension.name} \u521D\u59CB\u5316\u5931\u8D25\uFF1A${error.message}`);
|
|
1702
|
+
}
|
|
1703
|
+
webviewScripts.push(...setup.webviewScripts ?? []);
|
|
1704
|
+
middlewares.push(...setup.middlewares ?? []);
|
|
1705
|
+
if (setup.wxmlTransform) transforms.push(setup.wxmlTransform);
|
|
1706
|
+
if (setup.entry) entries.push({ name: extension.name, ...setup.entry });
|
|
1707
|
+
}
|
|
1708
|
+
const wxmlTransform = transforms.length === 0 ? void 0 : (code, file) => transforms.reduce((result, transform) => transform(result, file), code);
|
|
1709
|
+
return { webviewScripts, middlewares, wxmlTransform, entries };
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1433
1712
|
// src/server/DevServer.ts
|
|
1713
|
+
var RESERVED_PREFIXES = ["/__wxa__", "/__baselib__", "/__plugin__"];
|
|
1434
1714
|
var DEFAULT_DEVICE = {
|
|
1435
1715
|
model: "iPhone X",
|
|
1436
1716
|
screenWidth: 375,
|
|
@@ -1452,7 +1732,17 @@ var DevServer = class {
|
|
|
1452
1732
|
/** 运行时生成的临时文件(canvas 导出、拍照等),随服务器进程存在 */
|
|
1453
1733
|
this.fileSessions = /* @__PURE__ */ new Map();
|
|
1454
1734
|
this.logger = options.logger || console;
|
|
1455
|
-
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
|
+
}
|
|
1741
|
+
const extended = applyExtensions(options, options.extensions ?? [], { projectPath: this.project.projectPath, root: this.project.root });
|
|
1742
|
+
extended.middlewares.forEach(({ path: mountPath }) => assertMountPath(mountPath));
|
|
1743
|
+
this.webviewScripts = extended.webviewScripts;
|
|
1744
|
+
this.middlewares = extended.middlewares;
|
|
1745
|
+
this.extensionEntries = extended.entries;
|
|
1456
1746
|
this.baseLib = (0, import_wxa_runner_extractor5.resolveBaseLib)({ version: options.baseLibVersion, cacheDir: options.baseLibCacheDir });
|
|
1457
1747
|
if (!this.baseLib.compatible) {
|
|
1458
1748
|
this.logger.warn(`[wxa-runner] \u57FA\u7840\u5E93 ${this.baseLib.version} \u9AD8\u4E8E\u5DF2\u9A8C\u8BC1\u7684\u7248\u672C\u8303\u56F4\uFF0C\u53EF\u80FD\u65E0\u6CD5\u6B63\u5E38\u8FD0\u884C`);
|
|
@@ -1464,7 +1754,7 @@ var DevServer = class {
|
|
|
1464
1754
|
this.ticketSource = this.devtoolsLogin ? developerTicketSource(this.project.projectPath, this.logger) : null;
|
|
1465
1755
|
this.plugins = new PluginStore(this.ticketSource && new WechatDevClient(this.project.appid, this.ticketSource), this.logger);
|
|
1466
1756
|
this.device = { ...DEFAULT_DEVICE, ...options.device };
|
|
1467
|
-
this.builder = new AppBuilder(this.project);
|
|
1757
|
+
this.builder = new AppBuilder(this.project, { wxmlTransform: extended.wxmlTransform });
|
|
1468
1758
|
this.setupFile = resolveSetupFile(options.setup, this.project.projectPath);
|
|
1469
1759
|
this.setupEnv = collectSetupEnv(process.env);
|
|
1470
1760
|
this.app = this.createApp();
|
|
@@ -1477,7 +1767,7 @@ var DevServer = class {
|
|
|
1477
1767
|
get networkUrls() {
|
|
1478
1768
|
if (!this.lanAccess) return [];
|
|
1479
1769
|
const { port } = new URL(this.address);
|
|
1480
|
-
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}`);
|
|
1481
1771
|
}
|
|
1482
1772
|
async start() {
|
|
1483
1773
|
try {
|
|
@@ -1520,11 +1810,15 @@ ${error.message}`);
|
|
|
1520
1810
|
await new Promise((resolve) => server.close(() => resolve()));
|
|
1521
1811
|
}
|
|
1522
1812
|
this.fileSessions.clear();
|
|
1813
|
+
this.builder.dispose();
|
|
1523
1814
|
}
|
|
1524
1815
|
createApp() {
|
|
1525
1816
|
const app = (0, import_express2.default)();
|
|
1526
1817
|
const root = this.project.root;
|
|
1527
1818
|
const noStore = (res) => res.setHeader("Cache-Control", "no-store");
|
|
1819
|
+
for (const { path: mountPath, handler } of this.middlewares) {
|
|
1820
|
+
app.use(mountPath, (req, res, next) => handler(req, res, next));
|
|
1821
|
+
}
|
|
1528
1822
|
app.get("/", (_req, res) => {
|
|
1529
1823
|
noStore(res);
|
|
1530
1824
|
res.type("html").send(hostHtml());
|
|
@@ -1579,7 +1873,8 @@ ${error.message}`);
|
|
|
1579
1873
|
const pagePath = decodeURIComponent(req.params[0]);
|
|
1580
1874
|
if (!build2.pages.includes(pagePath)) return next();
|
|
1581
1875
|
const pluginPage = findPluginPage(build2, pagePath);
|
|
1582
|
-
|
|
1876
|
+
const scripts = this.webviewScripts;
|
|
1877
|
+
res.type("html").send(pluginPage ? pluginWebviewHtml(build2, pluginPage.plugin, pluginPage.innerPath, pagePath, Number(req.query.id), { scripts }) : webviewHtml(build2, pagePath, Number(req.query.id), { hot: this.hotEnabled, scripts }));
|
|
1583
1878
|
}));
|
|
1584
1879
|
app.get("/__wxa__/plugin/:provider/:version/:file(appservice.js|pageframe.js)", this.withBuild((build2, req, res, next) => {
|
|
1585
1880
|
const { provider, version, file } = req.params;
|
|
@@ -1590,7 +1885,7 @@ ${error.message}`);
|
|
|
1590
1885
|
app.get(/^\/__plugin__\/([^/]+)(\/.+)$/, this.withBuild((build2, req, res, next) => {
|
|
1591
1886
|
const file = build2.plugins.find((plugin) => plugin.provider === req.params[0])?.files.get(decodeURIComponent(req.params[1]));
|
|
1592
1887
|
if (!file) return next();
|
|
1593
|
-
res.type(
|
|
1888
|
+
res.type(import_path6.default.extname(req.params[1]) || "application/octet-stream").send(file);
|
|
1594
1889
|
}));
|
|
1595
1890
|
app.get(/^\/__wxa__\/module\/(.+\.js)$/, this.withBuild((build2, req, res) => {
|
|
1596
1891
|
const id = decodeURIComponent(req.params[0]);
|
|
@@ -1601,7 +1896,7 @@ ${error.message}`);
|
|
|
1601
1896
|
res.type("js").send(this.builder.getModuleScript(id));
|
|
1602
1897
|
}));
|
|
1603
1898
|
app.get("/__wxa__/worker.js", this.withBuild((build2, _req, res) => {
|
|
1604
|
-
const hasWorker =
|
|
1899
|
+
const hasWorker = import_fs6.default.existsSync(import_path6.default.join(this.baseLib.dir, "WAWorker.js"));
|
|
1605
1900
|
res.type("js").send(workerScript(build2, { version: this.baseLib.version, hasWorker }));
|
|
1606
1901
|
}));
|
|
1607
1902
|
app.get("/__wxa__/service-bootstrap.js", this.withBuild((build2, _req, res) => {
|
|
@@ -1620,28 +1915,28 @@ ${error.message}`);
|
|
|
1620
1915
|
res.type("js").send(build2.pageFrame);
|
|
1621
1916
|
}));
|
|
1622
1917
|
app.get("/__wxa__/runtime/frame.js.map", (_req, res, next) => {
|
|
1623
|
-
const file =
|
|
1624
|
-
if (!
|
|
1625
|
-
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")));
|
|
1626
1921
|
});
|
|
1627
1922
|
app.use("/__wxa__/runtime", import_express2.default.static(this.runtimeDir(), {
|
|
1628
1923
|
cacheControl: false,
|
|
1629
1924
|
// Service Worker 默认只能接管自己所在目录下的页面,宿主页面在根路径
|
|
1630
1925
|
setHeaders: (res, file) => {
|
|
1631
|
-
if (
|
|
1926
|
+
if (import_path6.default.basename(file) === "sw.js") res.setHeader("Service-Worker-Allowed", "/");
|
|
1632
1927
|
}
|
|
1633
1928
|
}));
|
|
1634
1929
|
const sourceMaps = /* @__PURE__ */ new Map();
|
|
1635
1930
|
app.get(/^\/__baselib__\/(.+\.js)\.map$/, (req, res, next) => {
|
|
1636
|
-
const file =
|
|
1637
|
-
if (!file.startsWith(this.baseLib.dir +
|
|
1638
|
-
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`));
|
|
1639
1934
|
res.type("json").send(sourceMaps.get(file));
|
|
1640
1935
|
});
|
|
1641
1936
|
app.use("/__baselib__", import_express2.default.static(this.baseLib.dir, {
|
|
1642
1937
|
maxAge: "1d",
|
|
1643
1938
|
setHeaders: (res, file) => {
|
|
1644
|
-
if (file.endsWith(".js")) res.setHeader("SourceMap", `${
|
|
1939
|
+
if (file.endsWith(".js")) res.setHeader("SourceMap", `${import_path6.default.basename(file)}.map`);
|
|
1645
1940
|
}
|
|
1646
1941
|
}));
|
|
1647
1942
|
app.use(PROXY_PREFIX, import_express2.default.raw({ type: () => true, limit: "100mb" }), handleProxy);
|
|
@@ -1701,7 +1996,7 @@ ${error.message}`);
|
|
|
1701
1996
|
return;
|
|
1702
1997
|
}
|
|
1703
1998
|
const data = node.data;
|
|
1704
|
-
res.type(
|
|
1999
|
+
res.type(import_path6.default.extname(req.params[0]));
|
|
1705
2000
|
res.setHeader("ETag", `"${node.etag}"`);
|
|
1706
2001
|
res.setHeader("Accept-Ranges", "bytes");
|
|
1707
2002
|
const ranges = req.range(data.length);
|
|
@@ -1771,11 +2066,11 @@ ${error.message}`);
|
|
|
1771
2066
|
runtimeDir() {
|
|
1772
2067
|
const candidates = [
|
|
1773
2068
|
this.options.runtimeDir,
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
2069
|
+
import_path6.default.join(__dirname, "runtime"),
|
|
2070
|
+
import_path6.default.join(__dirname, "../runtime"),
|
|
2071
|
+
import_path6.default.join(__dirname, "../../dist/runtime")
|
|
1777
2072
|
].filter((dir) => !!dir);
|
|
1778
|
-
const found = candidates.find((dir) =>
|
|
2073
|
+
const found = candidates.find((dir) => import_fs6.default.existsSync(import_path6.default.join(dir, "host.js")));
|
|
1779
2074
|
if (!found) {
|
|
1780
2075
|
throw new Error("\u627E\u4E0D\u5230\u6D4F\u89C8\u5668\u7AEF\u8FD0\u884C\u65F6\uFF08host.js\uFF09\uFF0C\u8BF7\u5148\u6267\u884C pnpm build");
|
|
1781
2076
|
}
|
|
@@ -1788,7 +2083,10 @@ ${error.message}`);
|
|
|
1788
2083
|
startWatching() {
|
|
1789
2084
|
let lastBuildFailed = false;
|
|
1790
2085
|
const watched = [this.project.root];
|
|
1791
|
-
|
|
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);
|
|
1792
2090
|
this.watcher = watchProject(this.project.root, watched, async ({ files, structural }) => {
|
|
1793
2091
|
const reloadOnly = structural || lastBuildFailed;
|
|
1794
2092
|
this.builder.invalidate(files);
|
|
@@ -1819,7 +2117,7 @@ var DEFAULT_PORT = 3e3;
|
|
|
1819
2117
|
var LOCAL_HOST_OF_ANY_ADDRESS = /* @__PURE__ */ new Map([["0.0.0.0", "127.0.0.1"], ["::", "localhost"]]);
|
|
1820
2118
|
async function listen(server, port, host, logger) {
|
|
1821
2119
|
for (let candidate = port ?? DEFAULT_PORT; ; candidate++) {
|
|
1822
|
-
const taken =
|
|
2120
|
+
const taken = await takenOnLocalAddresses(candidate) || !await bind(server, candidate, host);
|
|
1823
2121
|
if (!taken) {
|
|
1824
2122
|
const actual = server.address().port;
|
|
1825
2123
|
if (port === void 0 && actual !== DEFAULT_PORT) logger.warn(`[wxa-runner] \u7AEF\u53E3 ${DEFAULT_PORT} \u5DF2\u88AB\u5360\u7528\uFF0C\u6539\u7528 ${actual}`);
|
|
@@ -1838,8 +2136,8 @@ function bind(server, port, host) {
|
|
|
1838
2136
|
});
|
|
1839
2137
|
});
|
|
1840
2138
|
}
|
|
1841
|
-
async function
|
|
1842
|
-
for (const address of ["127.0.0.1", "::1"]) {
|
|
2139
|
+
async function takenOnLocalAddresses(port) {
|
|
2140
|
+
for (const address of ["127.0.0.1", "::1", "0.0.0.0", "::"]) {
|
|
1843
2141
|
const probe = import_net.default.createServer();
|
|
1844
2142
|
const free = await bind(probe, port, address).catch(() => true);
|
|
1845
2143
|
if (!free) return true;
|
|
@@ -1847,11 +2145,21 @@ async function takenOnLoopback(port) {
|
|
|
1847
2145
|
}
|
|
1848
2146
|
return false;
|
|
1849
2147
|
}
|
|
2148
|
+
function assertMountPath(mountPath) {
|
|
2149
|
+
const literal = typeof mountPath === "string" && /^(\/[\w.~-]+)+$/.test(mountPath);
|
|
2150
|
+
const lower = literal ? mountPath.toLowerCase() : "";
|
|
2151
|
+
const reserved = RESERVED_PREFIXES.some((prefix) => lower === prefix || lower.startsWith(prefix + "/"));
|
|
2152
|
+
if (!literal || reserved) {
|
|
2153
|
+
throw new Error(
|
|
2154
|
+
`\u4E2D\u95F4\u4EF6\u8DEF\u5F84 ${String(mountPath)} \u4E0D\u53EF\u7528\uFF1A\u8981\u4EE5 / \u5F00\u5934\uFF0C\u53EA\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u548C _ . ~ -\uFF08\u4E0D\u652F\u6301\u901A\u914D\u7B26\u548C\u53C2\u6570\uFF09\uFF0C\u4E0D\u80FD\u662F /\uFF0C\u4E5F\u4E0D\u80FD\u7528 wxa-runner \u4FDD\u7559\u7684\u524D\u7F00 ${RESERVED_PREFIXES.join("\u3001")}\uFF08\u4E0D\u533A\u5206\u5927\u5C0F\u5199\uFF09`
|
|
2155
|
+
);
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
1850
2158
|
function resolveSetupFile(option, projectPath) {
|
|
1851
2159
|
if (option === false) return null;
|
|
1852
2160
|
if (!option) return findSetupFile(projectPath);
|
|
1853
|
-
const file =
|
|
1854
|
-
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}`);
|
|
1855
2163
|
return file;
|
|
1856
2164
|
}
|
|
1857
2165
|
// Annotate the CommonJS export names for ESM import in node:
|