mp-weixin-back 0.0.7 → 0.0.9

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
@@ -4,9 +4,10 @@ const path = require('path');
4
4
  const fs = require('fs');
5
5
  const JSON5 = require('json5');
6
6
  const kolorist = require('kolorist');
7
- const generate = require('@babel/generator');
8
7
  const compilerSfc = require('@vue/compiler-sfc');
8
+ const generate = require('@babel/generator');
9
9
  const astKit = require('ast-kit');
10
+ const MagicString = require('magic-string');
10
11
 
11
12
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
12
13
 
@@ -14,130 +15,328 @@ const path__default = /*#__PURE__*/_interopDefaultCompat(path);
14
15
  const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
15
16
  const JSON5__default = /*#__PURE__*/_interopDefaultCompat(JSON5);
16
17
  const generate__default = /*#__PURE__*/_interopDefaultCompat(generate);
18
+ const MagicString__default = /*#__PURE__*/_interopDefaultCompat(MagicString);
17
19
 
18
20
  const virtualFileId = "mp-weixin-back-helper";
19
21
 
22
+ const pageContainerComp = ' <page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="false" @beforeleave="onBeforeLeave" :z-index="1" :duration="false"></page-container>\n';
20
23
  function isArrowFunction(func) {
21
24
  if (typeof func !== "function")
22
25
  return false;
23
26
  return !func.hasOwnProperty("prototype") && func.toString().includes("=>");
24
27
  }
25
- async function parseSFC(code) {
26
- try {
27
- return compilerSfc.parse(code).descriptor;
28
- } catch (error) {
29
- throw new Error(`\u89E3\u6790vue\u6587\u4EF6\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u6B63\u786E`);
30
- }
31
- }
32
- async function transformVueFile(code, id) {
33
- if (code.includes("<page-container")) {
34
- return code;
35
- }
36
- if (!code.includes("<template")) {
37
- return code;
38
- }
39
- const componentStr = '<page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="false" @beforeleave="onBeforeLeave" :z-index="1" :duration="false"></page-container>';
40
- const sfc = await parseSFC(code);
41
- const setupCode = sfc.scriptSetup?.loc.source;
42
- const setupAst = astKit.babelParse(setupCode || "", sfc.scriptSetup?.lang);
43
- let pageBackConfig = this.config;
44
- let hasPageBack = false, hasImportRef = false, pageBackFnName = "onPageBack", callbackCode = ``;
28
+ function compositionWalk(context, code, sfc, id) {
29
+ const codeMs = new MagicString__default(code);
30
+ const setupAst = astKit.babelParse(sfc.scriptSetup.loc.source, sfc.scriptSetup.lang);
31
+ let pageInfo = {
32
+ hasPageBack: false,
33
+ pageBackFnName: "onPageBack",
34
+ hasImportRef: false,
35
+ backConfig: { ...context.config },
36
+ callbackCode: ""
37
+ };
45
38
  if (setupAst) {
46
39
  astKit.walkAST(setupAst, {
47
40
  enter(node) {
48
- if (node.type == "ImportDeclaration" && node.source.value.includes(virtualFileId)) {
49
- const importSpecifier = node.specifiers[0];
50
- hasPageBack = true;
51
- pageBackFnName = importSpecifier.local.name;
52
- }
53
- if (node.type == "ImportDeclaration" && node.source.value === "vue") {
54
- const importSpecifiers = node.specifiers;
55
- for (let i = 0; i < importSpecifiers.length; i++) {
56
- const element = importSpecifiers[i];
57
- if (element.local.name == "ref") {
58
- hasImportRef = true;
59
- break;
60
- }
41
+ if (node.type === "ImportDeclaration") {
42
+ if (node.source.value.includes(virtualFileId)) {
43
+ const importSpecifier = node.specifiers[0];
44
+ pageInfo.hasPageBack = true;
45
+ pageInfo.pageBackFnName = importSpecifier.local.name;
46
+ }
47
+ if (node.source.value === "vue") {
48
+ node.specifiers.some((specifier) => {
49
+ if (specifier.local.name === "ref") {
50
+ pageInfo.hasImportRef = true;
51
+ return true;
52
+ }
53
+ return false;
54
+ });
61
55
  }
62
56
  }
63
- if (node.type == "ExpressionStatement" && node.expression.type == "CallExpression" && node.expression.callee.loc?.identifierName == pageBackFnName) {
57
+ if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageInfo.pageBackFnName) {
64
58
  const callback = node.expression.arguments[0];
65
59
  const backArguments = node.expression.arguments[1];
66
- if (backArguments && backArguments.type == "ObjectExpression") {
60
+ if (backArguments?.type === "ObjectExpression") {
67
61
  const config = new Function(
68
62
  // @ts-ignore
69
63
  `return (${(generate__default.default ? generate__default.default : generate__default)(backArguments).code});`
70
64
  )();
71
- pageBackConfig = { ...pageBackConfig, ...config };
65
+ Object.assign(pageInfo.backConfig, config);
72
66
  }
73
67
  if (callback && (callback.type === "ArrowFunctionExpression" || callback.type === "FunctionExpression")) {
74
68
  const body = callback.body;
75
69
  if (body.type === "BlockStatement") {
76
- body.body.forEach((statement) => {
77
- callbackCode += (generate__default.default ? generate__default.default : generate__default)(statement).code;
78
- });
70
+ pageInfo.callbackCode += body.body.map(
71
+ // @ts-ignore
72
+ (statement) => (generate__default.default ? generate__default.default : generate__default)(statement).code
73
+ ).join("");
79
74
  }
80
75
  }
81
76
  }
82
77
  }
83
78
  });
84
79
  }
85
- if (!hasPageBack)
86
- return;
87
- this.log.devLog(`\u9875\u9762${this.getPageById(id)}\u6CE8\u5165mp-weixin-back`);
88
- if (!pageBackConfig.preventDefault) {
89
- callbackCode += `uni.navigateBack({ delta: 1 });`;
80
+ if (!pageInfo.hasPageBack)
81
+ return code;
82
+ if (code.includes("<page-container")) {
83
+ context.log.debugLog(`${context.getPageById(id)}\u9875\u9762\u5DF2\u6709page-container\u7EC4\u4EF6\uFF0C\u6CE8\u5165\u5931\u8D25`);
84
+ return code;
85
+ }
86
+ if (!pageInfo.backConfig.preventDefault) {
87
+ pageInfo.callbackCode += "uni.navigateBack({ delta: 1 });";
90
88
  }
89
+ const importRefFromVue = !pageInfo.hasImportRef ? `import { ref } from 'vue'` : "";
90
+ const stateFrequency = "let __MP_BACK_FREQUENCY__ = 1;";
91
+ const statePageContainerVar = "const __MP_BACK_SHOW_PAGE_CONTAINER__ = ref(true);";
91
92
  const configBack = (() => {
92
- if (!pageBackConfig.onPageBack)
93
+ const onPageBack = pageInfo.backConfig.onPageBack;
94
+ if (!onPageBack)
93
95
  return "";
94
- if (typeof pageBackConfig.onPageBack !== "function") {
96
+ if (typeof onPageBack !== "function") {
95
97
  throw new Error("`onPageBack` must be a function");
96
98
  }
97
- const params = JSON.stringify({
98
- page: this.getPageById(id)
99
- });
100
- const hasFunction = pageBackConfig.onPageBack.toString().includes("function");
101
- if (isArrowFunction(pageBackConfig.onPageBack) || hasFunction) {
102
- return `(${pageBackConfig.onPageBack})(${params});`;
99
+ const params = JSON.stringify({ page: context.getPageById(id) });
100
+ if (isArrowFunction(onPageBack) || onPageBack.toString().includes("function")) {
101
+ return `(${onPageBack})(${params});`;
103
102
  }
104
- return `(function ${pageBackConfig.onPageBack})()`;
103
+ return `(function ${onPageBack})()`;
105
104
  })();
106
- const beforeLeaveStr = `
107
- ${!hasImportRef ? "import { ref } from 'vue'" : ""}
108
- let __MP_BACK_FREQUENCY__ = 1
109
- const __MP_BACK_SHOW_PAGE_CONTAINER__ = ref(true);
105
+ const stateBeforeLeave = `
110
106
  const onBeforeLeave = () => {
111
- console.log("__MP_BACK_FREQUENCY__", __MP_BACK_FREQUENCY__, ${pageBackConfig.frequency})
112
- if (__MP_BACK_FREQUENCY__ < ${pageBackConfig.frequency}) {
107
+ if (__MP_BACK_FREQUENCY__ < ${pageInfo.backConfig.frequency}) {
113
108
  __MP_BACK_SHOW_PAGE_CONTAINER__.value = false
114
- setTimeout(() => {
115
- __MP_BACK_SHOW_PAGE_CONTAINER__.value = true
116
- }, 0);
109
+ setTimeout(() => __MP_BACK_SHOW_PAGE_CONTAINER__.value = true, 0);
117
110
  __MP_BACK_FREQUENCY__++
118
111
  }
119
- // \u8FD0\u884C\u914D\u7F6E\u7684\u533F\u540D\u51FD\u6570
120
112
  ${configBack}
121
- ${callbackCode}
113
+ ${pageInfo.callbackCode}
122
114
  };
123
115
  `;
124
- const result = code.replace(
125
- /(<template.*?>)([\s\S]*?)(<\/template>)([\s\S]*?)(<script\s+(?:lang="ts"\s+)?setup.*?>|$)/,
126
- (match, templateStart, templateContent, templateEnd, middleContent, scriptSetup) => {
127
- const hasScriptSetup = Boolean(scriptSetup);
128
- const scriptStartTag = hasScriptSetup ? scriptSetup : "<script setup>";
129
- const scriptEndTag = hasScriptSetup ? "" : "<\/script>";
130
- const injectedTemplate = `${templateStart}${templateContent}
131
- ${componentStr}
132
- ${templateEnd}`;
133
- const injectedScript = `
134
- ${middleContent}${scriptStartTag}
135
- ${beforeLeaveStr}
136
- ${scriptEndTag}`;
137
- return injectedTemplate + injectedScript;
116
+ const { template, scriptSetup } = sfc;
117
+ const tempOffsets = {
118
+ start: template.loc.start.offset,
119
+ end: template.loc.end.offset,
120
+ content: template.content
121
+ };
122
+ const templateMagicString = new MagicString__default(tempOffsets.content);
123
+ templateMagicString.append(pageContainerComp);
124
+ codeMs.overwrite(tempOffsets.start, tempOffsets.end, templateMagicString.toString());
125
+ const scriptOffsets = {
126
+ start: scriptSetup.loc.start.offset,
127
+ end: scriptSetup.loc.end.offset,
128
+ content: scriptSetup.content || ""
129
+ };
130
+ const scriptMagicString = new MagicString__default(scriptOffsets.content);
131
+ scriptMagicString.prepend(
132
+ ` ${importRefFromVue}
133
+ ${stateFrequency}
134
+ ${statePageContainerVar}
135
+ ${stateBeforeLeave} `
136
+ );
137
+ codeMs.overwrite(scriptOffsets.start, scriptOffsets.end, scriptMagicString.toString());
138
+ return codeMs.toString();
139
+ }
140
+ function optionsWalk(context, code, sfc, id) {
141
+ const codeMs = new MagicString__default(code);
142
+ const ast = astKit.babelParse(sfc.script.loc.source, sfc.script.lang);
143
+ let pageInfo = {
144
+ hasPageBack: false,
145
+ pageBackFnName: "onPageBack",
146
+ backConfig: { ...context.config }
147
+ };
148
+ let exportDefaultNode = null;
149
+ let dataMethodNode = null;
150
+ let methodsNode = null;
151
+ let onPageBackNodeMethod = null;
152
+ let onPageBackNodeProperty = null;
153
+ if (ast) {
154
+ astKit.walkAST(ast, {
155
+ enter(node) {
156
+ if (node.type === "ExportDefaultDeclaration" && node.declaration.type === "ObjectExpression") {
157
+ exportDefaultNode = node.declaration;
158
+ const properties = node.declaration.properties;
159
+ for (let i = 0; i < properties.length; i++) {
160
+ const element = properties[i];
161
+ if (element.type === "ObjectMethod" && element.key.type === "Identifier" && element.key.name === "data" && element.body.type === "BlockStatement") {
162
+ dataMethodNode = element.body;
163
+ }
164
+ if (element.type === "ObjectProperty" && element.key.type === "Identifier" && element.key.name === "methods") {
165
+ methodsNode = element.value;
166
+ }
167
+ const blockStatementCondition = element.type === "ObjectMethod" && element.key.type === "Identifier" && element.key.name === pageInfo.pageBackFnName && element.body.type === "BlockStatement";
168
+ const functionExpressionCondition = element.type === "ObjectProperty" && element.key.type === "Identifier" && element.key.name === pageInfo.pageBackFnName && element.value.type === "FunctionExpression";
169
+ if (blockStatementCondition) {
170
+ pageInfo.hasPageBack = true;
171
+ onPageBackNodeMethod = element;
172
+ }
173
+ if (functionExpressionCondition) {
174
+ pageInfo.hasPageBack = true;
175
+ onPageBackNodeProperty = element;
176
+ }
177
+ }
178
+ }
179
+ }
180
+ });
181
+ }
182
+ if (!pageInfo.hasPageBack)
183
+ return;
184
+ const newDataProperty = [
185
+ {
186
+ type: "ObjectProperty",
187
+ key: { type: "Identifier", name: "__MP_BACK_SHOW_PAGE_CONTAINER__" },
188
+ value: { type: "BooleanLiteral", value: true },
189
+ computed: false,
190
+ shorthand: false
191
+ },
192
+ {
193
+ type: "ObjectProperty",
194
+ key: { type: "Identifier", name: "__MP_BACK_FREQUENCY__" },
195
+ value: { type: "NumericLiteral", value: 1 },
196
+ computed: false,
197
+ shorthand: false
198
+ }
199
+ ];
200
+ if (dataMethodNode) {
201
+ const returnStatement = dataMethodNode.body.find(
202
+ (node) => node.type === "ReturnStatement"
203
+ );
204
+ if (returnStatement && returnStatement.argument && returnStatement.argument.type === "ObjectExpression") {
205
+ returnStatement.argument.properties.push(...newDataProperty);
138
206
  }
207
+ } else if (exportDefaultNode) {
208
+ const addData = {
209
+ type: "ObjectMethod",
210
+ key: { type: "Identifier", name: "data" },
211
+ kind: "method",
212
+ params: [],
213
+ async: false,
214
+ generator: false,
215
+ computed: false,
216
+ body: {
217
+ type: "BlockStatement",
218
+ directives: [],
219
+ body: [
220
+ {
221
+ type: "ReturnStatement",
222
+ argument: {
223
+ type: "ObjectExpression",
224
+ properties: newDataProperty
225
+ }
226
+ }
227
+ ]
228
+ }
229
+ };
230
+ exportDefaultNode.properties.push(addData);
231
+ }
232
+ const configBack = (() => {
233
+ const onPageBack = pageInfo.backConfig.onPageBack;
234
+ if (!onPageBack)
235
+ return "";
236
+ if (typeof onPageBack !== "function") {
237
+ throw new Error("`onPageBack` must be a function");
238
+ }
239
+ const params = JSON.stringify({ page: context.getPageById(id) });
240
+ if (isArrowFunction(onPageBack) || onPageBack.toString().includes("function")) {
241
+ return `(${onPageBack})(${params});`;
242
+ }
243
+ return `(function ${onPageBack})()`;
244
+ })();
245
+ const stateBeforeLeave = `
246
+ function onBeforeLeave() {
247
+ if (this.__MP_BACK_FREQUENCY__ < ${pageInfo.backConfig.frequency}) {
248
+ this.__MP_BACK_SHOW_PAGE_CONTAINER__ = false
249
+ setTimeout(() => { this.__MP_BACK_SHOW_PAGE_CONTAINER__ = true }, 0);
250
+ this.__MP_BACK_FREQUENCY__++
251
+ }
252
+ ${configBack}
253
+ ${!pageInfo.backConfig.preventDefault ? "uni.navigateBack({ delta: 1 });" : ""}
254
+ };
255
+ `;
256
+ const stateBeforeLeaveAst = astKit.babelParse(stateBeforeLeave);
257
+ const stateBeforeLeaveNode = stateBeforeLeaveAst.body.find(
258
+ (node) => node.type === "FunctionDeclaration"
139
259
  );
140
- return result;
260
+ const newMethodsProperty = {
261
+ type: "ObjectMethod",
262
+ key: {
263
+ type: "Identifier",
264
+ name: "onBeforeLeave"
265
+ },
266
+ kind: "method",
267
+ generator: false,
268
+ async: false,
269
+ params: [],
270
+ computed: false,
271
+ body: {
272
+ type: "BlockStatement",
273
+ directives: [],
274
+ body: [
275
+ ...onPageBackNodeMethod ? onPageBackNodeMethod.body.body : [],
276
+ ...onPageBackNodeProperty ? onPageBackNodeProperty.value.body.body : [],
277
+ ...stateBeforeLeaveNode.body.body
278
+ ]
279
+ }
280
+ };
281
+ if (methodsNode) {
282
+ methodsNode.properties.push(newMethodsProperty);
283
+ const code2 = (generate__default.default ? generate__default.default : generate__default)(methodsNode);
284
+ console.log(code2);
285
+ } else if (exportDefaultNode) {
286
+ const addMethods = {
287
+ type: "ObjectProperty",
288
+ computed: false,
289
+ shorthand: false,
290
+ key: {
291
+ type: "Identifier",
292
+ name: "methods"
293
+ },
294
+ value: {
295
+ type: "ObjectExpression",
296
+ properties: [newMethodsProperty]
297
+ }
298
+ };
299
+ exportDefaultNode.properties.push(addMethods);
300
+ }
301
+ const { template, script } = sfc;
302
+ const tempOffsets = {
303
+ start: template.loc.start.offset,
304
+ end: template.loc.end.offset,
305
+ content: template.content
306
+ };
307
+ const templateMagicString = new MagicString__default(tempOffsets.content);
308
+ templateMagicString.append(pageContainerComp);
309
+ codeMs.overwrite(tempOffsets.start, tempOffsets.end, templateMagicString.toString());
310
+ const scriptOffsets = {
311
+ start: script.loc.start.offset,
312
+ end: script.loc.end.offset
313
+ };
314
+ const newScriptContent = (generate__default.default ? generate__default.default : generate__default)(ast).code;
315
+ codeMs.overwrite(scriptOffsets.start, scriptOffsets.end, newScriptContent);
316
+ return codeMs.toString();
317
+ }
318
+ const vueWalker = {
319
+ compositionWalk,
320
+ optionsWalk
321
+ };
322
+
323
+ async function transformVueFile(code, id) {
324
+ try {
325
+ const sfc = compilerSfc.parse(code).descriptor;
326
+ const { template, script, scriptSetup } = sfc;
327
+ if (!template?.content) {
328
+ return code;
329
+ }
330
+ if (!script?.content && !scriptSetup?.content) {
331
+ return code;
332
+ }
333
+ const walker = scriptSetup ? "compositionWalk" : "optionsWalk";
334
+ return vueWalker[walker](this, code, sfc, id);
335
+ } catch (error) {
336
+ this.log.error("\u89E3\u6790vue\u6587\u4EF6\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u6B63\u786E");
337
+ this.log.debugLog(String(error));
338
+ return code;
339
+ }
141
340
  }
142
341
 
143
342
  var __defProp = Object.defineProperty;
@@ -148,18 +347,19 @@ var __publicField = (obj, key, value) => {
148
347
  };
149
348
  class pageContext {
150
349
  constructor(config) {
350
+ __publicField(this, "logPreText", "[mp-weixin-back] : ");
151
351
  __publicField(this, "config");
152
352
  __publicField(this, "pages", []);
153
353
  __publicField(this, "log", {
154
354
  info: (text) => {
155
- console.log(kolorist.white(text));
355
+ console.log(kolorist.white(this.logPreText + text));
156
356
  },
157
357
  error: (text) => {
158
- console.log(kolorist.red(text));
358
+ console.log(kolorist.red(this.logPreText + text));
159
359
  },
160
- devLog: (text) => {
360
+ debugLog: (text) => {
161
361
  if (this.config.mode === "development" && this.config.debug) {
162
- console.log(kolorist.green(text));
362
+ console.log(kolorist.green(this.logPreText + text));
163
363
  }
164
364
  }
165
365
  });
@@ -198,7 +398,7 @@ class pageContext {
198
398
  }
199
399
  } catch (error) {
200
400
  this.log.error("\u8BFB\u53D6pages.json\u6587\u4EF6\u5931\u8D25");
201
- this.log.devLog(String(error));
401
+ this.log.debugLog(String(error));
202
402
  }
203
403
  }
204
404
  // 获取指定id的page