mp-weixin-back 0.0.16 → 0.0.18

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.cjs CHANGED
@@ -5,8 +5,9 @@ const fs = require('fs');
5
5
  const JSON5 = require('json5');
6
6
  const kolorist = require('kolorist');
7
7
  const module$1 = require('module');
8
- const generate = require('@babel/generator');
9
8
  const MagicString = require('magic-string');
9
+ const parser = require('@babel/parser');
10
+ const _traverse = require('@babel/traverse');
10
11
  const astKit = require('ast-kit');
11
12
 
12
13
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -15,398 +16,8 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
15
16
  const path__default = /*#__PURE__*/_interopDefaultCompat(path);
16
17
  const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
17
18
  const JSON5__default = /*#__PURE__*/_interopDefaultCompat(JSON5);
18
- const generate__default = /*#__PURE__*/_interopDefaultCompat(generate);
19
19
  const MagicString__default = /*#__PURE__*/_interopDefaultCompat(MagicString);
20
-
21
- const virtualFileId = "mp-weixin-back-helper";
22
-
23
- const pageContainerComp = ' <page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="false" @beforeleave="onBeforeLeave" :z-index="1" :duration="false"></page-container>\n';
24
- function isArrowFunction(func) {
25
- if (typeof func !== "function")
26
- return false;
27
- return !func.hasOwnProperty("prototype") && func.toString().includes("=>");
28
- }
29
- function compositionWalk(context, code, sfc, id) {
30
- const codeMs = new MagicString__default(code);
31
- const setupAst = astKit.babelParse(sfc.scriptSetup.loc.source, sfc.scriptSetup.lang);
32
- let pageInfo = {
33
- hasPageBack: false,
34
- pageBackFnName: "onPageBack",
35
- hasImportRef: false,
36
- backConfig: { ...context.config },
37
- onPageBackBodyAst: [],
38
- onPageBackCallNodeToRemove: null,
39
- activeFnName: "activeMpBack",
40
- inActiveFnName: "inactiveMpBack"
41
- };
42
- const activeFnCallsToModify = [];
43
- const inActiveFnCallsToModify = [];
44
- if (setupAst) {
45
- astKit.walkAST(setupAst, {
46
- enter(node) {
47
- if (node.type === "ImportDeclaration") {
48
- if (node.source.value.includes(virtualFileId)) {
49
- const importDefaultSpecifiers = node.specifiers.filter(
50
- (i) => i.type === "ImportDefaultSpecifier"
51
- );
52
- const importDefaultSpecifier = importDefaultSpecifiers[0];
53
- pageInfo.hasPageBack = true;
54
- pageInfo.pageBackFnName = importDefaultSpecifier.local.name;
55
- const importSpecifiers = node.specifiers.filter((i) => i.type === "ImportSpecifier");
56
- importSpecifiers.map((specifiers) => {
57
- if (specifiers.imported.type === "Identifier" && specifiers.imported.name === "activeMpBack") {
58
- pageInfo.activeFnName = specifiers.local.name;
59
- }
60
- if (specifiers.imported.type === "Identifier" && specifiers.imported.name === "inactiveMpBack") {
61
- pageInfo.inActiveFnName = specifiers.local.name;
62
- }
63
- });
64
- }
65
- if (node.source.value === "vue") {
66
- node.specifiers.some((specifier) => {
67
- if (specifier.local.name === "ref") {
68
- pageInfo.hasImportRef = true;
69
- return true;
70
- }
71
- return false;
72
- });
73
- }
74
- }
75
- if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageInfo.pageBackFnName) {
76
- pageInfo.onPageBackCallNodeToRemove = node;
77
- const callback = node.expression.arguments[0];
78
- const backArguments = node.expression.arguments[1];
79
- if (backArguments?.type === "ObjectExpression") {
80
- const config = new Function(
81
- // @ts-ignore
82
- `return (${(generate__default.default ? generate__default.default : generate__default)(backArguments).code});`
83
- )();
84
- Object.assign(pageInfo.backConfig, config);
85
- }
86
- if (callback && (callback.type === "ArrowFunctionExpression" || callback.type === "FunctionExpression")) {
87
- pageInfo.onPageBackBodyAst = callback.body.body;
88
- }
89
- return;
90
- }
91
- if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageInfo.activeFnName) {
92
- activeFnCallsToModify.push({
93
- start: node.expression.start,
94
- end: node.expression.end,
95
- original: sfc.scriptSetup.loc.source.substring(
96
- node.expression.start,
97
- node.expression.end
98
- ),
99
- name: pageInfo.activeFnName
100
- });
101
- }
102
- if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageInfo.inActiveFnName) {
103
- inActiveFnCallsToModify.push({
104
- start: node.expression.start,
105
- end: node.expression.end,
106
- original: sfc.scriptSetup.loc.source.substring(
107
- node.expression.start,
108
- node.expression.end
109
- ),
110
- name: pageInfo.inActiveFnName
111
- });
112
- }
113
- }
114
- });
115
- }
116
- if (!pageInfo.hasPageBack)
117
- return code;
118
- if (pageInfo.onPageBackCallNodeToRemove) {
119
- const scriptSetupOffset = sfc.scriptSetup.loc.start.offset;
120
- const nodeToRemove = pageInfo.onPageBackCallNodeToRemove;
121
- const globalStart = scriptSetupOffset + nodeToRemove.start;
122
- const globalEnd = scriptSetupOffset + nodeToRemove.end;
123
- codeMs.remove(globalStart, globalEnd);
124
- }
125
- let callbackCode = "";
126
- if (pageInfo.onPageBackBodyAst.length > 0) {
127
- const tempAstRoot = {
128
- type: "BlockStatement",
129
- body: pageInfo.onPageBackBodyAst
130
- };
131
- astKit.walkAST(tempAstRoot, {
132
- enter(node) {
133
- if (node.type === "CallExpression" && node.callee.type === "Identifier") {
134
- const createIdentifier = (name) => ({ type: "Identifier", name });
135
- if (node.callee.name === pageInfo.activeFnName) {
136
- node.arguments.unshift(createIdentifier("__MP_WEIXIN_ACTIVEBACK__"));
137
- } else if (node.callee.name === pageInfo.inActiveFnName) {
138
- node.arguments.unshift(createIdentifier("__MP_WEIXIN_INACTIVEBACK__"));
139
- }
140
- }
141
- }
142
- });
143
- callbackCode = pageInfo.onPageBackBodyAst.map((statement) => (generate__default.default ? generate__default.default : generate__default)(statement).code).join("\n");
144
- }
145
- if (code.includes("<page-container")) {
146
- context.log.debugLog(`${context.getPageById(id)}\u9875\u9762\u5DF2\u6709page-container\u7EC4\u4EF6\uFF0C\u6CE8\u5165\u5931\u8D25`);
147
- return code;
148
- }
149
- if (!pageInfo.backConfig.preventDefault) {
150
- callbackCode += "uni.navigateBack({ delta: 1 });";
151
- }
152
- const importUseMpWeixinBack = `import { useMpWeixinBack } from '${virtualFileId}'`;
153
- const importRefFromVue = !pageInfo.hasImportRef ? `import { ref } from 'vue'` : "";
154
- const stateFrequency = "let __MP_BACK_FREQUENCY__ = 1;";
155
- const statePageContainerVar = `
156
- const { __MP_BACK_SHOW_PAGE_CONTAINER__, __MP_WEIXIN_ACTIVEBACK__, __MP_WEIXIN_INACTIVEBACK__ } = useMpWeixinBack(${pageInfo.backConfig.initialValue})
157
- `;
158
- const configBack = (() => {
159
- const onPageBack = pageInfo.backConfig.onPageBack;
160
- if (!onPageBack)
161
- return "";
162
- if (typeof onPageBack !== "function") {
163
- throw new Error("`onPageBack` must be a function");
164
- }
165
- const params = JSON.stringify({ page: context.getPageById(id) });
166
- if (isArrowFunction(onPageBack) || onPageBack.toString().includes("function")) {
167
- return `(${onPageBack})(${params});`;
168
- }
169
- return `(function ${onPageBack})()`;
170
- })();
171
- const stateBeforeLeave = `
172
- const onBeforeLeave = () => {
173
- if (!__MP_BACK_SHOW_PAGE_CONTAINER__.value) {
174
- return
175
- }
176
- if (__MP_BACK_FREQUENCY__ < ${pageInfo.backConfig.frequency}) {
177
- __MP_BACK_SHOW_PAGE_CONTAINER__.value = false
178
- setTimeout(() => __MP_BACK_SHOW_PAGE_CONTAINER__.value = true, 0);
179
- __MP_BACK_FREQUENCY__++
180
- }
181
- ${configBack}
182
- ${callbackCode}
183
- };
184
- `;
185
- const { template, scriptSetup } = sfc;
186
- const tempOffsets = {
187
- start: template.loc.start.offset,
188
- end: template.loc.end.offset,
189
- content: template.content
190
- };
191
- const templateMagicString = new MagicString__default(tempOffsets.content);
192
- templateMagicString.append(pageContainerComp);
193
- codeMs.overwrite(tempOffsets.start, tempOffsets.end, templateMagicString.toString());
194
- const scriptOffsets = {
195
- start: scriptSetup.loc.start.offset,
196
- end: scriptSetup.loc.end.offset,
197
- content: scriptSetup.content || ""
198
- };
199
- const scriptMagicString = new MagicString__default(scriptOffsets.content);
200
- scriptMagicString.prepend(
201
- ` ${importRefFromVue}
202
- ${importUseMpWeixinBack}
203
- ${stateFrequency}
204
- ${statePageContainerVar}
205
- ${stateBeforeLeave} `
206
- );
207
- activeFnCallsToModify.forEach((call) => {
208
- const fnCallRegex = new RegExp(`${call.name}\\(([^)]*)\\)`, "g");
209
- const newCall = call.original.replace(fnCallRegex, (_match, args) => {
210
- if (!args.trim()) {
211
- return `${call.name}(__MP_WEIXIN_ACTIVEBACK__)`;
212
- }
213
- return `${call.name}(__MP_WEIXIN_ACTIVEBACK__, ${args})`;
214
- });
215
- scriptMagicString.overwrite(call.start, call.end, newCall);
216
- });
217
- inActiveFnCallsToModify.forEach((call) => {
218
- const fnCallRegex = new RegExp(`${call.name}\\(([^)]*)\\)`, "g");
219
- const newCall = call.original.replace(fnCallRegex, (_match, args) => {
220
- if (!args.trim()) {
221
- return `${call.name}(__MP_WEIXIN_INACTIVEBACK__)`;
222
- }
223
- return `${call.name}(__MP_WEIXIN_INACTIVEBACK__, ${args})`;
224
- });
225
- scriptMagicString.overwrite(call.start, call.end, newCall);
226
- });
227
- codeMs.overwrite(scriptOffsets.start, scriptOffsets.end, scriptMagicString.toString());
228
- return codeMs.toString();
229
- }
230
- function optionsWalk(context, code, sfc, id) {
231
- const codeMs = new MagicString__default(code);
232
- const ast = astKit.babelParse(sfc.script.loc.source, sfc.script.lang);
233
- let pageInfo = {
234
- hasPageBack: false,
235
- pageBackFnName: "onPageBack",
236
- backConfig: { ...context.config }
237
- };
238
- let exportDefaultNode = null;
239
- let dataMethodNode = null;
240
- let methodsNode = null;
241
- let onPageBackNodeMethod = null;
242
- let onPageBackNodeProperty = null;
243
- if (ast) {
244
- astKit.walkAST(ast, {
245
- enter(node) {
246
- if (node.type === "ExportDefaultDeclaration" && node.declaration.type === "ObjectExpression") {
247
- exportDefaultNode = node.declaration;
248
- const properties = node.declaration.properties;
249
- for (let i = 0; i < properties.length; i++) {
250
- const element = properties[i];
251
- if (element.type === "ObjectMethod" && element.key.type === "Identifier" && element.key.name === "data" && element.body.type === "BlockStatement") {
252
- dataMethodNode = element.body;
253
- }
254
- if (element.type === "ObjectProperty" && element.key.type === "Identifier" && element.key.name === "methods") {
255
- methodsNode = element.value;
256
- }
257
- const blockStatementCondition = element.type === "ObjectMethod" && element.key.type === "Identifier" && element.key.name === pageInfo.pageBackFnName && element.body.type === "BlockStatement";
258
- const functionExpressionCondition = element.type === "ObjectProperty" && element.key.type === "Identifier" && element.key.name === pageInfo.pageBackFnName && element.value.type === "FunctionExpression";
259
- if (blockStatementCondition) {
260
- pageInfo.hasPageBack = true;
261
- onPageBackNodeMethod = element;
262
- }
263
- if (functionExpressionCondition) {
264
- pageInfo.hasPageBack = true;
265
- onPageBackNodeProperty = element;
266
- }
267
- }
268
- }
269
- }
270
- });
271
- }
272
- if (!pageInfo.hasPageBack)
273
- return;
274
- const newDataProperty = [
275
- {
276
- type: "ObjectProperty",
277
- key: { type: "Identifier", name: "__MP_BACK_SHOW_PAGE_CONTAINER__" },
278
- value: { type: "BooleanLiteral", value: true },
279
- computed: false,
280
- shorthand: false
281
- },
282
- {
283
- type: "ObjectProperty",
284
- key: { type: "Identifier", name: "__MP_BACK_FREQUENCY__" },
285
- value: { type: "NumericLiteral", value: 1 },
286
- computed: false,
287
- shorthand: false
288
- }
289
- ];
290
- if (dataMethodNode) {
291
- const returnStatement = dataMethodNode.body.find(
292
- (node) => node.type === "ReturnStatement"
293
- );
294
- if (returnStatement && returnStatement.argument && returnStatement.argument.type === "ObjectExpression") {
295
- returnStatement.argument.properties.push(...newDataProperty);
296
- }
297
- } else if (exportDefaultNode) {
298
- const addData = {
299
- type: "ObjectMethod",
300
- key: { type: "Identifier", name: "data" },
301
- kind: "method",
302
- params: [],
303
- async: false,
304
- generator: false,
305
- computed: false,
306
- body: {
307
- type: "BlockStatement",
308
- directives: [],
309
- body: [
310
- {
311
- type: "ReturnStatement",
312
- argument: {
313
- type: "ObjectExpression",
314
- properties: newDataProperty
315
- }
316
- }
317
- ]
318
- }
319
- };
320
- exportDefaultNode.properties.push(addData);
321
- }
322
- const configBack = (() => {
323
- const onPageBack = pageInfo.backConfig.onPageBack;
324
- if (!onPageBack)
325
- return "";
326
- if (typeof onPageBack !== "function") {
327
- throw new Error("`onPageBack` must be a function");
328
- }
329
- const params = JSON.stringify({ page: context.getPageById(id) });
330
- if (isArrowFunction(onPageBack) || onPageBack.toString().includes("function")) {
331
- return `(${onPageBack})(${params});`;
332
- }
333
- return `(function ${onPageBack})()`;
334
- })();
335
- const stateBeforeLeave = `
336
- function onBeforeLeave() {
337
- if (this.__MP_BACK_FREQUENCY__ < ${pageInfo.backConfig.frequency}) {
338
- this.__MP_BACK_SHOW_PAGE_CONTAINER__ = false
339
- setTimeout(() => { this.__MP_BACK_SHOW_PAGE_CONTAINER__ = true }, 0);
340
- this.__MP_BACK_FREQUENCY__++
341
- }
342
- ${configBack}
343
- ${!pageInfo.backConfig.preventDefault ? "uni.navigateBack({ delta: 1 });" : ""}
344
- };
345
- `;
346
- const stateBeforeLeaveAst = astKit.babelParse(stateBeforeLeave);
347
- const stateBeforeLeaveNode = stateBeforeLeaveAst.body.find(
348
- (node) => node.type === "FunctionDeclaration"
349
- );
350
- const newMethodsProperty = {
351
- type: "ObjectMethod",
352
- key: {
353
- type: "Identifier",
354
- name: "onBeforeLeave"
355
- },
356
- kind: "method",
357
- generator: false,
358
- async: false,
359
- params: [],
360
- computed: false,
361
- body: {
362
- type: "BlockStatement",
363
- directives: [],
364
- body: [
365
- ...onPageBackNodeMethod ? onPageBackNodeMethod.body.body : [],
366
- ...onPageBackNodeProperty ? onPageBackNodeProperty.value.body.body : [],
367
- ...stateBeforeLeaveNode.body.body
368
- ]
369
- }
370
- };
371
- if (methodsNode) {
372
- methodsNode.properties.push(newMethodsProperty);
373
- } else if (exportDefaultNode) {
374
- const addMethods = {
375
- type: "ObjectProperty",
376
- computed: false,
377
- shorthand: false,
378
- key: {
379
- type: "Identifier",
380
- name: "methods"
381
- },
382
- value: {
383
- type: "ObjectExpression",
384
- properties: [newMethodsProperty]
385
- }
386
- };
387
- exportDefaultNode.properties.push(addMethods);
388
- }
389
- const { template, script } = sfc;
390
- const tempOffsets = {
391
- start: template.loc.start.offset,
392
- end: template.loc.end.offset,
393
- content: template.content
394
- };
395
- const templateMagicString = new MagicString__default(tempOffsets.content);
396
- templateMagicString.append(pageContainerComp);
397
- codeMs.overwrite(tempOffsets.start, tempOffsets.end, templateMagicString.toString());
398
- const scriptOffsets = {
399
- start: script.loc.start.offset,
400
- end: script.loc.end.offset
401
- };
402
- const newScriptContent = (generate__default.default ? generate__default.default : generate__default)(ast).code;
403
- codeMs.overwrite(scriptOffsets.start, scriptOffsets.end, newScriptContent);
404
- return codeMs.toString();
405
- }
406
- const vueWalker = {
407
- compositionWalk,
408
- optionsWalk
409
- };
20
+ const _traverse__default = /*#__PURE__*/_interopDefaultCompat(_traverse);
410
21
 
411
22
  let compilerPromise = null;
412
23
  async function resolveCompiler(root) {
@@ -418,10 +29,10 @@ async function resolveCompiler(root) {
418
29
  const _require = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
419
30
  const compilerPath = _require.resolve("@vue/compiler-sfc", { paths: [root] });
420
31
  return _require(compilerPath);
421
- } catch (firstError) {
32
+ } catch {
422
33
  try {
423
34
  return await import('@vue/compiler-sfc');
424
- } catch (secondError) {
35
+ } catch {
425
36
  throw new Error(
426
37
  `[mp-weixin-back] Cannot resolve @vue/compiler-sfc.
427
38
  This plugin requires @vue/compiler-sfc to be installed in your project.
@@ -434,26 +45,489 @@ Docs: https://github.com/DBAAZzz/mp-weixin-back#%EF%B8%8F-vite-\u914D\u7F6E
434
45
  })();
435
46
  return compilerPromise;
436
47
  }
437
- async function transformVueFile(code, id) {
438
- try {
439
- const sfcCompiler = await resolveCompiler(this.config.root);
440
- const sfc = sfcCompiler.parse(code).descriptor;
441
- const { template, script, scriptSetup } = sfc;
442
- if (!template?.content) {
443
- return code;
48
+
49
+ const virtualFileId = "mp-weixin-back-helper";
50
+ const resolvedVirtualFileId = "\0" + virtualFileId;
51
+ const ON_PAGE_BACK = "onPageBack";
52
+ const BEFORE_LEAVE_HANDLER = "__MP_BACK_ON_BEFORE_LEAVE__";
53
+
54
+ class MpBackConfigError extends Error {
55
+ constructor(message) {
56
+ super(message);
57
+ this.name = "MpBackConfigError";
58
+ }
59
+ }
60
+
61
+ const OPTION_TYPES = {
62
+ preventDefault: { nodeType: "BooleanLiteral", label: "\u5E03\u5C14" },
63
+ initialValue: { nodeType: "BooleanLiteral", label: "\u5E03\u5C14" },
64
+ frequency: { nodeType: "NumericLiteral", label: "\u6570\u5B57" }
65
+ };
66
+ function extractStaticOptions(node, where) {
67
+ const result = {};
68
+ for (const prop of node.properties) {
69
+ if (prop.type === "SpreadElement") {
70
+ throw new MpBackConfigError(`${where}\uFF1AonPageBack \u914D\u7F6E\u4E0D\u652F\u6301\u5C55\u5F00\u8FD0\u7B97\u7B26\uFF0C\u914D\u7F6E\u5728\u6784\u5EFA\u671F\u9759\u6001\u8BFB\u53D6`);
71
+ }
72
+ if (prop.type === "ObjectMethod")
73
+ continue;
74
+ if (prop.computed) {
75
+ throw new MpBackConfigError(`${where}\uFF1AonPageBack \u914D\u7F6E\u4E0D\u652F\u6301\u8BA1\u7B97\u5C5E\u6027\u540D\uFF0C\u914D\u7F6E\u5728\u6784\u5EFA\u671F\u9759\u6001\u8BFB\u53D6`);
76
+ }
77
+ const key = prop.key.type === "Identifier" ? prop.key.name : prop.key.type === "StringLiteral" ? prop.key.value : null;
78
+ if (key === null) {
79
+ throw new MpBackConfigError(`${where}\uFF1AonPageBack \u914D\u7F6E\u7684\u5C5E\u6027\u540D\u5FC5\u987B\u662F\u6807\u8BC6\u7B26\u6216\u5B57\u7B26\u4E32\u5B57\u9762\u91CF`);
80
+ }
81
+ if (!(key in OPTION_TYPES))
82
+ continue;
83
+ const expected = OPTION_TYPES[key];
84
+ const value = prop.value;
85
+ if (value.type === expected.nodeType) {
86
+ result[key] = value.value;
87
+ } else {
88
+ throw new MpBackConfigError(
89
+ `${where}\uFF1AonPageBack \u914D\u7F6E\u9879 ${key} \u5FC5\u987B\u662F${expected.label}\u5B57\u9762\u91CF\uFF08\u8BE5\u503C\u5728\u6784\u5EFA\u671F\u8BFB\u53D6\uFF0C\u6536\u5230 ${value.type}\uFF09\u3002\u5982\u9700\u8FD0\u884C\u65F6\u63A7\u5236\uFF0C\u8BF7\u4F7F\u7528 activeMpBack()/inactiveMpBack()`
90
+ );
91
+ }
92
+ }
93
+ return result;
94
+ }
95
+
96
+ const traverse$1 = _traverse__default.default ?? _traverse__default;
97
+ const DEFAULT_PAGE_CONTAINER = {
98
+ zIndex: 1,
99
+ overlay: false,
100
+ duration: false
101
+ };
102
+ function buildPageContainerTag(options = {}) {
103
+ const pc = { ...DEFAULT_PAGE_CONTAINER, ...options };
104
+ return `<page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="${pc.overlay}" @beforeleave="${BEFORE_LEAVE_HANDLER}" :z-index="${pc.zIndex}" :duration="${pc.duration}"></page-container>`;
105
+ }
106
+ function injectPageContainer(ms, template, tag) {
107
+ ms.appendLeft(template.loc.end.offset, `
108
+ ${tag}
109
+ `);
110
+ }
111
+ function buildCompositionBeforeLeave(cfg, globalHookCode) {
112
+ return `const ${BEFORE_LEAVE_HANDLER} = () => {
113
+ if (!__MP_BACK_SHOW_PAGE_CONTAINER__.value) return
114
+ if (__MP_BACK_FREQUENCY__ < ${cfg.frequency}) {
115
+ __MP_BACK_SHOW_PAGE_CONTAINER__.value = false
116
+ setTimeout(() => { __MP_BACK_SHOW_PAGE_CONTAINER__.value = true }, 0)
117
+ __MP_BACK_FREQUENCY__++
118
+ }
119
+ ${globalHookCode}
120
+ if (typeof __MP_BACK_CB__ === 'function') __MP_BACK_CB__()
121
+ ${cfg.preventDefault ? "" : "uni.navigateBack({ delta: 1 })"}
122
+ };`;
123
+ }
124
+ function buildOptionsBeforeLeaveMethod(cfg, globalHookCode) {
125
+ return `${BEFORE_LEAVE_HANDLER}() {
126
+ if (!this.__MP_BACK_SHOW_PAGE_CONTAINER__) return
127
+ if (this.__MP_BACK_FREQUENCY__ < ${cfg.frequency}) {
128
+ this.__MP_BACK_SHOW_PAGE_CONTAINER__ = false
129
+ setTimeout(() => { this.__MP_BACK_SHOW_PAGE_CONTAINER__ = true }, 0)
130
+ this.__MP_BACK_FREQUENCY__++
131
+ }
132
+ ${globalHookCode}
133
+ const __mpBackOption = this.$options.onPageBack
134
+ const __mpBackHandler = typeof __mpBackOption === 'function' ? __mpBackOption : __mpBackOption && __mpBackOption.handler
135
+ if (typeof __mpBackHandler === 'function') __mpBackHandler.call(this)
136
+ ${cfg.preventDefault ? "" : "uni.navigateBack({ delta: 1 })"}
137
+ }`;
138
+ }
139
+ const KNOWN_GLOBALS = /* @__PURE__ */ new Set([
140
+ "uni",
141
+ "wx",
142
+ "getApp",
143
+ "getCurrentPages",
144
+ "requirePlugin",
145
+ "console",
146
+ "JSON",
147
+ "Math",
148
+ "Date",
149
+ "Object",
150
+ "Array",
151
+ "String",
152
+ "Number",
153
+ "Boolean",
154
+ "Symbol",
155
+ "Promise",
156
+ "Reflect",
157
+ "RegExp",
158
+ "Map",
159
+ "Set",
160
+ "WeakMap",
161
+ "WeakSet",
162
+ "Error",
163
+ "TypeError",
164
+ "RangeError",
165
+ "parseInt",
166
+ "parseFloat",
167
+ "isNaN",
168
+ "isFinite",
169
+ "NaN",
170
+ "Infinity",
171
+ "undefined",
172
+ "globalThis",
173
+ "setTimeout",
174
+ "clearTimeout",
175
+ "setInterval",
176
+ "clearInterval",
177
+ "encodeURIComponent",
178
+ "decodeURIComponent",
179
+ "encodeURI",
180
+ "decodeURI",
181
+ "arguments"
182
+ ]);
183
+ function serializeGlobalHook(context, page) {
184
+ const hook = context.config.onPageBack;
185
+ if (!hook)
186
+ return "";
187
+ if (typeof hook !== "function") {
188
+ throw new MpBackConfigError("\u63D2\u4EF6\u914D\u7F6E\u9879 onPageBack \u5FC5\u987B\u662F\u51FD\u6570");
189
+ }
190
+ const source = hook.toString();
191
+ const { expression, ast } = normalizeFunctionSource(source);
192
+ warnOnClosureReferences(context, ast);
193
+ const params = JSON.stringify({ page });
194
+ return `;(${expression})(${params});`;
195
+ }
196
+ function normalizeFunctionSource(source) {
197
+ const candidates = [
198
+ source,
199
+ `function ${source}`,
200
+ source.startsWith("async ") ? `async function ${source.slice("async ".length)}` : null
201
+ ].filter((c) => c !== null);
202
+ for (const candidate of candidates) {
203
+ try {
204
+ const ast = parser.parse(`(${candidate});`, { sourceType: "module" });
205
+ return { expression: candidate, ast };
206
+ } catch {
207
+ }
208
+ }
209
+ throw new MpBackConfigError(
210
+ "\u65E0\u6CD5\u89E3\u6790\u63D2\u4EF6\u914D\u7F6E\u7684 onPageBack \u51FD\u6570\u6E90\u7801\uFF0C\u8BF7\u4F7F\u7528\u7BAD\u5934\u51FD\u6570\u3001function \u8868\u8FBE\u5F0F\u6216\u5BF9\u8C61\u65B9\u6CD5\u7B80\u5199"
211
+ );
212
+ }
213
+ function warnOnClosureReferences(context, ast) {
214
+ const unresolved = /* @__PURE__ */ new Set();
215
+ traverse$1(ast, {
216
+ Identifier(p) {
217
+ if (!p.isReferencedIdentifier())
218
+ return;
219
+ const name = p.node.name;
220
+ if (KNOWN_GLOBALS.has(name))
221
+ return;
222
+ if (p.scope.hasBinding(name))
223
+ return;
224
+ unresolved.add(name);
225
+ }
226
+ });
227
+ if (unresolved.size > 0) {
228
+ context.log.error(
229
+ `\u63D2\u4EF6\u914D\u7F6E\u7684 onPageBack \u5F15\u7528\u4E86\u5916\u90E8\u53D8\u91CF\uFF1A${[...unresolved].join(", ")}\u3002\u8BE5\u51FD\u6570\u4F1A\u88AB\u5E8F\u5217\u5316\u6CE8\u5165\u9875\u9762\uFF0C\u95ED\u5305\u53D8\u91CF\u5728\u8FD0\u884C\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u8BA9\u51FD\u6570\u81EA\u5305\u542B\u3002`
230
+ );
231
+ }
232
+ }
233
+
234
+ const traverse = _traverse__default.default ?? _traverse__default;
235
+ function compositionTransform(context, code, template, scriptSetup, id) {
236
+ const base = scriptSetup.loc.start.offset;
237
+ const ast = parseScript(scriptSetup.content, scriptSetup.lang);
238
+ const hasHelperImport = ast.program.body.some(
239
+ (stmt) => stmt.type === "ImportDeclaration" && stmt.source.value === virtualFileId
240
+ );
241
+ if (!hasHelperImport)
242
+ return;
243
+ const registerCalls = [];
244
+ const activeCalls = [];
245
+ const inactiveCalls = [];
246
+ traverse(ast, {
247
+ CallExpression(path) {
248
+ const exportName = resolveHelperCallee(path);
249
+ if (exportName === "onPageBack")
250
+ registerCalls.push(path.node);
251
+ else if (exportName === "activeMpBack")
252
+ activeCalls.push(path.node);
253
+ else if (exportName === "inactiveMpBack")
254
+ inactiveCalls.push(path.node);
255
+ }
256
+ });
257
+ if (registerCalls.length === 0) {
258
+ context.log.debugLog(`${id} import \u4E86 mp-weixin-back-helper \u4F46\u672A\u8C03\u7528 onPageBack\uFF0C\u8DF3\u8FC7\u6CE8\u5165`);
259
+ return;
260
+ }
261
+ let staticOptions = {};
262
+ let optionsFound = false;
263
+ for (const call of registerCalls) {
264
+ const optionsArg = call.arguments[1];
265
+ if (!optionsArg)
266
+ continue;
267
+ if (optionsArg.type !== "ObjectExpression") {
268
+ throw new MpBackConfigError(
269
+ `${id}\uFF1AonPageBack \u7684\u7B2C\u4E8C\u4E2A\u53C2\u6570\u5FC5\u987B\u662F\u5185\u8054\u5BF9\u8C61\u5B57\u9762\u91CF\uFF08\u914D\u7F6E\u5728\u6784\u5EFA\u671F\u9759\u6001\u8BFB\u53D6\uFF0C\u6536\u5230 ${optionsArg.type}\uFF09\u3002\u8BF7\u5199\u6210 onPageBack(cb, { preventDefault: true }) \u7684\u5F62\u5F0F`
270
+ );
271
+ }
272
+ if (optionsFound) {
273
+ context.log.error(`${id}\uFF1AonPageBack \u88AB\u591A\u6B21\u4F20\u5165\u914D\u7F6E\uFF0C\u4EC5\u7B2C\u4E00\u5904\u751F\u6548`);
274
+ continue;
275
+ }
276
+ staticOptions = extractStaticOptions(optionsArg, id);
277
+ optionsFound = true;
278
+ }
279
+ const cfg = {
280
+ preventDefault: context.config.preventDefault,
281
+ frequency: context.config.frequency,
282
+ initialValue: context.config.initialValue,
283
+ ...staticOptions
284
+ };
285
+ const ms = new MagicString__default(code);
286
+ injectPageContainer(ms, template, buildPageContainerTag(context.config.pageContainer));
287
+ for (const call of registerCalls) {
288
+ ms.overwrite(base + call.callee.start, base + call.callee.end, "__MP_BACK_REGISTER__");
289
+ }
290
+ for (const call of activeCalls) {
291
+ insertFirstArgument(ms, base, call, "__MP_WEIXIN_ACTIVEBACK__");
292
+ }
293
+ for (const call of inactiveCalls) {
294
+ insertFirstArgument(ms, base, call, "__MP_WEIXIN_INACTIVEBACK__");
295
+ }
296
+ const globalHookCode = serializeGlobalHook(context, context.getPageById(id));
297
+ const injected = `
298
+ import { useMpWeixinBack } from '${virtualFileId}';
299
+ let __MP_BACK_FREQUENCY__ = 1;
300
+ let __MP_BACK_CB__ = null;
301
+ const __MP_BACK_REGISTER__ = (cb) => { __MP_BACK_CB__ = cb };
302
+ const { __MP_BACK_SHOW_PAGE_CONTAINER__, __MP_WEIXIN_ACTIVEBACK__, __MP_WEIXIN_INACTIVEBACK__ } = useMpWeixinBack(${cfg.initialValue});
303
+ ${buildCompositionBeforeLeave(cfg, globalHookCode)}
304
+ `;
305
+ ms.appendRight(base, injected);
306
+ return {
307
+ code: ms.toString(),
308
+ map: ms.generateMap({ hires: true, source: id, includeContent: true })
309
+ };
310
+ }
311
+ function parseScript(content, lang) {
312
+ const plugins = [];
313
+ if (lang === "ts" || lang === "tsx")
314
+ plugins.push("typescript");
315
+ if (lang === "jsx" || lang === "tsx")
316
+ plugins.push("jsx");
317
+ return parser.parse(content, { sourceType: "module", plugins });
318
+ }
319
+ function resolveHelperCallee(path) {
320
+ const callee = path.node.callee;
321
+ if (callee.type !== "Identifier")
322
+ return null;
323
+ const binding = path.scope.getBinding(callee.name);
324
+ if (!binding)
325
+ return null;
326
+ const declaration = binding.path;
327
+ const parent = declaration.parent;
328
+ if (parent?.type !== "ImportDeclaration" || parent.source.value !== virtualFileId)
329
+ return null;
330
+ if (declaration.isImportDefaultSpecifier())
331
+ return "onPageBack";
332
+ if (declaration.isImportSpecifier()) {
333
+ const imported = declaration.node.imported;
334
+ if (imported.type === "Identifier") {
335
+ if (imported.name === "activeMpBack")
336
+ return "activeMpBack";
337
+ if (imported.name === "inactiveMpBack")
338
+ return "inactiveMpBack";
444
339
  }
445
- if (!script?.content && !scriptSetup?.content) {
446
- return code;
340
+ }
341
+ return null;
342
+ }
343
+ function insertFirstArgument(ms, base, call, name) {
344
+ const firstArg = call.arguments[0];
345
+ if (firstArg) {
346
+ ms.appendLeft(base + firstArg.start, `${name}, `);
347
+ } else {
348
+ ms.appendLeft(base + call.end - 1, name);
349
+ }
350
+ }
351
+
352
+ function optionsTransform(context, code, template, script, id) {
353
+ const base = script.loc.start.offset;
354
+ const ast = astKit.babelParse(script.content, script.lang);
355
+ let componentObject = null;
356
+ for (const stmt of ast.body) {
357
+ if (stmt.type !== "ExportDefaultDeclaration")
358
+ continue;
359
+ if (stmt.declaration.type === "ObjectExpression") {
360
+ componentObject = stmt.declaration;
361
+ } else if (stmt.declaration.type === "CallExpression" && stmt.declaration.arguments[0]?.type === "ObjectExpression") {
362
+ componentObject = stmt.declaration.arguments[0];
447
363
  }
448
- const walker = scriptSetup ? "compositionWalk" : "optionsWalk";
449
- return vueWalker[walker](this, code, sfc, id);
450
- } catch (error) {
451
- this.log.error(
452
- `Failed to transform ${id}. Please check the file is a valid Vue SFC.
453
- Docs: https://github.com/DBAAZzz/mp-weixin-back#-\u5FEB\u901F\u5F00\u59CB`
364
+ }
365
+ if (!componentObject)
366
+ return;
367
+ let dataOption = null;
368
+ let methodsOption = null;
369
+ let onPageBackOption = null;
370
+ let dataIndex = -1;
371
+ let methodsIndex = -1;
372
+ let onPageBackIndex = -1;
373
+ let lastSpreadIndex = -1;
374
+ for (let index = 0; index < componentObject.properties.length; index++) {
375
+ const prop = componentObject.properties[index];
376
+ if (prop.type === "SpreadElement") {
377
+ lastSpreadIndex = index;
378
+ continue;
379
+ }
380
+ const name = propertyName(prop);
381
+ if (name === "data") {
382
+ dataOption = prop;
383
+ dataIndex = index;
384
+ }
385
+ if (name === "methods") {
386
+ methodsOption = prop;
387
+ methodsIndex = index;
388
+ }
389
+ if (name === ON_PAGE_BACK) {
390
+ onPageBackOption = prop;
391
+ onPageBackIndex = index;
392
+ }
393
+ }
394
+ if (!onPageBackOption)
395
+ return;
396
+ if (lastSpreadIndex > onPageBackIndex) {
397
+ throw new MpBackConfigError(
398
+ `${id}\uFF1AonPageBack \u4E4B\u540E\u5B58\u5728\u5BF9\u8C61\u5C55\u5F00\uFF08...\uFF09\uFF0C\u8FD0\u884C\u65F6\u5B9E\u9645\u751F\u6548\u7684\u56DE\u8C03\u53EF\u80FD\u4E0E\u6784\u5EFA\u671F\u8BFB\u53D6\u7684\u914D\u7F6E\u4E0D\u4E00\u81F4\uFF1B\u8BF7\u5C06\u5C55\u5F00\u79FB\u5230 onPageBack \u4E4B\u524D`
454
399
  );
455
- this.log.error(String(error));
456
- return code;
400
+ }
401
+ let staticOptions = {};
402
+ if (onPageBackOption.type === "ObjectProperty" && onPageBackOption.value.type === "ObjectExpression") {
403
+ staticOptions = extractStaticOptions(onPageBackOption.value, id);
404
+ }
405
+ const cfg = {
406
+ preventDefault: context.config.preventDefault,
407
+ frequency: context.config.frequency,
408
+ initialValue: context.config.initialValue,
409
+ ...staticOptions
410
+ };
411
+ const ms = new MagicString__default(code);
412
+ injectPageContainer(ms, template, buildPageContainerTag(context.config.pageContainer));
413
+ const stateProps = `__MP_BACK_SHOW_PAGE_CONTAINER__: ${cfg.initialValue}, __MP_BACK_FREQUENCY__: 1,`;
414
+ if (dataOption) {
415
+ if (lastSpreadIndex > dataIndex) {
416
+ throw new MpBackConfigError(
417
+ `${id}\uFF1Adata \u4E4B\u540E\u5B58\u5728\u5BF9\u8C61\u5C55\u5F00\uFF08...\uFF09\uFF0C\u6CE8\u5165\u7684\u62E6\u622A\u72B6\u6001\u53EF\u80FD\u5728\u8FD0\u884C\u65F6\u88AB\u8986\u76D6\uFF1B\u8BF7\u5C06\u5C55\u5F00\u79FB\u5230 data \u4E4B\u524D`
418
+ );
419
+ }
420
+ const dataReturn = resolveDataReturnObject(dataOption);
421
+ if (!dataReturn) {
422
+ throw new MpBackConfigError(
423
+ `${id}\uFF1Adata \u5FC5\u987B\u662F\u65B9\u6CD5\u6216\u8FD4\u56DE\u5BF9\u8C61\u5B57\u9762\u91CF\u7684\u51FD\u6570\uFF08\u652F\u6301 data() { return {...} }\u3001data: () => ({...})\u3001data: function () { return {...} }\uFF09\uFF0C\u5426\u5219\u65E0\u6CD5\u6CE8\u5165\u62E6\u622A\u72B6\u6001`
424
+ );
425
+ }
426
+ ms.appendRight(base + dataReturn.start + 1, `
427
+ ${stateProps}`);
428
+ } else {
429
+ if (lastSpreadIndex >= 0) {
430
+ throw new MpBackConfigError(
431
+ `${id}\uFF1A\u7EC4\u4EF6\u9009\u9879\u4F7F\u7528\u4E86\u5BF9\u8C61\u5C55\u5F00\uFF08...\uFF09\u4E14\u672A\u663E\u5F0F\u58F0\u660E data\uFF0C\u63D2\u4EF6\u65E0\u6CD5\u5B89\u5168\u6CE8\u5165\u62E6\u622A\u72B6\u6001\uFF1B\u8BF7\u663E\u5F0F\u58F0\u660E data() { return {} }`
432
+ );
433
+ }
434
+ ms.appendRight(
435
+ base + componentObject.start + 1,
436
+ `
437
+ data() {
438
+ return { ${stateProps} }
439
+ },`
440
+ );
441
+ }
442
+ const globalHookCode = serializeGlobalHook(context, context.getPageById(id));
443
+ const method = buildOptionsBeforeLeaveMethod(cfg, globalHookCode);
444
+ if (methodsOption) {
445
+ if (lastSpreadIndex > methodsIndex) {
446
+ throw new MpBackConfigError(
447
+ `${id}\uFF1Amethods \u4E4B\u540E\u5B58\u5728\u5BF9\u8C61\u5C55\u5F00\uFF08...\uFF09\uFF0C\u6CE8\u5165\u7684\u5904\u7406\u65B9\u6CD5\u53EF\u80FD\u5728\u8FD0\u884C\u65F6\u88AB\u8986\u76D6\uFF1B\u8BF7\u5C06\u5C55\u5F00\u79FB\u5230 methods \u4E4B\u524D`
448
+ );
449
+ }
450
+ const methodsObject = methodsOption.type === "ObjectProperty" && methodsOption.value.type === "ObjectExpression" ? methodsOption.value : null;
451
+ if (!methodsObject) {
452
+ throw new MpBackConfigError(
453
+ `${id}\uFF1Amethods \u5FC5\u987B\u662F\u5BF9\u8C61\u5B57\u9762\u91CF\uFF0C\u5426\u5219\u65E0\u6CD5\u6CE8\u5165\u8FD4\u56DE\u62E6\u622A\u7684\u5904\u7406\u65B9\u6CD5`
454
+ );
455
+ }
456
+ ms.appendRight(base + methodsObject.start + 1, `
457
+ ${method},`);
458
+ } else {
459
+ if (lastSpreadIndex >= 0) {
460
+ throw new MpBackConfigError(
461
+ `${id}\uFF1A\u7EC4\u4EF6\u9009\u9879\u4F7F\u7528\u4E86\u5BF9\u8C61\u5C55\u5F00\uFF08...\uFF09\u4E14\u672A\u663E\u5F0F\u58F0\u660E methods\uFF0C\u63D2\u4EF6\u65E0\u6CD5\u5B89\u5168\u6CE8\u5165\u8FD4\u56DE\u62E6\u622A\u7684\u5904\u7406\u65B9\u6CD5\uFF1B\u8BF7\u663E\u5F0F\u58F0\u660E methods: {}`
462
+ );
463
+ }
464
+ ms.appendRight(base + componentObject.start + 1, `
465
+ methods: {
466
+ ${method},
467
+ },`);
468
+ }
469
+ return {
470
+ code: ms.toString(),
471
+ map: ms.generateMap({ hires: true, source: id, includeContent: true })
472
+ };
473
+ }
474
+ function propertyName(prop) {
475
+ if (prop.type === "SpreadElement" || prop.computed)
476
+ return null;
477
+ if (prop.key.type === "Identifier")
478
+ return prop.key.name;
479
+ if (prop.key.type === "StringLiteral")
480
+ return prop.key.value;
481
+ return null;
482
+ }
483
+ function resolveDataReturnObject(dataOption) {
484
+ if (dataOption.type === "ObjectMethod") {
485
+ return findReturnObject(dataOption.body);
486
+ }
487
+ const value = dataOption.value;
488
+ if (value.type === "FunctionExpression") {
489
+ return findReturnObject(value.body);
490
+ }
491
+ if (value.type === "ArrowFunctionExpression") {
492
+ if (value.body.type === "ObjectExpression")
493
+ return value.body;
494
+ if (value.body.type === "BlockStatement")
495
+ return findReturnObject(value.body);
496
+ }
497
+ return null;
498
+ }
499
+ function findReturnObject(block) {
500
+ for (const stmt of block.body) {
501
+ if (stmt.type === "ReturnStatement" && stmt.argument?.type === "ObjectExpression") {
502
+ return stmt.argument;
503
+ }
504
+ }
505
+ return null;
506
+ }
507
+
508
+ async function transformVueFile(context, code, id) {
509
+ if (context.hasPages && context.getPageById(id) === null) {
510
+ context.log.debugLog(`${id} \u4E0D\u662F pages.json \u4E2D\u6CE8\u518C\u7684\u9875\u9762\uFF0C\u8DF3\u8FC7\u6CE8\u5165`);
511
+ return;
512
+ }
513
+ const compiler = await resolveCompiler(context.config.root);
514
+ const { descriptor } = compiler.parse(code, { filename: id });
515
+ const template = descriptor.template;
516
+ const script = descriptor.script;
517
+ const scriptSetup = descriptor.scriptSetup;
518
+ if (!template?.content)
519
+ return;
520
+ if (template.content.includes("<page-container")) {
521
+ context.log.debugLog(`${id} \u9875\u9762\u5DF2\u6709 page-container \u7EC4\u4EF6\uFF0C\u8DF3\u8FC7\u6CE8\u5165`);
522
+ return;
523
+ }
524
+ if (scriptSetup?.content) {
525
+ const result = compositionTransform(context, code, template, scriptSetup, id);
526
+ if (result)
527
+ return result;
528
+ }
529
+ if (script?.content) {
530
+ return optionsTransform(context, code, template, script, id);
457
531
  }
458
532
  }
459
533
 
@@ -463,11 +537,15 @@ var __publicField = (obj, key, value) => {
463
537
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
464
538
  return value;
465
539
  };
466
- class pageContext {
540
+ class PageContext {
467
541
  constructor(config) {
468
542
  __publicField(this, "logPreText", "[mp-weixin-back] : ");
469
543
  __publicField(this, "config");
470
544
  __publicField(this, "pages", []);
545
+ /** pages.json 的绝对路径;找不到时为 null(此时禁用页面级过滤) */
546
+ __publicField(this, "pagesJsonPath");
547
+ /** 页面源码目录相对 root 的前缀:CLI 项目为 'src',HBuilderX 项目为 '' */
548
+ __publicField(this, "sourceBase", "src");
471
549
  __publicField(this, "log", {
472
550
  info: (text) => {
473
551
  console.log(kolorist.white(this.logPreText + text));
@@ -482,60 +560,100 @@ class pageContext {
482
560
  }
483
561
  });
484
562
  this.config = config;
563
+ this.pagesJsonPath = this.resolvePagesJsonPath();
564
+ }
565
+ /** 依次探测 src/pages.json(CLI 项目)和 pages.json(HBuilderX 项目) */
566
+ resolvePagesJsonPath() {
567
+ for (const base of ["src", ""]) {
568
+ const candidate = path__default.join(this.config.root, base, "pages.json");
569
+ if (fs__default.existsSync(candidate)) {
570
+ this.sourceBase = base;
571
+ return candidate;
572
+ }
573
+ }
574
+ return null;
575
+ }
576
+ get hasPages() {
577
+ return this.pagesJsonPath !== null;
485
578
  }
486
- getPagesJsonPath() {
487
- const pagesJsonPath = path__default.join(this.config.root, "src/pages.json");
488
- return pagesJsonPath;
579
+ isPagesJson(file) {
580
+ return this.pagesJsonPath !== null && path__default.normalize(file) === path__default.normalize(this.pagesJsonPath);
489
581
  }
490
- // 获取页面配置详情
491
- async getPagesJsonInfo() {
492
- const hasPagesJson = fs__default.existsSync(this.getPagesJsonPath());
493
- if (!hasPagesJson)
582
+ /** 读取并解析 pages.json;dev 下 pages.json 变更时会被重新调用 */
583
+ async loadPages() {
584
+ if (!this.pagesJsonPath) {
585
+ this.log.debugLog("\u672A\u627E\u5230 src/pages.json \u6216 pages.json\uFF0C\u7981\u7528\u9875\u9762\u7EA7\u8FC7\u6EE4\uFF08\u6240\u6709\u4F7F\u7528 helper \u7684 .vue \u6587\u4EF6\u90FD\u4F1A\u88AB\u5904\u7406\uFF09");
494
586
  return;
587
+ }
495
588
  try {
496
- const content = await fs__default.promises.readFile(this.getPagesJsonPath(), "utf-8");
589
+ const content = await fs__default.promises.readFile(this.pagesJsonPath, "utf-8");
497
590
  const pagesContent = JSON5__default.parse(content);
498
- const { pages, subpackages } = pagesContent;
499
- if (pages) {
500
- const mainPages = pages.reduce((acc, current) => {
501
- acc.push(current.path);
502
- return acc;
503
- }, []);
504
- this.pages.push(...mainPages);
591
+ const next = [];
592
+ for (const page of pagesContent.pages ?? []) {
593
+ next.push(page.path);
505
594
  }
506
- if (subpackages) {
507
- for (let i = 0; i < subpackages.length; i++) {
508
- const element = subpackages[i];
509
- const root = element.root;
510
- const subPages = element.pages.reduce((acc, current) => {
511
- acc.push(`${root}/${current.path}`.replace("//", "/"));
512
- return acc;
513
- }, []);
514
- this.pages.push(...subPages);
595
+ const subpackages = [...pagesContent.subpackages ?? [], ...pagesContent.subPackages ?? []];
596
+ for (const sub of subpackages) {
597
+ for (const page of sub.pages ?? []) {
598
+ next.push(`${sub.root}/${page.path}`.replace("//", "/"));
515
599
  }
516
600
  }
601
+ this.pages = next;
602
+ this.log.debugLog(`\u5DF2\u52A0\u8F7D ${next.length} \u4E2A\u9875\u9762\uFF08${this.pagesJsonPath}\uFF09`);
517
603
  } catch (error) {
518
604
  this.log.error(
519
- `Failed to read pages.json. Make sure src/pages.json exists and is valid JSON/JSON5.
520
- Path checked: ${this.getPagesJsonPath()}
605
+ `Failed to read pages.json. Make sure it is valid JSON/JSON5.
606
+ Path checked: ${this.pagesJsonPath}
521
607
  Docs: https://github.com/DBAAZzz/mp-weixin-back#%EF%B8%8F-vite-\u914D\u7F6E`
522
608
  );
523
609
  this.log.debugLog(String(error));
524
610
  }
525
611
  }
526
- // 获取指定id的page
612
+ /** 获取指定 id 对应的页面路径;不是已注册页面(或无 pages.json)时返回 null */
527
613
  getPageById(id) {
528
- const path2 = (id.split("src/")[1] || "").replace(".vue", "");
529
- return this.pages.find((i) => i === path2) || null;
614
+ if (!this.hasPages)
615
+ return null;
616
+ const filename = id.split("?")[0];
617
+ const rel = path__default.relative(path__default.join(this.config.root, this.sourceBase), filename).split(path__default.sep).join("/");
618
+ if (rel.startsWith(".."))
619
+ return null;
620
+ const pagePath = rel.replace(/\.vue$/, "");
621
+ return this.pages.includes(pagePath) ? pagePath : null;
530
622
  }
531
623
  async transform(code, id) {
532
- const result = await transformVueFile.call(this, code, id);
533
- return result;
624
+ return await transformVueFile(this, code, id);
534
625
  }
535
626
  }
536
627
 
628
+ function virtualModuleCode(dev) {
629
+ return `
630
+ import { ref } from 'vue'
631
+ const __MP_BACK_DEV__ = ${dev}
632
+ function __mpBackWarn(name) {
633
+ if (__MP_BACK_DEV__) {
634
+ console.warn('[mp-weixin-back] ' + name + ' \u672A\u751F\u6548\uFF1A\u8BE5\u6587\u4EF6\u672A\u88AB\u63D2\u4EF6\u7F16\u8BD1\u5904\u7406\u3002\u8BF7\u786E\u8BA4\u5B83\u662F pages.json \u4E2D\u6CE8\u518C\u7684\u9875\u9762\uFF0C\u4E14\u5F53\u524D\u6784\u5EFA\u5E73\u53F0\u4E3A mp-weixin\u3002')
635
+ }
636
+ }
637
+ export default function onPageBack() { __mpBackWarn('onPageBack') }
638
+ export function activeMpBack(fn) {
639
+ if (typeof fn === 'function') fn()
640
+ else __mpBackWarn('activeMpBack')
641
+ }
642
+ export function inactiveMpBack(fn) {
643
+ if (typeof fn === 'function') fn()
644
+ else __mpBackWarn('inactiveMpBack')
645
+ }
646
+ export function useMpWeixinBack(initialValue = true) {
647
+ const __MP_BACK_SHOW_PAGE_CONTAINER__ = ref(initialValue)
648
+ const __MP_WEIXIN_ACTIVEBACK__ = () => { __MP_BACK_SHOW_PAGE_CONTAINER__.value = true }
649
+ const __MP_WEIXIN_INACTIVEBACK__ = () => { __MP_BACK_SHOW_PAGE_CONTAINER__.value = false }
650
+ return { __MP_BACK_SHOW_PAGE_CONTAINER__, __MP_WEIXIN_ACTIVEBACK__, __MP_WEIXIN_INACTIVEBACK__ }
651
+ }
652
+ `;
653
+ }
537
654
  function MpBackPlugin(userOptions = {}) {
538
655
  let context;
656
+ let enabled = true;
539
657
  const defaultOptions = {
540
658
  initialValue: true,
541
659
  preventDefault: false,
@@ -547,58 +665,57 @@ function MpBackPlugin(userOptions = {}) {
547
665
  name: "vite-plugin-mp-weixin-back",
548
666
  enforce: "pre",
549
667
  configResolved(config) {
550
- context = new pageContext({ ...options, mode: config.mode, root: config.root });
668
+ const platform = process.env.UNI_PLATFORM;
669
+ enabled = !platform || platform === "mp-weixin";
670
+ context = new PageContext({ ...options, mode: config.mode, root: config.root });
671
+ if (!enabled) {
672
+ context.log.info(`\u5F53\u524D\u6784\u5EFA\u5E73\u53F0\u4E3A ${platform}\uFF0C\u63D2\u4EF6\u5DF2\u7981\u7528\uFF08\u4FDD\u7559 no-op API \u4EE5\u4FDD\u8BC1\u4EE3\u7801\u53EF\u7F16\u8BD1\uFF09`);
673
+ }
551
674
  },
552
675
  buildStart() {
553
- context.getPagesJsonInfo();
676
+ if (context.pagesJsonPath) {
677
+ this.addWatchFile(context.pagesJsonPath);
678
+ }
679
+ return context.loadPages();
680
+ },
681
+ async watchChange(id) {
682
+ if (context.isPagesJson(id)) {
683
+ await context.loadPages();
684
+ }
554
685
  },
555
686
  resolveId(id) {
556
687
  if (id === virtualFileId) {
557
- return virtualFileId;
688
+ return resolvedVirtualFileId;
558
689
  }
559
690
  },
560
691
  load(id) {
561
- if (id.includes("node_modules")) {
562
- return;
563
- }
564
- if (id === virtualFileId) {
565
- return `
566
- import { ref } from 'vue'
567
- export default function onPageBack() {}
568
- export function activeMpBack(fn = null) {
569
- fn?.()
570
- }
571
- export function inactiveMpBack(fn = null) {
572
- fn?.()
573
- }
574
- export function useMpWeixinBack(initialValue = true) {
575
- const __MP_BACK_SHOW_PAGE_CONTAINER__ = ref(initialValue)
576
-
577
- const __MP_WEIXIN_ACTIVEBACK__ = () => {
578
- __MP_BACK_SHOW_PAGE_CONTAINER__.value = true
579
- }
580
-
581
- const __MP_WEIXIN_INACTIVEBACK__ = () => {
582
- __MP_BACK_SHOW_PAGE_CONTAINER__.value = false
583
- }
584
-
585
- return {
586
- __MP_BACK_SHOW_PAGE_CONTAINER__,
587
- __MP_WEIXIN_ACTIVEBACK__,
588
- __MP_WEIXIN_INACTIVEBACK__
589
- }
590
- }
591
- `;
692
+ if (id === resolvedVirtualFileId) {
693
+ return virtualModuleCode(context.config.mode !== "production");
592
694
  }
593
695
  },
594
696
  async transform(code, id) {
595
- if (id.includes("node_modules") || !id.includes(".vue")) {
697
+ if (!enabled)
698
+ return;
699
+ const [filename, rawQuery] = id.split("?", 2);
700
+ if (filename.includes("node_modules"))
701
+ return;
702
+ if (!filename.endsWith(".vue"))
703
+ return;
704
+ if (rawQuery && new URLSearchParams(rawQuery).has("vue"))
705
+ return;
706
+ if (!code.includes(virtualFileId) && !code.includes(ON_PAGE_BACK))
707
+ return;
708
+ try {
709
+ return await context.transform(code, id);
710
+ } catch (error) {
711
+ if (error instanceof MpBackConfigError) {
712
+ this.error(`[mp-weixin-back] ${error.message}`);
713
+ }
714
+ const message = error instanceof Error ? error.message : String(error);
715
+ context.log.error(`Failed to transform ${id}: ${message}`);
716
+ this.warn(`[mp-weixin-back] \u8F6C\u6362 ${id} \u5931\u8D25\uFF0C\u8BE5\u9875\u9762\u7684\u8FD4\u56DE\u62E6\u622A\u672A\u751F\u6548\uFF1A${message}`);
596
717
  return;
597
718
  }
598
- return {
599
- code: await context.transform(code, id),
600
- map: null
601
- };
602
719
  }
603
720
  };
604
721
  }