vite-plugin-uni-inject 0.2.0 → 0.3.1
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 +9 -6
- package/dist/index.cjs +162 -8
- package/dist/index.d.cts +44 -7
- package/dist/index.d.mts +43 -7
- package/dist/index.mjs +160 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
# vite-plugin-uni-inject
|
|
2
2
|
|
|
3
|
-
利用 Vite
|
|
3
|
+
利用 Vite 插件机制,实现自动注入代码,解放你的双手。
|
|
4
4
|
|
|
5
5
|
## 功能特点
|
|
6
6
|
|
|
7
7
|
- 干净的注入任何代码,没有多余的结构。
|
|
8
8
|
- 支持注入 `<page-meta/>` 这种只能放在页面第一个位置的标签节点。
|
|
9
|
-
-
|
|
9
|
+
- 支持基于文件路由的 `page.json` 自动生成并注入,无需手动维护页面表。
|
|
10
10
|
|
|
11
11
|
## 如何使用
|
|
12
12
|
|
|
13
|
-
原理就是通过 vite 插件机制,在构建过程中自动注入 inject.vue 中的代码。
|
|
14
|
-
|
|
15
13
|
### 安装依赖
|
|
16
14
|
|
|
17
15
|
```bash
|
|
@@ -22,17 +20,22 @@ pnpm i -D vite-plugin-uni-inject
|
|
|
22
20
|
|
|
23
21
|
```ts
|
|
24
22
|
import { defineConfig } from "vite";
|
|
23
|
+
import { uniAutoPages, uniInject } from "vite-plugin-uni-inject";
|
|
25
24
|
import uni from "@dcloudio/vite-plugin-uni";
|
|
26
|
-
import uniInject from "vite-plugin-uni-inject";
|
|
27
25
|
|
|
28
26
|
// 如果有重写 page.json 文件的插件,请确保写在 uniInject 之前
|
|
29
27
|
export default defineConfig(() => {
|
|
30
28
|
return {
|
|
31
|
-
plugins: [uniInject(), uni()],
|
|
29
|
+
plugins: [uniAutoPages(), uniInject(), uni()],
|
|
32
30
|
};
|
|
33
31
|
});
|
|
34
32
|
```
|
|
35
33
|
|
|
34
|
+
### 独立插件说明
|
|
35
|
+
|
|
36
|
+
- `uniAutoPages(options)`:负责扫描文件路由并补全 `src/pages.json` 。
|
|
37
|
+
- `uniInject(options)`:负责注入 `App.inject.vue`(或自定义文件)到页面文件。
|
|
38
|
+
|
|
36
39
|
## 报告错误
|
|
37
40
|
|
|
38
41
|
欢迎提交 issue 与我们讨论。
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
1
2
|
//#region \0rolldown/runtime.js
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
@@ -28,9 +29,12 @@ let path = require("path");
|
|
|
28
29
|
path = __toESM(path);
|
|
29
30
|
let magic_string = require("magic-string");
|
|
30
31
|
magic_string = __toESM(magic_string);
|
|
31
|
-
//#region src/
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
//#region src/inject.ts
|
|
33
|
+
/**
|
|
34
|
+
* 注入代码插件
|
|
35
|
+
*/
|
|
36
|
+
function uniInject(opts) {
|
|
37
|
+
const { path: injectPath = "App.inject.vue" } = opts || {};
|
|
34
38
|
const pageSet = /* @__PURE__ */ new Set();
|
|
35
39
|
let injectTemplate = "";
|
|
36
40
|
let injectScriptSetup = "";
|
|
@@ -79,15 +83,15 @@ function src_default(opts) {
|
|
|
79
83
|
if (newDescriptor.scriptSetup) {
|
|
80
84
|
const start = newDescriptor.scriptSetup.loc.start.offset;
|
|
81
85
|
const end = newDescriptor.scriptSetup.loc.end.offset;
|
|
82
|
-
const
|
|
83
|
-
const s = new magic_string.default(
|
|
84
|
-
const ast = (0, _babel_parser.parse)(
|
|
86
|
+
const scriptCode = newDescriptor.scriptSetup.content;
|
|
87
|
+
const s = new magic_string.default(scriptCode);
|
|
88
|
+
const ast = (0, _babel_parser.parse)(scriptCode, {
|
|
85
89
|
sourceType: "module",
|
|
86
90
|
plugins: ["typescript"]
|
|
87
91
|
});
|
|
88
92
|
const imports = [];
|
|
89
93
|
for (const node of ast.program.body) if (node.type === "ImportDeclaration") {
|
|
90
|
-
imports.push(
|
|
94
|
+
imports.push(scriptCode.slice(node.start, node.end));
|
|
91
95
|
s.remove(node.start, node.end + 1);
|
|
92
96
|
}
|
|
93
97
|
if (imports.length) s.prepend("\n" + imports.join("\n"));
|
|
@@ -98,4 +102,154 @@ function src_default(opts) {
|
|
|
98
102
|
};
|
|
99
103
|
}
|
|
100
104
|
//#endregion
|
|
101
|
-
|
|
105
|
+
//#region src/auto-pages.ts
|
|
106
|
+
function toPosixPath(value) {
|
|
107
|
+
return value.replace(/\\/g, "/");
|
|
108
|
+
}
|
|
109
|
+
function normalizeDir(value) {
|
|
110
|
+
return toPosixPath(value).replace(/^\/+|\/+$/g, "");
|
|
111
|
+
}
|
|
112
|
+
function walkFiles(dirPath, filePaths = []) {
|
|
113
|
+
if (!fs.default.existsSync(dirPath)) return filePaths;
|
|
114
|
+
const entries = fs.default.readdirSync(dirPath, { withFileTypes: true });
|
|
115
|
+
for (const entry of entries) {
|
|
116
|
+
const fullPath = path.default.join(dirPath, entry.name);
|
|
117
|
+
if (entry.isDirectory()) {
|
|
118
|
+
walkFiles(fullPath, filePaths);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
filePaths.push(fullPath);
|
|
122
|
+
}
|
|
123
|
+
return filePaths;
|
|
124
|
+
}
|
|
125
|
+
function collectFileRoutes(srcRoot, routeDirs) {
|
|
126
|
+
const routeSet = /* @__PURE__ */ new Set();
|
|
127
|
+
routeDirs.forEach((dir) => {
|
|
128
|
+
walkFiles(path.default.resolve(srcRoot, dir)).forEach((filePath) => {
|
|
129
|
+
if (!filePath.endsWith(".vue")) return;
|
|
130
|
+
const noExt = path.default.relative(srcRoot, filePath).slice(0, -4);
|
|
131
|
+
routeSet.add(toPosixPath(noExt));
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
return Array.from(routeSet).sort();
|
|
135
|
+
}
|
|
136
|
+
function getScanDirs(dir, subPackages) {
|
|
137
|
+
const dirSet = /* @__PURE__ */ new Set();
|
|
138
|
+
const normalizedMainDir = normalizeDir(dir);
|
|
139
|
+
if (normalizedMainDir) dirSet.add(normalizedMainDir);
|
|
140
|
+
subPackages.forEach((subRoot) => {
|
|
141
|
+
const normalizedSubRoot = normalizeDir(subRoot);
|
|
142
|
+
if (!normalizedSubRoot) return;
|
|
143
|
+
if (normalizedMainDir) {
|
|
144
|
+
dirSet.add(`${normalizedSubRoot}/${normalizedMainDir}`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
dirSet.add(normalizedSubRoot);
|
|
148
|
+
});
|
|
149
|
+
return Array.from(dirSet);
|
|
150
|
+
}
|
|
151
|
+
function readPagesJson(pagesJsonPath) {
|
|
152
|
+
if (!fs.default.existsSync(pagesJsonPath)) return null;
|
|
153
|
+
const content = fs.default.readFileSync(pagesJsonPath, "utf-8");
|
|
154
|
+
return JSON.parse(content);
|
|
155
|
+
}
|
|
156
|
+
function mergePages(routes, existingPages) {
|
|
157
|
+
const existingMap = new Map(existingPages.map((p) => [p.path, p]));
|
|
158
|
+
return routes.map((route) => {
|
|
159
|
+
return existingMap.get(route) ?? { path: route };
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function hasPagesJsonChanged(current, next) {
|
|
163
|
+
return JSON.stringify({
|
|
164
|
+
pages: next.pages,
|
|
165
|
+
subPackages: next.subPackages ?? []
|
|
166
|
+
}) !== JSON.stringify({
|
|
167
|
+
pages: current.pages ?? [],
|
|
168
|
+
subPackages: current.subPackages ?? []
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function getPagesByFileRoute(routes, pagesJson, subPackageRoots) {
|
|
172
|
+
const originPages = pagesJson.pages ?? [];
|
|
173
|
+
const originSubPackages = pagesJson.subPackages ?? [];
|
|
174
|
+
const mainRoutes = [];
|
|
175
|
+
const subRouteMap = /* @__PURE__ */ new Map();
|
|
176
|
+
const normalizedSubRoots = subPackageRoots.map(normalizeDir);
|
|
177
|
+
for (const route of routes) {
|
|
178
|
+
const matchedRoot = normalizedSubRoots.find((root) => {
|
|
179
|
+
return route.startsWith(root + "/");
|
|
180
|
+
});
|
|
181
|
+
if (matchedRoot) {
|
|
182
|
+
const subPath = route.slice(matchedRoot.length + 1);
|
|
183
|
+
const list = subRouteMap.get(matchedRoot) ?? [];
|
|
184
|
+
list.push(subPath);
|
|
185
|
+
subRouteMap.set(matchedRoot, list);
|
|
186
|
+
} else mainRoutes.push(route);
|
|
187
|
+
}
|
|
188
|
+
const homePage = originPages[0]?.path;
|
|
189
|
+
if (mainRoutes.includes(homePage)) {
|
|
190
|
+
const idx = mainRoutes.indexOf(homePage);
|
|
191
|
+
mainRoutes.splice(idx, 1);
|
|
192
|
+
mainRoutes.unshift(homePage);
|
|
193
|
+
}
|
|
194
|
+
const pages = mergePages(mainRoutes, originPages);
|
|
195
|
+
const subPackages = normalizedSubRoots.map((root) => {
|
|
196
|
+
return {
|
|
197
|
+
root,
|
|
198
|
+
pages: mergePages(subRouteMap.get(root) ?? [], originSubPackages.find((s) => {
|
|
199
|
+
return normalizeDir(s.root) === root;
|
|
200
|
+
})?.pages ?? [])
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
const merged = {
|
|
204
|
+
...pagesJson,
|
|
205
|
+
pages,
|
|
206
|
+
subPackages
|
|
207
|
+
};
|
|
208
|
+
return {
|
|
209
|
+
merged,
|
|
210
|
+
changed: hasPagesJsonChanged(pagesJson, merged)
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function resolveDtsFilePath(srcRoot, dts) {
|
|
214
|
+
return path.default.isAbsolute(dts) ? dts : path.default.resolve(srcRoot, dts);
|
|
215
|
+
}
|
|
216
|
+
function buildRouteDts(routes) {
|
|
217
|
+
return [
|
|
218
|
+
"// Auto-generated by vite-plugin-uni-inject. Do not edit.",
|
|
219
|
+
"",
|
|
220
|
+
"/** 提取路径 */",
|
|
221
|
+
"export type ExtractPath<T extends string, Prefix extends string> = T extends `${Prefix}${infer P}` ? P : never;",
|
|
222
|
+
"",
|
|
223
|
+
"/** 路由路径 */",
|
|
224
|
+
"export type RoutePath =",
|
|
225
|
+
routes.map((route) => ` | "/${route}"`).join("\n") + ";",
|
|
226
|
+
""
|
|
227
|
+
].join("\n");
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* 自动补全 pages.json 插件
|
|
231
|
+
*/
|
|
232
|
+
function uniAutoPages(opts) {
|
|
233
|
+
const { dir = "pages", subPackages = [], dts = "uni-pages.d.ts" } = opts || {};
|
|
234
|
+
return {
|
|
235
|
+
name: "vite-plugin-uni-auto-pages",
|
|
236
|
+
enforce: "pre",
|
|
237
|
+
configResolved(config) {
|
|
238
|
+
const srcRoot = path.default.resolve(config.root, "src");
|
|
239
|
+
const pagesJsonPath = path.default.join(srcRoot, "pages.json");
|
|
240
|
+
const pagesJson = readPagesJson(pagesJsonPath);
|
|
241
|
+
if (!pagesJson) return;
|
|
242
|
+
const routes = collectFileRoutes(srcRoot, getScanDirs(dir, subPackages));
|
|
243
|
+
const { merged, changed } = getPagesByFileRoute(routes, pagesJson, subPackages);
|
|
244
|
+
if (changed) fs.default.writeFileSync(pagesJsonPath, JSON.stringify(merged, null, 2) + "\n");
|
|
245
|
+
if (dts) {
|
|
246
|
+
const dtsFilePath = resolveDtsFilePath(srcRoot, dts);
|
|
247
|
+
const dtsContent = buildRouteDts(routes);
|
|
248
|
+
fs.default.writeFileSync(dtsFilePath, dtsContent);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
exports.uniAutoPages = uniAutoPages;
|
|
255
|
+
exports.uniInject = uniInject;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,36 @@
|
|
|
1
1
|
//#region src/types.d.ts
|
|
2
|
-
/**
|
|
3
|
-
interface
|
|
4
|
-
/**
|
|
5
|
-
|
|
2
|
+
/** 注入插件配置 */
|
|
3
|
+
interface InjectPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 注入的文件路径
|
|
6
|
+
* @default './App.inject.vue'
|
|
7
|
+
*/
|
|
8
|
+
path?: string;
|
|
9
|
+
}
|
|
10
|
+
/** 自动补全 pages 插件配置 */
|
|
11
|
+
interface AutoPagesPluginOptions {
|
|
12
|
+
/**
|
|
13
|
+
* 输出类型
|
|
14
|
+
* @default './uni-pages.d.ts'
|
|
15
|
+
*/
|
|
16
|
+
dts?: string;
|
|
17
|
+
/**
|
|
18
|
+
* 扫描目录
|
|
19
|
+
* @default 'pages'
|
|
20
|
+
*/
|
|
21
|
+
dir?: string;
|
|
22
|
+
/**
|
|
23
|
+
* 分包目录
|
|
24
|
+
* @default []
|
|
25
|
+
*/
|
|
26
|
+
subPackages?: string[];
|
|
6
27
|
}
|
|
7
28
|
//#endregion
|
|
8
|
-
//#region src/
|
|
9
|
-
|
|
29
|
+
//#region src/inject.d.ts
|
|
30
|
+
/**
|
|
31
|
+
* 注入代码插件
|
|
32
|
+
*/
|
|
33
|
+
declare function uniInject(opts?: InjectPluginOptions): {
|
|
10
34
|
name: string;
|
|
11
35
|
enforce: "pre";
|
|
12
36
|
configResolved(config: {
|
|
@@ -16,4 +40,17 @@ declare function export_default(opts?: PluginOptions): {
|
|
|
16
40
|
code: string;
|
|
17
41
|
} | undefined;
|
|
18
42
|
};
|
|
19
|
-
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/auto-pages.d.ts
|
|
45
|
+
/**
|
|
46
|
+
* 自动补全 pages.json 插件
|
|
47
|
+
*/
|
|
48
|
+
declare function uniAutoPages(opts?: AutoPagesPluginOptions): {
|
|
49
|
+
name: string;
|
|
50
|
+
enforce: "pre";
|
|
51
|
+
configResolved(config: {
|
|
52
|
+
root: string;
|
|
53
|
+
}): void;
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
56
|
+
export { uniAutoPages, uniInject };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,12 +1,36 @@
|
|
|
1
1
|
//#region src/types.d.ts
|
|
2
|
-
/**
|
|
3
|
-
interface
|
|
4
|
-
/**
|
|
5
|
-
|
|
2
|
+
/** 注入插件配置 */
|
|
3
|
+
interface InjectPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 注入的文件路径
|
|
6
|
+
* @default './App.inject.vue'
|
|
7
|
+
*/
|
|
8
|
+
path?: string;
|
|
9
|
+
}
|
|
10
|
+
/** 自动补全 pages 插件配置 */
|
|
11
|
+
interface AutoPagesPluginOptions {
|
|
12
|
+
/**
|
|
13
|
+
* 输出类型
|
|
14
|
+
* @default './uni-pages.d.ts'
|
|
15
|
+
*/
|
|
16
|
+
dts?: string;
|
|
17
|
+
/**
|
|
18
|
+
* 扫描目录
|
|
19
|
+
* @default 'pages'
|
|
20
|
+
*/
|
|
21
|
+
dir?: string;
|
|
22
|
+
/**
|
|
23
|
+
* 分包目录
|
|
24
|
+
* @default []
|
|
25
|
+
*/
|
|
26
|
+
subPackages?: string[];
|
|
6
27
|
}
|
|
7
28
|
//#endregion
|
|
8
|
-
//#region src/
|
|
9
|
-
|
|
29
|
+
//#region src/inject.d.ts
|
|
30
|
+
/**
|
|
31
|
+
* 注入代码插件
|
|
32
|
+
*/
|
|
33
|
+
declare function uniInject(opts?: InjectPluginOptions): {
|
|
10
34
|
name: string;
|
|
11
35
|
enforce: "pre";
|
|
12
36
|
configResolved(config: {
|
|
@@ -17,4 +41,16 @@ declare function export_default(opts?: PluginOptions): {
|
|
|
17
41
|
} | undefined;
|
|
18
42
|
};
|
|
19
43
|
//#endregion
|
|
20
|
-
|
|
44
|
+
//#region src/auto-pages.d.ts
|
|
45
|
+
/**
|
|
46
|
+
* 自动补全 pages.json 插件
|
|
47
|
+
*/
|
|
48
|
+
declare function uniAutoPages(opts?: AutoPagesPluginOptions): {
|
|
49
|
+
name: string;
|
|
50
|
+
enforce: "pre";
|
|
51
|
+
configResolved(config: {
|
|
52
|
+
root: string;
|
|
53
|
+
}): void;
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
56
|
+
export { uniAutoPages, uniInject };
|
package/dist/index.mjs
CHANGED
|
@@ -3,9 +3,12 @@ import { parse as parse$1 } from "@vue/compiler-sfc";
|
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import MagicString from "magic-string";
|
|
6
|
-
//#region src/
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
//#region src/inject.ts
|
|
7
|
+
/**
|
|
8
|
+
* 注入代码插件
|
|
9
|
+
*/
|
|
10
|
+
function uniInject(opts) {
|
|
11
|
+
const { path: injectPath = "App.inject.vue" } = opts || {};
|
|
9
12
|
const pageSet = /* @__PURE__ */ new Set();
|
|
10
13
|
let injectTemplate = "";
|
|
11
14
|
let injectScriptSetup = "";
|
|
@@ -54,15 +57,15 @@ function src_default(opts) {
|
|
|
54
57
|
if (newDescriptor.scriptSetup) {
|
|
55
58
|
const start = newDescriptor.scriptSetup.loc.start.offset;
|
|
56
59
|
const end = newDescriptor.scriptSetup.loc.end.offset;
|
|
57
|
-
const
|
|
58
|
-
const s = new MagicString(
|
|
59
|
-
const ast = parse(
|
|
60
|
+
const scriptCode = newDescriptor.scriptSetup.content;
|
|
61
|
+
const s = new MagicString(scriptCode);
|
|
62
|
+
const ast = parse(scriptCode, {
|
|
60
63
|
sourceType: "module",
|
|
61
64
|
plugins: ["typescript"]
|
|
62
65
|
});
|
|
63
66
|
const imports = [];
|
|
64
67
|
for (const node of ast.program.body) if (node.type === "ImportDeclaration") {
|
|
65
|
-
imports.push(
|
|
68
|
+
imports.push(scriptCode.slice(node.start, node.end));
|
|
66
69
|
s.remove(node.start, node.end + 1);
|
|
67
70
|
}
|
|
68
71
|
if (imports.length) s.prepend("\n" + imports.join("\n"));
|
|
@@ -73,4 +76,153 @@ function src_default(opts) {
|
|
|
73
76
|
};
|
|
74
77
|
}
|
|
75
78
|
//#endregion
|
|
76
|
-
|
|
79
|
+
//#region src/auto-pages.ts
|
|
80
|
+
function toPosixPath(value) {
|
|
81
|
+
return value.replace(/\\/g, "/");
|
|
82
|
+
}
|
|
83
|
+
function normalizeDir(value) {
|
|
84
|
+
return toPosixPath(value).replace(/^\/+|\/+$/g, "");
|
|
85
|
+
}
|
|
86
|
+
function walkFiles(dirPath, filePaths = []) {
|
|
87
|
+
if (!fs.existsSync(dirPath)) return filePaths;
|
|
88
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
91
|
+
if (entry.isDirectory()) {
|
|
92
|
+
walkFiles(fullPath, filePaths);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
filePaths.push(fullPath);
|
|
96
|
+
}
|
|
97
|
+
return filePaths;
|
|
98
|
+
}
|
|
99
|
+
function collectFileRoutes(srcRoot, routeDirs) {
|
|
100
|
+
const routeSet = /* @__PURE__ */ new Set();
|
|
101
|
+
routeDirs.forEach((dir) => {
|
|
102
|
+
walkFiles(path.resolve(srcRoot, dir)).forEach((filePath) => {
|
|
103
|
+
if (!filePath.endsWith(".vue")) return;
|
|
104
|
+
const noExt = path.relative(srcRoot, filePath).slice(0, -4);
|
|
105
|
+
routeSet.add(toPosixPath(noExt));
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
return Array.from(routeSet).sort();
|
|
109
|
+
}
|
|
110
|
+
function getScanDirs(dir, subPackages) {
|
|
111
|
+
const dirSet = /* @__PURE__ */ new Set();
|
|
112
|
+
const normalizedMainDir = normalizeDir(dir);
|
|
113
|
+
if (normalizedMainDir) dirSet.add(normalizedMainDir);
|
|
114
|
+
subPackages.forEach((subRoot) => {
|
|
115
|
+
const normalizedSubRoot = normalizeDir(subRoot);
|
|
116
|
+
if (!normalizedSubRoot) return;
|
|
117
|
+
if (normalizedMainDir) {
|
|
118
|
+
dirSet.add(`${normalizedSubRoot}/${normalizedMainDir}`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
dirSet.add(normalizedSubRoot);
|
|
122
|
+
});
|
|
123
|
+
return Array.from(dirSet);
|
|
124
|
+
}
|
|
125
|
+
function readPagesJson(pagesJsonPath) {
|
|
126
|
+
if (!fs.existsSync(pagesJsonPath)) return null;
|
|
127
|
+
const content = fs.readFileSync(pagesJsonPath, "utf-8");
|
|
128
|
+
return JSON.parse(content);
|
|
129
|
+
}
|
|
130
|
+
function mergePages(routes, existingPages) {
|
|
131
|
+
const existingMap = new Map(existingPages.map((p) => [p.path, p]));
|
|
132
|
+
return routes.map((route) => {
|
|
133
|
+
return existingMap.get(route) ?? { path: route };
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function hasPagesJsonChanged(current, next) {
|
|
137
|
+
return JSON.stringify({
|
|
138
|
+
pages: next.pages,
|
|
139
|
+
subPackages: next.subPackages ?? []
|
|
140
|
+
}) !== JSON.stringify({
|
|
141
|
+
pages: current.pages ?? [],
|
|
142
|
+
subPackages: current.subPackages ?? []
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function getPagesByFileRoute(routes, pagesJson, subPackageRoots) {
|
|
146
|
+
const originPages = pagesJson.pages ?? [];
|
|
147
|
+
const originSubPackages = pagesJson.subPackages ?? [];
|
|
148
|
+
const mainRoutes = [];
|
|
149
|
+
const subRouteMap = /* @__PURE__ */ new Map();
|
|
150
|
+
const normalizedSubRoots = subPackageRoots.map(normalizeDir);
|
|
151
|
+
for (const route of routes) {
|
|
152
|
+
const matchedRoot = normalizedSubRoots.find((root) => {
|
|
153
|
+
return route.startsWith(root + "/");
|
|
154
|
+
});
|
|
155
|
+
if (matchedRoot) {
|
|
156
|
+
const subPath = route.slice(matchedRoot.length + 1);
|
|
157
|
+
const list = subRouteMap.get(matchedRoot) ?? [];
|
|
158
|
+
list.push(subPath);
|
|
159
|
+
subRouteMap.set(matchedRoot, list);
|
|
160
|
+
} else mainRoutes.push(route);
|
|
161
|
+
}
|
|
162
|
+
const homePage = originPages[0]?.path;
|
|
163
|
+
if (mainRoutes.includes(homePage)) {
|
|
164
|
+
const idx = mainRoutes.indexOf(homePage);
|
|
165
|
+
mainRoutes.splice(idx, 1);
|
|
166
|
+
mainRoutes.unshift(homePage);
|
|
167
|
+
}
|
|
168
|
+
const pages = mergePages(mainRoutes, originPages);
|
|
169
|
+
const subPackages = normalizedSubRoots.map((root) => {
|
|
170
|
+
return {
|
|
171
|
+
root,
|
|
172
|
+
pages: mergePages(subRouteMap.get(root) ?? [], originSubPackages.find((s) => {
|
|
173
|
+
return normalizeDir(s.root) === root;
|
|
174
|
+
})?.pages ?? [])
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
const merged = {
|
|
178
|
+
...pagesJson,
|
|
179
|
+
pages,
|
|
180
|
+
subPackages
|
|
181
|
+
};
|
|
182
|
+
return {
|
|
183
|
+
merged,
|
|
184
|
+
changed: hasPagesJsonChanged(pagesJson, merged)
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function resolveDtsFilePath(srcRoot, dts) {
|
|
188
|
+
return path.isAbsolute(dts) ? dts : path.resolve(srcRoot, dts);
|
|
189
|
+
}
|
|
190
|
+
function buildRouteDts(routes) {
|
|
191
|
+
return [
|
|
192
|
+
"// Auto-generated by vite-plugin-uni-inject. Do not edit.",
|
|
193
|
+
"",
|
|
194
|
+
"/** 提取路径 */",
|
|
195
|
+
"export type ExtractPath<T extends string, Prefix extends string> = T extends `${Prefix}${infer P}` ? P : never;",
|
|
196
|
+
"",
|
|
197
|
+
"/** 路由路径 */",
|
|
198
|
+
"export type RoutePath =",
|
|
199
|
+
routes.map((route) => ` | "/${route}"`).join("\n") + ";",
|
|
200
|
+
""
|
|
201
|
+
].join("\n");
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* 自动补全 pages.json 插件
|
|
205
|
+
*/
|
|
206
|
+
function uniAutoPages(opts) {
|
|
207
|
+
const { dir = "pages", subPackages = [], dts = "uni-pages.d.ts" } = opts || {};
|
|
208
|
+
return {
|
|
209
|
+
name: "vite-plugin-uni-auto-pages",
|
|
210
|
+
enforce: "pre",
|
|
211
|
+
configResolved(config) {
|
|
212
|
+
const srcRoot = path.resolve(config.root, "src");
|
|
213
|
+
const pagesJsonPath = path.join(srcRoot, "pages.json");
|
|
214
|
+
const pagesJson = readPagesJson(pagesJsonPath);
|
|
215
|
+
if (!pagesJson) return;
|
|
216
|
+
const routes = collectFileRoutes(srcRoot, getScanDirs(dir, subPackages));
|
|
217
|
+
const { merged, changed } = getPagesByFileRoute(routes, pagesJson, subPackages);
|
|
218
|
+
if (changed) fs.writeFileSync(pagesJsonPath, JSON.stringify(merged, null, 2) + "\n");
|
|
219
|
+
if (dts) {
|
|
220
|
+
const dtsFilePath = resolveDtsFilePath(srcRoot, dts);
|
|
221
|
+
const dtsContent = buildRouteDts(routes);
|
|
222
|
+
fs.writeFileSync(dtsFilePath, dtsContent);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
export { uniAutoPages, uniInject };
|