mp-weixin-back 0.0.14 → 0.0.16

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.
@@ -0,0 +1,8 @@
1
+ # Changesets
2
+
3
+ Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4
+ with multi-package repos, or single-package repos to help you version and publish your code. You can
5
+ find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6
+
7
+ We have a quick list of common questions to get you started engaging with this project in
8
+ [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
3
+ "changelog": "@changesets/cli/changelog",
4
+ "commit": false,
5
+ "fixed": [],
6
+ "linked": [],
7
+ "access": "public",
8
+ "baseBranch": "main",
9
+ "updateInternalDependencies": "patch",
10
+ "ignore": []
11
+ }
@@ -0,0 +1,38 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ concurrency: ${{ github.workflow }}-${{ github.ref }}
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: write
15
+ pull-requests: write
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: pnpm/action-setup@v4
20
+ with:
21
+ version: latest
22
+
23
+ - uses: actions/setup-node@v4
24
+ with:
25
+ node-version: 20
26
+ registry-url: https://registry.npmjs.org
27
+ cache: pnpm
28
+
29
+ - name: Install dependencies
30
+ run: pnpm install --no-frozen-lockfile
31
+
32
+ - name: Create Release Pull Request or Publish
33
+ uses: changesets/action@v1
34
+ with:
35
+ publish: pnpm release
36
+ env:
37
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/client.d.ts CHANGED
@@ -1,32 +1,109 @@
1
1
  declare module 'mp-weixin-back-helper' {
2
- type Config = {
2
+ /**
3
+ * Per-page options for `onPageBack()`.
4
+ */
5
+ type OnPageBackOptions = {
3
6
  /**
4
- * 初始化时是否监听手势返回,默认值为`true`
7
+ * Whether to block the default back navigation for this page.
8
+ * When `true`, the user stays on the current page and only the callback fires.
9
+ * @default false
5
10
  */
6
- initialValue: boolean
11
+ preventDefault?: boolean
12
+
7
13
  /**
8
- * 是否阻止默认的回退事件,默认为 false
14
+ * How many times to intercept the back event before allowing through.
15
+ * Set to a large number (e.g. `9999`) for persistent interception.
16
+ * @default 1
9
17
  */
10
- preventDefault: boolean
18
+ frequency?: number
19
+
11
20
  /**
12
- * 阻止次数,默认是 `1`
21
+ * Whether to start listening immediately when the page mounts.
22
+ * Set to `false` and call `activeMpBack()` manually to enable later.
23
+ * @default true
13
24
  */
14
- frequency: number
25
+ initialValue?: boolean
15
26
  }
16
27
 
17
- function onPageBack(callback: () => void, params?: Partial<Config>)
28
+ /**
29
+ * Listen to back navigation events (gesture back + navbar back button) on the current page.
30
+ *
31
+ * Must be called inside `<script setup>` at the top level.
32
+ *
33
+ * @param callback - Function to call when back navigation is detected.
34
+ * @param options - Optional per-page configuration. Overrides global plugin config.
35
+ *
36
+ * @example
37
+ * // Basic usage
38
+ * import onPageBack from 'mp-weixin-back-helper'
39
+ *
40
+ * onPageBack(() => {
41
+ * console.log('back detected')
42
+ * })
43
+ *
44
+ * @example
45
+ * // Prevent back and show a confirmation dialog
46
+ * import onPageBack from 'mp-weixin-back-helper'
47
+ *
48
+ * onPageBack(
49
+ * () => {
50
+ * uni.showModal({
51
+ * title: '提示',
52
+ * content: '确定要返回吗?',
53
+ * success: (res) => {
54
+ * if (res.confirm) uni.navigateBack()
55
+ * },
56
+ * })
57
+ * },
58
+ * { preventDefault: true }
59
+ * )
60
+ *
61
+ * @example
62
+ * // Start disabled, enable after a button click
63
+ * import onPageBack, { activeMpBack, inactiveMpBack } from 'mp-weixin-back-helper'
64
+ *
65
+ * onPageBack(() => { handleBack() }, { initialValue: false })
66
+ *
67
+ * function startListening() { activeMpBack() }
68
+ * function stopListening() { inactiveMpBack() }
69
+ */
70
+ function onPageBack(callback: () => void, options?: OnPageBackOptions): void
18
71
 
19
72
  /**
20
- * 开启监听手势滑动事件
73
+ * Enable the back event listener for the current page.
74
+ *
75
+ * Use when `initialValue: false` was passed to `onPageBack()`,
76
+ * or to re-enable after calling `inactiveMpBack()`.
77
+ *
78
+ * Must be called inside `<script setup>`.
79
+ *
80
+ * @example
81
+ * import { activeMpBack } from 'mp-weixin-back-helper'
82
+ *
83
+ * // Enable on button click
84
+ * function handleEnable() {
85
+ * activeMpBack()
86
+ * }
21
87
  */
22
- function activeMpBack()
88
+ function activeMpBack(): void
23
89
 
24
90
  /**
25
- * 禁用监听手势滑动事件
91
+ * Disable the back event listener for the current page.
92
+ *
93
+ * The page will resume its default back behavior until `activeMpBack()` is called again.
94
+ *
95
+ * Must be called inside `<script setup>`.
96
+ *
97
+ * @example
98
+ * import { inactiveMpBack } from 'mp-weixin-back-helper'
99
+ *
100
+ * // Disable when a modal is open
101
+ * function handleModalOpen() {
102
+ * inactiveMpBack()
103
+ * }
26
104
  */
27
- function inactiveMpBack()
105
+ function inactiveMpBack(): void
28
106
 
29
107
  export default onPageBack
30
-
31
108
  export { activeMpBack, inactiveMpBack }
32
109
  }
package/dist/index.cjs CHANGED
@@ -4,11 +4,12 @@ const path = require('path');
4
4
  const fs = require('fs');
5
5
  const JSON5 = require('json5');
6
6
  const kolorist = require('kolorist');
7
- const compilerSfc = require('@vue/compiler-sfc');
7
+ const module$1 = require('module');
8
8
  const generate = require('@babel/generator');
9
9
  const MagicString = require('magic-string');
10
10
  const astKit = require('ast-kit');
11
11
 
12
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
12
13
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
13
14
 
14
15
  const path__default = /*#__PURE__*/_interopDefaultCompat(path);
@@ -407,9 +408,36 @@ const vueWalker = {
407
408
  optionsWalk
408
409
  };
409
410
 
411
+ let compilerPromise = null;
412
+ async function resolveCompiler(root) {
413
+ if (compilerPromise) {
414
+ return compilerPromise;
415
+ }
416
+ compilerPromise = (async () => {
417
+ try {
418
+ const _require = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
419
+ const compilerPath = _require.resolve("@vue/compiler-sfc", { paths: [root] });
420
+ return _require(compilerPath);
421
+ } catch (firstError) {
422
+ try {
423
+ return await import('@vue/compiler-sfc');
424
+ } catch (secondError) {
425
+ throw new Error(
426
+ `[mp-weixin-back] Cannot resolve @vue/compiler-sfc.
427
+ This plugin requires @vue/compiler-sfc to be installed in your project.
428
+ Fix: pnpm add -D @vue/compiler-sfc
429
+ Docs: https://github.com/DBAAZzz/mp-weixin-back#%EF%B8%8F-vite-\u914D\u7F6E
430
+ `
431
+ );
432
+ }
433
+ }
434
+ })();
435
+ return compilerPromise;
436
+ }
410
437
  async function transformVueFile(code, id) {
411
438
  try {
412
- const sfc = compilerSfc.parse(code).descriptor;
439
+ const sfcCompiler = await resolveCompiler(this.config.root);
440
+ const sfc = sfcCompiler.parse(code).descriptor;
413
441
  const { template, script, scriptSetup } = sfc;
414
442
  if (!template?.content) {
415
443
  return code;
@@ -420,8 +448,11 @@ async function transformVueFile(code, id) {
420
448
  const walker = scriptSetup ? "compositionWalk" : "optionsWalk";
421
449
  return vueWalker[walker](this, code, sfc, id);
422
450
  } catch (error) {
423
- this.log.error("\u89E3\u6790vue\u6587\u4EF6\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u6B63\u786E");
424
- this.log.debugLog(String(error));
451
+ this.log.error(
452
+ `Failed to transform ${id}. Please check the file is a valid Vue SFC.
453
+ Docs: https://github.com/DBAAZzz/mp-weixin-back#-\u5FEB\u901F\u5F00\u59CB`
454
+ );
455
+ this.log.error(String(error));
425
456
  return code;
426
457
  }
427
458
  }
@@ -484,7 +515,11 @@ class pageContext {
484
515
  }
485
516
  }
486
517
  } catch (error) {
487
- this.log.error("\u8BFB\u53D6pages.json\u6587\u4EF6\u5931\u8D25");
518
+ this.log.error(
519
+ `Failed to read pages.json. Make sure src/pages.json exists and is valid JSON/JSON5.
520
+ Path checked: ${this.getPagesJsonPath()}
521
+ Docs: https://github.com/DBAAZzz/mp-weixin-back#%EF%B8%8F-vite-\u914D\u7F6E`
522
+ );
488
523
  this.log.debugLog(String(error));
489
524
  }
490
525
  }
package/dist/index.d.cts CHANGED
@@ -1,34 +1,67 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
- type UserOptions = Partial<Config>;
4
- type Config = {
3
+ /**
4
+ * Parameters passed to the onPageBack callback.
5
+ */
6
+ type BackParams = {
5
7
  /**
6
- * 初始化时是否监听手势返回,默认值为`true`
8
+ * The path of the current page that triggered the back event.
9
+ * @example 'pages/index/index'
7
10
  */
8
- initialValue: boolean;
11
+ page: string;
12
+ };
13
+ /**
14
+ * Per-page options for `onPageBack()`.
15
+ */
16
+ type OnPageBackOptions = {
9
17
  /**
10
- * 是否阻止默认的回退事件,默认为 false
18
+ * Whether to block the default back navigation for this page.
19
+ * When `true`, the user stays on the current page and only the callback fires.
20
+ * @default false
21
+ * @example
22
+ * onPageBack(() => showDialog(), { preventDefault: true })
11
23
  */
12
24
  preventDefault: boolean;
13
25
  /**
14
- * 阻止次数,默认是 `1`
26
+ * How many times to intercept the back event before allowing through.
27
+ * Set to a large number (e.g. `9999`) for persistent interception.
28
+ * @default 1
29
+ * @example
30
+ * // Block 3 times, then allow back
31
+ * onPageBack(() => {}, { frequency: 3 })
15
32
  */
16
33
  frequency: number;
17
34
  /**
18
- * 调试模式
35
+ * Whether to start listening immediately when the page mounts.
36
+ * Set to `false` to start disabled and enable manually via `activeMpBack()`.
37
+ * @default true
38
+ * @example
39
+ * // Start disabled, enable after user action
40
+ * onPageBack(() => {}, { initialValue: false })
41
+ * activeMpBack()
19
42
  */
20
- debug: boolean;
43
+ initialValue: boolean;
44
+ };
45
+ /**
46
+ * Global plugin options passed to `mpBackPlugin()` in `vite.config.ts`.
47
+ */
48
+ type Config = OnPageBackOptions & {
21
49
  /**
22
- * 页面回退时触发
50
+ * Enable debug logging in development mode.
51
+ * @default false
23
52
  */
24
- onPageBack?: (params: BackParams) => void;
25
- };
26
- type BackParams = {
53
+ debug: boolean;
27
54
  /**
28
- * 当前页面路径
55
+ * Global callback fired every time a back event is detected on any page.
56
+ * Page-level callbacks registered via `onPageBack()` run in addition to this.
57
+ * @example
58
+ * mpBackPlugin({
59
+ * onPageBack: ({ page }) => console.log('back on:', page)
60
+ * })
29
61
  */
30
- page: string;
62
+ onPageBack?: (params: BackParams) => void;
31
63
  };
64
+ type UserOptions = Partial<Config>;
32
65
 
33
66
  declare function MpBackPlugin(userOptions?: UserOptions): Plugin;
34
67
 
package/dist/index.d.mts CHANGED
@@ -1,34 +1,67 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
- type UserOptions = Partial<Config>;
4
- type Config = {
3
+ /**
4
+ * Parameters passed to the onPageBack callback.
5
+ */
6
+ type BackParams = {
5
7
  /**
6
- * 初始化时是否监听手势返回,默认值为`true`
8
+ * The path of the current page that triggered the back event.
9
+ * @example 'pages/index/index'
7
10
  */
8
- initialValue: boolean;
11
+ page: string;
12
+ };
13
+ /**
14
+ * Per-page options for `onPageBack()`.
15
+ */
16
+ type OnPageBackOptions = {
9
17
  /**
10
- * 是否阻止默认的回退事件,默认为 false
18
+ * Whether to block the default back navigation for this page.
19
+ * When `true`, the user stays on the current page and only the callback fires.
20
+ * @default false
21
+ * @example
22
+ * onPageBack(() => showDialog(), { preventDefault: true })
11
23
  */
12
24
  preventDefault: boolean;
13
25
  /**
14
- * 阻止次数,默认是 `1`
26
+ * How many times to intercept the back event before allowing through.
27
+ * Set to a large number (e.g. `9999`) for persistent interception.
28
+ * @default 1
29
+ * @example
30
+ * // Block 3 times, then allow back
31
+ * onPageBack(() => {}, { frequency: 3 })
15
32
  */
16
33
  frequency: number;
17
34
  /**
18
- * 调试模式
35
+ * Whether to start listening immediately when the page mounts.
36
+ * Set to `false` to start disabled and enable manually via `activeMpBack()`.
37
+ * @default true
38
+ * @example
39
+ * // Start disabled, enable after user action
40
+ * onPageBack(() => {}, { initialValue: false })
41
+ * activeMpBack()
19
42
  */
20
- debug: boolean;
43
+ initialValue: boolean;
44
+ };
45
+ /**
46
+ * Global plugin options passed to `mpBackPlugin()` in `vite.config.ts`.
47
+ */
48
+ type Config = OnPageBackOptions & {
21
49
  /**
22
- * 页面回退时触发
50
+ * Enable debug logging in development mode.
51
+ * @default false
23
52
  */
24
- onPageBack?: (params: BackParams) => void;
25
- };
26
- type BackParams = {
53
+ debug: boolean;
27
54
  /**
28
- * 当前页面路径
55
+ * Global callback fired every time a back event is detected on any page.
56
+ * Page-level callbacks registered via `onPageBack()` run in addition to this.
57
+ * @example
58
+ * mpBackPlugin({
59
+ * onPageBack: ({ page }) => console.log('back on:', page)
60
+ * })
29
61
  */
30
- page: string;
62
+ onPageBack?: (params: BackParams) => void;
31
63
  };
64
+ type UserOptions = Partial<Config>;
32
65
 
33
66
  declare function MpBackPlugin(userOptions?: UserOptions): Plugin;
34
67
 
package/dist/index.d.ts CHANGED
@@ -1,34 +1,67 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
- type UserOptions = Partial<Config>;
4
- type Config = {
3
+ /**
4
+ * Parameters passed to the onPageBack callback.
5
+ */
6
+ type BackParams = {
5
7
  /**
6
- * 初始化时是否监听手势返回,默认值为`true`
8
+ * The path of the current page that triggered the back event.
9
+ * @example 'pages/index/index'
7
10
  */
8
- initialValue: boolean;
11
+ page: string;
12
+ };
13
+ /**
14
+ * Per-page options for `onPageBack()`.
15
+ */
16
+ type OnPageBackOptions = {
9
17
  /**
10
- * 是否阻止默认的回退事件,默认为 false
18
+ * Whether to block the default back navigation for this page.
19
+ * When `true`, the user stays on the current page and only the callback fires.
20
+ * @default false
21
+ * @example
22
+ * onPageBack(() => showDialog(), { preventDefault: true })
11
23
  */
12
24
  preventDefault: boolean;
13
25
  /**
14
- * 阻止次数,默认是 `1`
26
+ * How many times to intercept the back event before allowing through.
27
+ * Set to a large number (e.g. `9999`) for persistent interception.
28
+ * @default 1
29
+ * @example
30
+ * // Block 3 times, then allow back
31
+ * onPageBack(() => {}, { frequency: 3 })
15
32
  */
16
33
  frequency: number;
17
34
  /**
18
- * 调试模式
35
+ * Whether to start listening immediately when the page mounts.
36
+ * Set to `false` to start disabled and enable manually via `activeMpBack()`.
37
+ * @default true
38
+ * @example
39
+ * // Start disabled, enable after user action
40
+ * onPageBack(() => {}, { initialValue: false })
41
+ * activeMpBack()
19
42
  */
20
- debug: boolean;
43
+ initialValue: boolean;
44
+ };
45
+ /**
46
+ * Global plugin options passed to `mpBackPlugin()` in `vite.config.ts`.
47
+ */
48
+ type Config = OnPageBackOptions & {
21
49
  /**
22
- * 页面回退时触发
50
+ * Enable debug logging in development mode.
51
+ * @default false
23
52
  */
24
- onPageBack?: (params: BackParams) => void;
25
- };
26
- type BackParams = {
53
+ debug: boolean;
27
54
  /**
28
- * 当前页面路径
55
+ * Global callback fired every time a back event is detected on any page.
56
+ * Page-level callbacks registered via `onPageBack()` run in addition to this.
57
+ * @example
58
+ * mpBackPlugin({
59
+ * onPageBack: ({ page }) => console.log('back on:', page)
60
+ * })
29
61
  */
30
- page: string;
62
+ onPageBack?: (params: BackParams) => void;
31
63
  };
64
+ type UserOptions = Partial<Config>;
32
65
 
33
66
  declare function MpBackPlugin(userOptions?: UserOptions): Plugin;
34
67
 
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import path from 'path';
2
2
  import fs from 'fs';
3
3
  import JSON5 from 'json5';
4
4
  import { white, red, green } from 'kolorist';
5
- import { parse } from '@vue/compiler-sfc';
5
+ import { createRequire } from 'module';
6
6
  import generate from '@babel/generator';
7
7
  import MagicString from 'magic-string';
8
8
  import { babelParse, walkAST } from 'ast-kit';
@@ -397,9 +397,36 @@ const vueWalker = {
397
397
  optionsWalk
398
398
  };
399
399
 
400
+ let compilerPromise = null;
401
+ async function resolveCompiler(root) {
402
+ if (compilerPromise) {
403
+ return compilerPromise;
404
+ }
405
+ compilerPromise = (async () => {
406
+ try {
407
+ const _require = createRequire(import.meta.url);
408
+ const compilerPath = _require.resolve("@vue/compiler-sfc", { paths: [root] });
409
+ return _require(compilerPath);
410
+ } catch (firstError) {
411
+ try {
412
+ return await import('@vue/compiler-sfc');
413
+ } catch (secondError) {
414
+ throw new Error(
415
+ `[mp-weixin-back] Cannot resolve @vue/compiler-sfc.
416
+ This plugin requires @vue/compiler-sfc to be installed in your project.
417
+ Fix: pnpm add -D @vue/compiler-sfc
418
+ Docs: https://github.com/DBAAZzz/mp-weixin-back#%EF%B8%8F-vite-\u914D\u7F6E
419
+ `
420
+ );
421
+ }
422
+ }
423
+ })();
424
+ return compilerPromise;
425
+ }
400
426
  async function transformVueFile(code, id) {
401
427
  try {
402
- const sfc = parse(code).descriptor;
428
+ const sfcCompiler = await resolveCompiler(this.config.root);
429
+ const sfc = sfcCompiler.parse(code).descriptor;
403
430
  const { template, script, scriptSetup } = sfc;
404
431
  if (!template?.content) {
405
432
  return code;
@@ -410,8 +437,11 @@ async function transformVueFile(code, id) {
410
437
  const walker = scriptSetup ? "compositionWalk" : "optionsWalk";
411
438
  return vueWalker[walker](this, code, sfc, id);
412
439
  } catch (error) {
413
- this.log.error("\u89E3\u6790vue\u6587\u4EF6\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u6B63\u786E");
414
- this.log.debugLog(String(error));
440
+ this.log.error(
441
+ `Failed to transform ${id}. Please check the file is a valid Vue SFC.
442
+ Docs: https://github.com/DBAAZzz/mp-weixin-back#-\u5FEB\u901F\u5F00\u59CB`
443
+ );
444
+ this.log.error(String(error));
415
445
  return code;
416
446
  }
417
447
  }
@@ -474,7 +504,11 @@ class pageContext {
474
504
  }
475
505
  }
476
506
  } catch (error) {
477
- this.log.error("\u8BFB\u53D6pages.json\u6587\u4EF6\u5931\u8D25");
507
+ this.log.error(
508
+ `Failed to read pages.json. Make sure src/pages.json exists and is valid JSON/JSON5.
509
+ Path checked: ${this.getPagesJsonPath()}
510
+ Docs: https://github.com/DBAAZzz/mp-weixin-back#%EF%B8%8F-vite-\u914D\u7F6E`
511
+ );
478
512
  this.log.debugLog(String(error));
479
513
  }
480
514
  }
package/llms.txt ADDED
@@ -0,0 +1,154 @@
1
+ # mp-weixin-back
2
+
3
+ > Vite plugin for intercepting back navigation (gesture back + navbar back button) in WeChat miniprogram (mp-weixin) built with uni-app + Vue 3.
4
+
5
+ ## When to use
6
+
7
+ Use this library when you need to:
8
+ - Intercept or customize the swipe-back gesture in a WeChat miniprogram page
9
+ - Intercept or customize the navbar back button behavior
10
+ - Show a confirmation dialog before the user leaves a page
11
+ - Prevent accidental back navigation (e.g., on a form page)
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install mp-weixin-back
17
+ # or
18
+ pnpm add mp-weixin-back
19
+ ```
20
+
21
+ ## Setup (vite.config.ts)
22
+
23
+ Register the Vite plugin once, globally:
24
+
25
+ ```ts
26
+ import { defineConfig } from 'vite'
27
+ import mpBackPlugin from 'mp-weixin-back'
28
+
29
+ export default defineConfig({
30
+ plugins: [
31
+ mpBackPlugin({
32
+ preventDefault: false, // true = block all back navigation by default
33
+ frequency: 1, // how many times to intercept before allowing through
34
+ debug: false, // enable debug logging in development
35
+ onPageBack: ({ page }) => {
36
+ console.log('back triggered on page:', page)
37
+ },
38
+ }),
39
+ ],
40
+ })
41
+ ```
42
+
43
+ ## Usage in Vue SFC (Composition API)
44
+
45
+ Import from the virtual module `mp-weixin-back-helper`:
46
+
47
+ ```ts
48
+ // Basic usage — listen to back event
49
+ import onPageBack from 'mp-weixin-back-helper'
50
+
51
+ onPageBack(() => {
52
+ console.log('back navigation detected')
53
+ })
54
+ ```
55
+
56
+ ```ts
57
+ // Advanced — prevent back and show dialog
58
+ import onPageBack from 'mp-weixin-back-helper'
59
+
60
+ onPageBack(
61
+ () => {
62
+ showConfirmDialog() // your logic here
63
+ },
64
+ {
65
+ preventDefault: true, // block default back behavior
66
+ frequency: 2, // intercept 2 times
67
+ initialValue: true, // start listening immediately (default)
68
+ }
69
+ )
70
+ ```
71
+
72
+ ```ts
73
+ // Manually toggle the listener
74
+ import onPageBack, { activeMpBack, inactiveMpBack } from 'mp-weixin-back-helper'
75
+
76
+ onPageBack(() => { /* ... */ }, { initialValue: false }) // start disabled
77
+
78
+ activeMpBack() // enable listener (call inside <script setup>)
79
+ inactiveMpBack() // disable listener (call inside <script setup>)
80
+ ```
81
+
82
+ ## TypeScript support
83
+
84
+ Add to `tsconfig.json`:
85
+
86
+ ```json
87
+ {
88
+ "compilerOptions": {
89
+ "types": ["mp-weixin-back/client"]
90
+ }
91
+ }
92
+ ```
93
+
94
+ Or in `env.d.ts`:
95
+
96
+ ```ts
97
+ /// <reference types="mp-weixin-back/client" />
98
+ ```
99
+
100
+ ## API Reference
101
+
102
+ ### Plugin options (`mpBackPlugin(options)`)
103
+
104
+ | Option | Type | Default | Description |
105
+ | --------------- | --------------------------------- | ------- | -------------------------------------------------------- |
106
+ | `preventDefault`| `boolean` | `false` | Block default back behavior globally |
107
+ | `frequency` | `number` | `1` | Number of times to intercept before allowing through |
108
+ | `debug` | `boolean` | `false` | Log debug info in development mode |
109
+ | `onPageBack` | `(params: { page: string }) => void` | — | Global callback fired on every back event |
110
+
111
+ ### `onPageBack(callback, options?)`
112
+
113
+ Listen to back events on the current page. Must be called in `<script setup>`.
114
+
115
+ | Param | Type | Required | Description |
116
+ | ---------- | --------------------- | -------- | ------------------------------- |
117
+ | `callback` | `() => void` | Yes | Function called when back fires |
118
+ | `options` | `OnPageBackOptions` | No | Per-page overrides |
119
+
120
+ #### `OnPageBackOptions`
121
+
122
+ | Option | Type | Default | Description |
123
+ | --------------- | --------- | ------- | ------------------------------------------------------------ |
124
+ | `preventDefault`| `boolean` | `false` | Block default back for this page |
125
+ | `frequency` | `number` | `1` | Intercept count for this page |
126
+ | `initialValue` | `boolean` | `true` | Start listening immediately; set `false` to enable manually |
127
+
128
+ ### `activeMpBack()`
129
+
130
+ Enable the back listener. Call inside `<script setup>` when `initialValue: false`.
131
+
132
+ ### `inactiveMpBack()`
133
+
134
+ Disable the back listener. Call inside `<script setup>`.
135
+
136
+ ## How it works
137
+
138
+ This is a **Vite transform plugin**. At build time it:
139
+ 1. Reads `src/pages.json` to identify miniprogram pages
140
+ 2. For each `.vue` page file, injects a `<page-container>` component into the template (the WeChat API used to intercept back gestures)
141
+ 3. Wires up the `onPageBack` composable to control `show` state of `<page-container>`
142
+
143
+ The virtual module `mp-weixin-back-helper` is resolved by the plugin and provides the runtime composable API.
144
+
145
+ ## Constraints
146
+
147
+ - Requires **uni-app** project with `src/pages.json`
148
+ - Requires **Vue 3** + `<script setup>` (Composition API)
149
+ - Works with **Vite 3–6**
150
+ - Only applies to page-level `.vue` files, not components
151
+
152
+ ## Repository
153
+
154
+ https://github.com/DBAAZzz/mp-weixin-back
package/package.json CHANGED
@@ -1,20 +1,27 @@
1
1
  {
2
2
  "name": "mp-weixin-back",
3
3
  "type": "module",
4
- "version": "0.0.14",
5
- "description": "监听微信小程序的手势返回和页面默认导航栏的返回",
4
+ "version": "0.0.16",
5
+ "description": "Vite plugin to intercept back navigation (gesture back & navbar back) in WeChat miniprogram (mp-weixin) built with uni-app + Vue 3. 监听微信小程序的手势返回和页面默认导航栏的返回",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",
9
- "scripts": {
10
- "build": "unbuild",
11
- "test": "vitest"
12
- },
13
9
  "keywords": [
10
+ "miniprogram",
11
+ "wechat",
12
+ "mp-weixin",
13
+ "back-navigation",
14
+ "gesture-back",
15
+ "navigate-back",
16
+ "page-back",
17
+ "navigation-intercept",
18
+ "vite-plugin",
19
+ "uniapp",
20
+ "uni-app",
21
+ "vue3",
14
22
  "微信小程序",
15
23
  "手势返回",
16
- "vite",
17
- "uniapp"
24
+ "页面返回拦截"
18
25
  ],
19
26
  "exports": {
20
27
  ".": {
@@ -47,13 +54,14 @@
47
54
  "magic-string": "^0.30.13"
48
55
  },
49
56
  "peerDependencies": {
50
- "@vue/compiler-sfc": "^2.7.0 || ^3.0.0",
51
- "vite": "^2.0.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0"
57
+ "@vue/compiler-sfc": "^3.2.0",
58
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
52
59
  },
53
60
  "lint-staged": {
54
61
  "*": "prettier --write"
55
62
  },
56
63
  "devDependencies": {
64
+ "@changesets/cli": "^2.29.8",
57
65
  "@types/babel__generator": "^7.6.8",
58
66
  "@types/node": "^22.9.3",
59
67
  "@vitejs/plugin-vue": "^5.2.0",
@@ -63,5 +71,12 @@
63
71
  "unbuild": "^2.0.0",
64
72
  "vitest": "^2.1.5",
65
73
  "vue": "^3.5.13"
74
+ },
75
+ "scripts": {
76
+ "build": "unbuild",
77
+ "test": "vitest",
78
+ "changeset": "changeset",
79
+ "version": "changeset version",
80
+ "release": "pnpm build && changeset publish"
66
81
  }
67
- }
82
+ }
package/readme.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # mp-weixin-back
2
2
 
3
+ Vite plugin to intercept back navigation (gesture back + navbar back button) in WeChat miniprogram (mp-weixin) built with uni-app + Vue 3.
4
+
5
+ ## TL;DR
6
+
7
+ ```ts
8
+ // vite.config.ts
9
+ import { defineConfig } from 'vite'
10
+ import mpBackPlugin from 'mp-weixin-back'
11
+
12
+ export default defineConfig({
13
+ plugins: [mpBackPlugin()],
14
+ })
15
+ ```
16
+
17
+ ```ts
18
+ // Any page .vue file — inside <script setup>
19
+ import onPageBack from 'mp-weixin-back-helper'
20
+
21
+ onPageBack(() => {
22
+ // handle back: show dialog, log analytics, etc.
23
+ })
24
+ ```
25
+
26
+ ---
27
+
3
28
  ## 功能概述
4
29
 
5
30
  `mp-weixin-back` 是一个专门用于监听微信小程序`手势返回`、`导航栏返回事件`、`navigateBack`的工具库,提供灵活的配置选项和简洁的 API。
@@ -9,7 +34,7 @@
9
34
  ```bash
10
35
  npm install mp-weixin-back
11
36
  # 或
12
- yarn add mp-weixin-back
37
+ pnpm add mp-weixin-back
13
38
  ```
14
39
 
15
40
  ## ⚙️ Vite 配置
@@ -27,9 +52,9 @@ export default defineConfig({
27
52
  preventDefault: false, // 是否阻止默认返回行为,设置成 true 则不会返回上一层
28
53
  frequency: 1, // 阻止次数,需要一直拦截则设置一个很大的值即可,如:9999
29
54
  debug: false, // 调试模式,默认为 false
30
- onPageBack: () => {
31
- console.log('返回事件触发')
32
- }, // 统一钩子,事件触发时执行
55
+ onPageBack: ({ page }) => {
56
+ console.log('返回事件触发,当前页面:', page)
57
+ }, // 全局钩子,任意页面触发时执行
33
58
  }),
34
59
  ],
35
60
  })
@@ -68,24 +93,45 @@ onPageBack(
68
93
  )
69
94
  ```
70
95
 
96
+ ### 显示确认弹窗(常见场景)
97
+
98
+ ```ts
99
+ <script setup>
100
+ import onPageBack from 'mp-weixin-back-helper'
101
+
102
+ onPageBack(
103
+ () => {
104
+ uni.showModal({
105
+ title: '提示',
106
+ content: '确定要离开当前页面吗?',
107
+ success: (res) => {
108
+ if (res.confirm) uni.navigateBack()
109
+ },
110
+ })
111
+ },
112
+ { preventDefault: true }
113
+ )
114
+ </script>
115
+ ```
116
+
71
117
  ## 📚 API 文档
72
118
 
73
- ### `onPageBack(callback, config?)`
119
+ ### `onPageBack(callback, options?)`
74
120
 
75
- 监听页面返回事件
121
+ 监听页面返回事件,必须在 `<script setup>` 顶层调用。
76
122
 
77
- | 参数 | 类型 | 必填 | 说明 |
78
- | -------- | ------------ | ---- | ------------------------ |
79
- | callback | `() => void` | 是 | 返回事件触发时的回调函数 |
80
- | options | Object | 否 | 监听器配置选项 |
123
+ | 参数 | 类型 | 必填 | 说明 |
124
+ | ---------- | ------------------- | ---- | ------------------------ |
125
+ | `callback` | `() => void` | 是 | 返回事件触发时的回调函数 |
126
+ | `options` | `OnPageBackOptions` | 否 | 监听器配置选项 |
81
127
 
82
- #### 配置选项
128
+ #### 配置选项 `OnPageBackOptions`
83
129
 
84
- | 参数 | 类型 | 默认值 | 说明 |
85
- | -------------- | ------- | ------ | ----------------------------------------------- |
86
- | preventDefault | boolean | false | 是否阻止默认返回行为(true 时页面不会实际返回) |
87
- | frequency | number | 1 | 阻止次数 |
88
- | initialValue | boolean | true | 是否立即启用监听(设为 false 时需手动激活) |
130
+ | 参数 | 类型 | 默认值 | 说明 |
131
+ | ---------------- | --------- | ------- | --------------------------------------------------------------- |
132
+ | `preventDefault` | `boolean` | `false` | 是否阻止默认返回行为(`true` 时页面不会实际返回) |
133
+ | `frequency` | `number` | `1` | 阻止次数 |
134
+ | `initialValue` | `boolean` | `true` | 是否立即启用监听(设为 `false` 时需手动调用 `activeMpBack()`) |
89
135
 
90
136
  ### 辅助方法
91
137
 
@@ -103,20 +149,27 @@ onPageBack(
103
149
  <template>
104
150
  <div>
105
151
  <!-- 页面代码 -->
106
- <button @click="toggleListener(true)">开启</button>
107
- <button @click="toggleListener(false)">禁用</button>
152
+ <button @click="activeMpBack()">开启</button>
153
+ <button @click="inactiveMpBack()">禁用</button>
108
154
  </div>
109
155
  </template>
110
156
 
111
157
  <script setup>
112
158
  import onPageBack, { activeMpBack, inactiveMpBack } from 'mp-weixin-back-helper'
113
159
 
114
- const toggleListener = (enable) => {
115
- enable ? activeMpBack() : inactiveMpBack()
116
- }
160
+ onPageBack(() => { /* 处理返回 */ }, { initialValue: false })
117
161
  </script>
118
162
  ```
119
163
 
164
+ ### 插件全局配置 `mpBackPlugin(options)`
165
+
166
+ | 参数 | 类型 | 默认值 | 说明 |
167
+ | ---------------- | ------------------------------------------- | ------- | -------------------------- |
168
+ | `preventDefault` | `boolean` | `false` | 全局阻止默认返回行为 |
169
+ | `frequency` | `number` | `1` | 全局阻止次数 |
170
+ | `debug` | `boolean` | `false` | 开发模式下开启调试日志 |
171
+ | `onPageBack` | `(params: { page: string }) => void` | — | 全局回调,任意页面触发执行 |
172
+
120
173
  ## 🎯 选项式 API 支持(未完善)
121
174
 
122
175
  组件内直接声明
@@ -171,3 +224,10 @@ onPageBack(
171
224
  ### Q2: 全局配置与页面配置的优先级?
172
225
 
173
226
  页面级配置会覆盖全局配置,建议将通用配置放在全局,特殊需求在页面单独设置。
227
+
228
+ ### Q3: 不生效怎么排查?
229
+
230
+ 1. 确认 `src/pages.json` 存在且格式正确
231
+ 2. 确认是页面级 `.vue` 文件(非组件)
232
+ 3. 开启 `debug: true` 查看插件日志
233
+ 4. 确认 `@vue/compiler-sfc` 已安装:`pnpm add -D @vue/compiler-sfc`
package/src/context.ts CHANGED
@@ -57,7 +57,11 @@ export class pageContext {
57
57
  }
58
58
  }
59
59
  } catch (error: unknown) {
60
- this.log.error('读取pages.json文件失败')
60
+ this.log.error(
61
+ `Failed to read pages.json. Make sure src/pages.json exists and is valid JSON/JSON5.\n` +
62
+ ` Path checked: ${this.getPagesJsonPath()}\n` +
63
+ ` Docs: https://github.com/DBAAZzz/mp-weixin-back#%EF%B8%8F-vite-配置`
64
+ )
61
65
  this.log.debugLog(String(error))
62
66
  }
63
67
  }
package/types/index.ts CHANGED
@@ -1,35 +1,72 @@
1
- export type UserOptions = Partial<Config>
2
-
3
- export type Config = {
1
+ /**
2
+ * Parameters passed to the onPageBack callback.
3
+ */
4
+ export type BackParams = {
4
5
  /**
5
- * 初始化时是否监听手势返回,默认值为`true`
6
+ * The path of the current page that triggered the back event.
7
+ * @example 'pages/index/index'
6
8
  */
7
- initialValue: boolean
9
+ page: string
10
+ }
11
+
12
+ /**
13
+ * Per-page options for `onPageBack()`.
14
+ */
15
+ export type OnPageBackOptions = {
8
16
  /**
9
- * 是否阻止默认的回退事件,默认为 false
17
+ * Whether to block the default back navigation for this page.
18
+ * When `true`, the user stays on the current page and only the callback fires.
19
+ * @default false
20
+ * @example
21
+ * onPageBack(() => showDialog(), { preventDefault: true })
10
22
  */
11
23
  preventDefault: boolean
24
+
12
25
  /**
13
- * 阻止次数,默认是 `1`
26
+ * How many times to intercept the back event before allowing through.
27
+ * Set to a large number (e.g. `9999`) for persistent interception.
28
+ * @default 1
29
+ * @example
30
+ * // Block 3 times, then allow back
31
+ * onPageBack(() => {}, { frequency: 3 })
14
32
  */
15
33
  frequency: number
34
+
16
35
  /**
17
- * 调试模式
36
+ * Whether to start listening immediately when the page mounts.
37
+ * Set to `false` to start disabled and enable manually via `activeMpBack()`.
38
+ * @default true
39
+ * @example
40
+ * // Start disabled, enable after user action
41
+ * onPageBack(() => {}, { initialValue: false })
42
+ * activeMpBack()
18
43
  */
19
- debug: boolean
44
+ initialValue: boolean
45
+ }
46
+
47
+ /**
48
+ * Global plugin options passed to `mpBackPlugin()` in `vite.config.ts`.
49
+ */
50
+ export type Config = OnPageBackOptions & {
20
51
  /**
21
- * 页面回退时触发
52
+ * Enable debug logging in development mode.
53
+ * @default false
22
54
  */
23
- onPageBack?: (params: BackParams) => void
24
- }
55
+ debug: boolean
25
56
 
26
- export type BackParams = {
27
57
  /**
28
- * 当前页面路径
58
+ * Global callback fired every time a back event is detected on any page.
59
+ * Page-level callbacks registered via `onPageBack()` run in addition to this.
60
+ * @example
61
+ * mpBackPlugin({
62
+ * onPageBack: ({ page }) => console.log('back on:', page)
63
+ * })
29
64
  */
30
- page: string
65
+ onPageBack?: (params: BackParams) => void
31
66
  }
32
67
 
68
+ export type UserOptions = Partial<Config>
69
+
33
70
  export type ContextConfig = Config & {
34
71
  mode: string
35
72
  root: string
package/utils/index.ts CHANGED
@@ -1,10 +1,44 @@
1
- import { parse } from '@vue/compiler-sfc'
1
+ import { createRequire } from 'module'
2
2
  import { pageContext } from '../src/context'
3
3
  import { vueWalker } from './walker'
4
4
 
5
+ let compilerPromise: Promise<typeof import('@vue/compiler-sfc')> | null = null
6
+
7
+ async function resolveCompiler(root: string): Promise<typeof import('@vue/compiler-sfc')> {
8
+ // 避免重复解析(防止并发调用时的竞态条件)
9
+ if (compilerPromise) {
10
+ return compilerPromise
11
+ }
12
+
13
+ compilerPromise = (async () => {
14
+ // 尝试加载用户项目中的 @vue/compiler-sfc
15
+ try {
16
+ const _require = createRequire(import.meta.url)
17
+ // 尝试从用户根目录解析
18
+ const compilerPath = _require.resolve('@vue/compiler-sfc', { paths: [root] })
19
+ return _require(compilerPath) as typeof import('@vue/compiler-sfc')
20
+ } catch (firstError) {
21
+ try {
22
+ // 降级尝试直接 import
23
+ return await import('@vue/compiler-sfc')
24
+ } catch (secondError) {
25
+ throw new Error(
26
+ `[mp-weixin-back] Cannot resolve @vue/compiler-sfc.\n` +
27
+ `This plugin requires @vue/compiler-sfc to be installed in your project.\n` +
28
+ `Fix: pnpm add -D @vue/compiler-sfc\n` +
29
+ `Docs: https://github.com/DBAAZzz/mp-weixin-back#%EF%B8%8F-vite-配置\n`
30
+ )
31
+ }
32
+ }
33
+ })()
34
+
35
+ return compilerPromise
36
+ }
37
+
5
38
  export async function transformVueFile(this: pageContext, code: string, id: string) {
6
39
  try {
7
- const sfc = parse(code).descriptor
40
+ const sfcCompiler = await resolveCompiler(this.config.root)
41
+ const sfc = sfcCompiler.parse(code).descriptor
8
42
  const { template, script, scriptSetup } = sfc
9
43
  if (!template?.content) {
10
44
  return code
@@ -18,8 +52,11 @@ export async function transformVueFile(this: pageContext, code: string, id: stri
18
52
  const walker = scriptSetup ? 'compositionWalk' : 'optionsWalk'
19
53
  return vueWalker[walker](this, code, sfc, id)
20
54
  } catch (error) {
21
- this.log.error('解析vue文件失败,请检查文件是否正确')
22
- this.log.debugLog(String(error))
55
+ this.log.error(
56
+ `Failed to transform ${id}. Please check the file is a valid Vue SFC.\n` +
57
+ ` Docs: https://github.com/DBAAZzz/mp-weixin-back#-快速开始`
58
+ )
59
+ this.log.error(String(error))
23
60
  return code
24
61
  }
25
62
  }