@v-long/vite-plugin-view-name-map 0.1.7 → 0.1.8
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 +51 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +48 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -6
- package/dist/client.cjs +0 -2
- package/dist/client.cjs.map +0 -1
- package/dist/client.d.cts +0 -20
- package/dist/client.d.ts +0 -20
- package/dist/client.js +0 -1
- package/dist/client.js.map +0 -1
- package/types/client.d.ts +0 -20
package/dist/index.cjs
CHANGED
|
@@ -28,13 +28,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
30
|
// src/index.ts
|
|
31
|
-
var
|
|
32
|
-
__export(
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
33
|
default: () => viewNameMapPlugin
|
|
34
34
|
});
|
|
35
|
-
module.exports = __toCommonJS(
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
var import_vite = require("vite");
|
|
37
|
-
var
|
|
37
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
38
38
|
|
|
39
39
|
// src/generate.ts
|
|
40
40
|
var import_path = __toESM(require("path"), 1);
|
|
@@ -209,6 +209,48 @@ export const viewNameMap = ${JSON.stringify(map, null, 2)};
|
|
|
209
209
|
`;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
// src/dts.ts
|
|
213
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
214
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
215
|
+
function resolveDtsOptions(dts, root) {
|
|
216
|
+
const defaultFilename = "view-name-map.d.ts";
|
|
217
|
+
const defaultOutputPath = import_path2.default.join(root, "types");
|
|
218
|
+
if (dts === true || dts === void 0) {
|
|
219
|
+
return {
|
|
220
|
+
outputPath: defaultOutputPath,
|
|
221
|
+
filename: defaultFilename
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (dts === false) {
|
|
225
|
+
return { outputPath: defaultOutputPath, filename: defaultFilename };
|
|
226
|
+
}
|
|
227
|
+
if (typeof dts === "string") {
|
|
228
|
+
if (dts.endsWith(".d.ts")) {
|
|
229
|
+
const filename = import_path2.default.basename(dts);
|
|
230
|
+
const dir = import_path2.default.resolve(root, import_path2.default.dirname(dts));
|
|
231
|
+
return { outputPath: dir, filename };
|
|
232
|
+
}
|
|
233
|
+
return { outputPath: import_path2.default.resolve(root, dts), filename: defaultFilename };
|
|
234
|
+
}
|
|
235
|
+
return { outputPath: defaultOutputPath, filename: defaultFilename };
|
|
236
|
+
}
|
|
237
|
+
async function generateDtsFile(map, dtsOptions) {
|
|
238
|
+
const filePath = import_path2.default.join(dtsOptions.outputPath, dtsOptions.filename);
|
|
239
|
+
const entries = Object.entries(map).map(([k, v]) => ` "${k}": "${v}";`).join("\n");
|
|
240
|
+
const content = `// Generated by vite-plugin-view-name-map
|
|
241
|
+
declare module 'virtual:view-name-map' {
|
|
242
|
+
export type ViewNameMap = {
|
|
243
|
+
${entries}
|
|
244
|
+
};
|
|
245
|
+
export const viewNameMap: ViewNameMap;
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
248
|
+
await import_promises2.default.mkdir(dtsOptions.outputPath, { recursive: true });
|
|
249
|
+
await import_promises2.default.writeFile(filePath, content, "utf-8");
|
|
250
|
+
console.log(`\x1B[32m[view-name-map] \u7C7B\u578B\u58F0\u660E\u6587\u4EF6\u5DF2\u751F\u6210: ${filePath}\x1B[0m`);
|
|
251
|
+
return filePath;
|
|
252
|
+
}
|
|
253
|
+
|
|
212
254
|
// src/index.ts
|
|
213
255
|
function viewNameMapPlugin(options = {}) {
|
|
214
256
|
const virtualId = "virtual:view-name-map";
|
|
@@ -217,7 +259,7 @@ function viewNameMapPlugin(options = {}) {
|
|
|
217
259
|
const autoInjectName = options.autoInjectName ?? true;
|
|
218
260
|
const autoGenerateName = autoInjectName ? true : options.autoGenerateName ?? true;
|
|
219
261
|
const root = (0, import_vite.normalizePath)(process.cwd());
|
|
220
|
-
const absoluteViewsDir = (0, import_vite.normalizePath)(
|
|
262
|
+
const absoluteViewsDir = (0, import_vite.normalizePath)(import_path3.default.resolve(root, viewsDir));
|
|
221
263
|
let virtualCode = "";
|
|
222
264
|
let nameMap = {};
|
|
223
265
|
let injectedCount = 0;
|
|
@@ -230,6 +272,10 @@ function viewNameMapPlugin(options = {}) {
|
|
|
230
272
|
autoGenerateName
|
|
231
273
|
});
|
|
232
274
|
virtualCode = createVirtualModule(nameMap);
|
|
275
|
+
const dtsOptions = resolveDtsOptions(options.dts, root);
|
|
276
|
+
if (options.dts !== false) {
|
|
277
|
+
await generateDtsFile(nameMap, dtsOptions);
|
|
278
|
+
}
|
|
233
279
|
const count = Object.keys(nameMap).length;
|
|
234
280
|
if (count > 0) {
|
|
235
281
|
const status = autoInjectName ? "\u5DF2\u5F00\u542F\u81EA\u52A8\u6CE8\u5165" : "\u4EC5\u751F\u6210\u6620\u5C04\u8868";
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/generate.ts","../src/plugin-errors.ts","../src/virtual-module.ts"],"sourcesContent":["import type {Plugin} from 'vite';\r\nimport {normalizePath} from 'vite'; // 必须使用 vite 提供的路径归一化工具\r\nimport path from 'path';\r\nimport {generateViewNameMap, injectDefineOptions, generateNameFromFilePath, ViewNameMap} from './generate';\r\nimport {createVirtualModule} from './virtual-module';\r\n\r\n/**\r\n * 插件配置项\r\n */\r\nexport interface ViewNameMapPluginOptions {\r\n /** Vue 页面根目录,默认 'src/views' */\r\n viewsDir?: string;\r\n /** 是否自动生成未声明 name 的组件 name(影响虚拟模块映射表),默认 true */\r\n autoGenerateName?: boolean;\r\n /** 是否自动注入 defineOptions 到组件中(影响运行时组件 name),默认 true */\r\n autoInjectName?: boolean;\r\n}\r\n\r\n/**\r\n * vite-plugin-view-name-map\r\n *\r\n * 构建期扫描 views 目录,提取组件 name 并生成 virtual module\r\n */\r\nexport default function viewNameMapPlugin(\r\n options: ViewNameMapPluginOptions = {}\r\n): Plugin {\r\n const virtualId = 'virtual:view-name-map';\r\n const resolvedVirtualId = '\\0' + virtualId;\r\n\r\n // 统一转换为正斜杠路径\r\n const viewsDir = options.viewsDir ?? 'src/views';\r\n const autoInjectName = options.autoInjectName ?? true;\r\n\r\n // 核心约束:如果开启了自动注入,则必须开启自动生成,否则注入没有数据源\r\n const autoGenerateName = autoInjectName ? true : (options.autoGenerateName ?? true);\r\n\r\n const root = normalizePath(process.cwd());\r\n const absoluteViewsDir = normalizePath(path.resolve(root, viewsDir));\r\n\r\n let virtualCode = '';\r\n let nameMap: ViewNameMap = {};\r\n let injectedCount = 0; // 注入计数器\r\n\r\n return {\r\n name: 'vite-plugin-view-name-map',\r\n enforce: 'pre',\r\n\r\n async buildStart(): Promise<void> {\r\n // 初始扫描\r\n nameMap = await generateViewNameMap({\r\n viewsDir: viewsDir,\r\n autoGenerateName: autoGenerateName\r\n });\r\n virtualCode = createVirtualModule(nameMap);\r\n\r\n // 打印初始化统计信息\r\n const count = Object.keys(nameMap).length;\r\n if (count > 0) {\r\n const status = autoInjectName ? '已开启自动注入' : '仅生成映射表';\r\n console.log(`\\x1b[32m[view-name-map] ${status}:扫描到 ${count} 个视图组件。\\x1b[0m`);\r\n }\r\n },\r\n\r\n resolveId(id: string): string | undefined {\r\n if (id === virtualId) return resolvedVirtualId;\r\n return undefined;\r\n },\r\n\r\n load(id: string): string | undefined {\r\n if (id === resolvedVirtualId) return virtualCode;\r\n return undefined;\r\n },\r\n\r\n async transform(code: string, id: string) {\r\n // 只有开启注入时才继续执行 transform 逻辑\r\n if (!autoInjectName) return null;\r\n\r\n const cleanId = normalizePath(id.split('?')[0]);\r\n\r\n // 过滤非目标文件\r\n if (!cleanId.endsWith('.vue') || !cleanId.startsWith(absoluteViewsDir)) {\r\n return null;\r\n }\r\n\r\n // 获取组件名称\r\n const componentName = generateNameFromFilePath(cleanId, absoluteViewsDir);\r\n\r\n try {\r\n // 执行 AST 注入\r\n const result = injectDefineOptions(code, cleanId, componentName);\r\n\r\n // 如果返回了结果,说明确实进行了注入(非 null)\r\n if (result) {\r\n injectedCount++;\r\n }\r\n\r\n return result;\r\n } catch (err) {\r\n console.error(`\\x1b[31m[view-name-map] 注入异常: ${cleanId}\\x1b[0m`, err);\r\n return null;\r\n }\r\n },\r\n\r\n // 在构建结束时打印统计信息\r\n buildEnd() {\r\n // 仅在开发/构建完成且有注入行为时输出\r\n if (autoInjectName && injectedCount > 0) {\r\n console.log(`\\x1b[36m[view-name-map] 本次运行已为 ${injectedCount} 个组件注入了 defineOptions({ name: \"...\" })。\\x1b[0m`);\r\n }\r\n }\r\n };\r\n}\r\n","import path from 'path';\nimport fs from 'fs/promises';\nimport fg from 'fast-glob';\nimport MagicString from 'magic-string'; // 需要安装: npm install magic-string\nimport {parse} from '@vue/compiler-sfc'; // Vue 自带\nimport {CallExpression, Expression, Node, ObjectLiteralExpression, Project, SourceFile, SyntaxKind} from 'ts-morph';\nimport {PluginError, ValidationError, IoError, LogicError} from './plugin-errors';\n\r\n/** 视图 name 映射表类型 */\r\nexport type ViewNameMap = Record<string, string>;\r\n\r\n/** 构建期生成逻辑配置 */\r\nexport interface GenerateOptions {\n viewsDir?: string;\n autoGenerateName?: boolean;\n}\n\n// 内部的配置校验,确保输入类型及字段符合期望,降低运行时错误\nfunction ensureGenerateOptionsValid(options: GenerateOptions): void {\n // Runtime type checks using loose typing to avoid TypeScript narrowing pitfalls\n const viewsDirAny: any = (options as any).viewsDir;\n if (viewsDirAny !== undefined && typeof viewsDirAny !== 'string') {\n throw new ValidationError('Invalid type for options.viewsDir; expected string', {\n receivedType: typeof viewsDirAny,\n receivedValue: viewsDirAny,\n });\n }\n const autoGenAny: any = (options as any).autoGenerateName;\n if (autoGenAny !== undefined && typeof autoGenAny !== 'boolean') {\n throw new ValidationError('Invalid type for options.autoGenerateName; expected boolean', {\n receivedType: typeof autoGenAny,\n receivedValue: autoGenAny,\n });\n }\n}\n\r\n/**\r\n * 扫描 views 目录生成组件 name 映射表\r\n */\r\nexport async function generateViewNameMap(\n options: GenerateOptions = {}\n): Promise<ViewNameMap> {\n // 参数校验,尽早抛出可追踪的错误\n ensureGenerateOptionsValid(options);\n\n try {\n const viewsDir: string = options.viewsDir ?? 'src/views';\n const autoGen: boolean = options.autoGenerateName ?? true;\n\n const map: ViewNameMap = {};\n const files: string[] = await fg(['**/*.vue', '**/*.tsx'], {cwd: viewsDir, absolute: true});\n\n for (const file of files) {\n let content: string;\n try {\n content = await fs.readFile(file, 'utf-8');\n } catch (err) {\n throw new IoError(`Failed to read file: ${file}`, { cause: err });\n }\n\n // 优先尝试从 Vue SFC 的 scriptSetup 提取 defineOptions.name\n let name: string | undefined = extractNameFromDefineOptionsInSfc(content);\n // 兜底:更直接的全文搜索作为额外鲁棒性补充\n if (!name) {\n const m2 = content.match(/defineOptions\\s*\\(\\s*\\{[^\\}]*name\\s*:\\s*['\"]([^'\"]+)['\"][^\\}]*\\}\\s*\\)/m);\n if (m2) name = m2[1];\n }\n\n // 兜底:若未从 defineOptions 提取,且开启自动命名,则基于文件路径生成名称\n if (!name && autoGen) {\n name = generateNameFromFilePath(file, viewsDir);\n }\n\n if (name) map[normalizePath(file, viewsDir)] = name;\n }\n\n return map;\n } catch (err) {\n // 分类错误并提供上下文,便于定位问题\n if (err instanceof PluginError) throw err;\n throw new LogicError('Failed to generate view name map', { cause: err });\n }\n}\n\r\n/**\r\n * 在 transform 阶段注入 defineOptions (不写入文件)\r\n */\r\nexport function injectDefineOptions(code: string, id: string, name: string) {\n const {descriptor} = parse(code);\r\n if (!descriptor.scriptSetup) return null;\r\n\r\n const content = descriptor.scriptSetup.content;\r\n const startOffset = descriptor.scriptSetup.loc.start.offset;\r\n\r\n // 使用 ts-morph 分析 scriptSetup 内容\r\n const project = new Project({useInMemoryFileSystem: true});\r\n const sourceFile = project.createSourceFile('temp.ts', content);\r\n\r\n // 检查是否已有 defineOptions\r\n const hasName = !!extractNameFromDefineOptions(sourceFile);\r\n if (hasName) return null;\r\n\r\n const s = new MagicString(code);\r\n const defineOptionsCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)\r\n .find(c => c.getExpression().getText() === 'defineOptions');\r\n\r\n if (defineOptionsCall) {\r\n // 情况 A: 已有 defineOptions 但没写 name 属性\r\n const arg = defineOptionsCall.getArguments()[0];\r\n if (Node.isObjectLiteralExpression(arg)) {\r\n const properties = arg.getProperties();\r\n const pos = startOffset + arg.getStart() + 1; // '{' 之后\r\n\r\n if (properties.length === 0) {\r\n // 空对象直接插入\r\n s.appendRight(pos, ` name: '${name}' `);\r\n } else {\r\n // 已有属性,必须补充逗号分隔\r\n s.appendRight(pos, ` name: '${name}', `);\r\n }\r\n }\r\n } else {\r\n // 情况 B: 完全没有 defineOptions,在 scriptSetup 顶部注入\r\n s.appendLeft(startOffset, `\\ndefineOptions({ name: '${name}' });\\n`);\r\n }\r\n\r\n return {\n code: s.toString(),\n map: s.generateMap({source: id, includeContent: true, hires: true})\n };\n}\n\r\n/**\r\n * 从 defineOptions 宏定义中提取组件 name 属性值\r\n */\r\nfunction extractNameFromDefineOptions(sourceFile: SourceFile): string | undefined {\n // 获取源文件中所有的调用表达式\r\n const calls: CallExpression[] = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n\r\n for (const call of calls) {\r\n const expression: Expression = call.getExpression();\r\n // 匹配 defineOptions 调用\r\n if (!expression || expression.getText() !== 'defineOptions') continue;\r\n\r\n const args = call.getArguments();\r\n if (args.length === 0) continue;\r\n\r\n // 获取第一个对象字面量参数\r\n const obj: ObjectLiteralExpression | undefined = args[0]?.asKind(SyntaxKind.ObjectLiteralExpression);\r\n if (!obj) continue;\r\n\r\n // 查找 name 属性并确保其为标准的 key: value 赋值形式\r\n const nameProp = obj.getProperty('name');\r\n if (nameProp && Node.isPropertyAssignment(nameProp)) {\r\n const initializer = nameProp.getInitializer();\r\n if (initializer && Node.isStringLiteral(initializer)) {\r\n return initializer.getLiteralText();\r\n }\r\n }\r\n }\r\n\r\n return undefined;\r\n}\n\n/**\n * Try to extract name from defineOptions within a Vue SFC's scriptSetup block.\n * It parses the <script setup> content using ts-morph to find defineOptions({ name: '...' }).\n */\nfunction extractNameFromDefineOptionsInSfc(content: string): string | undefined {\n try {\n const {descriptor} = parse(content);\n if (!descriptor.scriptSetup) return undefined;\n const scriptContent: string = descriptor.scriptSetup.content;\n // 直接正则匹配,兜底提升鲁棒性\n const regex = /defineOptions\\s*\\(\\s*\\{\\s*name\\s*:\\s*['\"]([^'\"]+)['\"]\\s*\\}\\s*\\)/;\n const m = scriptContent.match(regex);\n if (m) return m[1];\n\n // 兜底:再尝试通过 ts-morph 解析脚本块\n const project = new Project({useInMemoryFileSystem: true});\n const sourceFile = project.createSourceFile('temp.ts', scriptContent);\n return extractNameFromDefineOptions(sourceFile);\n } catch {\n return undefined;\n }\n}\n\n/**\r\n * 根据文件路径生成组件 name (PascalCase)\r\n * 规则:\r\n * - 保持层级关系\r\n * - 移除 index 后缀\r\n * - 自动处理路径中的横杠、下划线为大驼峰\r\n */\r\nexport function generateNameFromFilePath(file: string, viewsDir: string): string {\r\n const absoluteViewsDir: string = path.resolve(viewsDir);\r\n const relativePath: string = path.relative(absoluteViewsDir, file);\r\n const {dir, name} = path.parse(relativePath);\r\n\r\n // 拆分层级\r\n const segments: string[] = dir ? dir.split(path.sep) : [];\r\n if (name !== 'index') {\r\n segments.push(name);\r\n }\r\n\r\n // 转换每一级为 PascalCase 并拼接\r\n return segments\r\n .map((s: string) => s\r\n // 处理内部横杠或下划线: sys-user -> sysUser\r\n .replace(/[-_](.)/g, (_: string, c: string) => c.toUpperCase())\r\n // 首字母大写: sysUser -> SysUser\r\n .replace(/^[a-z]/, (c: string) => c.toUpperCase())\r\n )\r\n .join('') || 'Index';\r\n}\r\n\r\n/**\r\n * 路径统一处理:生成的 Key 包含 viewsDir 目录名\r\n * 示例:/Users/me/project/src/views/User/Index.vue -> src/views/User/Index.vue\r\n */\r\nfunction normalizePath(file: string, viewsDir: string): string {\r\n // 1. 获取 viewsDir 的基础名称 (例如 'src/views')\r\n // 2. 获取文件相对于 viewsDir 的部分\r\n // 3. 拼接并统一分隔符\r\n const relativePath: string = path.relative(path.resolve(viewsDir), file);\r\n const combinedPath: string = path.join(viewsDir, relativePath);\r\n\r\n return combinedPath.split(path.sep).join('/');\r\n}\r\n","// Centralized plugin error types for better error handling and debugging\n\nexport class PluginError extends Error {\n public details?: any;\n constructor(message: string, details?: any) {\n super(message);\n this.name = 'PluginError';\n this.details = details;\n }\n}\n\nexport class ValidationError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'ValidationError';\n }\n}\n\nexport class IoError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'IoError';\n }\n}\n\nexport class LogicError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'LogicError';\n }\n}\n","import type { ViewNameMap } from './generate';\r\n\r\n/**\r\n * 根据扫描结果生成 virtual module 的源码字符串\r\n *\r\n * 设计说明:\r\n * - 不使用 default export,便于未来扩展更多具名导出\r\n * - 同时更利于 TypeScript module augmentation\r\n */\r\nexport function createVirtualModule(map: ViewNameMap): string {\r\n return `\r\nexport const viewNameMap = ${JSON.stringify(map, null, 2)};\r\n`;\r\n}\r\n"],"mappings":"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","names":["import_path","fg","fs","MagicString","path","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/generate.ts","../src/plugin-errors.ts","../src/virtual-module.ts","../src/dts.ts"],"sourcesContent":["import type {Plugin} from 'vite';\r\nimport {normalizePath} from 'vite'; // 必须使用 vite 提供的路径归一化工具\r\nimport path from 'path';\r\nimport {generateViewNameMap, injectDefineOptions, generateNameFromFilePath, ViewNameMap} from './generate';\r\nimport {createVirtualModule} from './virtual-module';\r\nimport { generateDtsFile, resolveDtsOptions } from './dts';\r\n\r\n/**\r\n * 插件配置项\r\n */\r\nexport interface ViewNameMapPluginOptions {\r\n /** Vue 页面根目录,默认 'src/views' */\r\n viewsDir?: string;\r\n /** 是否自动生成未声明 name 的组件 name(影响虚拟模块映射表),默认 true */\r\n autoGenerateName?: boolean;\r\n /** 是否自动注入 defineOptions 到组件中(影响运行时组件 name),默认 true */\r\n autoInjectName?: boolean;\r\n /**\r\n * 类型声明文件配置\r\n * - true: 在 types/ 目录下生成 view-name-map.d.ts\r\n * - string: 作为目录,在该目录下生成 view-name-map.d.ts\r\n * - 以 .d.ts 结尾的 string: 直接作为文件路径\r\n * - false: 不生成类型声明文件\r\n * 默认值: true\r\n */\r\n dts?: boolean | string;\r\n}\r\n\r\n/**\r\n * vite-plugin-view-name-map\r\n *\r\n * 构建期扫描 views 目录,提取组件 name 并生成 virtual module\r\n */\r\nexport default function viewNameMapPlugin(\r\n options: ViewNameMapPluginOptions = {}\r\n): Plugin {\r\n const virtualId = 'virtual:view-name-map';\r\n const resolvedVirtualId = '\\0' + virtualId;\r\n\r\n // 统一转换为正斜杠路径\r\n const viewsDir = options.viewsDir ?? 'src/views';\r\n const autoInjectName = options.autoInjectName ?? true;\r\n\r\n // 核心约束:如果开启了自动注入,则必须开启自动生成,否则注入没有数据源\r\n const autoGenerateName = autoInjectName ? true : (options.autoGenerateName ?? true);\r\n\r\n const root = normalizePath(process.cwd());\r\n const absoluteViewsDir = normalizePath(path.resolve(root, viewsDir));\r\n\r\n let virtualCode = '';\r\n let nameMap: ViewNameMap = {};\r\n let injectedCount = 0; // 注入计数器\r\n\r\n return {\r\n name: 'vite-plugin-view-name-map',\r\n enforce: 'pre',\r\n\r\n async buildStart(): Promise<void> {\r\n // 初始扫描\r\n nameMap = await generateViewNameMap({\r\n viewsDir: viewsDir,\r\n autoGenerateName: autoGenerateName\r\n });\r\n virtualCode = createVirtualModule(nameMap);\r\n\r\n // 生成类型声明文件\r\n const dtsOptions = resolveDtsOptions(options.dts, root);\r\n if (options.dts !== false) {\r\n await generateDtsFile(nameMap, dtsOptions);\r\n }\r\n\r\n // 打印初始化统计信息\r\n const count = Object.keys(nameMap).length;\r\n if (count > 0) {\r\n const status = autoInjectName ? '已开启自动注入' : '仅生成映射表';\r\n console.log(`\\x1b[32m[view-name-map] ${status}:扫描到 ${count} 个视图组件。\\x1b[0m`);\r\n }\r\n },\r\n\r\n resolveId(id: string): string | undefined {\r\n if (id === virtualId) return resolvedVirtualId;\r\n return undefined;\r\n },\r\n\r\n load(id: string): string | undefined {\r\n if (id === resolvedVirtualId) return virtualCode;\r\n return undefined;\r\n },\r\n\r\n async transform(code: string, id: string) {\r\n // 只有开启注入时才继续执行 transform 逻辑\r\n if (!autoInjectName) return null;\r\n\r\n const cleanId = normalizePath(id.split('?')[0]);\r\n\r\n // 过滤非目标文件\r\n if (!cleanId.endsWith('.vue') || !cleanId.startsWith(absoluteViewsDir)) {\r\n return null;\r\n }\r\n\r\n // 获取组件名称\r\n const componentName = generateNameFromFilePath(cleanId, absoluteViewsDir);\r\n\r\n try {\r\n // 执行 AST 注入\r\n const result = injectDefineOptions(code, cleanId, componentName);\r\n\r\n // 如果返回了结果,说明确实进行了注入(非 null)\r\n if (result) {\r\n injectedCount++;\r\n }\r\n\r\n return result;\r\n } catch (err) {\r\n console.error(`\\x1b[31m[view-name-map] 注入异常: ${cleanId}\\x1b[0m`, err);\r\n return null;\r\n }\r\n },\r\n\r\n // 在构建结束时打印统计信息\r\n buildEnd() {\r\n // 仅在开发/构建完成且有注入行为时输出\r\n if (autoInjectName && injectedCount > 0) {\r\n console.log(`\\x1b[36m[view-name-map] 本次运行已为 ${injectedCount} 个组件注入了 defineOptions({ name: \"...\" })。\\x1b[0m`);\r\n }\r\n }\r\n };\r\n}\r\n","import path from 'path';\nimport fs from 'fs/promises';\nimport fg from 'fast-glob';\nimport MagicString from 'magic-string'; // 需要安装: npm install magic-string\nimport {parse} from '@vue/compiler-sfc'; // Vue 自带\nimport {CallExpression, Expression, Node, ObjectLiteralExpression, Project, SourceFile, SyntaxKind} from 'ts-morph';\nimport {PluginError, ValidationError, IoError, LogicError} from './plugin-errors';\n\r\n/** 视图 name 映射表类型 */\r\nexport type ViewNameMap = Record<string, string>;\r\n\r\n/** 构建期生成逻辑配置 */\r\nexport interface GenerateOptions {\n viewsDir?: string;\n autoGenerateName?: boolean;\n}\n\n// 内部的配置校验,确保输入类型及字段符合期望,降低运行时错误\nfunction ensureGenerateOptionsValid(options: GenerateOptions): void {\n // Runtime type checks using loose typing to avoid TypeScript narrowing pitfalls\n const viewsDirAny: any = (options as any).viewsDir;\n if (viewsDirAny !== undefined && typeof viewsDirAny !== 'string') {\n throw new ValidationError('Invalid type for options.viewsDir; expected string', {\n receivedType: typeof viewsDirAny,\n receivedValue: viewsDirAny,\n });\n }\n const autoGenAny: any = (options as any).autoGenerateName;\n if (autoGenAny !== undefined && typeof autoGenAny !== 'boolean') {\n throw new ValidationError('Invalid type for options.autoGenerateName; expected boolean', {\n receivedType: typeof autoGenAny,\n receivedValue: autoGenAny,\n });\n }\n}\n\r\n/**\r\n * 扫描 views 目录生成组件 name 映射表\r\n */\r\nexport async function generateViewNameMap(\n options: GenerateOptions = {}\n): Promise<ViewNameMap> {\n // 参数校验,尽早抛出可追踪的错误\n ensureGenerateOptionsValid(options);\n\n try {\n const viewsDir: string = options.viewsDir ?? 'src/views';\n const autoGen: boolean = options.autoGenerateName ?? true;\n\n const map: ViewNameMap = {};\n const files: string[] = await fg(['**/*.vue', '**/*.tsx'], {cwd: viewsDir, absolute: true});\n\n for (const file of files) {\n let content: string;\n try {\n content = await fs.readFile(file, 'utf-8');\n } catch (err) {\n throw new IoError(`Failed to read file: ${file}`, { cause: err });\n }\n\n // 优先尝试从 Vue SFC 的 scriptSetup 提取 defineOptions.name\n let name: string | undefined = extractNameFromDefineOptionsInSfc(content);\n // 兜底:更直接的全文搜索作为额外鲁棒性补充\n if (!name) {\n const m2 = content.match(/defineOptions\\s*\\(\\s*\\{[^\\}]*name\\s*:\\s*['\"]([^'\"]+)['\"][^\\}]*\\}\\s*\\)/m);\n if (m2) name = m2[1];\n }\n\n // 兜底:若未从 defineOptions 提取,且开启自动命名,则基于文件路径生成名称\n if (!name && autoGen) {\n name = generateNameFromFilePath(file, viewsDir);\n }\n\n if (name) map[normalizePath(file, viewsDir)] = name;\n }\n\n return map;\n } catch (err) {\n // 分类错误并提供上下文,便于定位问题\n if (err instanceof PluginError) throw err;\n throw new LogicError('Failed to generate view name map', { cause: err });\n }\n}\n\r\n/**\r\n * 在 transform 阶段注入 defineOptions (不写入文件)\r\n */\r\nexport function injectDefineOptions(code: string, id: string, name: string) {\n const {descriptor} = parse(code);\r\n if (!descriptor.scriptSetup) return null;\r\n\r\n const content = descriptor.scriptSetup.content;\r\n const startOffset = descriptor.scriptSetup.loc.start.offset;\r\n\r\n // 使用 ts-morph 分析 scriptSetup 内容\r\n const project = new Project({useInMemoryFileSystem: true});\r\n const sourceFile = project.createSourceFile('temp.ts', content);\r\n\r\n // 检查是否已有 defineOptions\r\n const hasName = !!extractNameFromDefineOptions(sourceFile);\r\n if (hasName) return null;\r\n\r\n const s = new MagicString(code);\r\n const defineOptionsCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)\r\n .find(c => c.getExpression().getText() === 'defineOptions');\r\n\r\n if (defineOptionsCall) {\r\n // 情况 A: 已有 defineOptions 但没写 name 属性\r\n const arg = defineOptionsCall.getArguments()[0];\r\n if (Node.isObjectLiteralExpression(arg)) {\r\n const properties = arg.getProperties();\r\n const pos = startOffset + arg.getStart() + 1; // '{' 之后\r\n\r\n if (properties.length === 0) {\r\n // 空对象直接插入\r\n s.appendRight(pos, ` name: '${name}' `);\r\n } else {\r\n // 已有属性,必须补充逗号分隔\r\n s.appendRight(pos, ` name: '${name}', `);\r\n }\r\n }\r\n } else {\r\n // 情况 B: 完全没有 defineOptions,在 scriptSetup 顶部注入\r\n s.appendLeft(startOffset, `\\ndefineOptions({ name: '${name}' });\\n`);\r\n }\r\n\r\n return {\n code: s.toString(),\n map: s.generateMap({source: id, includeContent: true, hires: true})\n };\n}\n\r\n/**\r\n * 从 defineOptions 宏定义中提取组件 name 属性值\r\n */\r\nfunction extractNameFromDefineOptions(sourceFile: SourceFile): string | undefined {\n // 获取源文件中所有的调用表达式\r\n const calls: CallExpression[] = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n\r\n for (const call of calls) {\r\n const expression: Expression = call.getExpression();\r\n // 匹配 defineOptions 调用\r\n if (!expression || expression.getText() !== 'defineOptions') continue;\r\n\r\n const args = call.getArguments();\r\n if (args.length === 0) continue;\r\n\r\n // 获取第一个对象字面量参数\r\n const obj: ObjectLiteralExpression | undefined = args[0]?.asKind(SyntaxKind.ObjectLiteralExpression);\r\n if (!obj) continue;\r\n\r\n // 查找 name 属性并确保其为标准的 key: value 赋值形式\r\n const nameProp = obj.getProperty('name');\r\n if (nameProp && Node.isPropertyAssignment(nameProp)) {\r\n const initializer = nameProp.getInitializer();\r\n if (initializer && Node.isStringLiteral(initializer)) {\r\n return initializer.getLiteralText();\r\n }\r\n }\r\n }\r\n\r\n return undefined;\r\n}\n\n/**\n * Try to extract name from defineOptions within a Vue SFC's scriptSetup block.\n * It parses the <script setup> content using ts-morph to find defineOptions({ name: '...' }).\n */\nfunction extractNameFromDefineOptionsInSfc(content: string): string | undefined {\n try {\n const {descriptor} = parse(content);\n if (!descriptor.scriptSetup) return undefined;\n const scriptContent: string = descriptor.scriptSetup.content;\n // 直接正则匹配,兜底提升鲁棒性\n const regex = /defineOptions\\s*\\(\\s*\\{\\s*name\\s*:\\s*['\"]([^'\"]+)['\"]\\s*\\}\\s*\\)/;\n const m = scriptContent.match(regex);\n if (m) return m[1];\n\n // 兜底:再尝试通过 ts-morph 解析脚本块\n const project = new Project({useInMemoryFileSystem: true});\n const sourceFile = project.createSourceFile('temp.ts', scriptContent);\n return extractNameFromDefineOptions(sourceFile);\n } catch {\n return undefined;\n }\n}\n\n/**\r\n * 根据文件路径生成组件 name (PascalCase)\r\n * 规则:\r\n * - 保持层级关系\r\n * - 移除 index 后缀\r\n * - 自动处理路径中的横杠、下划线为大驼峰\r\n */\r\nexport function generateNameFromFilePath(file: string, viewsDir: string): string {\r\n const absoluteViewsDir: string = path.resolve(viewsDir);\r\n const relativePath: string = path.relative(absoluteViewsDir, file);\r\n const {dir, name} = path.parse(relativePath);\r\n\r\n // 拆分层级\r\n const segments: string[] = dir ? dir.split(path.sep) : [];\r\n if (name !== 'index') {\r\n segments.push(name);\r\n }\r\n\r\n // 转换每一级为 PascalCase 并拼接\r\n return segments\r\n .map((s: string) => s\r\n // 处理内部横杠或下划线: sys-user -> sysUser\r\n .replace(/[-_](.)/g, (_: string, c: string) => c.toUpperCase())\r\n // 首字母大写: sysUser -> SysUser\r\n .replace(/^[a-z]/, (c: string) => c.toUpperCase())\r\n )\r\n .join('') || 'Index';\r\n}\r\n\r\n/**\r\n * 路径统一处理:生成的 Key 包含 viewsDir 目录名\r\n * 示例:/Users/me/project/src/views/User/Index.vue -> src/views/User/Index.vue\r\n */\r\nfunction normalizePath(file: string, viewsDir: string): string {\r\n // 1. 获取 viewsDir 的基础名称 (例如 'src/views')\r\n // 2. 获取文件相对于 viewsDir 的部分\r\n // 3. 拼接并统一分隔符\r\n const relativePath: string = path.relative(path.resolve(viewsDir), file);\r\n const combinedPath: string = path.join(viewsDir, relativePath);\r\n\r\n return combinedPath.split(path.sep).join('/');\r\n}\r\n","// Centralized plugin error types for better error handling and debugging\n\nexport class PluginError extends Error {\n public details?: any;\n constructor(message: string, details?: any) {\n super(message);\n this.name = 'PluginError';\n this.details = details;\n }\n}\n\nexport class ValidationError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'ValidationError';\n }\n}\n\nexport class IoError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'IoError';\n }\n}\n\nexport class LogicError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'LogicError';\n }\n}\n","import type { ViewNameMap } from './generate';\r\n\r\n/**\r\n * 根据扫描结果生成 virtual module 的源码字符串\r\n *\r\n * 设计说明:\r\n * - 不使用 default export,便于未来扩展更多具名导出\r\n * - 同时更利于 TypeScript module augmentation\r\n */\r\nexport function createVirtualModule(map: ViewNameMap): string {\r\n return `\r\nexport const viewNameMap = ${JSON.stringify(map, null, 2)};\r\n`;\r\n}\r\n","import fs from 'fs/promises';\nimport path from 'path';\nimport type { ViewNameMap } from './generate';\n\nexport interface DtsResolvedOptions {\n outputPath: string;\n filename: string;\n}\n\nexport function resolveDtsOptions(dts: boolean | string | undefined, root: string): DtsResolvedOptions {\n const defaultFilename = 'view-name-map.d.ts';\n const defaultOutputPath = path.join(root, 'types');\n\n if (dts === true || dts === undefined) {\n return {\n outputPath: defaultOutputPath,\n filename: defaultFilename\n };\n }\n\n if (dts === false) {\n return { outputPath: defaultOutputPath, filename: defaultFilename };\n }\n\n if (typeof dts === 'string') {\n if (dts.endsWith('.d.ts')) {\n const filename = path.basename(dts);\n const dir = path.resolve(root, path.dirname(dts));\n return { outputPath: dir, filename };\n }\n return { outputPath: path.resolve(root, dts), filename: defaultFilename };\n }\n\n return { outputPath: defaultOutputPath, filename: defaultFilename };\n}\n\nexport async function generateDtsFile(map: ViewNameMap, dtsOptions: DtsResolvedOptions): Promise<string> {\n const filePath = path.join(dtsOptions.outputPath, dtsOptions.filename);\n\n const entries = Object.entries(map).map(([k, v]) => ` \"${k}\": \"${v}\";`).join('\\n');\n\n const content = `// Generated by vite-plugin-view-name-map\ndeclare module 'virtual:view-name-map' {\n export type ViewNameMap = {\n${entries}\n };\n export const viewNameMap: ViewNameMap;\n}\n`;\n\n await fs.mkdir(dtsOptions.outputPath, { recursive: true });\n await fs.writeFile(filePath, content, 'utf-8');\n\n console.log(`\\x1b[32m[view-name-map] 类型声明文件已生成: ${filePath}\\x1b[0m`);\n\n return filePath;\n}\n"],"mappings":"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","names":["import_path","fg","fs","MagicString","path","import_promises","import_path","path","fs","path"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -10,6 +10,15 @@ interface ViewNameMapPluginOptions {
|
|
|
10
10
|
autoGenerateName?: boolean;
|
|
11
11
|
/** 是否自动注入 defineOptions 到组件中(影响运行时组件 name),默认 true */
|
|
12
12
|
autoInjectName?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 类型声明文件配置
|
|
15
|
+
* - true: 在 types/ 目录下生成 view-name-map.d.ts
|
|
16
|
+
* - string: 作为目录,在该目录下生成 view-name-map.d.ts
|
|
17
|
+
* - 以 .d.ts 结尾的 string: 直接作为文件路径
|
|
18
|
+
* - false: 不生成类型声明文件
|
|
19
|
+
* 默认值: true
|
|
20
|
+
*/
|
|
21
|
+
dts?: boolean | string;
|
|
13
22
|
}
|
|
14
23
|
/**
|
|
15
24
|
* vite-plugin-view-name-map
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,15 @@ interface ViewNameMapPluginOptions {
|
|
|
10
10
|
autoGenerateName?: boolean;
|
|
11
11
|
/** 是否自动注入 defineOptions 到组件中(影响运行时组件 name),默认 true */
|
|
12
12
|
autoInjectName?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 类型声明文件配置
|
|
15
|
+
* - true: 在 types/ 目录下生成 view-name-map.d.ts
|
|
16
|
+
* - string: 作为目录,在该目录下生成 view-name-map.d.ts
|
|
17
|
+
* - 以 .d.ts 结尾的 string: 直接作为文件路径
|
|
18
|
+
* - false: 不生成类型声明文件
|
|
19
|
+
* 默认值: true
|
|
20
|
+
*/
|
|
21
|
+
dts?: boolean | string;
|
|
13
22
|
}
|
|
14
23
|
/**
|
|
15
24
|
* vite-plugin-view-name-map
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { normalizePath as normalizePath2 } from "vite";
|
|
3
|
-
import
|
|
3
|
+
import path3 from "path";
|
|
4
4
|
|
|
5
5
|
// src/generate.ts
|
|
6
6
|
import path from "path";
|
|
@@ -175,6 +175,48 @@ export const viewNameMap = ${JSON.stringify(map, null, 2)};
|
|
|
175
175
|
`;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
// src/dts.ts
|
|
179
|
+
import fs2 from "fs/promises";
|
|
180
|
+
import path2 from "path";
|
|
181
|
+
function resolveDtsOptions(dts, root) {
|
|
182
|
+
const defaultFilename = "view-name-map.d.ts";
|
|
183
|
+
const defaultOutputPath = path2.join(root, "types");
|
|
184
|
+
if (dts === true || dts === void 0) {
|
|
185
|
+
return {
|
|
186
|
+
outputPath: defaultOutputPath,
|
|
187
|
+
filename: defaultFilename
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (dts === false) {
|
|
191
|
+
return { outputPath: defaultOutputPath, filename: defaultFilename };
|
|
192
|
+
}
|
|
193
|
+
if (typeof dts === "string") {
|
|
194
|
+
if (dts.endsWith(".d.ts")) {
|
|
195
|
+
const filename = path2.basename(dts);
|
|
196
|
+
const dir = path2.resolve(root, path2.dirname(dts));
|
|
197
|
+
return { outputPath: dir, filename };
|
|
198
|
+
}
|
|
199
|
+
return { outputPath: path2.resolve(root, dts), filename: defaultFilename };
|
|
200
|
+
}
|
|
201
|
+
return { outputPath: defaultOutputPath, filename: defaultFilename };
|
|
202
|
+
}
|
|
203
|
+
async function generateDtsFile(map, dtsOptions) {
|
|
204
|
+
const filePath = path2.join(dtsOptions.outputPath, dtsOptions.filename);
|
|
205
|
+
const entries = Object.entries(map).map(([k, v]) => ` "${k}": "${v}";`).join("\n");
|
|
206
|
+
const content = `// Generated by vite-plugin-view-name-map
|
|
207
|
+
declare module 'virtual:view-name-map' {
|
|
208
|
+
export type ViewNameMap = {
|
|
209
|
+
${entries}
|
|
210
|
+
};
|
|
211
|
+
export const viewNameMap: ViewNameMap;
|
|
212
|
+
}
|
|
213
|
+
`;
|
|
214
|
+
await fs2.mkdir(dtsOptions.outputPath, { recursive: true });
|
|
215
|
+
await fs2.writeFile(filePath, content, "utf-8");
|
|
216
|
+
console.log(`\x1B[32m[view-name-map] \u7C7B\u578B\u58F0\u660E\u6587\u4EF6\u5DF2\u751F\u6210: ${filePath}\x1B[0m`);
|
|
217
|
+
return filePath;
|
|
218
|
+
}
|
|
219
|
+
|
|
178
220
|
// src/index.ts
|
|
179
221
|
function viewNameMapPlugin(options = {}) {
|
|
180
222
|
const virtualId = "virtual:view-name-map";
|
|
@@ -183,7 +225,7 @@ function viewNameMapPlugin(options = {}) {
|
|
|
183
225
|
const autoInjectName = options.autoInjectName ?? true;
|
|
184
226
|
const autoGenerateName = autoInjectName ? true : options.autoGenerateName ?? true;
|
|
185
227
|
const root = normalizePath2(process.cwd());
|
|
186
|
-
const absoluteViewsDir = normalizePath2(
|
|
228
|
+
const absoluteViewsDir = normalizePath2(path3.resolve(root, viewsDir));
|
|
187
229
|
let virtualCode = "";
|
|
188
230
|
let nameMap = {};
|
|
189
231
|
let injectedCount = 0;
|
|
@@ -196,6 +238,10 @@ function viewNameMapPlugin(options = {}) {
|
|
|
196
238
|
autoGenerateName
|
|
197
239
|
});
|
|
198
240
|
virtualCode = createVirtualModule(nameMap);
|
|
241
|
+
const dtsOptions = resolveDtsOptions(options.dts, root);
|
|
242
|
+
if (options.dts !== false) {
|
|
243
|
+
await generateDtsFile(nameMap, dtsOptions);
|
|
244
|
+
}
|
|
199
245
|
const count = Object.keys(nameMap).length;
|
|
200
246
|
if (count > 0) {
|
|
201
247
|
const status = autoInjectName ? "\u5DF2\u5F00\u542F\u81EA\u52A8\u6CE8\u5165" : "\u4EC5\u751F\u6210\u6620\u5C04\u8868";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/generate.ts","../src/plugin-errors.ts","../src/virtual-module.ts"],"sourcesContent":["import type {Plugin} from 'vite';\r\nimport {normalizePath} from 'vite'; // 必须使用 vite 提供的路径归一化工具\r\nimport path from 'path';\r\nimport {generateViewNameMap, injectDefineOptions, generateNameFromFilePath, ViewNameMap} from './generate';\r\nimport {createVirtualModule} from './virtual-module';\r\n\r\n/**\r\n * 插件配置项\r\n */\r\nexport interface ViewNameMapPluginOptions {\r\n /** Vue 页面根目录,默认 'src/views' */\r\n viewsDir?: string;\r\n /** 是否自动生成未声明 name 的组件 name(影响虚拟模块映射表),默认 true */\r\n autoGenerateName?: boolean;\r\n /** 是否自动注入 defineOptions 到组件中(影响运行时组件 name),默认 true */\r\n autoInjectName?: boolean;\r\n}\r\n\r\n/**\r\n * vite-plugin-view-name-map\r\n *\r\n * 构建期扫描 views 目录,提取组件 name 并生成 virtual module\r\n */\r\nexport default function viewNameMapPlugin(\r\n options: ViewNameMapPluginOptions = {}\r\n): Plugin {\r\n const virtualId = 'virtual:view-name-map';\r\n const resolvedVirtualId = '\\0' + virtualId;\r\n\r\n // 统一转换为正斜杠路径\r\n const viewsDir = options.viewsDir ?? 'src/views';\r\n const autoInjectName = options.autoInjectName ?? true;\r\n\r\n // 核心约束:如果开启了自动注入,则必须开启自动生成,否则注入没有数据源\r\n const autoGenerateName = autoInjectName ? true : (options.autoGenerateName ?? true);\r\n\r\n const root = normalizePath(process.cwd());\r\n const absoluteViewsDir = normalizePath(path.resolve(root, viewsDir));\r\n\r\n let virtualCode = '';\r\n let nameMap: ViewNameMap = {};\r\n let injectedCount = 0; // 注入计数器\r\n\r\n return {\r\n name: 'vite-plugin-view-name-map',\r\n enforce: 'pre',\r\n\r\n async buildStart(): Promise<void> {\r\n // 初始扫描\r\n nameMap = await generateViewNameMap({\r\n viewsDir: viewsDir,\r\n autoGenerateName: autoGenerateName\r\n });\r\n virtualCode = createVirtualModule(nameMap);\r\n\r\n // 打印初始化统计信息\r\n const count = Object.keys(nameMap).length;\r\n if (count > 0) {\r\n const status = autoInjectName ? '已开启自动注入' : '仅生成映射表';\r\n console.log(`\\x1b[32m[view-name-map] ${status}:扫描到 ${count} 个视图组件。\\x1b[0m`);\r\n }\r\n },\r\n\r\n resolveId(id: string): string | undefined {\r\n if (id === virtualId) return resolvedVirtualId;\r\n return undefined;\r\n },\r\n\r\n load(id: string): string | undefined {\r\n if (id === resolvedVirtualId) return virtualCode;\r\n return undefined;\r\n },\r\n\r\n async transform(code: string, id: string) {\r\n // 只有开启注入时才继续执行 transform 逻辑\r\n if (!autoInjectName) return null;\r\n\r\n const cleanId = normalizePath(id.split('?')[0]);\r\n\r\n // 过滤非目标文件\r\n if (!cleanId.endsWith('.vue') || !cleanId.startsWith(absoluteViewsDir)) {\r\n return null;\r\n }\r\n\r\n // 获取组件名称\r\n const componentName = generateNameFromFilePath(cleanId, absoluteViewsDir);\r\n\r\n try {\r\n // 执行 AST 注入\r\n const result = injectDefineOptions(code, cleanId, componentName);\r\n\r\n // 如果返回了结果,说明确实进行了注入(非 null)\r\n if (result) {\r\n injectedCount++;\r\n }\r\n\r\n return result;\r\n } catch (err) {\r\n console.error(`\\x1b[31m[view-name-map] 注入异常: ${cleanId}\\x1b[0m`, err);\r\n return null;\r\n }\r\n },\r\n\r\n // 在构建结束时打印统计信息\r\n buildEnd() {\r\n // 仅在开发/构建完成且有注入行为时输出\r\n if (autoInjectName && injectedCount > 0) {\r\n console.log(`\\x1b[36m[view-name-map] 本次运行已为 ${injectedCount} 个组件注入了 defineOptions({ name: \"...\" })。\\x1b[0m`);\r\n }\r\n }\r\n };\r\n}\r\n","import path from 'path';\nimport fs from 'fs/promises';\nimport fg from 'fast-glob';\nimport MagicString from 'magic-string'; // 需要安装: npm install magic-string\nimport {parse} from '@vue/compiler-sfc'; // Vue 自带\nimport {CallExpression, Expression, Node, ObjectLiteralExpression, Project, SourceFile, SyntaxKind} from 'ts-morph';\nimport {PluginError, ValidationError, IoError, LogicError} from './plugin-errors';\n\r\n/** 视图 name 映射表类型 */\r\nexport type ViewNameMap = Record<string, string>;\r\n\r\n/** 构建期生成逻辑配置 */\r\nexport interface GenerateOptions {\n viewsDir?: string;\n autoGenerateName?: boolean;\n}\n\n// 内部的配置校验,确保输入类型及字段符合期望,降低运行时错误\nfunction ensureGenerateOptionsValid(options: GenerateOptions): void {\n // Runtime type checks using loose typing to avoid TypeScript narrowing pitfalls\n const viewsDirAny: any = (options as any).viewsDir;\n if (viewsDirAny !== undefined && typeof viewsDirAny !== 'string') {\n throw new ValidationError('Invalid type for options.viewsDir; expected string', {\n receivedType: typeof viewsDirAny,\n receivedValue: viewsDirAny,\n });\n }\n const autoGenAny: any = (options as any).autoGenerateName;\n if (autoGenAny !== undefined && typeof autoGenAny !== 'boolean') {\n throw new ValidationError('Invalid type for options.autoGenerateName; expected boolean', {\n receivedType: typeof autoGenAny,\n receivedValue: autoGenAny,\n });\n }\n}\n\r\n/**\r\n * 扫描 views 目录生成组件 name 映射表\r\n */\r\nexport async function generateViewNameMap(\n options: GenerateOptions = {}\n): Promise<ViewNameMap> {\n // 参数校验,尽早抛出可追踪的错误\n ensureGenerateOptionsValid(options);\n\n try {\n const viewsDir: string = options.viewsDir ?? 'src/views';\n const autoGen: boolean = options.autoGenerateName ?? true;\n\n const map: ViewNameMap = {};\n const files: string[] = await fg(['**/*.vue', '**/*.tsx'], {cwd: viewsDir, absolute: true});\n\n for (const file of files) {\n let content: string;\n try {\n content = await fs.readFile(file, 'utf-8');\n } catch (err) {\n throw new IoError(`Failed to read file: ${file}`, { cause: err });\n }\n\n // 优先尝试从 Vue SFC 的 scriptSetup 提取 defineOptions.name\n let name: string | undefined = extractNameFromDefineOptionsInSfc(content);\n // 兜底:更直接的全文搜索作为额外鲁棒性补充\n if (!name) {\n const m2 = content.match(/defineOptions\\s*\\(\\s*\\{[^\\}]*name\\s*:\\s*['\"]([^'\"]+)['\"][^\\}]*\\}\\s*\\)/m);\n if (m2) name = m2[1];\n }\n\n // 兜底:若未从 defineOptions 提取,且开启自动命名,则基于文件路径生成名称\n if (!name && autoGen) {\n name = generateNameFromFilePath(file, viewsDir);\n }\n\n if (name) map[normalizePath(file, viewsDir)] = name;\n }\n\n return map;\n } catch (err) {\n // 分类错误并提供上下文,便于定位问题\n if (err instanceof PluginError) throw err;\n throw new LogicError('Failed to generate view name map', { cause: err });\n }\n}\n\r\n/**\r\n * 在 transform 阶段注入 defineOptions (不写入文件)\r\n */\r\nexport function injectDefineOptions(code: string, id: string, name: string) {\n const {descriptor} = parse(code);\r\n if (!descriptor.scriptSetup) return null;\r\n\r\n const content = descriptor.scriptSetup.content;\r\n const startOffset = descriptor.scriptSetup.loc.start.offset;\r\n\r\n // 使用 ts-morph 分析 scriptSetup 内容\r\n const project = new Project({useInMemoryFileSystem: true});\r\n const sourceFile = project.createSourceFile('temp.ts', content);\r\n\r\n // 检查是否已有 defineOptions\r\n const hasName = !!extractNameFromDefineOptions(sourceFile);\r\n if (hasName) return null;\r\n\r\n const s = new MagicString(code);\r\n const defineOptionsCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)\r\n .find(c => c.getExpression().getText() === 'defineOptions');\r\n\r\n if (defineOptionsCall) {\r\n // 情况 A: 已有 defineOptions 但没写 name 属性\r\n const arg = defineOptionsCall.getArguments()[0];\r\n if (Node.isObjectLiteralExpression(arg)) {\r\n const properties = arg.getProperties();\r\n const pos = startOffset + arg.getStart() + 1; // '{' 之后\r\n\r\n if (properties.length === 0) {\r\n // 空对象直接插入\r\n s.appendRight(pos, ` name: '${name}' `);\r\n } else {\r\n // 已有属性,必须补充逗号分隔\r\n s.appendRight(pos, ` name: '${name}', `);\r\n }\r\n }\r\n } else {\r\n // 情况 B: 完全没有 defineOptions,在 scriptSetup 顶部注入\r\n s.appendLeft(startOffset, `\\ndefineOptions({ name: '${name}' });\\n`);\r\n }\r\n\r\n return {\n code: s.toString(),\n map: s.generateMap({source: id, includeContent: true, hires: true})\n };\n}\n\r\n/**\r\n * 从 defineOptions 宏定义中提取组件 name 属性值\r\n */\r\nfunction extractNameFromDefineOptions(sourceFile: SourceFile): string | undefined {\n // 获取源文件中所有的调用表达式\r\n const calls: CallExpression[] = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n\r\n for (const call of calls) {\r\n const expression: Expression = call.getExpression();\r\n // 匹配 defineOptions 调用\r\n if (!expression || expression.getText() !== 'defineOptions') continue;\r\n\r\n const args = call.getArguments();\r\n if (args.length === 0) continue;\r\n\r\n // 获取第一个对象字面量参数\r\n const obj: ObjectLiteralExpression | undefined = args[0]?.asKind(SyntaxKind.ObjectLiteralExpression);\r\n if (!obj) continue;\r\n\r\n // 查找 name 属性并确保其为标准的 key: value 赋值形式\r\n const nameProp = obj.getProperty('name');\r\n if (nameProp && Node.isPropertyAssignment(nameProp)) {\r\n const initializer = nameProp.getInitializer();\r\n if (initializer && Node.isStringLiteral(initializer)) {\r\n return initializer.getLiteralText();\r\n }\r\n }\r\n }\r\n\r\n return undefined;\r\n}\n\n/**\n * Try to extract name from defineOptions within a Vue SFC's scriptSetup block.\n * It parses the <script setup> content using ts-morph to find defineOptions({ name: '...' }).\n */\nfunction extractNameFromDefineOptionsInSfc(content: string): string | undefined {\n try {\n const {descriptor} = parse(content);\n if (!descriptor.scriptSetup) return undefined;\n const scriptContent: string = descriptor.scriptSetup.content;\n // 直接正则匹配,兜底提升鲁棒性\n const regex = /defineOptions\\s*\\(\\s*\\{\\s*name\\s*:\\s*['\"]([^'\"]+)['\"]\\s*\\}\\s*\\)/;\n const m = scriptContent.match(regex);\n if (m) return m[1];\n\n // 兜底:再尝试通过 ts-morph 解析脚本块\n const project = new Project({useInMemoryFileSystem: true});\n const sourceFile = project.createSourceFile('temp.ts', scriptContent);\n return extractNameFromDefineOptions(sourceFile);\n } catch {\n return undefined;\n }\n}\n\n/**\r\n * 根据文件路径生成组件 name (PascalCase)\r\n * 规则:\r\n * - 保持层级关系\r\n * - 移除 index 后缀\r\n * - 自动处理路径中的横杠、下划线为大驼峰\r\n */\r\nexport function generateNameFromFilePath(file: string, viewsDir: string): string {\r\n const absoluteViewsDir: string = path.resolve(viewsDir);\r\n const relativePath: string = path.relative(absoluteViewsDir, file);\r\n const {dir, name} = path.parse(relativePath);\r\n\r\n // 拆分层级\r\n const segments: string[] = dir ? dir.split(path.sep) : [];\r\n if (name !== 'index') {\r\n segments.push(name);\r\n }\r\n\r\n // 转换每一级为 PascalCase 并拼接\r\n return segments\r\n .map((s: string) => s\r\n // 处理内部横杠或下划线: sys-user -> sysUser\r\n .replace(/[-_](.)/g, (_: string, c: string) => c.toUpperCase())\r\n // 首字母大写: sysUser -> SysUser\r\n .replace(/^[a-z]/, (c: string) => c.toUpperCase())\r\n )\r\n .join('') || 'Index';\r\n}\r\n\r\n/**\r\n * 路径统一处理:生成的 Key 包含 viewsDir 目录名\r\n * 示例:/Users/me/project/src/views/User/Index.vue -> src/views/User/Index.vue\r\n */\r\nfunction normalizePath(file: string, viewsDir: string): string {\r\n // 1. 获取 viewsDir 的基础名称 (例如 'src/views')\r\n // 2. 获取文件相对于 viewsDir 的部分\r\n // 3. 拼接并统一分隔符\r\n const relativePath: string = path.relative(path.resolve(viewsDir), file);\r\n const combinedPath: string = path.join(viewsDir, relativePath);\r\n\r\n return combinedPath.split(path.sep).join('/');\r\n}\r\n","// Centralized plugin error types for better error handling and debugging\n\nexport class PluginError extends Error {\n public details?: any;\n constructor(message: string, details?: any) {\n super(message);\n this.name = 'PluginError';\n this.details = details;\n }\n}\n\nexport class ValidationError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'ValidationError';\n }\n}\n\nexport class IoError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'IoError';\n }\n}\n\nexport class LogicError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'LogicError';\n }\n}\n","import type { ViewNameMap } from './generate';\r\n\r\n/**\r\n * 根据扫描结果生成 virtual module 的源码字符串\r\n *\r\n * 设计说明:\r\n * - 不使用 default export,便于未来扩展更多具名导出\r\n * - 同时更利于 TypeScript module augmentation\r\n */\r\nexport function createVirtualModule(map: ViewNameMap): string {\r\n return `\r\nexport const viewNameMap = ${JSON.stringify(map, null, 2)};\r\n`;\r\n}\r\n"],"mappings":"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","names":["normalizePath","path","normalizePath","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/generate.ts","../src/plugin-errors.ts","../src/virtual-module.ts","../src/dts.ts"],"sourcesContent":["import type {Plugin} from 'vite';\r\nimport {normalizePath} from 'vite'; // 必须使用 vite 提供的路径归一化工具\r\nimport path from 'path';\r\nimport {generateViewNameMap, injectDefineOptions, generateNameFromFilePath, ViewNameMap} from './generate';\r\nimport {createVirtualModule} from './virtual-module';\r\nimport { generateDtsFile, resolveDtsOptions } from './dts';\r\n\r\n/**\r\n * 插件配置项\r\n */\r\nexport interface ViewNameMapPluginOptions {\r\n /** Vue 页面根目录,默认 'src/views' */\r\n viewsDir?: string;\r\n /** 是否自动生成未声明 name 的组件 name(影响虚拟模块映射表),默认 true */\r\n autoGenerateName?: boolean;\r\n /** 是否自动注入 defineOptions 到组件中(影响运行时组件 name),默认 true */\r\n autoInjectName?: boolean;\r\n /**\r\n * 类型声明文件配置\r\n * - true: 在 types/ 目录下生成 view-name-map.d.ts\r\n * - string: 作为目录,在该目录下生成 view-name-map.d.ts\r\n * - 以 .d.ts 结尾的 string: 直接作为文件路径\r\n * - false: 不生成类型声明文件\r\n * 默认值: true\r\n */\r\n dts?: boolean | string;\r\n}\r\n\r\n/**\r\n * vite-plugin-view-name-map\r\n *\r\n * 构建期扫描 views 目录,提取组件 name 并生成 virtual module\r\n */\r\nexport default function viewNameMapPlugin(\r\n options: ViewNameMapPluginOptions = {}\r\n): Plugin {\r\n const virtualId = 'virtual:view-name-map';\r\n const resolvedVirtualId = '\\0' + virtualId;\r\n\r\n // 统一转换为正斜杠路径\r\n const viewsDir = options.viewsDir ?? 'src/views';\r\n const autoInjectName = options.autoInjectName ?? true;\r\n\r\n // 核心约束:如果开启了自动注入,则必须开启自动生成,否则注入没有数据源\r\n const autoGenerateName = autoInjectName ? true : (options.autoGenerateName ?? true);\r\n\r\n const root = normalizePath(process.cwd());\r\n const absoluteViewsDir = normalizePath(path.resolve(root, viewsDir));\r\n\r\n let virtualCode = '';\r\n let nameMap: ViewNameMap = {};\r\n let injectedCount = 0; // 注入计数器\r\n\r\n return {\r\n name: 'vite-plugin-view-name-map',\r\n enforce: 'pre',\r\n\r\n async buildStart(): Promise<void> {\r\n // 初始扫描\r\n nameMap = await generateViewNameMap({\r\n viewsDir: viewsDir,\r\n autoGenerateName: autoGenerateName\r\n });\r\n virtualCode = createVirtualModule(nameMap);\r\n\r\n // 生成类型声明文件\r\n const dtsOptions = resolveDtsOptions(options.dts, root);\r\n if (options.dts !== false) {\r\n await generateDtsFile(nameMap, dtsOptions);\r\n }\r\n\r\n // 打印初始化统计信息\r\n const count = Object.keys(nameMap).length;\r\n if (count > 0) {\r\n const status = autoInjectName ? '已开启自动注入' : '仅生成映射表';\r\n console.log(`\\x1b[32m[view-name-map] ${status}:扫描到 ${count} 个视图组件。\\x1b[0m`);\r\n }\r\n },\r\n\r\n resolveId(id: string): string | undefined {\r\n if (id === virtualId) return resolvedVirtualId;\r\n return undefined;\r\n },\r\n\r\n load(id: string): string | undefined {\r\n if (id === resolvedVirtualId) return virtualCode;\r\n return undefined;\r\n },\r\n\r\n async transform(code: string, id: string) {\r\n // 只有开启注入时才继续执行 transform 逻辑\r\n if (!autoInjectName) return null;\r\n\r\n const cleanId = normalizePath(id.split('?')[0]);\r\n\r\n // 过滤非目标文件\r\n if (!cleanId.endsWith('.vue') || !cleanId.startsWith(absoluteViewsDir)) {\r\n return null;\r\n }\r\n\r\n // 获取组件名称\r\n const componentName = generateNameFromFilePath(cleanId, absoluteViewsDir);\r\n\r\n try {\r\n // 执行 AST 注入\r\n const result = injectDefineOptions(code, cleanId, componentName);\r\n\r\n // 如果返回了结果,说明确实进行了注入(非 null)\r\n if (result) {\r\n injectedCount++;\r\n }\r\n\r\n return result;\r\n } catch (err) {\r\n console.error(`\\x1b[31m[view-name-map] 注入异常: ${cleanId}\\x1b[0m`, err);\r\n return null;\r\n }\r\n },\r\n\r\n // 在构建结束时打印统计信息\r\n buildEnd() {\r\n // 仅在开发/构建完成且有注入行为时输出\r\n if (autoInjectName && injectedCount > 0) {\r\n console.log(`\\x1b[36m[view-name-map] 本次运行已为 ${injectedCount} 个组件注入了 defineOptions({ name: \"...\" })。\\x1b[0m`);\r\n }\r\n }\r\n };\r\n}\r\n","import path from 'path';\nimport fs from 'fs/promises';\nimport fg from 'fast-glob';\nimport MagicString from 'magic-string'; // 需要安装: npm install magic-string\nimport {parse} from '@vue/compiler-sfc'; // Vue 自带\nimport {CallExpression, Expression, Node, ObjectLiteralExpression, Project, SourceFile, SyntaxKind} from 'ts-morph';\nimport {PluginError, ValidationError, IoError, LogicError} from './plugin-errors';\n\r\n/** 视图 name 映射表类型 */\r\nexport type ViewNameMap = Record<string, string>;\r\n\r\n/** 构建期生成逻辑配置 */\r\nexport interface GenerateOptions {\n viewsDir?: string;\n autoGenerateName?: boolean;\n}\n\n// 内部的配置校验,确保输入类型及字段符合期望,降低运行时错误\nfunction ensureGenerateOptionsValid(options: GenerateOptions): void {\n // Runtime type checks using loose typing to avoid TypeScript narrowing pitfalls\n const viewsDirAny: any = (options as any).viewsDir;\n if (viewsDirAny !== undefined && typeof viewsDirAny !== 'string') {\n throw new ValidationError('Invalid type for options.viewsDir; expected string', {\n receivedType: typeof viewsDirAny,\n receivedValue: viewsDirAny,\n });\n }\n const autoGenAny: any = (options as any).autoGenerateName;\n if (autoGenAny !== undefined && typeof autoGenAny !== 'boolean') {\n throw new ValidationError('Invalid type for options.autoGenerateName; expected boolean', {\n receivedType: typeof autoGenAny,\n receivedValue: autoGenAny,\n });\n }\n}\n\r\n/**\r\n * 扫描 views 目录生成组件 name 映射表\r\n */\r\nexport async function generateViewNameMap(\n options: GenerateOptions = {}\n): Promise<ViewNameMap> {\n // 参数校验,尽早抛出可追踪的错误\n ensureGenerateOptionsValid(options);\n\n try {\n const viewsDir: string = options.viewsDir ?? 'src/views';\n const autoGen: boolean = options.autoGenerateName ?? true;\n\n const map: ViewNameMap = {};\n const files: string[] = await fg(['**/*.vue', '**/*.tsx'], {cwd: viewsDir, absolute: true});\n\n for (const file of files) {\n let content: string;\n try {\n content = await fs.readFile(file, 'utf-8');\n } catch (err) {\n throw new IoError(`Failed to read file: ${file}`, { cause: err });\n }\n\n // 优先尝试从 Vue SFC 的 scriptSetup 提取 defineOptions.name\n let name: string | undefined = extractNameFromDefineOptionsInSfc(content);\n // 兜底:更直接的全文搜索作为额外鲁棒性补充\n if (!name) {\n const m2 = content.match(/defineOptions\\s*\\(\\s*\\{[^\\}]*name\\s*:\\s*['\"]([^'\"]+)['\"][^\\}]*\\}\\s*\\)/m);\n if (m2) name = m2[1];\n }\n\n // 兜底:若未从 defineOptions 提取,且开启自动命名,则基于文件路径生成名称\n if (!name && autoGen) {\n name = generateNameFromFilePath(file, viewsDir);\n }\n\n if (name) map[normalizePath(file, viewsDir)] = name;\n }\n\n return map;\n } catch (err) {\n // 分类错误并提供上下文,便于定位问题\n if (err instanceof PluginError) throw err;\n throw new LogicError('Failed to generate view name map', { cause: err });\n }\n}\n\r\n/**\r\n * 在 transform 阶段注入 defineOptions (不写入文件)\r\n */\r\nexport function injectDefineOptions(code: string, id: string, name: string) {\n const {descriptor} = parse(code);\r\n if (!descriptor.scriptSetup) return null;\r\n\r\n const content = descriptor.scriptSetup.content;\r\n const startOffset = descriptor.scriptSetup.loc.start.offset;\r\n\r\n // 使用 ts-morph 分析 scriptSetup 内容\r\n const project = new Project({useInMemoryFileSystem: true});\r\n const sourceFile = project.createSourceFile('temp.ts', content);\r\n\r\n // 检查是否已有 defineOptions\r\n const hasName = !!extractNameFromDefineOptions(sourceFile);\r\n if (hasName) return null;\r\n\r\n const s = new MagicString(code);\r\n const defineOptionsCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)\r\n .find(c => c.getExpression().getText() === 'defineOptions');\r\n\r\n if (defineOptionsCall) {\r\n // 情况 A: 已有 defineOptions 但没写 name 属性\r\n const arg = defineOptionsCall.getArguments()[0];\r\n if (Node.isObjectLiteralExpression(arg)) {\r\n const properties = arg.getProperties();\r\n const pos = startOffset + arg.getStart() + 1; // '{' 之后\r\n\r\n if (properties.length === 0) {\r\n // 空对象直接插入\r\n s.appendRight(pos, ` name: '${name}' `);\r\n } else {\r\n // 已有属性,必须补充逗号分隔\r\n s.appendRight(pos, ` name: '${name}', `);\r\n }\r\n }\r\n } else {\r\n // 情况 B: 完全没有 defineOptions,在 scriptSetup 顶部注入\r\n s.appendLeft(startOffset, `\\ndefineOptions({ name: '${name}' });\\n`);\r\n }\r\n\r\n return {\n code: s.toString(),\n map: s.generateMap({source: id, includeContent: true, hires: true})\n };\n}\n\r\n/**\r\n * 从 defineOptions 宏定义中提取组件 name 属性值\r\n */\r\nfunction extractNameFromDefineOptions(sourceFile: SourceFile): string | undefined {\n // 获取源文件中所有的调用表达式\r\n const calls: CallExpression[] = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\r\n\r\n for (const call of calls) {\r\n const expression: Expression = call.getExpression();\r\n // 匹配 defineOptions 调用\r\n if (!expression || expression.getText() !== 'defineOptions') continue;\r\n\r\n const args = call.getArguments();\r\n if (args.length === 0) continue;\r\n\r\n // 获取第一个对象字面量参数\r\n const obj: ObjectLiteralExpression | undefined = args[0]?.asKind(SyntaxKind.ObjectLiteralExpression);\r\n if (!obj) continue;\r\n\r\n // 查找 name 属性并确保其为标准的 key: value 赋值形式\r\n const nameProp = obj.getProperty('name');\r\n if (nameProp && Node.isPropertyAssignment(nameProp)) {\r\n const initializer = nameProp.getInitializer();\r\n if (initializer && Node.isStringLiteral(initializer)) {\r\n return initializer.getLiteralText();\r\n }\r\n }\r\n }\r\n\r\n return undefined;\r\n}\n\n/**\n * Try to extract name from defineOptions within a Vue SFC's scriptSetup block.\n * It parses the <script setup> content using ts-morph to find defineOptions({ name: '...' }).\n */\nfunction extractNameFromDefineOptionsInSfc(content: string): string | undefined {\n try {\n const {descriptor} = parse(content);\n if (!descriptor.scriptSetup) return undefined;\n const scriptContent: string = descriptor.scriptSetup.content;\n // 直接正则匹配,兜底提升鲁棒性\n const regex = /defineOptions\\s*\\(\\s*\\{\\s*name\\s*:\\s*['\"]([^'\"]+)['\"]\\s*\\}\\s*\\)/;\n const m = scriptContent.match(regex);\n if (m) return m[1];\n\n // 兜底:再尝试通过 ts-morph 解析脚本块\n const project = new Project({useInMemoryFileSystem: true});\n const sourceFile = project.createSourceFile('temp.ts', scriptContent);\n return extractNameFromDefineOptions(sourceFile);\n } catch {\n return undefined;\n }\n}\n\n/**\r\n * 根据文件路径生成组件 name (PascalCase)\r\n * 规则:\r\n * - 保持层级关系\r\n * - 移除 index 后缀\r\n * - 自动处理路径中的横杠、下划线为大驼峰\r\n */\r\nexport function generateNameFromFilePath(file: string, viewsDir: string): string {\r\n const absoluteViewsDir: string = path.resolve(viewsDir);\r\n const relativePath: string = path.relative(absoluteViewsDir, file);\r\n const {dir, name} = path.parse(relativePath);\r\n\r\n // 拆分层级\r\n const segments: string[] = dir ? dir.split(path.sep) : [];\r\n if (name !== 'index') {\r\n segments.push(name);\r\n }\r\n\r\n // 转换每一级为 PascalCase 并拼接\r\n return segments\r\n .map((s: string) => s\r\n // 处理内部横杠或下划线: sys-user -> sysUser\r\n .replace(/[-_](.)/g, (_: string, c: string) => c.toUpperCase())\r\n // 首字母大写: sysUser -> SysUser\r\n .replace(/^[a-z]/, (c: string) => c.toUpperCase())\r\n )\r\n .join('') || 'Index';\r\n}\r\n\r\n/**\r\n * 路径统一处理:生成的 Key 包含 viewsDir 目录名\r\n * 示例:/Users/me/project/src/views/User/Index.vue -> src/views/User/Index.vue\r\n */\r\nfunction normalizePath(file: string, viewsDir: string): string {\r\n // 1. 获取 viewsDir 的基础名称 (例如 'src/views')\r\n // 2. 获取文件相对于 viewsDir 的部分\r\n // 3. 拼接并统一分隔符\r\n const relativePath: string = path.relative(path.resolve(viewsDir), file);\r\n const combinedPath: string = path.join(viewsDir, relativePath);\r\n\r\n return combinedPath.split(path.sep).join('/');\r\n}\r\n","// Centralized plugin error types for better error handling and debugging\n\nexport class PluginError extends Error {\n public details?: any;\n constructor(message: string, details?: any) {\n super(message);\n this.name = 'PluginError';\n this.details = details;\n }\n}\n\nexport class ValidationError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'ValidationError';\n }\n}\n\nexport class IoError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'IoError';\n }\n}\n\nexport class LogicError extends PluginError {\n constructor(message: string, details?: any) {\n super(message, details);\n this.name = 'LogicError';\n }\n}\n","import type { ViewNameMap } from './generate';\r\n\r\n/**\r\n * 根据扫描结果生成 virtual module 的源码字符串\r\n *\r\n * 设计说明:\r\n * - 不使用 default export,便于未来扩展更多具名导出\r\n * - 同时更利于 TypeScript module augmentation\r\n */\r\nexport function createVirtualModule(map: ViewNameMap): string {\r\n return `\r\nexport const viewNameMap = ${JSON.stringify(map, null, 2)};\r\n`;\r\n}\r\n","import fs from 'fs/promises';\nimport path from 'path';\nimport type { ViewNameMap } from './generate';\n\nexport interface DtsResolvedOptions {\n outputPath: string;\n filename: string;\n}\n\nexport function resolveDtsOptions(dts: boolean | string | undefined, root: string): DtsResolvedOptions {\n const defaultFilename = 'view-name-map.d.ts';\n const defaultOutputPath = path.join(root, 'types');\n\n if (dts === true || dts === undefined) {\n return {\n outputPath: defaultOutputPath,\n filename: defaultFilename\n };\n }\n\n if (dts === false) {\n return { outputPath: defaultOutputPath, filename: defaultFilename };\n }\n\n if (typeof dts === 'string') {\n if (dts.endsWith('.d.ts')) {\n const filename = path.basename(dts);\n const dir = path.resolve(root, path.dirname(dts));\n return { outputPath: dir, filename };\n }\n return { outputPath: path.resolve(root, dts), filename: defaultFilename };\n }\n\n return { outputPath: defaultOutputPath, filename: defaultFilename };\n}\n\nexport async function generateDtsFile(map: ViewNameMap, dtsOptions: DtsResolvedOptions): Promise<string> {\n const filePath = path.join(dtsOptions.outputPath, dtsOptions.filename);\n\n const entries = Object.entries(map).map(([k, v]) => ` \"${k}\": \"${v}\";`).join('\\n');\n\n const content = `// Generated by vite-plugin-view-name-map\ndeclare module 'virtual:view-name-map' {\n export type ViewNameMap = {\n${entries}\n };\n export const viewNameMap: ViewNameMap;\n}\n`;\n\n await fs.mkdir(dtsOptions.outputPath, { recursive: true });\n await fs.writeFile(filePath, content, 'utf-8');\n\n console.log(`\\x1b[32m[view-name-map] 类型声明文件已生成: ${filePath}\\x1b[0m`);\n\n return filePath;\n}\n"],"mappings":"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","names":["normalizePath","path","fs","path","normalizePath","path"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@v-long/vite-plugin-view-name-map",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vite plugin to generate view-name-map from Vue SFC defineOptions",
|
|
6
6
|
"keywords": [
|
|
@@ -15,17 +15,13 @@
|
|
|
15
15
|
"module": "./dist/index.js",
|
|
16
16
|
"types": "./dist/index.d.ts",
|
|
17
17
|
"files": [
|
|
18
|
-
"dist"
|
|
19
|
-
"types"
|
|
18
|
+
"dist"
|
|
20
19
|
],
|
|
21
20
|
"exports": {
|
|
22
21
|
".": {
|
|
23
22
|
"types": "./dist/index.d.ts",
|
|
24
23
|
"import": "./dist/index.js",
|
|
25
24
|
"require": "./dist/index.cjs"
|
|
26
|
-
},
|
|
27
|
-
"./client": {
|
|
28
|
-
"types": "./dist/client.d.ts"
|
|
29
25
|
}
|
|
30
26
|
},
|
|
31
27
|
"peerDependencies": {
|
package/dist/client.cjs
DELETED
package/dist/client.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/client.d.cts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* virtual:view-name-map 的基础类型声明
|
|
3
|
-
*
|
|
4
|
-
* 设计原则:
|
|
5
|
-
* - 插件侧不包含任何业务信息
|
|
6
|
-
* - 仅提供可被业务侧增强的类型锚点
|
|
7
|
-
*/
|
|
8
|
-
declare module 'virtual:view-name-map' {
|
|
9
|
-
/**
|
|
10
|
-
* 视图 name 映射表类型
|
|
11
|
-
* key:规范化路径
|
|
12
|
-
* value:组件 defineOptions 中声明的 name
|
|
13
|
-
*/
|
|
14
|
-
export type ViewNameMap = Record<string, string>;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* 构建期生成的视图 name 映射表
|
|
18
|
-
*/
|
|
19
|
-
export const viewNameMap: ViewNameMap;
|
|
20
|
-
}
|
package/dist/client.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* virtual:view-name-map 的基础类型声明
|
|
3
|
-
*
|
|
4
|
-
* 设计原则:
|
|
5
|
-
* - 插件侧不包含任何业务信息
|
|
6
|
-
* - 仅提供可被业务侧增强的类型锚点
|
|
7
|
-
*/
|
|
8
|
-
declare module 'virtual:view-name-map' {
|
|
9
|
-
/**
|
|
10
|
-
* 视图 name 映射表类型
|
|
11
|
-
* key:规范化路径
|
|
12
|
-
* value:组件 defineOptions 中声明的 name
|
|
13
|
-
*/
|
|
14
|
-
export type ViewNameMap = Record<string, string>;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* 构建期生成的视图 name 映射表
|
|
18
|
-
*/
|
|
19
|
-
export const viewNameMap: ViewNameMap;
|
|
20
|
-
}
|
package/dist/client.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/types/client.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* virtual:view-name-map 的基础类型声明
|
|
3
|
-
*
|
|
4
|
-
* 设计原则:
|
|
5
|
-
* - 插件侧不包含任何业务信息
|
|
6
|
-
* - 仅提供可被业务侧增强的类型锚点
|
|
7
|
-
*/
|
|
8
|
-
declare module 'virtual:view-name-map' {
|
|
9
|
-
/**
|
|
10
|
-
* 视图 name 映射表类型
|
|
11
|
-
* key:规范化路径
|
|
12
|
-
* value:组件 defineOptions 中声明的 name
|
|
13
|
-
*/
|
|
14
|
-
export type ViewNameMap = Record<string, string>;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* 构建期生成的视图 name 映射表
|
|
18
|
-
*/
|
|
19
|
-
export const viewNameMap: ViewNameMap;
|
|
20
|
-
}
|