@eycraf/permission-kit-vite-plugin 0.0.1 → 0.0.2
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 +142 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +142 -52
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -114,25 +114,62 @@ var PermissionState = class {
|
|
|
114
114
|
getUsedPermissions() {
|
|
115
115
|
return [...new Set(this.getAllUsages().map((item) => item.permission))].sort();
|
|
116
116
|
}
|
|
117
|
+
getSummary() {
|
|
118
|
+
const usages = this.getAllUsages();
|
|
119
|
+
return {
|
|
120
|
+
permissions: new Set(usages.map((item) => item.permission)).size,
|
|
121
|
+
usages: usages.length,
|
|
122
|
+
files: new Set(usages.map((item) => item.file)).size
|
|
123
|
+
};
|
|
124
|
+
}
|
|
117
125
|
toManifest() {
|
|
118
|
-
|
|
119
|
-
const result = {};
|
|
126
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
120
127
|
for (const usage of this.getAllUsages()) {
|
|
121
|
-
const items =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
};
|
|
125
|
-
if (usage.line !== void 0) {
|
|
126
|
-
entry.line = usage.line;
|
|
127
|
-
}
|
|
128
|
-
if (usage.column !== void 0) {
|
|
129
|
-
entry.column = usage.column;
|
|
130
|
-
}
|
|
131
|
-
items.push(entry);
|
|
128
|
+
const items = grouped.get(usage.permission) ?? [];
|
|
129
|
+
items.push(usage);
|
|
130
|
+
grouped.set(usage.permission, items);
|
|
132
131
|
}
|
|
133
|
-
return
|
|
132
|
+
return [...grouped.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([permission, usages]) => {
|
|
133
|
+
const sortedUsages = [...usages].sort((left, right) => compareUsage(left, right));
|
|
134
|
+
const files = [...new Set(sortedUsages.map((item) => item.file))].sort(
|
|
135
|
+
(left, right) => left.localeCompare(right)
|
|
136
|
+
);
|
|
137
|
+
return {
|
|
138
|
+
permission,
|
|
139
|
+
count: sortedUsages.length,
|
|
140
|
+
files,
|
|
141
|
+
usages: sortedUsages.map(({ file, line, column, component }) => {
|
|
142
|
+
const entry = { file };
|
|
143
|
+
if (line !== void 0) {
|
|
144
|
+
entry.line = line;
|
|
145
|
+
}
|
|
146
|
+
if (column !== void 0) {
|
|
147
|
+
entry.column = column;
|
|
148
|
+
}
|
|
149
|
+
if (component !== void 0) {
|
|
150
|
+
entry.component = component;
|
|
151
|
+
}
|
|
152
|
+
return entry;
|
|
153
|
+
})
|
|
154
|
+
};
|
|
155
|
+
});
|
|
134
156
|
}
|
|
135
157
|
};
|
|
158
|
+
function compareUsage(left, right) {
|
|
159
|
+
const fileCompare = left.file.localeCompare(right.file);
|
|
160
|
+
if (fileCompare !== 0) {
|
|
161
|
+
return fileCompare;
|
|
162
|
+
}
|
|
163
|
+
const lineCompare = (left.line ?? 0) - (right.line ?? 0);
|
|
164
|
+
if (lineCompare !== 0) {
|
|
165
|
+
return lineCompare;
|
|
166
|
+
}
|
|
167
|
+
const columnCompare = (left.column ?? 0) - (right.column ?? 0);
|
|
168
|
+
if (columnCompare !== 0) {
|
|
169
|
+
return columnCompare;
|
|
170
|
+
}
|
|
171
|
+
return (left.component ?? "").localeCompare(right.component ?? "");
|
|
172
|
+
}
|
|
136
173
|
|
|
137
174
|
// src/transformReact.ts
|
|
138
175
|
var import_parser = require("@babel/parser");
|
|
@@ -192,7 +229,8 @@ function transformReactPermission(code, id, options) {
|
|
|
192
229
|
if (permissionLiteral) {
|
|
193
230
|
const usage = {
|
|
194
231
|
permission: permissionLiteral,
|
|
195
|
-
file: id
|
|
232
|
+
file: id,
|
|
233
|
+
component: componentName
|
|
196
234
|
};
|
|
197
235
|
if (permissionAttr.loc?.start.line !== void 0) {
|
|
198
236
|
usage.line = permissionAttr.loc.start.line;
|
|
@@ -337,31 +375,57 @@ function isInsideCanElement(path4, componentName) {
|
|
|
337
375
|
}
|
|
338
376
|
|
|
339
377
|
// src/manifest.ts
|
|
378
|
+
function createManifest(state) {
|
|
379
|
+
return {
|
|
380
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
381
|
+
summary: state.getSummary(),
|
|
382
|
+
permissions: state.toManifest()
|
|
383
|
+
};
|
|
384
|
+
}
|
|
340
385
|
function createManifestJson(state) {
|
|
341
|
-
return JSON.stringify(
|
|
342
|
-
{
|
|
343
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
344
|
-
permissions: state.toManifest()
|
|
345
|
-
},
|
|
346
|
-
null,
|
|
347
|
-
2
|
|
348
|
-
);
|
|
386
|
+
return JSON.stringify(createManifest(state), null, 2);
|
|
349
387
|
}
|
|
350
388
|
|
|
351
389
|
// src/dts.ts
|
|
352
390
|
function createDts(permissions, typeName = "PermissionKey") {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
const union = permissions.map((item) => ` | ${JSON.stringify(item)}`).join("\n");
|
|
391
|
+
const uniquePermissions = [...new Set(permissions)].sort(
|
|
392
|
+
(left, right) => left.localeCompare(right)
|
|
393
|
+
);
|
|
394
|
+
const permissionKeysType = uniquePermissions.length > 0 ? `readonly [${uniquePermissions.map((item) => JSON.stringify(item)).join(", ")}]` : "readonly string[]";
|
|
358
395
|
return [
|
|
359
396
|
"/* eslint-disable */",
|
|
360
397
|
"// This file is generated by vite-plugin-permission.",
|
|
361
398
|
"// Do not edit manually.",
|
|
362
399
|
"",
|
|
363
|
-
`export
|
|
364
|
-
|
|
400
|
+
`export declare const permissionKeys: ${permissionKeysType}`,
|
|
401
|
+
`export type ${typeName} = typeof permissionKeys[number]`,
|
|
402
|
+
"",
|
|
403
|
+
`export type PermissionUsage = {`,
|
|
404
|
+
` permission: ${typeName}`,
|
|
405
|
+
" file: string",
|
|
406
|
+
" line?: number",
|
|
407
|
+
" column?: number",
|
|
408
|
+
" component?: string",
|
|
409
|
+
"}",
|
|
410
|
+
"",
|
|
411
|
+
`export type PermissionManifestEntry = {`,
|
|
412
|
+
` permission: ${typeName}`,
|
|
413
|
+
" count: number",
|
|
414
|
+
" files: readonly string[]",
|
|
415
|
+
" usages: readonly PermissionUsage[]",
|
|
416
|
+
"}",
|
|
417
|
+
"",
|
|
418
|
+
"export type PermissionManifestSummary = {",
|
|
419
|
+
" permissions: number",
|
|
420
|
+
" usages: number",
|
|
421
|
+
" files: number",
|
|
422
|
+
"}",
|
|
423
|
+
"",
|
|
424
|
+
"export type PermissionManifest = {",
|
|
425
|
+
" generatedAt: string",
|
|
426
|
+
" summary: PermissionManifestSummary",
|
|
427
|
+
" permissions: readonly PermissionManifestEntry[]",
|
|
428
|
+
"}",
|
|
365
429
|
""
|
|
366
430
|
].join("\n");
|
|
367
431
|
}
|
|
@@ -451,6 +515,50 @@ function toArray(value) {
|
|
|
451
515
|
return Array.isArray(value) ? value : [value];
|
|
452
516
|
}
|
|
453
517
|
|
|
518
|
+
// src/validate.ts
|
|
519
|
+
function validatePermissionUsages(usages, options, reporter) {
|
|
520
|
+
if (!options.enabled || options.unknownPermission === "off") {
|
|
521
|
+
return [];
|
|
522
|
+
}
|
|
523
|
+
const knownSet = new Set(options.permissions);
|
|
524
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
525
|
+
for (const usage of usages) {
|
|
526
|
+
if (knownSet.has(usage.permission)) {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
const items = grouped.get(usage.permission) ?? [];
|
|
530
|
+
items.push(usage);
|
|
531
|
+
grouped.set(usage.permission, items);
|
|
532
|
+
}
|
|
533
|
+
const unknownPermissions = [...grouped.keys()].sort((left, right) => left.localeCompare(right));
|
|
534
|
+
if (unknownPermissions.length === 0) {
|
|
535
|
+
return [];
|
|
536
|
+
}
|
|
537
|
+
const message = buildMessage(unknownPermissions, grouped);
|
|
538
|
+
if (options.unknownPermission === "error") {
|
|
539
|
+
throw new Error(message);
|
|
540
|
+
}
|
|
541
|
+
reporter?.warn(message);
|
|
542
|
+
return unknownPermissions;
|
|
543
|
+
}
|
|
544
|
+
function buildMessage(permissions, grouped) {
|
|
545
|
+
const lines = [`[vite-plugin-permission] Unknown permissions: ${permissions.join(", ")}`];
|
|
546
|
+
for (const permission of permissions) {
|
|
547
|
+
const usages = grouped.get(permission) ?? [];
|
|
548
|
+
for (const usage of usages.slice(0, 3)) {
|
|
549
|
+
lines.push(` - ${formatUsage(usage)}`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return lines.join("\n");
|
|
553
|
+
}
|
|
554
|
+
function formatUsage(usage) {
|
|
555
|
+
const location = [usage.file, usage.line, usage.column].filter((item) => item !== void 0).join(":");
|
|
556
|
+
if (!usage.component) {
|
|
557
|
+
return `${usage.permission} (${location})`;
|
|
558
|
+
}
|
|
559
|
+
return `${usage.permission} (${location}, component=${usage.component})`;
|
|
560
|
+
}
|
|
561
|
+
|
|
454
562
|
// src/index.ts
|
|
455
563
|
var VIRTUAL_ID = "virtual:permission-manifest";
|
|
456
564
|
var RESOLVED_VIRTUAL_ID = "\0" + VIRTUAL_ID;
|
|
@@ -504,7 +612,7 @@ function permissionPlugin(userOptions = {}) {
|
|
|
504
612
|
},
|
|
505
613
|
load(id) {
|
|
506
614
|
if (id === RESOLVED_VIRTUAL_ID) {
|
|
507
|
-
return `export default ${JSON.stringify(state
|
|
615
|
+
return `export default ${JSON.stringify(createManifest(state), null, 2)}`;
|
|
508
616
|
}
|
|
509
617
|
return null;
|
|
510
618
|
},
|
|
@@ -523,7 +631,7 @@ function permissionPlugin(userOptions = {}) {
|
|
|
523
631
|
return null;
|
|
524
632
|
}
|
|
525
633
|
state.setFileUsage(id, result.usages);
|
|
526
|
-
|
|
634
|
+
validatePermissionUsages(result.usages, options.validate, transformContext);
|
|
527
635
|
return {
|
|
528
636
|
code: result.code,
|
|
529
637
|
map: result.map
|
|
@@ -538,7 +646,7 @@ function permissionPlugin(userOptions = {}) {
|
|
|
538
646
|
server = _server;
|
|
539
647
|
server.middlewares.use("/__permission/manifest", async (_req, res) => {
|
|
540
648
|
res.setHeader("Content-Type", "application/json");
|
|
541
|
-
res.end(JSON.stringify(state
|
|
649
|
+
res.end(JSON.stringify(createManifest(state), null, 2));
|
|
542
650
|
});
|
|
543
651
|
},
|
|
544
652
|
async handleHotUpdate(ctx) {
|
|
@@ -549,28 +657,10 @@ function permissionPlugin(userOptions = {}) {
|
|
|
549
657
|
ctx.server.ws.send({
|
|
550
658
|
type: "custom",
|
|
551
659
|
event: "permission-manifest-update",
|
|
552
|
-
data: state
|
|
660
|
+
data: createManifest(state)
|
|
553
661
|
});
|
|
554
662
|
}
|
|
555
663
|
};
|
|
556
|
-
function validatePermissions(usedPermissions) {
|
|
557
|
-
if (!options.validate.enabled) {
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
560
|
-
if (options.validate.unknownPermission === "off") {
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
const knownSet = new Set(options.validate.permissions);
|
|
564
|
-
const unknown = usedPermissions.filter((item) => !knownSet.has(item));
|
|
565
|
-
if (unknown.length === 0) {
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
const message = `[vite-plugin-permission] Unknown permissions: ${unknown.join(", ")}`;
|
|
569
|
-
if (options.validate.unknownPermission === "error") {
|
|
570
|
-
throw new Error(message);
|
|
571
|
-
}
|
|
572
|
-
transformContext?.warn(message);
|
|
573
|
-
}
|
|
574
664
|
}
|
|
575
665
|
async function emitManifestAndDts(root, options, state) {
|
|
576
666
|
if (options.manifest.enabled) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/options.ts","../src/state.ts","../src/transformReact.ts","../src/manifest.ts","../src/dts.ts","../src/filter.ts","../src/rewrite.ts"],"sourcesContent":["// src/index.ts\nimport type { Plugin, ResolvedConfig, ViteDevServer } from 'vite'\nimport { createFilter } from 'vite'\nimport path from 'node:path'\nimport fs from 'node:fs/promises'\nimport { resolveOptions, type PermissionPluginOptions } from './options'\nimport { PermissionState } from './state'\nimport { transformReactPermission } from './transformReact'\nimport { createManifestJson } from './manifest'\nimport { createDts } from './dts'\nimport { createRootFilter } from './filter'\nimport { rewriteSourceFiles } from './rewrite'\n\nconst VIRTUAL_ID = 'virtual:permission-manifest'\nconst RESOLVED_VIRTUAL_ID = '\\0' + VIRTUAL_ID\n\nexport default function permissionPlugin(userOptions: PermissionPluginOptions = {}): Plugin {\n const options = resolveOptions(userOptions)\n\n let config: ResolvedConfig\n let server: ViteDevServer | undefined\n let rewriteExecuted = false\n let transformContext: { warn: (message: string) => void } | undefined\n let transformFilter: ReturnType<typeof createRootFilter> | undefined\n const state = new PermissionState()\n\n const filter = createFilter(\n options.include ?? [/\\.tsx$/, /\\.jsx$/, /\\.vue$/],\n options.exclude ?? [/node_modules/, /\\.git/]\n )\n return {\n name: 'vite-plugin-permission',\n enforce: 'pre',\n\n configResolved(resolvedConfig) {\n config = resolvedConfig\n\n transformFilter = createRootFilter(\n config.root,\n options.transform.include ?? options.include ?? ['src/**/*.{tsx,jsx}'],\n options.transform.exclude ??\n options.exclude ?? [\n '**/node_modules/**',\n '**/dist/**',\n '**/.vite/**',\n '**/.turbo/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/*.stories.*'\n ]\n )\n },\n\n async buildStart() {\n state.clear?.()\n if (!options.rewrite.enabled) {\n return\n }\n\n if (options.rewrite.once && rewriteExecuted) {\n return\n }\n\n rewriteExecuted = true\n\n await rewriteSourceFiles(config.root, options, this)\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ID) {\n return RESOLVED_VIRTUAL_ID\n }\n\n return null\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `export default ${JSON.stringify(state.toManifest(), null, 2)}`\n }\n\n return null\n },\n\n async transform(code, id) {\n transformContext = this\n\n if (!filter(id) || !transformFilter?.(id)) {\n return null\n }\n\n if (!options.transform.enabled) {\n return null\n }\n\n if (options.framework === 'react') {\n const result = transformReactPermission(code, id, options)\n\n if (!result) {\n state.removeFile(id)\n return null\n }\n\n state.setFileUsage(id, result.usages)\n\n validatePermissions(result.usages.map((item) => item.permission))\n\n return {\n code: result.code,\n map: result.map\n }\n }\n\n // Vue MVP 阶段建议只扫描,不强行 template 改写\n return null\n },\n\n async generateBundle() {\n await emitManifestAndDts(config.root, options, state)\n },\n\n configureServer(_server) {\n server = _server\n\n server.middlewares.use('/__permission/manifest', async (_req, res) => {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(state.toManifest(), null, 2))\n })\n },\n\n async handleHotUpdate(ctx) {\n if (!filter(ctx.file)) {\n return\n }\n\n // 文件变化后,先删除旧记录,等待下一次 transform 写入新记录\n state.removeFile(ctx.file)\n\n // 通知前端或 devtools 权限 manifest 变了\n ctx.server.ws.send({\n type: 'custom',\n event: 'permission-manifest-update',\n data: state.toManifest()\n })\n }\n }\n\n function validatePermissions(usedPermissions: string[]) {\n if (!options.validate.enabled) {\n return\n }\n\n if (options.validate.unknownPermission === 'off') {\n return\n }\n\n const knownSet = new Set(options.validate.permissions)\n\n const unknown = usedPermissions.filter((item) => !knownSet.has(item))\n\n if (unknown.length === 0) {\n return\n }\n\n const message = `[vite-plugin-permission] Unknown permissions: ${unknown.join(', ')}`\n\n if (options.validate.unknownPermission === 'error') {\n throw new Error(message)\n }\n\n transformContext?.warn(message)\n }\n}\n\nasync function emitManifestAndDts(\n root: string,\n options: ReturnType<typeof resolveOptions>,\n state: PermissionState\n) {\n if (options.manifest.enabled) {\n const manifestPath = path.resolve(root, options.manifest.output)\n await fs.mkdir(path.dirname(manifestPath), { recursive: true })\n await fs.writeFile(manifestPath, createManifestJson(state), 'utf-8')\n }\n\n if (options.dts.enabled) {\n const dtsPath = path.resolve(root, options.dts.output)\n await fs.mkdir(path.dirname(dtsPath), { recursive: true })\n await fs.writeFile(\n dtsPath,\n createDts(state.getUsedPermissions(), options.dts.typeName),\n 'utf-8'\n )\n }\n}\n","// src/options.ts\nimport { z } from 'zod'\n\nconst FilterPatternSchema = z.custom<string | RegExp | Array<string | RegExp>>()\n\nconst GlobPatternSchema = z.union([z.string(), z.array(z.string())])\n\nexport const PluginOptionsSchema = z.object({\n framework: z.enum(['react', 'vue']).default('react'),\n\n componentName: z.string().default('Can'),\n\n importFrom: z.string().default('@eycraf/permission-kit-react'),\n\n /**\n * 全局 include/exclude。\n * transform/rewrite 没单独配置时,会回退使用这里。\n */\n include: FilterPatternSchema.optional(),\n exclude: FilterPatternSchema.optional(),\n\n transform: z\n .object({\n enabled: z.boolean().default(true),\n include: FilterPatternSchema.optional(),\n exclude: FilterPatternSchema.optional(),\n attributes: z.array(z.string()).default(['permission']),\n modeAttribute: z.string().default('permissionMode'),\n strategyAttribute: z.string().default('permissionStrategy'),\n allowNested: z.boolean().default(false)\n })\n .default({}),\n\n rewrite: z\n .object({\n enabled: z.boolean().default(false),\n\n /**\n * false = dry-run,只打印会修改哪些文件\n * true = 真实写回源文件\n */\n write: z.boolean().default(false),\n\n include: GlobPatternSchema.optional(),\n exclude: GlobPatternSchema.optional(),\n\n /**\n * rewrite 只在 serve/build 启动时跑一次。\n * 默认 true,避免 HMR 时反复改文件。\n */\n once: z.boolean().default(true)\n })\n .default({}),\n\n manifest: z\n .object({\n enabled: z.boolean().default(true),\n output: z.string().default('src/generated/permission-manifest.json')\n })\n .default({}),\n\n dts: z\n .object({\n enabled: z.boolean().default(true),\n output: z.string().default('src/generated/permission.d.ts'),\n typeName: z.string().default('PermissionKey')\n })\n .default({}),\n\n validate: z\n .object({\n enabled: z.boolean().default(false),\n permissions: z.array(z.string()).default([]),\n unknownPermission: z.enum(['off', 'warn', 'error']).default('warn')\n })\n .default({})\n})\n\nexport type PermissionPluginOptions = z.input<typeof PluginOptionsSchema>\nexport type NormalizedPermissionPluginOptions = z.output<typeof PluginOptionsSchema>\n\nexport function resolveOptions(options: PermissionPluginOptions) {\n return PluginOptionsSchema.parse(options)\n}\n","// src/state.ts\nexport type PermissionUsage = {\n permission: string\n file: string\n line?: number\n column?: number\n}\n\nexport class PermissionState {\n private fileUsageMap = new Map<string, PermissionUsage[]>()\n\n clear() {\n this.fileUsageMap.clear()\n }\n\n setFileUsage(file: string, usages: PermissionUsage[]) {\n this.fileUsageMap.set(file, usages)\n }\n\n removeFile(file: string) {\n this.fileUsageMap.delete(file)\n }\n\n getAllUsages() {\n return [...this.fileUsageMap.values()].flat()\n }\n\n getUsedPermissions() {\n return [...new Set(this.getAllUsages().map((item) => item.permission))].sort()\n }\n\n toManifest() {\n const result: Record<\n string,\n Array<{\n file: string\n line?: number\n column?: number\n }>\n > = {}\n\n for (const usage of this.getAllUsages()) {\n const items = (result[usage.permission] ??= [])\n const entry: {\n file: string\n line?: number\n column?: number\n } = {\n file: usage.file\n }\n\n if (usage.line !== undefined) {\n entry.line = usage.line\n }\n\n if (usage.column !== undefined) {\n entry.column = usage.column\n }\n\n items.push(entry)\n }\n\n return result\n }\n}\n","// src/transformReact.ts\nimport { parse } from '@babel/parser'\nimport traverse, { type NodePath } from '@babel/traverse'\nimport type {\n JSXAttribute,\n JSXElement,\n JSXIdentifier,\n JSXMemberExpression,\n JSXNamespacedName\n} from '@babel/types'\nimport MagicString from 'magic-string'\nimport type { NormalizedPermissionPluginOptions } from './options'\nimport type { PermissionUsage } from './state'\n\ntype TransformResult = {\n code: string\n map: ReturnType<MagicString['generateMap']>\n usages: PermissionUsage[]\n}\n\ntype PendingWrap = {\n node: JSXElement\n permissionCode: string\n modeCode: string | undefined\n attrsToRemove: JSXAttribute[]\n}\n\nexport function transformReactPermission(\n code: string,\n id: string,\n options: NormalizedPermissionPluginOptions\n): TransformResult | null {\n if (!/\\.[jt]sx$/.test(id)) {\n return null\n }\n\n const attrs = options.transform.attributes\n const modeAttrName = options.transform.modeAttribute\n const componentName = options.componentName\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n errorRecovery: true\n })\n\n const ms = new MagicString(code)\n const wraps: PendingWrap[] = []\n const usages: PermissionUsage[] = []\n const removes: JSXAttribute[] = []\n\n let hasCanImport = false\n let lastImportEnd = 0\n\n traverse(ast, {\n ImportDeclaration(path) {\n lastImportEnd = Math.max(lastImportEnd, path.node.end ?? 0)\n\n if (path.node.source.value === options.importFrom) {\n for (const specifier of path.node.specifiers) {\n if (specifier.type === 'ImportSpecifier' && specifier.local.name === componentName) {\n hasCanImport = true\n }\n }\n }\n },\n\n JSXElement(path) {\n const node = path.node\n\n // 避免 <Can permission=\"x\"> 被再次包裹\n const openingName = node.openingElement.name\n if (openingName.type === 'JSXIdentifier' && openingName.name === componentName) {\n return\n }\n\n const attributes = node.openingElement.attributes\n const permissionAttr = attributes.find((attr): attr is JSXAttribute => {\n return (\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attrs.includes(attr.name.name)\n )\n })\n\n if (!permissionAttr) {\n return\n }\n\n const modeAttr = attributes.find((attr): attr is JSXAttribute => {\n return (\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attr.name.name === modeAttrName\n )\n })\n\n const permissionCode = getJSXAttributeValueCode(code, permissionAttr)\n if (!permissionCode) {\n return\n }\n\n const modeCode = modeAttr ? getJSXAttributeValueCode(code, modeAttr) : undefined\n\n const permissionLiteral = getStaticStringValue(permissionAttr)\n if (permissionLiteral) {\n const usage: PermissionUsage = {\n permission: permissionLiteral,\n file: id\n }\n\n if (permissionAttr.loc?.start.line !== undefined) {\n usage.line = permissionAttr.loc.start.line\n }\n\n if (permissionAttr.loc?.start.column !== undefined) {\n usage.column = permissionAttr.loc.start.column\n }\n\n usages.push(usage)\n }\n\n const attrsToRemove = modeAttr ? [permissionAttr, modeAttr] : [permissionAttr]\n\n // 2. 已经在 <Can> 内部:只删除 permission 属性,不再包裹\n if (isInsideCanElement(path, componentName)) {\n removes.push(...attrsToRemove)\n return\n }\n\n const resolvedModeCode = modeCode ?? undefined\n\n wraps.push({\n node,\n permissionCode,\n modeCode: resolvedModeCode,\n attrsToRemove: modeAttr ? [permissionAttr, modeAttr] : [permissionAttr]\n })\n\n // 4. 可选:如果当前元素已经要被包裹,可以跳过它的子节点\n // 这样可以避免父子都有 permission 时产生嵌套。\n if (!options.transform.allowNested) {\n path.skip()\n }\n }\n })\n\n if (removes.length) {\n // 先删除在 <Can> 内部的冗余 permission 属性\n removes\n .sort((a, b) => (b.start ?? 0) - (a.start ?? 0))\n .forEach((attr) => {\n removeJSXAttribute(ms, code, attr)\n })\n }\n\n if (wraps.length === 0) {\n if (removes.length === 0) {\n return null\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({\n source: id,\n includeContent: true,\n hires: true\n }),\n usages\n }\n }\n\n // 倒序修改,避免位置偏移\n wraps\n .sort((a, b) => (b.node.start ?? 0) - (a.node.start ?? 0))\n .forEach((item) => {\n for (const attr of item.attrsToRemove) {\n removeJSXAttribute(ms, code, attr)\n }\n\n const start = item.node.start\n const end = item.node.end\n\n if (start == null || end == null) {\n return\n }\n\n const modePart = item.modeCode ? ` mode={${item.modeCode}}` : ''\n\n ms.prependLeft(start, `<${componentName} permission={${item.permissionCode}}${modePart}>`)\n\n ms.appendRight(end, `</${componentName}>`)\n })\n\n if (!hasCanImport) {\n const importCode = `import { ${componentName} } from '${options.importFrom}'\\n`\n\n if (lastImportEnd > 0) {\n ms.appendRight(lastImportEnd, `\\n${importCode}`)\n } else {\n ms.prepend(importCode)\n }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({\n source: id,\n includeContent: true,\n hires: true\n }),\n usages\n }\n}\n\nfunction getJSXAttributeValueCode(code: string, attr: JSXAttribute): string | null {\n const value = attr.value\n\n if (!value) {\n return null\n }\n\n if (value.type === 'StringLiteral') {\n return JSON.stringify(value.value)\n }\n\n if (value.type === 'JSXExpressionContainer') {\n const expr = value.expression\n\n if (expr.type === 'JSXEmptyExpression' || expr.start == null || expr.end == null) {\n return null\n }\n\n return code.slice(expr.start, expr.end)\n }\n\n return null\n}\n\nfunction getStaticStringValue(attr: JSXAttribute): string | null {\n const value = attr.value\n\n if (!value) {\n return null\n }\n\n if (value.type === 'StringLiteral') {\n return value.value\n }\n\n if (value.type === 'JSXExpressionContainer' && value.expression.type === 'StringLiteral') {\n return value.expression.value\n }\n\n return null\n}\n\nfunction removeJSXAttribute(ms: MagicString, code: string, attr: JSXAttribute) {\n if (attr.start == null || attr.end == null) {\n return\n }\n\n let start = attr.start\n let end = attr.end\n\n // 顺手删除属性前面的空格,避免留下多余空白\n while (start > 0 && /\\s/.test(code.charAt(start - 1))) {\n start--\n }\n\n ms.remove(start, end)\n}\n\nfunction getJSXName(name: JSXIdentifier | JSXMemberExpression | JSXNamespacedName): string {\n if (name.type === 'JSXIdentifier') {\n return name.name\n }\n\n if (name.type === 'JSXMemberExpression') {\n return `${getJSXName(name.object)}.${getJSXName(name.property)}`\n }\n\n return `${name.namespace.name}:${name.name.name}`\n}\n\nfunction isCanElement(node: JSXElement, componentName: string) {\n const name = getJSXName(node.openingElement.name)\n\n return name === componentName\n}\n\nfunction isInsideCanElement(path: NodePath<JSXElement>, componentName: string) {\n return Boolean(\n path.findParent((parentPath) => {\n if (!parentPath.isJSXElement()) {\n return false\n }\n\n return isCanElement(parentPath.node, componentName)\n })\n )\n}\n","// src/manifest.ts\nimport type { PermissionState } from './state'\n\nexport function createManifestJson(state: PermissionState) {\n return JSON.stringify(\n {\n generatedAt: new Date().toISOString(),\n permissions: state.toManifest()\n },\n null,\n 2\n )\n}\n","// src/dts.ts\nexport function createDts(permissions: string[], typeName = 'PermissionKey') {\n if (permissions.length === 0) {\n return `export type ${typeName} = string\\n`\n }\n\n const union = permissions.map((item) => ` | ${JSON.stringify(item)}`).join('\\n')\n\n return [\n '/* eslint-disable */',\n '// This file is generated by vite-plugin-permission.',\n '// Do not edit manually.',\n '',\n `export type ${typeName} =`,\n union,\n ''\n ].join('\\n')\n}\n","// packages/vite-plugin/src/filter.ts\nimport path from 'node:path'\nimport { createFilter, normalizePath } from 'vite'\n\ntype Pattern = string | RegExp | Array<string | RegExp> | undefined\n\nexport function createRootFilter(root: string, include: Pattern, exclude: Pattern) {\n return createFilter(normalizePatterns(root, include), normalizePatterns(root, exclude))\n}\n\nfunction normalizePatterns(root: string, patterns: Pattern) {\n if (!patterns) {\n return undefined\n }\n\n const list = Array.isArray(patterns) ? patterns : [patterns]\n\n return list.map((item) => {\n if (item instanceof RegExp) {\n return item\n }\n\n // 绝对路径 glob:只做 POSIX 规范化\n if (path.isAbsolute(item)) {\n return normalizePath(item)\n }\n\n // 相对路径 glob:基于 Vite root 转绝对\n return normalizePath(path.resolve(root, item))\n })\n}\n\nexport function cleanId(id: string) {\n return normalizePath(id.split('?')[0] ?? id)\n}\n","// packages/vite-plugin/src/rewrite.ts\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport fg from 'fast-glob'\nimport { normalizePath } from 'vite'\nimport type { NormalizedPermissionPluginOptions } from './options'\nimport { transformReactPermission } from './transformReact'\n\ntype LoggerLike = {\n warn(message: string): void\n}\n\nexport type RewriteResult = {\n checked: number\n changed: number\n files: string[]\n}\n\nexport async function rewriteSourceFiles(\n root: string,\n options: NormalizedPermissionPluginOptions,\n context: LoggerLike\n): Promise<RewriteResult> {\n const include = toArray(options.rewrite.include ?? ['src/**/*.{tsx,jsx}'])\n\n const exclude = toArray(\n options.rewrite.exclude ?? [\n '**/node_modules/**',\n '**/dist/**',\n '**/.vite/**',\n '**/.turbo/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/*.stories.*'\n ]\n )\n\n const files = await fg(include, {\n cwd: root,\n absolute: true,\n onlyFiles: true,\n ignore: exclude\n })\n\n let changed = 0\n const changedFiles: string[] = []\n\n for (const file of files) {\n const id = normalizePath(file)\n const code = await fs.readFile(file, 'utf-8')\n\n if (options.framework !== 'react') {\n continue\n }\n\n const result = transformReactPermission(code, id, options)\n\n if (!result || result.code === code) {\n continue\n }\n\n changed++\n changedFiles.push(path.relative(root, file))\n\n if (options.rewrite.write) {\n await fs.writeFile(file, result.code, 'utf-8')\n }\n }\n\n const mode = options.rewrite.write ? 'write' : 'dry-run'\n\n if (changedFiles.length > 0) {\n context.warn(\n [\n `[vite-plugin-permission] rewrite ${mode}: ${changed} file(s) would change`,\n ...changedFiles.map((file) => ` - ${file}`)\n ].join('\\n')\n )\n } else {\n context.warn(`[vite-plugin-permission] rewrite ${mode}: no files changed`)\n }\n\n return {\n checked: files.length,\n changed,\n files: changedFiles\n }\n}\n\nfunction toArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value]\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,IAAAA,eAA6B;AAC7B,IAAAC,oBAAiB;AACjB,IAAAC,mBAAe;;;ACHf,iBAAkB;AAElB,IAAM,sBAAsB,aAAE,OAAiD;AAE/E,IAAM,oBAAoB,aAAE,MAAM,CAAC,aAAE,OAAO,GAAG,aAAE,MAAM,aAAE,OAAO,CAAC,CAAC,CAAC;AAE5D,IAAM,sBAAsB,aAAE,OAAO;AAAA,EAC1C,WAAW,aAAE,KAAK,CAAC,SAAS,KAAK,CAAC,EAAE,QAAQ,OAAO;AAAA,EAEnD,eAAe,aAAE,OAAO,EAAE,QAAQ,KAAK;AAAA,EAEvC,YAAY,aAAE,OAAO,EAAE,QAAQ,8BAA8B;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7D,SAAS,oBAAoB,SAAS;AAAA,EACtC,SAAS,oBAAoB,SAAS;AAAA,EAEtC,WAAW,aACR,OAAO;AAAA,IACN,SAAS,aAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IACjC,SAAS,oBAAoB,SAAS;AAAA,IACtC,SAAS,oBAAoB,SAAS;AAAA,IACtC,YAAY,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC;AAAA,IACtD,eAAe,aAAE,OAAO,EAAE,QAAQ,gBAAgB;AAAA,IAClD,mBAAmB,aAAE,OAAO,EAAE,QAAQ,oBAAoB;AAAA,IAC1D,aAAa,aAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EACxC,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EAEb,SAAS,aACN,OAAO;AAAA,IACN,SAAS,aAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,IAMlC,OAAO,aAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IAEhC,SAAS,kBAAkB,SAAS;AAAA,IACpC,SAAS,kBAAkB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,IAMpC,MAAM,aAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EAChC,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EAEb,UAAU,aACP,OAAO;AAAA,IACN,SAAS,aAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IACjC,QAAQ,aAAE,OAAO,EAAE,QAAQ,wCAAwC;AAAA,EACrE,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EAEb,KAAK,aACF,OAAO;AAAA,IACN,SAAS,aAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IACjC,QAAQ,aAAE,OAAO,EAAE,QAAQ,+BAA+B;AAAA,IAC1D,UAAU,aAAE,OAAO,EAAE,QAAQ,eAAe;AAAA,EAC9C,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EAEb,UAAU,aACP,OAAO;AAAA,IACN,SAAS,aAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IAClC,aAAa,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,IAC3C,mBAAmB,aAAE,KAAK,CAAC,OAAO,QAAQ,OAAO,CAAC,EAAE,QAAQ,MAAM;AAAA,EACpE,CAAC,EACA,QAAQ,CAAC,CAAC;AACf,CAAC;AAKM,SAAS,eAAe,SAAkC;AAC/D,SAAO,oBAAoB,MAAM,OAAO;AAC1C;;;AC3EO,IAAM,kBAAN,MAAsB;AAAA,EAAtB;AACL,SAAQ,eAAe,oBAAI,IAA+B;AAAA;AAAA,EAE1D,QAAQ;AACN,SAAK,aAAa,MAAM;AAAA,EAC1B;AAAA,EAEA,aAAa,MAAc,QAA2B;AACpD,SAAK,aAAa,IAAI,MAAM,MAAM;AAAA,EACpC;AAAA,EAEA,WAAW,MAAc;AACvB,SAAK,aAAa,OAAO,IAAI;AAAA,EAC/B;AAAA,EAEA,eAAe;AACb,WAAO,CAAC,GAAG,KAAK,aAAa,OAAO,CAAC,EAAE,KAAK;AAAA,EAC9C;AAAA,EAEA,qBAAqB;AACnB,WAAO,CAAC,GAAG,IAAI,IAAI,KAAK,aAAa,EAAE,IAAI,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK;AAAA,EAC/E;AAAA,EAEA,aAAa;AA/Bf;AAgCI,UAAM,SAOF,CAAC;AAEL,eAAW,SAAS,KAAK,aAAa,GAAG;AACvC,YAAM,QAAS,YAAO,MAAM,gBAAb,aAA6B,CAAC;AAC7C,YAAM,QAIF;AAAA,QACF,MAAM,MAAM;AAAA,MACd;AAEA,UAAI,MAAM,SAAS,QAAW;AAC5B,cAAM,OAAO,MAAM;AAAA,MACrB;AAEA,UAAI,MAAM,WAAW,QAAW;AAC9B,cAAM,SAAS,MAAM;AAAA,MACvB;AAEA,YAAM,KAAK,KAAK;AAAA,IAClB;AAEA,WAAO;AAAA,EACT;AACF;;;AC/DA,oBAAsB;AACtB,sBAAwC;AAQxC,0BAAwB;AAiBjB,SAAS,yBACd,MACA,IACA,SACwB;AACxB,MAAI,CAAC,YAAY,KAAK,EAAE,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,QAAQ,UAAU;AAChC,QAAM,eAAe,QAAQ,UAAU;AACvC,QAAM,gBAAgB,QAAQ;AAC9B,QAAM,UAAM,qBAAM,MAAM;AAAA,IACtB,YAAY;AAAA,IACZ,SAAS,CAAC,OAAO,YAAY;AAAA,IAC7B,eAAe;AAAA,EACjB,CAAC;AAED,QAAM,KAAK,IAAI,oBAAAC,QAAY,IAAI;AAC/B,QAAM,QAAuB,CAAC;AAC9B,QAAM,SAA4B,CAAC;AACnC,QAAM,UAA0B,CAAC;AAEjC,MAAI,eAAe;AACnB,MAAI,gBAAgB;AAEpB,sBAAAC,SAAS,KAAK;AAAA,IACZ,kBAAkBC,OAAM;AACtB,sBAAgB,KAAK,IAAI,eAAeA,MAAK,KAAK,OAAO,CAAC;AAE1D,UAAIA,MAAK,KAAK,OAAO,UAAU,QAAQ,YAAY;AACjD,mBAAW,aAAaA,MAAK,KAAK,YAAY;AAC5C,cAAI,UAAU,SAAS,qBAAqB,UAAU,MAAM,SAAS,eAAe;AAClF,2BAAe;AAAA,UACjB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,WAAWA,OAAM;AACf,YAAM,OAAOA,MAAK;AAGlB,YAAM,cAAc,KAAK,eAAe;AACxC,UAAI,YAAY,SAAS,mBAAmB,YAAY,SAAS,eAAe;AAC9E;AAAA,MACF;AAEA,YAAM,aAAa,KAAK,eAAe;AACvC,YAAM,iBAAiB,WAAW,KAAK,CAAC,SAA+B;AACrE,eACE,KAAK,SAAS,kBACd,KAAK,KAAK,SAAS,mBACnB,MAAM,SAAS,KAAK,KAAK,IAAI;AAAA,MAEjC,CAAC;AAED,UAAI,CAAC,gBAAgB;AACnB;AAAA,MACF;AAEA,YAAM,WAAW,WAAW,KAAK,CAAC,SAA+B;AAC/D,eACE,KAAK,SAAS,kBACd,KAAK,KAAK,SAAS,mBACnB,KAAK,KAAK,SAAS;AAAA,MAEvB,CAAC;AAED,YAAM,iBAAiB,yBAAyB,MAAM,cAAc;AACpE,UAAI,CAAC,gBAAgB;AACnB;AAAA,MACF;AAEA,YAAM,WAAW,WAAW,yBAAyB,MAAM,QAAQ,IAAI;AAEvE,YAAM,oBAAoB,qBAAqB,cAAc;AAC7D,UAAI,mBAAmB;AACrB,cAAM,QAAyB;AAAA,UAC7B,YAAY;AAAA,UACZ,MAAM;AAAA,QACR;AAEA,YAAI,eAAe,KAAK,MAAM,SAAS,QAAW;AAChD,gBAAM,OAAO,eAAe,IAAI,MAAM;AAAA,QACxC;AAEA,YAAI,eAAe,KAAK,MAAM,WAAW,QAAW;AAClD,gBAAM,SAAS,eAAe,IAAI,MAAM;AAAA,QAC1C;AAEA,eAAO,KAAK,KAAK;AAAA,MACnB;AAEA,YAAM,gBAAgB,WAAW,CAAC,gBAAgB,QAAQ,IAAI,CAAC,cAAc;AAG7E,UAAI,mBAAmBA,OAAM,aAAa,GAAG;AAC3C,gBAAQ,KAAK,GAAG,aAAa;AAC7B;AAAA,MACF;AAEA,YAAM,mBAAmB,YAAY;AAErC,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,eAAe,WAAW,CAAC,gBAAgB,QAAQ,IAAI,CAAC,cAAc;AAAA,MACxE,CAAC;AAID,UAAI,CAAC,QAAQ,UAAU,aAAa;AAClC,QAAAA,MAAK,KAAK;AAAA,MACZ;AAAA,IACF;AAAA,EACF,CAAC;AAED,MAAI,QAAQ,QAAQ;AAElB,YACG,KAAK,CAAC,GAAG,OAAO,EAAE,SAAS,MAAM,EAAE,SAAS,EAAE,EAC9C,QAAQ,CAAC,SAAS;AACjB,yBAAmB,IAAI,MAAM,IAAI;AAAA,IACnC,CAAC;AAAA,EACL;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,MAAM,GAAG,SAAS;AAAA,MAClB,KAAK,GAAG,YAAY;AAAA,QAClB,QAAQ;AAAA,QACR,gBAAgB;AAAA,QAChB,OAAO;AAAA,MACT,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AAGA,QACG,KAAK,CAAC,GAAG,OAAO,EAAE,KAAK,SAAS,MAAM,EAAE,KAAK,SAAS,EAAE,EACxD,QAAQ,CAAC,SAAS;AACjB,eAAW,QAAQ,KAAK,eAAe;AACrC,yBAAmB,IAAI,MAAM,IAAI;AAAA,IACnC;AAEA,UAAM,QAAQ,KAAK,KAAK;AACxB,UAAM,MAAM,KAAK,KAAK;AAEtB,QAAI,SAAS,QAAQ,OAAO,MAAM;AAChC;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,WAAW,UAAU,KAAK,QAAQ,MAAM;AAE9D,OAAG,YAAY,OAAO,IAAI,aAAa,gBAAgB,KAAK,cAAc,IAAI,QAAQ,GAAG;AAEzF,OAAG,YAAY,KAAK,KAAK,aAAa,GAAG;AAAA,EAC3C,CAAC;AAEH,MAAI,CAAC,cAAc;AACjB,UAAM,aAAa,YAAY,aAAa,YAAY,QAAQ,UAAU;AAAA;AAE1E,QAAI,gBAAgB,GAAG;AACrB,SAAG,YAAY,eAAe;AAAA,EAAK,UAAU,EAAE;AAAA,IACjD,OAAO;AACL,SAAG,QAAQ,UAAU;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,GAAG,SAAS;AAAA,IAClB,KAAK,GAAG,YAAY;AAAA,MAClB,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,OAAO;AAAA,IACT,CAAC;AAAA,IACD;AAAA,EACF;AACF;AAEA,SAAS,yBAAyB,MAAc,MAAmC;AACjF,QAAM,QAAQ,KAAK;AAEnB,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,SAAS,iBAAiB;AAClC,WAAO,KAAK,UAAU,MAAM,KAAK;AAAA,EACnC;AAEA,MAAI,MAAM,SAAS,0BAA0B;AAC3C,UAAM,OAAO,MAAM;AAEnB,QAAI,KAAK,SAAS,wBAAwB,KAAK,SAAS,QAAQ,KAAK,OAAO,MAAM;AAChF,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,MAAM,KAAK,OAAO,KAAK,GAAG;AAAA,EACxC;AAEA,SAAO;AACT;AAEA,SAAS,qBAAqB,MAAmC;AAC/D,QAAM,QAAQ,KAAK;AAEnB,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,SAAS,iBAAiB;AAClC,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,MAAM,SAAS,4BAA4B,MAAM,WAAW,SAAS,iBAAiB;AACxF,WAAO,MAAM,WAAW;AAAA,EAC1B;AAEA,SAAO;AACT;AAEA,SAAS,mBAAmB,IAAiB,MAAc,MAAoB;AAC7E,MAAI,KAAK,SAAS,QAAQ,KAAK,OAAO,MAAM;AAC1C;AAAA,EACF;AAEA,MAAI,QAAQ,KAAK;AACjB,MAAI,MAAM,KAAK;AAGf,SAAO,QAAQ,KAAK,KAAK,KAAK,KAAK,OAAO,QAAQ,CAAC,CAAC,GAAG;AACrD;AAAA,EACF;AAEA,KAAG,OAAO,OAAO,GAAG;AACtB;AAEA,SAAS,WAAW,MAAuE;AACzF,MAAI,KAAK,SAAS,iBAAiB;AACjC,WAAO,KAAK;AAAA,EACd;AAEA,MAAI,KAAK,SAAS,uBAAuB;AACvC,WAAO,GAAG,WAAW,KAAK,MAAM,CAAC,IAAI,WAAW,KAAK,QAAQ,CAAC;AAAA,EAChE;AAEA,SAAO,GAAG,KAAK,UAAU,IAAI,IAAI,KAAK,KAAK,IAAI;AACjD;AAEA,SAAS,aAAa,MAAkB,eAAuB;AAC7D,QAAM,OAAO,WAAW,KAAK,eAAe,IAAI;AAEhD,SAAO,SAAS;AAClB;AAEA,SAAS,mBAAmBA,OAA4B,eAAuB;AAC7E,SAAO;AAAA,IACLA,MAAK,WAAW,CAAC,eAAe;AAC9B,UAAI,CAAC,WAAW,aAAa,GAAG;AAC9B,eAAO;AAAA,MACT;AAEA,aAAO,aAAa,WAAW,MAAM,aAAa;AAAA,IACpD,CAAC;AAAA,EACH;AACF;;;ACzSO,SAAS,mBAAmB,OAAwB;AACzD,SAAO,KAAK;AAAA,IACV;AAAA,MACE,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,aAAa,MAAM,WAAW;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACXO,SAAS,UAAU,aAAuB,WAAW,iBAAiB;AAC3E,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAO,eAAe,QAAQ;AAAA;AAAA,EAChC;AAEA,QAAM,QAAQ,YAAY,IAAI,CAAC,SAAS,OAAO,KAAK,UAAU,IAAI,CAAC,EAAE,EAAE,KAAK,IAAI;AAEhF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe,QAAQ;AAAA,IACvB;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;;;AChBA,uBAAiB;AACjB,kBAA4C;AAIrC,SAAS,iBAAiB,MAAc,SAAkB,SAAkB;AACjF,aAAO,0BAAa,kBAAkB,MAAM,OAAO,GAAG,kBAAkB,MAAM,OAAO,CAAC;AACxF;AAEA,SAAS,kBAAkB,MAAc,UAAmB;AAC1D,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;AAE3D,SAAO,KAAK,IAAI,CAAC,SAAS;AACxB,QAAI,gBAAgB,QAAQ;AAC1B,aAAO;AAAA,IACT;AAGA,QAAI,iBAAAC,QAAK,WAAW,IAAI,GAAG;AACzB,iBAAO,2BAAc,IAAI;AAAA,IAC3B;AAGA,eAAO,2BAAc,iBAAAA,QAAK,QAAQ,MAAM,IAAI,CAAC;AAAA,EAC/C,CAAC;AACH;;;AC7BA,sBAAe;AACf,IAAAC,oBAAiB;AACjB,uBAAe;AACf,IAAAC,eAA8B;AAc9B,eAAsB,mBACpB,MACA,SACA,SACwB;AACxB,QAAM,UAAU,QAAQ,QAAQ,QAAQ,WAAW,CAAC,oBAAoB,CAAC;AAEzE,QAAM,UAAU;AAAA,IACd,QAAQ,QAAQ,WAAW;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,UAAM,iBAAAC,SAAG,SAAS;AAAA,IAC9B,KAAK;AAAA,IACL,UAAU;AAAA,IACV,WAAW;AAAA,IACX,QAAQ;AAAA,EACV,CAAC;AAED,MAAI,UAAU;AACd,QAAM,eAAyB,CAAC;AAEhC,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAK,4BAAc,IAAI;AAC7B,UAAM,OAAO,MAAM,gBAAAC,QAAG,SAAS,MAAM,OAAO;AAE5C,QAAI,QAAQ,cAAc,SAAS;AACjC;AAAA,IACF;AAEA,UAAM,SAAS,yBAAyB,MAAM,IAAI,OAAO;AAEzD,QAAI,CAAC,UAAU,OAAO,SAAS,MAAM;AACnC;AAAA,IACF;AAEA;AACA,iBAAa,KAAK,kBAAAC,QAAK,SAAS,MAAM,IAAI,CAAC;AAE3C,QAAI,QAAQ,QAAQ,OAAO;AACzB,YAAM,gBAAAD,QAAG,UAAU,MAAM,OAAO,MAAM,OAAO;AAAA,IAC/C;AAAA,EACF;AAEA,QAAM,OAAO,QAAQ,QAAQ,QAAQ,UAAU;AAE/C,MAAI,aAAa,SAAS,GAAG;AAC3B,YAAQ;AAAA,MACN;AAAA,QACE,oCAAoC,IAAI,KAAK,OAAO;AAAA,QACpD,GAAG,aAAa,IAAI,CAAC,SAAS,OAAO,IAAI,EAAE;AAAA,MAC7C,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF,OAAO;AACL,YAAQ,KAAK,oCAAoC,IAAI,oBAAoB;AAAA,EAC3E;AAEA,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf;AAAA,IACA,OAAO;AAAA,EACT;AACF;AAEA,SAAS,QAAW,OAAqB;AACvC,SAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC9C;;;AP9EA,IAAM,aAAa;AACnB,IAAM,sBAAsB,OAAO;AAEpB,SAAR,iBAAkC,cAAuC,CAAC,GAAW;AAC1F,QAAM,UAAU,eAAe,WAAW;AAE1C,MAAI;AACJ,MAAI;AACJ,MAAI,kBAAkB;AACtB,MAAI;AACJ,MAAI;AACJ,QAAM,QAAQ,IAAI,gBAAgB;AAElC,QAAM,aAAS;AAAA,IACb,QAAQ,WAAW,CAAC,UAAU,UAAU,QAAQ;AAAA,IAChD,QAAQ,WAAW,CAAC,gBAAgB,OAAO;AAAA,EAC7C;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IAET,eAAe,gBAAgB;AAC7B,eAAS;AAET,wBAAkB;AAAA,QAChB,OAAO;AAAA,QACP,QAAQ,UAAU,WAAW,QAAQ,WAAW,CAAC,oBAAoB;AAAA,QACrE,QAAQ,UAAU,WAChB,QAAQ,WAAW;AAAA,UACjB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,MAAM,aAAa;AACjB,YAAM,QAAQ;AACd,UAAI,CAAC,QAAQ,QAAQ,SAAS;AAC5B;AAAA,MACF;AAEA,UAAI,QAAQ,QAAQ,QAAQ,iBAAiB;AAC3C;AAAA,MACF;AAEA,wBAAkB;AAElB,YAAM,mBAAmB,OAAO,MAAM,SAAS,IAAI;AAAA,IACrD;AAAA,IAEA,UAAU,IAAI;AACZ,UAAI,OAAO,YAAY;AACrB,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,IAAI;AACP,UAAI,OAAO,qBAAqB;AAC9B,eAAO,kBAAkB,KAAK,UAAU,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC;AAAA,MACtE;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,UAAU,MAAM,IAAI;AACxB,yBAAmB;AAEnB,UAAI,CAAC,OAAO,EAAE,KAAK,CAAC,kBAAkB,EAAE,GAAG;AACzC,eAAO;AAAA,MACT;AAEA,UAAI,CAAC,QAAQ,UAAU,SAAS;AAC9B,eAAO;AAAA,MACT;AAEA,UAAI,QAAQ,cAAc,SAAS;AACjC,cAAM,SAAS,yBAAyB,MAAM,IAAI,OAAO;AAEzD,YAAI,CAAC,QAAQ;AACX,gBAAM,WAAW,EAAE;AACnB,iBAAO;AAAA,QACT;AAEA,cAAM,aAAa,IAAI,OAAO,MAAM;AAEpC,4BAAoB,OAAO,OAAO,IAAI,CAAC,SAAS,KAAK,UAAU,CAAC;AAEhE,eAAO;AAAA,UACL,MAAM,OAAO;AAAA,UACb,KAAK,OAAO;AAAA,QACd;AAAA,MACF;AAGA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,iBAAiB;AACrB,YAAM,mBAAmB,OAAO,MAAM,SAAS,KAAK;AAAA,IACtD;AAAA,IAEA,gBAAgB,SAAS;AACvB,eAAS;AAET,aAAO,YAAY,IAAI,0BAA0B,OAAO,MAAM,QAAQ;AACpE,YAAI,UAAU,gBAAgB,kBAAkB;AAChD,YAAI,IAAI,KAAK,UAAU,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC;AAAA,MACrD,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,gBAAgB,KAAK;AACzB,UAAI,CAAC,OAAO,IAAI,IAAI,GAAG;AACrB;AAAA,MACF;AAGA,YAAM,WAAW,IAAI,IAAI;AAGzB,UAAI,OAAO,GAAG,KAAK;AAAA,QACjB,MAAM;AAAA,QACN,OAAO;AAAA,QACP,MAAM,MAAM,WAAW;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,WAAS,oBAAoB,iBAA2B;AACtD,QAAI,CAAC,QAAQ,SAAS,SAAS;AAC7B;AAAA,IACF;AAEA,QAAI,QAAQ,SAAS,sBAAsB,OAAO;AAChD;AAAA,IACF;AAEA,UAAM,WAAW,IAAI,IAAI,QAAQ,SAAS,WAAW;AAErD,UAAM,UAAU,gBAAgB,OAAO,CAAC,SAAS,CAAC,SAAS,IAAI,IAAI,CAAC;AAEpE,QAAI,QAAQ,WAAW,GAAG;AACxB;AAAA,IACF;AAEA,UAAM,UAAU,iDAAiD,QAAQ,KAAK,IAAI,CAAC;AAEnF,QAAI,QAAQ,SAAS,sBAAsB,SAAS;AAClD,YAAM,IAAI,MAAM,OAAO;AAAA,IACzB;AAEA,sBAAkB,KAAK,OAAO;AAAA,EAChC;AACF;AAEA,eAAe,mBACb,MACA,SACA,OACA;AACA,MAAI,QAAQ,SAAS,SAAS;AAC5B,UAAM,eAAe,kBAAAE,QAAK,QAAQ,MAAM,QAAQ,SAAS,MAAM;AAC/D,UAAM,iBAAAC,QAAG,MAAM,kBAAAD,QAAK,QAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9D,UAAM,iBAAAC,QAAG,UAAU,cAAc,mBAAmB,KAAK,GAAG,OAAO;AAAA,EACrE;AAEA,MAAI,QAAQ,IAAI,SAAS;AACvB,UAAM,UAAU,kBAAAD,QAAK,QAAQ,MAAM,QAAQ,IAAI,MAAM;AACrD,UAAM,iBAAAC,QAAG,MAAM,kBAAAD,QAAK,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACzD,UAAM,iBAAAC,QAAG;AAAA,MACP;AAAA,MACA,UAAU,MAAM,mBAAmB,GAAG,QAAQ,IAAI,QAAQ;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AACF;","names":["import_vite","import_node_path","import_promises","MagicString","traverse","path","path","import_node_path","import_vite","fg","fs","path","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/options.ts","../src/state.ts","../src/transformReact.ts","../src/manifest.ts","../src/dts.ts","../src/filter.ts","../src/rewrite.ts","../src/validate.ts"],"sourcesContent":["// src/index.ts\nimport type { Plugin, ResolvedConfig, ViteDevServer } from 'vite'\nimport { createFilter } from 'vite'\nimport path from 'node:path'\nimport fs from 'node:fs/promises'\nimport { resolveOptions, type PermissionPluginOptions } from './options'\nimport { PermissionState } from './state'\nimport { transformReactPermission } from './transformReact'\nimport { createManifest, createManifestJson } from './manifest'\nimport { createDts } from './dts'\nimport { createRootFilter } from './filter'\nimport { rewriteSourceFiles } from './rewrite'\nimport { validatePermissionUsages } from './validate'\n\nconst VIRTUAL_ID = 'virtual:permission-manifest'\nconst RESOLVED_VIRTUAL_ID = '\\0' + VIRTUAL_ID\n\nexport default function permissionPlugin(userOptions: PermissionPluginOptions = {}): Plugin {\n const options = resolveOptions(userOptions)\n\n let config: ResolvedConfig\n let server: ViteDevServer | undefined\n let rewriteExecuted = false\n let transformContext: { warn: (message: string) => void } | undefined\n let transformFilter: ReturnType<typeof createRootFilter> | undefined\n const state = new PermissionState()\n\n const filter = createFilter(\n options.include ?? [/\\.tsx$/, /\\.jsx$/, /\\.vue$/],\n options.exclude ?? [/node_modules/, /\\.git/]\n )\n return {\n name: 'vite-plugin-permission',\n enforce: 'pre',\n\n configResolved(resolvedConfig) {\n config = resolvedConfig\n\n transformFilter = createRootFilter(\n config.root,\n options.transform.include ?? options.include ?? ['src/**/*.{tsx,jsx}'],\n options.transform.exclude ??\n options.exclude ?? [\n '**/node_modules/**',\n '**/dist/**',\n '**/.vite/**',\n '**/.turbo/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/*.stories.*'\n ]\n )\n },\n\n async buildStart() {\n state.clear?.()\n if (!options.rewrite.enabled) {\n return\n }\n\n if (options.rewrite.once && rewriteExecuted) {\n return\n }\n\n rewriteExecuted = true\n\n await rewriteSourceFiles(config.root, options, this)\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ID) {\n return RESOLVED_VIRTUAL_ID\n }\n\n return null\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `export default ${JSON.stringify(createManifest(state), null, 2)}`\n }\n\n return null\n },\n\n async transform(code, id) {\n transformContext = this\n\n if (!filter(id) || !transformFilter?.(id)) {\n return null\n }\n\n if (!options.transform.enabled) {\n return null\n }\n\n if (options.framework === 'react') {\n const result = transformReactPermission(code, id, options)\n\n if (!result) {\n state.removeFile(id)\n return null\n }\n\n state.setFileUsage(id, result.usages)\n\n validatePermissionUsages(result.usages, options.validate, transformContext)\n\n return {\n code: result.code,\n map: result.map\n }\n }\n\n // Vue MVP 阶段建议只扫描,不强行 template 改写\n return null\n },\n\n async generateBundle() {\n await emitManifestAndDts(config.root, options, state)\n },\n\n configureServer(_server) {\n server = _server\n\n server.middlewares.use('/__permission/manifest', async (_req, res) => {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(createManifest(state), null, 2))\n })\n },\n\n async handleHotUpdate(ctx) {\n if (!filter(ctx.file)) {\n return\n }\n\n // 文件变化后,先删除旧记录,等待下一次 transform 写入新记录\n state.removeFile(ctx.file)\n\n // 通知前端或 devtools 权限 manifest 变了\n ctx.server.ws.send({\n type: 'custom',\n event: 'permission-manifest-update',\n data: createManifest(state)\n })\n }\n }\n}\n\nasync function emitManifestAndDts(\n root: string,\n options: ReturnType<typeof resolveOptions>,\n state: PermissionState\n) {\n if (options.manifest.enabled) {\n const manifestPath = path.resolve(root, options.manifest.output)\n await fs.mkdir(path.dirname(manifestPath), { recursive: true })\n await fs.writeFile(manifestPath, createManifestJson(state), 'utf-8')\n }\n\n if (options.dts.enabled) {\n const dtsPath = path.resolve(root, options.dts.output)\n await fs.mkdir(path.dirname(dtsPath), { recursive: true })\n await fs.writeFile(\n dtsPath,\n createDts(state.getUsedPermissions(), options.dts.typeName),\n 'utf-8'\n )\n }\n}\n","// src/options.ts\nimport { z } from 'zod'\n\nconst FilterPatternSchema = z.custom<string | RegExp | Array<string | RegExp>>()\n\nconst GlobPatternSchema = z.union([z.string(), z.array(z.string())])\n\nexport const PluginOptionsSchema = z.object({\n framework: z.enum(['react', 'vue']).default('react'),\n\n componentName: z.string().default('Can'),\n\n importFrom: z.string().default('@eycraf/permission-kit-react'),\n\n /**\n * 全局 include/exclude。\n * transform/rewrite 没单独配置时,会回退使用这里。\n */\n include: FilterPatternSchema.optional(),\n exclude: FilterPatternSchema.optional(),\n\n transform: z\n .object({\n enabled: z.boolean().default(true),\n include: FilterPatternSchema.optional(),\n exclude: FilterPatternSchema.optional(),\n attributes: z.array(z.string()).default(['permission']),\n modeAttribute: z.string().default('permissionMode'),\n strategyAttribute: z.string().default('permissionStrategy'),\n allowNested: z.boolean().default(false)\n })\n .default({}),\n\n rewrite: z\n .object({\n enabled: z.boolean().default(false),\n\n /**\n * false = dry-run,只打印会修改哪些文件\n * true = 真实写回源文件\n */\n write: z.boolean().default(false),\n\n include: GlobPatternSchema.optional(),\n exclude: GlobPatternSchema.optional(),\n\n /**\n * rewrite 只在 serve/build 启动时跑一次。\n * 默认 true,避免 HMR 时反复改文件。\n */\n once: z.boolean().default(true)\n })\n .default({}),\n\n manifest: z\n .object({\n enabled: z.boolean().default(true),\n output: z.string().default('src/generated/permission-manifest.json')\n })\n .default({}),\n\n dts: z\n .object({\n enabled: z.boolean().default(true),\n output: z.string().default('src/generated/permission.d.ts'),\n typeName: z.string().default('PermissionKey')\n })\n .default({}),\n\n validate: z\n .object({\n enabled: z.boolean().default(false),\n permissions: z.array(z.string()).default([]),\n unknownPermission: z.enum(['off', 'warn', 'error']).default('warn')\n })\n .default({})\n})\n\nexport type PermissionPluginOptions = z.input<typeof PluginOptionsSchema>\nexport type NormalizedPermissionPluginOptions = z.output<typeof PluginOptionsSchema>\n\nexport function resolveOptions(options: PermissionPluginOptions) {\n return PluginOptionsSchema.parse(options)\n}\n","// src/state.ts\nexport type PermissionUsage = {\n permission: string\n file: string\n line?: number\n column?: number\n component?: string\n}\n\nexport type PermissionManifestUsage = {\n file: string\n line?: number\n column?: number\n component?: string\n}\n\nexport type PermissionManifestEntry = {\n permission: string\n count: number\n files: string[]\n usages: PermissionManifestUsage[]\n}\n\nexport type PermissionManifestSummary = {\n permissions: number\n usages: number\n files: number\n}\n\nexport type PermissionManifest = {\n generatedAt: string\n summary: PermissionManifestSummary\n permissions: PermissionManifestEntry[]\n}\n\nexport class PermissionState {\n private fileUsageMap = new Map<string, PermissionUsage[]>()\n\n clear() {\n this.fileUsageMap.clear()\n }\n\n setFileUsage(file: string, usages: PermissionUsage[]) {\n this.fileUsageMap.set(file, usages)\n }\n\n removeFile(file: string) {\n this.fileUsageMap.delete(file)\n }\n\n getAllUsages() {\n return [...this.fileUsageMap.values()].flat()\n }\n\n getUsedPermissions() {\n return [...new Set(this.getAllUsages().map((item) => item.permission))].sort()\n }\n\n getSummary(): PermissionManifestSummary {\n const usages = this.getAllUsages()\n\n return {\n permissions: new Set(usages.map((item) => item.permission)).size,\n usages: usages.length,\n files: new Set(usages.map((item) => item.file)).size\n }\n }\n\n toManifest() {\n const grouped = new Map<string, PermissionUsage[]>()\n\n for (const usage of this.getAllUsages()) {\n const items = grouped.get(usage.permission) ?? []\n items.push(usage)\n grouped.set(usage.permission, items)\n }\n\n return [...grouped.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([permission, usages]) => {\n const sortedUsages = [...usages].sort((left, right) => compareUsage(left, right))\n const files = [...new Set(sortedUsages.map((item) => item.file))].sort((left, right) =>\n left.localeCompare(right)\n )\n\n return {\n permission,\n count: sortedUsages.length,\n files,\n usages: sortedUsages.map(({ file, line, column, component }) => {\n const entry: PermissionManifestUsage = { file }\n\n if (line !== undefined) {\n entry.line = line\n }\n\n if (column !== undefined) {\n entry.column = column\n }\n\n if (component !== undefined) {\n entry.component = component\n }\n\n return entry\n })\n } satisfies PermissionManifestEntry\n })\n }\n}\n\nfunction compareUsage(left: PermissionUsage, right: PermissionUsage) {\n const fileCompare = left.file.localeCompare(right.file)\n if (fileCompare !== 0) {\n return fileCompare\n }\n\n const lineCompare = (left.line ?? 0) - (right.line ?? 0)\n if (lineCompare !== 0) {\n return lineCompare\n }\n\n const columnCompare = (left.column ?? 0) - (right.column ?? 0)\n if (columnCompare !== 0) {\n return columnCompare\n }\n\n return (left.component ?? '').localeCompare(right.component ?? '')\n}\n","// src/transformReact.ts\nimport { parse } from '@babel/parser'\nimport traverse, { type NodePath } from '@babel/traverse'\nimport type {\n JSXAttribute,\n JSXElement,\n JSXIdentifier,\n JSXMemberExpression,\n JSXNamespacedName\n} from '@babel/types'\nimport MagicString from 'magic-string'\nimport type { NormalizedPermissionPluginOptions } from './options'\nimport type { PermissionUsage } from './state'\n\ntype TransformResult = {\n code: string\n map: ReturnType<MagicString['generateMap']>\n usages: PermissionUsage[]\n}\n\ntype PendingWrap = {\n node: JSXElement\n permissionCode: string\n modeCode: string | undefined\n attrsToRemove: JSXAttribute[]\n}\n\nexport function transformReactPermission(\n code: string,\n id: string,\n options: NormalizedPermissionPluginOptions\n): TransformResult | null {\n if (!/\\.[jt]sx$/.test(id)) {\n return null\n }\n\n const attrs = options.transform.attributes\n const modeAttrName = options.transform.modeAttribute\n const componentName = options.componentName\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n errorRecovery: true\n })\n\n const ms = new MagicString(code)\n const wraps: PendingWrap[] = []\n const usages: PermissionUsage[] = []\n const removes: JSXAttribute[] = []\n\n let hasCanImport = false\n let lastImportEnd = 0\n\n traverse(ast, {\n ImportDeclaration(path) {\n lastImportEnd = Math.max(lastImportEnd, path.node.end ?? 0)\n\n if (path.node.source.value === options.importFrom) {\n for (const specifier of path.node.specifiers) {\n if (specifier.type === 'ImportSpecifier' && specifier.local.name === componentName) {\n hasCanImport = true\n }\n }\n }\n },\n\n JSXElement(path) {\n const node = path.node\n\n // 避免 <Can permission=\"x\"> 被再次包裹\n const openingName = node.openingElement.name\n if (openingName.type === 'JSXIdentifier' && openingName.name === componentName) {\n return\n }\n\n const attributes = node.openingElement.attributes\n const permissionAttr = attributes.find((attr): attr is JSXAttribute => {\n return (\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attrs.includes(attr.name.name)\n )\n })\n\n if (!permissionAttr) {\n return\n }\n\n const modeAttr = attributes.find((attr): attr is JSXAttribute => {\n return (\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attr.name.name === modeAttrName\n )\n })\n\n const permissionCode = getJSXAttributeValueCode(code, permissionAttr)\n if (!permissionCode) {\n return\n }\n\n const modeCode = modeAttr ? getJSXAttributeValueCode(code, modeAttr) : undefined\n\n const permissionLiteral = getStaticStringValue(permissionAttr)\n if (permissionLiteral) {\n const usage: PermissionUsage = {\n permission: permissionLiteral,\n file: id,\n component: componentName\n }\n\n if (permissionAttr.loc?.start.line !== undefined) {\n usage.line = permissionAttr.loc.start.line\n }\n\n if (permissionAttr.loc?.start.column !== undefined) {\n usage.column = permissionAttr.loc.start.column\n }\n\n usages.push(usage)\n }\n\n const attrsToRemove = modeAttr ? [permissionAttr, modeAttr] : [permissionAttr]\n\n // 2. 已经在 <Can> 内部:只删除 permission 属性,不再包裹\n if (isInsideCanElement(path, componentName)) {\n removes.push(...attrsToRemove)\n return\n }\n\n const resolvedModeCode = modeCode ?? undefined\n\n wraps.push({\n node,\n permissionCode,\n modeCode: resolvedModeCode,\n attrsToRemove: modeAttr ? [permissionAttr, modeAttr] : [permissionAttr]\n })\n\n // 4. 可选:如果当前元素已经要被包裹,可以跳过它的子节点\n // 这样可以避免父子都有 permission 时产生嵌套。\n if (!options.transform.allowNested) {\n path.skip()\n }\n }\n })\n\n if (removes.length) {\n // 先删除在 <Can> 内部的冗余 permission 属性\n removes\n .sort((a, b) => (b.start ?? 0) - (a.start ?? 0))\n .forEach((attr) => {\n removeJSXAttribute(ms, code, attr)\n })\n }\n\n if (wraps.length === 0) {\n if (removes.length === 0) {\n return null\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({\n source: id,\n includeContent: true,\n hires: true\n }),\n usages\n }\n }\n\n // 倒序修改,避免位置偏移\n wraps\n .sort((a, b) => (b.node.start ?? 0) - (a.node.start ?? 0))\n .forEach((item) => {\n for (const attr of item.attrsToRemove) {\n removeJSXAttribute(ms, code, attr)\n }\n\n const start = item.node.start\n const end = item.node.end\n\n if (start == null || end == null) {\n return\n }\n\n const modePart = item.modeCode ? ` mode={${item.modeCode}}` : ''\n\n ms.prependLeft(start, `<${componentName} permission={${item.permissionCode}}${modePart}>`)\n\n ms.appendRight(end, `</${componentName}>`)\n })\n\n if (!hasCanImport) {\n const importCode = `import { ${componentName} } from '${options.importFrom}'\\n`\n\n if (lastImportEnd > 0) {\n ms.appendRight(lastImportEnd, `\\n${importCode}`)\n } else {\n ms.prepend(importCode)\n }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({\n source: id,\n includeContent: true,\n hires: true\n }),\n usages\n }\n}\n\nfunction getJSXAttributeValueCode(code: string, attr: JSXAttribute): string | null {\n const value = attr.value\n\n if (!value) {\n return null\n }\n\n if (value.type === 'StringLiteral') {\n return JSON.stringify(value.value)\n }\n\n if (value.type === 'JSXExpressionContainer') {\n const expr = value.expression\n\n if (expr.type === 'JSXEmptyExpression' || expr.start == null || expr.end == null) {\n return null\n }\n\n return code.slice(expr.start, expr.end)\n }\n\n return null\n}\n\nfunction getStaticStringValue(attr: JSXAttribute): string | null {\n const value = attr.value\n\n if (!value) {\n return null\n }\n\n if (value.type === 'StringLiteral') {\n return value.value\n }\n\n if (value.type === 'JSXExpressionContainer' && value.expression.type === 'StringLiteral') {\n return value.expression.value\n }\n\n return null\n}\n\nfunction removeJSXAttribute(ms: MagicString, code: string, attr: JSXAttribute) {\n if (attr.start == null || attr.end == null) {\n return\n }\n\n let start = attr.start\n let end = attr.end\n\n // 顺手删除属性前面的空格,避免留下多余空白\n while (start > 0 && /\\s/.test(code.charAt(start - 1))) {\n start--\n }\n\n ms.remove(start, end)\n}\n\nfunction getJSXName(name: JSXIdentifier | JSXMemberExpression | JSXNamespacedName): string {\n if (name.type === 'JSXIdentifier') {\n return name.name\n }\n\n if (name.type === 'JSXMemberExpression') {\n return `${getJSXName(name.object)}.${getJSXName(name.property)}`\n }\n\n return `${name.namespace.name}:${name.name.name}`\n}\n\nfunction isCanElement(node: JSXElement, componentName: string) {\n const name = getJSXName(node.openingElement.name)\n\n return name === componentName\n}\n\nfunction isInsideCanElement(path: NodePath<JSXElement>, componentName: string) {\n return Boolean(\n path.findParent((parentPath) => {\n if (!parentPath.isJSXElement()) {\n return false\n }\n\n return isCanElement(parentPath.node, componentName)\n })\n )\n}\n","// src/manifest.ts\nimport type { PermissionManifest, PermissionState } from './state'\n\nexport function createManifest(state: PermissionState): PermissionManifest {\n return {\n generatedAt: new Date().toISOString(),\n summary: state.getSummary(),\n permissions: state.toManifest()\n }\n}\n\nexport function createManifestJson(state: PermissionState) {\n return JSON.stringify(createManifest(state), null, 2)\n}\n","// src/dts.ts\nexport function createDts(permissions: string[], typeName = 'PermissionKey') {\n const uniquePermissions = [...new Set(permissions)].sort((left, right) =>\n left.localeCompare(right)\n )\n\n const permissionKeysType =\n uniquePermissions.length > 0\n ? `readonly [${uniquePermissions.map((item) => JSON.stringify(item)).join(', ')}]`\n : 'readonly string[]'\n\n return [\n '/* eslint-disable */',\n '// This file is generated by vite-plugin-permission.',\n '// Do not edit manually.',\n '',\n `export declare const permissionKeys: ${permissionKeysType}`,\n `export type ${typeName} = typeof permissionKeys[number]`,\n '',\n `export type PermissionUsage = {`,\n ` permission: ${typeName}`,\n ' file: string',\n ' line?: number',\n ' column?: number',\n ' component?: string',\n '}',\n '',\n `export type PermissionManifestEntry = {`,\n ` permission: ${typeName}`,\n ' count: number',\n ' files: readonly string[]',\n ' usages: readonly PermissionUsage[]',\n '}',\n '',\n 'export type PermissionManifestSummary = {',\n ' permissions: number',\n ' usages: number',\n ' files: number',\n '}',\n '',\n 'export type PermissionManifest = {',\n ' generatedAt: string',\n ' summary: PermissionManifestSummary',\n ' permissions: readonly PermissionManifestEntry[]',\n '}',\n ''\n ].join('\\n')\n}\n","// packages/vite-plugin/src/filter.ts\nimport path from 'node:path'\nimport { createFilter, normalizePath } from 'vite'\n\ntype Pattern = string | RegExp | Array<string | RegExp> | undefined\n\nexport function createRootFilter(root: string, include: Pattern, exclude: Pattern) {\n return createFilter(normalizePatterns(root, include), normalizePatterns(root, exclude))\n}\n\nfunction normalizePatterns(root: string, patterns: Pattern) {\n if (!patterns) {\n return undefined\n }\n\n const list = Array.isArray(patterns) ? patterns : [patterns]\n\n return list.map((item) => {\n if (item instanceof RegExp) {\n return item\n }\n\n // 绝对路径 glob:只做 POSIX 规范化\n if (path.isAbsolute(item)) {\n return normalizePath(item)\n }\n\n // 相对路径 glob:基于 Vite root 转绝对\n return normalizePath(path.resolve(root, item))\n })\n}\n\nexport function cleanId(id: string) {\n return normalizePath(id.split('?')[0] ?? id)\n}\n","// packages/vite-plugin/src/rewrite.ts\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport fg from 'fast-glob'\nimport { normalizePath } from 'vite'\nimport type { NormalizedPermissionPluginOptions } from './options'\nimport { transformReactPermission } from './transformReact'\n\ntype LoggerLike = {\n warn(message: string): void\n}\n\nexport type RewriteResult = {\n checked: number\n changed: number\n files: string[]\n}\n\nexport async function rewriteSourceFiles(\n root: string,\n options: NormalizedPermissionPluginOptions,\n context: LoggerLike\n): Promise<RewriteResult> {\n const include = toArray(options.rewrite.include ?? ['src/**/*.{tsx,jsx}'])\n\n const exclude = toArray(\n options.rewrite.exclude ?? [\n '**/node_modules/**',\n '**/dist/**',\n '**/.vite/**',\n '**/.turbo/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/*.stories.*'\n ]\n )\n\n const files = await fg(include, {\n cwd: root,\n absolute: true,\n onlyFiles: true,\n ignore: exclude\n })\n\n let changed = 0\n const changedFiles: string[] = []\n\n for (const file of files) {\n const id = normalizePath(file)\n const code = await fs.readFile(file, 'utf-8')\n\n if (options.framework !== 'react') {\n continue\n }\n\n const result = transformReactPermission(code, id, options)\n\n if (!result || result.code === code) {\n continue\n }\n\n changed++\n changedFiles.push(path.relative(root, file))\n\n if (options.rewrite.write) {\n await fs.writeFile(file, result.code, 'utf-8')\n }\n }\n\n const mode = options.rewrite.write ? 'write' : 'dry-run'\n\n if (changedFiles.length > 0) {\n context.warn(\n [\n `[vite-plugin-permission] rewrite ${mode}: ${changed} file(s) would change`,\n ...changedFiles.map((file) => ` - ${file}`)\n ].join('\\n')\n )\n } else {\n context.warn(`[vite-plugin-permission] rewrite ${mode}: no files changed`)\n }\n\n return {\n checked: files.length,\n changed,\n files: changedFiles\n }\n}\n\nfunction toArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value]\n}\n","// src/validate.ts\nimport type { NormalizedPermissionPluginOptions } from './options'\nimport type { PermissionUsage } from './state'\n\ntype ValidationReporter = {\n warn(message: string): void\n}\n\nexport function validatePermissionUsages(\n usages: PermissionUsage[],\n options: NormalizedPermissionPluginOptions['validate'],\n reporter?: ValidationReporter\n) {\n if (!options.enabled || options.unknownPermission === 'off') {\n return []\n }\n\n const knownSet = new Set(options.permissions)\n const grouped = new Map<string, PermissionUsage[]>()\n\n for (const usage of usages) {\n if (knownSet.has(usage.permission)) {\n continue\n }\n\n const items = grouped.get(usage.permission) ?? []\n items.push(usage)\n grouped.set(usage.permission, items)\n }\n\n const unknownPermissions = [...grouped.keys()].sort((left, right) => left.localeCompare(right))\n\n if (unknownPermissions.length === 0) {\n return []\n }\n\n const message = buildMessage(unknownPermissions, grouped)\n\n if (options.unknownPermission === 'error') {\n throw new Error(message)\n }\n\n reporter?.warn(message)\n\n return unknownPermissions\n}\n\nfunction buildMessage(permissions: string[], grouped: Map<string, PermissionUsage[]>) {\n const lines = [`[vite-plugin-permission] Unknown permissions: ${permissions.join(', ')}`]\n\n for (const permission of permissions) {\n const usages = grouped.get(permission) ?? []\n for (const usage of usages.slice(0, 3)) {\n lines.push(` - ${formatUsage(usage)}`)\n }\n }\n\n return lines.join('\\n')\n}\n\nfunction formatUsage(usage: PermissionUsage) {\n const location = [usage.file, usage.line, usage.column]\n .filter((item) => item !== undefined)\n .join(':')\n\n if (!usage.component) {\n return `${usage.permission} (${location})`\n }\n\n return `${usage.permission} (${location}, component=${usage.component})`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,IAAAA,eAA6B;AAC7B,IAAAC,oBAAiB;AACjB,IAAAC,mBAAe;;;ACHf,iBAAkB;AAElB,IAAM,sBAAsB,aAAE,OAAiD;AAE/E,IAAM,oBAAoB,aAAE,MAAM,CAAC,aAAE,OAAO,GAAG,aAAE,MAAM,aAAE,OAAO,CAAC,CAAC,CAAC;AAE5D,IAAM,sBAAsB,aAAE,OAAO;AAAA,EAC1C,WAAW,aAAE,KAAK,CAAC,SAAS,KAAK,CAAC,EAAE,QAAQ,OAAO;AAAA,EAEnD,eAAe,aAAE,OAAO,EAAE,QAAQ,KAAK;AAAA,EAEvC,YAAY,aAAE,OAAO,EAAE,QAAQ,8BAA8B;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7D,SAAS,oBAAoB,SAAS;AAAA,EACtC,SAAS,oBAAoB,SAAS;AAAA,EAEtC,WAAW,aACR,OAAO;AAAA,IACN,SAAS,aAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IACjC,SAAS,oBAAoB,SAAS;AAAA,IACtC,SAAS,oBAAoB,SAAS;AAAA,IACtC,YAAY,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC;AAAA,IACtD,eAAe,aAAE,OAAO,EAAE,QAAQ,gBAAgB;AAAA,IAClD,mBAAmB,aAAE,OAAO,EAAE,QAAQ,oBAAoB;AAAA,IAC1D,aAAa,aAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,EACxC,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EAEb,SAAS,aACN,OAAO;AAAA,IACN,SAAS,aAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,IAMlC,OAAO,aAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IAEhC,SAAS,kBAAkB,SAAS;AAAA,IACpC,SAAS,kBAAkB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,IAMpC,MAAM,aAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,EAChC,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EAEb,UAAU,aACP,OAAO;AAAA,IACN,SAAS,aAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IACjC,QAAQ,aAAE,OAAO,EAAE,QAAQ,wCAAwC;AAAA,EACrE,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EAEb,KAAK,aACF,OAAO;AAAA,IACN,SAAS,aAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IACjC,QAAQ,aAAE,OAAO,EAAE,QAAQ,+BAA+B;AAAA,IAC1D,UAAU,aAAE,OAAO,EAAE,QAAQ,eAAe;AAAA,EAC9C,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EAEb,UAAU,aACP,OAAO;AAAA,IACN,SAAS,aAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IAClC,aAAa,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,IAC3C,mBAAmB,aAAE,KAAK,CAAC,OAAO,QAAQ,OAAO,CAAC,EAAE,QAAQ,MAAM;AAAA,EACpE,CAAC,EACA,QAAQ,CAAC,CAAC;AACf,CAAC;AAKM,SAAS,eAAe,SAAkC;AAC/D,SAAO,oBAAoB,MAAM,OAAO;AAC1C;;;AChDO,IAAM,kBAAN,MAAsB;AAAA,EAAtB;AACL,SAAQ,eAAe,oBAAI,IAA+B;AAAA;AAAA,EAE1D,QAAQ;AACN,SAAK,aAAa,MAAM;AAAA,EAC1B;AAAA,EAEA,aAAa,MAAc,QAA2B;AACpD,SAAK,aAAa,IAAI,MAAM,MAAM;AAAA,EACpC;AAAA,EAEA,WAAW,MAAc;AACvB,SAAK,aAAa,OAAO,IAAI;AAAA,EAC/B;AAAA,EAEA,eAAe;AACb,WAAO,CAAC,GAAG,KAAK,aAAa,OAAO,CAAC,EAAE,KAAK;AAAA,EAC9C;AAAA,EAEA,qBAAqB;AACnB,WAAO,CAAC,GAAG,IAAI,IAAI,KAAK,aAAa,EAAE,IAAI,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK;AAAA,EAC/E;AAAA,EAEA,aAAwC;AACtC,UAAM,SAAS,KAAK,aAAa;AAEjC,WAAO;AAAA,MACL,aAAa,IAAI,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,UAAU,CAAC,EAAE;AAAA,MAC5D,QAAQ,OAAO;AAAA,MACf,OAAO,IAAI,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,EAAE;AAAA,IAClD;AAAA,EACF;AAAA,EAEA,aAAa;AACX,UAAM,UAAU,oBAAI,IAA+B;AAEnD,eAAW,SAAS,KAAK,aAAa,GAAG;AACvC,YAAM,QAAQ,QAAQ,IAAI,MAAM,UAAU,KAAK,CAAC;AAChD,YAAM,KAAK,KAAK;AAChB,cAAQ,IAAI,MAAM,YAAY,KAAK;AAAA,IACrC;AAEA,WAAO,CAAC,GAAG,QAAQ,QAAQ,CAAC,EACzB,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,MAAM,KAAK,cAAc,KAAK,CAAC,EACnD,IAAI,CAAC,CAAC,YAAY,MAAM,MAAM;AAC7B,YAAM,eAAe,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,MAAM,UAAU,aAAa,MAAM,KAAK,CAAC;AAChF,YAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,aAAa,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,EAAE;AAAA,QAAK,CAAC,MAAM,UAC5E,KAAK,cAAc,KAAK;AAAA,MAC1B;AAEA,aAAO;AAAA,QACL;AAAA,QACA,OAAO,aAAa;AAAA,QACpB;AAAA,QACA,QAAQ,aAAa,IAAI,CAAC,EAAE,MAAM,MAAM,QAAQ,UAAU,MAAM;AAC9D,gBAAM,QAAiC,EAAE,KAAK;AAE9C,cAAI,SAAS,QAAW;AACtB,kBAAM,OAAO;AAAA,UACf;AAEA,cAAI,WAAW,QAAW;AACxB,kBAAM,SAAS;AAAA,UACjB;AAEA,cAAI,cAAc,QAAW;AAC3B,kBAAM,YAAY;AAAA,UACpB;AAEA,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACL;AACF;AAEA,SAAS,aAAa,MAAuB,OAAwB;AACnE,QAAM,cAAc,KAAK,KAAK,cAAc,MAAM,IAAI;AACtD,MAAI,gBAAgB,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,KAAK,QAAQ,MAAM,MAAM,QAAQ;AACtD,MAAI,gBAAgB,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,iBAAiB,KAAK,UAAU,MAAM,MAAM,UAAU;AAC5D,MAAI,kBAAkB,GAAG;AACvB,WAAO;AAAA,EACT;AAEA,UAAQ,KAAK,aAAa,IAAI,cAAc,MAAM,aAAa,EAAE;AACnE;;;AC/HA,oBAAsB;AACtB,sBAAwC;AAQxC,0BAAwB;AAiBjB,SAAS,yBACd,MACA,IACA,SACwB;AACxB,MAAI,CAAC,YAAY,KAAK,EAAE,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,QAAQ,UAAU;AAChC,QAAM,eAAe,QAAQ,UAAU;AACvC,QAAM,gBAAgB,QAAQ;AAC9B,QAAM,UAAM,qBAAM,MAAM;AAAA,IACtB,YAAY;AAAA,IACZ,SAAS,CAAC,OAAO,YAAY;AAAA,IAC7B,eAAe;AAAA,EACjB,CAAC;AAED,QAAM,KAAK,IAAI,oBAAAC,QAAY,IAAI;AAC/B,QAAM,QAAuB,CAAC;AAC9B,QAAM,SAA4B,CAAC;AACnC,QAAM,UAA0B,CAAC;AAEjC,MAAI,eAAe;AACnB,MAAI,gBAAgB;AAEpB,sBAAAC,SAAS,KAAK;AAAA,IACZ,kBAAkBC,OAAM;AACtB,sBAAgB,KAAK,IAAI,eAAeA,MAAK,KAAK,OAAO,CAAC;AAE1D,UAAIA,MAAK,KAAK,OAAO,UAAU,QAAQ,YAAY;AACjD,mBAAW,aAAaA,MAAK,KAAK,YAAY;AAC5C,cAAI,UAAU,SAAS,qBAAqB,UAAU,MAAM,SAAS,eAAe;AAClF,2BAAe;AAAA,UACjB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,WAAWA,OAAM;AACf,YAAM,OAAOA,MAAK;AAGlB,YAAM,cAAc,KAAK,eAAe;AACxC,UAAI,YAAY,SAAS,mBAAmB,YAAY,SAAS,eAAe;AAC9E;AAAA,MACF;AAEA,YAAM,aAAa,KAAK,eAAe;AACvC,YAAM,iBAAiB,WAAW,KAAK,CAAC,SAA+B;AACrE,eACE,KAAK,SAAS,kBACd,KAAK,KAAK,SAAS,mBACnB,MAAM,SAAS,KAAK,KAAK,IAAI;AAAA,MAEjC,CAAC;AAED,UAAI,CAAC,gBAAgB;AACnB;AAAA,MACF;AAEA,YAAM,WAAW,WAAW,KAAK,CAAC,SAA+B;AAC/D,eACE,KAAK,SAAS,kBACd,KAAK,KAAK,SAAS,mBACnB,KAAK,KAAK,SAAS;AAAA,MAEvB,CAAC;AAED,YAAM,iBAAiB,yBAAyB,MAAM,cAAc;AACpE,UAAI,CAAC,gBAAgB;AACnB;AAAA,MACF;AAEA,YAAM,WAAW,WAAW,yBAAyB,MAAM,QAAQ,IAAI;AAEvE,YAAM,oBAAoB,qBAAqB,cAAc;AAC7D,UAAI,mBAAmB;AACrB,cAAM,QAAyB;AAAA,UAC7B,YAAY;AAAA,UACZ,MAAM;AAAA,UACN,WAAW;AAAA,QACb;AAEA,YAAI,eAAe,KAAK,MAAM,SAAS,QAAW;AAChD,gBAAM,OAAO,eAAe,IAAI,MAAM;AAAA,QACxC;AAEA,YAAI,eAAe,KAAK,MAAM,WAAW,QAAW;AAClD,gBAAM,SAAS,eAAe,IAAI,MAAM;AAAA,QAC1C;AAEA,eAAO,KAAK,KAAK;AAAA,MACnB;AAEA,YAAM,gBAAgB,WAAW,CAAC,gBAAgB,QAAQ,IAAI,CAAC,cAAc;AAG7E,UAAI,mBAAmBA,OAAM,aAAa,GAAG;AAC3C,gBAAQ,KAAK,GAAG,aAAa;AAC7B;AAAA,MACF;AAEA,YAAM,mBAAmB,YAAY;AAErC,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,eAAe,WAAW,CAAC,gBAAgB,QAAQ,IAAI,CAAC,cAAc;AAAA,MACxE,CAAC;AAID,UAAI,CAAC,QAAQ,UAAU,aAAa;AAClC,QAAAA,MAAK,KAAK;AAAA,MACZ;AAAA,IACF;AAAA,EACF,CAAC;AAED,MAAI,QAAQ,QAAQ;AAElB,YACG,KAAK,CAAC,GAAG,OAAO,EAAE,SAAS,MAAM,EAAE,SAAS,EAAE,EAC9C,QAAQ,CAAC,SAAS;AACjB,yBAAmB,IAAI,MAAM,IAAI;AAAA,IACnC,CAAC;AAAA,EACL;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,MAAM,GAAG,SAAS;AAAA,MAClB,KAAK,GAAG,YAAY;AAAA,QAClB,QAAQ;AAAA,QACR,gBAAgB;AAAA,QAChB,OAAO;AAAA,MACT,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AAGA,QACG,KAAK,CAAC,GAAG,OAAO,EAAE,KAAK,SAAS,MAAM,EAAE,KAAK,SAAS,EAAE,EACxD,QAAQ,CAAC,SAAS;AACjB,eAAW,QAAQ,KAAK,eAAe;AACrC,yBAAmB,IAAI,MAAM,IAAI;AAAA,IACnC;AAEA,UAAM,QAAQ,KAAK,KAAK;AACxB,UAAM,MAAM,KAAK,KAAK;AAEtB,QAAI,SAAS,QAAQ,OAAO,MAAM;AAChC;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,WAAW,UAAU,KAAK,QAAQ,MAAM;AAE9D,OAAG,YAAY,OAAO,IAAI,aAAa,gBAAgB,KAAK,cAAc,IAAI,QAAQ,GAAG;AAEzF,OAAG,YAAY,KAAK,KAAK,aAAa,GAAG;AAAA,EAC3C,CAAC;AAEH,MAAI,CAAC,cAAc;AACjB,UAAM,aAAa,YAAY,aAAa,YAAY,QAAQ,UAAU;AAAA;AAE1E,QAAI,gBAAgB,GAAG;AACrB,SAAG,YAAY,eAAe;AAAA,EAAK,UAAU,EAAE;AAAA,IACjD,OAAO;AACL,SAAG,QAAQ,UAAU;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,GAAG,SAAS;AAAA,IAClB,KAAK,GAAG,YAAY;AAAA,MAClB,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,OAAO;AAAA,IACT,CAAC;AAAA,IACD;AAAA,EACF;AACF;AAEA,SAAS,yBAAyB,MAAc,MAAmC;AACjF,QAAM,QAAQ,KAAK;AAEnB,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,SAAS,iBAAiB;AAClC,WAAO,KAAK,UAAU,MAAM,KAAK;AAAA,EACnC;AAEA,MAAI,MAAM,SAAS,0BAA0B;AAC3C,UAAM,OAAO,MAAM;AAEnB,QAAI,KAAK,SAAS,wBAAwB,KAAK,SAAS,QAAQ,KAAK,OAAO,MAAM;AAChF,aAAO;AAAA,IACT;AAEA,WAAO,KAAK,MAAM,KAAK,OAAO,KAAK,GAAG;AAAA,EACxC;AAEA,SAAO;AACT;AAEA,SAAS,qBAAqB,MAAmC;AAC/D,QAAM,QAAQ,KAAK;AAEnB,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,SAAS,iBAAiB;AAClC,WAAO,MAAM;AAAA,EACf;AAEA,MAAI,MAAM,SAAS,4BAA4B,MAAM,WAAW,SAAS,iBAAiB;AACxF,WAAO,MAAM,WAAW;AAAA,EAC1B;AAEA,SAAO;AACT;AAEA,SAAS,mBAAmB,IAAiB,MAAc,MAAoB;AAC7E,MAAI,KAAK,SAAS,QAAQ,KAAK,OAAO,MAAM;AAC1C;AAAA,EACF;AAEA,MAAI,QAAQ,KAAK;AACjB,MAAI,MAAM,KAAK;AAGf,SAAO,QAAQ,KAAK,KAAK,KAAK,KAAK,OAAO,QAAQ,CAAC,CAAC,GAAG;AACrD;AAAA,EACF;AAEA,KAAG,OAAO,OAAO,GAAG;AACtB;AAEA,SAAS,WAAW,MAAuE;AACzF,MAAI,KAAK,SAAS,iBAAiB;AACjC,WAAO,KAAK;AAAA,EACd;AAEA,MAAI,KAAK,SAAS,uBAAuB;AACvC,WAAO,GAAG,WAAW,KAAK,MAAM,CAAC,IAAI,WAAW,KAAK,QAAQ,CAAC;AAAA,EAChE;AAEA,SAAO,GAAG,KAAK,UAAU,IAAI,IAAI,KAAK,KAAK,IAAI;AACjD;AAEA,SAAS,aAAa,MAAkB,eAAuB;AAC7D,QAAM,OAAO,WAAW,KAAK,eAAe,IAAI;AAEhD,SAAO,SAAS;AAClB;AAEA,SAAS,mBAAmBA,OAA4B,eAAuB;AAC7E,SAAO;AAAA,IACLA,MAAK,WAAW,CAAC,eAAe;AAC9B,UAAI,CAAC,WAAW,aAAa,GAAG;AAC9B,eAAO;AAAA,MACT;AAEA,aAAO,aAAa,WAAW,MAAM,aAAa;AAAA,IACpD,CAAC;AAAA,EACH;AACF;;;AC1SO,SAAS,eAAe,OAA4C;AACzE,SAAO;AAAA,IACL,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,SAAS,MAAM,WAAW;AAAA,IAC1B,aAAa,MAAM,WAAW;AAAA,EAChC;AACF;AAEO,SAAS,mBAAmB,OAAwB;AACzD,SAAO,KAAK,UAAU,eAAe,KAAK,GAAG,MAAM,CAAC;AACtD;;;ACZO,SAAS,UAAU,aAAuB,WAAW,iBAAiB;AAC3E,QAAM,oBAAoB,CAAC,GAAG,IAAI,IAAI,WAAW,CAAC,EAAE;AAAA,IAAK,CAAC,MAAM,UAC9D,KAAK,cAAc,KAAK;AAAA,EAC1B;AAEA,QAAM,qBACJ,kBAAkB,SAAS,IACvB,aAAa,kBAAkB,IAAI,CAAC,SAAS,KAAK,UAAU,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,MAC7E;AAEN,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,wCAAwC,kBAAkB;AAAA,IAC1D,eAAe,QAAQ;AAAA,IACvB;AAAA,IACA;AAAA,IACA,iBAAiB,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;;;AC9CA,uBAAiB;AACjB,kBAA4C;AAIrC,SAAS,iBAAiB,MAAc,SAAkB,SAAkB;AACjF,aAAO,0BAAa,kBAAkB,MAAM,OAAO,GAAG,kBAAkB,MAAM,OAAO,CAAC;AACxF;AAEA,SAAS,kBAAkB,MAAc,UAAmB;AAC1D,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;AAE3D,SAAO,KAAK,IAAI,CAAC,SAAS;AACxB,QAAI,gBAAgB,QAAQ;AAC1B,aAAO;AAAA,IACT;AAGA,QAAI,iBAAAC,QAAK,WAAW,IAAI,GAAG;AACzB,iBAAO,2BAAc,IAAI;AAAA,IAC3B;AAGA,eAAO,2BAAc,iBAAAA,QAAK,QAAQ,MAAM,IAAI,CAAC;AAAA,EAC/C,CAAC;AACH;;;AC7BA,sBAAe;AACf,IAAAC,oBAAiB;AACjB,uBAAe;AACf,IAAAC,eAA8B;AAc9B,eAAsB,mBACpB,MACA,SACA,SACwB;AACxB,QAAM,UAAU,QAAQ,QAAQ,QAAQ,WAAW,CAAC,oBAAoB,CAAC;AAEzE,QAAM,UAAU;AAAA,IACd,QAAQ,QAAQ,WAAW;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,UAAM,iBAAAC,SAAG,SAAS;AAAA,IAC9B,KAAK;AAAA,IACL,UAAU;AAAA,IACV,WAAW;AAAA,IACX,QAAQ;AAAA,EACV,CAAC;AAED,MAAI,UAAU;AACd,QAAM,eAAyB,CAAC;AAEhC,aAAW,QAAQ,OAAO;AACxB,UAAM,SAAK,4BAAc,IAAI;AAC7B,UAAM,OAAO,MAAM,gBAAAC,QAAG,SAAS,MAAM,OAAO;AAE5C,QAAI,QAAQ,cAAc,SAAS;AACjC;AAAA,IACF;AAEA,UAAM,SAAS,yBAAyB,MAAM,IAAI,OAAO;AAEzD,QAAI,CAAC,UAAU,OAAO,SAAS,MAAM;AACnC;AAAA,IACF;AAEA;AACA,iBAAa,KAAK,kBAAAC,QAAK,SAAS,MAAM,IAAI,CAAC;AAE3C,QAAI,QAAQ,QAAQ,OAAO;AACzB,YAAM,gBAAAD,QAAG,UAAU,MAAM,OAAO,MAAM,OAAO;AAAA,IAC/C;AAAA,EACF;AAEA,QAAM,OAAO,QAAQ,QAAQ,QAAQ,UAAU;AAE/C,MAAI,aAAa,SAAS,GAAG;AAC3B,YAAQ;AAAA,MACN;AAAA,QACE,oCAAoC,IAAI,KAAK,OAAO;AAAA,QACpD,GAAG,aAAa,IAAI,CAAC,SAAS,OAAO,IAAI,EAAE;AAAA,MAC7C,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF,OAAO;AACL,YAAQ,KAAK,oCAAoC,IAAI,oBAAoB;AAAA,EAC3E;AAEA,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IACf;AAAA,IACA,OAAO;AAAA,EACT;AACF;AAEA,SAAS,QAAW,OAAqB;AACvC,SAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC9C;;;ACnFO,SAAS,yBACd,QACA,SACA,UACA;AACA,MAAI,CAAC,QAAQ,WAAW,QAAQ,sBAAsB,OAAO;AAC3D,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,WAAW,IAAI,IAAI,QAAQ,WAAW;AAC5C,QAAM,UAAU,oBAAI,IAA+B;AAEnD,aAAW,SAAS,QAAQ;AAC1B,QAAI,SAAS,IAAI,MAAM,UAAU,GAAG;AAClC;AAAA,IACF;AAEA,UAAM,QAAQ,QAAQ,IAAI,MAAM,UAAU,KAAK,CAAC;AAChD,UAAM,KAAK,KAAK;AAChB,YAAQ,IAAI,MAAM,YAAY,KAAK;AAAA,EACrC;AAEA,QAAM,qBAAqB,CAAC,GAAG,QAAQ,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;AAE9F,MAAI,mBAAmB,WAAW,GAAG;AACnC,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,UAAU,aAAa,oBAAoB,OAAO;AAExD,MAAI,QAAQ,sBAAsB,SAAS;AACzC,UAAM,IAAI,MAAM,OAAO;AAAA,EACzB;AAEA,YAAU,KAAK,OAAO;AAEtB,SAAO;AACT;AAEA,SAAS,aAAa,aAAuB,SAAyC;AACpF,QAAM,QAAQ,CAAC,iDAAiD,YAAY,KAAK,IAAI,CAAC,EAAE;AAExF,aAAW,cAAc,aAAa;AACpC,UAAM,SAAS,QAAQ,IAAI,UAAU,KAAK,CAAC;AAC3C,eAAW,SAAS,OAAO,MAAM,GAAG,CAAC,GAAG;AACtC,YAAM,KAAK,OAAO,YAAY,KAAK,CAAC,EAAE;AAAA,IACxC;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,YAAY,OAAwB;AAC3C,QAAM,WAAW,CAAC,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,EACnD,OAAO,CAAC,SAAS,SAAS,MAAS,EACnC,KAAK,GAAG;AAEX,MAAI,CAAC,MAAM,WAAW;AACpB,WAAO,GAAG,MAAM,UAAU,KAAK,QAAQ;AAAA,EACzC;AAEA,SAAO,GAAG,MAAM,UAAU,KAAK,QAAQ,eAAe,MAAM,SAAS;AACvE;;;ARxDA,IAAM,aAAa;AACnB,IAAM,sBAAsB,OAAO;AAEpB,SAAR,iBAAkC,cAAuC,CAAC,GAAW;AAC1F,QAAM,UAAU,eAAe,WAAW;AAE1C,MAAI;AACJ,MAAI;AACJ,MAAI,kBAAkB;AACtB,MAAI;AACJ,MAAI;AACJ,QAAM,QAAQ,IAAI,gBAAgB;AAElC,QAAM,aAAS;AAAA,IACb,QAAQ,WAAW,CAAC,UAAU,UAAU,QAAQ;AAAA,IAChD,QAAQ,WAAW,CAAC,gBAAgB,OAAO;AAAA,EAC7C;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IAET,eAAe,gBAAgB;AAC7B,eAAS;AAET,wBAAkB;AAAA,QAChB,OAAO;AAAA,QACP,QAAQ,UAAU,WAAW,QAAQ,WAAW,CAAC,oBAAoB;AAAA,QACrE,QAAQ,UAAU,WAChB,QAAQ,WAAW;AAAA,UACjB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,MAAM,aAAa;AACjB,YAAM,QAAQ;AACd,UAAI,CAAC,QAAQ,QAAQ,SAAS;AAC5B;AAAA,MACF;AAEA,UAAI,QAAQ,QAAQ,QAAQ,iBAAiB;AAC3C;AAAA,MACF;AAEA,wBAAkB;AAElB,YAAM,mBAAmB,OAAO,MAAM,SAAS,IAAI;AAAA,IACrD;AAAA,IAEA,UAAU,IAAI;AACZ,UAAI,OAAO,YAAY;AACrB,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,IAAI;AACP,UAAI,OAAO,qBAAqB;AAC9B,eAAO,kBAAkB,KAAK,UAAU,eAAe,KAAK,GAAG,MAAM,CAAC,CAAC;AAAA,MACzE;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,UAAU,MAAM,IAAI;AACxB,yBAAmB;AAEnB,UAAI,CAAC,OAAO,EAAE,KAAK,CAAC,kBAAkB,EAAE,GAAG;AACzC,eAAO;AAAA,MACT;AAEA,UAAI,CAAC,QAAQ,UAAU,SAAS;AAC9B,eAAO;AAAA,MACT;AAEA,UAAI,QAAQ,cAAc,SAAS;AACjC,cAAM,SAAS,yBAAyB,MAAM,IAAI,OAAO;AAEzD,YAAI,CAAC,QAAQ;AACX,gBAAM,WAAW,EAAE;AACnB,iBAAO;AAAA,QACT;AAEA,cAAM,aAAa,IAAI,OAAO,MAAM;AAEpC,iCAAyB,OAAO,QAAQ,QAAQ,UAAU,gBAAgB;AAE1E,eAAO;AAAA,UACL,MAAM,OAAO;AAAA,UACb,KAAK,OAAO;AAAA,QACd;AAAA,MACF;AAGA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,iBAAiB;AACrB,YAAM,mBAAmB,OAAO,MAAM,SAAS,KAAK;AAAA,IACtD;AAAA,IAEA,gBAAgB,SAAS;AACvB,eAAS;AAET,aAAO,YAAY,IAAI,0BAA0B,OAAO,MAAM,QAAQ;AACpE,YAAI,UAAU,gBAAgB,kBAAkB;AAChD,YAAI,IAAI,KAAK,UAAU,eAAe,KAAK,GAAG,MAAM,CAAC,CAAC;AAAA,MACxD,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,gBAAgB,KAAK;AACzB,UAAI,CAAC,OAAO,IAAI,IAAI,GAAG;AACrB;AAAA,MACF;AAGA,YAAM,WAAW,IAAI,IAAI;AAGzB,UAAI,OAAO,GAAG,KAAK;AAAA,QACjB,MAAM;AAAA,QACN,OAAO;AAAA,QACP,MAAM,eAAe,KAAK;AAAA,MAC5B,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,eAAe,mBACb,MACA,SACA,OACA;AACA,MAAI,QAAQ,SAAS,SAAS;AAC5B,UAAM,eAAe,kBAAAE,QAAK,QAAQ,MAAM,QAAQ,SAAS,MAAM;AAC/D,UAAM,iBAAAC,QAAG,MAAM,kBAAAD,QAAK,QAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9D,UAAM,iBAAAC,QAAG,UAAU,cAAc,mBAAmB,KAAK,GAAG,OAAO;AAAA,EACrE;AAEA,MAAI,QAAQ,IAAI,SAAS;AACvB,UAAM,UAAU,kBAAAD,QAAK,QAAQ,MAAM,QAAQ,IAAI,MAAM;AACrD,UAAM,iBAAAC,QAAG,MAAM,kBAAAD,QAAK,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACzD,UAAM,iBAAAC,QAAG;AAAA,MACP;AAAA,MACA,UAAU,MAAM,mBAAmB,GAAG,QAAQ,IAAI,QAAQ;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AACF;","names":["import_vite","import_node_path","import_promises","MagicString","traverse","path","path","import_node_path","import_vite","fg","fs","path","path","fs"]}
|
package/dist/index.js
CHANGED
|
@@ -80,25 +80,62 @@ var PermissionState = class {
|
|
|
80
80
|
getUsedPermissions() {
|
|
81
81
|
return [...new Set(this.getAllUsages().map((item) => item.permission))].sort();
|
|
82
82
|
}
|
|
83
|
+
getSummary() {
|
|
84
|
+
const usages = this.getAllUsages();
|
|
85
|
+
return {
|
|
86
|
+
permissions: new Set(usages.map((item) => item.permission)).size,
|
|
87
|
+
usages: usages.length,
|
|
88
|
+
files: new Set(usages.map((item) => item.file)).size
|
|
89
|
+
};
|
|
90
|
+
}
|
|
83
91
|
toManifest() {
|
|
84
|
-
|
|
85
|
-
const result = {};
|
|
92
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
86
93
|
for (const usage of this.getAllUsages()) {
|
|
87
|
-
const items =
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
};
|
|
91
|
-
if (usage.line !== void 0) {
|
|
92
|
-
entry.line = usage.line;
|
|
93
|
-
}
|
|
94
|
-
if (usage.column !== void 0) {
|
|
95
|
-
entry.column = usage.column;
|
|
96
|
-
}
|
|
97
|
-
items.push(entry);
|
|
94
|
+
const items = grouped.get(usage.permission) ?? [];
|
|
95
|
+
items.push(usage);
|
|
96
|
+
grouped.set(usage.permission, items);
|
|
98
97
|
}
|
|
99
|
-
return
|
|
98
|
+
return [...grouped.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([permission, usages]) => {
|
|
99
|
+
const sortedUsages = [...usages].sort((left, right) => compareUsage(left, right));
|
|
100
|
+
const files = [...new Set(sortedUsages.map((item) => item.file))].sort(
|
|
101
|
+
(left, right) => left.localeCompare(right)
|
|
102
|
+
);
|
|
103
|
+
return {
|
|
104
|
+
permission,
|
|
105
|
+
count: sortedUsages.length,
|
|
106
|
+
files,
|
|
107
|
+
usages: sortedUsages.map(({ file, line, column, component }) => {
|
|
108
|
+
const entry = { file };
|
|
109
|
+
if (line !== void 0) {
|
|
110
|
+
entry.line = line;
|
|
111
|
+
}
|
|
112
|
+
if (column !== void 0) {
|
|
113
|
+
entry.column = column;
|
|
114
|
+
}
|
|
115
|
+
if (component !== void 0) {
|
|
116
|
+
entry.component = component;
|
|
117
|
+
}
|
|
118
|
+
return entry;
|
|
119
|
+
})
|
|
120
|
+
};
|
|
121
|
+
});
|
|
100
122
|
}
|
|
101
123
|
};
|
|
124
|
+
function compareUsage(left, right) {
|
|
125
|
+
const fileCompare = left.file.localeCompare(right.file);
|
|
126
|
+
if (fileCompare !== 0) {
|
|
127
|
+
return fileCompare;
|
|
128
|
+
}
|
|
129
|
+
const lineCompare = (left.line ?? 0) - (right.line ?? 0);
|
|
130
|
+
if (lineCompare !== 0) {
|
|
131
|
+
return lineCompare;
|
|
132
|
+
}
|
|
133
|
+
const columnCompare = (left.column ?? 0) - (right.column ?? 0);
|
|
134
|
+
if (columnCompare !== 0) {
|
|
135
|
+
return columnCompare;
|
|
136
|
+
}
|
|
137
|
+
return (left.component ?? "").localeCompare(right.component ?? "");
|
|
138
|
+
}
|
|
102
139
|
|
|
103
140
|
// src/transformReact.ts
|
|
104
141
|
import { parse } from "@babel/parser";
|
|
@@ -158,7 +195,8 @@ function transformReactPermission(code, id, options) {
|
|
|
158
195
|
if (permissionLiteral) {
|
|
159
196
|
const usage = {
|
|
160
197
|
permission: permissionLiteral,
|
|
161
|
-
file: id
|
|
198
|
+
file: id,
|
|
199
|
+
component: componentName
|
|
162
200
|
};
|
|
163
201
|
if (permissionAttr.loc?.start.line !== void 0) {
|
|
164
202
|
usage.line = permissionAttr.loc.start.line;
|
|
@@ -303,31 +341,57 @@ function isInsideCanElement(path4, componentName) {
|
|
|
303
341
|
}
|
|
304
342
|
|
|
305
343
|
// src/manifest.ts
|
|
344
|
+
function createManifest(state) {
|
|
345
|
+
return {
|
|
346
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
347
|
+
summary: state.getSummary(),
|
|
348
|
+
permissions: state.toManifest()
|
|
349
|
+
};
|
|
350
|
+
}
|
|
306
351
|
function createManifestJson(state) {
|
|
307
|
-
return JSON.stringify(
|
|
308
|
-
{
|
|
309
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
310
|
-
permissions: state.toManifest()
|
|
311
|
-
},
|
|
312
|
-
null,
|
|
313
|
-
2
|
|
314
|
-
);
|
|
352
|
+
return JSON.stringify(createManifest(state), null, 2);
|
|
315
353
|
}
|
|
316
354
|
|
|
317
355
|
// src/dts.ts
|
|
318
356
|
function createDts(permissions, typeName = "PermissionKey") {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
const union = permissions.map((item) => ` | ${JSON.stringify(item)}`).join("\n");
|
|
357
|
+
const uniquePermissions = [...new Set(permissions)].sort(
|
|
358
|
+
(left, right) => left.localeCompare(right)
|
|
359
|
+
);
|
|
360
|
+
const permissionKeysType = uniquePermissions.length > 0 ? `readonly [${uniquePermissions.map((item) => JSON.stringify(item)).join(", ")}]` : "readonly string[]";
|
|
324
361
|
return [
|
|
325
362
|
"/* eslint-disable */",
|
|
326
363
|
"// This file is generated by vite-plugin-permission.",
|
|
327
364
|
"// Do not edit manually.",
|
|
328
365
|
"",
|
|
329
|
-
`export
|
|
330
|
-
|
|
366
|
+
`export declare const permissionKeys: ${permissionKeysType}`,
|
|
367
|
+
`export type ${typeName} = typeof permissionKeys[number]`,
|
|
368
|
+
"",
|
|
369
|
+
`export type PermissionUsage = {`,
|
|
370
|
+
` permission: ${typeName}`,
|
|
371
|
+
" file: string",
|
|
372
|
+
" line?: number",
|
|
373
|
+
" column?: number",
|
|
374
|
+
" component?: string",
|
|
375
|
+
"}",
|
|
376
|
+
"",
|
|
377
|
+
`export type PermissionManifestEntry = {`,
|
|
378
|
+
` permission: ${typeName}`,
|
|
379
|
+
" count: number",
|
|
380
|
+
" files: readonly string[]",
|
|
381
|
+
" usages: readonly PermissionUsage[]",
|
|
382
|
+
"}",
|
|
383
|
+
"",
|
|
384
|
+
"export type PermissionManifestSummary = {",
|
|
385
|
+
" permissions: number",
|
|
386
|
+
" usages: number",
|
|
387
|
+
" files: number",
|
|
388
|
+
"}",
|
|
389
|
+
"",
|
|
390
|
+
"export type PermissionManifest = {",
|
|
391
|
+
" generatedAt: string",
|
|
392
|
+
" summary: PermissionManifestSummary",
|
|
393
|
+
" permissions: readonly PermissionManifestEntry[]",
|
|
394
|
+
"}",
|
|
331
395
|
""
|
|
332
396
|
].join("\n");
|
|
333
397
|
}
|
|
@@ -417,6 +481,50 @@ function toArray(value) {
|
|
|
417
481
|
return Array.isArray(value) ? value : [value];
|
|
418
482
|
}
|
|
419
483
|
|
|
484
|
+
// src/validate.ts
|
|
485
|
+
function validatePermissionUsages(usages, options, reporter) {
|
|
486
|
+
if (!options.enabled || options.unknownPermission === "off") {
|
|
487
|
+
return [];
|
|
488
|
+
}
|
|
489
|
+
const knownSet = new Set(options.permissions);
|
|
490
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
491
|
+
for (const usage of usages) {
|
|
492
|
+
if (knownSet.has(usage.permission)) {
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
const items = grouped.get(usage.permission) ?? [];
|
|
496
|
+
items.push(usage);
|
|
497
|
+
grouped.set(usage.permission, items);
|
|
498
|
+
}
|
|
499
|
+
const unknownPermissions = [...grouped.keys()].sort((left, right) => left.localeCompare(right));
|
|
500
|
+
if (unknownPermissions.length === 0) {
|
|
501
|
+
return [];
|
|
502
|
+
}
|
|
503
|
+
const message = buildMessage(unknownPermissions, grouped);
|
|
504
|
+
if (options.unknownPermission === "error") {
|
|
505
|
+
throw new Error(message);
|
|
506
|
+
}
|
|
507
|
+
reporter?.warn(message);
|
|
508
|
+
return unknownPermissions;
|
|
509
|
+
}
|
|
510
|
+
function buildMessage(permissions, grouped) {
|
|
511
|
+
const lines = [`[vite-plugin-permission] Unknown permissions: ${permissions.join(", ")}`];
|
|
512
|
+
for (const permission of permissions) {
|
|
513
|
+
const usages = grouped.get(permission) ?? [];
|
|
514
|
+
for (const usage of usages.slice(0, 3)) {
|
|
515
|
+
lines.push(` - ${formatUsage(usage)}`);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return lines.join("\n");
|
|
519
|
+
}
|
|
520
|
+
function formatUsage(usage) {
|
|
521
|
+
const location = [usage.file, usage.line, usage.column].filter((item) => item !== void 0).join(":");
|
|
522
|
+
if (!usage.component) {
|
|
523
|
+
return `${usage.permission} (${location})`;
|
|
524
|
+
}
|
|
525
|
+
return `${usage.permission} (${location}, component=${usage.component})`;
|
|
526
|
+
}
|
|
527
|
+
|
|
420
528
|
// src/index.ts
|
|
421
529
|
var VIRTUAL_ID = "virtual:permission-manifest";
|
|
422
530
|
var RESOLVED_VIRTUAL_ID = "\0" + VIRTUAL_ID;
|
|
@@ -470,7 +578,7 @@ function permissionPlugin(userOptions = {}) {
|
|
|
470
578
|
},
|
|
471
579
|
load(id) {
|
|
472
580
|
if (id === RESOLVED_VIRTUAL_ID) {
|
|
473
|
-
return `export default ${JSON.stringify(state
|
|
581
|
+
return `export default ${JSON.stringify(createManifest(state), null, 2)}`;
|
|
474
582
|
}
|
|
475
583
|
return null;
|
|
476
584
|
},
|
|
@@ -489,7 +597,7 @@ function permissionPlugin(userOptions = {}) {
|
|
|
489
597
|
return null;
|
|
490
598
|
}
|
|
491
599
|
state.setFileUsage(id, result.usages);
|
|
492
|
-
|
|
600
|
+
validatePermissionUsages(result.usages, options.validate, transformContext);
|
|
493
601
|
return {
|
|
494
602
|
code: result.code,
|
|
495
603
|
map: result.map
|
|
@@ -504,7 +612,7 @@ function permissionPlugin(userOptions = {}) {
|
|
|
504
612
|
server = _server;
|
|
505
613
|
server.middlewares.use("/__permission/manifest", async (_req, res) => {
|
|
506
614
|
res.setHeader("Content-Type", "application/json");
|
|
507
|
-
res.end(JSON.stringify(state
|
|
615
|
+
res.end(JSON.stringify(createManifest(state), null, 2));
|
|
508
616
|
});
|
|
509
617
|
},
|
|
510
618
|
async handleHotUpdate(ctx) {
|
|
@@ -515,28 +623,10 @@ function permissionPlugin(userOptions = {}) {
|
|
|
515
623
|
ctx.server.ws.send({
|
|
516
624
|
type: "custom",
|
|
517
625
|
event: "permission-manifest-update",
|
|
518
|
-
data: state
|
|
626
|
+
data: createManifest(state)
|
|
519
627
|
});
|
|
520
628
|
}
|
|
521
629
|
};
|
|
522
|
-
function validatePermissions(usedPermissions) {
|
|
523
|
-
if (!options.validate.enabled) {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
if (options.validate.unknownPermission === "off") {
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
const knownSet = new Set(options.validate.permissions);
|
|
530
|
-
const unknown = usedPermissions.filter((item) => !knownSet.has(item));
|
|
531
|
-
if (unknown.length === 0) {
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
const message = `[vite-plugin-permission] Unknown permissions: ${unknown.join(", ")}`;
|
|
535
|
-
if (options.validate.unknownPermission === "error") {
|
|
536
|
-
throw new Error(message);
|
|
537
|
-
}
|
|
538
|
-
transformContext?.warn(message);
|
|
539
|
-
}
|
|
540
630
|
}
|
|
541
631
|
async function emitManifestAndDts(root, options, state) {
|
|
542
632
|
if (options.manifest.enabled) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/options.ts","../src/state.ts","../src/transformReact.ts","../src/manifest.ts","../src/dts.ts","../src/filter.ts","../src/rewrite.ts"],"sourcesContent":["// src/index.ts\nimport type { Plugin, ResolvedConfig, ViteDevServer } from 'vite'\nimport { createFilter } from 'vite'\nimport path from 'node:path'\nimport fs from 'node:fs/promises'\nimport { resolveOptions, type PermissionPluginOptions } from './options'\nimport { PermissionState } from './state'\nimport { transformReactPermission } from './transformReact'\nimport { createManifestJson } from './manifest'\nimport { createDts } from './dts'\nimport { createRootFilter } from './filter'\nimport { rewriteSourceFiles } from './rewrite'\n\nconst VIRTUAL_ID = 'virtual:permission-manifest'\nconst RESOLVED_VIRTUAL_ID = '\\0' + VIRTUAL_ID\n\nexport default function permissionPlugin(userOptions: PermissionPluginOptions = {}): Plugin {\n const options = resolveOptions(userOptions)\n\n let config: ResolvedConfig\n let server: ViteDevServer | undefined\n let rewriteExecuted = false\n let transformContext: { warn: (message: string) => void } | undefined\n let transformFilter: ReturnType<typeof createRootFilter> | undefined\n const state = new PermissionState()\n\n const filter = createFilter(\n options.include ?? [/\\.tsx$/, /\\.jsx$/, /\\.vue$/],\n options.exclude ?? [/node_modules/, /\\.git/]\n )\n return {\n name: 'vite-plugin-permission',\n enforce: 'pre',\n\n configResolved(resolvedConfig) {\n config = resolvedConfig\n\n transformFilter = createRootFilter(\n config.root,\n options.transform.include ?? options.include ?? ['src/**/*.{tsx,jsx}'],\n options.transform.exclude ??\n options.exclude ?? [\n '**/node_modules/**',\n '**/dist/**',\n '**/.vite/**',\n '**/.turbo/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/*.stories.*'\n ]\n )\n },\n\n async buildStart() {\n state.clear?.()\n if (!options.rewrite.enabled) {\n return\n }\n\n if (options.rewrite.once && rewriteExecuted) {\n return\n }\n\n rewriteExecuted = true\n\n await rewriteSourceFiles(config.root, options, this)\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ID) {\n return RESOLVED_VIRTUAL_ID\n }\n\n return null\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `export default ${JSON.stringify(state.toManifest(), null, 2)}`\n }\n\n return null\n },\n\n async transform(code, id) {\n transformContext = this\n\n if (!filter(id) || !transformFilter?.(id)) {\n return null\n }\n\n if (!options.transform.enabled) {\n return null\n }\n\n if (options.framework === 'react') {\n const result = transformReactPermission(code, id, options)\n\n if (!result) {\n state.removeFile(id)\n return null\n }\n\n state.setFileUsage(id, result.usages)\n\n validatePermissions(result.usages.map((item) => item.permission))\n\n return {\n code: result.code,\n map: result.map\n }\n }\n\n // Vue MVP 阶段建议只扫描,不强行 template 改写\n return null\n },\n\n async generateBundle() {\n await emitManifestAndDts(config.root, options, state)\n },\n\n configureServer(_server) {\n server = _server\n\n server.middlewares.use('/__permission/manifest', async (_req, res) => {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(state.toManifest(), null, 2))\n })\n },\n\n async handleHotUpdate(ctx) {\n if (!filter(ctx.file)) {\n return\n }\n\n // 文件变化后,先删除旧记录,等待下一次 transform 写入新记录\n state.removeFile(ctx.file)\n\n // 通知前端或 devtools 权限 manifest 变了\n ctx.server.ws.send({\n type: 'custom',\n event: 'permission-manifest-update',\n data: state.toManifest()\n })\n }\n }\n\n function validatePermissions(usedPermissions: string[]) {\n if (!options.validate.enabled) {\n return\n }\n\n if (options.validate.unknownPermission === 'off') {\n return\n }\n\n const knownSet = new Set(options.validate.permissions)\n\n const unknown = usedPermissions.filter((item) => !knownSet.has(item))\n\n if (unknown.length === 0) {\n return\n }\n\n const message = `[vite-plugin-permission] Unknown permissions: ${unknown.join(', ')}`\n\n if (options.validate.unknownPermission === 'error') {\n throw new Error(message)\n }\n\n transformContext?.warn(message)\n }\n}\n\nasync function emitManifestAndDts(\n root: string,\n options: ReturnType<typeof resolveOptions>,\n state: PermissionState\n) {\n if (options.manifest.enabled) {\n const manifestPath = path.resolve(root, options.manifest.output)\n await fs.mkdir(path.dirname(manifestPath), { recursive: true })\n await fs.writeFile(manifestPath, createManifestJson(state), 'utf-8')\n }\n\n if (options.dts.enabled) {\n const dtsPath = path.resolve(root, options.dts.output)\n await fs.mkdir(path.dirname(dtsPath), { recursive: true })\n await fs.writeFile(\n dtsPath,\n createDts(state.getUsedPermissions(), options.dts.typeName),\n 'utf-8'\n )\n }\n}\n","// src/options.ts\nimport { z } from 'zod'\n\nconst FilterPatternSchema = z.custom<string | RegExp | Array<string | RegExp>>()\n\nconst GlobPatternSchema = z.union([z.string(), z.array(z.string())])\n\nexport const PluginOptionsSchema = z.object({\n framework: z.enum(['react', 'vue']).default('react'),\n\n componentName: z.string().default('Can'),\n\n importFrom: z.string().default('@eycraf/permission-kit-react'),\n\n /**\n * 全局 include/exclude。\n * transform/rewrite 没单独配置时,会回退使用这里。\n */\n include: FilterPatternSchema.optional(),\n exclude: FilterPatternSchema.optional(),\n\n transform: z\n .object({\n enabled: z.boolean().default(true),\n include: FilterPatternSchema.optional(),\n exclude: FilterPatternSchema.optional(),\n attributes: z.array(z.string()).default(['permission']),\n modeAttribute: z.string().default('permissionMode'),\n strategyAttribute: z.string().default('permissionStrategy'),\n allowNested: z.boolean().default(false)\n })\n .default({}),\n\n rewrite: z\n .object({\n enabled: z.boolean().default(false),\n\n /**\n * false = dry-run,只打印会修改哪些文件\n * true = 真实写回源文件\n */\n write: z.boolean().default(false),\n\n include: GlobPatternSchema.optional(),\n exclude: GlobPatternSchema.optional(),\n\n /**\n * rewrite 只在 serve/build 启动时跑一次。\n * 默认 true,避免 HMR 时反复改文件。\n */\n once: z.boolean().default(true)\n })\n .default({}),\n\n manifest: z\n .object({\n enabled: z.boolean().default(true),\n output: z.string().default('src/generated/permission-manifest.json')\n })\n .default({}),\n\n dts: z\n .object({\n enabled: z.boolean().default(true),\n output: z.string().default('src/generated/permission.d.ts'),\n typeName: z.string().default('PermissionKey')\n })\n .default({}),\n\n validate: z\n .object({\n enabled: z.boolean().default(false),\n permissions: z.array(z.string()).default([]),\n unknownPermission: z.enum(['off', 'warn', 'error']).default('warn')\n })\n .default({})\n})\n\nexport type PermissionPluginOptions = z.input<typeof PluginOptionsSchema>\nexport type NormalizedPermissionPluginOptions = z.output<typeof PluginOptionsSchema>\n\nexport function resolveOptions(options: PermissionPluginOptions) {\n return PluginOptionsSchema.parse(options)\n}\n","// src/state.ts\nexport type PermissionUsage = {\n permission: string\n file: string\n line?: number\n column?: number\n}\n\nexport class PermissionState {\n private fileUsageMap = new Map<string, PermissionUsage[]>()\n\n clear() {\n this.fileUsageMap.clear()\n }\n\n setFileUsage(file: string, usages: PermissionUsage[]) {\n this.fileUsageMap.set(file, usages)\n }\n\n removeFile(file: string) {\n this.fileUsageMap.delete(file)\n }\n\n getAllUsages() {\n return [...this.fileUsageMap.values()].flat()\n }\n\n getUsedPermissions() {\n return [...new Set(this.getAllUsages().map((item) => item.permission))].sort()\n }\n\n toManifest() {\n const result: Record<\n string,\n Array<{\n file: string\n line?: number\n column?: number\n }>\n > = {}\n\n for (const usage of this.getAllUsages()) {\n const items = (result[usage.permission] ??= [])\n const entry: {\n file: string\n line?: number\n column?: number\n } = {\n file: usage.file\n }\n\n if (usage.line !== undefined) {\n entry.line = usage.line\n }\n\n if (usage.column !== undefined) {\n entry.column = usage.column\n }\n\n items.push(entry)\n }\n\n return result\n }\n}\n","// src/transformReact.ts\nimport { parse } from '@babel/parser'\nimport traverse, { type NodePath } from '@babel/traverse'\nimport type {\n JSXAttribute,\n JSXElement,\n JSXIdentifier,\n JSXMemberExpression,\n JSXNamespacedName\n} from '@babel/types'\nimport MagicString from 'magic-string'\nimport type { NormalizedPermissionPluginOptions } from './options'\nimport type { PermissionUsage } from './state'\n\ntype TransformResult = {\n code: string\n map: ReturnType<MagicString['generateMap']>\n usages: PermissionUsage[]\n}\n\ntype PendingWrap = {\n node: JSXElement\n permissionCode: string\n modeCode: string | undefined\n attrsToRemove: JSXAttribute[]\n}\n\nexport function transformReactPermission(\n code: string,\n id: string,\n options: NormalizedPermissionPluginOptions\n): TransformResult | null {\n if (!/\\.[jt]sx$/.test(id)) {\n return null\n }\n\n const attrs = options.transform.attributes\n const modeAttrName = options.transform.modeAttribute\n const componentName = options.componentName\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n errorRecovery: true\n })\n\n const ms = new MagicString(code)\n const wraps: PendingWrap[] = []\n const usages: PermissionUsage[] = []\n const removes: JSXAttribute[] = []\n\n let hasCanImport = false\n let lastImportEnd = 0\n\n traverse(ast, {\n ImportDeclaration(path) {\n lastImportEnd = Math.max(lastImportEnd, path.node.end ?? 0)\n\n if (path.node.source.value === options.importFrom) {\n for (const specifier of path.node.specifiers) {\n if (specifier.type === 'ImportSpecifier' && specifier.local.name === componentName) {\n hasCanImport = true\n }\n }\n }\n },\n\n JSXElement(path) {\n const node = path.node\n\n // 避免 <Can permission=\"x\"> 被再次包裹\n const openingName = node.openingElement.name\n if (openingName.type === 'JSXIdentifier' && openingName.name === componentName) {\n return\n }\n\n const attributes = node.openingElement.attributes\n const permissionAttr = attributes.find((attr): attr is JSXAttribute => {\n return (\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attrs.includes(attr.name.name)\n )\n })\n\n if (!permissionAttr) {\n return\n }\n\n const modeAttr = attributes.find((attr): attr is JSXAttribute => {\n return (\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attr.name.name === modeAttrName\n )\n })\n\n const permissionCode = getJSXAttributeValueCode(code, permissionAttr)\n if (!permissionCode) {\n return\n }\n\n const modeCode = modeAttr ? getJSXAttributeValueCode(code, modeAttr) : undefined\n\n const permissionLiteral = getStaticStringValue(permissionAttr)\n if (permissionLiteral) {\n const usage: PermissionUsage = {\n permission: permissionLiteral,\n file: id\n }\n\n if (permissionAttr.loc?.start.line !== undefined) {\n usage.line = permissionAttr.loc.start.line\n }\n\n if (permissionAttr.loc?.start.column !== undefined) {\n usage.column = permissionAttr.loc.start.column\n }\n\n usages.push(usage)\n }\n\n const attrsToRemove = modeAttr ? [permissionAttr, modeAttr] : [permissionAttr]\n\n // 2. 已经在 <Can> 内部:只删除 permission 属性,不再包裹\n if (isInsideCanElement(path, componentName)) {\n removes.push(...attrsToRemove)\n return\n }\n\n const resolvedModeCode = modeCode ?? undefined\n\n wraps.push({\n node,\n permissionCode,\n modeCode: resolvedModeCode,\n attrsToRemove: modeAttr ? [permissionAttr, modeAttr] : [permissionAttr]\n })\n\n // 4. 可选:如果当前元素已经要被包裹,可以跳过它的子节点\n // 这样可以避免父子都有 permission 时产生嵌套。\n if (!options.transform.allowNested) {\n path.skip()\n }\n }\n })\n\n if (removes.length) {\n // 先删除在 <Can> 内部的冗余 permission 属性\n removes\n .sort((a, b) => (b.start ?? 0) - (a.start ?? 0))\n .forEach((attr) => {\n removeJSXAttribute(ms, code, attr)\n })\n }\n\n if (wraps.length === 0) {\n if (removes.length === 0) {\n return null\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({\n source: id,\n includeContent: true,\n hires: true\n }),\n usages\n }\n }\n\n // 倒序修改,避免位置偏移\n wraps\n .sort((a, b) => (b.node.start ?? 0) - (a.node.start ?? 0))\n .forEach((item) => {\n for (const attr of item.attrsToRemove) {\n removeJSXAttribute(ms, code, attr)\n }\n\n const start = item.node.start\n const end = item.node.end\n\n if (start == null || end == null) {\n return\n }\n\n const modePart = item.modeCode ? ` mode={${item.modeCode}}` : ''\n\n ms.prependLeft(start, `<${componentName} permission={${item.permissionCode}}${modePart}>`)\n\n ms.appendRight(end, `</${componentName}>`)\n })\n\n if (!hasCanImport) {\n const importCode = `import { ${componentName} } from '${options.importFrom}'\\n`\n\n if (lastImportEnd > 0) {\n ms.appendRight(lastImportEnd, `\\n${importCode}`)\n } else {\n ms.prepend(importCode)\n }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({\n source: id,\n includeContent: true,\n hires: true\n }),\n usages\n }\n}\n\nfunction getJSXAttributeValueCode(code: string, attr: JSXAttribute): string | null {\n const value = attr.value\n\n if (!value) {\n return null\n }\n\n if (value.type === 'StringLiteral') {\n return JSON.stringify(value.value)\n }\n\n if (value.type === 'JSXExpressionContainer') {\n const expr = value.expression\n\n if (expr.type === 'JSXEmptyExpression' || expr.start == null || expr.end == null) {\n return null\n }\n\n return code.slice(expr.start, expr.end)\n }\n\n return null\n}\n\nfunction getStaticStringValue(attr: JSXAttribute): string | null {\n const value = attr.value\n\n if (!value) {\n return null\n }\n\n if (value.type === 'StringLiteral') {\n return value.value\n }\n\n if (value.type === 'JSXExpressionContainer' && value.expression.type === 'StringLiteral') {\n return value.expression.value\n }\n\n return null\n}\n\nfunction removeJSXAttribute(ms: MagicString, code: string, attr: JSXAttribute) {\n if (attr.start == null || attr.end == null) {\n return\n }\n\n let start = attr.start\n let end = attr.end\n\n // 顺手删除属性前面的空格,避免留下多余空白\n while (start > 0 && /\\s/.test(code.charAt(start - 1))) {\n start--\n }\n\n ms.remove(start, end)\n}\n\nfunction getJSXName(name: JSXIdentifier | JSXMemberExpression | JSXNamespacedName): string {\n if (name.type === 'JSXIdentifier') {\n return name.name\n }\n\n if (name.type === 'JSXMemberExpression') {\n return `${getJSXName(name.object)}.${getJSXName(name.property)}`\n }\n\n return `${name.namespace.name}:${name.name.name}`\n}\n\nfunction isCanElement(node: JSXElement, componentName: string) {\n const name = getJSXName(node.openingElement.name)\n\n return name === componentName\n}\n\nfunction isInsideCanElement(path: NodePath<JSXElement>, componentName: string) {\n return Boolean(\n path.findParent((parentPath) => {\n if (!parentPath.isJSXElement()) {\n return false\n }\n\n return isCanElement(parentPath.node, componentName)\n })\n )\n}\n","// src/manifest.ts\nimport type { PermissionState } from './state'\n\nexport function createManifestJson(state: PermissionState) {\n return JSON.stringify(\n {\n generatedAt: new Date().toISOString(),\n permissions: state.toManifest()\n },\n null,\n 2\n )\n}\n","// src/dts.ts\nexport function createDts(permissions: string[], typeName = 'PermissionKey') {\n if (permissions.length === 0) {\n return `export type ${typeName} = string\\n`\n }\n\n const union = permissions.map((item) => ` | ${JSON.stringify(item)}`).join('\\n')\n\n return [\n '/* eslint-disable */',\n '// This file is generated by vite-plugin-permission.',\n '// Do not edit manually.',\n '',\n `export type ${typeName} =`,\n union,\n ''\n ].join('\\n')\n}\n","// packages/vite-plugin/src/filter.ts\nimport path from 'node:path'\nimport { createFilter, normalizePath } from 'vite'\n\ntype Pattern = string | RegExp | Array<string | RegExp> | undefined\n\nexport function createRootFilter(root: string, include: Pattern, exclude: Pattern) {\n return createFilter(normalizePatterns(root, include), normalizePatterns(root, exclude))\n}\n\nfunction normalizePatterns(root: string, patterns: Pattern) {\n if (!patterns) {\n return undefined\n }\n\n const list = Array.isArray(patterns) ? patterns : [patterns]\n\n return list.map((item) => {\n if (item instanceof RegExp) {\n return item\n }\n\n // 绝对路径 glob:只做 POSIX 规范化\n if (path.isAbsolute(item)) {\n return normalizePath(item)\n }\n\n // 相对路径 glob:基于 Vite root 转绝对\n return normalizePath(path.resolve(root, item))\n })\n}\n\nexport function cleanId(id: string) {\n return normalizePath(id.split('?')[0] ?? id)\n}\n","// packages/vite-plugin/src/rewrite.ts\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport fg from 'fast-glob'\nimport { normalizePath } from 'vite'\nimport type { NormalizedPermissionPluginOptions } from './options'\nimport { transformReactPermission } from './transformReact'\n\ntype LoggerLike = {\n warn(message: string): void\n}\n\nexport type RewriteResult = {\n checked: number\n changed: number\n files: string[]\n}\n\nexport async function rewriteSourceFiles(\n root: string,\n options: NormalizedPermissionPluginOptions,\n context: LoggerLike\n): Promise<RewriteResult> {\n const include = toArray(options.rewrite.include ?? ['src/**/*.{tsx,jsx}'])\n\n const exclude = toArray(\n options.rewrite.exclude ?? [\n '**/node_modules/**',\n '**/dist/**',\n '**/.vite/**',\n '**/.turbo/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/*.stories.*'\n ]\n )\n\n const files = await fg(include, {\n cwd: root,\n absolute: true,\n onlyFiles: true,\n ignore: exclude\n })\n\n let changed = 0\n const changedFiles: string[] = []\n\n for (const file of files) {\n const id = normalizePath(file)\n const code = await fs.readFile(file, 'utf-8')\n\n if (options.framework !== 'react') {\n continue\n }\n\n const result = transformReactPermission(code, id, options)\n\n if (!result || result.code === code) {\n continue\n }\n\n changed++\n changedFiles.push(path.relative(root, file))\n\n if (options.rewrite.write) {\n await fs.writeFile(file, result.code, 'utf-8')\n }\n }\n\n const mode = options.rewrite.write ? 'write' : 'dry-run'\n\n if (changedFiles.length > 0) {\n context.warn(\n [\n `[vite-plugin-permission] rewrite ${mode}: ${changed} file(s) would change`,\n ...changedFiles.map((file) => ` - ${file}`)\n ].join('\\n')\n )\n } else {\n context.warn(`[vite-plugin-permission] rewrite ${mode}: no files changed`)\n }\n\n return {\n checked: files.length,\n changed,\n files: changedFiles\n }\n}\n\nfunction toArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value]\n}\n"],"mappings":"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","names":["createFilter","path","fs","path","path","normalizePath","normalizePath","path","createFilter","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/options.ts","../src/state.ts","../src/transformReact.ts","../src/manifest.ts","../src/dts.ts","../src/filter.ts","../src/rewrite.ts","../src/validate.ts"],"sourcesContent":["// src/index.ts\nimport type { Plugin, ResolvedConfig, ViteDevServer } from 'vite'\nimport { createFilter } from 'vite'\nimport path from 'node:path'\nimport fs from 'node:fs/promises'\nimport { resolveOptions, type PermissionPluginOptions } from './options'\nimport { PermissionState } from './state'\nimport { transformReactPermission } from './transformReact'\nimport { createManifest, createManifestJson } from './manifest'\nimport { createDts } from './dts'\nimport { createRootFilter } from './filter'\nimport { rewriteSourceFiles } from './rewrite'\nimport { validatePermissionUsages } from './validate'\n\nconst VIRTUAL_ID = 'virtual:permission-manifest'\nconst RESOLVED_VIRTUAL_ID = '\\0' + VIRTUAL_ID\n\nexport default function permissionPlugin(userOptions: PermissionPluginOptions = {}): Plugin {\n const options = resolveOptions(userOptions)\n\n let config: ResolvedConfig\n let server: ViteDevServer | undefined\n let rewriteExecuted = false\n let transformContext: { warn: (message: string) => void } | undefined\n let transformFilter: ReturnType<typeof createRootFilter> | undefined\n const state = new PermissionState()\n\n const filter = createFilter(\n options.include ?? [/\\.tsx$/, /\\.jsx$/, /\\.vue$/],\n options.exclude ?? [/node_modules/, /\\.git/]\n )\n return {\n name: 'vite-plugin-permission',\n enforce: 'pre',\n\n configResolved(resolvedConfig) {\n config = resolvedConfig\n\n transformFilter = createRootFilter(\n config.root,\n options.transform.include ?? options.include ?? ['src/**/*.{tsx,jsx}'],\n options.transform.exclude ??\n options.exclude ?? [\n '**/node_modules/**',\n '**/dist/**',\n '**/.vite/**',\n '**/.turbo/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/*.stories.*'\n ]\n )\n },\n\n async buildStart() {\n state.clear?.()\n if (!options.rewrite.enabled) {\n return\n }\n\n if (options.rewrite.once && rewriteExecuted) {\n return\n }\n\n rewriteExecuted = true\n\n await rewriteSourceFiles(config.root, options, this)\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ID) {\n return RESOLVED_VIRTUAL_ID\n }\n\n return null\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) {\n return `export default ${JSON.stringify(createManifest(state), null, 2)}`\n }\n\n return null\n },\n\n async transform(code, id) {\n transformContext = this\n\n if (!filter(id) || !transformFilter?.(id)) {\n return null\n }\n\n if (!options.transform.enabled) {\n return null\n }\n\n if (options.framework === 'react') {\n const result = transformReactPermission(code, id, options)\n\n if (!result) {\n state.removeFile(id)\n return null\n }\n\n state.setFileUsage(id, result.usages)\n\n validatePermissionUsages(result.usages, options.validate, transformContext)\n\n return {\n code: result.code,\n map: result.map\n }\n }\n\n // Vue MVP 阶段建议只扫描,不强行 template 改写\n return null\n },\n\n async generateBundle() {\n await emitManifestAndDts(config.root, options, state)\n },\n\n configureServer(_server) {\n server = _server\n\n server.middlewares.use('/__permission/manifest', async (_req, res) => {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(createManifest(state), null, 2))\n })\n },\n\n async handleHotUpdate(ctx) {\n if (!filter(ctx.file)) {\n return\n }\n\n // 文件变化后,先删除旧记录,等待下一次 transform 写入新记录\n state.removeFile(ctx.file)\n\n // 通知前端或 devtools 权限 manifest 变了\n ctx.server.ws.send({\n type: 'custom',\n event: 'permission-manifest-update',\n data: createManifest(state)\n })\n }\n }\n}\n\nasync function emitManifestAndDts(\n root: string,\n options: ReturnType<typeof resolveOptions>,\n state: PermissionState\n) {\n if (options.manifest.enabled) {\n const manifestPath = path.resolve(root, options.manifest.output)\n await fs.mkdir(path.dirname(manifestPath), { recursive: true })\n await fs.writeFile(manifestPath, createManifestJson(state), 'utf-8')\n }\n\n if (options.dts.enabled) {\n const dtsPath = path.resolve(root, options.dts.output)\n await fs.mkdir(path.dirname(dtsPath), { recursive: true })\n await fs.writeFile(\n dtsPath,\n createDts(state.getUsedPermissions(), options.dts.typeName),\n 'utf-8'\n )\n }\n}\n","// src/options.ts\nimport { z } from 'zod'\n\nconst FilterPatternSchema = z.custom<string | RegExp | Array<string | RegExp>>()\n\nconst GlobPatternSchema = z.union([z.string(), z.array(z.string())])\n\nexport const PluginOptionsSchema = z.object({\n framework: z.enum(['react', 'vue']).default('react'),\n\n componentName: z.string().default('Can'),\n\n importFrom: z.string().default('@eycraf/permission-kit-react'),\n\n /**\n * 全局 include/exclude。\n * transform/rewrite 没单独配置时,会回退使用这里。\n */\n include: FilterPatternSchema.optional(),\n exclude: FilterPatternSchema.optional(),\n\n transform: z\n .object({\n enabled: z.boolean().default(true),\n include: FilterPatternSchema.optional(),\n exclude: FilterPatternSchema.optional(),\n attributes: z.array(z.string()).default(['permission']),\n modeAttribute: z.string().default('permissionMode'),\n strategyAttribute: z.string().default('permissionStrategy'),\n allowNested: z.boolean().default(false)\n })\n .default({}),\n\n rewrite: z\n .object({\n enabled: z.boolean().default(false),\n\n /**\n * false = dry-run,只打印会修改哪些文件\n * true = 真实写回源文件\n */\n write: z.boolean().default(false),\n\n include: GlobPatternSchema.optional(),\n exclude: GlobPatternSchema.optional(),\n\n /**\n * rewrite 只在 serve/build 启动时跑一次。\n * 默认 true,避免 HMR 时反复改文件。\n */\n once: z.boolean().default(true)\n })\n .default({}),\n\n manifest: z\n .object({\n enabled: z.boolean().default(true),\n output: z.string().default('src/generated/permission-manifest.json')\n })\n .default({}),\n\n dts: z\n .object({\n enabled: z.boolean().default(true),\n output: z.string().default('src/generated/permission.d.ts'),\n typeName: z.string().default('PermissionKey')\n })\n .default({}),\n\n validate: z\n .object({\n enabled: z.boolean().default(false),\n permissions: z.array(z.string()).default([]),\n unknownPermission: z.enum(['off', 'warn', 'error']).default('warn')\n })\n .default({})\n})\n\nexport type PermissionPluginOptions = z.input<typeof PluginOptionsSchema>\nexport type NormalizedPermissionPluginOptions = z.output<typeof PluginOptionsSchema>\n\nexport function resolveOptions(options: PermissionPluginOptions) {\n return PluginOptionsSchema.parse(options)\n}\n","// src/state.ts\nexport type PermissionUsage = {\n permission: string\n file: string\n line?: number\n column?: number\n component?: string\n}\n\nexport type PermissionManifestUsage = {\n file: string\n line?: number\n column?: number\n component?: string\n}\n\nexport type PermissionManifestEntry = {\n permission: string\n count: number\n files: string[]\n usages: PermissionManifestUsage[]\n}\n\nexport type PermissionManifestSummary = {\n permissions: number\n usages: number\n files: number\n}\n\nexport type PermissionManifest = {\n generatedAt: string\n summary: PermissionManifestSummary\n permissions: PermissionManifestEntry[]\n}\n\nexport class PermissionState {\n private fileUsageMap = new Map<string, PermissionUsage[]>()\n\n clear() {\n this.fileUsageMap.clear()\n }\n\n setFileUsage(file: string, usages: PermissionUsage[]) {\n this.fileUsageMap.set(file, usages)\n }\n\n removeFile(file: string) {\n this.fileUsageMap.delete(file)\n }\n\n getAllUsages() {\n return [...this.fileUsageMap.values()].flat()\n }\n\n getUsedPermissions() {\n return [...new Set(this.getAllUsages().map((item) => item.permission))].sort()\n }\n\n getSummary(): PermissionManifestSummary {\n const usages = this.getAllUsages()\n\n return {\n permissions: new Set(usages.map((item) => item.permission)).size,\n usages: usages.length,\n files: new Set(usages.map((item) => item.file)).size\n }\n }\n\n toManifest() {\n const grouped = new Map<string, PermissionUsage[]>()\n\n for (const usage of this.getAllUsages()) {\n const items = grouped.get(usage.permission) ?? []\n items.push(usage)\n grouped.set(usage.permission, items)\n }\n\n return [...grouped.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([permission, usages]) => {\n const sortedUsages = [...usages].sort((left, right) => compareUsage(left, right))\n const files = [...new Set(sortedUsages.map((item) => item.file))].sort((left, right) =>\n left.localeCompare(right)\n )\n\n return {\n permission,\n count: sortedUsages.length,\n files,\n usages: sortedUsages.map(({ file, line, column, component }) => {\n const entry: PermissionManifestUsage = { file }\n\n if (line !== undefined) {\n entry.line = line\n }\n\n if (column !== undefined) {\n entry.column = column\n }\n\n if (component !== undefined) {\n entry.component = component\n }\n\n return entry\n })\n } satisfies PermissionManifestEntry\n })\n }\n}\n\nfunction compareUsage(left: PermissionUsage, right: PermissionUsage) {\n const fileCompare = left.file.localeCompare(right.file)\n if (fileCompare !== 0) {\n return fileCompare\n }\n\n const lineCompare = (left.line ?? 0) - (right.line ?? 0)\n if (lineCompare !== 0) {\n return lineCompare\n }\n\n const columnCompare = (left.column ?? 0) - (right.column ?? 0)\n if (columnCompare !== 0) {\n return columnCompare\n }\n\n return (left.component ?? '').localeCompare(right.component ?? '')\n}\n","// src/transformReact.ts\nimport { parse } from '@babel/parser'\nimport traverse, { type NodePath } from '@babel/traverse'\nimport type {\n JSXAttribute,\n JSXElement,\n JSXIdentifier,\n JSXMemberExpression,\n JSXNamespacedName\n} from '@babel/types'\nimport MagicString from 'magic-string'\nimport type { NormalizedPermissionPluginOptions } from './options'\nimport type { PermissionUsage } from './state'\n\ntype TransformResult = {\n code: string\n map: ReturnType<MagicString['generateMap']>\n usages: PermissionUsage[]\n}\n\ntype PendingWrap = {\n node: JSXElement\n permissionCode: string\n modeCode: string | undefined\n attrsToRemove: JSXAttribute[]\n}\n\nexport function transformReactPermission(\n code: string,\n id: string,\n options: NormalizedPermissionPluginOptions\n): TransformResult | null {\n if (!/\\.[jt]sx$/.test(id)) {\n return null\n }\n\n const attrs = options.transform.attributes\n const modeAttrName = options.transform.modeAttribute\n const componentName = options.componentName\n const ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n errorRecovery: true\n })\n\n const ms = new MagicString(code)\n const wraps: PendingWrap[] = []\n const usages: PermissionUsage[] = []\n const removes: JSXAttribute[] = []\n\n let hasCanImport = false\n let lastImportEnd = 0\n\n traverse(ast, {\n ImportDeclaration(path) {\n lastImportEnd = Math.max(lastImportEnd, path.node.end ?? 0)\n\n if (path.node.source.value === options.importFrom) {\n for (const specifier of path.node.specifiers) {\n if (specifier.type === 'ImportSpecifier' && specifier.local.name === componentName) {\n hasCanImport = true\n }\n }\n }\n },\n\n JSXElement(path) {\n const node = path.node\n\n // 避免 <Can permission=\"x\"> 被再次包裹\n const openingName = node.openingElement.name\n if (openingName.type === 'JSXIdentifier' && openingName.name === componentName) {\n return\n }\n\n const attributes = node.openingElement.attributes\n const permissionAttr = attributes.find((attr): attr is JSXAttribute => {\n return (\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attrs.includes(attr.name.name)\n )\n })\n\n if (!permissionAttr) {\n return\n }\n\n const modeAttr = attributes.find((attr): attr is JSXAttribute => {\n return (\n attr.type === 'JSXAttribute' &&\n attr.name.type === 'JSXIdentifier' &&\n attr.name.name === modeAttrName\n )\n })\n\n const permissionCode = getJSXAttributeValueCode(code, permissionAttr)\n if (!permissionCode) {\n return\n }\n\n const modeCode = modeAttr ? getJSXAttributeValueCode(code, modeAttr) : undefined\n\n const permissionLiteral = getStaticStringValue(permissionAttr)\n if (permissionLiteral) {\n const usage: PermissionUsage = {\n permission: permissionLiteral,\n file: id,\n component: componentName\n }\n\n if (permissionAttr.loc?.start.line !== undefined) {\n usage.line = permissionAttr.loc.start.line\n }\n\n if (permissionAttr.loc?.start.column !== undefined) {\n usage.column = permissionAttr.loc.start.column\n }\n\n usages.push(usage)\n }\n\n const attrsToRemove = modeAttr ? [permissionAttr, modeAttr] : [permissionAttr]\n\n // 2. 已经在 <Can> 内部:只删除 permission 属性,不再包裹\n if (isInsideCanElement(path, componentName)) {\n removes.push(...attrsToRemove)\n return\n }\n\n const resolvedModeCode = modeCode ?? undefined\n\n wraps.push({\n node,\n permissionCode,\n modeCode: resolvedModeCode,\n attrsToRemove: modeAttr ? [permissionAttr, modeAttr] : [permissionAttr]\n })\n\n // 4. 可选:如果当前元素已经要被包裹,可以跳过它的子节点\n // 这样可以避免父子都有 permission 时产生嵌套。\n if (!options.transform.allowNested) {\n path.skip()\n }\n }\n })\n\n if (removes.length) {\n // 先删除在 <Can> 内部的冗余 permission 属性\n removes\n .sort((a, b) => (b.start ?? 0) - (a.start ?? 0))\n .forEach((attr) => {\n removeJSXAttribute(ms, code, attr)\n })\n }\n\n if (wraps.length === 0) {\n if (removes.length === 0) {\n return null\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({\n source: id,\n includeContent: true,\n hires: true\n }),\n usages\n }\n }\n\n // 倒序修改,避免位置偏移\n wraps\n .sort((a, b) => (b.node.start ?? 0) - (a.node.start ?? 0))\n .forEach((item) => {\n for (const attr of item.attrsToRemove) {\n removeJSXAttribute(ms, code, attr)\n }\n\n const start = item.node.start\n const end = item.node.end\n\n if (start == null || end == null) {\n return\n }\n\n const modePart = item.modeCode ? ` mode={${item.modeCode}}` : ''\n\n ms.prependLeft(start, `<${componentName} permission={${item.permissionCode}}${modePart}>`)\n\n ms.appendRight(end, `</${componentName}>`)\n })\n\n if (!hasCanImport) {\n const importCode = `import { ${componentName} } from '${options.importFrom}'\\n`\n\n if (lastImportEnd > 0) {\n ms.appendRight(lastImportEnd, `\\n${importCode}`)\n } else {\n ms.prepend(importCode)\n }\n }\n\n return {\n code: ms.toString(),\n map: ms.generateMap({\n source: id,\n includeContent: true,\n hires: true\n }),\n usages\n }\n}\n\nfunction getJSXAttributeValueCode(code: string, attr: JSXAttribute): string | null {\n const value = attr.value\n\n if (!value) {\n return null\n }\n\n if (value.type === 'StringLiteral') {\n return JSON.stringify(value.value)\n }\n\n if (value.type === 'JSXExpressionContainer') {\n const expr = value.expression\n\n if (expr.type === 'JSXEmptyExpression' || expr.start == null || expr.end == null) {\n return null\n }\n\n return code.slice(expr.start, expr.end)\n }\n\n return null\n}\n\nfunction getStaticStringValue(attr: JSXAttribute): string | null {\n const value = attr.value\n\n if (!value) {\n return null\n }\n\n if (value.type === 'StringLiteral') {\n return value.value\n }\n\n if (value.type === 'JSXExpressionContainer' && value.expression.type === 'StringLiteral') {\n return value.expression.value\n }\n\n return null\n}\n\nfunction removeJSXAttribute(ms: MagicString, code: string, attr: JSXAttribute) {\n if (attr.start == null || attr.end == null) {\n return\n }\n\n let start = attr.start\n let end = attr.end\n\n // 顺手删除属性前面的空格,避免留下多余空白\n while (start > 0 && /\\s/.test(code.charAt(start - 1))) {\n start--\n }\n\n ms.remove(start, end)\n}\n\nfunction getJSXName(name: JSXIdentifier | JSXMemberExpression | JSXNamespacedName): string {\n if (name.type === 'JSXIdentifier') {\n return name.name\n }\n\n if (name.type === 'JSXMemberExpression') {\n return `${getJSXName(name.object)}.${getJSXName(name.property)}`\n }\n\n return `${name.namespace.name}:${name.name.name}`\n}\n\nfunction isCanElement(node: JSXElement, componentName: string) {\n const name = getJSXName(node.openingElement.name)\n\n return name === componentName\n}\n\nfunction isInsideCanElement(path: NodePath<JSXElement>, componentName: string) {\n return Boolean(\n path.findParent((parentPath) => {\n if (!parentPath.isJSXElement()) {\n return false\n }\n\n return isCanElement(parentPath.node, componentName)\n })\n )\n}\n","// src/manifest.ts\nimport type { PermissionManifest, PermissionState } from './state'\n\nexport function createManifest(state: PermissionState): PermissionManifest {\n return {\n generatedAt: new Date().toISOString(),\n summary: state.getSummary(),\n permissions: state.toManifest()\n }\n}\n\nexport function createManifestJson(state: PermissionState) {\n return JSON.stringify(createManifest(state), null, 2)\n}\n","// src/dts.ts\nexport function createDts(permissions: string[], typeName = 'PermissionKey') {\n const uniquePermissions = [...new Set(permissions)].sort((left, right) =>\n left.localeCompare(right)\n )\n\n const permissionKeysType =\n uniquePermissions.length > 0\n ? `readonly [${uniquePermissions.map((item) => JSON.stringify(item)).join(', ')}]`\n : 'readonly string[]'\n\n return [\n '/* eslint-disable */',\n '// This file is generated by vite-plugin-permission.',\n '// Do not edit manually.',\n '',\n `export declare const permissionKeys: ${permissionKeysType}`,\n `export type ${typeName} = typeof permissionKeys[number]`,\n '',\n `export type PermissionUsage = {`,\n ` permission: ${typeName}`,\n ' file: string',\n ' line?: number',\n ' column?: number',\n ' component?: string',\n '}',\n '',\n `export type PermissionManifestEntry = {`,\n ` permission: ${typeName}`,\n ' count: number',\n ' files: readonly string[]',\n ' usages: readonly PermissionUsage[]',\n '}',\n '',\n 'export type PermissionManifestSummary = {',\n ' permissions: number',\n ' usages: number',\n ' files: number',\n '}',\n '',\n 'export type PermissionManifest = {',\n ' generatedAt: string',\n ' summary: PermissionManifestSummary',\n ' permissions: readonly PermissionManifestEntry[]',\n '}',\n ''\n ].join('\\n')\n}\n","// packages/vite-plugin/src/filter.ts\nimport path from 'node:path'\nimport { createFilter, normalizePath } from 'vite'\n\ntype Pattern = string | RegExp | Array<string | RegExp> | undefined\n\nexport function createRootFilter(root: string, include: Pattern, exclude: Pattern) {\n return createFilter(normalizePatterns(root, include), normalizePatterns(root, exclude))\n}\n\nfunction normalizePatterns(root: string, patterns: Pattern) {\n if (!patterns) {\n return undefined\n }\n\n const list = Array.isArray(patterns) ? patterns : [patterns]\n\n return list.map((item) => {\n if (item instanceof RegExp) {\n return item\n }\n\n // 绝对路径 glob:只做 POSIX 规范化\n if (path.isAbsolute(item)) {\n return normalizePath(item)\n }\n\n // 相对路径 glob:基于 Vite root 转绝对\n return normalizePath(path.resolve(root, item))\n })\n}\n\nexport function cleanId(id: string) {\n return normalizePath(id.split('?')[0] ?? id)\n}\n","// packages/vite-plugin/src/rewrite.ts\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport fg from 'fast-glob'\nimport { normalizePath } from 'vite'\nimport type { NormalizedPermissionPluginOptions } from './options'\nimport { transformReactPermission } from './transformReact'\n\ntype LoggerLike = {\n warn(message: string): void\n}\n\nexport type RewriteResult = {\n checked: number\n changed: number\n files: string[]\n}\n\nexport async function rewriteSourceFiles(\n root: string,\n options: NormalizedPermissionPluginOptions,\n context: LoggerLike\n): Promise<RewriteResult> {\n const include = toArray(options.rewrite.include ?? ['src/**/*.{tsx,jsx}'])\n\n const exclude = toArray(\n options.rewrite.exclude ?? [\n '**/node_modules/**',\n '**/dist/**',\n '**/.vite/**',\n '**/.turbo/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/*.stories.*'\n ]\n )\n\n const files = await fg(include, {\n cwd: root,\n absolute: true,\n onlyFiles: true,\n ignore: exclude\n })\n\n let changed = 0\n const changedFiles: string[] = []\n\n for (const file of files) {\n const id = normalizePath(file)\n const code = await fs.readFile(file, 'utf-8')\n\n if (options.framework !== 'react') {\n continue\n }\n\n const result = transformReactPermission(code, id, options)\n\n if (!result || result.code === code) {\n continue\n }\n\n changed++\n changedFiles.push(path.relative(root, file))\n\n if (options.rewrite.write) {\n await fs.writeFile(file, result.code, 'utf-8')\n }\n }\n\n const mode = options.rewrite.write ? 'write' : 'dry-run'\n\n if (changedFiles.length > 0) {\n context.warn(\n [\n `[vite-plugin-permission] rewrite ${mode}: ${changed} file(s) would change`,\n ...changedFiles.map((file) => ` - ${file}`)\n ].join('\\n')\n )\n } else {\n context.warn(`[vite-plugin-permission] rewrite ${mode}: no files changed`)\n }\n\n return {\n checked: files.length,\n changed,\n files: changedFiles\n }\n}\n\nfunction toArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value]\n}\n","// src/validate.ts\nimport type { NormalizedPermissionPluginOptions } from './options'\nimport type { PermissionUsage } from './state'\n\ntype ValidationReporter = {\n warn(message: string): void\n}\n\nexport function validatePermissionUsages(\n usages: PermissionUsage[],\n options: NormalizedPermissionPluginOptions['validate'],\n reporter?: ValidationReporter\n) {\n if (!options.enabled || options.unknownPermission === 'off') {\n return []\n }\n\n const knownSet = new Set(options.permissions)\n const grouped = new Map<string, PermissionUsage[]>()\n\n for (const usage of usages) {\n if (knownSet.has(usage.permission)) {\n continue\n }\n\n const items = grouped.get(usage.permission) ?? []\n items.push(usage)\n grouped.set(usage.permission, items)\n }\n\n const unknownPermissions = [...grouped.keys()].sort((left, right) => left.localeCompare(right))\n\n if (unknownPermissions.length === 0) {\n return []\n }\n\n const message = buildMessage(unknownPermissions, grouped)\n\n if (options.unknownPermission === 'error') {\n throw new Error(message)\n }\n\n reporter?.warn(message)\n\n return unknownPermissions\n}\n\nfunction buildMessage(permissions: string[], grouped: Map<string, PermissionUsage[]>) {\n const lines = [`[vite-plugin-permission] Unknown permissions: ${permissions.join(', ')}`]\n\n for (const permission of permissions) {\n const usages = grouped.get(permission) ?? []\n for (const usage of usages.slice(0, 3)) {\n lines.push(` - ${formatUsage(usage)}`)\n }\n }\n\n return lines.join('\\n')\n}\n\nfunction formatUsage(usage: PermissionUsage) {\n const location = [usage.file, usage.line, usage.column]\n .filter((item) => item !== undefined)\n .join(':')\n\n if (!usage.component) {\n return `${usage.permission} (${location})`\n }\n\n return `${usage.permission} (${location}, component=${usage.component})`\n}\n"],"mappings":"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","names":["createFilter","path","fs","path","path","normalizePath","normalizePath","path","createFilter","path","fs"]}
|