@intlify/unplugin-vue-i18n 11.2.5 → 11.2.7

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 CHANGED
@@ -238,6 +238,18 @@ export default defineConfig({
238
238
 
239
239
  unplugin-vue-i18n will automatically merge locale files into `@intlify/unplugin-vue-i18n/messages`. This allows locales to be split across multiple files, for example `src/locales/fruits/en.json` and `src/locales/vegetables/en.json`.
240
240
 
241
+ > [!NOTE]
242
+ > On v11, `@intlify/unplugin-vue-i18n/messages` bundles JSON and YAML resources only. JS / TS resources under `include` are left out of it, and the plugin warns about them. v12 bundles JS / TS resources too.
243
+
244
+ To load JS / TS resources at once on v11, import them directly, or collect them with `import.meta.glob`:
245
+
246
+ ```ts
247
+ const modules = import.meta.glob('./src/locales/*.{ts,js}', { eager: true, import: 'default' })
248
+ const messages = Object.fromEntries(
249
+ Object.entries(modules).map(([path, m]) => [path.replace(/^.*\/|\.[jt]s$/g, ''), m])
250
+ )
251
+ ```
252
+
241
253
  ### Types
242
254
 
243
255
  If you want type definition of `@intlify/unplugin-vue-i18n/messages`, add `unplugin-vue-i18n/messages` to `compilerOptions.types` of your tsconfig:
package/lib/index.cjs CHANGED
@@ -8,22 +8,19 @@ const shared = require('@intlify/shared');
8
8
  const pathe = require('pathe');
9
9
  const bundleUtils = require('@intlify/bundle-utils');
10
10
  const pluginutils = require('@rollup/pluginutils');
11
- const fg = require('fast-glob');
12
11
  const node_fs = require('node:fs');
12
+ const tinyglobby = require('tinyglobby');
13
13
  const compilerSfc = require('vue/compiler-sfc');
14
14
  const pc = require('picocolors');
15
15
  const path = require('node:path');
16
- const eslintUitls = require('@eslint-community/eslint-utils');
17
16
  const vueI18nExtensions = require('@intlify/vue-i18n-extensions');
18
17
  const scopeManager = require('@typescript-eslint/scope-manager');
19
18
 
20
19
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
21
20
 
22
21
  const createDebug__default = /*#__PURE__*/_interopDefaultCompat(createDebug);
23
- const fg__default = /*#__PURE__*/_interopDefaultCompat(fg);
24
22
  const pc__default = /*#__PURE__*/_interopDefaultCompat(pc);
25
23
  const path__default = /*#__PURE__*/_interopDefaultCompat(path);
26
- const eslintUitls__default = /*#__PURE__*/_interopDefaultCompat(eslintUitls);
27
24
 
28
25
  function normalizeGlobOption(val) {
29
26
  if (!val) return void 0;
@@ -181,7 +178,6 @@ const VIRTUAL_PREFIX = "virtual:";
181
178
  const RE_RESOURCE_FORMAT = /\.(json5?|ya?ml|[c|m]?[j|t]s)$/;
182
179
  const RE_SFC_I18N_CUSTOM_BLOCK = /\?vue&type=i18n/;
183
180
  const RE_SFC_I18N_WEBPACK_CUSTOM_BLOCK = /blockType=i18n/;
184
- const VITE_VIRTUAL_PREFIX = "virtual:intlify-i18n-";
185
181
  const debug$3 = createDebug__default(resolveNamespace("resource"));
186
182
  function resourcePlugin({
187
183
  onlyLocales,
@@ -207,7 +203,7 @@ function resourcePlugin({
207
203
  }
208
204
  function resolveIncludeExcludeForLegacy() {
209
205
  const customBlockInclude = meta.framework === "vite" ? RE_SFC_I18N_CUSTOM_BLOCK : RE_SFC_I18N_WEBPACK_CUSTOM_BLOCK;
210
- return include ? [[...include, customBlockInclude], void 0] : [void 0, ["**/**"]];
206
+ return include ? [[...include, customBlockInclude], exclude] : [void 0, ["**/**"]];
211
207
  }
212
208
  let _filter = null;
213
209
  async function getFilter() {
@@ -232,28 +228,13 @@ function resourcePlugin({
232
228
  let isProduction = false;
233
229
  let sourceMap = false;
234
230
  let hasViteJsonPlugin = false;
231
+ let warnedScriptResources = false;
235
232
  const vueI18nAliasName = module;
236
233
  debug$3(`vue-i18n alias name: ${vueI18nAliasName}`);
237
234
  let vuePlugin = null;
238
235
  const getSfcParser = () => {
239
236
  return vuePlugin ? getVueCompiler(vuePlugin).parse : compilerSfc.parse;
240
237
  };
241
- const virtualIdToRealPath = /* @__PURE__ */ new Map();
242
- const realPathToVirtualId = /* @__PURE__ */ new Map();
243
- let virtualCounter = 0;
244
- function intlifyVirtualize(realPath) {
245
- const normalized = pathe.normalize(realPath);
246
- let virtualId = realPathToVirtualId.get(normalized);
247
- if (!virtualId) {
248
- virtualId = `${VITE_VIRTUAL_PREFIX}${virtualCounter++}`;
249
- virtualIdToRealPath.set(virtualId, normalized);
250
- realPathToVirtualId.set(normalized, virtualId);
251
- }
252
- return virtualId;
253
- }
254
- function isIntlifyVirtualId(id) {
255
- return virtualIdToRealPath.has(id);
256
- }
257
238
  return {
258
239
  name: resolveNamespace("resource"),
259
240
  /**
@@ -324,18 +305,12 @@ function resourcePlugin({
324
305
  },
325
306
  handleHotUpdate({ file, modules, server }) {
326
307
  const key = pathe.normalize(file);
327
- const virtualId = realPathToVirtualId.get(key);
328
- const inBundle = !!(include && pluginutils.createFilter(include, exclude)(key));
329
- if (!virtualId && !inBundle) return;
308
+ if (!(include && pluginutils.createFilter(include, exclude)(key))) return;
330
309
  const affected = new Set(modules);
331
- const addById = (id) => {
332
- const mod = id && server.moduleGraph.getModuleById(id);
333
- if (mod) affected.add(mod);
334
- };
335
- addById(virtualId);
336
- if (inBundle) {
337
- addById(asVirtualId(INTLIFY_BUNDLE_IMPORT_ID, meta.framework));
338
- }
310
+ const mod = server.moduleGraph.getModuleById(
311
+ asVirtualId(INTLIFY_BUNDLE_IMPORT_ID, meta.framework)
312
+ );
313
+ if (mod) affected.add(mod);
339
314
  if (affected.size === modules.length) return;
340
315
  return [...affected];
341
316
  }
@@ -380,153 +355,77 @@ function resourcePlugin({
380
355
  }
381
356
  },
382
357
  resolveId: {
383
- async handler(id, importer) {
358
+ async handler(id) {
384
359
  if (id === INTLIFY_BUNDLE_IMPORT_ID) {
385
360
  return asVirtualId(id, meta.framework);
386
361
  }
387
- if (meta.framework === "vite" && !hasViteJsonPlugin) {
388
- if (id.includes("?vue&type=i18n") && /[?&]lang\.(?:json|json5)(?:$|&)/.test(id)) {
389
- const { filename, query } = parseVueRequest(id);
390
- let virtualKey = id;
391
- if (query.src && importer) {
392
- const importerPath = (isIntlifyVirtualId(importer) ? virtualIdToRealPath.get(importer) : importer).split("?")[0];
393
- if (importerPath.endsWith(".vue")) {
394
- const resolvedSidecar = pathe.resolve(pathe.dirname(importerPath), filename);
395
- virtualKey = id.replace(filename, resolvedSidecar);
396
- }
397
- }
398
- return intlifyVirtualize(virtualKey);
399
- }
400
- const idPath = id.split("?")[0];
401
- if (!RE_RESOURCE_FORMAT.test(idPath)) return;
402
- let resolvedPath;
403
- if (idPath.startsWith(".")) {
404
- const realImporter = importer && isIntlifyVirtualId(importer) ? virtualIdToRealPath.get(importer) : importer;
405
- if (!realImporter) return;
406
- resolvedPath = pathe.resolve(pathe.dirname(realImporter), idPath);
407
- } else if (idPath.startsWith("/") || /^[a-z]:[/\\]/i.test(idPath)) {
408
- resolvedPath = idPath;
409
- } else {
410
- return;
411
- }
412
- const filter = await getFilter();
413
- if (!filter(resolvedPath)) return;
414
- return intlifyVirtualize(resolvedPath);
415
- }
416
362
  }
417
363
  },
364
+ loadInclude(id) {
365
+ return INTLIFY_BUNDLE_IMPORT_ID === getVirtualId(id, meta.framework);
366
+ },
418
367
  load: {
419
368
  async handler(id) {
420
369
  debug$3("load", id);
421
370
  if (INTLIFY_BUNDLE_IMPORT_ID === getVirtualId(id, meta.framework) && include) {
422
371
  let resourcePaths = [];
423
372
  for (const inc of include) {
424
- resourcePaths = [...resourcePaths, ...await fg__default(inc)];
373
+ resourcePaths = [
374
+ ...resourcePaths,
375
+ ...await tinyglobby.glob(inc, {
376
+ cwd: process.cwd(),
377
+ expandDirectories: false,
378
+ absolute: true,
379
+ ignore: exclude
380
+ })
381
+ ];
425
382
  }
426
383
  resourcePaths = resourcePaths.filter((el, pos) => resourcePaths.indexOf(el) === pos);
384
+ if (!warnedScriptResources) {
385
+ const scripts = resourcePaths.filter((path) => /\.[cm]?[jt]s$/.test(path)).sort();
386
+ if (scripts.length > 0) {
387
+ warnedScriptResources = true;
388
+ const listed = scripts.slice(0, 10).map((path) => pathe.relative(process.cwd(), path));
389
+ const more = scripts.length > listed.length ? `, and ${scripts.length - listed.length} more` : "";
390
+ warn(
391
+ `JS / TS files under \`include\` are not bundled into \`${INTLIFY_BUNDLE_IMPORT_ID}\` (v11 bundles JSON / YAML only): ${listed.join(", ")}${more}. Import them directly, or collect them with \`import.meta.glob\`.`
392
+ );
393
+ }
394
+ }
395
+ for (const path of resourcePaths) {
396
+ if (/\.(?:json5?|ya?ml)$/.test(path)) {
397
+ this.addWatchFile(path);
398
+ }
399
+ }
427
400
  const code = await generateBundleResources(resourcePaths, isProduction, {
428
401
  forceStringify,
429
402
  strictMessage,
430
403
  escapeHtml,
431
404
  usedKeyFilter: collector ? (keyPath) => collector.shouldKeepKey(keyPath) : void 0
432
405
  });
433
- return {
434
- moduleType: "js",
435
- code,
436
- map: { mappings: "" }
437
- };
438
- }
439
- if (isIntlifyVirtualId(id)) {
440
- const realId = virtualIdToRealPath.get(id);
441
- if (realId.includes("?vue&type=i18n")) {
442
- const { filename, query } = parseVueRequest(realId);
443
- let source;
444
- if (query.src) {
445
- this.addWatchFile(filename);
446
- source = await node_fs.promises.readFile(filename, "utf-8");
447
- } else {
448
- this.addWatchFile(filename);
449
- const sfcSource = await node_fs.promises.readFile(filename, "utf-8");
450
- const { descriptor } = getSfcParser()(sfcSource, {
451
- sourceMap,
452
- filename
453
- });
454
- const block = descriptor.customBlocks[query.index];
455
- if (!block) return;
456
- source = block.content;
457
- }
458
- if (typeof transformI18nBlock === "function") {
459
- const modifiedSource = transformI18nBlock(source);
460
- if (modifiedSource && typeof modifiedSource === "string") {
461
- source = modifiedSource;
462
- } else {
463
- warn("transformI18nBlock should return a string");
464
- }
465
- }
466
- let langInfo2 = defaultSFCLang;
467
- if (shared.isString(query.lang)) {
468
- langInfo2 = query.src ? query.lang === "i18n" ? defaultSFCLang : query.lang : query.lang;
469
- }
470
- if (/\.?[cm]?[jt]s$/.test(langInfo2)) {
471
- source = `export default ${source.replace(/^[\s;]+/, "")}`;
472
- }
473
- const generate2 = getGenerator(langInfo2, bundleUtils.generateYAML);
474
- const isGlobalBlock = globalSFCScope || !!query.global;
475
- const parseOptions2 = getOptions(
476
- filename,
477
- isProduction,
478
- query,
479
- sourceMap,
480
- {
481
- isGlobal: globalSFCScope,
482
- allowDynamic,
483
- jit: true,
484
- strictMessage,
485
- escapeHtml,
486
- onlyLocales,
487
- forceStringify,
488
- usedKeyFilter: collector && isGlobalBlock ? (keyPath) => collector.shouldKeepKey(keyPath) : void 0
489
- }
490
- );
491
- const { code: generatedCode2 } = await generate2(source, parseOptions2);
492
- return {
493
- moduleType: "js",
494
- code: generatedCode2,
495
- map: { mappings: "" }
496
- };
406
+ if (meta.framework !== "vite") {
407
+ return { code };
497
408
  }
498
- this.addWatchFile(realId);
499
- const code = await node_fs.promises.readFile(realId, "utf-8");
500
- const langInfo = pathe.parse(realId).ext;
501
- const generate = getGenerator(langInfo);
502
- const parseOptions = getOptions(
503
- realId,
504
- isProduction,
505
- {},
506
- sourceMap,
507
- {
508
- isGlobal: globalSFCScope,
509
- allowDynamic,
510
- strictMessage,
511
- escapeHtml,
512
- jit: true,
513
- onlyLocales,
514
- forceStringify,
515
- usedKeyFilter: collector ? (keyPath) => collector.shouldKeepKey(keyPath) : void 0
516
- }
517
- );
518
- const { code: generatedCode } = await generate(code, parseOptions);
519
409
  return {
520
410
  moduleType: "js",
521
- code: generatedCode,
411
+ code,
522
412
  map: { mappings: "" }
523
413
  };
524
414
  }
525
415
  }
526
416
  },
417
+ // webpack puts the transform loader on every module this lets through. A loader in the chain
418
+ // stops html-webpack-plugin from rendering its template, and turns binary assets into UTF-8
419
+ // text, so let through only what `transform` handles (the same filter as the one in it)
420
+ transformInclude(id) {
421
+ if (meta.framework !== "webpack") {
422
+ return true;
423
+ }
424
+ _filter ??= pluginutils.createFilter(...resolveIncludeExclude());
425
+ return _filter(id);
426
+ },
527
427
  transform: {
528
428
  async handler(code, id) {
529
- if (isIntlifyVirtualId(id)) return;
530
429
  const filter = await getFilter();
531
430
  if (!filter(id)) {
532
431
  return;
@@ -535,7 +434,7 @@ function resourcePlugin({
535
434
  debug$3("transform", id, JSON.stringify(query), filename);
536
435
  let langInfo = defaultSFCLang;
537
436
  let inSourceMap;
538
- if (!query.vue) {
437
+ if (!query.vue && RE_RESOURCE_FORMAT.test(id)) {
539
438
  langInfo = pathe.parse(filename).ext;
540
439
  const generate = getGenerator(langInfo);
541
440
  const parseOptions = getOptions(
@@ -610,10 +509,7 @@ function resourcePlugin({
610
509
  warn("transformI18nBlock should return a string");
611
510
  }
612
511
  }
613
- if (/\.?[cm]?[jt]s$/.test(langInfo)) {
614
- source = `export default ${source.replace(/^[\s;]+/, "")}`;
615
- }
616
- const { code: generatedCode, map } = await generate(source, parseOptions);
512
+ const { code: generatedCode, map } = /\.?[cm]?[jt]s$/.test(langInfo) ? await generateJsTsBlock(generate, source, parseOptions) : await generate(source, parseOptions);
617
513
  debug$3("generated code", generatedCode);
618
514
  debug$3("sourcemap", map, sourceMap);
619
515
  if (code === generatedCode) return;
@@ -644,6 +540,17 @@ function getGenerator(ext, fallback = bundleUtils.generateJSON) {
644
540
  }
645
541
  return fallback;
646
542
  }
543
+ async function generateJsTsBlock(generate, source, options) {
544
+ try {
545
+ return await generate(source, options);
546
+ } catch (error2) {
547
+ try {
548
+ return await generate(`export default ${source.replace(/^[\s;]+/, "")}`, options);
549
+ } catch (wrappedError) {
550
+ throw /^\s*export\s+default\b/m.test(source) ? error2 : wrappedError;
551
+ }
552
+ }
553
+ }
647
554
  function normalizeConfigResolveAlias(resolve2, framework) {
648
555
  if (resolve2 && resolve2.alias) {
649
556
  return resolve2;
@@ -969,15 +876,20 @@ function getImportLocalName(scope, source, imported) {
969
876
  return null;
970
877
  }
971
878
  function getImportDeclaration(scope, source) {
972
- const tracker = new eslintUitls__default.ReferenceTracker(scope);
973
- const traceMap = {
974
- [source]: {
975
- [eslintUitls__default.ReferenceTracker.ESM]: true,
976
- [eslintUitls__default.ReferenceTracker.READ]: true
879
+ const program = scope.block;
880
+ const importDecl = program.body.find(
881
+ (node) => node.type === tsEstree.AST_NODE_TYPES.ImportDeclaration && node.source.value === source
882
+ );
883
+ return importDecl ? importDecl : null;
884
+ }
885
+ function findVariable(scope, name) {
886
+ for (let current = scope; current != null; current = current.upper) {
887
+ const variable = current.set.get(name);
888
+ if (variable != null) {
889
+ return variable;
977
890
  }
978
- };
979
- const refs = Array.from(tracker.iterateEsmReferences(traceMap));
980
- return refs.length ? refs[0].node : null;
891
+ }
892
+ return null;
981
893
  }
982
894
  function isImportedIdentifierInImportClause(node) {
983
895
  return "imported" in node;
@@ -1002,7 +914,7 @@ const EMPTY_NODE_RETURN = {
1002
914
  returnStatement: null
1003
915
  };
1004
916
  function getCallExpressionAndReturnStatement(scope, local) {
1005
- const variable = eslintUitls__default.findVariable(scope, local);
917
+ const variable = findVariable(scope, local);
1006
918
  if (variable == null) {
1007
919
  return EMPTY_NODE_RETURN;
1008
920
  }
@@ -1013,16 +925,12 @@ function getCallExpressionAndReturnStatement(scope, local) {
1013
925
  return EMPTY_NODE_RETURN;
1014
926
  }
1015
927
  let returnStatement = null;
1016
- if (callExpressionRef.from.type === "function" && callExpressionRef.from.block.type === tsEstree.AST_NODE_TYPES.FunctionExpression && // @ts-expect-error -- FIXME: type error
1017
- callExpressionRef.from.block.parent.type === tsEstree.AST_NODE_TYPES.Property && // @ts-expect-error -- FIXME: type error
1018
- callExpressionRef.from.block.parent.key.type === tsEstree.AST_NODE_TYPES.Identifier && // @ts-expect-error -- FIXME: type error
1019
- callExpressionRef.from.block.parent.key.name === "setup") {
928
+ if (callExpressionRef.from.type === "function" && callExpressionRef.from.block.type === tsEstree.AST_NODE_TYPES.FunctionExpression && callExpressionRef.from.block.parent.type === tsEstree.AST_NODE_TYPES.Property && callExpressionRef.from.block.parent.key.type === tsEstree.AST_NODE_TYPES.Identifier && callExpressionRef.from.block.parent.key.name === "setup") {
1020
929
  returnStatement = callExpressionRef.from.block.body.body.find((statement) => {
1021
930
  return statement.type === tsEstree.AST_NODE_TYPES.ReturnStatement;
1022
931
  });
1023
932
  }
1024
933
  return {
1025
- // @ts-expect-error -- FIXME: type error
1026
934
  callExpression: callExpressionRef.identifier.parent,
1027
935
  returnStatement
1028
936
  };
@@ -1327,8 +1235,10 @@ function treeShakingPlugin(resolvedOptions, collector) {
1327
1235
  const treeShaking = resolvedOptions.treeShaking;
1328
1236
  const patterns = treeShaking?.scanPatterns || [`${projectRoot}/src/**/*.{vue,ts,js,tsx,jsx}`];
1329
1237
  debug$1("scanning patterns:", patterns);
1330
- const files = await fg__default(patterns, {
1238
+ const files = await tinyglobby.glob(patterns, {
1239
+ cwd: process.cwd(),
1331
1240
  ignore: ["**/node_modules/**"],
1241
+ expandDirectories: false,
1332
1242
  absolute: true
1333
1243
  });
1334
1244
  debug$1(`found ${files.length} source files to scan`);
package/lib/index.mjs CHANGED
@@ -1,15 +1,14 @@
1
1
  import createDebug from 'debug';
2
2
  import { createUnplugin } from 'unplugin';
3
3
  import { isArray, isString, isBoolean, assign, isEmptyObject, isNumber, generateCodeFrame } from '@intlify/shared';
4
- import { normalize, parse, resolve, dirname } from 'pathe';
4
+ import { normalize, parse, relative, resolve, dirname } from 'pathe';
5
5
  import { generateJSON, generateYAML, generateJavaScript, generateTypescript } from '@intlify/bundle-utils';
6
6
  import { createFilter } from '@rollup/pluginutils';
7
- import fg from 'fast-glob';
8
7
  import { promises } from 'node:fs';
8
+ import { glob } from 'tinyglobby';
9
9
  import { parse as parse$1 } from 'vue/compiler-sfc';
10
10
  import pc from 'picocolors';
11
11
  import path from 'node:path';
12
- import eslintUitls from '@eslint-community/eslint-utils';
13
12
  import { transformVTDirective } from '@intlify/vue-i18n-extensions';
14
13
  import { analyze } from '@typescript-eslint/scope-manager';
15
14
 
@@ -169,7 +168,6 @@ const VIRTUAL_PREFIX = "virtual:";
169
168
  const RE_RESOURCE_FORMAT = /\.(json5?|ya?ml|[c|m]?[j|t]s)$/;
170
169
  const RE_SFC_I18N_CUSTOM_BLOCK = /\?vue&type=i18n/;
171
170
  const RE_SFC_I18N_WEBPACK_CUSTOM_BLOCK = /blockType=i18n/;
172
- const VITE_VIRTUAL_PREFIX = "virtual:intlify-i18n-";
173
171
  const debug$3 = createDebug(resolveNamespace("resource"));
174
172
  function resourcePlugin({
175
173
  onlyLocales,
@@ -195,7 +193,7 @@ function resourcePlugin({
195
193
  }
196
194
  function resolveIncludeExcludeForLegacy() {
197
195
  const customBlockInclude = meta.framework === "vite" ? RE_SFC_I18N_CUSTOM_BLOCK : RE_SFC_I18N_WEBPACK_CUSTOM_BLOCK;
198
- return include ? [[...include, customBlockInclude], void 0] : [void 0, ["**/**"]];
196
+ return include ? [[...include, customBlockInclude], exclude] : [void 0, ["**/**"]];
199
197
  }
200
198
  let _filter = null;
201
199
  async function getFilter() {
@@ -220,28 +218,13 @@ function resourcePlugin({
220
218
  let isProduction = false;
221
219
  let sourceMap = false;
222
220
  let hasViteJsonPlugin = false;
221
+ let warnedScriptResources = false;
223
222
  const vueI18nAliasName = module;
224
223
  debug$3(`vue-i18n alias name: ${vueI18nAliasName}`);
225
224
  let vuePlugin = null;
226
225
  const getSfcParser = () => {
227
226
  return vuePlugin ? getVueCompiler(vuePlugin).parse : parse$1;
228
227
  };
229
- const virtualIdToRealPath = /* @__PURE__ */ new Map();
230
- const realPathToVirtualId = /* @__PURE__ */ new Map();
231
- let virtualCounter = 0;
232
- function intlifyVirtualize(realPath) {
233
- const normalized = normalize(realPath);
234
- let virtualId = realPathToVirtualId.get(normalized);
235
- if (!virtualId) {
236
- virtualId = `${VITE_VIRTUAL_PREFIX}${virtualCounter++}`;
237
- virtualIdToRealPath.set(virtualId, normalized);
238
- realPathToVirtualId.set(normalized, virtualId);
239
- }
240
- return virtualId;
241
- }
242
- function isIntlifyVirtualId(id) {
243
- return virtualIdToRealPath.has(id);
244
- }
245
228
  return {
246
229
  name: resolveNamespace("resource"),
247
230
  /**
@@ -312,18 +295,12 @@ function resourcePlugin({
312
295
  },
313
296
  handleHotUpdate({ file, modules, server }) {
314
297
  const key = normalize(file);
315
- const virtualId = realPathToVirtualId.get(key);
316
- const inBundle = !!(include && createFilter(include, exclude)(key));
317
- if (!virtualId && !inBundle) return;
298
+ if (!(include && createFilter(include, exclude)(key))) return;
318
299
  const affected = new Set(modules);
319
- const addById = (id) => {
320
- const mod = id && server.moduleGraph.getModuleById(id);
321
- if (mod) affected.add(mod);
322
- };
323
- addById(virtualId);
324
- if (inBundle) {
325
- addById(asVirtualId(INTLIFY_BUNDLE_IMPORT_ID, meta.framework));
326
- }
300
+ const mod = server.moduleGraph.getModuleById(
301
+ asVirtualId(INTLIFY_BUNDLE_IMPORT_ID, meta.framework)
302
+ );
303
+ if (mod) affected.add(mod);
327
304
  if (affected.size === modules.length) return;
328
305
  return [...affected];
329
306
  }
@@ -368,153 +345,77 @@ function resourcePlugin({
368
345
  }
369
346
  },
370
347
  resolveId: {
371
- async handler(id, importer) {
348
+ async handler(id) {
372
349
  if (id === INTLIFY_BUNDLE_IMPORT_ID) {
373
350
  return asVirtualId(id, meta.framework);
374
351
  }
375
- if (meta.framework === "vite" && !hasViteJsonPlugin) {
376
- if (id.includes("?vue&type=i18n") && /[?&]lang\.(?:json|json5)(?:$|&)/.test(id)) {
377
- const { filename, query } = parseVueRequest(id);
378
- let virtualKey = id;
379
- if (query.src && importer) {
380
- const importerPath = (isIntlifyVirtualId(importer) ? virtualIdToRealPath.get(importer) : importer).split("?")[0];
381
- if (importerPath.endsWith(".vue")) {
382
- const resolvedSidecar = resolve(dirname(importerPath), filename);
383
- virtualKey = id.replace(filename, resolvedSidecar);
384
- }
385
- }
386
- return intlifyVirtualize(virtualKey);
387
- }
388
- const idPath = id.split("?")[0];
389
- if (!RE_RESOURCE_FORMAT.test(idPath)) return;
390
- let resolvedPath;
391
- if (idPath.startsWith(".")) {
392
- const realImporter = importer && isIntlifyVirtualId(importer) ? virtualIdToRealPath.get(importer) : importer;
393
- if (!realImporter) return;
394
- resolvedPath = resolve(dirname(realImporter), idPath);
395
- } else if (idPath.startsWith("/") || /^[a-z]:[/\\]/i.test(idPath)) {
396
- resolvedPath = idPath;
397
- } else {
398
- return;
399
- }
400
- const filter = await getFilter();
401
- if (!filter(resolvedPath)) return;
402
- return intlifyVirtualize(resolvedPath);
403
- }
404
352
  }
405
353
  },
354
+ loadInclude(id) {
355
+ return INTLIFY_BUNDLE_IMPORT_ID === getVirtualId(id, meta.framework);
356
+ },
406
357
  load: {
407
358
  async handler(id) {
408
359
  debug$3("load", id);
409
360
  if (INTLIFY_BUNDLE_IMPORT_ID === getVirtualId(id, meta.framework) && include) {
410
361
  let resourcePaths = [];
411
362
  for (const inc of include) {
412
- resourcePaths = [...resourcePaths, ...await fg(inc)];
363
+ resourcePaths = [
364
+ ...resourcePaths,
365
+ ...await glob(inc, {
366
+ cwd: process.cwd(),
367
+ expandDirectories: false,
368
+ absolute: true,
369
+ ignore: exclude
370
+ })
371
+ ];
413
372
  }
414
373
  resourcePaths = resourcePaths.filter((el, pos) => resourcePaths.indexOf(el) === pos);
374
+ if (!warnedScriptResources) {
375
+ const scripts = resourcePaths.filter((path) => /\.[cm]?[jt]s$/.test(path)).sort();
376
+ if (scripts.length > 0) {
377
+ warnedScriptResources = true;
378
+ const listed = scripts.slice(0, 10).map((path) => relative(process.cwd(), path));
379
+ const more = scripts.length > listed.length ? `, and ${scripts.length - listed.length} more` : "";
380
+ warn(
381
+ `JS / TS files under \`include\` are not bundled into \`${INTLIFY_BUNDLE_IMPORT_ID}\` (v11 bundles JSON / YAML only): ${listed.join(", ")}${more}. Import them directly, or collect them with \`import.meta.glob\`.`
382
+ );
383
+ }
384
+ }
385
+ for (const path of resourcePaths) {
386
+ if (/\.(?:json5?|ya?ml)$/.test(path)) {
387
+ this.addWatchFile(path);
388
+ }
389
+ }
415
390
  const code = await generateBundleResources(resourcePaths, isProduction, {
416
391
  forceStringify,
417
392
  strictMessage,
418
393
  escapeHtml,
419
394
  usedKeyFilter: collector ? (keyPath) => collector.shouldKeepKey(keyPath) : void 0
420
395
  });
421
- return {
422
- moduleType: "js",
423
- code,
424
- map: { mappings: "" }
425
- };
426
- }
427
- if (isIntlifyVirtualId(id)) {
428
- const realId = virtualIdToRealPath.get(id);
429
- if (realId.includes("?vue&type=i18n")) {
430
- const { filename, query } = parseVueRequest(realId);
431
- let source;
432
- if (query.src) {
433
- this.addWatchFile(filename);
434
- source = await promises.readFile(filename, "utf-8");
435
- } else {
436
- this.addWatchFile(filename);
437
- const sfcSource = await promises.readFile(filename, "utf-8");
438
- const { descriptor } = getSfcParser()(sfcSource, {
439
- sourceMap,
440
- filename
441
- });
442
- const block = descriptor.customBlocks[query.index];
443
- if (!block) return;
444
- source = block.content;
445
- }
446
- if (typeof transformI18nBlock === "function") {
447
- const modifiedSource = transformI18nBlock(source);
448
- if (modifiedSource && typeof modifiedSource === "string") {
449
- source = modifiedSource;
450
- } else {
451
- warn("transformI18nBlock should return a string");
452
- }
453
- }
454
- let langInfo2 = defaultSFCLang;
455
- if (isString(query.lang)) {
456
- langInfo2 = query.src ? query.lang === "i18n" ? defaultSFCLang : query.lang : query.lang;
457
- }
458
- if (/\.?[cm]?[jt]s$/.test(langInfo2)) {
459
- source = `export default ${source.replace(/^[\s;]+/, "")}`;
460
- }
461
- const generate2 = getGenerator(langInfo2, generateYAML);
462
- const isGlobalBlock = globalSFCScope || !!query.global;
463
- const parseOptions2 = getOptions(
464
- filename,
465
- isProduction,
466
- query,
467
- sourceMap,
468
- {
469
- isGlobal: globalSFCScope,
470
- allowDynamic,
471
- jit: true,
472
- strictMessage,
473
- escapeHtml,
474
- onlyLocales,
475
- forceStringify,
476
- usedKeyFilter: collector && isGlobalBlock ? (keyPath) => collector.shouldKeepKey(keyPath) : void 0
477
- }
478
- );
479
- const { code: generatedCode2 } = await generate2(source, parseOptions2);
480
- return {
481
- moduleType: "js",
482
- code: generatedCode2,
483
- map: { mappings: "" }
484
- };
396
+ if (meta.framework !== "vite") {
397
+ return { code };
485
398
  }
486
- this.addWatchFile(realId);
487
- const code = await promises.readFile(realId, "utf-8");
488
- const langInfo = parse(realId).ext;
489
- const generate = getGenerator(langInfo);
490
- const parseOptions = getOptions(
491
- realId,
492
- isProduction,
493
- {},
494
- sourceMap,
495
- {
496
- isGlobal: globalSFCScope,
497
- allowDynamic,
498
- strictMessage,
499
- escapeHtml,
500
- jit: true,
501
- onlyLocales,
502
- forceStringify,
503
- usedKeyFilter: collector ? (keyPath) => collector.shouldKeepKey(keyPath) : void 0
504
- }
505
- );
506
- const { code: generatedCode } = await generate(code, parseOptions);
507
399
  return {
508
400
  moduleType: "js",
509
- code: generatedCode,
401
+ code,
510
402
  map: { mappings: "" }
511
403
  };
512
404
  }
513
405
  }
514
406
  },
407
+ // webpack puts the transform loader on every module this lets through. A loader in the chain
408
+ // stops html-webpack-plugin from rendering its template, and turns binary assets into UTF-8
409
+ // text, so let through only what `transform` handles (the same filter as the one in it)
410
+ transformInclude(id) {
411
+ if (meta.framework !== "webpack") {
412
+ return true;
413
+ }
414
+ _filter ??= createFilter(...resolveIncludeExclude());
415
+ return _filter(id);
416
+ },
515
417
  transform: {
516
418
  async handler(code, id) {
517
- if (isIntlifyVirtualId(id)) return;
518
419
  const filter = await getFilter();
519
420
  if (!filter(id)) {
520
421
  return;
@@ -523,7 +424,7 @@ function resourcePlugin({
523
424
  debug$3("transform", id, JSON.stringify(query), filename);
524
425
  let langInfo = defaultSFCLang;
525
426
  let inSourceMap;
526
- if (!query.vue) {
427
+ if (!query.vue && RE_RESOURCE_FORMAT.test(id)) {
527
428
  langInfo = parse(filename).ext;
528
429
  const generate = getGenerator(langInfo);
529
430
  const parseOptions = getOptions(
@@ -598,10 +499,7 @@ function resourcePlugin({
598
499
  warn("transformI18nBlock should return a string");
599
500
  }
600
501
  }
601
- if (/\.?[cm]?[jt]s$/.test(langInfo)) {
602
- source = `export default ${source.replace(/^[\s;]+/, "")}`;
603
- }
604
- const { code: generatedCode, map } = await generate(source, parseOptions);
502
+ const { code: generatedCode, map } = /\.?[cm]?[jt]s$/.test(langInfo) ? await generateJsTsBlock(generate, source, parseOptions) : await generate(source, parseOptions);
605
503
  debug$3("generated code", generatedCode);
606
504
  debug$3("sourcemap", map, sourceMap);
607
505
  if (code === generatedCode) return;
@@ -632,6 +530,17 @@ function getGenerator(ext, fallback = generateJSON) {
632
530
  }
633
531
  return fallback;
634
532
  }
533
+ async function generateJsTsBlock(generate, source, options) {
534
+ try {
535
+ return await generate(source, options);
536
+ } catch (error2) {
537
+ try {
538
+ return await generate(`export default ${source.replace(/^[\s;]+/, "")}`, options);
539
+ } catch (wrappedError) {
540
+ throw /^\s*export\s+default\b/m.test(source) ? error2 : wrappedError;
541
+ }
542
+ }
543
+ }
635
544
  function normalizeConfigResolveAlias(resolve2, framework) {
636
545
  if (resolve2 && resolve2.alias) {
637
546
  return resolve2;
@@ -957,15 +866,20 @@ function getImportLocalName(scope, source, imported) {
957
866
  return null;
958
867
  }
959
868
  function getImportDeclaration(scope, source) {
960
- const tracker = new eslintUitls.ReferenceTracker(scope);
961
- const traceMap = {
962
- [source]: {
963
- [eslintUitls.ReferenceTracker.ESM]: true,
964
- [eslintUitls.ReferenceTracker.READ]: true
869
+ const program = scope.block;
870
+ const importDecl = program.body.find(
871
+ (node) => node.type === tsEstree.AST_NODE_TYPES.ImportDeclaration && node.source.value === source
872
+ );
873
+ return importDecl ? importDecl : null;
874
+ }
875
+ function findVariable(scope, name) {
876
+ for (let current = scope; current != null; current = current.upper) {
877
+ const variable = current.set.get(name);
878
+ if (variable != null) {
879
+ return variable;
965
880
  }
966
- };
967
- const refs = Array.from(tracker.iterateEsmReferences(traceMap));
968
- return refs.length ? refs[0].node : null;
881
+ }
882
+ return null;
969
883
  }
970
884
  function isImportedIdentifierInImportClause(node) {
971
885
  return "imported" in node;
@@ -990,7 +904,7 @@ const EMPTY_NODE_RETURN = {
990
904
  returnStatement: null
991
905
  };
992
906
  function getCallExpressionAndReturnStatement(scope, local) {
993
- const variable = eslintUitls.findVariable(scope, local);
907
+ const variable = findVariable(scope, local);
994
908
  if (variable == null) {
995
909
  return EMPTY_NODE_RETURN;
996
910
  }
@@ -1001,16 +915,12 @@ function getCallExpressionAndReturnStatement(scope, local) {
1001
915
  return EMPTY_NODE_RETURN;
1002
916
  }
1003
917
  let returnStatement = null;
1004
- if (callExpressionRef.from.type === "function" && callExpressionRef.from.block.type === tsEstree.AST_NODE_TYPES.FunctionExpression && // @ts-expect-error -- FIXME: type error
1005
- callExpressionRef.from.block.parent.type === tsEstree.AST_NODE_TYPES.Property && // @ts-expect-error -- FIXME: type error
1006
- callExpressionRef.from.block.parent.key.type === tsEstree.AST_NODE_TYPES.Identifier && // @ts-expect-error -- FIXME: type error
1007
- callExpressionRef.from.block.parent.key.name === "setup") {
918
+ if (callExpressionRef.from.type === "function" && callExpressionRef.from.block.type === tsEstree.AST_NODE_TYPES.FunctionExpression && callExpressionRef.from.block.parent.type === tsEstree.AST_NODE_TYPES.Property && callExpressionRef.from.block.parent.key.type === tsEstree.AST_NODE_TYPES.Identifier && callExpressionRef.from.block.parent.key.name === "setup") {
1008
919
  returnStatement = callExpressionRef.from.block.body.body.find((statement) => {
1009
920
  return statement.type === tsEstree.AST_NODE_TYPES.ReturnStatement;
1010
921
  });
1011
922
  }
1012
923
  return {
1013
- // @ts-expect-error -- FIXME: type error
1014
924
  callExpression: callExpressionRef.identifier.parent,
1015
925
  returnStatement
1016
926
  };
@@ -1315,8 +1225,10 @@ function treeShakingPlugin(resolvedOptions, collector) {
1315
1225
  const treeShaking = resolvedOptions.treeShaking;
1316
1226
  const patterns = treeShaking?.scanPatterns || [`${projectRoot}/src/**/*.{vue,ts,js,tsx,jsx}`];
1317
1227
  debug$1("scanning patterns:", patterns);
1318
- const files = await fg(patterns, {
1228
+ const files = await glob(patterns, {
1229
+ cwd: process.cwd(),
1319
1230
  ignore: ["**/node_modules/**"],
1231
+ expandDirectories: false,
1320
1232
  absolute: true
1321
1233
  });
1322
1234
  debug$1(`found ${files.length} source files to scan`);
package/lib/vite.cjs CHANGED
@@ -7,12 +7,11 @@ require('@intlify/shared');
7
7
  require('pathe');
8
8
  require('@intlify/bundle-utils');
9
9
  require('@rollup/pluginutils');
10
- require('fast-glob');
11
10
  require('node:fs');
11
+ require('tinyglobby');
12
12
  require('vue/compiler-sfc');
13
13
  require('picocolors');
14
14
  require('node:path');
15
- require('@eslint-community/eslint-utils');
16
15
  require('@intlify/vue-i18n-extensions');
17
16
  require('@typescript-eslint/scope-manager');
18
17
 
package/lib/vite.mjs CHANGED
@@ -5,12 +5,11 @@ import '@intlify/shared';
5
5
  import 'pathe';
6
6
  import '@intlify/bundle-utils';
7
7
  import '@rollup/pluginutils';
8
- import 'fast-glob';
9
8
  import 'node:fs';
9
+ import 'tinyglobby';
10
10
  import 'vue/compiler-sfc';
11
11
  import 'picocolors';
12
12
  import 'node:path';
13
- import '@eslint-community/eslint-utils';
14
13
  import '@intlify/vue-i18n-extensions';
15
14
  import '@typescript-eslint/scope-manager';
16
15
 
package/lib/webpack.cjs CHANGED
@@ -7,12 +7,11 @@ require('@intlify/shared');
7
7
  require('pathe');
8
8
  require('@intlify/bundle-utils');
9
9
  require('@rollup/pluginutils');
10
- require('fast-glob');
11
10
  require('node:fs');
11
+ require('tinyglobby');
12
12
  require('vue/compiler-sfc');
13
13
  require('picocolors');
14
14
  require('node:path');
15
- require('@eslint-community/eslint-utils');
16
15
  require('@intlify/vue-i18n-extensions');
17
16
  require('@typescript-eslint/scope-manager');
18
17
 
package/lib/webpack.mjs CHANGED
@@ -5,12 +5,11 @@ import '@intlify/shared';
5
5
  import 'pathe';
6
6
  import '@intlify/bundle-utils';
7
7
  import '@rollup/pluginutils';
8
- import 'fast-glob';
9
8
  import 'node:fs';
9
+ import 'tinyglobby';
10
10
  import 'vue/compiler-sfc';
11
11
  import 'picocolors';
12
12
  import 'node:path';
13
- import '@eslint-community/eslint-utils';
14
13
  import '@intlify/vue-i18n-extensions';
15
14
  import '@typescript-eslint/scope-manager';
16
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlify/unplugin-vue-i18n",
3
- "version": "11.2.5",
3
+ "version": "11.2.7",
4
4
  "description": "unplugin for Vue I18n",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
@@ -27,19 +27,18 @@
27
27
  }
28
28
  },
29
29
  "dependencies": {
30
- "@eslint-community/eslint-utils": "^4.4.0",
31
- "@intlify/shared": "~11.4.2",
30
+ "@intlify/shared": "~11.4.15",
32
31
  "@intlify/vue-i18n-extensions": "^8.0.0",
33
32
  "@rollup/pluginutils": "^5.1.0",
34
33
  "@typescript-eslint/scope-manager": "^8.13.0",
35
34
  "@typescript-eslint/typescript-estree": "^8.13.0",
36
35
  "debug": "^4.3.3",
37
36
  "pathe": "^2.0.3",
38
- "fast-glob": "^3.2.12",
39
37
  "picocolors": "^1.0.0",
38
+ "tinyglobby": "^0.2.15",
40
39
  "unplugin": "^2.3.4",
41
40
  "vue": "~3.5.34",
42
- "@intlify/bundle-utils": "11.2.5"
41
+ "@intlify/bundle-utils": "11.2.7"
43
42
  },
44
43
  "devDependencies": {
45
44
  "@intlify/core-base": "next",
@@ -55,6 +54,7 @@
55
54
  "unbuild": "^3.6.1",
56
55
  "vite": "^8.0.0",
57
56
  "rollup-vite": "npm:vite@6.3.5",
57
+ "vite7": "npm:vite@^7.0.0",
58
58
  "vue-loader": "~17.4.2",
59
59
  "webpack": "~5.106.2",
60
60
  "webpack-merge": "^5.9.0"