@uni-helper/vite-plugin-uni-pages 0.4.9 → 0.5.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/dist/index.cjs DELETED
@@ -1,1401 +0,0 @@
1
- Object.defineProperties(exports, {
2
- __esModule: { value: true },
3
- [Symbol.toStringTag]: { value: "Module" }
4
- });
5
- //#region \0rolldown/runtime.js
6
- var __create = Object.create;
7
- var __defProp = Object.defineProperty;
8
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
- var __getOwnPropNames = Object.getOwnPropertyNames;
10
- var __getProtoOf = Object.getPrototypeOf;
11
- var __hasOwnProp = Object.prototype.hasOwnProperty;
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
14
- key = keys[i];
15
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
- get: ((k) => from[k]).bind(null, key),
17
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
- });
19
- }
20
- return to;
21
- };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
- value: mod,
24
- enumerable: true
25
- }) : target, mod));
26
- //#endregion
27
- let node_path = require("node:path");
28
- node_path = __toESM(node_path, 1);
29
- let node_process = require("node:process");
30
- node_process = __toESM(node_process, 1);
31
- let ast_kit = require("ast-kit");
32
- let chokidar = require("chokidar");
33
- chokidar = __toESM(chokidar, 1);
34
- let magic_string = require("magic-string");
35
- magic_string = __toESM(magic_string, 1);
36
- let vite = require("vite");
37
- let node_fs = require("node:fs");
38
- node_fs = __toESM(node_fs, 1);
39
- let _uni_helper_uni_env = require("@uni-helper/uni-env");
40
- let comment_json = require("comment-json");
41
- let debug = require("debug");
42
- debug = __toESM(debug, 1);
43
- let detect_indent = require("detect-indent");
44
- detect_indent = __toESM(detect_indent, 1);
45
- let detect_newline = require("detect-newline");
46
- detect_newline = __toESM(detect_newline, 1);
47
- let unconfig = require("unconfig");
48
- let write_file_atomic = require("write-file-atomic");
49
- write_file_atomic = __toESM(write_file_atomic, 1);
50
- let node_fs_promises = require("node:fs/promises");
51
- let proper_lockfile = require("proper-lockfile");
52
- proper_lockfile = __toESM(proper_lockfile, 1);
53
- let tinyglobby = require("tinyglobby");
54
- let node_module = require("node:module");
55
- let node_vm = require("node:vm");
56
- node_vm = __toESM(node_vm, 1);
57
- let _babel_generator = require("@babel/generator");
58
- _babel_generator = __toESM(_babel_generator, 1);
59
- let lodash_groupby = require("lodash.groupby");
60
- lodash_groupby = __toESM(lodash_groupby, 1);
61
- let typescript = require("typescript");
62
- typescript = __toESM(typescript, 1);
63
- let _babel_types = require("@babel/types");
64
- _babel_types = __toESM(_babel_types, 1);
65
- let _vue_compiler_sfc = require("@vue/compiler-sfc");
66
- //#region src/constant.ts
67
- /** Virtual module identifier for Vite module resolution */
68
- const MODULE_ID_VIRTUAL = "virtual:uni-pages";
69
- /** Resolved virtual module identifier with \0 prefix to avoid conflicts with other modules */
70
- const RESOLVED_MODULE_ID_VIRTUAL = `\0${MODULE_ID_VIRTUAL}`;
71
- /** Output filename, the uni-app page configuration file */
72
- const OUTPUT_NAME = "pages.json";
73
- /** Supported page file extensions */
74
- const FILE_EXTENSIONS = [
75
- "vue",
76
- "nvue",
77
- "uvue"
78
- ];
79
- //#endregion
80
- //#region ../../node_modules/.pnpm/@antfu+utils@9.3.0/node_modules/@antfu/utils/dist/index.mjs
81
- function slash(str) {
82
- return str.replace(/\\/g, "/");
83
- }
84
- //#endregion
85
- //#region src/declaration.ts
86
- /**
87
- * Generate TypeScript declaration file content
88
- * Provide type hints for uni-app navigation APIs (navigateTo, redirectTo, switchTab, reLaunch)
89
- * Ensure page paths get type checking at compile time
90
- *
91
- * @param ctx - Page context instance
92
- * @returns Declaration file code string
93
- */
94
- function getDeclaration(ctx) {
95
- const subPagesPath = ctx.subPageMetaData.map((sub) => {
96
- return sub.pages.map((v) => `"/${(0, vite.normalizePath)((0, node_path.join)(sub.root, v.path))}"`);
97
- }).flat();
98
- const tabsPagesPath = ctx.pagesGlobConfig?.tabBar?.list?.map((v) => {
99
- return `"/${v.pagePath}"`;
100
- }) ?? [];
101
- return `/* eslint-disable */
102
- /* prettier-ignore */
103
- /* oxlint-disable */
104
- /* oxfmt-ignore */
105
- // @ts-nocheck
106
- // Generated by vite-plugin-uni-pages
107
-
108
- type _LocationUrl =
109
- ${[...ctx.pageMetaData.filter((page) => !tabsPagesPath.includes(page.path)).map((v) => `"/${v.path}"`), ...subPagesPath].join(" |\n ")};
110
-
111
- interface NavigateToOptions {
112
- url: _LocationUrl;
113
- }
114
- interface RedirectToOptions extends NavigateToOptions {}
115
-
116
- interface SwitchTabOptions {
117
- ${tabsPagesPath.length ? `url: ${tabsPagesPath.join(" | ")}` : ""}
118
- }
119
-
120
- type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;
121
-
122
- declare interface Uni {
123
- navigateTo(options: UniNamespace.NavigateToOptions & NavigateToOptions): void;
124
- redirectTo(options: UniNamespace.RedirectToOptions & RedirectToOptions): void;
125
- switchTab(options: UniNamespace.SwitchTabOptions & SwitchTabOptions): void;
126
- reLaunch(options: UniNamespace.ReLaunchOptions & ReLaunchOptions): void;
127
- }
128
-
129
- declare module "virtual:uni-pages" {
130
- export type LocationUrl = _LocationUrl;
131
- }
132
- `;
133
- }
134
- /**
135
- * Write file, automatically create directories
136
- * @param filePath - File path
137
- * @param content - File content
138
- */
139
- async function writeFile(filePath, content) {
140
- await (0, node_fs_promises.mkdir)((0, node_path.dirname)(filePath), { recursive: true });
141
- return await (0, node_fs_promises.writeFile)(filePath, content, "utf-8");
142
- }
143
- /**
144
- * Write declaration file to disk
145
- * Only writes when content changes to avoid unnecessary file operations
146
- *
147
- * @param ctx - Page context instance
148
- * @param filepath - Declaration file output path
149
- */
150
- async function writeDeclaration(ctx, filepath) {
151
- const originalContent = (0, node_fs.existsSync)(filepath) ? await (0, node_fs_promises.readFile)(filepath, "utf-8") : "";
152
- const code = getDeclaration(ctx);
153
- if (!code) return;
154
- if (code !== originalContent) await writeFile(filepath, code);
155
- }
156
- //#endregion
157
- //#region src/utils.ts
158
- /**
159
- * Invalidate virtual module to trigger HMR update
160
- * When page configuration changes, the virtual module needs to be invalidated to regenerate content
161
- *
162
- * @param server - Vite dev server instance
163
- */
164
- function invalidatePagesModule(server) {
165
- const { moduleGraph } = server;
166
- const mods = moduleGraph.getModulesByFile(RESOLVED_MODULE_ID_VIRTUAL);
167
- if (mods) {
168
- const seen = /* @__PURE__ */ new Set();
169
- mods.forEach((mod) => {
170
- moduleGraph.invalidateModule(mod, seen);
171
- });
172
- }
173
- }
174
- /**
175
- * Debug logging utility
176
- * Uses debug package for leveled log output
177
- * Enable via environment variable DEBUG=vite-plugin-uni-pages:*
178
- */
179
- const debug$1 = {
180
- /** HMR related logs */
181
- hmr: (0, debug.default)("vite-plugin-uni-pages:hmr"),
182
- /** Configuration options related logs */
183
- options: (0, debug.default)("vite-plugin-uni-pages:options"),
184
- /** Main package page scanning logs */
185
- pages: (0, debug.default)("vite-plugin-uni-pages:pages"),
186
- /** Sub-package page scanning logs */
187
- subPages: (0, debug.default)("vite-plugin-uni-pages:subPages"),
188
- /** Error logs */
189
- error: (0, debug.default)("vite-plugin-uni-pages:error"),
190
- /** Cache related logs */
191
- cache: (0, debug.default)("vite-plugin-uni-pages:cache"),
192
- /** Declaration file generation logs */
193
- declaration: (0, debug.default)("vite-plugin-uni-pages:declaration"),
194
- /** definePage macro parsing logs */
195
- definePage: (0, debug.default)("vite-plugin-uni-pages:definePage")
196
- };
197
- /**
198
- * Convert file extensions array to glob pattern
199
- * @param extensions - File extensions array
200
- * @returns Glob pattern string
201
- */
202
- function extsToGlob(extensions) {
203
- return extensions.length > 1 ? `{${extensions.join(",")}}` : extensions[0] || "";
204
- }
205
- /**
206
- * Check if file is a target page file
207
- * Determine if the file extension is in the supported page file types list
208
- *
209
- * @param path - File path
210
- * @returns Whether it's a target file
211
- */
212
- function isTargetFile(path) {
213
- const ext = path.split(".").pop();
214
- return FILE_EXTENSIONS.includes(ext);
215
- }
216
- /**
217
- * merge page meta data array by path and assign style
218
- * @param pageMetaData page meta data array
219
- * TODO: support merge middleware
220
- */
221
- function mergePageMetaDataArray(pageMetaData) {
222
- const pageMetaDataObj = (0, lodash_groupby.default)(pageMetaData, "path");
223
- const result = [];
224
- for (const path in pageMetaDataObj) {
225
- const _pageMetaData = pageMetaDataObj[path];
226
- const options = _pageMetaData[0];
227
- for (const page of _pageMetaData) {
228
- options.style = Object.assign(options.style ?? {}, page.style ?? {});
229
- Object.assign(options, page);
230
- }
231
- result.push(options);
232
- }
233
- return result;
234
- }
235
- /**
236
- * Convert TypeScript / JavaScript script code to object/function
237
- *
238
- * @param options - Configuration required for script execution
239
- * @param options.imports - List of module import statements to include
240
- * @param options.code - TypeScript code content to execute
241
- * @param options.filename - Script filename for error location and context
242
- * @returns Script execution result, if export is a function then execute and return its return value
243
- */
244
- async function parseCode(options) {
245
- const { imports = [], code, filename } = options;
246
- let jsCode = "";
247
- try {
248
- const tmpCode = `${imports.join("\n")}\n export default ${code}`;
249
- jsCode = typescript.transpileModule(tmpCode, {
250
- compilerOptions: {
251
- module: typescript.ModuleKind.CommonJS,
252
- target: typescript.ScriptTarget.ES2022,
253
- noEmit: true,
254
- strict: false,
255
- noImplicitAny: false,
256
- strictNullChecks: false,
257
- strictFunctionTypes: false,
258
- strictBindCallApply: false,
259
- strictPropertyInitialization: false,
260
- noImplicitThis: false,
261
- alwaysStrict: false,
262
- allowJs: true,
263
- checkJs: false,
264
- skipLibCheck: true,
265
- esModuleInterop: true,
266
- removeComments: true
267
- },
268
- jsDocParsingMode: typescript.JSDocParsingMode.ParseNone
269
- }).outputText;
270
- const dir = node_path.default.dirname(filename);
271
- const vmContext = {
272
- module: {},
273
- exports: {},
274
- __filename: filename,
275
- __dirname: dir,
276
- require: (0, node_module.createRequire)(dir),
277
- import: (id) => import(id),
278
- setTimeout,
279
- clearTimeout,
280
- setInterval,
281
- clearInterval,
282
- setImmediate,
283
- clearImmediate,
284
- console,
285
- URL,
286
- URLSearchParams,
287
- performance,
288
- global: globalThis,
289
- globalThis
290
- };
291
- await new node_vm.default.Script(jsCode, { filename }).runInNewContext(vmContext, { timeout: 1e3 });
292
- return vmContext.exports.default || vmContext.exports;
293
- } catch (error) {
294
- throw new Error(`EXEC SCRIPT FAIL IN ${filename}: ${error.message} \n\n${jsCode}\n\n`);
295
- }
296
- }
297
- /**
298
- * Async sleep function
299
- * @param ms - Sleep duration in milliseconds
300
- * @returns Promise object
301
- */
302
- function sleep(ms) {
303
- return new Promise((resolve) => setTimeout(resolve, ms));
304
- }
305
- /**
306
- * Get default export of a module
307
- * Compatible with CommonJS and ES Module export styles
308
- *
309
- * @param expr - Module object
310
- * @returns Default export value
311
- */
312
- function getDefaultExport(expr) {
313
- return expr.default === void 0 ? expr : expr.default;
314
- }
315
- /** Babel code generator for converting AST back to code */
316
- const babelGenerate = getDefaultExport(_babel_generator.default);
317
- //#endregion
318
- //#region src/files.ts
319
- /**
320
- * Resolves the files that are valid pages for the given context.
321
- */
322
- function getPageFiles(path, options) {
323
- const { exclude } = options;
324
- return (0, tinyglobby.globSync)(`**/*.${extsToGlob(FILE_EXTENSIONS)}`, {
325
- ignore: exclude,
326
- onlyFiles: true,
327
- cwd: path
328
- });
329
- }
330
- /**
331
- * Check the pages.json file at the specified path, create an empty pages.json file if it doesn't exist or is not a valid file
332
- * @param path - File path to check
333
- * @returns boolean - Whether the operation was successful
334
- */
335
- function checkPagesJsonFileSync(path) {
336
- /**
337
- * Create an empty pages.json file
338
- * @param path - File path
339
- * @returns boolean - Whether the creation was successful
340
- */
341
- const createEmptyFile = (path) => {
342
- try {
343
- node_fs.default.writeFileSync(path, JSON.stringify({ pages: [{ path: "" }] }, null, 2), { encoding: "utf-8" });
344
- return true;
345
- } catch {
346
- return false;
347
- }
348
- };
349
- /**
350
- * Delete the file at the specified path
351
- * @param path - File path
352
- * @returns boolean - Whether the deletion was successful
353
- */
354
- const unlinkFile = (path) => {
355
- try {
356
- node_fs.default.unlinkSync(path);
357
- return true;
358
- } catch {
359
- return false;
360
- }
361
- };
362
- try {
363
- try {
364
- node_fs.default.accessSync(path, node_fs.default.constants.F_OK);
365
- } catch {
366
- return createEmptyFile(path);
367
- }
368
- if (!node_fs.default.statSync(path).isFile()) {
369
- if (!unlinkFile(path)) return false;
370
- return createEmptyFile(path);
371
- }
372
- try {
373
- node_fs.default.accessSync(path, node_fs.default.constants.R_OK | node_fs.default.constants.W_OK);
374
- return true;
375
- } catch {
376
- if (!unlinkFile(path)) return false;
377
- return createEmptyFile(path);
378
- }
379
- } catch {
380
- return createEmptyFile(path);
381
- }
382
- }
383
- /**
384
- * Run a task while holding an exclusive file lock.
385
- *
386
- * Unlike {@link writeFileWithLock}, this protects the whole read-modify-write
387
- * critical section. The lock is held from the moment `task` starts until it
388
- * resolves, so concurrent processes cannot observe or overwrite a half-written
389
- * state. This is required by pages.json generation, where the new content
390
- * depends on the current content (other platforms' `#ifdef` blocks).
391
- *
392
- * @param path - File path used as the lock target
393
- * @param task - Async work to run inside the lock; return value is forwarded
394
- * @param retry - Number of retries when lock acquisition fails, defaults to 3
395
- * @returns The value resolved by `task`, or `undefined` if the lock could not be acquired
396
- */
397
- async function withFileLock(path, task, retry = 3) {
398
- if (retry <= 0) {
399
- debug$1.error(`${path} Failed to acquire file lock, task aborted`);
400
- return;
401
- }
402
- let release;
403
- try {
404
- try {
405
- release = await proper_lockfile.default.lock(path, { realpath: false });
406
- } catch {
407
- await sleep(500);
408
- return withFileLock(path, task, retry - 1);
409
- }
410
- return await task();
411
- } finally {
412
- if (release) await release();
413
- }
414
- }
415
- /**
416
- * Safely write file using file lock
417
- * Avoid data corruption caused by concurrent writes through file lock
418
- * Use atomic write to ensure file write integrity
419
- *
420
- * Note: this only makes the *write* atomic. Callers that need to read the
421
- * current content before computing the new one must use {@link withFileLock}
422
- * so the read and write belong to the same critical section.
423
- *
424
- * @param path - File path
425
- * @param content - File content
426
- * @param retry - Number of retries when lock acquisition fails, defaults to 3
427
- */
428
- async function writeFileWithLock(path, content, retry = 3) {
429
- return withFileLock(path, async () => {
430
- await (0, write_file_atomic.default)(path, content);
431
- }, retry);
432
- }
433
- //#endregion
434
- //#region src/options.ts
435
- /**
436
- * Resolve user configuration options
437
- * Merge user-provided configuration with default values and handle path resolution
438
- *
439
- * @param userOptions - User configuration options
440
- * @param viteRoot - Vite project root directory
441
- * @returns Resolved configuration options
442
- */
443
- function resolveOptions(userOptions, viteRoot = node_process.default.cwd()) {
444
- const { dts = true, configSource = "pages.config", homePage = ["pages/index", "pages/index/index"], mergePages = true, dir = "src/pages", subPackages = [], outDir = "src", exclude = [
445
- "node_modules",
446
- ".git",
447
- "**/__*__/**"
448
- ], minify = false, debug = false, onBeforeLoadUserConfig = () => {}, onAfterLoadUserConfig = () => {}, onBeforeScanPages = () => {}, onAfterScanPages = () => {}, onBeforeMergePageMetaData = () => {}, onAfterMergePageMetaData = () => {}, onBeforeWriteFile = () => {}, onAfterWriteFile = () => {} } = userOptions;
449
- const root = viteRoot || slash(node_process.default.env.VITE_ROOT_DIR || node_process.default.cwd());
450
- const resolvedDirs = resolvePageDirs(dir, root, exclude);
451
- const subPackageRootMap = /* @__PURE__ */ new Map();
452
- const resolvedSubDirs = [];
453
- for (const sub of subPackages) if (typeof sub === "string") resolvedSubDirs.push(slash(sub));
454
- else {
455
- const dirPath = slash(sub.dir);
456
- resolvedSubDirs.push(dirPath);
457
- subPackageRootMap.set(dirPath, sub.root);
458
- }
459
- const resolvedHomePage = typeof homePage === "string" ? [homePage] : homePage;
460
- const resolvedConfigSource = typeof configSource === "string" ? [{ files: configSource }] : configSource;
461
- return {
462
- dts: !dts ? false : typeof dts === "string" ? dts : (0, node_path.resolve)(viteRoot, "uni-pages.d.ts"),
463
- configSource: Array.isArray(resolvedConfigSource) ? resolvedConfigSource : [resolvedConfigSource],
464
- homePage: resolvedHomePage,
465
- mergePages,
466
- dirs: resolvedDirs,
467
- subPackages: resolvedSubDirs,
468
- subPackageRootMap,
469
- outDir,
470
- exclude,
471
- root,
472
- minify,
473
- debug,
474
- onBeforeLoadUserConfig,
475
- onAfterLoadUserConfig,
476
- onBeforeScanPages,
477
- onAfterScanPages,
478
- onBeforeMergePageMetaData,
479
- onAfterMergePageMetaData,
480
- onBeforeWriteFile,
481
- onAfterWriteFile
482
- };
483
- }
484
- /**
485
- * Resolves the page dirs for its for its given globs
486
- */
487
- function resolvePageDirs(dir, root, exclude) {
488
- return (0, tinyglobby.globSync)(slash(dir), {
489
- ignore: exclude,
490
- onlyDirectories: true,
491
- expandDirectories: false,
492
- dot: true,
493
- cwd: root
494
- });
495
- }
496
- //#endregion
497
- //#region src/page.ts
498
- /**
499
- * Page class representing a Vue page file
500
- *
501
- * Responsibilities:
502
- * 1. Read page file content
503
- * 2. Parse page metadata defined by definePage macro
504
- * 3. Provide tabBar configuration information
505
- * 4. Track page file change status
506
- */
507
- var Page = class {
508
- /** Page context instance */
509
- ctx;
510
- /** Page path information containing relative and absolute paths */
511
- path;
512
- /** Page URI used for pages.json path field */
513
- uri;
514
- /** Whether the page has changed, used for incremental update judgment */
515
- changed = true;
516
- /** Raw JSON string of page metadata for change detection */
517
- raw = "";
518
- /** Parsed page metadata */
519
- meta;
520
- /**
521
- * Create a page instance
522
- * @param ctx - Page context instance
523
- * @param path - Page path information
524
- */
525
- constructor(ctx, path) {
526
- this.ctx = ctx;
527
- this.path = path;
528
- this.uri = (0, vite.normalizePath)(path.relativePath.replace((0, node_path.extname)(path.relativePath), ""));
529
- }
530
- /**
531
- * Get page metadata
532
- * Parse configuration defined by definePage macro and return metadata for pages.json
533
- *
534
- * @param forceUpdate - Whether to force update, ignoring cache
535
- * @returns Page metadata object
536
- */
537
- async getPageMeta(forceUpdate = false) {
538
- if (forceUpdate || !this.meta) await this.read();
539
- const { path, tabBar: _, ...others } = this.meta || {};
540
- return {
541
- path: path ?? this.uri,
542
- ...others
543
- };
544
- }
545
- /**
546
- * Get page tabBar configuration
547
- * Extract tabBar related configuration from definePage macro
548
- *
549
- * @param forceUpdate - Whether to force update, ignoring cache
550
- * @returns tabBar configuration object, or undefined if page doesn't define tabBar
551
- */
552
- async getTabBar(forceUpdate = false) {
553
- if (forceUpdate || !this.meta) await this.read();
554
- const { tabBar } = this.meta || {};
555
- if (tabBar === void 0) return;
556
- return {
557
- ...tabBar,
558
- pagePath: tabBar.pagePath || this.uri,
559
- index: tabBar.index ?? 0
560
- };
561
- }
562
- /**
563
- * Check if the page has changed
564
- * @returns Whether the page has changed
565
- */
566
- hasChanged() {
567
- return this.changed;
568
- }
569
- /**
570
- * Read page file and parse metadata
571
- * Extract configuration defined by definePage macro from Vue SFC
572
- */
573
- async read() {
574
- let meta;
575
- try {
576
- meta = await this.readPageMetaFromFile();
577
- } catch (err) {
578
- debug$1.error(err);
579
- return;
580
- }
581
- let raw = "";
582
- try {
583
- raw = JSON.stringify(meta);
584
- } catch {}
585
- this.changed = this.raw !== raw;
586
- this.meta = meta;
587
- this.raw = raw;
588
- }
589
- async readPageMetaFromFile() {
590
- try {
591
- const meta = await tryPageMetaFromMacro(parseSFC(await node_fs.default.promises.readFile(this.path.absolutePath, { encoding: "utf-8" }), { filename: this.path.absolutePath }));
592
- if (meta) return meta;
593
- return { type: "page" };
594
- } catch (err) {
595
- throw new Error(`Read page meta fail in ${this.path.relativePath}\n${err.message}`);
596
- }
597
- }
598
- };
599
- /**
600
- * Parse Vue Single File Component (SFC)
601
- * Compatible with different versions of @vue/compiler-sfc
602
- *
603
- * @param code - Vue SFC source code
604
- * @param options - Parse options
605
- * @returns SFC descriptor object
606
- */
607
- function parseSFC(code, options) {
608
- return (0, _vue_compiler_sfc.parse)(code, {
609
- pad: "space",
610
- ...options
611
- }).descriptor || (0, _vue_compiler_sfc.parse)({
612
- source: code,
613
- ...options
614
- });
615
- }
616
- /**
617
- * Try to extract page metadata defined by definePage macro from SFC
618
- * Support using definePage in script setup or regular script
619
- *
620
- * @param sfc - Vue SFC descriptor
621
- * @returns Page metadata object, or undefined if definePage is not found
622
- */
623
- async function tryPageMetaFromMacro(sfc) {
624
- const sfcScript = sfc.scriptSetup || sfc.script;
625
- if (!sfcScript) return;
626
- const ast = (0, ast_kit.babelParse)(sfcScript.content, sfcScript.lang || "js", { plugins: [["importAttributes", { deprecatedAssertSyntax: true }]] });
627
- const macro = findMacro(ast.body, sfc.filename);
628
- if (macro) {
629
- const imports = findImports(ast.body).filter((imp) => !!imp.specifiers.length).map((imp) => babelGenerate(imp).code);
630
- const [macroOption] = macro.arguments;
631
- const code = babelGenerate(macroOption).code;
632
- const parsed = await parseCode({
633
- imports,
634
- code,
635
- filename: sfc.filename
636
- });
637
- return {
638
- type: "page",
639
- ...typeof parsed === "function" ? await Promise.resolve(parsed()) : await Promise.resolve(parsed)
640
- };
641
- }
642
- }
643
- /**
644
- * Find definePage macro call in AST
645
- * Support function expressions, arrow functions and object expressions as arguments
646
- *
647
- * @param stmts - AST statement array
648
- * @param filename - Filename for error reporting
649
- * @returns definePage call expression, or undefined if not found
650
- */
651
- function findMacro(stmts, filename) {
652
- let macro;
653
- for (const stmt of stmts) {
654
- let node = stmt;
655
- if (stmt.type === "ExpressionStatement") node = stmt.expression;
656
- if ((0, ast_kit.isCallOf)(node, "definePage")) {
657
- macro = node;
658
- break;
659
- }
660
- }
661
- if (!macro) return;
662
- const [opt] = macro.arguments;
663
- if (opt && !_babel_types.isFunctionExpression(opt) && !_babel_types.isArrowFunctionExpression(opt) && !_babel_types.isObjectExpression(opt)) {
664
- debug$1.definePage(`definePage() 参数仅支持函数或对象:${filename}`);
665
- return;
666
- }
667
- return macro;
668
- }
669
- /**
670
- * Extract all import declarations from AST
671
- * Used to provide necessary imports when executing definePage arguments
672
- *
673
- * @param stmts - AST statement array
674
- * @returns Import declaration array
675
- */
676
- function findImports(stmts) {
677
- const imports = [];
678
- for (const stmt of stmts) if (_babel_types.isImportDeclaration(stmt)) imports.push(stmt);
679
- return imports;
680
- }
681
- //#endregion
682
- //#region src/context.ts
683
- /**
684
- * Page context class responsible for page scanning, config loading, page metadata merging and pages.json generation
685
- *
686
- * Core responsibilities:
687
- * 1. Scan page directories and collect page files
688
- * 2. Load user configuration files (pages.config.ts, etc.)
689
- * 3. Parse page metadata (definePage macro)
690
- * 4. Merge page configurations and generate pages.json
691
- * 5. Provide virtual module and HMR support
692
- */
693
- var PageContext = class {
694
- _server;
695
- /** Parsed configuration object from user config file (pages.config.ts) */
696
- pagesGlobConfig;
697
- /** Source path list of user configuration files */
698
- pagesConfigSourcePaths = [];
699
- /** Main package page map, key is the absolute path of the page file */
700
- pages = /* @__PURE__ */ new Map();
701
- /** Sub-package page map, key is the sub-package root directory, value is the page map under that sub-package */
702
- subPages = /* @__PURE__ */ new Map();
703
- /** Main package page metadata array for generating pages.json pages field */
704
- pageMetaData = [];
705
- /** Sub-package page metadata array for generating pages.json subPackages field */
706
- subPageMetaData = [];
707
- /** Generated pages.json file path */
708
- resolvedPagesJSONPath = "";
709
- resolvedPagesJSONIndent;
710
- resolvedPagesJSONNewline;
711
- resolvedPagesJSONEofNewline;
712
- /** Original options passed by the user */
713
- rawOptions;
714
- /** Project root directory */
715
- root;
716
- /** Resolved configuration options */
717
- options;
718
- logger;
719
- /** Whether to work with vite-plugin-uni-platform plugin */
720
- withUniPlatform = false;
721
- /** Cached previous pages.json content to avoid redundant writes */
722
- lastPagesJson = "";
723
- /**
724
- * Create a PageContext instance
725
- * @param userOptions - User configuration options
726
- * @param viteRoot - Vite project root directory, defaults to current working directory
727
- */
728
- constructor(userOptions, viteRoot = node_process.default.cwd()) {
729
- this.rawOptions = userOptions;
730
- this.root = slash(viteRoot);
731
- debug$1.options("root", this.root);
732
- this.options = resolveOptions(userOptions, this.root);
733
- const debugOption = this.options.debug;
734
- if (debugOption) {
735
- const prefix = "vite-plugin-uni-pages:";
736
- const suffix = typeof debugOption === "boolean" ? "*" : debugOption;
737
- debug.default.enable(`${prefix}${suffix}`);
738
- }
739
- this.resolvedPagesJSONPath = node_path.default.join(this.root, this.options.outDir, OUTPUT_NAME);
740
- debug$1.options(this.options);
741
- }
742
- /**
743
- * Set the Vite logger
744
- * @param logger - Vite logger instance
745
- */
746
- setLogger(logger) {
747
- this.logger = logger;
748
- }
749
- /**
750
- * Load user page configuration file (e.g. pages.config.ts)
751
- * Uses unconfig to load configuration, supporting multiple config file formats
752
- */
753
- async loadUserPagesConfig() {
754
- const configSource = this.options.configSource;
755
- const { config, sources } = await (0, unconfig.loadConfig)({
756
- cwd: this.root,
757
- sources: configSource,
758
- defaults: {}
759
- });
760
- this.pagesGlobConfig = config.default || config;
761
- this.pagesConfigSourcePaths = sources;
762
- debug$1.options(this.pagesGlobConfig);
763
- }
764
- /**
765
- * Scan main package page directories and collect all page file paths
766
- * Scan corresponding directories based on the configured dirs option
767
- */
768
- async scanPages() {
769
- const paths = this.options.dirs.map((dir) => {
770
- return {
771
- dir,
772
- files: getPagePaths(dir, this.options)
773
- };
774
- }).map((page) => page.files).flat();
775
- debug$1.pages(paths);
776
- const pages = /* @__PURE__ */ new Map();
777
- for (const path of paths) {
778
- const page = this.pages.get(path.absolutePath) || new Page(this, path);
779
- pages.set(path.absolutePath, page);
780
- }
781
- this.pages = pages;
782
- }
783
- /**
784
- * Scan sub-package page directories and collect all sub-package page file paths
785
- * Scan corresponding directories based on the configured subPackages option
786
- */
787
- async scanSubPages() {
788
- const paths = {};
789
- const subPages = /* @__PURE__ */ new Map();
790
- for (const dir of this.options.subPackages) {
791
- const pagePaths = getPagePaths(dir, this.options);
792
- paths[dir] = pagePaths;
793
- const pages = /* @__PURE__ */ new Map();
794
- for (const path of pagePaths) {
795
- const page = this.subPages.get(dir)?.get(path.absolutePath) || new Page(this, path);
796
- pages.set(path.absolutePath, page);
797
- }
798
- subPages.set(dir, pages);
799
- }
800
- debug$1.subPages(JSON.stringify(paths, null, 2));
801
- this.subPages = subPages;
802
- }
803
- /**
804
- * Set up Vite dev server for HMR and file watching
805
- * @param server - Vite dev server instance
806
- */
807
- setupViteServer(server) {
808
- if (this._server === server) return;
809
- this._server = server;
810
- this.setupWatcher(server.watcher);
811
- }
812
- /**
813
- * Set up file watcher to monitor page file and config file changes
814
- * Automatically update pages.json when page files or config files change
815
- * @param watcher - chokidar file watcher instance
816
- */
817
- async setupWatcher(watcher) {
818
- watcher.add(this.pagesConfigSourcePaths);
819
- const targetDirs = [...this.options.dirs, ...this.options.subPackages].map((v) => slash(node_path.default.resolve(this.root, v)));
820
- const isInTargetDirs = (filePath) => targetDirs.some((v) => slash(node_path.default.resolve(this.root, filePath)).startsWith(v));
821
- watcher.on("add", async (path) => {
822
- path = slash(path);
823
- if (!isTargetFile(path)) return;
824
- if (!isInTargetDirs(path)) return;
825
- debug$1.pages(`File added: ${path}`);
826
- if (await this.updatePagesJSON()) this.onUpdate();
827
- });
828
- watcher.on("change", async (path) => {
829
- path = slash(path);
830
- if (!isTargetFile(path)) return;
831
- if (!isInTargetDirs(path)) return;
832
- debug$1.pages(`File changed: ${path}`);
833
- debug$1.pages(targetDirs);
834
- debug$1.pages(isInTargetDirs(path));
835
- if (await this.updatePagesJSON(path)) this.onUpdate();
836
- });
837
- watcher.on("change", async (path) => {
838
- if (this.pagesConfigSourcePaths.includes(path)) {
839
- debug$1.pages(`Config source changed: ${path}`);
840
- if (await this.updatePagesJSON()) this.onUpdate();
841
- }
842
- });
843
- watcher.on("unlink", async (path) => {
844
- path = slash(path);
845
- if (!isTargetFile(path)) return;
846
- if (!isInTargetDirs(path)) return;
847
- debug$1.pages(`File removed: ${path}`);
848
- if (await this.updatePagesJSON()) this.onUpdate();
849
- });
850
- }
851
- /**
852
- * Page update callback, triggered when page files or configuration changes
853
- * Responsible for invalidating virtual modules and notifying the browser to do a full reload
854
- */
855
- onUpdate() {
856
- if (!this._server) return;
857
- invalidatePagesModule(this._server);
858
- debug$1.hmr("Reload generated pages.");
859
- this._server.ws.send({ type: "full-reload" });
860
- }
861
- /**
862
- * parse pages rules && set page type
863
- * @param pages page path array
864
- * @param type page type
865
- * @param overrides custom page config
866
- * @returns pages rules
867
- */
868
- async parsePages(pages, type, overrides) {
869
- const jobs = [];
870
- for (const [_, page] of pages) jobs.push(page.getPageMeta());
871
- const generatedPageMetaData = await Promise.all(jobs);
872
- const customPageMetaData = overrides || [];
873
- const result = customPageMetaData.length ? mergePageMetaDataArray(generatedPageMetaData.concat(customPageMetaData)) : generatedPageMetaData;
874
- const parseMeta = Array.from(result.reduce((map, page) => {
875
- map.set(page.path, page);
876
- return map;
877
- }, /* @__PURE__ */ new Map()).values());
878
- return type === "main" ? this.setHomePage(parseMeta) : parseMeta;
879
- }
880
- /**
881
- * set home page
882
- * @param result pages rules array
883
- * @returns pages rules
884
- */
885
- setHomePage(result) {
886
- if (!result.some(({ type }) => type === "home")) {
887
- const basePath = slash(node_path.default.join(this.options.root, this.options.outDir));
888
- const resolvedHomePages = this.options.homePage.map((v) => {
889
- return slash(node_path.default.relative(basePath, slash(node_path.default.resolve(basePath, v))));
890
- });
891
- const matchHomePage = (itemPath, configPath) => {
892
- if (itemPath === configPath) return true;
893
- const normalizedItem = itemPath.replace(/\\/g, "/");
894
- const normalizedConfig = configPath.replace(/\\/g, "/");
895
- return normalizedItem.endsWith(`/${normalizedConfig}`);
896
- };
897
- if (!result.some((item) => {
898
- const isFound = resolvedHomePages.some((expectedPath) => matchHomePage(item.path, expectedPath));
899
- if (isFound) item.type = "home";
900
- return isFound;
901
- })) this.logger?.warn("No home page found, check the configuration of pages.config.ts, or add the `homePage` option to UniPages in vite.config.js, or add `definePage({ type: \"home\" })` in your vue page.", { timestamp: true });
902
- }
903
- result.sort((page) => page.type === "home" ? -1 : 0);
904
- return result;
905
- }
906
- /**
907
- * Merge main package page metadata
908
- * Filter out pages belonging to sub-packages, then parse page metadata and merge user configuration
909
- */
910
- async mergePageMetaData() {
911
- const subPageAbsolutePaths = Array.from(this.subPages.values()).flatMap((v) => Array.from(v.keys()));
912
- for (const subPageAbsolutePath of subPageAbsolutePaths) this.pages.delete(subPageAbsolutePath);
913
- const pageMetaData = await this.parsePages(this.pages, "main", this.pagesGlobConfig?.pages);
914
- this.pageMetaData = pageMetaData;
915
- debug$1.pages(this.pageMetaData);
916
- }
917
- /**
918
- * Merge sub-package page metadata
919
- * Parse page metadata for each sub-package and handle sub-package configuration inheritance
920
- * Preserves sub-package level properties like plugins from user config
921
- */
922
- async mergeSubPageMetaData() {
923
- const subPageMaps = {};
924
- const subPlugins = {};
925
- const subPackages = this.pagesGlobConfig?.subPackages || [];
926
- for (const [dir, pages] of this.subPages) {
927
- const basePath = slash(node_path.default.join(this.options.root, this.options.outDir));
928
- const root = this.options.subPackageRootMap.get(dir) ?? slash(node_path.default.relative(basePath, node_path.default.join(this.options.root, dir)));
929
- const globPackage = subPackages?.find((v) => v.root === root);
930
- subPageMaps[root] = await this.parsePages(pages, "sub", globPackage?.pages);
931
- subPageMaps[root] = subPageMaps[root].map((page) => ({
932
- ...page,
933
- path: slash(node_path.default.relative(root, page.path))
934
- }));
935
- if (globPackage?.plugins) subPlugins[root] = globPackage.plugins;
936
- }
937
- for (const { root, pages, plugins } of subPackages) if (root && !subPageMaps[root]) {
938
- subPageMaps[root] = pages || [];
939
- if (plugins) subPlugins[root] = plugins;
940
- }
941
- const subPageMetaData = Object.keys(subPageMaps).map((root) => ({
942
- root,
943
- pages: subPageMaps[root],
944
- ...subPlugins[root] && { plugins: subPlugins[root] }
945
- })).filter((meta) => meta.pages.length > 0);
946
- this.subPageMetaData = subPageMetaData;
947
- debug$1.subPages(this.subPageMetaData);
948
- }
949
- /**
950
- * Update pages.json file
951
- * This is the core method responsible for coordinating the entire page configuration generation flow:
952
- * 1. Check file changes (if filepath is specified)
953
- * 2. Load user configuration
954
- * 3. Scan page files
955
- * 4. Merge page metadata
956
- * 5. Generate and write pages.json
957
- * @param filepath - Changed file path for incremental update judgment
958
- * @returns Whether pages.json was successfully updated
959
- */
960
- async updatePagesJSON(filepath) {
961
- if (filepath) {
962
- let page = this.pages.get(filepath);
963
- if (!page) {
964
- let subPage;
965
- for (const [_, pages] of this.subPages) {
966
- subPage = pages.get(filepath);
967
- if (subPage) break;
968
- }
969
- page = subPage;
970
- }
971
- if (page) {
972
- await page.read();
973
- if (!page.hasChanged()) {
974
- debug$1.cache(`The page meta on page ${filepath} did not send any changes, skipping`);
975
- return false;
976
- }
977
- }
978
- }
979
- checkPagesJsonFileSync(this.resolvedPagesJSONPath);
980
- this.options.onBeforeLoadUserConfig(this);
981
- await this.loadUserPagesConfig();
982
- this.options.onAfterLoadUserConfig(this);
983
- if (this.options.mergePages) {
984
- this.options.onBeforeScanPages(this);
985
- await this.scanPages();
986
- await this.scanSubPages();
987
- this.options.onAfterScanPages(this);
988
- }
989
- this.options.onBeforeMergePageMetaData(this);
990
- await this.mergePageMetaData();
991
- await this.mergeSubPageMetaData();
992
- this.options.onAfterMergePageMetaData(this);
993
- const pagesMap = /* @__PURE__ */ new Map();
994
- (this.withUniPlatform ? this.pageMetaData.filter((v) => !/\..*$/.test(v.path) || v.path.includes(_uni_helper_uni_env.platform)).map((v) => {
995
- v.path = v.path.replace(/\..*$/, "");
996
- return v;
997
- }) : this.pageMetaData).forEach((v) => pagesMap.set(v.path, v));
998
- this.pageMetaData = [...pagesMap.values()];
999
- this.options.onBeforeWriteFile(this);
1000
- const updated = await withFileLock(this.resolvedPagesJSONPath, async () => {
1001
- const pagesJson = (0, comment_json.stringify)(await this.genratePagesJSON(), null, this.options.minify ? void 0 : await this.getIndent()) + (await this.getEndOfLine() ? await this.getNewline() : "");
1002
- if (this.lastPagesJson === pagesJson) {
1003
- debug$1.pages("PagesJson Not have change");
1004
- return false;
1005
- }
1006
- await (0, write_file_atomic.default)(this.resolvedPagesJSONPath, pagesJson);
1007
- this.lastPagesJson = pagesJson;
1008
- return true;
1009
- });
1010
- this.generateDeclaration();
1011
- if (updated) this.options.onAfterWriteFile(this);
1012
- return updated ?? false;
1013
- }
1014
- /**
1015
- * Generate virtual module content
1016
- * Returns JavaScript code containing pages and subPackages exports
1017
- * @returns Virtual module code string
1018
- */
1019
- virtualModule() {
1020
- return [`export const pages = ${this.resolveRoutes()};`, `export const subPackages = ${this.resolveSubRoutes()};`].join("\n");
1021
- }
1022
- /**
1023
- * Resolve main package route data to JSON string
1024
- * @returns JSON string of main package page metadata
1025
- */
1026
- resolveRoutes() {
1027
- return (0, comment_json.stringify)(this.pageMetaData, null, 2);
1028
- }
1029
- /**
1030
- * Resolve sub-package route data to JSON string
1031
- * @returns JSON string of sub-package page metadata
1032
- */
1033
- resolveSubRoutes() {
1034
- return (0, comment_json.stringify)(this.subPageMetaData, null, 2);
1035
- }
1036
- /**
1037
- * Resolve tabBar configuration
1038
- * Merges page-defined tabBar items with config-defined tabBar
1039
- * @returns Merged tabBar configuration object, or undefined if no tabBar exists
1040
- */
1041
- async resolveTabBar() {
1042
- const tabBarItems = [];
1043
- for (const [_, page] of this.pages) {
1044
- const tabbar = await page.getTabBar();
1045
- if (tabbar) tabBarItems.push(tabbar);
1046
- }
1047
- if (tabBarItems.length === 0) return this.pagesGlobConfig?.tabBar;
1048
- const tabBar = {
1049
- ...this.pagesGlobConfig?.tabBar,
1050
- list: this.pagesGlobConfig?.tabBar?.list || []
1051
- };
1052
- const pagePaths = /* @__PURE__ */ new Map();
1053
- for (const item of tabBar.list) pagePaths.set(item.pagePath, true);
1054
- tabBarItems.sort((a, b) => a.index - b.index);
1055
- for (const item of tabBarItems) if (!pagePaths.has(item.pagePath)) {
1056
- const { index: _, ...tabbar } = item;
1057
- tabBar.list.push(tabbar);
1058
- }
1059
- return tabBar;
1060
- }
1061
- /**
1062
- * Generate TypeScript declaration file
1063
- * Generate type definitions for page paths to provide type hints during navigation
1064
- */
1065
- generateDeclaration() {
1066
- if (!this.options.dts) return;
1067
- debug$1.declaration("generating");
1068
- return writeDeclaration(this, this.options.dts);
1069
- }
1070
- async genratePagesJSON() {
1071
- const { pages: oldPages, subPackages: oldSubPackages, tabBar: oldTabBar } = (0, comment_json.parse)(await node_fs.default.promises.readFile(this.resolvedPagesJSONPath, { encoding: "utf-8" }).catch(() => "") || "{}");
1072
- const { pages: _pages, subPackages: _subPackages, tabBar: _tabBar, ...pageJson } = this.pagesGlobConfig || {};
1073
- const currentPlatform = _uni_helper_uni_env.platform.toUpperCase();
1074
- pageJson.pages = mergePlatformItems(oldPages, currentPlatform, this.pageMetaData, "path");
1075
- if (pageJson.pages && pageJson.pages.length > 0) {
1076
- const pagesArray = pageJson.pages;
1077
- const homeIndex = pagesArray.findIndex((page) => page.type === "home");
1078
- if (homeIndex > 0) {
1079
- const [homePage] = pagesArray.splice(homeIndex, 1);
1080
- pagesArray.unshift(homePage);
1081
- }
1082
- }
1083
- pageJson.subPackages = oldSubPackages || new comment_json.CommentArray();
1084
- const newSubPackages = /* @__PURE__ */ new Map();
1085
- for (const item of this.subPageMetaData) newSubPackages.set(item.root, item);
1086
- for (const existing of pageJson.subPackages) {
1087
- const sub = newSubPackages.get(existing.root);
1088
- if (sub) {
1089
- existing.pages = mergePlatformItems(existing.pages, currentPlatform, sub.pages, "path");
1090
- if (sub.plugins) existing.plugins = sub.plugins;
1091
- newSubPackages.delete(existing.root);
1092
- }
1093
- }
1094
- for (const [_, newSub] of newSubPackages) {
1095
- const subPackage = {
1096
- root: newSub.root,
1097
- pages: mergePlatformItems(void 0, currentPlatform, newSub.pages, "path")
1098
- };
1099
- if (newSub.plugins) subPackage.plugins = newSub.plugins;
1100
- pageJson.subPackages.push(subPackage);
1101
- }
1102
- const { list, ...tabBarOthers } = await this.resolveTabBar() || {};
1103
- if (list) {
1104
- const { list: oldList } = oldTabBar || {};
1105
- const newList = mergePlatformItems(oldList, currentPlatform, list, "pagePath");
1106
- pageJson.tabBar = {
1107
- ...tabBarOthers,
1108
- list: newList
1109
- };
1110
- } else pageJson.tabBar = void 0;
1111
- return pageJson;
1112
- }
1113
- async readInfoFromPagesJSON() {
1114
- const resolvedPagesJSONContent = await node_fs.default.promises.readFile(this.resolvedPagesJSONPath, { encoding: "utf-8" }).catch(() => "");
1115
- this.resolvedPagesJSONIndent = (0, detect_indent.default)(resolvedPagesJSONContent).indent || " ";
1116
- this.resolvedPagesJSONNewline = (0, detect_newline.default)(resolvedPagesJSONContent) || "\n";
1117
- this.resolvedPagesJSONEofNewline = (resolvedPagesJSONContent.at(-1) ?? "\n") === this.resolvedPagesJSONNewline;
1118
- }
1119
- async getIndent() {
1120
- if (!this.resolvedPagesJSONIndent) await this.readInfoFromPagesJSON();
1121
- return this.resolvedPagesJSONIndent;
1122
- }
1123
- async getNewline() {
1124
- if (!this.resolvedPagesJSONNewline) await this.readInfoFromPagesJSON();
1125
- return this.resolvedPagesJSONNewline;
1126
- }
1127
- async getEndOfLine() {
1128
- if (!this.resolvedPagesJSONEofNewline) await this.readInfoFromPagesJSON();
1129
- return this.resolvedPagesJSONEofNewline;
1130
- }
1131
- };
1132
- /**
1133
- * Get all page paths in the specified directory
1134
- * @param dir - Page directory path
1135
- * @param options - Resolved configuration options
1136
- * @returns Page path array containing relative and absolute paths
1137
- */
1138
- function getPagePaths(dir, options) {
1139
- const pagesDirPath = slash(node_path.default.resolve(options.root, dir));
1140
- const basePath = slash(node_path.default.join(options.root, options.outDir));
1141
- const files = getPageFiles(pagesDirPath, options);
1142
- debug$1.pages(dir, files);
1143
- return files.map((file) => slash(file)).map((file) => ({
1144
- relativePath: node_path.default.relative(basePath, slash(node_path.default.resolve(pagesDirPath, file))),
1145
- absolutePath: slash(node_path.default.resolve(pagesDirPath, file))
1146
- }));
1147
- }
1148
- /**
1149
- * Merge multi-platform page configuration items
1150
- * Handle conditional compilation comments (#ifdef / #endif), merge configuration items from different platforms into one array
1151
- * Same configuration items will automatically merge platform identifiers, different configuration items will keep conditional compilation comments
1152
- *
1153
- * @param source - Existing configuration item array (from pages.json)
1154
- * @param currentPlatform - Current platform identifier (e.g. H5, MP-WEIXIN)
1155
- * @param items - New configuration item array
1156
- * @param uniqueKeyName - Field name used to identify configuration item uniqueness (e.g. 'path' or 'pagePath')
1157
- * @returns Merged configuration item array with conditional compilation comments
1158
- */
1159
- function mergePlatformItems(source, currentPlatform, items, uniqueKeyName) {
1160
- const src = source || new comment_json.CommentArray();
1161
- currentPlatform = currentPlatform.toUpperCase();
1162
- let lastPlatforms = [];
1163
- for (const comment of src[Symbol.for("before:0")] || []) {
1164
- const trimed = comment.value.trim();
1165
- if (trimed.startsWith("GENERATED BY UNI-PAGES, PLATFORM:")) lastPlatforms = trimed.split(":")[1].split("||").map((s) => s.trim()).filter((s) => s !== currentPlatform).sort();
1166
- }
1167
- const stringifyForCompare = (val) => {
1168
- if (val && typeof val === "object" && "type" in val) {
1169
- const { type: _type, ...rest } = val;
1170
- return JSON.stringify(rest);
1171
- }
1172
- return JSON.stringify(val);
1173
- };
1174
- const tmpMap = /* @__PURE__ */ new Map();
1175
- for (let i = 0; i < src.length; i++) {
1176
- const item = src[i];
1177
- const uniqueKey = item[uniqueKeyName];
1178
- if (!uniqueKey) continue;
1179
- const ifdefComment = src[Symbol.for(`before:${i}`)]?.find((c) => c.value.trim().startsWith("#ifdef"));
1180
- let platforms = [...lastPlatforms];
1181
- if (ifdefComment) {
1182
- const match = ifdefComment.value.match(/#ifdef\s+(.+)/);
1183
- if (match) platforms = match[1].split("||").map((p) => p.trim()).filter((s) => s !== currentPlatform).sort();
1184
- }
1185
- if (platforms.length === 0) continue;
1186
- const existing = tmpMap.get(uniqueKey) || [];
1187
- existing.push({
1188
- item,
1189
- itemStr: stringifyForCompare(item),
1190
- platforms,
1191
- platformStr: platforms.join(" || ")
1192
- });
1193
- tmpMap.set(uniqueKey, existing);
1194
- }
1195
- for (const item of items) {
1196
- const newItem = item;
1197
- const uniqueKey = item[uniqueKeyName];
1198
- if (!uniqueKey) continue;
1199
- if (!tmpMap.has(uniqueKey)) {
1200
- tmpMap.set(uniqueKey, [{
1201
- item: newItem,
1202
- itemStr: stringifyForCompare(newItem),
1203
- platforms: [currentPlatform],
1204
- platformStr: currentPlatform
1205
- }]);
1206
- continue;
1207
- }
1208
- const existing = tmpMap.get(uniqueKey);
1209
- const newItemStr = stringifyForCompare(newItem);
1210
- const equalObj = existing.find((val) => val.itemStr === newItemStr);
1211
- if (equalObj) {
1212
- equalObj.platforms.push(currentPlatform);
1213
- equalObj.platforms.sort();
1214
- equalObj.platformStr = equalObj.platforms.join(" || ");
1215
- } else existing.push({
1216
- item: newItem,
1217
- itemStr: stringifyForCompare(newItem),
1218
- platforms: [currentPlatform],
1219
- platformStr: currentPlatform
1220
- });
1221
- }
1222
- const result = new comment_json.CommentArray();
1223
- const platformUsage = {};
1224
- tmpMap.forEach((val) => {
1225
- Object.values(val).forEach((v) => {
1226
- platformUsage[v.platformStr] = (platformUsage[v.platformStr] || 0) + 1;
1227
- });
1228
- });
1229
- const usageKeys = Object.keys(platformUsage);
1230
- const defaultPlatformStr = usageKeys.length ? usageKeys.reduce((a, b) => platformUsage[a] > platformUsage[b] ? a : b) : currentPlatform;
1231
- result[Symbol.for("before:0")] = [{
1232
- type: "LineComment",
1233
- value: ` GENERATED BY UNI-PAGES, PLATFORM: ${defaultPlatformStr}`,
1234
- inline: false,
1235
- loc: {
1236
- start: {
1237
- line: 0,
1238
- column: 0
1239
- },
1240
- end: {
1241
- line: 0,
1242
- column: 0
1243
- }
1244
- }
1245
- }];
1246
- for (const [_, list] of tmpMap) for (const { item, platformStr } of list) {
1247
- result.push(item);
1248
- if (platformStr !== defaultPlatformStr) {
1249
- result[Symbol.for(`before:${result.length - 1}`)] = [{
1250
- type: "LineComment",
1251
- value: ` #ifdef ${platformStr}`,
1252
- inline: false,
1253
- loc: {
1254
- start: {
1255
- line: 0,
1256
- column: 0
1257
- },
1258
- end: {
1259
- line: 0,
1260
- column: 0
1261
- }
1262
- }
1263
- }];
1264
- result[Symbol.for(`after:${result.length - 1}`)] = [{
1265
- type: "LineComment",
1266
- value: " #endif",
1267
- inline: false,
1268
- loc: {
1269
- start: {
1270
- line: 0,
1271
- column: 0
1272
- },
1273
- end: {
1274
- line: 0,
1275
- column: 0
1276
- }
1277
- }
1278
- }];
1279
- }
1280
- }
1281
- return result;
1282
- }
1283
- //#endregion
1284
- //#region src/config/index.ts
1285
- /**
1286
- * Define uni-pages configuration
1287
- * Used in pages.config.ts configuration file to provide type hints and validation
1288
- *
1289
- * @param config - Page configuration object
1290
- * @returns Configuration object
1291
- */
1292
- function defineUniPages(config) {
1293
- return config;
1294
- }
1295
- //#endregion
1296
- //#region src/index.ts
1297
- /**
1298
- * vite-plugin-uni-pages plugin main entry
1299
- *
1300
- * Automatically scan page directories and generate pages.json configuration file
1301
- * Support definePage macro for defining page metadata
1302
- * Support multi-platform conditional compilation
1303
- * Support sub-package configuration
1304
- * Support TypeScript declaration file generation
1305
- *
1306
- * @param userOptions - User configuration options
1307
- * @returns Vite plugin instance
1308
- */
1309
- function VitePluginUniPages(userOptions = {}) {
1310
- let ctx;
1311
- checkPagesJsonFileSync(node_path.default.join(node_process.default.env.VITE_ROOT_DIR || node_process.default.cwd(), userOptions.outDir ?? "src", OUTPUT_NAME));
1312
- return {
1313
- name: "vite-plugin-uni-pages",
1314
- enforce: "pre",
1315
- /**
1316
- * Vite configResolved hook
1317
- * Initialize PageContext, set logger, generate initial pages.json
1318
- */
1319
- async configResolved(config) {
1320
- ctx = new PageContext(userOptions, config.root);
1321
- if (config.plugins.some((v) => v.name === "vite-plugin-uni-platform")) ctx.withUniPlatform = true;
1322
- const logger = (0, vite.createLogger)(void 0, { prefix: "[vite-plugin-uni-pages]" });
1323
- ctx.setLogger(logger);
1324
- await ctx.updatePagesJSON();
1325
- if (config.command === "build") {
1326
- if (config.build.watch) ctx.setupWatcher(chokidar.default.watch([...ctx.options.dirs, ...ctx.options.subPackages]));
1327
- }
1328
- },
1329
- /**
1330
- * Code transform hook
1331
- * Remove definePage macro calls from Vue SFC to avoid runtime errors
1332
- */
1333
- async transform(code, id) {
1334
- if (!FILE_EXTENSIONS.some((ext) => id.endsWith(ext))) return null;
1335
- const sfc = parseSFC(code, { filename: id });
1336
- let macro;
1337
- if (sfc.scriptSetup) macro = findMacro((0, ast_kit.babelParse)(sfc.scriptSetup.content, sfc.scriptSetup.lang || "js").body, sfc.filename);
1338
- if (!macro && sfc.script) macro = findMacro((0, ast_kit.babelParse)(sfc.script.content, sfc.script.lang || "js").body, sfc.filename);
1339
- if (!macro) return null;
1340
- const s = new magic_string.default(code);
1341
- s.remove(macro.start, macro.end);
1342
- if (s.hasChanged()) return {
1343
- code: s.toString(),
1344
- map: s.generateMap({
1345
- source: id,
1346
- includeContent: true,
1347
- file: `${id}.map`
1348
- })
1349
- };
1350
- },
1351
- /**
1352
- * Configure server hook
1353
- * Set up file watching and HMR support
1354
- */
1355
- configureServer(server) {
1356
- ctx.setupViteServer(server);
1357
- },
1358
- /**
1359
- * Module resolution hook
1360
- * Resolve virtual module identifier to internal path
1361
- */
1362
- resolveId(id) {
1363
- if (id === "virtual:uni-pages") return RESOLVED_MODULE_ID_VIRTUAL;
1364
- },
1365
- /**
1366
- * Module load hook
1367
- * Return the code content of the virtual module
1368
- */
1369
- load(id) {
1370
- if (id === RESOLVED_MODULE_ID_VIRTUAL) return ctx.virtualModule();
1371
- }
1372
- };
1373
- }
1374
- //#endregion
1375
- exports.FILE_EXTENSIONS = FILE_EXTENSIONS;
1376
- exports.MODULE_ID_VIRTUAL = MODULE_ID_VIRTUAL;
1377
- exports.OUTPUT_NAME = OUTPUT_NAME;
1378
- exports.Page = Page;
1379
- exports.PageContext = PageContext;
1380
- exports.RESOLVED_MODULE_ID_VIRTUAL = RESOLVED_MODULE_ID_VIRTUAL;
1381
- exports.VitePluginUniPages = VitePluginUniPages;
1382
- exports.default = VitePluginUniPages;
1383
- exports.babelGenerate = babelGenerate;
1384
- exports.checkPagesJsonFileSync = checkPagesJsonFileSync;
1385
- exports.debug = debug$1;
1386
- exports.defineUniPages = defineUniPages;
1387
- exports.extsToGlob = extsToGlob;
1388
- exports.findImports = findImports;
1389
- exports.findMacro = findMacro;
1390
- exports.getPageFiles = getPageFiles;
1391
- exports.invalidatePagesModule = invalidatePagesModule;
1392
- exports.isTargetFile = isTargetFile;
1393
- exports.mergePageMetaDataArray = mergePageMetaDataArray;
1394
- exports.parseCode = parseCode;
1395
- exports.parseSFC = parseSFC;
1396
- exports.resolveOptions = resolveOptions;
1397
- exports.resolvePageDirs = resolvePageDirs;
1398
- exports.sleep = sleep;
1399
- exports.tryPageMetaFromMacro = tryPageMetaFromMacro;
1400
- exports.withFileLock = withFileLock;
1401
- exports.writeFileWithLock = writeFileWithLock;