mp-weixin-back 0.0.11 → 0.0.13
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/client.d.ts +19 -6
- package/dist/index.cjs +130 -14
- package/dist/index.d.cts +4 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.mjs +130 -14
- package/package.json +1 -1
- package/readme.md +129 -50
- package/shims-vue.d.ts +6 -0
- package/src/index.ts +28 -1
- package/test/data/index-utils.vue +29 -0
- package/test/generate.spec.ts +55 -22
- package/types/index.ts +4 -0
- package/utils/index.ts +0 -1
- package/utils/walker.ts +152 -18
- package/vite.config.ts +10 -1
package/client.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
declare module 'mp-weixin-back-helper' {
|
|
2
2
|
type Config = {
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* 初始化时是否监听手势返回,默认值为`true`
|
|
5
|
+
*/
|
|
6
|
+
initialValue: boolean
|
|
7
|
+
/**
|
|
4
8
|
* 是否阻止默认的回退事件,默认为 false
|
|
5
9
|
*/
|
|
6
10
|
preventDefault: boolean
|
|
@@ -10,10 +14,19 @@ declare module 'mp-weixin-back-helper' {
|
|
|
10
14
|
frequency: number
|
|
11
15
|
}
|
|
12
16
|
|
|
13
|
-
function onPageBack(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
function onPageBack(callback: () => void, params?: Partial<Config>)
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 开启监听手势滑动事件
|
|
21
|
+
*/
|
|
22
|
+
function activeMpBack()
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 禁用监听手势滑动事件
|
|
26
|
+
*/
|
|
27
|
+
function inactiveMpBack()
|
|
17
28
|
|
|
18
29
|
export default onPageBack
|
|
19
|
-
|
|
30
|
+
|
|
31
|
+
export { activeMpBack, inactiveMpBack }
|
|
32
|
+
}
|
package/dist/index.cjs
CHANGED
|
@@ -33,16 +33,33 @@ function compositionWalk(context, code, sfc, id) {
|
|
|
33
33
|
pageBackFnName: "onPageBack",
|
|
34
34
|
hasImportRef: false,
|
|
35
35
|
backConfig: { ...context.config },
|
|
36
|
-
|
|
36
|
+
onPageBackBodyAst: [],
|
|
37
|
+
onPageBackCallNodeToRemove: null,
|
|
38
|
+
activeFnName: "activeMpBack",
|
|
39
|
+
inActiveFnName: "inactiveMpBack"
|
|
37
40
|
};
|
|
41
|
+
const activeFnCallsToModify = [];
|
|
42
|
+
const inActiveFnCallsToModify = [];
|
|
38
43
|
if (setupAst) {
|
|
39
44
|
astKit.walkAST(setupAst, {
|
|
40
45
|
enter(node) {
|
|
41
46
|
if (node.type === "ImportDeclaration") {
|
|
42
47
|
if (node.source.value.includes(virtualFileId)) {
|
|
43
|
-
const
|
|
48
|
+
const importDefaultSpecifiers = node.specifiers.filter(
|
|
49
|
+
(i) => i.type === "ImportDefaultSpecifier"
|
|
50
|
+
);
|
|
51
|
+
const importDefaultSpecifier = importDefaultSpecifiers[0];
|
|
44
52
|
pageInfo.hasPageBack = true;
|
|
45
|
-
pageInfo.pageBackFnName =
|
|
53
|
+
pageInfo.pageBackFnName = importDefaultSpecifier.local.name;
|
|
54
|
+
const importSpecifiers = node.specifiers.filter((i) => i.type === "ImportSpecifier");
|
|
55
|
+
importSpecifiers.map((specifiers) => {
|
|
56
|
+
if (specifiers.imported.type === "Identifier" && specifiers.imported.name === "activeMpBack") {
|
|
57
|
+
pageInfo.activeFnName = specifiers.local.name;
|
|
58
|
+
}
|
|
59
|
+
if (specifiers.imported.type === "Identifier" && specifiers.imported.name === "inactiveMpBack") {
|
|
60
|
+
pageInfo.inActiveFnName = specifiers.local.name;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
46
63
|
}
|
|
47
64
|
if (node.source.value === "vue") {
|
|
48
65
|
node.specifiers.some((specifier) => {
|
|
@@ -55,6 +72,7 @@ function compositionWalk(context, code, sfc, id) {
|
|
|
55
72
|
}
|
|
56
73
|
}
|
|
57
74
|
if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageInfo.pageBackFnName) {
|
|
75
|
+
pageInfo.onPageBackCallNodeToRemove = node;
|
|
58
76
|
const callback = node.expression.arguments[0];
|
|
59
77
|
const backArguments = node.expression.arguments[1];
|
|
60
78
|
if (backArguments?.type === "ObjectExpression") {
|
|
@@ -65,30 +83,77 @@ function compositionWalk(context, code, sfc, id) {
|
|
|
65
83
|
Object.assign(pageInfo.backConfig, config);
|
|
66
84
|
}
|
|
67
85
|
if (callback && (callback.type === "ArrowFunctionExpression" || callback.type === "FunctionExpression")) {
|
|
68
|
-
|
|
69
|
-
if (body.type === "BlockStatement") {
|
|
70
|
-
pageInfo.callbackCode += body.body.map(
|
|
71
|
-
// @ts-ignore
|
|
72
|
-
(statement) => (generate__default.default ? generate__default.default : generate__default)(statement).code
|
|
73
|
-
).join("");
|
|
74
|
-
}
|
|
86
|
+
pageInfo.onPageBackBodyAst = callback.body.body;
|
|
75
87
|
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageInfo.activeFnName) {
|
|
91
|
+
activeFnCallsToModify.push({
|
|
92
|
+
start: node.expression.start,
|
|
93
|
+
end: node.expression.end,
|
|
94
|
+
original: sfc.scriptSetup.loc.source.substring(
|
|
95
|
+
node.expression.start,
|
|
96
|
+
node.expression.end
|
|
97
|
+
),
|
|
98
|
+
name: pageInfo.activeFnName
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageInfo.inActiveFnName) {
|
|
102
|
+
inActiveFnCallsToModify.push({
|
|
103
|
+
start: node.expression.start,
|
|
104
|
+
end: node.expression.end,
|
|
105
|
+
original: sfc.scriptSetup.loc.source.substring(
|
|
106
|
+
node.expression.start,
|
|
107
|
+
node.expression.end
|
|
108
|
+
),
|
|
109
|
+
name: pageInfo.inActiveFnName
|
|
110
|
+
});
|
|
76
111
|
}
|
|
77
112
|
}
|
|
78
113
|
});
|
|
79
114
|
}
|
|
80
115
|
if (!pageInfo.hasPageBack)
|
|
81
116
|
return code;
|
|
117
|
+
if (pageInfo.onPageBackCallNodeToRemove) {
|
|
118
|
+
const scriptSetupOffset = sfc.scriptSetup.loc.start.offset;
|
|
119
|
+
const nodeToRemove = pageInfo.onPageBackCallNodeToRemove;
|
|
120
|
+
const globalStart = scriptSetupOffset + nodeToRemove.start;
|
|
121
|
+
const globalEnd = scriptSetupOffset + nodeToRemove.end;
|
|
122
|
+
codeMs.remove(globalStart, globalEnd);
|
|
123
|
+
}
|
|
124
|
+
let callbackCode = "";
|
|
125
|
+
if (pageInfo.onPageBackBodyAst.length > 0) {
|
|
126
|
+
const tempAstRoot = {
|
|
127
|
+
type: "BlockStatement",
|
|
128
|
+
body: pageInfo.onPageBackBodyAst
|
|
129
|
+
};
|
|
130
|
+
astKit.walkAST(tempAstRoot, {
|
|
131
|
+
enter(node) {
|
|
132
|
+
if (node.type === "CallExpression" && node.callee.type === "Identifier") {
|
|
133
|
+
const createIdentifier = (name) => ({ type: "Identifier", name });
|
|
134
|
+
if (node.callee.name === pageInfo.activeFnName) {
|
|
135
|
+
node.arguments.unshift(createIdentifier("__MP_WEIXIN_ACTIVEBACK__"));
|
|
136
|
+
} else if (node.callee.name === pageInfo.inActiveFnName) {
|
|
137
|
+
node.arguments.unshift(createIdentifier("__MP_WEIXIN_INACTIVEBACK__"));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
callbackCode = pageInfo.onPageBackBodyAst.map((statement) => (generate__default.default ? generate__default.default : generate__default)(statement).code).join("\n");
|
|
143
|
+
}
|
|
82
144
|
if (code.includes("<page-container")) {
|
|
83
145
|
context.log.debugLog(`${context.getPageById(id)}\u9875\u9762\u5DF2\u6709page-container\u7EC4\u4EF6\uFF0C\u6CE8\u5165\u5931\u8D25`);
|
|
84
146
|
return code;
|
|
85
147
|
}
|
|
86
148
|
if (!pageInfo.backConfig.preventDefault) {
|
|
87
|
-
|
|
149
|
+
callbackCode += "uni.navigateBack({ delta: 1 });";
|
|
88
150
|
}
|
|
151
|
+
const importUseMpWeixinBack = `import { useMpWeixinBack } from '${virtualFileId}'`;
|
|
89
152
|
const importRefFromVue = !pageInfo.hasImportRef ? `import { ref } from 'vue'` : "";
|
|
90
153
|
const stateFrequency = "let __MP_BACK_FREQUENCY__ = 1;";
|
|
91
|
-
const statePageContainerVar =
|
|
154
|
+
const statePageContainerVar = `
|
|
155
|
+
const { __MP_BACK_SHOW_PAGE_CONTAINER__, __MP_WEIXIN_ACTIVEBACK__, __MP_WEIXIN_INACTIVEBACK__ } = useMpWeixinBack(${pageInfo.backConfig.initialValue})
|
|
156
|
+
`;
|
|
92
157
|
const configBack = (() => {
|
|
93
158
|
const onPageBack = pageInfo.backConfig.onPageBack;
|
|
94
159
|
if (!onPageBack)
|
|
@@ -104,13 +169,16 @@ function compositionWalk(context, code, sfc, id) {
|
|
|
104
169
|
})();
|
|
105
170
|
const stateBeforeLeave = `
|
|
106
171
|
const onBeforeLeave = () => {
|
|
172
|
+
if (!__MP_BACK_SHOW_PAGE_CONTAINER__.value) {
|
|
173
|
+
return
|
|
174
|
+
}
|
|
107
175
|
if (__MP_BACK_FREQUENCY__ < ${pageInfo.backConfig.frequency}) {
|
|
108
176
|
__MP_BACK_SHOW_PAGE_CONTAINER__.value = false
|
|
109
177
|
setTimeout(() => __MP_BACK_SHOW_PAGE_CONTAINER__.value = true, 0);
|
|
110
178
|
__MP_BACK_FREQUENCY__++
|
|
111
179
|
}
|
|
112
180
|
${configBack}
|
|
113
|
-
${
|
|
181
|
+
${callbackCode}
|
|
114
182
|
};
|
|
115
183
|
`;
|
|
116
184
|
const { template, scriptSetup } = sfc;
|
|
@@ -130,10 +198,31 @@ function compositionWalk(context, code, sfc, id) {
|
|
|
130
198
|
const scriptMagicString = new MagicString__default(scriptOffsets.content);
|
|
131
199
|
scriptMagicString.prepend(
|
|
132
200
|
` ${importRefFromVue}
|
|
201
|
+
${importUseMpWeixinBack}
|
|
133
202
|
${stateFrequency}
|
|
134
203
|
${statePageContainerVar}
|
|
135
204
|
${stateBeforeLeave} `
|
|
136
205
|
);
|
|
206
|
+
activeFnCallsToModify.forEach((call) => {
|
|
207
|
+
const fnCallRegex = new RegExp(`${call.name}\\(([^)]*)\\)`, "g");
|
|
208
|
+
const newCall = call.original.replace(fnCallRegex, (_match, args) => {
|
|
209
|
+
if (!args.trim()) {
|
|
210
|
+
return `${call.name}(__MP_WEIXIN_ACTIVEBACK__)`;
|
|
211
|
+
}
|
|
212
|
+
return `${call.name}(__MP_WEIXIN_ACTIVEBACK__, ${args})`;
|
|
213
|
+
});
|
|
214
|
+
scriptMagicString.overwrite(call.start, call.end, newCall);
|
|
215
|
+
});
|
|
216
|
+
inActiveFnCallsToModify.forEach((call) => {
|
|
217
|
+
const fnCallRegex = new RegExp(`${call.name}\\(([^)]*)\\)`, "g");
|
|
218
|
+
const newCall = call.original.replace(fnCallRegex, (_match, args) => {
|
|
219
|
+
if (!args.trim()) {
|
|
220
|
+
return `${call.name}(__MP_WEIXIN_INACTIVEBACK__)`;
|
|
221
|
+
}
|
|
222
|
+
return `${call.name}(__MP_WEIXIN_INACTIVEBACK__, ${args})`;
|
|
223
|
+
});
|
|
224
|
+
scriptMagicString.overwrite(call.start, call.end, newCall);
|
|
225
|
+
});
|
|
137
226
|
codeMs.overwrite(scriptOffsets.start, scriptOffsets.end, scriptMagicString.toString());
|
|
138
227
|
return codeMs.toString();
|
|
139
228
|
}
|
|
@@ -413,6 +502,7 @@ class pageContext {
|
|
|
413
502
|
function MpBackPlugin(userOptions = {}) {
|
|
414
503
|
let context;
|
|
415
504
|
const defaultOptions = {
|
|
505
|
+
initialValue: true,
|
|
416
506
|
preventDefault: false,
|
|
417
507
|
frequency: 1,
|
|
418
508
|
debug: false
|
|
@@ -437,7 +527,33 @@ function MpBackPlugin(userOptions = {}) {
|
|
|
437
527
|
return;
|
|
438
528
|
}
|
|
439
529
|
if (id === virtualFileId) {
|
|
440
|
-
return `
|
|
530
|
+
return `
|
|
531
|
+
import { ref } from 'vue'
|
|
532
|
+
export default function onPageBack() {}
|
|
533
|
+
export function activeMpBack(fn = null) {
|
|
534
|
+
fn?.()
|
|
535
|
+
}
|
|
536
|
+
export function inactiveMpBack(fn = null) {
|
|
537
|
+
fn?.()
|
|
538
|
+
}
|
|
539
|
+
export function useMpWeixinBack(initialValue = true) {
|
|
540
|
+
const __MP_BACK_SHOW_PAGE_CONTAINER__ = ref(initialValue)
|
|
541
|
+
|
|
542
|
+
const __MP_WEIXIN_ACTIVEBACK__ = () => {
|
|
543
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__.value = true
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const __MP_WEIXIN_INACTIVEBACK__ = () => {
|
|
547
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__.value = false
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return {
|
|
551
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__,
|
|
552
|
+
__MP_WEIXIN_ACTIVEBACK__,
|
|
553
|
+
__MP_WEIXIN_INACTIVEBACK__
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
`;
|
|
441
557
|
}
|
|
442
558
|
},
|
|
443
559
|
async transform(code, id) {
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -23,16 +23,33 @@ function compositionWalk(context, code, sfc, id) {
|
|
|
23
23
|
pageBackFnName: "onPageBack",
|
|
24
24
|
hasImportRef: false,
|
|
25
25
|
backConfig: { ...context.config },
|
|
26
|
-
|
|
26
|
+
onPageBackBodyAst: [],
|
|
27
|
+
onPageBackCallNodeToRemove: null,
|
|
28
|
+
activeFnName: "activeMpBack",
|
|
29
|
+
inActiveFnName: "inactiveMpBack"
|
|
27
30
|
};
|
|
31
|
+
const activeFnCallsToModify = [];
|
|
32
|
+
const inActiveFnCallsToModify = [];
|
|
28
33
|
if (setupAst) {
|
|
29
34
|
walkAST(setupAst, {
|
|
30
35
|
enter(node) {
|
|
31
36
|
if (node.type === "ImportDeclaration") {
|
|
32
37
|
if (node.source.value.includes(virtualFileId)) {
|
|
33
|
-
const
|
|
38
|
+
const importDefaultSpecifiers = node.specifiers.filter(
|
|
39
|
+
(i) => i.type === "ImportDefaultSpecifier"
|
|
40
|
+
);
|
|
41
|
+
const importDefaultSpecifier = importDefaultSpecifiers[0];
|
|
34
42
|
pageInfo.hasPageBack = true;
|
|
35
|
-
pageInfo.pageBackFnName =
|
|
43
|
+
pageInfo.pageBackFnName = importDefaultSpecifier.local.name;
|
|
44
|
+
const importSpecifiers = node.specifiers.filter((i) => i.type === "ImportSpecifier");
|
|
45
|
+
importSpecifiers.map((specifiers) => {
|
|
46
|
+
if (specifiers.imported.type === "Identifier" && specifiers.imported.name === "activeMpBack") {
|
|
47
|
+
pageInfo.activeFnName = specifiers.local.name;
|
|
48
|
+
}
|
|
49
|
+
if (specifiers.imported.type === "Identifier" && specifiers.imported.name === "inactiveMpBack") {
|
|
50
|
+
pageInfo.inActiveFnName = specifiers.local.name;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
36
53
|
}
|
|
37
54
|
if (node.source.value === "vue") {
|
|
38
55
|
node.specifiers.some((specifier) => {
|
|
@@ -45,6 +62,7 @@ function compositionWalk(context, code, sfc, id) {
|
|
|
45
62
|
}
|
|
46
63
|
}
|
|
47
64
|
if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageInfo.pageBackFnName) {
|
|
65
|
+
pageInfo.onPageBackCallNodeToRemove = node;
|
|
48
66
|
const callback = node.expression.arguments[0];
|
|
49
67
|
const backArguments = node.expression.arguments[1];
|
|
50
68
|
if (backArguments?.type === "ObjectExpression") {
|
|
@@ -55,30 +73,77 @@ function compositionWalk(context, code, sfc, id) {
|
|
|
55
73
|
Object.assign(pageInfo.backConfig, config);
|
|
56
74
|
}
|
|
57
75
|
if (callback && (callback.type === "ArrowFunctionExpression" || callback.type === "FunctionExpression")) {
|
|
58
|
-
|
|
59
|
-
if (body.type === "BlockStatement") {
|
|
60
|
-
pageInfo.callbackCode += body.body.map(
|
|
61
|
-
// @ts-ignore
|
|
62
|
-
(statement) => (generate.default ? generate.default : generate)(statement).code
|
|
63
|
-
).join("");
|
|
64
|
-
}
|
|
76
|
+
pageInfo.onPageBackBodyAst = callback.body.body;
|
|
65
77
|
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageInfo.activeFnName) {
|
|
81
|
+
activeFnCallsToModify.push({
|
|
82
|
+
start: node.expression.start,
|
|
83
|
+
end: node.expression.end,
|
|
84
|
+
original: sfc.scriptSetup.loc.source.substring(
|
|
85
|
+
node.expression.start,
|
|
86
|
+
node.expression.end
|
|
87
|
+
),
|
|
88
|
+
name: pageInfo.activeFnName
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.loc?.identifierName === pageInfo.inActiveFnName) {
|
|
92
|
+
inActiveFnCallsToModify.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.inActiveFnName
|
|
100
|
+
});
|
|
66
101
|
}
|
|
67
102
|
}
|
|
68
103
|
});
|
|
69
104
|
}
|
|
70
105
|
if (!pageInfo.hasPageBack)
|
|
71
106
|
return code;
|
|
107
|
+
if (pageInfo.onPageBackCallNodeToRemove) {
|
|
108
|
+
const scriptSetupOffset = sfc.scriptSetup.loc.start.offset;
|
|
109
|
+
const nodeToRemove = pageInfo.onPageBackCallNodeToRemove;
|
|
110
|
+
const globalStart = scriptSetupOffset + nodeToRemove.start;
|
|
111
|
+
const globalEnd = scriptSetupOffset + nodeToRemove.end;
|
|
112
|
+
codeMs.remove(globalStart, globalEnd);
|
|
113
|
+
}
|
|
114
|
+
let callbackCode = "";
|
|
115
|
+
if (pageInfo.onPageBackBodyAst.length > 0) {
|
|
116
|
+
const tempAstRoot = {
|
|
117
|
+
type: "BlockStatement",
|
|
118
|
+
body: pageInfo.onPageBackBodyAst
|
|
119
|
+
};
|
|
120
|
+
walkAST(tempAstRoot, {
|
|
121
|
+
enter(node) {
|
|
122
|
+
if (node.type === "CallExpression" && node.callee.type === "Identifier") {
|
|
123
|
+
const createIdentifier = (name) => ({ type: "Identifier", name });
|
|
124
|
+
if (node.callee.name === pageInfo.activeFnName) {
|
|
125
|
+
node.arguments.unshift(createIdentifier("__MP_WEIXIN_ACTIVEBACK__"));
|
|
126
|
+
} else if (node.callee.name === pageInfo.inActiveFnName) {
|
|
127
|
+
node.arguments.unshift(createIdentifier("__MP_WEIXIN_INACTIVEBACK__"));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
callbackCode = pageInfo.onPageBackBodyAst.map((statement) => (generate.default ? generate.default : generate)(statement).code).join("\n");
|
|
133
|
+
}
|
|
72
134
|
if (code.includes("<page-container")) {
|
|
73
135
|
context.log.debugLog(`${context.getPageById(id)}\u9875\u9762\u5DF2\u6709page-container\u7EC4\u4EF6\uFF0C\u6CE8\u5165\u5931\u8D25`);
|
|
74
136
|
return code;
|
|
75
137
|
}
|
|
76
138
|
if (!pageInfo.backConfig.preventDefault) {
|
|
77
|
-
|
|
139
|
+
callbackCode += "uni.navigateBack({ delta: 1 });";
|
|
78
140
|
}
|
|
141
|
+
const importUseMpWeixinBack = `import { useMpWeixinBack } from '${virtualFileId}'`;
|
|
79
142
|
const importRefFromVue = !pageInfo.hasImportRef ? `import { ref } from 'vue'` : "";
|
|
80
143
|
const stateFrequency = "let __MP_BACK_FREQUENCY__ = 1;";
|
|
81
|
-
const statePageContainerVar =
|
|
144
|
+
const statePageContainerVar = `
|
|
145
|
+
const { __MP_BACK_SHOW_PAGE_CONTAINER__, __MP_WEIXIN_ACTIVEBACK__, __MP_WEIXIN_INACTIVEBACK__ } = useMpWeixinBack(${pageInfo.backConfig.initialValue})
|
|
146
|
+
`;
|
|
82
147
|
const configBack = (() => {
|
|
83
148
|
const onPageBack = pageInfo.backConfig.onPageBack;
|
|
84
149
|
if (!onPageBack)
|
|
@@ -94,13 +159,16 @@ function compositionWalk(context, code, sfc, id) {
|
|
|
94
159
|
})();
|
|
95
160
|
const stateBeforeLeave = `
|
|
96
161
|
const onBeforeLeave = () => {
|
|
162
|
+
if (!__MP_BACK_SHOW_PAGE_CONTAINER__.value) {
|
|
163
|
+
return
|
|
164
|
+
}
|
|
97
165
|
if (__MP_BACK_FREQUENCY__ < ${pageInfo.backConfig.frequency}) {
|
|
98
166
|
__MP_BACK_SHOW_PAGE_CONTAINER__.value = false
|
|
99
167
|
setTimeout(() => __MP_BACK_SHOW_PAGE_CONTAINER__.value = true, 0);
|
|
100
168
|
__MP_BACK_FREQUENCY__++
|
|
101
169
|
}
|
|
102
170
|
${configBack}
|
|
103
|
-
${
|
|
171
|
+
${callbackCode}
|
|
104
172
|
};
|
|
105
173
|
`;
|
|
106
174
|
const { template, scriptSetup } = sfc;
|
|
@@ -120,10 +188,31 @@ function compositionWalk(context, code, sfc, id) {
|
|
|
120
188
|
const scriptMagicString = new MagicString(scriptOffsets.content);
|
|
121
189
|
scriptMagicString.prepend(
|
|
122
190
|
` ${importRefFromVue}
|
|
191
|
+
${importUseMpWeixinBack}
|
|
123
192
|
${stateFrequency}
|
|
124
193
|
${statePageContainerVar}
|
|
125
194
|
${stateBeforeLeave} `
|
|
126
195
|
);
|
|
196
|
+
activeFnCallsToModify.forEach((call) => {
|
|
197
|
+
const fnCallRegex = new RegExp(`${call.name}\\(([^)]*)\\)`, "g");
|
|
198
|
+
const newCall = call.original.replace(fnCallRegex, (_match, args) => {
|
|
199
|
+
if (!args.trim()) {
|
|
200
|
+
return `${call.name}(__MP_WEIXIN_ACTIVEBACK__)`;
|
|
201
|
+
}
|
|
202
|
+
return `${call.name}(__MP_WEIXIN_ACTIVEBACK__, ${args})`;
|
|
203
|
+
});
|
|
204
|
+
scriptMagicString.overwrite(call.start, call.end, newCall);
|
|
205
|
+
});
|
|
206
|
+
inActiveFnCallsToModify.forEach((call) => {
|
|
207
|
+
const fnCallRegex = new RegExp(`${call.name}\\(([^)]*)\\)`, "g");
|
|
208
|
+
const newCall = call.original.replace(fnCallRegex, (_match, args) => {
|
|
209
|
+
if (!args.trim()) {
|
|
210
|
+
return `${call.name}(__MP_WEIXIN_INACTIVEBACK__)`;
|
|
211
|
+
}
|
|
212
|
+
return `${call.name}(__MP_WEIXIN_INACTIVEBACK__, ${args})`;
|
|
213
|
+
});
|
|
214
|
+
scriptMagicString.overwrite(call.start, call.end, newCall);
|
|
215
|
+
});
|
|
127
216
|
codeMs.overwrite(scriptOffsets.start, scriptOffsets.end, scriptMagicString.toString());
|
|
128
217
|
return codeMs.toString();
|
|
129
218
|
}
|
|
@@ -403,6 +492,7 @@ class pageContext {
|
|
|
403
492
|
function MpBackPlugin(userOptions = {}) {
|
|
404
493
|
let context;
|
|
405
494
|
const defaultOptions = {
|
|
495
|
+
initialValue: true,
|
|
406
496
|
preventDefault: false,
|
|
407
497
|
frequency: 1,
|
|
408
498
|
debug: false
|
|
@@ -427,7 +517,33 @@ function MpBackPlugin(userOptions = {}) {
|
|
|
427
517
|
return;
|
|
428
518
|
}
|
|
429
519
|
if (id === virtualFileId) {
|
|
430
|
-
return `
|
|
520
|
+
return `
|
|
521
|
+
import { ref } from 'vue'
|
|
522
|
+
export default function onPageBack() {}
|
|
523
|
+
export function activeMpBack(fn = null) {
|
|
524
|
+
fn?.()
|
|
525
|
+
}
|
|
526
|
+
export function inactiveMpBack(fn = null) {
|
|
527
|
+
fn?.()
|
|
528
|
+
}
|
|
529
|
+
export function useMpWeixinBack(initialValue = true) {
|
|
530
|
+
const __MP_BACK_SHOW_PAGE_CONTAINER__ = ref(initialValue)
|
|
531
|
+
|
|
532
|
+
const __MP_WEIXIN_ACTIVEBACK__ = () => {
|
|
533
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__.value = true
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const __MP_WEIXIN_INACTIVEBACK__ = () => {
|
|
537
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__.value = false
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return {
|
|
541
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__,
|
|
542
|
+
__MP_WEIXIN_ACTIVEBACK__,
|
|
543
|
+
__MP_WEIXIN_INACTIVEBACK__
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
`;
|
|
431
547
|
}
|
|
432
548
|
},
|
|
433
549
|
async transform(code, id) {
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,82 +1,151 @@
|
|
|
1
|
-
|
|
1
|
+
# mp-weixin-back
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## 功能概述
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`mp-weixin-back` 是一个专门用于监听微信小程序`手势返回`、`导航栏返回事件`、`navigateBack`的工具库,提供灵活的配置选项和简洁的 API。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## 📦 安装
|
|
8
8
|
|
|
9
|
-
```
|
|
9
|
+
```bash
|
|
10
10
|
npm install mp-weixin-back
|
|
11
|
+
# 或
|
|
12
|
+
yarn add mp-weixin-back
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
## ⚙️ Vite 配置
|
|
14
16
|
|
|
15
|
-
`vite.config.ts`
|
|
17
|
+
在 `vite.config.ts` 中添加插件:
|
|
16
18
|
|
|
17
19
|
```ts
|
|
20
|
+
import { defineConfig } from 'vite'
|
|
18
21
|
import mpBackPlugin from 'mp-weixin-back'
|
|
19
22
|
|
|
20
23
|
export default defineConfig({
|
|
21
|
-
plugins: [
|
|
24
|
+
plugins: [
|
|
25
|
+
mpBackPlugin({
|
|
26
|
+
// 可选配置项
|
|
27
|
+
preventDefault: false, // 是否阻止默认返回行为,设置成 true 则不会返回上一层
|
|
28
|
+
frequency: 1, // 阻止次数,需要一直拦截则设置一个很大的值即可,如:9999
|
|
29
|
+
debug: false, // 调试模式,默认为 false
|
|
30
|
+
onPageBack: () => {
|
|
31
|
+
console.log('返回事件触发')
|
|
32
|
+
}, // 统一钩子,事件触发时执行
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
22
35
|
})
|
|
23
36
|
```
|
|
24
37
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
type Config = {
|
|
29
|
-
/**
|
|
30
|
-
* 是否阻止默认的回退事件,默认为 false
|
|
31
|
-
*/
|
|
32
|
-
preventDefault: boolean
|
|
33
|
-
/**
|
|
34
|
-
* 阻止次数,默认是 `1`
|
|
35
|
-
*/
|
|
36
|
-
frequency: number
|
|
37
|
-
/**
|
|
38
|
-
* 页面回退时触发
|
|
39
|
-
*/
|
|
40
|
-
onPageBack?: (params: BackParams) => void
|
|
41
|
-
}
|
|
42
|
-
```
|
|
38
|
+
## 🚀 快速开始
|
|
43
39
|
|
|
44
|
-
|
|
40
|
+
### 基本使用
|
|
45
41
|
|
|
46
42
|
```ts
|
|
43
|
+
<script setup>
|
|
47
44
|
import onPageBack from 'mp-weixin-back-helper'
|
|
48
45
|
|
|
46
|
+
// 简单监听返回事件
|
|
49
47
|
onPageBack(() => {
|
|
50
|
-
console.log('
|
|
48
|
+
console.log('检测到返回操作(手势或导航栏返回)')
|
|
49
|
+
// 在这里添加你的处理逻辑
|
|
51
50
|
})
|
|
51
|
+
</script>
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
### 高级配置
|
|
55
55
|
|
|
56
56
|
```ts
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
// 带配置的监听
|
|
58
|
+
onPageBack(
|
|
59
|
+
() => {
|
|
60
|
+
console.log('返回事件被触发')
|
|
61
|
+
// 自定义处理逻辑
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
initialValue: false, // 立即生效,默认值为`true`
|
|
65
|
+
preventDefault: true, // 阻止默认返回行为
|
|
66
|
+
frequency: 2, // 阻止次数为2次
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
## 📚 API 文档
|
|
72
|
+
|
|
73
|
+
### `onPageBack(callback, config?)`
|
|
74
|
+
|
|
75
|
+
监听页面返回事件
|
|
76
|
+
|
|
77
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
78
|
+
| -------- | ------------ | ---- | ------------------------ |
|
|
79
|
+
| callback | `() => void` | 是 | 返回事件触发时的回调函数 |
|
|
80
|
+
| options | Object | 否 | 监听器配置选项 |
|
|
81
|
+
|
|
82
|
+
#### 配置选项
|
|
83
|
+
|
|
84
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
85
|
+
| -------------- | ------- | ------ | ----------------------------------------------- |
|
|
86
|
+
| preventDefault | boolean | false | 是否阻止默认返回行为(true 时页面不会实际返回) |
|
|
87
|
+
| frequency | number | 1 | 阻止次数 |
|
|
88
|
+
| initialValue | boolean | true | 是否立即启用监听(设为 false 时需手动激活) |
|
|
89
|
+
|
|
90
|
+
### 辅助方法
|
|
91
|
+
|
|
92
|
+
#### `activeMpBack()`
|
|
93
|
+
|
|
94
|
+
启用返回事件监听(需在`<script setup>`中执行)
|
|
95
|
+
|
|
96
|
+
#### `inactiveMpBack()`
|
|
97
|
+
|
|
98
|
+
禁用返回事件监听(需在`<script setup>`中执行)
|
|
99
|
+
|
|
100
|
+
举例:
|
|
72
101
|
|
|
73
|
-
|
|
102
|
+
```html
|
|
103
|
+
<template>
|
|
104
|
+
<div>
|
|
105
|
+
<!-- 页面代码 -->
|
|
106
|
+
<button @click="toggleListener(true)">开启</button>
|
|
107
|
+
<button @click="toggleListener(false)">禁用</button>
|
|
108
|
+
</div>
|
|
109
|
+
</template>
|
|
74
110
|
|
|
111
|
+
<script setup>
|
|
112
|
+
import onPageBack, { activeMpBack, inactiveMpBack } from 'mp-weixin-back-helper'
|
|
113
|
+
|
|
114
|
+
const toggleListener = (enable) => {
|
|
115
|
+
enable ? activeMpBack() : inactiveMpBack()
|
|
116
|
+
}
|
|
117
|
+
</script>
|
|
75
118
|
```
|
|
76
|
-
|
|
119
|
+
|
|
120
|
+
## 🎯 选项式 API 支持(未完善)
|
|
121
|
+
|
|
122
|
+
组件内直接声明
|
|
123
|
+
|
|
124
|
+
在 Vue 组件的选项对象中直接定义 onPageBack 方法:
|
|
125
|
+
|
|
126
|
+
```html
|
|
127
|
+
<template>
|
|
128
|
+
<div class="container">
|
|
129
|
+
<div>当前页面内容</div>
|
|
130
|
+
</div>
|
|
131
|
+
</template>
|
|
132
|
+
|
|
133
|
+
<script>
|
|
134
|
+
export default {
|
|
135
|
+
// 读取 vite 中的配置
|
|
136
|
+
onPageBack() {
|
|
137
|
+
console.log('检测到返回操作')
|
|
138
|
+
// 业务逻辑处理
|
|
139
|
+
},
|
|
140
|
+
}
|
|
141
|
+
</script>
|
|
77
142
|
```
|
|
78
143
|
|
|
79
|
-
|
|
144
|
+
## 🛠 类型支持
|
|
145
|
+
|
|
146
|
+
### 类型声明配置
|
|
147
|
+
|
|
148
|
+
在 `tsconfig.json` 中添加:
|
|
80
149
|
|
|
81
150
|
```json
|
|
82
151
|
{
|
|
@@ -86,9 +155,19 @@ function onPageBack(callback: () => void, params: Partial<Config>)
|
|
|
86
155
|
}
|
|
87
156
|
```
|
|
88
157
|
|
|
89
|
-
|
|
158
|
+
或通过声明文件引用:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// env.d.ts
|
|
162
|
+
/// <reference types="mp-weixin-back/client" />
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## ❓ 常见问题
|
|
166
|
+
|
|
167
|
+
### Q1: 如何实现多页面独立配置?
|
|
168
|
+
|
|
169
|
+
每个页面单独调用 `onPageBack` 时传入不同的配置参数即可实现页面级定制。
|
|
170
|
+
|
|
171
|
+
### Q2: 全局配置与页面配置的优先级?
|
|
90
172
|
|
|
91
|
-
|
|
92
|
-
- [ ] debug 模式
|
|
93
|
-
- [ ] 热更新 pages.json 文件
|
|
94
|
-
- [ ] 单元测试
|
|
173
|
+
页面级配置会覆盖全局配置,建议将通用配置放在全局,特殊需求在页面单独设置。
|
package/shims-vue.d.ts
ADDED
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ function MpBackPlugin(userOptions: UserOptions = {}): Plugin {
|
|
|
7
7
|
let context: pageContext
|
|
8
8
|
|
|
9
9
|
const defaultOptions: Config = {
|
|
10
|
+
initialValue: true,
|
|
10
11
|
preventDefault: false,
|
|
11
12
|
frequency: 1,
|
|
12
13
|
debug: false,
|
|
@@ -33,7 +34,33 @@ function MpBackPlugin(userOptions: UserOptions = {}): Plugin {
|
|
|
33
34
|
}
|
|
34
35
|
// 导出一个对象
|
|
35
36
|
if (id === virtualFileId) {
|
|
36
|
-
return `
|
|
37
|
+
return `
|
|
38
|
+
import { ref } from 'vue'
|
|
39
|
+
export default function onPageBack() {}
|
|
40
|
+
export function activeMpBack(fn = null) {
|
|
41
|
+
fn?.()
|
|
42
|
+
}
|
|
43
|
+
export function inactiveMpBack(fn = null) {
|
|
44
|
+
fn?.()
|
|
45
|
+
}
|
|
46
|
+
export function useMpWeixinBack(initialValue = true) {
|
|
47
|
+
const __MP_BACK_SHOW_PAGE_CONTAINER__ = ref(initialValue)
|
|
48
|
+
|
|
49
|
+
const __MP_WEIXIN_ACTIVEBACK__ = () => {
|
|
50
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__.value = true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const __MP_WEIXIN_INACTIVEBACK__ = () => {
|
|
54
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__.value = false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
__MP_BACK_SHOW_PAGE_CONTAINER__,
|
|
59
|
+
__MP_WEIXIN_ACTIVEBACK__,
|
|
60
|
+
__MP_WEIXIN_INACTIVEBACK__
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
`
|
|
37
64
|
}
|
|
38
65
|
},
|
|
39
66
|
async transform(code, id) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>我是默认的界面</div>
|
|
3
|
+
<button id="button" @click="disableMpBack"></button>
|
|
4
|
+
<button id="button2" @click="activeMpBack"></button>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup>
|
|
8
|
+
import onPageBack, { activeMpBack as mpppacitve, inactiveMpBack } from 'mp-weixin-back-helper'
|
|
9
|
+
|
|
10
|
+
onPageBack(
|
|
11
|
+
() => {
|
|
12
|
+
console.log('触发了手势返回')
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
initialValue: false,
|
|
16
|
+
}
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
const activeMpBack = () => {
|
|
20
|
+
console.log('执行了activeMpBack')
|
|
21
|
+
mpppacitve()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const disableMpBack = () => {
|
|
25
|
+
inactiveMpBack()
|
|
26
|
+
}
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<style></style>
|
package/test/generate.spec.ts
CHANGED
|
@@ -1,31 +1,64 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
1
|
+
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
|
|
2
2
|
import { mount } from '@vue/test-utils'
|
|
3
|
-
// @ts-ignore
|
|
4
3
|
import IndexSetup from './data/index-setup.vue'
|
|
5
|
-
|
|
4
|
+
import IndexUtils from './data/index-utils.vue'
|
|
6
5
|
import IndexDefault from './data/index-default.vue'
|
|
7
6
|
|
|
8
7
|
describe('generate page-container components', () => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
let logSpy: ReturnType<typeof vi.spyOn>
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
logSpy.mockRestore()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('setup compositionAPI', () => {
|
|
19
|
+
it('default case', async () => {
|
|
20
|
+
const wrapper = mount(IndexSetup)
|
|
21
|
+
await wrapper.vm.$nextTick()
|
|
22
|
+
|
|
23
|
+
const pageContainerRef = wrapper.find('page-container')
|
|
24
|
+
expect(pageContainerRef.exists()).toBe(true)
|
|
25
|
+
expect(wrapper.vm.__MP_BACK_SHOW_PAGE_CONTAINER__).toBe(true)
|
|
26
|
+
expect(typeof wrapper.vm.onBeforeLeave).toBe('function')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('utils case', async () => {
|
|
30
|
+
const wrapper = mount(IndexUtils)
|
|
31
|
+
await wrapper.vm.$nextTick()
|
|
32
|
+
|
|
33
|
+
const pageContainerRef = wrapper.find('page-container')
|
|
34
|
+
expect(pageContainerRef.exists()).toBe(true)
|
|
35
|
+
expect(wrapper.vm.__MP_BACK_SHOW_PAGE_CONTAINER__).toBe(false)
|
|
36
|
+
expect(typeof wrapper.vm.onBeforeLeave).toBe('function')
|
|
37
|
+
|
|
38
|
+
await wrapper.find('#button2').trigger('click')
|
|
39
|
+
await new Promise(resolve => setTimeout(resolve, 0))
|
|
40
|
+
|
|
41
|
+
expect(logSpy).toHaveBeenCalledWith('执行了activeMpBack')
|
|
42
|
+
|
|
43
|
+
expect(wrapper.vm.__MP_BACK_SHOW_PAGE_CONTAINER__).toBe(true)
|
|
44
|
+
|
|
45
|
+
await wrapper.find('#button').trigger('click')
|
|
46
|
+
await new Promise(resolve => setTimeout(resolve, 0))
|
|
47
|
+
|
|
48
|
+
expect(wrapper.vm.__MP_BACK_SHOW_PAGE_CONTAINER__).toBe(false)
|
|
49
|
+
})
|
|
17
50
|
})
|
|
18
51
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
52
|
+
describe('optionsAPI', () => {
|
|
53
|
+
it('default case', async () => {
|
|
54
|
+
const wrapper = mount(IndexDefault)
|
|
55
|
+
await wrapper.vm.$nextTick()
|
|
56
|
+
|
|
57
|
+
const pageContainerRef = wrapper.find('page-container')
|
|
58
|
+
expect(pageContainerRef.exists()).toBe(true)
|
|
59
|
+
expect(wrapper.vm.__MP_BACK_SHOW_PAGE_CONTAINER__).toBe(true)
|
|
60
|
+
expect(wrapper.vm.__MP_BACK_FREQUENCY__).toBe(1)
|
|
61
|
+
expect(wrapper.vm.onBeforeLeave()).toBe(true)
|
|
62
|
+
})
|
|
30
63
|
})
|
|
31
64
|
})
|
package/types/index.ts
CHANGED
package/utils/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { parse } from '@vue/compiler-sfc'
|
|
2
2
|
import { pageContext } from '../src/context'
|
|
3
3
|
import { vueWalker } from './walker'
|
|
4
|
-
import type { SFCDescriptor } from '@vue/compiler-sfc'
|
|
5
4
|
|
|
6
5
|
export async function transformVueFile(this: pageContext, code: string, id: string) {
|
|
7
6
|
try {
|
package/utils/walker.ts
CHANGED
|
@@ -4,7 +4,9 @@ import { babelParse, walkAST } from 'ast-kit'
|
|
|
4
4
|
import { pageContext } from '../src/context'
|
|
5
5
|
import { virtualFileId } from './constant'
|
|
6
6
|
import type {
|
|
7
|
+
WithStatement,
|
|
7
8
|
BlockStatement,
|
|
9
|
+
Statement,
|
|
8
10
|
FunctionExpression,
|
|
9
11
|
Node,
|
|
10
12
|
ObjectExpression,
|
|
@@ -29,17 +31,42 @@ function compositionWalk(context: pageContext, code: string, sfc: any, id: strin
|
|
|
29
31
|
pageBackFnName: 'onPageBack',
|
|
30
32
|
hasImportRef: false,
|
|
31
33
|
backConfig: { ...context.config },
|
|
32
|
-
|
|
34
|
+
onPageBackBodyAst: [] as Statement[],
|
|
35
|
+
onPageBackCallNodeToRemove: null as Node | null,
|
|
36
|
+
activeFnName: 'activeMpBack',
|
|
37
|
+
inActiveFnName: 'inactiveMpBack',
|
|
33
38
|
}
|
|
34
39
|
|
|
40
|
+
const activeFnCallsToModify: any[] = []
|
|
41
|
+
const inActiveFnCallsToModify: any[] = []
|
|
42
|
+
|
|
35
43
|
if (setupAst) {
|
|
36
44
|
walkAST<Node>(setupAst, {
|
|
37
45
|
enter(node) {
|
|
38
46
|
if (node.type === 'ImportDeclaration') {
|
|
39
47
|
if (node.source.value.includes(virtualFileId)) {
|
|
40
|
-
const
|
|
48
|
+
const importDefaultSpecifiers = node.specifiers.filter(
|
|
49
|
+
(i) => i.type === 'ImportDefaultSpecifier'
|
|
50
|
+
)
|
|
51
|
+
const importDefaultSpecifier = importDefaultSpecifiers[0]
|
|
41
52
|
pageInfo.hasPageBack = true
|
|
42
|
-
pageInfo.pageBackFnName =
|
|
53
|
+
pageInfo.pageBackFnName = importDefaultSpecifier.local.name
|
|
54
|
+
|
|
55
|
+
const importSpecifiers = node.specifiers.filter((i) => i.type === 'ImportSpecifier')
|
|
56
|
+
importSpecifiers.map((specifiers) => {
|
|
57
|
+
if (
|
|
58
|
+
specifiers.imported.type === 'Identifier' &&
|
|
59
|
+
specifiers.imported.name === 'activeMpBack'
|
|
60
|
+
) {
|
|
61
|
+
pageInfo.activeFnName = specifiers.local.name
|
|
62
|
+
}
|
|
63
|
+
if (
|
|
64
|
+
specifiers.imported.type === 'Identifier' &&
|
|
65
|
+
specifiers.imported.name === 'inactiveMpBack'
|
|
66
|
+
) {
|
|
67
|
+
pageInfo.inActiveFnName = specifiers.local.name
|
|
68
|
+
}
|
|
69
|
+
})
|
|
43
70
|
}
|
|
44
71
|
if (node.source.value === 'vue') {
|
|
45
72
|
node.specifiers.some((specifier) => {
|
|
@@ -57,6 +84,8 @@ function compositionWalk(context: pageContext, code: string, sfc: any, id: strin
|
|
|
57
84
|
node.expression.type === 'CallExpression' &&
|
|
58
85
|
node.expression.callee.loc?.identifierName === pageInfo.pageBackFnName
|
|
59
86
|
) {
|
|
87
|
+
// 记录下整个 onPageBack(...) 语句节点,以便后续移除
|
|
88
|
+
pageInfo.onPageBackCallNodeToRemove = node
|
|
60
89
|
const callback = node.expression.arguments[0]
|
|
61
90
|
const backArguments = node.expression.arguments[1]
|
|
62
91
|
|
|
@@ -72,16 +101,43 @@ function compositionWalk(context: pageContext, code: string, sfc: any, id: strin
|
|
|
72
101
|
callback &&
|
|
73
102
|
(callback.type === 'ArrowFunctionExpression' || callback.type === 'FunctionExpression')
|
|
74
103
|
) {
|
|
75
|
-
|
|
76
|
-
if (body.type === 'BlockStatement') {
|
|
77
|
-
pageInfo.callbackCode += body.body
|
|
78
|
-
.map(
|
|
79
|
-
// @ts-ignore
|
|
80
|
-
(statement) => (generate.default ? generate.default : generate)(statement).code
|
|
81
|
-
)
|
|
82
|
-
.join('')
|
|
83
|
-
}
|
|
104
|
+
pageInfo.onPageBackBodyAst = callback.body.body
|
|
84
105
|
}
|
|
106
|
+
|
|
107
|
+
// 跳过此节点的子节点遍历,因为我们将手动处理其内部逻辑
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
node.type === 'ExpressionStatement' &&
|
|
113
|
+
node.expression.type === 'CallExpression' &&
|
|
114
|
+
node.expression.callee.loc?.identifierName === pageInfo.activeFnName
|
|
115
|
+
) {
|
|
116
|
+
activeFnCallsToModify.push({
|
|
117
|
+
start: node.expression.start,
|
|
118
|
+
end: node.expression.end,
|
|
119
|
+
original: sfc.scriptSetup!.loc.source.substring(
|
|
120
|
+
node.expression.start,
|
|
121
|
+
node.expression.end
|
|
122
|
+
),
|
|
123
|
+
name: pageInfo.activeFnName,
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
node.type === 'ExpressionStatement' &&
|
|
129
|
+
node.expression.type === 'CallExpression' &&
|
|
130
|
+
node.expression.callee.loc?.identifierName === pageInfo.inActiveFnName
|
|
131
|
+
) {
|
|
132
|
+
inActiveFnCallsToModify.push({
|
|
133
|
+
start: node.expression.start,
|
|
134
|
+
end: node.expression.end,
|
|
135
|
+
original: sfc.scriptSetup!.loc.source.substring(
|
|
136
|
+
node.expression.start,
|
|
137
|
+
node.expression.end
|
|
138
|
+
),
|
|
139
|
+
name: pageInfo.inActiveFnName,
|
|
140
|
+
})
|
|
85
141
|
}
|
|
86
142
|
},
|
|
87
143
|
})
|
|
@@ -90,18 +146,59 @@ function compositionWalk(context: pageContext, code: string, sfc: any, id: strin
|
|
|
90
146
|
// 没有引入mp-weixin-back-helper
|
|
91
147
|
if (!pageInfo.hasPageBack) return code
|
|
92
148
|
|
|
149
|
+
if (pageInfo.onPageBackCallNodeToRemove) {
|
|
150
|
+
const scriptSetupOffset = sfc.scriptSetup!.loc.start.offset
|
|
151
|
+
const nodeToRemove = pageInfo.onPageBackCallNodeToRemove as any
|
|
152
|
+
const globalStart = scriptSetupOffset + nodeToRemove.start
|
|
153
|
+
const globalEnd = scriptSetupOffset + nodeToRemove.end
|
|
154
|
+
codeMs.remove(globalStart, globalEnd)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let callbackCode = ''
|
|
158
|
+
if (pageInfo.onPageBackBodyAst.length > 0) {
|
|
159
|
+
// 包装成一个临时的 AST 根节点以供遍历
|
|
160
|
+
const tempAstRoot = {
|
|
161
|
+
type: 'BlockStatement',
|
|
162
|
+
body: pageInfo.onPageBackBodyAst,
|
|
163
|
+
} as WithStatement
|
|
164
|
+
|
|
165
|
+
walkAST(tempAstRoot, {
|
|
166
|
+
enter(node: any) {
|
|
167
|
+
if (node.type === 'CallExpression' && node.callee.type === 'Identifier') {
|
|
168
|
+
const createIdentifier = (name: string) => ({ type: 'Identifier', name })
|
|
169
|
+
|
|
170
|
+
if (node.callee.name === pageInfo.activeFnName) {
|
|
171
|
+
node.arguments.unshift(createIdentifier('__MP_WEIXIN_ACTIVEBACK__'))
|
|
172
|
+
} else if (node.callee.name === pageInfo.inActiveFnName) {
|
|
173
|
+
node.arguments.unshift(createIdentifier('__MP_WEIXIN_INACTIVEBACK__'))
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
callbackCode = pageInfo.onPageBackBodyAst
|
|
180
|
+
// @ts-ignore
|
|
181
|
+
.map((statement) => (generate.default ? generate.default : generate)(statement).code)
|
|
182
|
+
.join('\n')
|
|
183
|
+
}
|
|
184
|
+
|
|
93
185
|
if (code.includes('<page-container')) {
|
|
94
186
|
context.log.debugLog(`${context.getPageById(id)}页面已有page-container组件,注入失败`)
|
|
95
187
|
return code
|
|
96
188
|
}
|
|
97
189
|
|
|
98
190
|
if (!pageInfo.backConfig.preventDefault) {
|
|
99
|
-
|
|
191
|
+
callbackCode += 'uni.navigateBack({ delta: 1 });'
|
|
100
192
|
}
|
|
101
193
|
|
|
194
|
+
const importUseMpWeixinBack = `import { useMpWeixinBack } from '${virtualFileId}'`
|
|
102
195
|
const importRefFromVue = !pageInfo.hasImportRef ? `import { ref } from 'vue'` : ''
|
|
103
196
|
const stateFrequency = 'let __MP_BACK_FREQUENCY__ = 1;'
|
|
104
|
-
|
|
197
|
+
|
|
198
|
+
const statePageContainerVar = `
|
|
199
|
+
const { __MP_BACK_SHOW_PAGE_CONTAINER__, __MP_WEIXIN_ACTIVEBACK__, __MP_WEIXIN_INACTIVEBACK__ } = useMpWeixinBack(${pageInfo.backConfig.initialValue})
|
|
200
|
+
`
|
|
201
|
+
|
|
105
202
|
// 获取传入插件的统一方法
|
|
106
203
|
const configBack = (() => {
|
|
107
204
|
const onPageBack = pageInfo.backConfig.onPageBack
|
|
@@ -115,15 +212,19 @@ function compositionWalk(context: pageContext, code: string, sfc: any, id: strin
|
|
|
115
212
|
}
|
|
116
213
|
return `(function ${onPageBack})()`
|
|
117
214
|
})()
|
|
215
|
+
|
|
118
216
|
const stateBeforeLeave = `
|
|
119
217
|
const onBeforeLeave = () => {
|
|
218
|
+
if (!__MP_BACK_SHOW_PAGE_CONTAINER__.value) {
|
|
219
|
+
return
|
|
220
|
+
}
|
|
120
221
|
if (__MP_BACK_FREQUENCY__ < ${pageInfo.backConfig.frequency}) {
|
|
121
222
|
__MP_BACK_SHOW_PAGE_CONTAINER__.value = false
|
|
122
223
|
setTimeout(() => __MP_BACK_SHOW_PAGE_CONTAINER__.value = true, 0);
|
|
123
224
|
__MP_BACK_FREQUENCY__++
|
|
124
225
|
}
|
|
125
226
|
${configBack}
|
|
126
|
-
${
|
|
227
|
+
${callbackCode}
|
|
127
228
|
};
|
|
128
229
|
`
|
|
129
230
|
const { template, scriptSetup } = sfc
|
|
@@ -147,10 +248,43 @@ function compositionWalk(context: pageContext, code: string, sfc: any, id: strin
|
|
|
147
248
|
const scriptMagicString = new MagicString(scriptOffsets.content)
|
|
148
249
|
scriptMagicString.prepend(
|
|
149
250
|
` ${importRefFromVue}
|
|
251
|
+
${importUseMpWeixinBack}
|
|
150
252
|
${stateFrequency}
|
|
151
253
|
${statePageContainerVar}
|
|
152
254
|
${stateBeforeLeave} `
|
|
153
255
|
)
|
|
256
|
+
|
|
257
|
+
// 应用 activeMpBack 调用的修改
|
|
258
|
+
activeFnCallsToModify.forEach((call) => {
|
|
259
|
+
// 使用正则匹配函数调用结构,确保我们只修改括号内的内容
|
|
260
|
+
const fnCallRegex = new RegExp(`${call.name}\\(([^)]*)\\)`, 'g')
|
|
261
|
+
const newCall = call.original.replace(fnCallRegex, (_match: any, args: string) => {
|
|
262
|
+
// 如果原调用没有参数
|
|
263
|
+
if (!args.trim()) {
|
|
264
|
+
return `${call.name}(__MP_WEIXIN_ACTIVEBACK__)`
|
|
265
|
+
}
|
|
266
|
+
// 如果有参数,添加新参数
|
|
267
|
+
return `${call.name}(__MP_WEIXIN_ACTIVEBACK__, ${args})`
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
scriptMagicString.overwrite(call.start, call.end, newCall)
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
inActiveFnCallsToModify.forEach((call) => {
|
|
274
|
+
// 使用正则匹配函数调用结构,确保我们只修改括号内的内容
|
|
275
|
+
const fnCallRegex = new RegExp(`${call.name}\\(([^)]*)\\)`, 'g')
|
|
276
|
+
const newCall = call.original.replace(fnCallRegex, (_match: any, args: string) => {
|
|
277
|
+
// 如果原调用没有参数
|
|
278
|
+
if (!args.trim()) {
|
|
279
|
+
return `${call.name}(__MP_WEIXIN_INACTIVEBACK__)`
|
|
280
|
+
}
|
|
281
|
+
// 如果有参数,添加新参数
|
|
282
|
+
return `${call.name}(__MP_WEIXIN_INACTIVEBACK__, ${args})`
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
scriptMagicString.overwrite(call.start, call.end, newCall)
|
|
286
|
+
})
|
|
287
|
+
|
|
154
288
|
codeMs.overwrite(scriptOffsets.start, scriptOffsets.end, scriptMagicString.toString())
|
|
155
289
|
|
|
156
290
|
return codeMs.toString()
|
|
@@ -284,7 +418,7 @@ function optionsWalk(context: pageContext, code: string, sfc: any, id: string) {
|
|
|
284
418
|
],
|
|
285
419
|
},
|
|
286
420
|
}
|
|
287
|
-
|
|
421
|
+
;(exportDefaultNode as ObjectExpression).properties.push(addData)
|
|
288
422
|
}
|
|
289
423
|
|
|
290
424
|
// 获取传入插件的统一方法
|
|
@@ -340,7 +474,7 @@ function optionsWalk(context: pageContext, code: string, sfc: any, id: string) {
|
|
|
340
474
|
},
|
|
341
475
|
} as ObjectMethod
|
|
342
476
|
if (methodsNode) {
|
|
343
|
-
;
|
|
477
|
+
;(methodsNode as ObjectExpression).properties.push(newMethodsProperty)
|
|
344
478
|
} else if (exportDefaultNode) {
|
|
345
479
|
const addMethods: ObjectProperty = {
|
|
346
480
|
type: 'ObjectProperty',
|
|
@@ -355,7 +489,7 @@ function optionsWalk(context: pageContext, code: string, sfc: any, id: string) {
|
|
|
355
489
|
properties: [newMethodsProperty],
|
|
356
490
|
},
|
|
357
491
|
}
|
|
358
|
-
|
|
492
|
+
;(exportDefaultNode as ObjectExpression).properties.push(addMethods)
|
|
359
493
|
}
|
|
360
494
|
|
|
361
495
|
const { template, script } = sfc
|
package/vite.config.ts
CHANGED
|
@@ -3,7 +3,16 @@ import vue from '@vitejs/plugin-vue'
|
|
|
3
3
|
import mpBack from './dist/index.mjs'
|
|
4
4
|
|
|
5
5
|
export default defineConfig({
|
|
6
|
-
plugins: [
|
|
6
|
+
plugins: [
|
|
7
|
+
mpBack(),
|
|
8
|
+
vue({
|
|
9
|
+
template: {
|
|
10
|
+
compilerOptions: {
|
|
11
|
+
isCustomElement: (tag) => tag.startsWith('page-') || tag.startsWith('mp-'),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
}),
|
|
15
|
+
],
|
|
7
16
|
test: {
|
|
8
17
|
environment: 'happy-dom',
|
|
9
18
|
},
|