@intlify/unplugin-vue-i18n 11.0.3 → 11.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.
- package/README.md +91 -0
- package/lib/index.cjs +356 -63
- package/lib/index.d.cts +1 -1
- package/lib/index.d.mts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.mjs +356 -63
- package/lib/types.d.cts +26 -1
- package/lib/types.d.mts +26 -1
- package/lib/types.d.ts +26 -1
- package/lib/vite.cjs +0 -1
- package/lib/vite.mjs +0 -1
- package/lib/webpack.cjs +0 -1
- package/lib/webpack.mjs +0 -1
- package/package.json +2 -2
package/lib/index.cjs
CHANGED
|
@@ -16,7 +16,6 @@ const pc = require('picocolors');
|
|
|
16
16
|
const eslintUitls = require('@eslint-community/eslint-utils');
|
|
17
17
|
const vueI18nExtensions = require('@intlify/vue-i18n-extensions');
|
|
18
18
|
const scopeManager = require('@typescript-eslint/scope-manager');
|
|
19
|
-
const typescriptEstree = require('@typescript-eslint/typescript-estree');
|
|
20
19
|
|
|
21
20
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
22
21
|
|
|
@@ -50,6 +49,11 @@ function resolveOptions(options) {
|
|
|
50
49
|
const optimizeTranslationDirective = shared.isString(options.optimizeTranslationDirective) || shared.isArray(options.optimizeTranslationDirective) ? options.optimizeTranslationDirective : !!options.optimizeTranslationDirective;
|
|
51
50
|
const translationIdentifiers = /* @__PURE__ */ new Map();
|
|
52
51
|
const transformI18nBlock = typeof options.transformI18nBlock === "function" ? options.transformI18nBlock : null;
|
|
52
|
+
const treeShaking = options.treeShaking ? typeof options.treeShaking === "object" ? {
|
|
53
|
+
safelist: options.treeShaking.safelist || [],
|
|
54
|
+
dynamicKeyStrategy: options.treeShaking.dynamicKeyStrategy || "keep-all",
|
|
55
|
+
scanPatterns: options.treeShaking.scanPatterns
|
|
56
|
+
} : { safelist: [], dynamicKeyStrategy: "keep-all" } : null;
|
|
53
57
|
return {
|
|
54
58
|
include: normalizeGlobOption(options.include || []),
|
|
55
59
|
exclude: normalizeGlobOption(options.exclude),
|
|
@@ -68,7 +72,8 @@ function resolveOptions(options) {
|
|
|
68
72
|
escapeHtml,
|
|
69
73
|
optimizeTranslationDirective,
|
|
70
74
|
translationIdentifiers,
|
|
71
|
-
transformI18nBlock
|
|
75
|
+
transformI18nBlock,
|
|
76
|
+
treeShaking
|
|
72
77
|
};
|
|
73
78
|
}
|
|
74
79
|
|
|
@@ -180,7 +185,7 @@ const RE_VIRTUAL_PREFIXED_INTLIFY_BUNDLE_IMPORT_ID = new RegExp(
|
|
|
180
185
|
const RE_RESOURCE_FORMAT = /\.(json5?|ya?ml|[c|m]?[j|t]s)$/;
|
|
181
186
|
const RE_SFC_I18N_CUSTOM_BLOCK = /\?vue&type=i18n/;
|
|
182
187
|
const RE_SFC_I18N_WEBPACK_CUSTOM_BLOCK = /blockType=i18n/;
|
|
183
|
-
const debug$
|
|
188
|
+
const debug$3 = createDebug__default(resolveNamespace("resource"));
|
|
184
189
|
function resourcePlugin({
|
|
185
190
|
onlyLocales,
|
|
186
191
|
include,
|
|
@@ -198,7 +203,7 @@ function resourcePlugin({
|
|
|
198
203
|
allowDynamic,
|
|
199
204
|
escapeHtml,
|
|
200
205
|
transformI18nBlock
|
|
201
|
-
}, meta) {
|
|
206
|
+
}, meta, collector) {
|
|
202
207
|
let viteModule = null;
|
|
203
208
|
async function getViteModule() {
|
|
204
209
|
if (viteModule != null) {
|
|
@@ -226,15 +231,15 @@ function resourcePlugin({
|
|
|
226
231
|
return _filter;
|
|
227
232
|
}
|
|
228
233
|
if (meta.framework == "webpack") {
|
|
229
|
-
debug$
|
|
234
|
+
debug$3("Using filter for webpack");
|
|
230
235
|
_filter = pluginutils.createFilter(...resolveIncludeExclude());
|
|
231
236
|
} else {
|
|
232
237
|
const viteModule2 = await getViteModule();
|
|
233
238
|
if (viteModule2.rolldownVersion) {
|
|
234
|
-
debug$
|
|
239
|
+
debug$3("Using filter for rolldown-vite");
|
|
235
240
|
_filter = pluginutils.createFilter(...resolveIncludeExclude());
|
|
236
241
|
} else {
|
|
237
|
-
debug$
|
|
242
|
+
debug$3("Using filter for rollup-vite");
|
|
238
243
|
_filter = pluginutils.createFilter(...resolveIncludeExcludeForLegacy());
|
|
239
244
|
}
|
|
240
245
|
}
|
|
@@ -246,7 +251,7 @@ function resourcePlugin({
|
|
|
246
251
|
let isProduction = false;
|
|
247
252
|
let sourceMap = false;
|
|
248
253
|
const vueI18nAliasName = module;
|
|
249
|
-
debug$
|
|
254
|
+
debug$3(`vue-i18n alias name: ${vueI18nAliasName}`);
|
|
250
255
|
let vuePlugin = null;
|
|
251
256
|
const getSfcParser = () => {
|
|
252
257
|
return vuePlugin ? getVueCompiler(vuePlugin).parse : compilerSfc.parse;
|
|
@@ -272,7 +277,7 @@ function resourcePlugin({
|
|
|
272
277
|
__VUE_I18N_PROD_DEVTOOLS__: false
|
|
273
278
|
}
|
|
274
279
|
};
|
|
275
|
-
debug$
|
|
280
|
+
debug$3("define Config:", defineConfig);
|
|
276
281
|
const aliasConfig = {
|
|
277
282
|
resolve: {
|
|
278
283
|
alias: {
|
|
@@ -283,7 +288,7 @@ function resourcePlugin({
|
|
|
283
288
|
}
|
|
284
289
|
}
|
|
285
290
|
};
|
|
286
|
-
debug$
|
|
291
|
+
debug$3("alias Config:", aliasConfig);
|
|
287
292
|
return shared.assign(defineConfig, aliasConfig);
|
|
288
293
|
},
|
|
289
294
|
async configResolved(config) {
|
|
@@ -293,9 +298,9 @@ function resourcePlugin({
|
|
|
293
298
|
}
|
|
294
299
|
isProduction = config.isProduction;
|
|
295
300
|
sourceMap = config.command === "build" ? !!config.build.sourcemap : false;
|
|
296
|
-
debug$
|
|
301
|
+
debug$3(`configResolved: isProduction = ${isProduction}, sourceMap = ${sourceMap}`);
|
|
297
302
|
const isRolldownVite = !!(await getViteModule()).rolldownVersion;
|
|
298
|
-
debug$
|
|
303
|
+
debug$3(`Using ${isRolldownVite ? "rolldown-vite" : "vite"} for the build`);
|
|
299
304
|
if (!isRolldownVite) {
|
|
300
305
|
const jsonPlugin = getVitePlugin(config, "vite:json");
|
|
301
306
|
if (jsonPlugin) {
|
|
@@ -309,7 +314,7 @@ function resourcePlugin({
|
|
|
309
314
|
if (query.vue) {
|
|
310
315
|
return;
|
|
311
316
|
}
|
|
312
|
-
debug$
|
|
317
|
+
debug$3("org json plugin");
|
|
313
318
|
return orgTransform.apply(this, [code, id]);
|
|
314
319
|
}
|
|
315
320
|
jsonPlugin.transform = overrideJson;
|
|
@@ -334,7 +339,7 @@ function resourcePlugin({
|
|
|
334
339
|
webpack(compiler) {
|
|
335
340
|
isProduction = compiler.options.mode !== "development";
|
|
336
341
|
sourceMap = !!compiler.options.devtool;
|
|
337
|
-
debug$
|
|
342
|
+
debug$3(`webpack: isProduction = ${isProduction}, sourceMap = ${sourceMap}`);
|
|
338
343
|
compiler.options.resolve = normalizeConfigResolveAlias(
|
|
339
344
|
compiler.options.resolve,
|
|
340
345
|
meta.framework
|
|
@@ -343,7 +348,7 @@ function resourcePlugin({
|
|
|
343
348
|
ssr: ssrBuild,
|
|
344
349
|
runtimeOnly
|
|
345
350
|
});
|
|
346
|
-
debug$
|
|
351
|
+
debug$3(
|
|
347
352
|
`set ${vueI18nAliasName}: ${getVueI18nAliasPath({
|
|
348
353
|
ssr: ssrBuild,
|
|
349
354
|
runtimeOnly
|
|
@@ -356,15 +361,15 @@ function resourcePlugin({
|
|
|
356
361
|
__INTLIFY_PROD_DEVTOOLS__: "false"
|
|
357
362
|
})
|
|
358
363
|
);
|
|
359
|
-
debug$
|
|
360
|
-
debug$
|
|
364
|
+
debug$3(`set __VUE_I18N_LEGACY_API__ is '${compositionOnly}'`);
|
|
365
|
+
debug$3(`set __VUE_I18N_FULL_INSTALL__ is '${fullInstall}'`);
|
|
361
366
|
const filter = pluginutils.createFilter(...resolveIncludeExclude());
|
|
362
367
|
if (compiler.options.module) {
|
|
363
368
|
compiler.options.module.rules.push({
|
|
364
369
|
test: RE_RESOURCE_FORMAT,
|
|
365
370
|
type: "javascript/auto",
|
|
366
371
|
include(resource) {
|
|
367
|
-
debug$
|
|
372
|
+
debug$3("webpack resource include", resource);
|
|
368
373
|
return filter(resource);
|
|
369
374
|
}
|
|
370
375
|
});
|
|
@@ -383,7 +388,7 @@ function resourcePlugin({
|
|
|
383
388
|
id: RE_VIRTUAL_PREFIXED_INTLIFY_BUNDLE_IMPORT_ID
|
|
384
389
|
},
|
|
385
390
|
async handler(id) {
|
|
386
|
-
debug$
|
|
391
|
+
debug$3("load", id);
|
|
387
392
|
if (INTLIFY_BUNDLE_IMPORT_ID === getVirtualId(id, meta.framework) && include) {
|
|
388
393
|
let resourcePaths = [];
|
|
389
394
|
for (const inc of include) {
|
|
@@ -393,7 +398,8 @@ function resourcePlugin({
|
|
|
393
398
|
const code = await generateBundleResources(resourcePaths, isProduction, {
|
|
394
399
|
forceStringify,
|
|
395
400
|
strictMessage,
|
|
396
|
-
escapeHtml
|
|
401
|
+
escapeHtml,
|
|
402
|
+
usedKeyFilter: collector ? (keyPath) => collector.shouldKeepKey(keyPath) : void 0
|
|
397
403
|
});
|
|
398
404
|
return {
|
|
399
405
|
moduleType: "js",
|
|
@@ -410,7 +416,7 @@ function resourcePlugin({
|
|
|
410
416
|
return;
|
|
411
417
|
}
|
|
412
418
|
const { filename, query } = parseVueRequest(id);
|
|
413
|
-
debug$
|
|
419
|
+
debug$3("transform", id, JSON.stringify(query), filename);
|
|
414
420
|
let langInfo = defaultSFCLang;
|
|
415
421
|
let inSourceMap;
|
|
416
422
|
if (!query.vue) {
|
|
@@ -429,13 +435,14 @@ function resourcePlugin({
|
|
|
429
435
|
escapeHtml,
|
|
430
436
|
jit: true,
|
|
431
437
|
onlyLocales,
|
|
432
|
-
forceStringify
|
|
438
|
+
forceStringify,
|
|
439
|
+
usedKeyFilter: collector ? (keyPath) => collector.shouldKeepKey(keyPath) : void 0
|
|
433
440
|
}
|
|
434
441
|
);
|
|
435
|
-
debug$
|
|
442
|
+
debug$3("parseOptions", parseOptions);
|
|
436
443
|
const { code: generatedCode, map } = await generate(code, parseOptions);
|
|
437
|
-
debug$
|
|
438
|
-
debug$
|
|
444
|
+
debug$3("generated code", generatedCode);
|
|
445
|
+
debug$3("sourcemap", map, sourceMap);
|
|
439
446
|
if (code === generatedCode) return;
|
|
440
447
|
return {
|
|
441
448
|
moduleType: "js",
|
|
@@ -450,8 +457,9 @@ function resourcePlugin({
|
|
|
450
457
|
} else if (defaultSFCLang) {
|
|
451
458
|
langInfo = defaultSFCLang;
|
|
452
459
|
}
|
|
453
|
-
debug$
|
|
460
|
+
debug$3("langInfo", langInfo);
|
|
454
461
|
const generate = /\.?json5?/.test(langInfo) ? bundleUtils.generateJSON : bundleUtils.generateYAML;
|
|
462
|
+
const isGlobalBlock = globalSFCScope || !!query.global;
|
|
455
463
|
const parseOptions = getOptions(
|
|
456
464
|
filename,
|
|
457
465
|
isProduction,
|
|
@@ -464,10 +472,11 @@ function resourcePlugin({
|
|
|
464
472
|
strictMessage,
|
|
465
473
|
escapeHtml,
|
|
466
474
|
onlyLocales,
|
|
467
|
-
forceStringify
|
|
475
|
+
forceStringify,
|
|
476
|
+
usedKeyFilter: collector && isGlobalBlock ? (keyPath) => collector.shouldKeepKey(keyPath) : void 0
|
|
468
477
|
}
|
|
469
478
|
);
|
|
470
|
-
debug$
|
|
479
|
+
debug$3("parseOptions", parseOptions);
|
|
471
480
|
let source = await getCode(
|
|
472
481
|
code,
|
|
473
482
|
filename,
|
|
@@ -485,8 +494,8 @@ function resourcePlugin({
|
|
|
485
494
|
}
|
|
486
495
|
}
|
|
487
496
|
const { code: generatedCode, map } = generate(source, parseOptions);
|
|
488
|
-
debug$
|
|
489
|
-
debug$
|
|
497
|
+
debug$3("generated code", generatedCode);
|
|
498
|
+
debug$3("sourcemap", map, sourceMap);
|
|
490
499
|
if (code === generatedCode) return;
|
|
491
500
|
return {
|
|
492
501
|
moduleType: "js",
|
|
@@ -542,11 +551,12 @@ async function generateBundleResources(resources, isProduction, {
|
|
|
542
551
|
strictMessage = true,
|
|
543
552
|
escapeHtml = false,
|
|
544
553
|
jit = true,
|
|
545
|
-
transformI18nBlock = void 0
|
|
554
|
+
transformI18nBlock = void 0,
|
|
555
|
+
usedKeyFilter = void 0
|
|
546
556
|
}) {
|
|
547
557
|
const codes = [];
|
|
548
558
|
for (const res of resources) {
|
|
549
|
-
debug$
|
|
559
|
+
debug$3(`${res} bundle loading ...`);
|
|
550
560
|
if (/\.(json5?|ya?ml)$/.test(res)) {
|
|
551
561
|
const { ext, name } = path.parse(res);
|
|
552
562
|
const source = await getRaw(res);
|
|
@@ -558,11 +568,12 @@ async function generateBundleResources(resources, isProduction, {
|
|
|
558
568
|
strictMessage,
|
|
559
569
|
escapeHtml,
|
|
560
570
|
forceStringify,
|
|
561
|
-
transformI18nBlock
|
|
571
|
+
transformI18nBlock,
|
|
572
|
+
usedKeyFilter
|
|
562
573
|
});
|
|
563
574
|
parseOptions.type = "bare";
|
|
564
575
|
const { code } = generate(source, parseOptions);
|
|
565
|
-
debug$
|
|
576
|
+
debug$3("generated code", code);
|
|
566
577
|
codes.push(`${JSON.stringify(name)}: ${code}`);
|
|
567
578
|
}
|
|
568
579
|
}
|
|
@@ -597,7 +608,7 @@ async function getCode(source, filename, sourceMap, query, parser, framework = "
|
|
|
597
608
|
}
|
|
598
609
|
if (framework === "webpack") {
|
|
599
610
|
if (issuerPath) {
|
|
600
|
-
debug$
|
|
611
|
+
debug$3(`getCode (webpack) ${index} via issuerPath`, issuerPath);
|
|
601
612
|
return await getRaw(filename);
|
|
602
613
|
} else {
|
|
603
614
|
const result = parser(await getRaw(filename), {
|
|
@@ -607,7 +618,7 @@ async function getCode(source, filename, sourceMap, query, parser, framework = "
|
|
|
607
618
|
const block = result.descriptor.customBlocks[index];
|
|
608
619
|
if (block) {
|
|
609
620
|
const code = block.src ? await getRaw(block.src) : block.content;
|
|
610
|
-
debug$
|
|
621
|
+
debug$3(`getCode (webpack) ${index} from SFC`, code);
|
|
611
622
|
return code;
|
|
612
623
|
} else {
|
|
613
624
|
return source;
|
|
@@ -631,7 +642,8 @@ function getOptions(filename, isProduction, query, sourceMap, {
|
|
|
631
642
|
strictMessage = true,
|
|
632
643
|
escapeHtml = false,
|
|
633
644
|
jit = true,
|
|
634
|
-
transformI18nBlock = null
|
|
645
|
+
transformI18nBlock = null,
|
|
646
|
+
usedKeyFilter = void 0
|
|
635
647
|
}) {
|
|
636
648
|
const mode = isProduction ? "production" : "development";
|
|
637
649
|
const baseOptions = {
|
|
@@ -644,6 +656,7 @@ function getOptions(filename, isProduction, query, sourceMap, {
|
|
|
644
656
|
escapeHtml,
|
|
645
657
|
jit,
|
|
646
658
|
onlyLocales,
|
|
659
|
+
usedKeyFilter,
|
|
647
660
|
env: mode,
|
|
648
661
|
transformI18nBlock,
|
|
649
662
|
onWarn: (msg) => {
|
|
@@ -688,7 +701,12 @@ async function getRaw(path) {
|
|
|
688
701
|
return node_fs.promises.readFile(path, { encoding: "utf-8" });
|
|
689
702
|
}
|
|
690
703
|
|
|
691
|
-
const debug$
|
|
704
|
+
const debug$2 = createDebug__default(resolveNamespace("directive"));
|
|
705
|
+
const tsEstree = {
|
|
706
|
+
parse: void 0,
|
|
707
|
+
simpleTraverse: void 0,
|
|
708
|
+
AST_NODE_TYPES: void 0
|
|
709
|
+
};
|
|
692
710
|
function directivePlugin({
|
|
693
711
|
optimizeTranslationDirective,
|
|
694
712
|
translationIdentifiers
|
|
@@ -700,7 +718,13 @@ function directivePlugin({
|
|
|
700
718
|
name: resolveNamespace("directive"),
|
|
701
719
|
enforce: "pre",
|
|
702
720
|
vite: {
|
|
703
|
-
config(config) {
|
|
721
|
+
async config(config) {
|
|
722
|
+
await import('@typescript-eslint/typescript-estree').then((r) => {
|
|
723
|
+
tsEstree.parse = r.parse;
|
|
724
|
+
tsEstree.simpleTraverse = r.simpleTraverse;
|
|
725
|
+
tsEstree.AST_NODE_TYPES = r.AST_NODE_TYPES;
|
|
726
|
+
return;
|
|
727
|
+
});
|
|
704
728
|
vuePlugin = getVitePlugin(config, "vite:vue");
|
|
705
729
|
if (!checkVuePlugin(vuePlugin)) {
|
|
706
730
|
return;
|
|
@@ -711,7 +735,7 @@ function directivePlugin({
|
|
|
711
735
|
optimizeTranslationDirective,
|
|
712
736
|
translationIdentifiers
|
|
713
737
|
);
|
|
714
|
-
debug$
|
|
738
|
+
debug$2(`vite:vue options['template']:`, vuePlugin.api.options);
|
|
715
739
|
}
|
|
716
740
|
},
|
|
717
741
|
configResolved(config) {
|
|
@@ -754,7 +778,7 @@ function resolveVueOptions(vuePlugin, optimizeTranslationDirective, translationI
|
|
|
754
778
|
const vuePluginOptions = getVuePluginOptions(vuePlugin);
|
|
755
779
|
const normalizedFilename = normalizePath(path__default.relative(vuePluginOptions.root, filename));
|
|
756
780
|
const resolveIdentifier2 = translationIdentifiers.get(normalizedFilename);
|
|
757
|
-
debug$
|
|
781
|
+
debug$2("resolved vue-i18n Identifier", resolveIdentifier2);
|
|
758
782
|
if (resolveIdentifier2 == null) {
|
|
759
783
|
return void 0;
|
|
760
784
|
}
|
|
@@ -772,12 +796,12 @@ function resolveVueOptions(vuePlugin, optimizeTranslationDirective, translationI
|
|
|
772
796
|
}
|
|
773
797
|
function analyzeIdentifiers(descriptor, { root }, translationIdentifiers) {
|
|
774
798
|
const source = descriptor.scriptSetup?.content || descriptor.script?.content;
|
|
775
|
-
debug$
|
|
799
|
+
debug$2("getDescriptor content", source);
|
|
776
800
|
if (!source) {
|
|
777
801
|
return;
|
|
778
802
|
}
|
|
779
|
-
const ast =
|
|
780
|
-
|
|
803
|
+
const ast = tsEstree.parse(source, { range: true });
|
|
804
|
+
tsEstree.simpleTraverse(ast, {
|
|
781
805
|
enter(node, parent) {
|
|
782
806
|
if (parent) {
|
|
783
807
|
node.parent = parent;
|
|
@@ -790,11 +814,11 @@ function analyzeIdentifiers(descriptor, { root }, translationIdentifiers) {
|
|
|
790
814
|
if (importLocalName == null) {
|
|
791
815
|
return;
|
|
792
816
|
}
|
|
793
|
-
debug$
|
|
817
|
+
debug$2("importLocalName", importLocalName);
|
|
794
818
|
const resolvedIdentifier = getVueI18nIdentifier(scope, importLocalName);
|
|
795
819
|
if (resolvedIdentifier) {
|
|
796
820
|
const normalizedFilename = normalizePath(path__default.relative(root, descriptor.filename));
|
|
797
|
-
debug$
|
|
821
|
+
debug$2("set vue-i18n resolved identifier: ", resolvedIdentifier);
|
|
798
822
|
translationIdentifiers.set(normalizedFilename, resolvedIdentifier);
|
|
799
823
|
}
|
|
800
824
|
}
|
|
@@ -842,9 +866,9 @@ function getVueI18nIdentifier(scope, local) {
|
|
|
842
866
|
return null;
|
|
843
867
|
}
|
|
844
868
|
const variableIdPairs = parseVariableId(id);
|
|
845
|
-
debug$
|
|
869
|
+
debug$2("variableIdPairs:", variableIdPairs);
|
|
846
870
|
const returnVariableIdPairs = parseReturnStatement(returnStatement);
|
|
847
|
-
debug$
|
|
871
|
+
debug$2("returnVariableIdPairs:", returnVariableIdPairs);
|
|
848
872
|
return resolveIdentifier(variableIdPairs, returnVariableIdPairs);
|
|
849
873
|
}
|
|
850
874
|
const EMPTY_NODE_RETURN = {
|
|
@@ -857,18 +881,18 @@ function getCallExpressionAndReturnStatement(scope, local) {
|
|
|
857
881
|
return EMPTY_NODE_RETURN;
|
|
858
882
|
}
|
|
859
883
|
const callExpressionRef = variable.references.find((ref) => {
|
|
860
|
-
return ref.identifier.parent?.type ===
|
|
884
|
+
return ref.identifier.parent?.type === tsEstree.AST_NODE_TYPES.CallExpression;
|
|
861
885
|
});
|
|
862
886
|
if (callExpressionRef == null) {
|
|
863
887
|
return EMPTY_NODE_RETURN;
|
|
864
888
|
}
|
|
865
889
|
let returnStatement = null;
|
|
866
|
-
if (callExpressionRef.from.type === "function" && callExpressionRef.from.block.type ===
|
|
867
|
-
callExpressionRef.from.block.parent.type ===
|
|
868
|
-
callExpressionRef.from.block.parent.key.type ===
|
|
890
|
+
if (callExpressionRef.from.type === "function" && callExpressionRef.from.block.type === tsEstree.AST_NODE_TYPES.FunctionExpression && // @ts-expect-error -- FIXME: type error
|
|
891
|
+
callExpressionRef.from.block.parent.type === tsEstree.AST_NODE_TYPES.Property && // @ts-expect-error -- FIXME: type error
|
|
892
|
+
callExpressionRef.from.block.parent.key.type === tsEstree.AST_NODE_TYPES.Identifier && // @ts-expect-error -- FIXME: type error
|
|
869
893
|
callExpressionRef.from.block.parent.key.name === "setup") {
|
|
870
894
|
returnStatement = callExpressionRef.from.block.body.body.find((statement) => {
|
|
871
|
-
return statement.type ===
|
|
895
|
+
return statement.type === tsEstree.AST_NODE_TYPES.ReturnStatement;
|
|
872
896
|
});
|
|
873
897
|
}
|
|
874
898
|
return {
|
|
@@ -878,22 +902,22 @@ function getCallExpressionAndReturnStatement(scope, local) {
|
|
|
878
902
|
};
|
|
879
903
|
}
|
|
880
904
|
function getVariableDeclarationIdFrom(node) {
|
|
881
|
-
if (node.parent?.type !==
|
|
905
|
+
if (node.parent?.type !== tsEstree.AST_NODE_TYPES.VariableDeclarator) {
|
|
882
906
|
return null;
|
|
883
907
|
}
|
|
884
908
|
return node.parent.id;
|
|
885
909
|
}
|
|
886
910
|
function parseVariableId(node) {
|
|
887
|
-
if (node.type ===
|
|
911
|
+
if (node.type === tsEstree.AST_NODE_TYPES.Identifier) {
|
|
888
912
|
return [{ key: node.name, value: null }];
|
|
889
913
|
} else {
|
|
890
914
|
const props = node.properties.filter(
|
|
891
915
|
// ignore RestElement
|
|
892
|
-
(prop) => prop.type ===
|
|
916
|
+
(prop) => prop.type === tsEstree.AST_NODE_TYPES.Property
|
|
893
917
|
);
|
|
894
918
|
const pairs = [];
|
|
895
919
|
for (const prop of props) {
|
|
896
|
-
if (prop?.key.type ===
|
|
920
|
+
if (prop?.key.type === tsEstree.AST_NODE_TYPES.Identifier && prop?.value.type === tsEstree.AST_NODE_TYPES.Identifier) {
|
|
897
921
|
pairs.push({ key: prop.key.name, value: prop.value.name });
|
|
898
922
|
}
|
|
899
923
|
}
|
|
@@ -905,12 +929,12 @@ function parseReturnStatement(node) {
|
|
|
905
929
|
if (node == null || node.argument == null) {
|
|
906
930
|
return pairs;
|
|
907
931
|
}
|
|
908
|
-
if (node.argument.type ===
|
|
932
|
+
if (node.argument.type === tsEstree.AST_NODE_TYPES.ObjectExpression) {
|
|
909
933
|
for (const prop of node.argument.properties) {
|
|
910
|
-
if (prop.type ===
|
|
911
|
-
if (prop.key.type ===
|
|
934
|
+
if (prop.type === tsEstree.AST_NODE_TYPES.Property) {
|
|
935
|
+
if (prop.key.type === tsEstree.AST_NODE_TYPES.Identifier && prop.value.type === tsEstree.AST_NODE_TYPES.Identifier) {
|
|
912
936
|
pairs.push({ key: prop.key.name, value: prop.value.name });
|
|
913
|
-
} else if (prop.key.type ===
|
|
937
|
+
} else if (prop.key.type === tsEstree.AST_NODE_TYPES.Identifier && prop.value.type === tsEstree.AST_NODE_TYPES.MemberExpression && prop.value.object.type === tsEstree.AST_NODE_TYPES.Identifier && prop.value.property.type === tsEstree.AST_NODE_TYPES.Identifier) {
|
|
914
938
|
pairs.push({
|
|
915
939
|
key: prop.key.name,
|
|
916
940
|
value: `${prop.value.object.name}.${prop.value.property.name}`
|
|
@@ -919,7 +943,7 @@ function parseReturnStatement(node) {
|
|
|
919
943
|
}
|
|
920
944
|
}
|
|
921
945
|
return pairs;
|
|
922
|
-
} else if (node.argument.type ===
|
|
946
|
+
} else if (node.argument.type === tsEstree.AST_NODE_TYPES.Identifier) {
|
|
923
947
|
return pairs;
|
|
924
948
|
} else {
|
|
925
949
|
return pairs;
|
|
@@ -956,6 +980,270 @@ function resolveIdentifier(localVariables, returnVariable) {
|
|
|
956
980
|
}
|
|
957
981
|
}
|
|
958
982
|
|
|
983
|
+
function matchSafelistPattern(keyPath, pattern) {
|
|
984
|
+
const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^.]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
985
|
+
return new RegExp(`^${regexStr}$`).test(keyPath);
|
|
986
|
+
}
|
|
987
|
+
function createUsedKeysCollector(options) {
|
|
988
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
989
|
+
const safelistPatterns = options.safelist || [];
|
|
990
|
+
const dynamicKeyStrategy = options.dynamicKeyStrategy || "keep-all";
|
|
991
|
+
const removedKeys = /* @__PURE__ */ new Map();
|
|
992
|
+
let dynamicKeysDetected = false;
|
|
993
|
+
return {
|
|
994
|
+
get usedKeys() {
|
|
995
|
+
return usedKeys;
|
|
996
|
+
},
|
|
997
|
+
get dynamicKeysDetected() {
|
|
998
|
+
return dynamicKeysDetected;
|
|
999
|
+
},
|
|
1000
|
+
set dynamicKeysDetected(val) {
|
|
1001
|
+
dynamicKeysDetected = val;
|
|
1002
|
+
},
|
|
1003
|
+
safelistPatterns,
|
|
1004
|
+
removedKeys,
|
|
1005
|
+
addKey(key) {
|
|
1006
|
+
usedKeys.add(key);
|
|
1007
|
+
},
|
|
1008
|
+
markDynamic() {
|
|
1009
|
+
dynamicKeysDetected = true;
|
|
1010
|
+
},
|
|
1011
|
+
shouldKeepKey(keyPath) {
|
|
1012
|
+
if (dynamicKeysDetected && dynamicKeyStrategy === "keep-all") {
|
|
1013
|
+
return true;
|
|
1014
|
+
}
|
|
1015
|
+
if (safelistPatterns.some((pattern) => matchSafelistPattern(keyPath, pattern))) {
|
|
1016
|
+
return true;
|
|
1017
|
+
}
|
|
1018
|
+
if (usedKeys.has(keyPath)) {
|
|
1019
|
+
return true;
|
|
1020
|
+
}
|
|
1021
|
+
for (const key of usedKeys) {
|
|
1022
|
+
if (key.startsWith(keyPath + ".")) {
|
|
1023
|
+
return true;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return false;
|
|
1027
|
+
},
|
|
1028
|
+
reportRemoved(filename, keyPath) {
|
|
1029
|
+
const existing = removedKeys.get(filename);
|
|
1030
|
+
if (existing) {
|
|
1031
|
+
existing.push(keyPath);
|
|
1032
|
+
} else {
|
|
1033
|
+
removedKeys.set(filename, [keyPath]);
|
|
1034
|
+
}
|
|
1035
|
+
},
|
|
1036
|
+
getDiagnostics() {
|
|
1037
|
+
let totalRemoved = 0;
|
|
1038
|
+
for (const keys of removedKeys.values()) {
|
|
1039
|
+
totalRemoved += keys.length;
|
|
1040
|
+
}
|
|
1041
|
+
return { totalRemoved, byFile: removedKeys };
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
const I18N_FUNCTIONS = /* @__PURE__ */ new Set(["t", "$t", "tc", "$tc", "te", "$te", "d", "$d", "n", "$n"]);
|
|
1047
|
+
const TEMPLATE_T_REGEX = /\$?t\s*\(\s*['"]([^'"]+)['"]/g;
|
|
1048
|
+
const VT_DIRECTIVE_STRING_REGEX = /v-t\s*=\s*"'([^']+)'"/g;
|
|
1049
|
+
const VT_DIRECTIVE_PATH_REGEX = /v-t\s*=\s*"\{[^}]*path\s*:\s*'([^']+)'/g;
|
|
1050
|
+
const TEMPLATE_DYNAMIC_T_REGEX = /\$?t\s*\(\s*[a-zA-Z_$]/g;
|
|
1051
|
+
async function extractKeysFromScript(source) {
|
|
1052
|
+
const keys = [];
|
|
1053
|
+
let hasDynamic = false;
|
|
1054
|
+
let tsEstree;
|
|
1055
|
+
try {
|
|
1056
|
+
tsEstree = await import('@typescript-eslint/typescript-estree');
|
|
1057
|
+
} catch {
|
|
1058
|
+
return extractKeysFromScriptRegex(source);
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
const ast = tsEstree.parse(source, {
|
|
1062
|
+
range: true,
|
|
1063
|
+
jsx: true,
|
|
1064
|
+
allowInvalidAST: true,
|
|
1065
|
+
suppressDeprecatedPropertyWarnings: true
|
|
1066
|
+
});
|
|
1067
|
+
tsEstree.simpleTraverse(ast, {
|
|
1068
|
+
enter(node) {
|
|
1069
|
+
if (node.type !== tsEstree.AST_NODE_TYPES.CallExpression) {
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
const calleeName = getCalleeName(node, tsEstree.AST_NODE_TYPES);
|
|
1073
|
+
if (!calleeName || !I18N_FUNCTIONS.has(calleeName)) {
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
const firstArg = node.arguments[0];
|
|
1077
|
+
if (!firstArg) {
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
if (firstArg.type === tsEstree.AST_NODE_TYPES.Literal && typeof firstArg.value === "string") {
|
|
1081
|
+
keys.push(firstArg.value);
|
|
1082
|
+
} else if (firstArg.type === tsEstree.AST_NODE_TYPES.TemplateLiteral && firstArg.expressions.length === 0 && firstArg.quasis.length === 1) {
|
|
1083
|
+
const value = firstArg.quasis[0].value.cooked;
|
|
1084
|
+
if (value) {
|
|
1085
|
+
keys.push(value);
|
|
1086
|
+
}
|
|
1087
|
+
} else {
|
|
1088
|
+
hasDynamic = true;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
} catch {
|
|
1093
|
+
return extractKeysFromScriptRegex(source);
|
|
1094
|
+
}
|
|
1095
|
+
return { keys, hasDynamic };
|
|
1096
|
+
}
|
|
1097
|
+
function getCalleeName(node, AST_NODE_TYPES) {
|
|
1098
|
+
const callee = node.callee;
|
|
1099
|
+
if (callee.type === AST_NODE_TYPES.Identifier) {
|
|
1100
|
+
return callee.name;
|
|
1101
|
+
}
|
|
1102
|
+
if (callee.type === AST_NODE_TYPES.MemberExpression && callee.property.type === AST_NODE_TYPES.Identifier) {
|
|
1103
|
+
return callee.property.name;
|
|
1104
|
+
}
|
|
1105
|
+
return null;
|
|
1106
|
+
}
|
|
1107
|
+
function extractKeysFromScriptRegex(source) {
|
|
1108
|
+
const keys = [];
|
|
1109
|
+
let match;
|
|
1110
|
+
const regex = /\b(?:\$?t|tc|\$tc|te|\$te|d|\$d|n|\$n)\s*\(\s*['"]([^'"]+)['"]/g;
|
|
1111
|
+
while ((match = regex.exec(source)) !== null) {
|
|
1112
|
+
keys.push(match[1]);
|
|
1113
|
+
}
|
|
1114
|
+
return { keys, hasDynamic: false };
|
|
1115
|
+
}
|
|
1116
|
+
function extractKeysFromTemplate(templateContent) {
|
|
1117
|
+
const keys = [];
|
|
1118
|
+
let hasDynamic = false;
|
|
1119
|
+
let match;
|
|
1120
|
+
TEMPLATE_T_REGEX.lastIndex = 0;
|
|
1121
|
+
VT_DIRECTIVE_STRING_REGEX.lastIndex = 0;
|
|
1122
|
+
VT_DIRECTIVE_PATH_REGEX.lastIndex = 0;
|
|
1123
|
+
TEMPLATE_DYNAMIC_T_REGEX.lastIndex = 0;
|
|
1124
|
+
while ((match = TEMPLATE_T_REGEX.exec(templateContent)) !== null) {
|
|
1125
|
+
keys.push(match[1]);
|
|
1126
|
+
}
|
|
1127
|
+
while ((match = VT_DIRECTIVE_STRING_REGEX.exec(templateContent)) !== null) {
|
|
1128
|
+
keys.push(match[1]);
|
|
1129
|
+
}
|
|
1130
|
+
while ((match = VT_DIRECTIVE_PATH_REGEX.exec(templateContent)) !== null) {
|
|
1131
|
+
keys.push(match[1]);
|
|
1132
|
+
}
|
|
1133
|
+
if (TEMPLATE_DYNAMIC_T_REGEX.test(templateContent)) {
|
|
1134
|
+
hasDynamic = true;
|
|
1135
|
+
}
|
|
1136
|
+
return { keys, hasDynamic };
|
|
1137
|
+
}
|
|
1138
|
+
async function analyzeVueSFC(content) {
|
|
1139
|
+
const keys = [];
|
|
1140
|
+
let hasDynamic = false;
|
|
1141
|
+
let parse;
|
|
1142
|
+
try {
|
|
1143
|
+
const compilerSfc = await import('vue/compiler-sfc');
|
|
1144
|
+
parse = compilerSfc.parse;
|
|
1145
|
+
} catch {
|
|
1146
|
+
const templateResult = extractKeysFromTemplate(content);
|
|
1147
|
+
const scriptResult = await extractKeysFromScript(content);
|
|
1148
|
+
return {
|
|
1149
|
+
keys: [...templateResult.keys, ...scriptResult.keys],
|
|
1150
|
+
hasDynamic: templateResult.hasDynamic || scriptResult.hasDynamic
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
const { descriptor } = parse(content);
|
|
1154
|
+
if (descriptor.template?.content) {
|
|
1155
|
+
const templateResult = extractKeysFromTemplate(descriptor.template.content);
|
|
1156
|
+
keys.push(...templateResult.keys);
|
|
1157
|
+
if (templateResult.hasDynamic) {
|
|
1158
|
+
hasDynamic = true;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
const scriptContent = descriptor.scriptSetup?.content || descriptor.script?.content;
|
|
1162
|
+
if (scriptContent) {
|
|
1163
|
+
const scriptResult = await extractKeysFromScript(scriptContent);
|
|
1164
|
+
keys.push(...scriptResult.keys);
|
|
1165
|
+
if (scriptResult.hasDynamic) {
|
|
1166
|
+
hasDynamic = true;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return { keys, hasDynamic };
|
|
1170
|
+
}
|
|
1171
|
+
async function analyzeFile(content, filePath, collector) {
|
|
1172
|
+
let result;
|
|
1173
|
+
if (filePath.endsWith(".vue")) {
|
|
1174
|
+
result = await analyzeVueSFC(content);
|
|
1175
|
+
} else {
|
|
1176
|
+
result = await extractKeysFromScript(content);
|
|
1177
|
+
}
|
|
1178
|
+
for (const key of result.keys) {
|
|
1179
|
+
collector.addKey(key);
|
|
1180
|
+
}
|
|
1181
|
+
if (result.hasDynamic) {
|
|
1182
|
+
collector.markDynamic();
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
const debug$1 = createDebug__default(resolveNamespace("tree-shaking"));
|
|
1187
|
+
function treeShakingPlugin(resolvedOptions, collector) {
|
|
1188
|
+
let projectRoot = "";
|
|
1189
|
+
return {
|
|
1190
|
+
name: resolveNamespace("tree-shaking"),
|
|
1191
|
+
enforce: "pre",
|
|
1192
|
+
vite: {
|
|
1193
|
+
configResolved(config) {
|
|
1194
|
+
projectRoot = config.root;
|
|
1195
|
+
}
|
|
1196
|
+
},
|
|
1197
|
+
webpack(compiler) {
|
|
1198
|
+
projectRoot = compiler.options.context || process.cwd();
|
|
1199
|
+
},
|
|
1200
|
+
async buildStart() {
|
|
1201
|
+
const treeShaking = resolvedOptions.treeShaking;
|
|
1202
|
+
const patterns = treeShaking?.scanPatterns || [`${projectRoot}/src/**/*.{vue,ts,js,tsx,jsx}`];
|
|
1203
|
+
debug$1("scanning patterns:", patterns);
|
|
1204
|
+
const files = await fg__default(patterns, {
|
|
1205
|
+
ignore: ["**/node_modules/**"],
|
|
1206
|
+
absolute: true
|
|
1207
|
+
});
|
|
1208
|
+
debug$1(`found ${files.length} source files to scan`);
|
|
1209
|
+
for (const file of files) {
|
|
1210
|
+
try {
|
|
1211
|
+
const content = await node_fs.promises.readFile(file, "utf-8");
|
|
1212
|
+
await analyzeFile(content, file, collector);
|
|
1213
|
+
} catch (err) {
|
|
1214
|
+
debug$1(`failed to analyze file: ${file}`, err);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
debug$1(
|
|
1218
|
+
`scan complete: ${collector.usedKeys.size} used keys found, dynamic: ${collector.dynamicKeysDetected}`
|
|
1219
|
+
);
|
|
1220
|
+
if (collector.dynamicKeysDetected) {
|
|
1221
|
+
const strategy = treeShaking?.dynamicKeyStrategy || "keep-all";
|
|
1222
|
+
if (strategy === "keep-all") {
|
|
1223
|
+
warn(
|
|
1224
|
+
"Tree-shaking: dynamic key usage detected. All keys will be preserved (dynamicKeyStrategy: keep-all)."
|
|
1225
|
+
);
|
|
1226
|
+
} else {
|
|
1227
|
+
warn(
|
|
1228
|
+
"Tree-shaking: dynamic key usage detected. Unused keys will still be removed (dynamicKeyStrategy: ignore)."
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
},
|
|
1233
|
+
buildEnd() {
|
|
1234
|
+
const diagnostics = collector.getDiagnostics();
|
|
1235
|
+
if (diagnostics.totalRemoved > 0) {
|
|
1236
|
+
debug$1(`tree-shaking: removed ${diagnostics.totalRemoved} unused message keys`);
|
|
1237
|
+
for (const [file, keys] of diagnostics.byFile) {
|
|
1238
|
+
debug$1(` ${file}: removed ${keys.length} keys (${keys.join(", ")})`);
|
|
1239
|
+
}
|
|
1240
|
+
} else {
|
|
1241
|
+
debug$1("tree-shaking: no keys removed");
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
|
|
959
1247
|
const debug = createDebug__default(resolveNamespace("root"));
|
|
960
1248
|
const unpluginFactory = (options = {}, meta) => {
|
|
961
1249
|
debug("meta framework", meta.framework);
|
|
@@ -965,7 +1253,12 @@ const unpluginFactory = (options = {}, meta) => {
|
|
|
965
1253
|
debug("plugin options (resolving):", options);
|
|
966
1254
|
const resolvedOptions = resolveOptions(options);
|
|
967
1255
|
debug("plugin options (resolved):", resolvedOptions);
|
|
968
|
-
const
|
|
1256
|
+
const collector = resolvedOptions.treeShaking ? createUsedKeysCollector(resolvedOptions.treeShaking) : null;
|
|
1257
|
+
const plugins = [];
|
|
1258
|
+
if (resolvedOptions.treeShaking && collector) {
|
|
1259
|
+
plugins.push(treeShakingPlugin(resolvedOptions, collector));
|
|
1260
|
+
}
|
|
1261
|
+
plugins.push(resourcePlugin(resolvedOptions, meta, collector));
|
|
969
1262
|
if (resolvedOptions.optimizeTranslationDirective) {
|
|
970
1263
|
if (meta.framework === "webpack") {
|
|
971
1264
|
raiseError(
|