mp-weixin-back 0.0.6 → 0.0.8

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/build.config.ts CHANGED
@@ -8,5 +8,5 @@ export default defineBuildConfig({
8
8
  emitCJS: true,
9
9
  },
10
10
  failOnWarn: false,
11
- externals: ['vite', 'vue']
12
- })
11
+ externals: ['vite', 'vue', '@babel/generator'],
12
+ })
package/dist/index.cjs CHANGED
@@ -3,18 +3,19 @@
3
3
  const path = require('path');
4
4
  const fs = require('fs');
5
5
  const JSON5 = require('json5');
6
- const chalk = require('chalk');
6
+ const kolorist = require('kolorist');
7
7
  const generate = require('@babel/generator');
8
8
  const compilerSfc = require('@vue/compiler-sfc');
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
 
13
14
  const path__default = /*#__PURE__*/_interopDefaultCompat(path);
14
15
  const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
15
16
  const JSON5__default = /*#__PURE__*/_interopDefaultCompat(JSON5);
16
- const chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
17
17
  const generate__default = /*#__PURE__*/_interopDefaultCompat(generate);
18
+ const MagicString__default = /*#__PURE__*/_interopDefaultCompat(MagicString);
18
19
 
19
20
  const virtualFileId = "mp-weixin-back-helper";
20
21
 
@@ -31,49 +32,55 @@ async function parseSFC(code) {
31
32
  }
32
33
  }
33
34
  async function transformVueFile(code, id) {
34
- if (code.includes("<page-container")) {
35
- return code;
36
- }
37
- if (!code.includes("<template")) {
35
+ const sfc = await parseSFC(code);
36
+ if (!sfc.template?.content) {
38
37
  return code;
39
38
  }
40
- const componentStr = '<page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="false" @beforeleave="onBeforeLeave" :z-index="1" :duration="false"></page-container>';
41
- const sfc = await parseSFC(code);
42
- const setupCode = sfc.scriptSetup?.loc.source;
43
- const setupAst = astKit.babelParse(setupCode || "", sfc.scriptSetup?.lang);
44
- let pageBackConfig = this.config;
45
- let hasPageBack = false, hasImportRef = false, pageBackFnName = "onPageBack", callbackCode = ``;
39
+ const componentStr = ' <page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="false" @beforeleave="onBeforeLeave" :z-index="1" :duration="false"></page-container>\n';
40
+ let pageBackConfig = { ...this.config };
41
+ let hasPageBack = false;
42
+ let hasImportRef = false;
43
+ let pageBackFnName = "onPageBack";
44
+ let callbackCode = ``;
45
+ const codeMs = new MagicString__default(code);
46
+ const setupCode = sfc.scriptSetup?.loc.source || "";
47
+ const setupAst = astKit.babelParse(setupCode, sfc.scriptSetup?.lang);
46
48
  if (setupAst) {
47
49
  astKit.walkAST(setupAst, {
48
50
  enter(node) {
49
- if (node.type == "ImportDeclaration" && node.source.value.includes(virtualFileId)) {
50
- const importSpecifier = node.specifiers[0];
51
- hasPageBack = true;
52
- pageBackFnName = importSpecifier.local.name;
53
- }
54
- if (node.type == "ImportDeclaration" && node.source.value === "vue") {
55
- const importSpecifiers = node.specifiers;
56
- for (let i = 0; i < importSpecifiers.length; i++) {
57
- const element = importSpecifiers[i];
58
- if (element.local.name == "ref") {
59
- hasImportRef = true;
60
- break;
61
- }
51
+ if (node.type === "ImportDeclaration") {
52
+ if (node.source.value.includes(virtualFileId)) {
53
+ const importSpecifier = node.specifiers[0];
54
+ hasPageBack = true;
55
+ pageBackFnName = importSpecifier.local.name;
56
+ }
57
+ if (node.source.value === "vue") {
58
+ node.specifiers.some((specifier) => {
59
+ if (specifier.local.name === "ref") {
60
+ hasImportRef = true;
61
+ return true;
62
+ }
63
+ return false;
64
+ });
62
65
  }
63
66
  }
64
- if (node.type == "ExpressionStatement" && node.expression.type == "CallExpression" && node.expression.callee.loc?.identifierName == pageBackFnName) {
67
+ if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageBackFnName) {
65
68
  const callback = node.expression.arguments[0];
66
69
  const backArguments = node.expression.arguments[1];
67
- if (backArguments && backArguments.type == "ObjectExpression") {
68
- const config = new Function(`return (${generate__default(backArguments).code});`)();
69
- pageBackConfig = { ...pageBackConfig, ...config };
70
+ if (backArguments?.type === "ObjectExpression") {
71
+ const config = new Function(
72
+ // @ts-ignore
73
+ `return (${(generate__default.default ? generate__default.default : generate__default)(backArguments).code});`
74
+ )();
75
+ Object.assign(pageBackConfig, config);
70
76
  }
71
77
  if (callback && (callback.type === "ArrowFunctionExpression" || callback.type === "FunctionExpression")) {
72
78
  const body = callback.body;
73
79
  if (body.type === "BlockStatement") {
74
- body.body.forEach((statement) => {
75
- callbackCode += generate__default(statement).code;
76
- });
80
+ callbackCode += body.body.map(
81
+ // @ts-ignore
82
+ (statement) => (generate__default.default ? generate__default.default : generate__default)(statement).code
83
+ ).join("");
77
84
  }
78
85
  }
79
86
  }
@@ -83,23 +90,25 @@ async function transformVueFile(code, id) {
83
90
  if (!hasPageBack)
84
91
  return;
85
92
  this.log.devLog(`\u9875\u9762${this.getPageById(id)}\u6CE8\u5165mp-weixin-back`);
93
+ if (code.includes("<page-container")) {
94
+ this.log.devLog(`${this.getPageById(id)}\u9875\u9762\u5DF2\u6709page-container\u7EC4\u4EF6\uFF0C\u6CE8\u5165\u5931\u8D25`);
95
+ return code;
96
+ }
86
97
  if (!pageBackConfig.preventDefault) {
87
98
  callbackCode += `uni.navigateBack({ delta: 1 });`;
88
99
  }
89
100
  const configBack = (() => {
90
- if (!pageBackConfig.onPageBack)
101
+ const onPageBack = pageBackConfig.onPageBack;
102
+ if (!onPageBack)
91
103
  return "";
92
- if (typeof pageBackConfig.onPageBack !== "function") {
104
+ if (typeof onPageBack !== "function") {
93
105
  throw new Error("`onPageBack` must be a function");
94
106
  }
95
- const params = JSON.stringify({
96
- page: this.getPageById(id)
97
- });
98
- const hasFunction = pageBackConfig.onPageBack.toString().includes("function");
99
- if (isArrowFunction(pageBackConfig.onPageBack) || hasFunction) {
100
- return `(${pageBackConfig.onPageBack})(${params});`;
107
+ const params = JSON.stringify({ page: this.getPageById(id) });
108
+ if (isArrowFunction(onPageBack) || onPageBack.toString().includes("function")) {
109
+ return `(${onPageBack})(${params});`;
101
110
  }
102
- return `(function ${pageBackConfig.onPageBack})()`;
111
+ return `(function ${onPageBack})()`;
103
112
  })();
104
113
  const beforeLeaveStr = `
105
114
  ${!hasImportRef ? "import { ref } from 'vue'" : ""}
@@ -109,33 +118,34 @@ async function transformVueFile(code, id) {
109
118
  console.log("__MP_BACK_FREQUENCY__", __MP_BACK_FREQUENCY__, ${pageBackConfig.frequency})
110
119
  if (__MP_BACK_FREQUENCY__ < ${pageBackConfig.frequency}) {
111
120
  __MP_BACK_SHOW_PAGE_CONTAINER__.value = false
112
- setTimeout(() => {
113
- __MP_BACK_SHOW_PAGE_CONTAINER__.value = true
114
- }, 0);
121
+ setTimeout(() => __MP_BACK_SHOW_PAGE_CONTAINER__.value = true, 0);
115
122
  __MP_BACK_FREQUENCY__++
116
123
  }
117
- // \u8FD0\u884C\u914D\u7F6E\u7684\u533F\u540D\u51FD\u6570
118
124
  ${configBack}
119
125
  ${callbackCode}
120
126
  };
121
127
  `;
122
- const result = code.replace(
123
- /(<template.*?>)([\s\S]*?)(<\/template>)([\s\S]*?)(<script\s+(?:lang="ts"\s+)?setup.*?>|$)/,
124
- (match, templateStart, templateContent, templateEnd, middleContent, scriptSetup) => {
125
- const hasScriptSetup = Boolean(scriptSetup);
126
- const scriptStartTag = hasScriptSetup ? scriptSetup : "<script setup>";
127
- const scriptEndTag = hasScriptSetup ? "" : "<\/script>";
128
- const injectedTemplate = `${templateStart}${templateContent}
129
- ${componentStr}
130
- ${templateEnd}`;
131
- const injectedScript = `
132
- ${middleContent}${scriptStartTag}
133
- ${beforeLeaveStr}
134
- ${scriptEndTag}`;
135
- return injectedTemplate + injectedScript;
136
- }
137
- );
138
- return result;
128
+ const { template, script, scriptSetup } = sfc;
129
+ const tempOffsets = {
130
+ start: template.loc.start.offset,
131
+ end: template.loc.end.offset,
132
+ content: template.content
133
+ };
134
+ const templateMagicString = new MagicString__default(tempOffsets.content);
135
+ templateMagicString.append(componentStr);
136
+ codeMs.overwrite(tempOffsets.start, tempOffsets.end, templateMagicString.toString());
137
+ const scriptSfc = script || scriptSetup;
138
+ if (!scriptSfc)
139
+ return;
140
+ const scriptOffsets = {
141
+ start: scriptSfc.loc.start.offset,
142
+ end: scriptSfc.loc.end.offset,
143
+ content: scriptSfc.content || ""
144
+ };
145
+ const scriptMagicString = new MagicString__default(scriptOffsets.content);
146
+ scriptMagicString.prepend(beforeLeaveStr);
147
+ codeMs.overwrite(scriptOffsets.start, scriptOffsets.end, scriptMagicString.toString());
148
+ return codeMs.toString();
139
149
  }
140
150
 
141
151
  var __defProp = Object.defineProperty;
@@ -150,14 +160,14 @@ class pageContext {
150
160
  __publicField(this, "pages", []);
151
161
  __publicField(this, "log", {
152
162
  info: (text) => {
153
- console.log(chalk__default.white(text));
163
+ console.log(kolorist.white(text));
154
164
  },
155
165
  error: (text) => {
156
- console.log(chalk__default.red(text));
166
+ console.log(kolorist.red(text));
157
167
  },
158
168
  devLog: (text) => {
159
169
  if (this.config.mode === "development" && this.config.debug) {
160
- console.log(chalk__default.green(text));
170
+ console.log(kolorist.green(text));
161
171
  }
162
172
  }
163
173
  });
package/dist/index.mjs CHANGED
@@ -1,10 +1,11 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs';
3
3
  import JSON5 from 'json5';
4
- import chalk from 'chalk';
4
+ import { white, red, green } from 'kolorist';
5
5
  import generate from '@babel/generator';
6
6
  import { parse } from '@vue/compiler-sfc';
7
7
  import { babelParse, walkAST } from 'ast-kit';
8
+ import MagicString from 'magic-string';
8
9
 
9
10
  const virtualFileId = "mp-weixin-back-helper";
10
11
 
@@ -21,49 +22,55 @@ async function parseSFC(code) {
21
22
  }
22
23
  }
23
24
  async function transformVueFile(code, id) {
24
- if (code.includes("<page-container")) {
25
- return code;
26
- }
27
- if (!code.includes("<template")) {
25
+ const sfc = await parseSFC(code);
26
+ if (!sfc.template?.content) {
28
27
  return code;
29
28
  }
30
- const componentStr = '<page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="false" @beforeleave="onBeforeLeave" :z-index="1" :duration="false"></page-container>';
31
- const sfc = await parseSFC(code);
32
- const setupCode = sfc.scriptSetup?.loc.source;
33
- const setupAst = babelParse(setupCode || "", sfc.scriptSetup?.lang);
34
- let pageBackConfig = this.config;
35
- let hasPageBack = false, hasImportRef = false, pageBackFnName = "onPageBack", callbackCode = ``;
29
+ const componentStr = ' <page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="false" @beforeleave="onBeforeLeave" :z-index="1" :duration="false"></page-container>\n';
30
+ let pageBackConfig = { ...this.config };
31
+ let hasPageBack = false;
32
+ let hasImportRef = false;
33
+ let pageBackFnName = "onPageBack";
34
+ let callbackCode = ``;
35
+ const codeMs = new MagicString(code);
36
+ const setupCode = sfc.scriptSetup?.loc.source || "";
37
+ const setupAst = babelParse(setupCode, sfc.scriptSetup?.lang);
36
38
  if (setupAst) {
37
39
  walkAST(setupAst, {
38
40
  enter(node) {
39
- if (node.type == "ImportDeclaration" && node.source.value.includes(virtualFileId)) {
40
- const importSpecifier = node.specifiers[0];
41
- hasPageBack = true;
42
- pageBackFnName = importSpecifier.local.name;
43
- }
44
- if (node.type == "ImportDeclaration" && node.source.value === "vue") {
45
- const importSpecifiers = node.specifiers;
46
- for (let i = 0; i < importSpecifiers.length; i++) {
47
- const element = importSpecifiers[i];
48
- if (element.local.name == "ref") {
49
- hasImportRef = true;
50
- break;
51
- }
41
+ if (node.type === "ImportDeclaration") {
42
+ if (node.source.value.includes(virtualFileId)) {
43
+ const importSpecifier = node.specifiers[0];
44
+ hasPageBack = true;
45
+ pageBackFnName = importSpecifier.local.name;
46
+ }
47
+ if (node.source.value === "vue") {
48
+ node.specifiers.some((specifier) => {
49
+ if (specifier.local.name === "ref") {
50
+ hasImportRef = true;
51
+ return true;
52
+ }
53
+ return false;
54
+ });
52
55
  }
53
56
  }
54
- 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 === pageBackFnName) {
55
58
  const callback = node.expression.arguments[0];
56
59
  const backArguments = node.expression.arguments[1];
57
- if (backArguments && backArguments.type == "ObjectExpression") {
58
- const config = new Function(`return (${generate(backArguments).code});`)();
59
- pageBackConfig = { ...pageBackConfig, ...config };
60
+ if (backArguments?.type === "ObjectExpression") {
61
+ const config = new Function(
62
+ // @ts-ignore
63
+ `return (${(generate.default ? generate.default : generate)(backArguments).code});`
64
+ )();
65
+ Object.assign(pageBackConfig, config);
60
66
  }
61
67
  if (callback && (callback.type === "ArrowFunctionExpression" || callback.type === "FunctionExpression")) {
62
68
  const body = callback.body;
63
69
  if (body.type === "BlockStatement") {
64
- body.body.forEach((statement) => {
65
- callbackCode += generate(statement).code;
66
- });
70
+ callbackCode += body.body.map(
71
+ // @ts-ignore
72
+ (statement) => (generate.default ? generate.default : generate)(statement).code
73
+ ).join("");
67
74
  }
68
75
  }
69
76
  }
@@ -73,23 +80,25 @@ async function transformVueFile(code, id) {
73
80
  if (!hasPageBack)
74
81
  return;
75
82
  this.log.devLog(`\u9875\u9762${this.getPageById(id)}\u6CE8\u5165mp-weixin-back`);
83
+ if (code.includes("<page-container")) {
84
+ this.log.devLog(`${this.getPageById(id)}\u9875\u9762\u5DF2\u6709page-container\u7EC4\u4EF6\uFF0C\u6CE8\u5165\u5931\u8D25`);
85
+ return code;
86
+ }
76
87
  if (!pageBackConfig.preventDefault) {
77
88
  callbackCode += `uni.navigateBack({ delta: 1 });`;
78
89
  }
79
90
  const configBack = (() => {
80
- if (!pageBackConfig.onPageBack)
91
+ const onPageBack = pageBackConfig.onPageBack;
92
+ if (!onPageBack)
81
93
  return "";
82
- if (typeof pageBackConfig.onPageBack !== "function") {
94
+ if (typeof onPageBack !== "function") {
83
95
  throw new Error("`onPageBack` must be a function");
84
96
  }
85
- const params = JSON.stringify({
86
- page: this.getPageById(id)
87
- });
88
- const hasFunction = pageBackConfig.onPageBack.toString().includes("function");
89
- if (isArrowFunction(pageBackConfig.onPageBack) || hasFunction) {
90
- return `(${pageBackConfig.onPageBack})(${params});`;
97
+ const params = JSON.stringify({ page: this.getPageById(id) });
98
+ if (isArrowFunction(onPageBack) || onPageBack.toString().includes("function")) {
99
+ return `(${onPageBack})(${params});`;
91
100
  }
92
- return `(function ${pageBackConfig.onPageBack})()`;
101
+ return `(function ${onPageBack})()`;
93
102
  })();
94
103
  const beforeLeaveStr = `
95
104
  ${!hasImportRef ? "import { ref } from 'vue'" : ""}
@@ -99,33 +108,34 @@ async function transformVueFile(code, id) {
99
108
  console.log("__MP_BACK_FREQUENCY__", __MP_BACK_FREQUENCY__, ${pageBackConfig.frequency})
100
109
  if (__MP_BACK_FREQUENCY__ < ${pageBackConfig.frequency}) {
101
110
  __MP_BACK_SHOW_PAGE_CONTAINER__.value = false
102
- setTimeout(() => {
103
- __MP_BACK_SHOW_PAGE_CONTAINER__.value = true
104
- }, 0);
111
+ setTimeout(() => __MP_BACK_SHOW_PAGE_CONTAINER__.value = true, 0);
105
112
  __MP_BACK_FREQUENCY__++
106
113
  }
107
- // \u8FD0\u884C\u914D\u7F6E\u7684\u533F\u540D\u51FD\u6570
108
114
  ${configBack}
109
115
  ${callbackCode}
110
116
  };
111
117
  `;
112
- const result = code.replace(
113
- /(<template.*?>)([\s\S]*?)(<\/template>)([\s\S]*?)(<script\s+(?:lang="ts"\s+)?setup.*?>|$)/,
114
- (match, templateStart, templateContent, templateEnd, middleContent, scriptSetup) => {
115
- const hasScriptSetup = Boolean(scriptSetup);
116
- const scriptStartTag = hasScriptSetup ? scriptSetup : "<script setup>";
117
- const scriptEndTag = hasScriptSetup ? "" : "<\/script>";
118
- const injectedTemplate = `${templateStart}${templateContent}
119
- ${componentStr}
120
- ${templateEnd}`;
121
- const injectedScript = `
122
- ${middleContent}${scriptStartTag}
123
- ${beforeLeaveStr}
124
- ${scriptEndTag}`;
125
- return injectedTemplate + injectedScript;
126
- }
127
- );
128
- return result;
118
+ const { template, script, scriptSetup } = sfc;
119
+ const tempOffsets = {
120
+ start: template.loc.start.offset,
121
+ end: template.loc.end.offset,
122
+ content: template.content
123
+ };
124
+ const templateMagicString = new MagicString(tempOffsets.content);
125
+ templateMagicString.append(componentStr);
126
+ codeMs.overwrite(tempOffsets.start, tempOffsets.end, templateMagicString.toString());
127
+ const scriptSfc = script || scriptSetup;
128
+ if (!scriptSfc)
129
+ return;
130
+ const scriptOffsets = {
131
+ start: scriptSfc.loc.start.offset,
132
+ end: scriptSfc.loc.end.offset,
133
+ content: scriptSfc.content || ""
134
+ };
135
+ const scriptMagicString = new MagicString(scriptOffsets.content);
136
+ scriptMagicString.prepend(beforeLeaveStr);
137
+ codeMs.overwrite(scriptOffsets.start, scriptOffsets.end, scriptMagicString.toString());
138
+ return codeMs.toString();
129
139
  }
130
140
 
131
141
  var __defProp = Object.defineProperty;
@@ -140,14 +150,14 @@ class pageContext {
140
150
  __publicField(this, "pages", []);
141
151
  __publicField(this, "log", {
142
152
  info: (text) => {
143
- console.log(chalk.white(text));
153
+ console.log(white(text));
144
154
  },
145
155
  error: (text) => {
146
- console.log(chalk.red(text));
156
+ console.log(red(text));
147
157
  },
148
158
  devLog: (text) => {
149
159
  if (this.config.mode === "development" && this.config.debug) {
150
- console.log(chalk.green(text));
160
+ console.log(green(text));
151
161
  }
152
162
  }
153
163
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mp-weixin-back",
3
3
  "type": "module",
4
- "version": "0.0.6",
4
+ "version": "0.0.8",
5
5
  "description": "监听微信小程序的手势返回和页面默认导航栏的返回",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.mjs",
@@ -41,14 +41,13 @@
41
41
  "@babel/generator": "^7.26.2",
42
42
  "@babel/parser": "^7.26.2",
43
43
  "@babel/traverse": "^7.25.9",
44
- "@types/babel__generator": "^7.6.8",
45
- "@types/node": "^22.9.3",
46
44
  "@vitejs/plugin-vue": "^5.2.0",
47
45
  "@vue/test-utils": "^2.4.6",
48
46
  "ast-kit": "^1.3.1",
49
- "chalk": "^5.3.0",
50
47
  "happy-dom": "^15.11.6",
51
48
  "json5": "^2.2.3",
49
+ "kolorist": "^1.8.0",
50
+ "magic-string": "^0.30.13",
52
51
  "typescript": "^5.7.2",
53
52
  "vitest": "^2.1.5"
54
53
  },
@@ -60,6 +59,8 @@
60
59
  "*": "prettier --write"
61
60
  },
62
61
  "devDependencies": {
62
+ "@types/babel__generator": "^7.6.8",
63
+ "@types/node": "^22.9.3",
63
64
  "unbuild": "^2.0.0",
64
65
  "vue": "^3.5.13"
65
66
  }
package/src/context.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import path from 'path'
2
2
  import fs from 'fs'
3
3
  import JSON5 from 'json5'
4
- import chalk from 'chalk'
4
+ import { red, white, green } from 'kolorist'
5
5
  import { ContextConfig, PagesJson } from '../types'
6
6
  import { transformVueFile } from '../utils'
7
7
 
@@ -10,14 +10,14 @@ export class pageContext {
10
10
  pages: string[] = []
11
11
  log = {
12
12
  info: (text: string) => {
13
- console.log(chalk.white(text))
13
+ console.log(white(text))
14
14
  },
15
15
  error: (text: string) => {
16
- console.log(chalk.red(text))
16
+ console.log(red(text))
17
17
  },
18
18
  devLog: (text: string) => {
19
19
  if (this.config.mode === 'development' && this.config.debug) {
20
- console.log(chalk.green(text))
20
+ console.log(green(text))
21
21
  }
22
22
  },
23
23
  }
package/utils/index.ts CHANGED
@@ -2,6 +2,7 @@ import generate from '@babel/generator'
2
2
  import { parse } from '@vue/compiler-sfc'
3
3
  import { babelParse, walkAST } from 'ast-kit'
4
4
  import { pageContext } from '../src/context'
5
+ import MagicString from 'magic-string'
5
6
  import { virtualFileId } from './constant'
6
7
  import type { SFCDescriptor } from '@vue/compiler-sfc'
7
8
  import type { Node } from '@babel/types'
@@ -20,59 +21,58 @@ async function parseSFC(code: string): Promise<SFCDescriptor> {
20
21
  }
21
22
 
22
23
  export async function transformVueFile(this: pageContext, code: string, id: string) {
23
- // 检查代码中是否已经包含page-container组件
24
- if (code.includes('<page-container')) {
25
- return code
26
- }
27
-
28
- // 检查是否包含template标签
29
- if (!code.includes('<template')) {
24
+ const sfc = await parseSFC(code)
25
+ if (!sfc.template?.content) {
30
26
  return code
31
27
  }
32
28
 
33
29
  const componentStr =
34
- '<page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="false" @beforeleave="onBeforeLeave" :z-index="1" :duration="false"></page-container>'
30
+ ' <page-container :show="__MP_BACK_SHOW_PAGE_CONTAINER__" :overlay="false" @beforeleave="onBeforeLeave" :z-index="1" :duration="false"></page-container>\n'
35
31
 
36
- const sfc = await parseSFC(code)
37
- const setupCode = sfc.scriptSetup?.loc.source
38
- const setupAst = babelParse(setupCode || '', sfc.scriptSetup?.lang)
39
- let pageBackConfig = this.config
40
- let hasPageBack = false,
41
- hasImportRef = false,
42
- pageBackFnName = 'onPageBack',
43
- callbackCode = ``
32
+ let pageBackConfig = { ...this.config }
33
+ let hasPageBack = false
34
+ let hasImportRef = false
35
+ let pageBackFnName = 'onPageBack'
36
+ let callbackCode = ``
37
+
38
+ const codeMs = new MagicString(code)
39
+ const setupCode = sfc.scriptSetup?.loc.source || ''
40
+ const setupAst = babelParse(setupCode, sfc.scriptSetup?.lang)
44
41
 
45
42
  if (setupAst) {
46
43
  walkAST<Node>(setupAst, {
47
44
  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
-
54
- if (node.type == 'ImportDeclaration' && node.source.value === 'vue') {
55
- const importSpecifiers = node.specifiers
56
- for (let i = 0; i < importSpecifiers.length; i++) {
57
- const element = importSpecifiers[i]
58
- if (element.local.name == 'ref') {
59
- hasImportRef = true
60
- break
61
- }
45
+ if (node.type === 'ImportDeclaration') {
46
+ if (node.source.value.includes(virtualFileId)) {
47
+ const importSpecifier = node.specifiers[0]
48
+ hasPageBack = true
49
+ pageBackFnName = importSpecifier.local.name
50
+ }
51
+ if (node.source.value === 'vue') {
52
+ node.specifiers.some((specifier) => {
53
+ if (specifier.local.name === 'ref') {
54
+ hasImportRef = true
55
+ return true
56
+ }
57
+ return false
58
+ })
62
59
  }
63
60
  }
64
61
 
65
62
  if (
66
- node.type == 'ExpressionStatement' &&
67
- node.expression.type == 'CallExpression' &&
68
- node.expression.callee.loc?.identifierName == pageBackFnName
63
+ node.type === 'ExpressionStatement' &&
64
+ node.expression.type === 'CallExpression' &&
65
+ node.expression.callee.loc?.identifierName === pageBackFnName
69
66
  ) {
70
- const callback = node.expression.arguments[0] // 获取第一个参数
67
+ const callback = node.expression.arguments[0]
71
68
  const backArguments = node.expression.arguments[1]
72
- // 第二个参数为object才有效,覆盖插件传入的配置
73
- if (backArguments && backArguments.type == 'ObjectExpression') {
74
- const config = new Function(`return (${generate(backArguments).code});`)()
75
- pageBackConfig = { ...pageBackConfig, ...config }
69
+
70
+ if (backArguments?.type === 'ObjectExpression') {
71
+ const config = new Function(
72
+ // @ts-ignore
73
+ `return (${(generate.default ? generate.default : generate)(backArguments).code});`
74
+ )()
75
+ Object.assign(pageBackConfig, config)
76
76
  }
77
77
 
78
78
  if (
@@ -81,10 +81,12 @@ export async function transformVueFile(this: pageContext, code: string, id: stri
81
81
  ) {
82
82
  const body = callback.body
83
83
  if (body.type === 'BlockStatement') {
84
- // 遍历 BlockStatement 的内容
85
- body.body.forEach((statement) => {
86
- callbackCode += generate(statement).code // 将 AST 节点生成代码
87
- })
84
+ callbackCode += body.body
85
+ .map(
86
+ // @ts-ignore
87
+ (statement) => (generate.default ? generate.default : generate)(statement).code
88
+ )
89
+ .join('')
88
90
  }
89
91
  }
90
92
  }
@@ -96,29 +98,29 @@ export async function transformVueFile(this: pageContext, code: string, id: stri
96
98
 
97
99
  this.log.devLog(`页面${this.getPageById(id)}注入mp-weixin-back`)
98
100
 
99
- // 不阻止默认行为就返回到上一层
101
+ if (code.includes('<page-container')) {
102
+ this.log.devLog(`${this.getPageById(id)}页面已有page-container组件,注入失败`)
103
+ return code
104
+ }
105
+
100
106
  if (!pageBackConfig.preventDefault) {
101
107
  callbackCode += `uni.navigateBack({ delta: 1 });`
102
108
  }
103
109
 
104
- // 处理统一的返回方法
105
110
  const configBack = (() => {
106
- if (!pageBackConfig.onPageBack) return ''
107
- if (typeof pageBackConfig.onPageBack !== 'function') {
111
+ const onPageBack = pageBackConfig.onPageBack
112
+ if (!onPageBack) return ''
113
+ if (typeof onPageBack !== 'function') {
108
114
  throw new Error('`onPageBack` must be a function')
109
115
  }
110
116
 
111
- const params = JSON.stringify({
112
- page: this.getPageById(id),
113
- })
114
-
115
- const hasFunction = pageBackConfig.onPageBack.toString().includes('function')
117
+ const params = JSON.stringify({ page: this.getPageById(id) })
116
118
 
117
- if (isArrowFunction(pageBackConfig.onPageBack) || hasFunction) {
118
- return `(${pageBackConfig.onPageBack})(${params});`
119
+ if (isArrowFunction(onPageBack) || onPageBack.toString().includes('function')) {
120
+ return `(${onPageBack})(${params});`
119
121
  }
120
122
 
121
- return `(function ${pageBackConfig.onPageBack})()`
123
+ return `(function ${onPageBack})()`
122
124
  })()
123
125
 
124
126
  const beforeLeaveStr = `
@@ -129,33 +131,37 @@ export async function transformVueFile(this: pageContext, code: string, id: stri
129
131
  console.log("__MP_BACK_FREQUENCY__", __MP_BACK_FREQUENCY__, ${pageBackConfig.frequency})
130
132
  if (__MP_BACK_FREQUENCY__ < ${pageBackConfig.frequency}) {
131
133
  __MP_BACK_SHOW_PAGE_CONTAINER__.value = false
132
- setTimeout(() => {
133
- __MP_BACK_SHOW_PAGE_CONTAINER__.value = true
134
- }, 0);
134
+ setTimeout(() => __MP_BACK_SHOW_PAGE_CONTAINER__.value = true, 0);
135
135
  __MP_BACK_FREQUENCY__++
136
136
  }
137
- // 运行配置的匿名函数
138
137
  ${configBack}
139
138
  ${callbackCode}
140
139
  };
141
140
  `
142
141
 
143
- // template标签后插入page-container组件和script setup声明
144
- const result = code.replace(
145
- /(<template.*?>)([\s\S]*?)(<\/template>)([\s\S]*?)(<script\s+(?:lang="ts"\s+)?setup.*?>|$)/,
146
- (match, templateStart, templateContent, templateEnd, middleContent, scriptSetup) => {
147
- // 处理script setup标签
148
- const hasScriptSetup = Boolean(scriptSetup)
149
- const scriptStartTag = hasScriptSetup ? scriptSetup : '<script setup>'
150
- const scriptEndTag = hasScriptSetup ? '' : '</script>'
142
+ const { template, script, scriptSetup } = sfc
143
+ const tempOffsets = {
144
+ start: template.loc.start.offset,
145
+ end: template.loc.end.offset,
146
+ content: template.content,
147
+ }
151
148
 
152
- // 构建注入的内容
153
- const injectedTemplate = `${templateStart}${templateContent}\n ${componentStr}\n${templateEnd}`
154
- const injectedScript = `\n${middleContent}${scriptStartTag}\n${beforeLeaveStr}\n${scriptEndTag}`
149
+ const templateMagicString = new MagicString(tempOffsets.content)
150
+ templateMagicString.append(componentStr)
151
+ codeMs.overwrite(tempOffsets.start, tempOffsets.end, templateMagicString.toString())
155
152
 
156
- return injectedTemplate + injectedScript
157
- }
158
- )
153
+ const scriptSfc = script || scriptSetup
154
+ if (!scriptSfc) return
155
+
156
+ const scriptOffsets = {
157
+ start: scriptSfc.loc.start.offset,
158
+ end: scriptSfc.loc.end.offset,
159
+ content: scriptSfc.content || '',
160
+ }
161
+
162
+ const scriptMagicString = new MagicString(scriptOffsets.content)
163
+ scriptMagicString.prepend(beforeLeaveStr)
164
+ codeMs.overwrite(scriptOffsets.start, scriptOffsets.end, scriptMagicString.toString())
159
165
 
160
- return result
166
+ return codeMs.toString()
161
167
  }