@simposons/vite-plugin-mpa 1.0.0

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/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # vite-plugin-mpa
2
+
3
+ 一个 Vite 插件,用于多页面应用(MPA)构建,支持共享入口和自动扫描。
4
+
5
+ ## 特性
6
+
7
+ - 🚀 **自动扫描** – 自动扫描 `src/pages` 目录下的 `.vue` 文件,生成对应的 HTML 入口。
8
+ - 🔗 **共享入口** – 所有页面共享同一个 JavaScript 入口文件(如 `src/main.ts`)。
9
+ - 📄 **基于模板生成 HTML** – 使用一个 HTML 模板(如 `template.html`)自动生成所有页面,支持动态标题。
10
+ - 🎯 **TypeScript 支持** – 包含完整的类型定义。
11
+ - ⚡ **灵活切换** – 通过 `enable` 选项或 `--mpa` 命令行标志控制是否启用 MPA。
12
+ - 🧩 **高度可定制** – 支持手动指定页面列表、自定义输出目录、自定义标题等。
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ npm install -D vite-plugin-mpa
18
+ ```
19
+
20
+ ## 使用
21
+
22
+ ### 基本配置
23
+
24
+ 在 `vite.config.ts` 中:
25
+
26
+ ```typescript
27
+ import { defineConfig } from 'vite'
28
+ import vue from '@vitejs/plugin-vue'
29
+ import mpa from 'vite-plugin-mpa'
30
+
31
+ export default defineConfig({
32
+ plugins: [
33
+ vue(),
34
+ mpa({
35
+ enable: true, // 启用 MPA 模式
36
+ pagesDir: 'src/pages', // 页面组件目录
37
+ template: 'index.html', // HTML 模板文件
38
+ entry: 'src/main.ts', // 共享入口文件
39
+ verbose: true,
40
+ }),
41
+ ],
42
+ })
43
+ ```
44
+
45
+ ### 目录结构
46
+
47
+ ```
48
+ project/
49
+ ├── index.html # 模板 HTML(包含 <!-- PAGE_TITLE -->)
50
+ ├── src/
51
+ │ ├── main.ts # 共享入口
52
+ │ ├── pages/
53
+ │ │ ├── index.vue # → 生成 index.html
54
+ │ │ ├── about.vue # → 生成 about.html
55
+ │ │ └── user/
56
+ │ │ ├── profile.vue # → 生成 user-profile.html
57
+ │ │ └── settings.vue # → 生成 user-settings.html
58
+ │ └── ...
59
+ └── vite.config.ts
60
+ ```
61
+
62
+ ### 模板 HTML(`index.html`)
63
+
64
+ 在模板中放置 `<!-- PAGE_TITLE -->` 占位符,插件会自动替换为页面标题。如果模板中没有该占位符,插件会自动将 `<title>` 标签内容替换为占位符。
65
+
66
+ ```html
67
+ <!DOCTYPE html>
68
+ <html lang="zh">
69
+ <head>
70
+ <meta charset="UTF-8" />
71
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
72
+ <title><!-- PAGE_TITLE --></title>
73
+ </head>
74
+ <body>
75
+ <div id="app"></div>
76
+ <script type="module" src="<!-- ENTRY_PATH -->"></script>
77
+ </body>
78
+ </html>
79
+ ```
80
+
81
+ ### 构建与开发
82
+
83
+ 在 `package.json` 中添加脚本:
84
+
85
+ ```json
86
+ {
87
+ "scripts": {
88
+ "dev": "vite",
89
+ "build": "vite build"
90
+ }
91
+ }
92
+ ```
93
+
94
+ 如果你希望使用 `--mpa` 标志(而不是在配置中设置 `enable: true`),插件同样支持:
95
+
96
+ ```json
97
+ {
98
+ "scripts": {
99
+ "dev:mpa": "vite -- --mpa",
100
+ "build:mpa": "vite build -- --mpa"
101
+ }
102
+ }
103
+ ```
104
+
105
+ ### 使用现有 HTML 文件
106
+
107
+ 如果你已经为每个页面准备了独立的 HTML 文件,不希望插件自动生成,可以设置 `useExistingHtml: true`。插件会扫描 `pagesDir` 目录下的 `.html` 文件,并直接将其作为入口。
108
+
109
+ ```typescript
110
+ mpa({
111
+ enable: true,
112
+ pagesDir: 'src/pages',
113
+ useExistingHtml: true, // 扫描 .html 文件,不生成
114
+ })
115
+ ```
116
+
117
+ ## 配置选项
118
+
119
+ | 选项 | 类型 | 默认值 | 描述 |
120
+ |------|------|--------|------|
121
+ | `enable` | `boolean` | `undefined` | 强制启用/禁用 MPA。如果未设置,则回退到检测 `--mpa` 标志。 |
122
+ | `pagesDir` | `string` | `'src/pages'` | 扫描页面组件(`.vue`)或 HTML 文件的目录。 |
123
+ | `template` | `string` | `'template.html'` | HTML 模板文件(当 `useExistingHtml` 为 `false` 时使用)。 |
124
+ | `entry` | `string` | `'src/main.ts'` | 所有页面共享的 JavaScript 入口文件。 |
125
+ | `outDir` | `string` | `'node_modules/.vite-mpa'` | 生成的 HTML 文件临时存放目录。 |
126
+ | `useExistingHtml` | `boolean` | `false` | 如果为 `true`,扫描 `pagesDir` 下的 `.html` 文件直接作为入口(不生成)。 |
127
+ | `modeFlag` | `string` | `'--mpa'` | 启用 MPA 的命令行标志(当 `enable` 未设置时生效)。 |
128
+ | `verbose` | `boolean` | `false` | 是否打印详细日志。 |
129
+ | `defaultTitle` | `string` | `'App'` | 默认页面标题(模板生成时,页面未指定标题时使用)。 |
130
+ | `pages` | `Page[]` | `undefined` | 手动指定页面列表(优先级高于自动扫描)。每个 `Page` 可包含 `name`、`title`、`template`、`entry`。 |
131
+
132
+ ## 工作原理
133
+
134
+ 1. 启用插件后,它会扫描 `pagesDir` 目录下的 `.vue` 文件(或 `useExistingHtml: true` 时的 `.html` 文件)。
135
+ 2. 从文件路径中提取页面名称(例如 `about/index.vue` → `about`)。
136
+ 3. 如果使用模板生成模式,则基于模板为每个页面生成 HTML 文件,并将 `<!-- PAGE_TITLE -->` 替换为页面标题。
137
+ 4. 将生成的 HTML 文件路径设置为 `build.rollupOptions.input`,让 Vite 将其视为多入口。
138
+ 5. 所有页面共享同一个 JavaScript 入口(`entry` 选项),保证全局初始化逻辑一致。
139
+
140
+ ## 许可证
141
+
142
+ MIT
@@ -0,0 +1,67 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ /**
4
+ * 页面配置
5
+ */
6
+ interface Page {
7
+ name: string;
8
+ title?: string;
9
+ template?: string;
10
+ entry?: string;
11
+ }
12
+ /**
13
+ * 插件配置选项
14
+ */
15
+ interface MpaOptions {
16
+ /**
17
+ * 强制启用或禁用 MPA 模式。
18
+ * 若未设置,则检测命令行参数 `--mpa`。
19
+ * @default undefined
20
+ */
21
+ enable?: boolean;
22
+ /**
23
+ * 页面组件目录(相对于项目根目录)
24
+ * @default 'src/pages'
25
+ */
26
+ pagesDir?: string;
27
+ /**
28
+ * 全局 HTML 模板路径(相对于项目根目录)
29
+ * @default 'index.html'
30
+ */
31
+ template?: string;
32
+ /**
33
+ * 全局入口文件路径(所有页面共享)
34
+ * @default 'src/main.ts'
35
+ */
36
+ entry?: string;
37
+ /**
38
+ * 生成的 HTML 临时存放目录
39
+ * @default 'node_modules/.vite-mpa'
40
+ */
41
+ outDir?: string;
42
+ /**
43
+ * 启用 MPA 的命令行标志(仅当 enable 未设置时生效)
44
+ * @default '--mpa'
45
+ */
46
+ modeFlag?: string;
47
+ /**
48
+ * 是否打印详细日志
49
+ * @default false
50
+ */
51
+ verbose?: boolean;
52
+ /**
53
+ * 默认标题(当页面未指定 title 时使用)
54
+ * @default 'App'
55
+ */
56
+ defaultTitle?: string;
57
+ /**
58
+ * 手动指定页面列表(优先级高于自动扫描)
59
+ */
60
+ pages?: Page[];
61
+ }
62
+ /**
63
+ * Vite MPA 插件
64
+ */
65
+ declare function vitePluginMpa(options?: MpaOptions): Plugin;
66
+
67
+ export { type MpaOptions, type Page, vitePluginMpa as default };
@@ -0,0 +1,67 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ /**
4
+ * 页面配置
5
+ */
6
+ interface Page {
7
+ name: string;
8
+ title?: string;
9
+ template?: string;
10
+ entry?: string;
11
+ }
12
+ /**
13
+ * 插件配置选项
14
+ */
15
+ interface MpaOptions {
16
+ /**
17
+ * 强制启用或禁用 MPA 模式。
18
+ * 若未设置,则检测命令行参数 `--mpa`。
19
+ * @default undefined
20
+ */
21
+ enable?: boolean;
22
+ /**
23
+ * 页面组件目录(相对于项目根目录)
24
+ * @default 'src/pages'
25
+ */
26
+ pagesDir?: string;
27
+ /**
28
+ * 全局 HTML 模板路径(相对于项目根目录)
29
+ * @default 'index.html'
30
+ */
31
+ template?: string;
32
+ /**
33
+ * 全局入口文件路径(所有页面共享)
34
+ * @default 'src/main.ts'
35
+ */
36
+ entry?: string;
37
+ /**
38
+ * 生成的 HTML 临时存放目录
39
+ * @default 'node_modules/.vite-mpa'
40
+ */
41
+ outDir?: string;
42
+ /**
43
+ * 启用 MPA 的命令行标志(仅当 enable 未设置时生效)
44
+ * @default '--mpa'
45
+ */
46
+ modeFlag?: string;
47
+ /**
48
+ * 是否打印详细日志
49
+ * @default false
50
+ */
51
+ verbose?: boolean;
52
+ /**
53
+ * 默认标题(当页面未指定 title 时使用)
54
+ * @default 'App'
55
+ */
56
+ defaultTitle?: string;
57
+ /**
58
+ * 手动指定页面列表(优先级高于自动扫描)
59
+ */
60
+ pages?: Page[];
61
+ }
62
+ /**
63
+ * Vite MPA 插件
64
+ */
65
+ declare function vitePluginMpa(options?: MpaOptions): Plugin;
66
+
67
+ export { type MpaOptions, type Page, vitePluginMpa as default };
package/dist/index.js ADDED
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ default: () => vitePluginMpa
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+ var import_fs = __toESM(require("fs"));
37
+ var import_path = __toESM(require("path"));
38
+ function scanVueFiles(dir, base = "") {
39
+ const entries = import_fs.default.readdirSync(dir, { withFileTypes: true });
40
+ const results = [];
41
+ for (const entry of entries) {
42
+ const fullPath = import_path.default.join(dir, entry.name);
43
+ if (entry.isDirectory()) {
44
+ results.push(...scanVueFiles(fullPath, import_path.default.join(base, entry.name)));
45
+ } else if (entry.isFile() && entry.name.endsWith(".vue") && !entry.name.includes("[")) {
46
+ let relative = import_path.default.join(base, entry.name).replace(/\.vue$/, "");
47
+ let name = relative.replace(/[\\/]/g, "-");
48
+ if (relative.endsWith("index")) {
49
+ const parts = relative.split(/[\\/]/);
50
+ if (parts.length > 1) {
51
+ name = parts.slice(0, -1).join("-");
52
+ } else {
53
+ name = "index";
54
+ }
55
+ }
56
+ results.push({ name, filePath: fullPath });
57
+ }
58
+ }
59
+ return results;
60
+ }
61
+ function prepareTemplate(templatePath) {
62
+ if (!import_fs.default.existsSync(templatePath)) {
63
+ throw new Error(`[MPA] \u6A21\u677F\u6587\u4EF6\u4E0D\u5B58\u5728: ${templatePath}`);
64
+ }
65
+ let content = import_fs.default.readFileSync(templatePath, "utf-8");
66
+ if (!content.includes("<!-- PAGE_TITLE -->")) {
67
+ content = content.replace(/<title>(.*?)<\/title>/, "<title><!-- PAGE_TITLE --></title>");
68
+ }
69
+ return content;
70
+ }
71
+ function generateHtmlFiles(pages, templatePath, outDir, defaultTitle, entry) {
72
+ const template = prepareTemplate(templatePath);
73
+ const entryMap = {};
74
+ if (!import_fs.default.existsSync(outDir)) {
75
+ import_fs.default.mkdirSync(outDir, { recursive: true });
76
+ }
77
+ const entryPath = entry.startsWith("/") ? entry : "/" + entry;
78
+ for (const page of pages) {
79
+ const htmlName = `${page.name}.html`;
80
+ const outputPath = import_path.default.join(outDir, htmlName);
81
+ const title = page.title || page.name.charAt(0).toUpperCase() + page.name.slice(1) || defaultTitle;
82
+ let content = template.replace("<!-- PAGE_TITLE -->", title);
83
+ if (content.includes("<!-- ENTRY_PATH -->")) {
84
+ content = content.replace(/<!-- ENTRY_PATH -->/g, entryPath);
85
+ } else {
86
+ content = content.replace(/src="\/src\/main\.ts"/g, `src="${entryPath}"`);
87
+ content = content.replace(/src='\/src\/main\.ts'/g, `src='${entryPath}'`);
88
+ }
89
+ import_fs.default.writeFileSync(outputPath, content, "utf-8");
90
+ entryMap[page.name] = outputPath;
91
+ }
92
+ return entryMap;
93
+ }
94
+ function vitePluginMpa(options = {}) {
95
+ const {
96
+ enable: enableOption,
97
+ pagesDir = "src/pages",
98
+ template = "index.html",
99
+ entry = "src/main.ts",
100
+ outDir = "node_modules/.vite-mpa",
101
+ modeFlag = "--mpa",
102
+ verbose = false,
103
+ defaultTitle = "App",
104
+ pages: manualPages
105
+ } = options;
106
+ let isMPA = false;
107
+ let entryMap = {};
108
+ let root = "";
109
+ return {
110
+ name: "vite-plugin-mpa",
111
+ config(conf) {
112
+ if (enableOption !== void 0) {
113
+ isMPA = enableOption;
114
+ } else {
115
+ isMPA = process.argv.includes(modeFlag);
116
+ }
117
+ if (verbose) {
118
+ console.log(`[MPA] \u6A21\u5F0F\u72B6\u6001: ${isMPA ? "\u2705 \u542F\u7528" : "\u23ED\uFE0F \u7981\u7528"}`);
119
+ }
120
+ if (!isMPA) return;
121
+ root = conf.root || process.cwd();
122
+ let resolvedPages = [];
123
+ if (manualPages && manualPages.length > 0) {
124
+ resolvedPages = manualPages;
125
+ if (verbose) {
126
+ console.log(`[MPA] \u4F7F\u7528\u624B\u52A8\u914D\u7F6E\u7684 ${resolvedPages.length} \u4E2A\u9875\u9762`);
127
+ }
128
+ } else {
129
+ const pagesDirAbs = import_path.default.resolve(root, pagesDir);
130
+ if (!import_fs.default.existsSync(pagesDirAbs)) {
131
+ console.warn(`[MPA] \u26A0\uFE0F \u9875\u9762\u76EE\u5F55\u4E0D\u5B58\u5728: ${pagesDirAbs}\uFF0C\u8DF3\u8FC7 MPA`);
132
+ isMPA = false;
133
+ return;
134
+ }
135
+ const scanned = scanVueFiles(pagesDirAbs);
136
+ if (scanned.length === 0) {
137
+ console.warn(`[MPA] \u26A0\uFE0F \u672A\u627E\u5230\u4EFB\u4F55 .vue \u9875\u9762\u6587\u4EF6\uFF0C\u8DF3\u8FC7 MPA`);
138
+ isMPA = false;
139
+ return;
140
+ }
141
+ resolvedPages = scanned.map((p) => ({
142
+ name: p.name,
143
+ title: p.name.charAt(0).toUpperCase() + p.name.slice(1)
144
+ }));
145
+ if (verbose) {
146
+ console.log(`[MPA] \u81EA\u52A8\u626B\u63CF\u5230 ${resolvedPages.length} \u4E2A\u9875\u9762:`, resolvedPages.map((p) => p.name).join(", "));
147
+ }
148
+ }
149
+ const templateAbs = import_path.default.resolve(root, template);
150
+ const outDirAbs = import_path.default.resolve(root, outDir);
151
+ entryMap = generateHtmlFiles(resolvedPages, templateAbs, outDirAbs, defaultTitle, entry);
152
+ if (verbose) {
153
+ console.log(`[MPA] \u2705 \u5DF2\u751F\u6210 ${Object.keys(entryMap).length} \u4E2A HTML \u5165\u53E3\u6587\u4EF6`);
154
+ }
155
+ const build = conf.build || {};
156
+ const rollupOptions = build.rollupOptions || {};
157
+ rollupOptions.input = entryMap;
158
+ conf.build = { ...build, rollupOptions };
159
+ if (verbose) {
160
+ console.log(`[MPA] \u2705 \u5DF2\u8BBE\u7F6E\u591A\u5165\u53E3:`, Object.keys(entryMap).join(", "));
161
+ }
162
+ },
163
+ configResolved(config) {
164
+ if (!isMPA || Object.keys(entryMap).length === 0) return;
165
+ if (config.command === "serve") {
166
+ const port = config.server?.port || 5173;
167
+ console.log(`[MPA] \u{1F680} \u5F00\u53D1\u670D\u52A1\u5668\u5DF2\u542F\u52A8\uFF0C\u591A\u9875\u9762\u5165\u53E3\u5728 ${outDir} \u76EE\u5F55`);
168
+ console.log(`[MPA] \u{1F4C4} \u8BBF\u95EE http://localhost:${port}/${outDir}/xxx.html`);
169
+ }
170
+ },
171
+ closeBundle() {
172
+ if (!isMPA || Object.keys(entryMap).length === 0) return;
173
+ const distDir = import_path.default.resolve(root, "dist");
174
+ const htmlDir = import_path.default.join(distDir, "node_modules", ".vite-mpa");
175
+ if (!import_fs.default.existsSync(htmlDir)) return;
176
+ try {
177
+ let removeEmptyDir2 = function(dir) {
178
+ if (!import_fs.default.existsSync(dir)) return;
179
+ const files2 = import_fs.default.readdirSync(dir);
180
+ if (files2.length === 0) {
181
+ import_fs.default.rmdirSync(dir);
182
+ const parentDir = import_path.default.dirname(dir);
183
+ if (parentDir !== distDir) {
184
+ removeEmptyDir2(parentDir);
185
+ }
186
+ }
187
+ };
188
+ var removeEmptyDir = removeEmptyDir2;
189
+ const files = import_fs.default.readdirSync(htmlDir).filter((f) => f.endsWith(".html"));
190
+ for (const file of files) {
191
+ const src = import_path.default.join(htmlDir, file);
192
+ const dest = import_path.default.join(distDir, file);
193
+ if (import_fs.default.existsSync(dest)) {
194
+ import_fs.default.unlinkSync(dest);
195
+ }
196
+ import_fs.default.copyFileSync(src, dest);
197
+ import_fs.default.unlinkSync(src);
198
+ if (verbose) {
199
+ console.log(`[MPA] \u{1F4C4} \u79FB\u52A8 ${file} \u5230 dist \u6839\u76EE\u5F55`);
200
+ }
201
+ }
202
+ removeEmptyDir2(htmlDir);
203
+ if (verbose) {
204
+ console.log(`[MPA] \u{1F5D1}\uFE0F \u5DF2\u6E05\u7406\u4E34\u65F6\u76EE\u5F55 ${htmlDir}`);
205
+ }
206
+ } catch (error) {
207
+ console.warn(`[MPA] \u26A0\uFE0F \u79FB\u52A8 HTML \u6587\u4EF6\u65F6\u53D1\u751F\u9519\u8BEF:`, error.message);
208
+ }
209
+ }
210
+ };
211
+ }
212
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Plugin } from 'vite'\r\nimport fs from 'fs'\r\nimport path from 'path'\r\n\r\n/**\r\n * 页面配置\r\n */\r\nexport interface Page {\r\n name: string\r\n title?: string\r\n template?: string\r\n entry?: string\r\n}\r\n\r\n/**\r\n * 插件配置选项\r\n */\r\nexport interface MpaOptions {\r\n /**\r\n * 强制启用或禁用 MPA 模式。\r\n * 若未设置,则检测命令行参数 `--mpa`。\r\n * @default undefined\r\n */\r\n enable?: boolean\r\n /**\r\n * 页面组件目录(相对于项目根目录)\r\n * @default 'src/pages'\r\n */\r\n pagesDir?: string\r\n /**\r\n * 全局 HTML 模板路径(相对于项目根目录)\r\n * @default 'index.html'\r\n */\r\n template?: string\r\n /**\r\n * 全局入口文件路径(所有页面共享)\r\n * @default 'src/main.ts'\r\n */\r\n entry?: string\r\n /**\r\n * 生成的 HTML 临时存放目录\r\n * @default 'node_modules/.vite-mpa'\r\n */\r\n outDir?: string\r\n /**\r\n * 启用 MPA 的命令行标志(仅当 enable 未设置时生效)\r\n * @default '--mpa'\r\n */\r\n modeFlag?: string\r\n /**\r\n * 是否打印详细日志\r\n * @default false\r\n */\r\n verbose?: boolean\r\n /**\r\n * 默认标题(当页面未指定 title 时使用)\r\n * @default 'App'\r\n */\r\n defaultTitle?: string\r\n /**\r\n * 手动指定页面列表(优先级高于自动扫描)\r\n */\r\n pages?: Page[]\r\n}\r\n\r\n/**\r\n * 扫描到的页面信息(内部使用)\r\n */\r\ninterface ScannedPage {\r\n name: string\r\n filePath: string\r\n}\r\n\r\n/**\r\n * 递归扫描 .vue 文件(排除动态路由)\r\n */\r\nfunction scanVueFiles(dir: string, base = ''): ScannedPage[] {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true })\r\n const results: ScannedPage[] = []\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name)\r\n if (entry.isDirectory()) {\r\n results.push(...scanVueFiles(fullPath, path.join(base, entry.name)))\r\n } else if (entry.isFile() && entry.name.endsWith('.vue') && !entry.name.includes('[')) {\r\n let relative = path.join(base, entry.name).replace(/\\.vue$/, '')\r\n let name = relative.replace(/[\\\\/]/g, '-')\r\n if (relative.endsWith('index')) {\r\n const parts = relative.split(/[\\\\/]/)\r\n if (parts.length > 1) {\r\n name = parts.slice(0, -1).join('-')\r\n } else {\r\n name = 'index'\r\n }\r\n }\r\n results.push({ name, filePath: fullPath })\r\n }\r\n }\r\n return results\r\n}\r\n\r\n/**\r\n * 准备模板内容(确保包含标题占位符)\r\n */\r\nfunction prepareTemplate(templatePath: string): string {\r\n if (!fs.existsSync(templatePath)) {\r\n throw new Error(`[MPA] 模板文件不存在: ${templatePath}`)\r\n }\r\n let content = fs.readFileSync(templatePath, 'utf-8')\r\n if (!content.includes('<!-- PAGE_TITLE -->')) {\r\n content = content.replace(/<title>(.*?)<\\/title>/, '<title><!-- PAGE_TITLE --></title>')\r\n }\r\n return content\r\n}\r\n\r\n/**\r\n * 生成 HTML 文件\r\n */\r\nfunction generateHtmlFiles(\r\n pages: Page[],\r\n templatePath: string,\r\n outDir: string,\r\n defaultTitle: string,\r\n entry: string\r\n): Record<string, string> {\r\n const template = prepareTemplate(templatePath)\r\n const entryMap: Record<string, string> = {}\r\n\r\n if (!fs.existsSync(outDir)) {\r\n fs.mkdirSync(outDir, { recursive: true })\r\n }\r\n\r\n // 确保 entry 以 / 开头(用于 HTML 中的路径)\r\n const entryPath = entry.startsWith('/') ? entry : '/' + entry\r\n\r\n for (const page of pages) {\r\n const htmlName = `${page.name}.html`\r\n const outputPath = path.join(outDir, htmlName)\r\n const title = page.title || page.name.charAt(0).toUpperCase() + page.name.slice(1) || defaultTitle\r\n\r\n // 替换标题\r\n let content = template.replace('<!-- PAGE_TITLE -->', title)\r\n\r\n // 替换入口路径:优先使用占位符 <!-- ENTRY_PATH -->\r\n if (content.includes('<!-- ENTRY_PATH -->')) {\r\n content = content.replace(/<!-- ENTRY_PATH -->/g, entryPath)\r\n } else {\r\n // 兼容旧模板:替换常见的 <script> 标签中的 src\r\n content = content.replace(/src=\"\\/src\\/main\\.ts\"/g, `src=\"${entryPath}\"`)\r\n content = content.replace(/src='\\/src\\/main\\.ts'/g, `src='${entryPath}'`)\r\n }\r\n\r\n fs.writeFileSync(outputPath, content, 'utf-8')\r\n entryMap[page.name] = outputPath\r\n }\r\n\r\n return entryMap\r\n}\r\n\r\n/**\r\n * Vite MPA 插件\r\n */\r\nexport default function vitePluginMpa(options: MpaOptions = {}): Plugin {\r\n const {\r\n enable: enableOption,\r\n pagesDir = 'src/pages',\r\n template = 'index.html',\r\n entry = 'src/main.ts',\r\n outDir = 'node_modules/.vite-mpa',\r\n modeFlag = '--mpa',\r\n verbose = false,\r\n defaultTitle = 'App',\r\n pages: manualPages,\r\n } = options\r\n\r\n let isMPA = false\r\n let entryMap: Record<string, string> = {}\r\n let root = ''\r\n\r\n return {\r\n name: 'vite-plugin-mpa',\r\n\r\n config(conf) {\r\n if (enableOption !== undefined) {\r\n isMPA = enableOption\r\n } else {\r\n isMPA = process.argv.includes(modeFlag)\r\n }\r\n\r\n if (verbose) {\r\n console.log(`[MPA] 模式状态: ${isMPA ? '✅ 启用' : '⏭️ 禁用'}`)\r\n }\r\n\r\n if (!isMPA) return\r\n\r\n root = conf.root || process.cwd()\r\n\r\n let resolvedPages: Page[] = []\r\n if (manualPages && manualPages.length > 0) {\r\n resolvedPages = manualPages\r\n if (verbose) {\r\n console.log(`[MPA] 使用手动配置的 ${resolvedPages.length} 个页面`)\r\n }\r\n } else {\r\n const pagesDirAbs = path.resolve(root, pagesDir)\r\n if (!fs.existsSync(pagesDirAbs)) {\r\n console.warn(`[MPA] ⚠️ 页面目录不存在: ${pagesDirAbs},跳过 MPA`)\r\n isMPA = false\r\n return\r\n }\r\n\r\n const scanned = scanVueFiles(pagesDirAbs)\r\n if (scanned.length === 0) {\r\n console.warn(`[MPA] ⚠️ 未找到任何 .vue 页面文件,跳过 MPA`)\r\n isMPA = false\r\n return\r\n }\r\n\r\n resolvedPages = scanned.map(p => ({\r\n name: p.name,\r\n title: p.name.charAt(0).toUpperCase() + p.name.slice(1),\r\n }))\r\n\r\n if (verbose) {\r\n console.log(`[MPA] 自动扫描到 ${resolvedPages.length} 个页面:`, resolvedPages.map(p => p.name).join(', '))\r\n }\r\n }\r\n\r\n const templateAbs = path.resolve(root, template)\r\n const outDirAbs = path.resolve(root, outDir)\r\n\r\n entryMap = generateHtmlFiles(resolvedPages, templateAbs, outDirAbs, defaultTitle, entry)\r\n\r\n if (verbose) {\r\n console.log(`[MPA] ✅ 已生成 ${Object.keys(entryMap).length} 个 HTML 入口文件`)\r\n }\r\n\r\n const build = conf.build || {}\r\n const rollupOptions = build.rollupOptions || {}\r\n rollupOptions.input = entryMap\r\n conf.build = { ...build, rollupOptions }\r\n\r\n if (verbose) {\r\n console.log(`[MPA] ✅ 已设置多入口:`, Object.keys(entryMap).join(', '))\r\n }\r\n },\r\n\r\n configResolved(config) {\r\n if (!isMPA || Object.keys(entryMap).length === 0) return\r\n\r\n if (config.command === 'serve') {\r\n const port = config.server?.port || 5173\r\n console.log(`[MPA] 🚀 开发服务器已启动,多页面入口在 ${outDir} 目录`)\r\n console.log(`[MPA] 📄 访问 http://localhost:${port}/${outDir}/xxx.html`)\r\n }\r\n },\r\n\r\n closeBundle() {\r\n if (!isMPA || Object.keys(entryMap).length === 0) return\r\n\r\n const distDir = path.resolve(root, 'dist')\r\n const htmlDir = path.join(distDir, 'node_modules', '.vite-mpa')\r\n\r\n if (!fs.existsSync(htmlDir)) return\r\n\r\n try {\r\n const files = fs.readdirSync(htmlDir).filter(f => f.endsWith('.html'))\r\n for (const file of files) {\r\n const src = path.join(htmlDir, file)\r\n const dest = path.join(distDir, file)\r\n if (fs.existsSync(dest)) {\r\n fs.unlinkSync(dest)\r\n }\r\n fs.copyFileSync(src, dest)\r\n fs.unlinkSync(src)\r\n if (verbose) {\r\n console.log(`[MPA] 📄 移动 ${file} 到 dist 根目录`)\r\n }\r\n }\r\n\r\n function removeEmptyDir(dir: string) {\r\n if (!fs.existsSync(dir)) return\r\n const files = fs.readdirSync(dir)\r\n if (files.length === 0) {\r\n fs.rmdirSync(dir)\r\n const parentDir = path.dirname(dir)\r\n if (parentDir !== distDir) {\r\n removeEmptyDir(parentDir)\r\n }\r\n }\r\n }\r\n removeEmptyDir(htmlDir)\r\n\r\n if (verbose) {\r\n console.log(`[MPA] 🗑️ 已清理临时目录 ${htmlDir}`)\r\n }\r\n } catch (error: any) {\r\n console.warn(`[MPA] ⚠️ 移动 HTML 文件时发生错误:`, error.message)\r\n }\r\n },\r\n }\r\n}"],"mappings":"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","names":["fs","path","removeEmptyDir","files"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,181 @@
1
+ // src/index.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ function scanVueFiles(dir, base = "") {
5
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
6
+ const results = [];
7
+ for (const entry of entries) {
8
+ const fullPath = path.join(dir, entry.name);
9
+ if (entry.isDirectory()) {
10
+ results.push(...scanVueFiles(fullPath, path.join(base, entry.name)));
11
+ } else if (entry.isFile() && entry.name.endsWith(".vue") && !entry.name.includes("[")) {
12
+ let relative = path.join(base, entry.name).replace(/\.vue$/, "");
13
+ let name = relative.replace(/[\\/]/g, "-");
14
+ if (relative.endsWith("index")) {
15
+ const parts = relative.split(/[\\/]/);
16
+ if (parts.length > 1) {
17
+ name = parts.slice(0, -1).join("-");
18
+ } else {
19
+ name = "index";
20
+ }
21
+ }
22
+ results.push({ name, filePath: fullPath });
23
+ }
24
+ }
25
+ return results;
26
+ }
27
+ function prepareTemplate(templatePath) {
28
+ if (!fs.existsSync(templatePath)) {
29
+ throw new Error(`[MPA] \u6A21\u677F\u6587\u4EF6\u4E0D\u5B58\u5728: ${templatePath}`);
30
+ }
31
+ let content = fs.readFileSync(templatePath, "utf-8");
32
+ if (!content.includes("<!-- PAGE_TITLE -->")) {
33
+ content = content.replace(/<title>(.*?)<\/title>/, "<title><!-- PAGE_TITLE --></title>");
34
+ }
35
+ return content;
36
+ }
37
+ function generateHtmlFiles(pages, templatePath, outDir, defaultTitle, entry) {
38
+ const template = prepareTemplate(templatePath);
39
+ const entryMap = {};
40
+ if (!fs.existsSync(outDir)) {
41
+ fs.mkdirSync(outDir, { recursive: true });
42
+ }
43
+ const entryPath = entry.startsWith("/") ? entry : "/" + entry;
44
+ for (const page of pages) {
45
+ const htmlName = `${page.name}.html`;
46
+ const outputPath = path.join(outDir, htmlName);
47
+ const title = page.title || page.name.charAt(0).toUpperCase() + page.name.slice(1) || defaultTitle;
48
+ let content = template.replace("<!-- PAGE_TITLE -->", title);
49
+ if (content.includes("<!-- ENTRY_PATH -->")) {
50
+ content = content.replace(/<!-- ENTRY_PATH -->/g, entryPath);
51
+ } else {
52
+ content = content.replace(/src="\/src\/main\.ts"/g, `src="${entryPath}"`);
53
+ content = content.replace(/src='\/src\/main\.ts'/g, `src='${entryPath}'`);
54
+ }
55
+ fs.writeFileSync(outputPath, content, "utf-8");
56
+ entryMap[page.name] = outputPath;
57
+ }
58
+ return entryMap;
59
+ }
60
+ function vitePluginMpa(options = {}) {
61
+ const {
62
+ enable: enableOption,
63
+ pagesDir = "src/pages",
64
+ template = "index.html",
65
+ entry = "src/main.ts",
66
+ outDir = "node_modules/.vite-mpa",
67
+ modeFlag = "--mpa",
68
+ verbose = false,
69
+ defaultTitle = "App",
70
+ pages: manualPages
71
+ } = options;
72
+ let isMPA = false;
73
+ let entryMap = {};
74
+ let root = "";
75
+ return {
76
+ name: "vite-plugin-mpa",
77
+ config(conf) {
78
+ if (enableOption !== void 0) {
79
+ isMPA = enableOption;
80
+ } else {
81
+ isMPA = process.argv.includes(modeFlag);
82
+ }
83
+ if (verbose) {
84
+ console.log(`[MPA] \u6A21\u5F0F\u72B6\u6001: ${isMPA ? "\u2705 \u542F\u7528" : "\u23ED\uFE0F \u7981\u7528"}`);
85
+ }
86
+ if (!isMPA) return;
87
+ root = conf.root || process.cwd();
88
+ let resolvedPages = [];
89
+ if (manualPages && manualPages.length > 0) {
90
+ resolvedPages = manualPages;
91
+ if (verbose) {
92
+ console.log(`[MPA] \u4F7F\u7528\u624B\u52A8\u914D\u7F6E\u7684 ${resolvedPages.length} \u4E2A\u9875\u9762`);
93
+ }
94
+ } else {
95
+ const pagesDirAbs = path.resolve(root, pagesDir);
96
+ if (!fs.existsSync(pagesDirAbs)) {
97
+ console.warn(`[MPA] \u26A0\uFE0F \u9875\u9762\u76EE\u5F55\u4E0D\u5B58\u5728: ${pagesDirAbs}\uFF0C\u8DF3\u8FC7 MPA`);
98
+ isMPA = false;
99
+ return;
100
+ }
101
+ const scanned = scanVueFiles(pagesDirAbs);
102
+ if (scanned.length === 0) {
103
+ console.warn(`[MPA] \u26A0\uFE0F \u672A\u627E\u5230\u4EFB\u4F55 .vue \u9875\u9762\u6587\u4EF6\uFF0C\u8DF3\u8FC7 MPA`);
104
+ isMPA = false;
105
+ return;
106
+ }
107
+ resolvedPages = scanned.map((p) => ({
108
+ name: p.name,
109
+ title: p.name.charAt(0).toUpperCase() + p.name.slice(1)
110
+ }));
111
+ if (verbose) {
112
+ console.log(`[MPA] \u81EA\u52A8\u626B\u63CF\u5230 ${resolvedPages.length} \u4E2A\u9875\u9762:`, resolvedPages.map((p) => p.name).join(", "));
113
+ }
114
+ }
115
+ const templateAbs = path.resolve(root, template);
116
+ const outDirAbs = path.resolve(root, outDir);
117
+ entryMap = generateHtmlFiles(resolvedPages, templateAbs, outDirAbs, defaultTitle, entry);
118
+ if (verbose) {
119
+ console.log(`[MPA] \u2705 \u5DF2\u751F\u6210 ${Object.keys(entryMap).length} \u4E2A HTML \u5165\u53E3\u6587\u4EF6`);
120
+ }
121
+ const build = conf.build || {};
122
+ const rollupOptions = build.rollupOptions || {};
123
+ rollupOptions.input = entryMap;
124
+ conf.build = { ...build, rollupOptions };
125
+ if (verbose) {
126
+ console.log(`[MPA] \u2705 \u5DF2\u8BBE\u7F6E\u591A\u5165\u53E3:`, Object.keys(entryMap).join(", "));
127
+ }
128
+ },
129
+ configResolved(config) {
130
+ if (!isMPA || Object.keys(entryMap).length === 0) return;
131
+ if (config.command === "serve") {
132
+ const port = config.server?.port || 5173;
133
+ console.log(`[MPA] \u{1F680} \u5F00\u53D1\u670D\u52A1\u5668\u5DF2\u542F\u52A8\uFF0C\u591A\u9875\u9762\u5165\u53E3\u5728 ${outDir} \u76EE\u5F55`);
134
+ console.log(`[MPA] \u{1F4C4} \u8BBF\u95EE http://localhost:${port}/${outDir}/xxx.html`);
135
+ }
136
+ },
137
+ closeBundle() {
138
+ if (!isMPA || Object.keys(entryMap).length === 0) return;
139
+ const distDir = path.resolve(root, "dist");
140
+ const htmlDir = path.join(distDir, "node_modules", ".vite-mpa");
141
+ if (!fs.existsSync(htmlDir)) return;
142
+ try {
143
+ let removeEmptyDir2 = function(dir) {
144
+ if (!fs.existsSync(dir)) return;
145
+ const files2 = fs.readdirSync(dir);
146
+ if (files2.length === 0) {
147
+ fs.rmdirSync(dir);
148
+ const parentDir = path.dirname(dir);
149
+ if (parentDir !== distDir) {
150
+ removeEmptyDir2(parentDir);
151
+ }
152
+ }
153
+ };
154
+ var removeEmptyDir = removeEmptyDir2;
155
+ const files = fs.readdirSync(htmlDir).filter((f) => f.endsWith(".html"));
156
+ for (const file of files) {
157
+ const src = path.join(htmlDir, file);
158
+ const dest = path.join(distDir, file);
159
+ if (fs.existsSync(dest)) {
160
+ fs.unlinkSync(dest);
161
+ }
162
+ fs.copyFileSync(src, dest);
163
+ fs.unlinkSync(src);
164
+ if (verbose) {
165
+ console.log(`[MPA] \u{1F4C4} \u79FB\u52A8 ${file} \u5230 dist \u6839\u76EE\u5F55`);
166
+ }
167
+ }
168
+ removeEmptyDir2(htmlDir);
169
+ if (verbose) {
170
+ console.log(`[MPA] \u{1F5D1}\uFE0F \u5DF2\u6E05\u7406\u4E34\u65F6\u76EE\u5F55 ${htmlDir}`);
171
+ }
172
+ } catch (error) {
173
+ console.warn(`[MPA] \u26A0\uFE0F \u79FB\u52A8 HTML \u6587\u4EF6\u65F6\u53D1\u751F\u9519\u8BEF:`, error.message);
174
+ }
175
+ }
176
+ };
177
+ }
178
+ export {
179
+ vitePluginMpa as default
180
+ };
181
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Plugin } from 'vite'\r\nimport fs from 'fs'\r\nimport path from 'path'\r\n\r\n/**\r\n * 页面配置\r\n */\r\nexport interface Page {\r\n name: string\r\n title?: string\r\n template?: string\r\n entry?: string\r\n}\r\n\r\n/**\r\n * 插件配置选项\r\n */\r\nexport interface MpaOptions {\r\n /**\r\n * 强制启用或禁用 MPA 模式。\r\n * 若未设置,则检测命令行参数 `--mpa`。\r\n * @default undefined\r\n */\r\n enable?: boolean\r\n /**\r\n * 页面组件目录(相对于项目根目录)\r\n * @default 'src/pages'\r\n */\r\n pagesDir?: string\r\n /**\r\n * 全局 HTML 模板路径(相对于项目根目录)\r\n * @default 'index.html'\r\n */\r\n template?: string\r\n /**\r\n * 全局入口文件路径(所有页面共享)\r\n * @default 'src/main.ts'\r\n */\r\n entry?: string\r\n /**\r\n * 生成的 HTML 临时存放目录\r\n * @default 'node_modules/.vite-mpa'\r\n */\r\n outDir?: string\r\n /**\r\n * 启用 MPA 的命令行标志(仅当 enable 未设置时生效)\r\n * @default '--mpa'\r\n */\r\n modeFlag?: string\r\n /**\r\n * 是否打印详细日志\r\n * @default false\r\n */\r\n verbose?: boolean\r\n /**\r\n * 默认标题(当页面未指定 title 时使用)\r\n * @default 'App'\r\n */\r\n defaultTitle?: string\r\n /**\r\n * 手动指定页面列表(优先级高于自动扫描)\r\n */\r\n pages?: Page[]\r\n}\r\n\r\n/**\r\n * 扫描到的页面信息(内部使用)\r\n */\r\ninterface ScannedPage {\r\n name: string\r\n filePath: string\r\n}\r\n\r\n/**\r\n * 递归扫描 .vue 文件(排除动态路由)\r\n */\r\nfunction scanVueFiles(dir: string, base = ''): ScannedPage[] {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true })\r\n const results: ScannedPage[] = []\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name)\r\n if (entry.isDirectory()) {\r\n results.push(...scanVueFiles(fullPath, path.join(base, entry.name)))\r\n } else if (entry.isFile() && entry.name.endsWith('.vue') && !entry.name.includes('[')) {\r\n let relative = path.join(base, entry.name).replace(/\\.vue$/, '')\r\n let name = relative.replace(/[\\\\/]/g, '-')\r\n if (relative.endsWith('index')) {\r\n const parts = relative.split(/[\\\\/]/)\r\n if (parts.length > 1) {\r\n name = parts.slice(0, -1).join('-')\r\n } else {\r\n name = 'index'\r\n }\r\n }\r\n results.push({ name, filePath: fullPath })\r\n }\r\n }\r\n return results\r\n}\r\n\r\n/**\r\n * 准备模板内容(确保包含标题占位符)\r\n */\r\nfunction prepareTemplate(templatePath: string): string {\r\n if (!fs.existsSync(templatePath)) {\r\n throw new Error(`[MPA] 模板文件不存在: ${templatePath}`)\r\n }\r\n let content = fs.readFileSync(templatePath, 'utf-8')\r\n if (!content.includes('<!-- PAGE_TITLE -->')) {\r\n content = content.replace(/<title>(.*?)<\\/title>/, '<title><!-- PAGE_TITLE --></title>')\r\n }\r\n return content\r\n}\r\n\r\n/**\r\n * 生成 HTML 文件\r\n */\r\nfunction generateHtmlFiles(\r\n pages: Page[],\r\n templatePath: string,\r\n outDir: string,\r\n defaultTitle: string,\r\n entry: string\r\n): Record<string, string> {\r\n const template = prepareTemplate(templatePath)\r\n const entryMap: Record<string, string> = {}\r\n\r\n if (!fs.existsSync(outDir)) {\r\n fs.mkdirSync(outDir, { recursive: true })\r\n }\r\n\r\n // 确保 entry 以 / 开头(用于 HTML 中的路径)\r\n const entryPath = entry.startsWith('/') ? entry : '/' + entry\r\n\r\n for (const page of pages) {\r\n const htmlName = `${page.name}.html`\r\n const outputPath = path.join(outDir, htmlName)\r\n const title = page.title || page.name.charAt(0).toUpperCase() + page.name.slice(1) || defaultTitle\r\n\r\n // 替换标题\r\n let content = template.replace('<!-- PAGE_TITLE -->', title)\r\n\r\n // 替换入口路径:优先使用占位符 <!-- ENTRY_PATH -->\r\n if (content.includes('<!-- ENTRY_PATH -->')) {\r\n content = content.replace(/<!-- ENTRY_PATH -->/g, entryPath)\r\n } else {\r\n // 兼容旧模板:替换常见的 <script> 标签中的 src\r\n content = content.replace(/src=\"\\/src\\/main\\.ts\"/g, `src=\"${entryPath}\"`)\r\n content = content.replace(/src='\\/src\\/main\\.ts'/g, `src='${entryPath}'`)\r\n }\r\n\r\n fs.writeFileSync(outputPath, content, 'utf-8')\r\n entryMap[page.name] = outputPath\r\n }\r\n\r\n return entryMap\r\n}\r\n\r\n/**\r\n * Vite MPA 插件\r\n */\r\nexport default function vitePluginMpa(options: MpaOptions = {}): Plugin {\r\n const {\r\n enable: enableOption,\r\n pagesDir = 'src/pages',\r\n template = 'index.html',\r\n entry = 'src/main.ts',\r\n outDir = 'node_modules/.vite-mpa',\r\n modeFlag = '--mpa',\r\n verbose = false,\r\n defaultTitle = 'App',\r\n pages: manualPages,\r\n } = options\r\n\r\n let isMPA = false\r\n let entryMap: Record<string, string> = {}\r\n let root = ''\r\n\r\n return {\r\n name: 'vite-plugin-mpa',\r\n\r\n config(conf) {\r\n if (enableOption !== undefined) {\r\n isMPA = enableOption\r\n } else {\r\n isMPA = process.argv.includes(modeFlag)\r\n }\r\n\r\n if (verbose) {\r\n console.log(`[MPA] 模式状态: ${isMPA ? '✅ 启用' : '⏭️ 禁用'}`)\r\n }\r\n\r\n if (!isMPA) return\r\n\r\n root = conf.root || process.cwd()\r\n\r\n let resolvedPages: Page[] = []\r\n if (manualPages && manualPages.length > 0) {\r\n resolvedPages = manualPages\r\n if (verbose) {\r\n console.log(`[MPA] 使用手动配置的 ${resolvedPages.length} 个页面`)\r\n }\r\n } else {\r\n const pagesDirAbs = path.resolve(root, pagesDir)\r\n if (!fs.existsSync(pagesDirAbs)) {\r\n console.warn(`[MPA] ⚠️ 页面目录不存在: ${pagesDirAbs},跳过 MPA`)\r\n isMPA = false\r\n return\r\n }\r\n\r\n const scanned = scanVueFiles(pagesDirAbs)\r\n if (scanned.length === 0) {\r\n console.warn(`[MPA] ⚠️ 未找到任何 .vue 页面文件,跳过 MPA`)\r\n isMPA = false\r\n return\r\n }\r\n\r\n resolvedPages = scanned.map(p => ({\r\n name: p.name,\r\n title: p.name.charAt(0).toUpperCase() + p.name.slice(1),\r\n }))\r\n\r\n if (verbose) {\r\n console.log(`[MPA] 自动扫描到 ${resolvedPages.length} 个页面:`, resolvedPages.map(p => p.name).join(', '))\r\n }\r\n }\r\n\r\n const templateAbs = path.resolve(root, template)\r\n const outDirAbs = path.resolve(root, outDir)\r\n\r\n entryMap = generateHtmlFiles(resolvedPages, templateAbs, outDirAbs, defaultTitle, entry)\r\n\r\n if (verbose) {\r\n console.log(`[MPA] ✅ 已生成 ${Object.keys(entryMap).length} 个 HTML 入口文件`)\r\n }\r\n\r\n const build = conf.build || {}\r\n const rollupOptions = build.rollupOptions || {}\r\n rollupOptions.input = entryMap\r\n conf.build = { ...build, rollupOptions }\r\n\r\n if (verbose) {\r\n console.log(`[MPA] ✅ 已设置多入口:`, Object.keys(entryMap).join(', '))\r\n }\r\n },\r\n\r\n configResolved(config) {\r\n if (!isMPA || Object.keys(entryMap).length === 0) return\r\n\r\n if (config.command === 'serve') {\r\n const port = config.server?.port || 5173\r\n console.log(`[MPA] 🚀 开发服务器已启动,多页面入口在 ${outDir} 目录`)\r\n console.log(`[MPA] 📄 访问 http://localhost:${port}/${outDir}/xxx.html`)\r\n }\r\n },\r\n\r\n closeBundle() {\r\n if (!isMPA || Object.keys(entryMap).length === 0) return\r\n\r\n const distDir = path.resolve(root, 'dist')\r\n const htmlDir = path.join(distDir, 'node_modules', '.vite-mpa')\r\n\r\n if (!fs.existsSync(htmlDir)) return\r\n\r\n try {\r\n const files = fs.readdirSync(htmlDir).filter(f => f.endsWith('.html'))\r\n for (const file of files) {\r\n const src = path.join(htmlDir, file)\r\n const dest = path.join(distDir, file)\r\n if (fs.existsSync(dest)) {\r\n fs.unlinkSync(dest)\r\n }\r\n fs.copyFileSync(src, dest)\r\n fs.unlinkSync(src)\r\n if (verbose) {\r\n console.log(`[MPA] 📄 移动 ${file} 到 dist 根目录`)\r\n }\r\n }\r\n\r\n function removeEmptyDir(dir: string) {\r\n if (!fs.existsSync(dir)) return\r\n const files = fs.readdirSync(dir)\r\n if (files.length === 0) {\r\n fs.rmdirSync(dir)\r\n const parentDir = path.dirname(dir)\r\n if (parentDir !== distDir) {\r\n removeEmptyDir(parentDir)\r\n }\r\n }\r\n }\r\n removeEmptyDir(htmlDir)\r\n\r\n if (verbose) {\r\n console.log(`[MPA] 🗑️ 已清理临时目录 ${htmlDir}`)\r\n }\r\n } catch (error: any) {\r\n console.warn(`[MPA] ⚠️ 移动 HTML 文件时发生错误:`, error.message)\r\n }\r\n },\r\n }\r\n}"],"mappings":"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","names":["removeEmptyDir","files"]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@simposons/vite-plugin-mpa",
3
+ "version": "1.0.0",
4
+ "description": "A Vite plugin for Multi-Page Applications (MPA) with shared entry and auto-scanning",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "license": "MIT",
25
+ "keywords": [
26
+ "vite",
27
+ "vite-plugin",
28
+ "mpa",
29
+ "multi-page",
30
+ "multi-entry",
31
+ "html"
32
+ ],
33
+ "peerDependencies": {
34
+ "vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.0.0",
38
+ "tsup": "^8.0.0",
39
+ "typescript": "^5.0.0",
40
+ "vite": "^8.0.0"
41
+ },
42
+ "author": {
43
+ "name": "simposons",
44
+ "email": "simposons@qq.com",
45
+ "url": "https://github.com/simposons"
46
+ },
47
+ "contributors": [],
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/simposons/vite-plugin-mpa.git"
51
+ },
52
+ "github": "https://github.com/simposons/vite-plugin-mpa.git",
53
+ "dependencies": {}
54
+ }