@vite-plugin-i18n-auto/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,64 @@
1
+ declare function hasChinese(text: string): boolean;
2
+
3
+ /**
4
+ * 基于文件路径与原文生成稳定 key,并在一次处理周期内按 registry 去重碰撞。
5
+ */
6
+ declare function makeStableKey(registry: object, filePath: string, literalText: string): string;
7
+ /** 新一轮构建开始前清空某 registry(用聚合根对象作为 registry) */
8
+ declare function clearKeyRegistry(registry: object): void;
9
+
10
+ type TranslateMode = false | 'manual' | 'ai';
11
+ interface ExtractCodeOptions {
12
+ /** 用于稳定 key 去重的 registry,通常每轮 build 一个对象 */
13
+ keyRegistry: object;
14
+ /** 当前文件绝对路径或 id */
15
+ filePath: string;
16
+ /** 归属语言模块名 */
17
+ moduleName: string;
18
+ /** 默认语言代码 */
19
+ defaultLocale: string;
20
+ /** 是否改写 AST 为 t('key') */
21
+ replaceInSource: boolean;
22
+ /** 静态通道 callee:处于这些调用内的字面量不提取 */
23
+ skipCallNames?: string[];
24
+ }
25
+ interface ExtractCodeResult {
26
+ /** 本文件为 defaultLocale 贡献的 key -> 原文 */
27
+ messages: Record<string, string>;
28
+ /** 替换后的完整源码;未改写则与输入相同 */
29
+ code: string;
30
+ /** 是否发生过替换 */
31
+ modified: boolean;
32
+ }
33
+
34
+ /**
35
+ * 从单文件源码提取中文并按选项替换为 t('key')
36
+ */
37
+ declare function extractFromSource(code: string, options: ExtractCodeOptions): ExtractCodeResult;
38
+
39
+ interface WriteLocalesParams {
40
+ cwd: string;
41
+ outputDir: string;
42
+ /** moduleName -> locale -> messages */
43
+ aggregate: Map<string, Map<string, Record<string, string>>>;
44
+ defaultLocale: string;
45
+ targetLocales: string[];
46
+ translate: TranslateMode;
47
+ }
48
+ /** 生成可供 runtime 安全解析的 index.ts(JSON 数组单行,便于正则 + JSON.parse) */
49
+ declare function generateIndexTsContent(moduleNames: string[], locales: string[]): string;
50
+ /**
51
+ * 将聚合结果写入磁盘并生成 index.ts
52
+ */
53
+ declare function writeLocaleArtifacts(params: WriteLocalesParams): Promise<void>;
54
+
55
+ /**
56
+ * 从 extract 生成的 index.ts 源码中解析 modules、locales(禁止 eval)
57
+ */
58
+ declare function parseModulesAndLocalesFromIndexTs(source: string): {
59
+ modules: string[];
60
+ locales: string[];
61
+ };
62
+
63
+ export { clearKeyRegistry, extractFromSource, generateIndexTsContent, hasChinese, makeStableKey, parseModulesAndLocalesFromIndexTs, writeLocaleArtifacts };
64
+ export type { ExtractCodeOptions, ExtractCodeResult, TranslateMode, WriteLocalesParams };
package/dist/index.js ADDED
@@ -0,0 +1,238 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { parse } from '@babel/parser';
3
+ import traverseImport from '@babel/traverse';
4
+ import genImport from '@babel/generator';
5
+ import * as t from '@babel/types';
6
+ import { mkdir, writeFile, readFile } from 'node:fs/promises';
7
+ import path from 'node:path';
8
+
9
+ const CHINESE_RE = /[\u4e00-\u9fff\u3000-\u303f\uff00-\uffef]/;
10
+ function hasChinese(text) {
11
+ return CHINESE_RE.test(text);
12
+ }
13
+
14
+ const usedKeys = /* @__PURE__ */ new WeakMap();
15
+ function makeStableKey(registry, filePath, literalText) {
16
+ const h = createHash("sha256").update(`${filePath}\0${literalText}`, "utf8").digest("hex").slice(0, 12);
17
+ let base = `i18n_${h}`;
18
+ let set = usedKeys.get(registry);
19
+ if (!set) {
20
+ set = /* @__PURE__ */ new Set();
21
+ usedKeys.set(registry, set);
22
+ }
23
+ let key = base;
24
+ let n = 0;
25
+ while (set.has(key)) {
26
+ key = `${base}_${++n}`;
27
+ }
28
+ set.add(key);
29
+ return key;
30
+ }
31
+ function clearKeyRegistry(registry) {
32
+ usedKeys.delete(registry);
33
+ }
34
+
35
+ const traverseFn = traverseImport.default ?? traverseImport;
36
+ function traverseAst(ast, visitor) {
37
+ traverseFn(ast, visitor);
38
+ }
39
+
40
+ const generateFn = genImport.default ?? genImport;
41
+ function generateCode(ast, code) {
42
+ return generateFn(ast, { retainLines: false, compact: false }, code).code;
43
+ }
44
+
45
+ const DEFAULT_SKIP_CALLS = ["t", "$t"];
46
+ function buildSkipSet(extra) {
47
+ const s = new Set(DEFAULT_SKIP_CALLS);
48
+ if (extra) for (const n of extra) s.add(n);
49
+ return s;
50
+ }
51
+ function calleeMatchesSkip(callee, skipNames) {
52
+ if (t.isIdentifier(callee) && skipNames.has(callee.name)) return true;
53
+ if (t.isMemberExpression(callee) && !callee.computed && t.isIdentifier(callee.object, { name: "i18n" }) && t.isIdentifier(callee.property, { name: "t" })) {
54
+ return true;
55
+ }
56
+ return false;
57
+ }
58
+ function isInsideSkippedCall(path, skipSet) {
59
+ let p = path.parentPath;
60
+ while (p) {
61
+ if (p.isCallExpression() && calleeMatchesSkip(p.node.callee, skipSet)) {
62
+ return true;
63
+ }
64
+ p = p.parentPath;
65
+ }
66
+ return false;
67
+ }
68
+ function isModuleSpecifierLiteral(path) {
69
+ const p = path.parentPath;
70
+ return !!(p?.isImportDeclaration() || p?.isExportAllDeclaration() || p?.isExportNamedDeclaration() || p?.isImportExpression());
71
+ }
72
+ function replaceStringLiteral(path, key, messages, value) {
73
+ messages[key] = value;
74
+ path.replaceWith(t.callExpression(t.identifier("t"), [t.stringLiteral(key)]));
75
+ }
76
+ function replaceJsxText(path, key, messages, value) {
77
+ messages[key] = value;
78
+ path.replaceWith(
79
+ t.jsxExpressionContainer(t.callExpression(t.identifier("t"), [t.stringLiteral(key)]))
80
+ );
81
+ }
82
+ function extractFromSource(code, options) {
83
+ const messages = {};
84
+ let ast;
85
+ try {
86
+ ast = parse(code, {
87
+ sourceType: "module",
88
+ plugins: ["typescript", "jsx"],
89
+ errorRecovery: false
90
+ });
91
+ } catch {
92
+ return { messages: {}, code, modified: false };
93
+ }
94
+ const { keyRegistry, filePath, replaceInSource } = options;
95
+ const skipSet = buildSkipSet(options.skipCallNames);
96
+ traverseAst(ast, {
97
+ StringLiteral(path) {
98
+ const v = path.node.value;
99
+ if (!hasChinese(v)) return;
100
+ if (isModuleSpecifierLiteral(path)) return;
101
+ if (isInsideSkippedCall(path, skipSet)) return;
102
+ const key = makeStableKey(keyRegistry, filePath, v);
103
+ if (replaceInSource) {
104
+ replaceStringLiteral(path, key, messages, v);
105
+ } else {
106
+ messages[key] = v;
107
+ }
108
+ },
109
+ JSXText(path) {
110
+ const v = path.node.value;
111
+ if (!hasChinese(v)) return;
112
+ if (isInsideSkippedCall(path, skipSet)) return;
113
+ if (!v.trim()) return;
114
+ const key = makeStableKey(keyRegistry, filePath, v);
115
+ if (replaceInSource) {
116
+ replaceJsxText(path, key, messages, v);
117
+ } else {
118
+ messages[key] = v;
119
+ }
120
+ }
121
+ });
122
+ const hasMsgs = Object.keys(messages).length > 0;
123
+ if (!hasMsgs) {
124
+ return { messages: {}, code, modified: false };
125
+ }
126
+ if (!replaceInSource) {
127
+ return { messages, code, modified: false };
128
+ }
129
+ const out = generateCode(ast, code);
130
+ return {
131
+ messages,
132
+ code: out,
133
+ modified: true
134
+ };
135
+ }
136
+
137
+ function generateIndexTsContent(moduleNames, locales) {
138
+ const modSorted = [...moduleNames].sort();
139
+ const locSorted = [...locales].sort();
140
+ const localeUnion = locSorted.map((l) => JSON.stringify(l)).join(" | ");
141
+ const moduleUnion = modSorted.map((m) => JSON.stringify(m)).join(" | ");
142
+ const modulesJson = JSON.stringify(modSorted);
143
+ const localesJson = JSON.stringify(locSorted);
144
+ return `/* \u7531 vite-plugin-i18n-auto extract \u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u6539\u6A21\u5757/\u8BED\u8A00\u5217\u8868\u7ED3\u6784 */
145
+ export type Locale = ${localeUnion};
146
+ export type ModuleName = ${moduleUnion};
147
+
148
+ export const modules = ${modulesJson} as const;
149
+ export const locales = ${localesJson} as const;
150
+
151
+ export async function loadModule(
152
+ locale: Locale,
153
+ module: ModuleName
154
+ ): Promise<Record<string, string>> {
155
+ const mod = await import(/* @vite-ignore */ \`./\${module}/\${locale}.json\`);
156
+ return (mod as { default: Record<string, string> }).default;
157
+ }
158
+ `;
159
+ }
160
+ function ensureTargetPlaceholders(aggregate, defaultLocale, targetLocales) {
161
+ for (const [, byLoc] of aggregate) {
162
+ const def = byLoc.get(defaultLocale) ?? {};
163
+ for (const tl of targetLocales) {
164
+ if (!byLoc.has(tl)) byLoc.set(tl, {});
165
+ const tgt = byLoc.get(tl);
166
+ for (const key of Object.keys(def)) {
167
+ if (!(key in tgt)) tgt[key] = "";
168
+ }
169
+ }
170
+ }
171
+ }
172
+ async function readJsonFile(file) {
173
+ try {
174
+ const raw = await readFile(file, "utf8");
175
+ return JSON.parse(raw);
176
+ } catch {
177
+ return {};
178
+ }
179
+ }
180
+ function mergeDefaultLocale(existing, incoming) {
181
+ return { ...existing, ...incoming };
182
+ }
183
+ function mergeTargetLocale(existing, incoming) {
184
+ const out = { ...incoming };
185
+ for (const [k, v] of Object.entries(existing)) {
186
+ if (v && String(v).trim() !== "" && (!out[k] || String(out[k]).trim() === "")) {
187
+ out[k] = v;
188
+ }
189
+ }
190
+ return out;
191
+ }
192
+ async function writeLocaleArtifacts(params) {
193
+ const root = path.resolve(params.cwd, params.outputDir);
194
+ await mkdir(root, { recursive: true });
195
+ const { aggregate, defaultLocale, targetLocales, translate } = params;
196
+ if (translate !== false) {
197
+ ensureTargetPlaceholders(aggregate, defaultLocale, targetLocales);
198
+ }
199
+ const localesToWrite = translate === false ? [defaultLocale] : [defaultLocale, ...targetLocales];
200
+ const moduleNames = [...aggregate.keys()].sort();
201
+ for (const mod of moduleNames) {
202
+ const byLoc = aggregate.get(mod);
203
+ const modDir = path.join(root, mod);
204
+ await mkdir(modDir, { recursive: true });
205
+ for (const loc of localesToWrite) {
206
+ const file = path.join(modDir, `${loc}.json`);
207
+ const incoming = { ...byLoc.get(loc) ?? {} };
208
+ const existing = await readJsonFile(file);
209
+ const merged = loc === defaultLocale ? mergeDefaultLocale(existing, incoming) : mergeTargetLocale(existing, incoming);
210
+ await writeFile(file, `${JSON.stringify(merged, null, 2)}
211
+ `, "utf8");
212
+ }
213
+ }
214
+ const indexLocales = translate === false ? [defaultLocale] : [defaultLocale, ...targetLocales];
215
+ const indexContent = generateIndexTsContent(moduleNames, [...new Set(indexLocales)]);
216
+ await writeFile(path.join(root, "index.ts"), indexContent, "utf8");
217
+ }
218
+
219
+ function parseModulesAndLocalesFromIndexTs(source) {
220
+ const modMatch = source.match(/export const modules = (\[[^\]]*\])\s+as\s+const/);
221
+ const locMatch = source.match(/export const locales = (\[[^\]]*\])\s+as\s+const/);
222
+ let modules = [];
223
+ let locales = [];
224
+ try {
225
+ if (modMatch) modules = JSON.parse(modMatch[1]);
226
+ } catch {
227
+ modules = [];
228
+ }
229
+ try {
230
+ if (locMatch) locales = JSON.parse(locMatch[1]);
231
+ } catch {
232
+ locales = [];
233
+ }
234
+ return { modules, locales };
235
+ }
236
+
237
+ export { clearKeyRegistry, extractFromSource, generateIndexTsContent, hasChinese, makeStableKey, parseModulesAndLocalesFromIndexTs, writeLocaleArtifacts };
238
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/chinese.ts","../src/stable-key.ts","../src/babel-traverse.ts","../src/babel-generate.ts","../src/babel-extract.ts","../src/locale-writer.ts","../src/parse-index.ts"],"sourcesContent":["/** 是否包含需提取的中文(含常见 CJK 标点范围) */\nconst CHINESE_RE = /[\\u4e00-\\u9fff\\u3000-\\u303f\\uff00-\\uffef]/;\n\nexport function hasChinese(text: string): boolean {\n return CHINESE_RE.test(text);\n}\n","import { createHash } from 'node:crypto';\n\nconst usedKeys = new WeakMap<object, Set<string>>();\n\n/**\n * 基于文件路径与原文生成稳定 key,并在一次处理周期内按 registry 去重碰撞。\n */\nexport function makeStableKey(\n registry: object,\n filePath: string,\n literalText: string\n): string {\n const h = createHash('sha256')\n .update(`${filePath}\\0${literalText}`, 'utf8')\n .digest('hex')\n .slice(0, 12);\n let base = `i18n_${h}`;\n let set = usedKeys.get(registry);\n if (!set) {\n set = new Set();\n usedKeys.set(registry, set);\n }\n let key = base;\n let n = 0;\n while (set.has(key)) {\n key = `${base}_${++n}`;\n }\n set.add(key);\n return key;\n}\n\n/** 新一轮构建开始前清空某 registry(用聚合根对象作为 registry) */\nexport function clearKeyRegistry(registry: object): void {\n usedKeys.delete(registry);\n}\n","import traverseImport from '@babel/traverse';\nimport type { NodePath } from '@babel/traverse';\nimport type * as t from '@babel/types';\n\n/* CJS 打包进 ESM 后 default 可能落在 .default 上 */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst traverseFn = ((traverseImport as any).default ?? traverseImport) as (\n ast: t.File,\n visitor: object\n) => void;\n\nexport function traverseAst(ast: t.File, visitor: object): void {\n traverseFn(ast, visitor);\n}\n\nexport type { NodePath };\n","import genImport from '@babel/generator';\nimport type * as t from '@babel/types';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst generateFn = ((genImport as any).default ?? genImport) as (\n ast: t.Node,\n opts?: object,\n code?: string\n) => { code: string };\n\nexport function generateCode(ast: t.File, code: string): string {\n return generateFn(ast, { retainLines: false, compact: false }, code).code;\n}\n","import { parse } from '@babel/parser';\nimport type { NodePath } from '@babel/traverse';\nimport { traverseAst } from './babel-traverse.js';\nimport { generateCode } from './babel-generate.js';\nimport * as t from '@babel/types';\nimport type { ExtractCodeOptions, ExtractCodeResult } from './types.js';\nimport { hasChinese } from './chinese.js';\nimport { makeStableKey } from './stable-key.js';\n\nconst DEFAULT_SKIP_CALLS = ['t', '$t'];\n\nfunction buildSkipSet(extra?: string[]): Set<string> {\n const s = new Set<string>(DEFAULT_SKIP_CALLS);\n if (extra) for (const n of extra) s.add(n);\n return s;\n}\n\nfunction calleeMatchesSkip(\n callee: t.CallExpression['callee'],\n skipNames: Set<string>\n): boolean {\n if (t.isIdentifier(callee) && skipNames.has(callee.name)) return true;\n if (\n t.isMemberExpression(callee) &&\n !callee.computed &&\n t.isIdentifier(callee.object, { name: 'i18n' }) &&\n t.isIdentifier(callee.property, { name: 't' })\n ) {\n return true;\n }\n return false;\n}\n\n/** 字面量是否位于应跳过的 CallExpression 参数树内(t / $t / i18n.t) */\nfunction isInsideSkippedCall(\n path: NodePath<t.StringLiteral | t.JSXText>,\n skipSet: Set<string>\n): boolean {\n let p: NodePath<t.Node> | null = path.parentPath;\n while (p) {\n if (p.isCallExpression() && calleeMatchesSkip(p.node.callee, skipSet)) {\n return true;\n }\n p = p.parentPath;\n }\n return false;\n}\n\n/** import / export / dynamic import 的源字符串不提取 */\nfunction isModuleSpecifierLiteral(path: NodePath<t.StringLiteral>): boolean {\n const p = path.parentPath;\n return !!(\n p?.isImportDeclaration() ||\n p?.isExportAllDeclaration() ||\n p?.isExportNamedDeclaration() ||\n p?.isImportExpression()\n );\n}\n\nfunction replaceStringLiteral(\n path: NodePath<t.StringLiteral>,\n key: string,\n messages: Record<string, string>,\n value: string\n): void {\n messages[key] = value;\n path.replaceWith(t.callExpression(t.identifier('t'), [t.stringLiteral(key)]));\n}\n\nfunction replaceJsxText(\n path: NodePath<t.JSXText>,\n key: string,\n messages: Record<string, string>,\n value: string\n): void {\n messages[key] = value;\n path.replaceWith(\n t.jsxExpressionContainer(t.callExpression(t.identifier('t'), [t.stringLiteral(key)]))\n );\n}\n\n/**\n * 从单文件源码提取中文并按选项替换为 t('key')\n */\nexport function extractFromSource(code: string, options: ExtractCodeOptions): ExtractCodeResult {\n const messages: Record<string, string> = {};\n\n let ast: t.File;\n try {\n ast = parse(code, {\n sourceType: 'module',\n plugins: ['typescript', 'jsx'],\n errorRecovery: false,\n });\n } catch {\n return { messages: {}, code, modified: false };\n }\n\n const { keyRegistry, filePath, replaceInSource } = options;\n const skipSet = buildSkipSet(options.skipCallNames);\n\n traverseAst(ast, {\n StringLiteral(path) {\n const v = path.node.value;\n if (!hasChinese(v)) return;\n if (isModuleSpecifierLiteral(path)) return;\n if (isInsideSkippedCall(path, skipSet)) return;\n\n const key = makeStableKey(keyRegistry, filePath, v);\n if (replaceInSource) {\n replaceStringLiteral(path, key, messages, v);\n } else {\n messages[key] = v;\n }\n },\n JSXText(path) {\n const v = path.node.value;\n if (!hasChinese(v)) return;\n if (isInsideSkippedCall(path, skipSet)) return;\n if (!v.trim()) return;\n\n const key = makeStableKey(keyRegistry, filePath, v);\n if (replaceInSource) {\n replaceJsxText(path, key, messages, v);\n } else {\n messages[key] = v;\n }\n },\n });\n\n const hasMsgs = Object.keys(messages).length > 0;\n if (!hasMsgs) {\n return { messages: {}, code, modified: false };\n }\n\n if (!replaceInSource) {\n return { messages, code, modified: false };\n }\n\n const out = generateCode(ast, code);\n return {\n messages,\n code: out,\n modified: true,\n };\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { TranslateMode } from './types.js';\n\nexport interface WriteLocalesParams {\n cwd: string;\n outputDir: string;\n /** moduleName -> locale -> messages */\n aggregate: Map<string, Map<string, Record<string, string>>>;\n defaultLocale: string;\n targetLocales: string[];\n translate: TranslateMode;\n}\n\n/** 生成可供 runtime 安全解析的 index.ts(JSON 数组单行,便于正则 + JSON.parse) */\nexport function generateIndexTsContent(\n moduleNames: string[],\n locales: string[]\n): string {\n const modSorted = [...moduleNames].sort();\n const locSorted = [...locales].sort();\n const localeUnion = locSorted.map((l) => JSON.stringify(l)).join(' | ');\n const moduleUnion = modSorted.map((m) => JSON.stringify(m)).join(' | ');\n const modulesJson = JSON.stringify(modSorted);\n const localesJson = JSON.stringify(locSorted);\n\n return `/* 由 vite-plugin-i18n-auto extract 生成,请勿手改模块/语言列表结构 */\nexport type Locale = ${localeUnion};\nexport type ModuleName = ${moduleUnion};\n\nexport const modules = ${modulesJson} as const;\nexport const locales = ${localesJson} as const;\n\nexport async function loadModule(\n locale: Locale,\n module: ModuleName\n): Promise<Record<string, string>> {\n const mod = await import(/* @vite-ignore */ \\`./\\${module}/\\${locale}.json\\`);\n return (mod as { default: Record<string, string> }).default;\n}\n`;\n}\n\n/**\n * 填充目标语言 key(占位空串),便于 manual / ai 写入\n */\nfunction ensureTargetPlaceholders(\n aggregate: Map<string, Map<string, Record<string, string>>>,\n defaultLocale: string,\n targetLocales: string[]\n): void {\n for (const [, byLoc] of aggregate) {\n const def = byLoc.get(defaultLocale) ?? {};\n for (const tl of targetLocales) {\n if (!byLoc.has(tl)) byLoc.set(tl, {});\n const tgt = byLoc.get(tl)!;\n for (const key of Object.keys(def)) {\n if (!(key in tgt)) tgt[key] = '';\n }\n }\n }\n}\n\nasync function readJsonFile(file: string): Promise<Record<string, string>> {\n try {\n const raw = await readFile(file, 'utf8');\n return JSON.parse(raw) as Record<string, string>;\n } catch {\n return {};\n }\n}\n\nfunction mergeDefaultLocale(\n existing: Record<string, string>,\n incoming: Record<string, string>\n): Record<string, string> {\n return { ...existing, ...incoming };\n}\n\n/** 目标语言:已有非空译文不被空字符串覆盖 */\nfunction mergeTargetLocale(\n existing: Record<string, string>,\n incoming: Record<string, string>\n): Record<string, string> {\n const out = { ...incoming };\n for (const [k, v] of Object.entries(existing)) {\n if (v && String(v).trim() !== '' && (!out[k] || String(out[k]).trim() === '')) {\n out[k] = v;\n }\n }\n return out;\n}\n\n/**\n * 将聚合结果写入磁盘并生成 index.ts\n */\nexport async function writeLocaleArtifacts(params: WriteLocalesParams): Promise<void> {\n const root = path.resolve(params.cwd, params.outputDir);\n await mkdir(root, { recursive: true });\n\n const { aggregate, defaultLocale, targetLocales, translate } = params;\n\n if (translate !== false) {\n ensureTargetPlaceholders(aggregate, defaultLocale, targetLocales);\n }\n\n const localesToWrite =\n translate === false ? [defaultLocale] : [defaultLocale, ...targetLocales];\n\n const moduleNames = [...aggregate.keys()].sort();\n\n for (const mod of moduleNames) {\n const byLoc = aggregate.get(mod)!;\n const modDir = path.join(root, mod);\n await mkdir(modDir, { recursive: true });\n\n for (const loc of localesToWrite) {\n const file = path.join(modDir, `${loc}.json`);\n const incoming = { ...(byLoc.get(loc) ?? {}) };\n const existing = await readJsonFile(file);\n const merged =\n loc === defaultLocale\n ? mergeDefaultLocale(existing, incoming)\n : mergeTargetLocale(existing, incoming);\n await writeFile(file, `${JSON.stringify(merged, null, 2)}\\n`, 'utf8');\n }\n }\n\n const indexLocales =\n translate === false ? [defaultLocale] : [defaultLocale, ...targetLocales];\n const indexContent = generateIndexTsContent(moduleNames, [...new Set(indexLocales)]);\n await writeFile(path.join(root, 'index.ts'), indexContent, 'utf8');\n}\n","/**\n * 从 extract 生成的 index.ts 源码中解析 modules、locales(禁止 eval)\n */\nexport function parseModulesAndLocalesFromIndexTs(source: string): {\n modules: string[];\n locales: string[];\n} {\n const modMatch = source.match(/export const modules = (\\[[^\\]]*\\])\\s+as\\s+const/);\n const locMatch = source.match(/export const locales = (\\[[^\\]]*\\])\\s+as\\s+const/);\n let modules: string[] = [];\n let locales: string[] = [];\n try {\n if (modMatch) modules = JSON.parse(modMatch[1]) as string[];\n } catch {\n modules = [];\n }\n try {\n if (locMatch) locales = JSON.parse(locMatch[1]) as string[];\n } catch {\n locales = [];\n }\n return { modules, locales };\n}\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@vite-plugin-i18n-auto/core",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "dependencies": {
18
+ "@babel/generator": "^7.26.5",
19
+ "@babel/parser": "^7.26.5",
20
+ "@babel/traverse": "^7.26.5",
21
+ "@babel/types": "^7.26.5",
22
+ "picomatch": "^4.0.2"
23
+ },
24
+ "peerDependencies": {
25
+ "vite": "^4.0.0 || ^5.0.0 || ^6.0.0"
26
+ },
27
+ "devDependencies": {
28
+ "@rollup/plugin-node-resolve": "^15.3.1",
29
+ "@types/babel__generator": "^7.6.8",
30
+ "@types/babel__traverse": "^7.20.6",
31
+ "@types/node": "^22.10.2",
32
+ "rollup": "^4.28.1",
33
+ "rollup-plugin-dts": "^6.1.1",
34
+ "rollup-plugin-esbuild": "^6.1.1",
35
+ "typescript": "^5.7.2",
36
+ "vite": "^6.0.3"
37
+ },
38
+ "scripts": {
39
+ "build": "rollup -c",
40
+ "typecheck": "tsc -p tsconfig.json --noEmit"
41
+ }
42
+ }