@intlify/unplugin-vue-i18n 0.2.1 → 0.5.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/CHANGELOG.md CHANGED
@@ -1,4 +1,41 @@
1
1
 
2
+ ## @intlify/unplugin-vue-i18n@0.5.0 (2022-07-26)
3
+
4
+ #### :star: Features
5
+ * [#156](https://github.com/intlify/bundle-tools/pull/156) feat(unplugin-vue-i18n): support petite-vue-i18n and useVueI18nImportName option ([@kazupon](https://github.com/kazupon))
6
+
7
+ #### :zap: Improvement Features
8
+ * [#155](https://github.com/intlify/bundle-tools/pull/155) fix(unplugin-vue-i18n): improve wrong virtual module implementation ([@kazupon](https://github.com/kazupon))
9
+ * [#154](https://github.com/intlify/bundle-tools/pull/154) fix(unplugin-vue-i18n): support unplugin v0.8 ([@kazupon](https://github.com/kazupon))
10
+
11
+ #### Committers: 1
12
+ - kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
13
+
14
+
15
+ ## @intlify/unplugin-vue-i18n@0.4.0 (2022-07-07)
16
+
17
+ #### :star: Features
18
+ * [#138](https://github.com/intlify/bundle-tools/pull/138) feat(unplugin-vue-i18n): support esm options for bridge ([@kazupon](https://github.com/kazupon))
19
+
20
+ #### Committers: 1
21
+ - kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
22
+
23
+
24
+ ## @intlify/unplugin-vue-i18n@0.3.0 (2022-07-07)
25
+
26
+ #### :star: Features
27
+ * [#136](https://github.com/intlify/bundle-tools/pull/136) feat(unplugin-vue-i18n): support fullinstall option ([@kazupon](https://github.com/kazupon))
28
+ * [#135](https://github.com/intlify/bundle-tools/pull/135) feat(unplugin-vue-i18n): support composition only option ([@kazupon](https://github.com/kazupon))
29
+ * [#134](https://github.com/intlify/bundle-tools/pull/134) feat(unplugin-vue-i18n): support runtimeOnly option ([@kazupon](https://github.com/kazupon))
30
+
31
+ #### :pencil: Documentation
32
+ * [#133](https://github.com/intlify/bundle-tools/pull/133) docs: consistent plugin name and add Nuxt ([@Atinux](https://github.com/Atinux))
33
+
34
+ #### Committers: 2
35
+ - Sébastien Chopin ([@Atinux](https://github.com/Atinux))
36
+ - kazuya kawaguchi ([@kazupon](https://github.com/kazupon))
37
+
38
+
2
39
  ## @intlify/unplugin-vue-i18n@0.2.1 (2022-05-07)
3
40
 
4
41
  #### :bug: Bug Fixes
package/README.md CHANGED
@@ -28,11 +28,11 @@ npm i @intlify/unplugin-vue-i18n
28
28
 
29
29
  ```ts
30
30
  // vite.config.ts
31
- import vueI18n from '@intlify/unplugin-vue-i18n/vite'
31
+ import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
32
32
 
33
33
  export default defineConfig({
34
34
  plugins: [
35
- vueI18n({ /* options */ }),
35
+ VueI18nPlugin({ /* options */ }),
36
36
  ],
37
37
  })
38
38
  ```
@@ -56,6 +56,32 @@ module.exports = {
56
56
 
57
57
  <br></details>
58
58
 
59
+ <details>
60
+ <summary>Nuxt</summary><br>
61
+
62
+ ```ts
63
+ // nuxt.config.ts
64
+ import { defineNuxtConfig } from 'nuxt'
65
+ import VueI18nPlugin from '@intlify/unplugin-vue-i18n'
66
+
67
+ export default defineNuxtConfig({
68
+ vite: {
69
+ plugins: [
70
+ VueI18nPlugin.vite({ /* options */ }),
71
+ ],
72
+ },
73
+ // When using Webpack
74
+ // builder: '@nuxt/webpack-builder',
75
+ webpack: {
76
+ plugins: [
77
+ VueI18nPlugin.webpack({ /* options */ }),
78
+ ]
79
+ }
80
+ })
81
+ ```
82
+
83
+ <br></details>
84
+
59
85
  ## 🚀 Usage
60
86
 
61
87
  ### i18n resources pre-compilation
@@ -199,6 +225,26 @@ If you want type definition of `@intlify/unplugin-vue-i18n/messages`, add `unplu
199
225
  }
200
226
  ```
201
227
 
228
+
229
+ ## 📦 Automatic bundling
230
+ ### For Vue I18n
231
+ As noted [here](https://vue-i18n.intlify.dev/installation.html#explanation-of-different-builds), NPM provides many different builds of Vue I18n.
232
+
233
+ This plugin will automatically select and bundle Vue I18n build according to the following behavior:
234
+
235
+ - development: `vue-i18n.esm-bundler.js`
236
+ - production: `vue-i18n.runtime.esm-bundler.js`
237
+
238
+ About details, See the [here](https://vue-i18n.intlify.dev/guide/advanced/optimization.html#improve-performance-and-reduce-bundle-size-with-runtime-build-only)
239
+
240
+ ### For `petite-vue-i18n`
241
+
242
+ This plugin will automatically select and bundle `petite-vue-i18n` build according to the following vite behavior:
243
+
244
+ - vite dev: `petite-vue-i18n.esm-bundler.js`
245
+ - vite build: `petite-vue-i18n.runtime.esm-bundler.js`
246
+
247
+
202
248
  ## 🔧 Options
203
249
 
204
250
  ### `include`
@@ -217,6 +263,42 @@ If you want type definition of `@intlify/unplugin-vue-i18n/messages`, add `unplu
217
263
 
218
264
  Note `json` resources matches this option, it will be handled **before the internal json plugin of bundler, and will not be processed afterwards**, else the option doesn't match, the bundler side will handle.
219
265
 
266
+ ### `runtimeOnly`
267
+
268
+ - **Type:** `boolean`
269
+ - **Default:** `true`
270
+
271
+ Whether or not to automatically use Vue I18n **runtime-only** in production build, set `vue-i18n.runtime.esm-bundler.js` in the `vue-i18n` field of bundler config, the below:
272
+
273
+ ```
274
+ - vite config: `resolve.alias`
275
+ - webpack config: `resolve.alias`
276
+ ```
277
+
278
+ If `false` is specified, Vue I18n (vue-i18n) package.json `module` field will be used.
279
+
280
+ For more details, See [here](#-automatic-bundling)
281
+
282
+ ### `compositionOnly`
283
+
284
+ - **Type:** `boolean`
285
+ - **Default:** `true`
286
+
287
+ Whether to make vue-i18n API only composition API. **By default the legacy API is tree-shaken.**
288
+
289
+ For more details, See [here](https://vue-i18n.intlify.dev/guide/advanced/optimization.html#reduce-bundle-size-with-feature-build-flags)
290
+
291
+ ### `fullInstall`
292
+
293
+ - **Type:** `boolean`
294
+ - **Default:** `true`
295
+
296
+ Whether to install the full set of APIs, components, etc. provided by Vue I18n. By default, all of them will be installed.
297
+
298
+ If `false` is specified, **buld-in components and directive will not be installed in vue and will be tree-shaken.**
299
+
300
+ For more details, See [here](https://vue-i18n.intlify.dev/guide/advanced/optimization.html#reduce-bundle-size-with-feature-build-flags)
301
+
220
302
  ### `forceStringify`
221
303
 
222
304
  - **Type:** `boolean`
@@ -339,6 +421,13 @@ If you want type definition of `@intlify/unplugin-vue-i18n/messages`, add `unplu
339
421
 
340
422
  > ⚠️ Note that if you set `bridge: true`, the bundle size will increase. It is recommended to disable this mode after the migration from vue-i18n@v8.26 to vue-i18n@v9.x is completed.
341
423
 
424
+ ### `esm`
425
+
426
+ - **Type:** `boolean`
427
+ - **Default:** `true`
428
+
429
+ For `bridge` option is `true`, whether to bundle locale resources with ESM. By default ESM, if you need to bundl with commonjs for especialy webpack, you need to set `false`
430
+
342
431
  ### `useClassComponent`
343
432
 
344
433
  - **Type:** `boolean`
@@ -346,10 +435,15 @@ If you want type definition of `@intlify/unplugin-vue-i18n/messages`, add `unplu
346
435
 
347
436
  This option that to use i18n custom blocks in `vue-class-component`.
348
437
 
438
+ ### `useVueI18nImportName` (Experimental)
439
+
440
+ - **Type:** `boolean`
441
+ - **Default:** `false`
442
+
443
+ Whether to use the import name of `petite-vue-i18n` with the same import name as vue-i18n (`import { xxx } from 'vue-i18n'`).
444
+
445
+ This option allows a smooth migration from `petite-vue-i18n` to `vue-i18n` and allows progressive enhacement.
349
446
 
350
- ## ✅ TODO
351
- - [ ] Bundling optimizations
352
- - [ ] Support nuxt
353
447
 
354
448
  ## 📜 Changelog
355
449
 
package/lib/index.cjs CHANGED
@@ -13,6 +13,7 @@ const compilerSfc = require('@vue/compiler-sfc');
13
13
  const JSON5 = require('json5');
14
14
  const yaml = require('js-yaml');
15
15
  const fs = require('fs');
16
+ const pc = require('picocolors');
16
17
 
17
18
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
18
19
 
@@ -20,6 +21,7 @@ const createDebug__default = /*#__PURE__*/_interopDefaultLegacy(createDebug);
20
21
  const fg__default = /*#__PURE__*/_interopDefaultLegacy(fg);
21
22
  const JSON5__default = /*#__PURE__*/_interopDefaultLegacy(JSON5);
22
23
  const yaml__default = /*#__PURE__*/_interopDefaultLegacy(yaml);
24
+ const pc__default = /*#__PURE__*/_interopDefaultLegacy(pc);
23
25
 
24
26
  function parseVueRequest(id) {
25
27
  const [filename, rawQuery] = id.split(`?`, 2);
@@ -81,10 +83,10 @@ function convert(source, lang) {
81
83
  }
82
84
 
83
85
  function warn(...args) {
84
- console.warn(`[unplugin-vue-i18n] `, ...args);
86
+ console.warn(pc__default.yellow(pc__default.bold(`[unplugin-vue-i18n] `)), ...args);
85
87
  }
86
88
  function error(...args) {
87
- console.error(`[unplugin-vue-i18n] `, ...args);
89
+ console.error(pc__default.red(pc__default.bold(`[unplugin-vue-i18n] `)), ...args);
88
90
  }
89
91
  async function getRaw(path) {
90
92
  return fs.promises.readFile(path, { encoding: "utf-8" });
@@ -95,7 +97,9 @@ function raiseError(message) {
95
97
 
96
98
  const INTLIFY_BUNDLE_IMPORT_ID = "@intlify/unplugin-vue-i18n/messages";
97
99
  const INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID = "@intlify/vite-plugin-vue-i18n/messages";
100
+ const VIRTUAL_PREFIX = "\0";
98
101
  const debug = createDebug__default("unplugin-vue-i18n");
102
+ const installedPkg = bundleUtils.checkInstallPackage("@intlify/unplugin-vue-i18n", debug);
99
103
  const unplugin = unplugin$1.createUnplugin((options = {}, meta) => {
100
104
  debug("plugin options:", options, meta.framework);
101
105
  if (!["vite", "webpack"].includes(meta.framework)) {
@@ -116,21 +120,58 @@ const unplugin = unplugin$1.createUnplugin((options = {}, meta) => {
116
120
  const useClassComponent = !!options.useClassComponent;
117
121
  const bridge = !!options.bridge;
118
122
  debug("bridge", bridge);
123
+ const runtimeOnly = shared.isBoolean(options.runtimeOnly) ? options.runtimeOnly : true;
124
+ debug("runtimeOnly", runtimeOnly);
125
+ const compositionOnly = installedPkg === "vue-i18n" ? shared.isBoolean(options.compositionOnly) ? options.compositionOnly : true : true;
126
+ debug("compositionOnly", compositionOnly);
127
+ const fullInstall = installedPkg === "vue-i18n" ? shared.isBoolean(options.fullInstall) ? options.fullInstall : true : false;
128
+ debug("fullInstall", fullInstall);
129
+ const useVueI18nImportName = options.useVueI18nImportName;
130
+ if (useVueI18nImportName != null) {
131
+ warn(`'useVueI18nImportName' option is experimental`);
132
+ }
133
+ debug("useVueI18nImportName", useVueI18nImportName);
134
+ const getAliasName = () => installedPkg === "petite-vue-i18n" && shared.isBoolean(useVueI18nImportName) && useVueI18nImportName ? "vue-i18n" : `${installedPkg}`;
135
+ const esm = shared.isBoolean(options.esm) ? options.esm : true;
136
+ debug("esm", esm);
119
137
  let isProduction = false;
120
138
  let sourceMap = false;
121
139
  return {
122
140
  name: "unplugin-vue-i18n",
123
141
  enforce: meta.framework === "vite" ? "pre" : "post",
124
- transformInclude(id) {
125
- debug("transformInclude", id);
126
- if (meta.framework === "vite") {
127
- return true;
128
- } else {
129
- const { filename } = parseVueRequest(id);
130
- return filename.endsWith("vue") || filename.endsWith(INTLIFY_BUNDLE_IMPORT_ID) || filename.endsWith(INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID) ? true : /\.(json5?|ya?ml)$/.test(id) && filter(id);
131
- }
132
- },
133
142
  vite: {
143
+ config(config, { command }) {
144
+ normalizeConfigResolveAlias(config.resolve, meta.framework);
145
+ if (command === "build" && runtimeOnly) {
146
+ if (shared.isArray(config.resolve.alias)) {
147
+ config.resolve.alias.push({
148
+ find: getAliasName(),
149
+ replacement: `${installedPkg}/dist/${installedPkg}.runtime.esm-bundler.js`
150
+ });
151
+ } else if (shared.isObject(config.resolve.alias)) {
152
+ config.resolve.alias[getAliasName()] = `${installedPkg}/dist/${installedPkg}.runtime.esm-bundler.js`;
153
+ }
154
+ debug(`alias name: ${getAliasName()}`);
155
+ debug(`set ${installedPkg} runtime only: ${installedPkg}/dist/${installedPkg}.runtime.esm-bundler.js`);
156
+ } else if (command === "serve" && installedPkg === "petite-vue-i18n" && useVueI18nImportName) {
157
+ normalizeConfigResolveAlias(config.resolve, meta.framework);
158
+ if (shared.isArray(config.resolve.alias)) {
159
+ config.resolve.alias.push({
160
+ find: "vue-i18n",
161
+ replacement: `petite-vue-i18n/dist/petite-vue-i18n.esm-bundler.js`
162
+ });
163
+ } else {
164
+ config.resolve.alias["vue-i18n"] = `petite-vue-i18n/dist/petite-vue-i18n.esm-bundler.js`;
165
+ }
166
+ debug(`alias name: ${getAliasName()}`);
167
+ }
168
+ config.define = config.define || {};
169
+ config.define["__VUE_I18N_LEGACY_API__"] = !compositionOnly;
170
+ debug(`set __VUE_I18N_LEGACY_API__ is '${config.define["__VUE_I18N_LEGACY_API__"]}'`);
171
+ config.define["__VUE_I18N_FULL_INSTALL__"] = fullInstall;
172
+ debug(`set __VUE_I18N_FULL_INSTALL__ is '${config.define["__VUE_I18N_FULL_INSTALL__"]}'`);
173
+ config.define["__VUE_I18N_PROD_DEVTOOLS__"] = false;
174
+ },
134
175
  configResolved(config) {
135
176
  isProduction = config.isProduction;
136
177
  sourceMap = config.command === "build" ? !!config.build.sourcemap : false;
@@ -156,6 +197,23 @@ const unplugin = unplugin$1.createUnplugin((options = {}, meta) => {
156
197
  isProduction = compiler.options.mode !== "development";
157
198
  sourceMap = !!compiler.options.devtool;
158
199
  debug(`webpack: isProduction = ${isProduction}, sourceMap = ${sourceMap}`);
200
+ if (isProduction && runtimeOnly) {
201
+ normalizeConfigResolveAlias(compiler.options.resolve, meta.framework);
202
+ compiler.options.resolve.alias["vue-i18n"] = `vue-i18n/dist/vue-i18n.runtime.esm-bundler.js`;
203
+ }
204
+ loadWebpack().then((webpack) => {
205
+ if (webpack) {
206
+ compiler.options.plugins.push(new webpack.DefinePlugin({
207
+ __VUE_I18N_LEGACY_API__: JSON.stringify(compositionOnly),
208
+ __VUE_I18N_FULL_INSTALL__: JSON.stringify(fullInstall),
209
+ __INTLIFY_PROD_DEVTOOLS__: "false"
210
+ }));
211
+ debug(`set __VUE_I18N_LEGACY_API__ is '${compositionOnly}'`);
212
+ debug(`set __VUE_I18N_FULL_INSTALL__ is '${fullInstall}'`);
213
+ } else {
214
+ debug("ignore vue-i18n feature flags with webpack.DefinePlugin");
215
+ }
216
+ });
159
217
  if (compiler.options.module) {
160
218
  compiler.options.module.rules.push({
161
219
  test: /\.(json5?|ya?ml)$/,
@@ -168,16 +226,16 @@ const unplugin = unplugin$1.createUnplugin((options = {}, meta) => {
168
226
  debug("resolveId", id, importer);
169
227
  if (id === INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID) {
170
228
  warn(`deprected '${INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID}', you should switch to '${INTLIFY_BUNDLE_IMPORT_ID}'`);
171
- return id;
229
+ return asVirtualId(id, meta.framework);
172
230
  }
173
231
  if (id === INTLIFY_BUNDLE_IMPORT_ID) {
174
- return id;
232
+ return asVirtualId(id, meta.framework);
175
233
  }
176
234
  },
177
235
  async load(id) {
178
236
  debug("load", id);
179
237
  const { query } = parseVueRequest(id);
180
- if ([INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID, INTLIFY_BUNDLE_IMPORT_ID].includes(id) && include) {
238
+ if ([INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID, INTLIFY_BUNDLE_IMPORT_ID].includes(getVirtualId(id, meta.framework)) && include) {
181
239
  let resourcePaths = [];
182
240
  const includePaths = shared.isArray(include) ? include : [include];
183
241
  for (const inc of includePaths) {
@@ -187,6 +245,7 @@ const unplugin = unplugin$1.createUnplugin((options = {}, meta) => {
187
245
  const code = await generateBundleResources(resourcePaths, query, isProduction, {
188
246
  forceStringify,
189
247
  bridge,
248
+ exportESM: esm,
190
249
  useClassComponent
191
250
  });
192
251
  return {
@@ -195,6 +254,15 @@ const unplugin = unplugin$1.createUnplugin((options = {}, meta) => {
195
254
  };
196
255
  }
197
256
  },
257
+ transformInclude(id) {
258
+ debug("transformInclude", id);
259
+ if (meta.framework === "vite") {
260
+ return true;
261
+ } else {
262
+ const { filename } = parseVueRequest(id);
263
+ return filename.endsWith("vue") || filename.endsWith(INTLIFY_BUNDLE_IMPORT_ID) || filename.endsWith(INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID) || /\.(json5?|ya?ml)$/.test(filename) && filter(filename);
264
+ }
265
+ },
198
266
  async transform(code, id) {
199
267
  const { filename, query } = parseVueRequest(id);
200
268
  debug("transform", id, JSON.stringify(query), filename);
@@ -209,6 +277,7 @@ const unplugin = unplugin$1.createUnplugin((options = {}, meta) => {
209
277
  isGlobal: globalSFCScope,
210
278
  useClassComponent,
211
279
  bridge,
280
+ exportESM: esm,
212
281
  forceStringify
213
282
  });
214
283
  debug("parseOptions", parseOptions);
@@ -241,6 +310,7 @@ const unplugin = unplugin$1.createUnplugin((options = {}, meta) => {
241
310
  isGlobal: globalSFCScope,
242
311
  useClassComponent,
243
312
  bridge,
313
+ exportESM: esm,
244
314
  forceStringify
245
315
  });
246
316
  debug("parseOptions", parseOptions);
@@ -259,10 +329,38 @@ const unplugin = unplugin$1.createUnplugin((options = {}, meta) => {
259
329
  }
260
330
  };
261
331
  });
332
+ function normalizeConfigResolveAlias(resolve, framework) {
333
+ if (resolve && resolve.alias) {
334
+ return;
335
+ }
336
+ if (!resolve) {
337
+ if (framework === "vite") {
338
+ resolve = { alias: [] };
339
+ } else if (framework === "webpack") {
340
+ resolve = { alias: {} };
341
+ }
342
+ } else if (!resolve.alias) {
343
+ if (framework === "vite") {
344
+ resolve.alias = [];
345
+ } else if (framework === "webpack") {
346
+ resolve.alias = {};
347
+ }
348
+ }
349
+ }
350
+ async function loadWebpack() {
351
+ let webpack = null;
352
+ try {
353
+ webpack = await import('webpack').then((m) => m.default || m);
354
+ } catch (e) {
355
+ warn(`webpack not found, please install webpack.`);
356
+ }
357
+ return webpack;
358
+ }
262
359
  async function generateBundleResources(resources, query, isProduction, {
263
360
  forceStringify = false,
264
361
  isGlobal = false,
265
362
  bridge = false,
363
+ exportESM = true,
266
364
  useClassComponent = false
267
365
  }) {
268
366
  const codes = [];
@@ -276,6 +374,7 @@ async function generateBundleResources(resources, query, isProduction, {
276
374
  isGlobal,
277
375
  useClassComponent,
278
376
  bridge,
377
+ exportESM,
279
378
  forceStringify
280
379
  });
281
380
  parseOptions.type = "bare";
@@ -324,6 +423,7 @@ function getOptions(filename, isProduction, query, sourceMap, {
324
423
  forceStringify = false,
325
424
  isGlobal = false,
326
425
  bridge = false,
426
+ exportESM = true,
327
427
  useClassComponent = false
328
428
  }) {
329
429
  const mode = isProduction ? "production" : "development";
@@ -334,6 +434,7 @@ function getOptions(filename, isProduction, query, sourceMap, {
334
434
  forceStringify,
335
435
  useClassComponent,
336
436
  bridge,
437
+ exportESM,
337
438
  env: mode,
338
439
  onWarn: (msg) => {
339
440
  warn(`${filename} ${msg}`);
@@ -355,6 +456,12 @@ function getOptions(filename, isProduction, query, sourceMap, {
355
456
  });
356
457
  }
357
458
  }
459
+ function getVirtualId(id, framework = "vite") {
460
+ return framework === "vite" ? id.startsWith(VIRTUAL_PREFIX) ? id.slice(VIRTUAL_PREFIX.length) : "" : id;
461
+ }
462
+ function asVirtualId(id, framework = "vite") {
463
+ return framework === "vite" ? VIRTUAL_PREFIX + id : id;
464
+ }
358
465
 
359
466
  exports["default"] = unplugin;
360
467
  exports.unplugin = unplugin;
package/lib/index.mjs CHANGED
@@ -2,13 +2,14 @@ import { createUnplugin } from 'unplugin';
2
2
  import { normalize, parse } from 'pathe';
3
3
  import createDebug from 'debug';
4
4
  import fg from 'fast-glob';
5
- import { isString, isArray, isEmptyObject, isNumber, assign } from '@intlify/shared';
5
+ import { isString, isArray, isBoolean, isObject, isEmptyObject, isNumber, assign } from '@intlify/shared';
6
6
  import { createFilter } from '@rollup/pluginutils';
7
- import { generateJSON, generateYAML } from '@intlify/bundle-utils';
7
+ import { checkInstallPackage, generateJSON, generateYAML } from '@intlify/bundle-utils';
8
8
  import { parse as parse$1 } from '@vue/compiler-sfc';
9
9
  import JSON5 from 'json5';
10
10
  import yaml from 'js-yaml';
11
11
  import { promises } from 'fs';
12
+ import pc from 'picocolors';
12
13
 
13
14
  function parseVueRequest(id) {
14
15
  const [filename, rawQuery] = id.split(`?`, 2);
@@ -70,10 +71,10 @@ function convert(source, lang) {
70
71
  }
71
72
 
72
73
  function warn(...args) {
73
- console.warn(`[unplugin-vue-i18n] `, ...args);
74
+ console.warn(pc.yellow(pc.bold(`[unplugin-vue-i18n] `)), ...args);
74
75
  }
75
76
  function error(...args) {
76
- console.error(`[unplugin-vue-i18n] `, ...args);
77
+ console.error(pc.red(pc.bold(`[unplugin-vue-i18n] `)), ...args);
77
78
  }
78
79
  async function getRaw(path) {
79
80
  return promises.readFile(path, { encoding: "utf-8" });
@@ -84,7 +85,9 @@ function raiseError(message) {
84
85
 
85
86
  const INTLIFY_BUNDLE_IMPORT_ID = "@intlify/unplugin-vue-i18n/messages";
86
87
  const INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID = "@intlify/vite-plugin-vue-i18n/messages";
88
+ const VIRTUAL_PREFIX = "\0";
87
89
  const debug = createDebug("unplugin-vue-i18n");
90
+ const installedPkg = checkInstallPackage("@intlify/unplugin-vue-i18n", debug);
88
91
  const unplugin = createUnplugin((options = {}, meta) => {
89
92
  debug("plugin options:", options, meta.framework);
90
93
  if (!["vite", "webpack"].includes(meta.framework)) {
@@ -105,21 +108,58 @@ const unplugin = createUnplugin((options = {}, meta) => {
105
108
  const useClassComponent = !!options.useClassComponent;
106
109
  const bridge = !!options.bridge;
107
110
  debug("bridge", bridge);
111
+ const runtimeOnly = isBoolean(options.runtimeOnly) ? options.runtimeOnly : true;
112
+ debug("runtimeOnly", runtimeOnly);
113
+ const compositionOnly = installedPkg === "vue-i18n" ? isBoolean(options.compositionOnly) ? options.compositionOnly : true : true;
114
+ debug("compositionOnly", compositionOnly);
115
+ const fullInstall = installedPkg === "vue-i18n" ? isBoolean(options.fullInstall) ? options.fullInstall : true : false;
116
+ debug("fullInstall", fullInstall);
117
+ const useVueI18nImportName = options.useVueI18nImportName;
118
+ if (useVueI18nImportName != null) {
119
+ warn(`'useVueI18nImportName' option is experimental`);
120
+ }
121
+ debug("useVueI18nImportName", useVueI18nImportName);
122
+ const getAliasName = () => installedPkg === "petite-vue-i18n" && isBoolean(useVueI18nImportName) && useVueI18nImportName ? "vue-i18n" : `${installedPkg}`;
123
+ const esm = isBoolean(options.esm) ? options.esm : true;
124
+ debug("esm", esm);
108
125
  let isProduction = false;
109
126
  let sourceMap = false;
110
127
  return {
111
128
  name: "unplugin-vue-i18n",
112
129
  enforce: meta.framework === "vite" ? "pre" : "post",
113
- transformInclude(id) {
114
- debug("transformInclude", id);
115
- if (meta.framework === "vite") {
116
- return true;
117
- } else {
118
- const { filename } = parseVueRequest(id);
119
- return filename.endsWith("vue") || filename.endsWith(INTLIFY_BUNDLE_IMPORT_ID) || filename.endsWith(INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID) ? true : /\.(json5?|ya?ml)$/.test(id) && filter(id);
120
- }
121
- },
122
130
  vite: {
131
+ config(config, { command }) {
132
+ normalizeConfigResolveAlias(config.resolve, meta.framework);
133
+ if (command === "build" && runtimeOnly) {
134
+ if (isArray(config.resolve.alias)) {
135
+ config.resolve.alias.push({
136
+ find: getAliasName(),
137
+ replacement: `${installedPkg}/dist/${installedPkg}.runtime.esm-bundler.js`
138
+ });
139
+ } else if (isObject(config.resolve.alias)) {
140
+ config.resolve.alias[getAliasName()] = `${installedPkg}/dist/${installedPkg}.runtime.esm-bundler.js`;
141
+ }
142
+ debug(`alias name: ${getAliasName()}`);
143
+ debug(`set ${installedPkg} runtime only: ${installedPkg}/dist/${installedPkg}.runtime.esm-bundler.js`);
144
+ } else if (command === "serve" && installedPkg === "petite-vue-i18n" && useVueI18nImportName) {
145
+ normalizeConfigResolveAlias(config.resolve, meta.framework);
146
+ if (isArray(config.resolve.alias)) {
147
+ config.resolve.alias.push({
148
+ find: "vue-i18n",
149
+ replacement: `petite-vue-i18n/dist/petite-vue-i18n.esm-bundler.js`
150
+ });
151
+ } else {
152
+ config.resolve.alias["vue-i18n"] = `petite-vue-i18n/dist/petite-vue-i18n.esm-bundler.js`;
153
+ }
154
+ debug(`alias name: ${getAliasName()}`);
155
+ }
156
+ config.define = config.define || {};
157
+ config.define["__VUE_I18N_LEGACY_API__"] = !compositionOnly;
158
+ debug(`set __VUE_I18N_LEGACY_API__ is '${config.define["__VUE_I18N_LEGACY_API__"]}'`);
159
+ config.define["__VUE_I18N_FULL_INSTALL__"] = fullInstall;
160
+ debug(`set __VUE_I18N_FULL_INSTALL__ is '${config.define["__VUE_I18N_FULL_INSTALL__"]}'`);
161
+ config.define["__VUE_I18N_PROD_DEVTOOLS__"] = false;
162
+ },
123
163
  configResolved(config) {
124
164
  isProduction = config.isProduction;
125
165
  sourceMap = config.command === "build" ? !!config.build.sourcemap : false;
@@ -145,6 +185,23 @@ const unplugin = createUnplugin((options = {}, meta) => {
145
185
  isProduction = compiler.options.mode !== "development";
146
186
  sourceMap = !!compiler.options.devtool;
147
187
  debug(`webpack: isProduction = ${isProduction}, sourceMap = ${sourceMap}`);
188
+ if (isProduction && runtimeOnly) {
189
+ normalizeConfigResolveAlias(compiler.options.resolve, meta.framework);
190
+ compiler.options.resolve.alias["vue-i18n"] = `vue-i18n/dist/vue-i18n.runtime.esm-bundler.js`;
191
+ }
192
+ loadWebpack().then((webpack) => {
193
+ if (webpack) {
194
+ compiler.options.plugins.push(new webpack.DefinePlugin({
195
+ __VUE_I18N_LEGACY_API__: JSON.stringify(compositionOnly),
196
+ __VUE_I18N_FULL_INSTALL__: JSON.stringify(fullInstall),
197
+ __INTLIFY_PROD_DEVTOOLS__: "false"
198
+ }));
199
+ debug(`set __VUE_I18N_LEGACY_API__ is '${compositionOnly}'`);
200
+ debug(`set __VUE_I18N_FULL_INSTALL__ is '${fullInstall}'`);
201
+ } else {
202
+ debug("ignore vue-i18n feature flags with webpack.DefinePlugin");
203
+ }
204
+ });
148
205
  if (compiler.options.module) {
149
206
  compiler.options.module.rules.push({
150
207
  test: /\.(json5?|ya?ml)$/,
@@ -157,16 +214,16 @@ const unplugin = createUnplugin((options = {}, meta) => {
157
214
  debug("resolveId", id, importer);
158
215
  if (id === INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID) {
159
216
  warn(`deprected '${INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID}', you should switch to '${INTLIFY_BUNDLE_IMPORT_ID}'`);
160
- return id;
217
+ return asVirtualId(id, meta.framework);
161
218
  }
162
219
  if (id === INTLIFY_BUNDLE_IMPORT_ID) {
163
- return id;
220
+ return asVirtualId(id, meta.framework);
164
221
  }
165
222
  },
166
223
  async load(id) {
167
224
  debug("load", id);
168
225
  const { query } = parseVueRequest(id);
169
- if ([INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID, INTLIFY_BUNDLE_IMPORT_ID].includes(id) && include) {
226
+ if ([INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID, INTLIFY_BUNDLE_IMPORT_ID].includes(getVirtualId(id, meta.framework)) && include) {
170
227
  let resourcePaths = [];
171
228
  const includePaths = isArray(include) ? include : [include];
172
229
  for (const inc of includePaths) {
@@ -176,6 +233,7 @@ const unplugin = createUnplugin((options = {}, meta) => {
176
233
  const code = await generateBundleResources(resourcePaths, query, isProduction, {
177
234
  forceStringify,
178
235
  bridge,
236
+ exportESM: esm,
179
237
  useClassComponent
180
238
  });
181
239
  return {
@@ -184,6 +242,15 @@ const unplugin = createUnplugin((options = {}, meta) => {
184
242
  };
185
243
  }
186
244
  },
245
+ transformInclude(id) {
246
+ debug("transformInclude", id);
247
+ if (meta.framework === "vite") {
248
+ return true;
249
+ } else {
250
+ const { filename } = parseVueRequest(id);
251
+ return filename.endsWith("vue") || filename.endsWith(INTLIFY_BUNDLE_IMPORT_ID) || filename.endsWith(INTLIFY_BUNDLE_IMPORT_DEPRECTED_ID) || /\.(json5?|ya?ml)$/.test(filename) && filter(filename);
252
+ }
253
+ },
187
254
  async transform(code, id) {
188
255
  const { filename, query } = parseVueRequest(id);
189
256
  debug("transform", id, JSON.stringify(query), filename);
@@ -198,6 +265,7 @@ const unplugin = createUnplugin((options = {}, meta) => {
198
265
  isGlobal: globalSFCScope,
199
266
  useClassComponent,
200
267
  bridge,
268
+ exportESM: esm,
201
269
  forceStringify
202
270
  });
203
271
  debug("parseOptions", parseOptions);
@@ -230,6 +298,7 @@ const unplugin = createUnplugin((options = {}, meta) => {
230
298
  isGlobal: globalSFCScope,
231
299
  useClassComponent,
232
300
  bridge,
301
+ exportESM: esm,
233
302
  forceStringify
234
303
  });
235
304
  debug("parseOptions", parseOptions);
@@ -248,10 +317,38 @@ const unplugin = createUnplugin((options = {}, meta) => {
248
317
  }
249
318
  };
250
319
  });
320
+ function normalizeConfigResolveAlias(resolve, framework) {
321
+ if (resolve && resolve.alias) {
322
+ return;
323
+ }
324
+ if (!resolve) {
325
+ if (framework === "vite") {
326
+ resolve = { alias: [] };
327
+ } else if (framework === "webpack") {
328
+ resolve = { alias: {} };
329
+ }
330
+ } else if (!resolve.alias) {
331
+ if (framework === "vite") {
332
+ resolve.alias = [];
333
+ } else if (framework === "webpack") {
334
+ resolve.alias = {};
335
+ }
336
+ }
337
+ }
338
+ async function loadWebpack() {
339
+ let webpack = null;
340
+ try {
341
+ webpack = await import('webpack').then((m) => m.default || m);
342
+ } catch (e) {
343
+ warn(`webpack not found, please install webpack.`);
344
+ }
345
+ return webpack;
346
+ }
251
347
  async function generateBundleResources(resources, query, isProduction, {
252
348
  forceStringify = false,
253
349
  isGlobal = false,
254
350
  bridge = false,
351
+ exportESM = true,
255
352
  useClassComponent = false
256
353
  }) {
257
354
  const codes = [];
@@ -265,6 +362,7 @@ async function generateBundleResources(resources, query, isProduction, {
265
362
  isGlobal,
266
363
  useClassComponent,
267
364
  bridge,
365
+ exportESM,
268
366
  forceStringify
269
367
  });
270
368
  parseOptions.type = "bare";
@@ -313,6 +411,7 @@ function getOptions(filename, isProduction, query, sourceMap, {
313
411
  forceStringify = false,
314
412
  isGlobal = false,
315
413
  bridge = false,
414
+ exportESM = true,
316
415
  useClassComponent = false
317
416
  }) {
318
417
  const mode = isProduction ? "production" : "development";
@@ -323,6 +422,7 @@ function getOptions(filename, isProduction, query, sourceMap, {
323
422
  forceStringify,
324
423
  useClassComponent,
325
424
  bridge,
425
+ exportESM,
326
426
  env: mode,
327
427
  onWarn: (msg) => {
328
428
  warn(`${filename} ${msg}`);
@@ -344,5 +444,11 @@ function getOptions(filename, isProduction, query, sourceMap, {
344
444
  });
345
445
  }
346
446
  }
447
+ function getVirtualId(id, framework = "vite") {
448
+ return framework === "vite" ? id.startsWith(VIRTUAL_PREFIX) ? id.slice(VIRTUAL_PREFIX.length) : "" : id;
449
+ }
450
+ function asVirtualId(id, framework = "vite") {
451
+ return framework === "vite" ? VIRTUAL_PREFIX + id : id;
452
+ }
347
453
 
348
454
  export { unplugin as default, unplugin };
package/lib/types.d.ts CHANGED
@@ -1,11 +1,16 @@
1
1
  declare type SFCLangFormat = 'json' | 'json5' | 'yml' | 'yaml';
2
2
  interface PluginOptions {
3
3
  include?: string | string[];
4
+ runtimeOnly?: boolean;
5
+ compositionOnly?: boolean;
6
+ fullInstall?: boolean;
7
+ esm?: boolean;
4
8
  forceStringify?: boolean;
5
9
  defaultSFCLang?: SFCLangFormat;
6
10
  globalSFCScope?: boolean;
7
11
  bridge?: boolean;
8
12
  useClassComponent?: boolean;
13
+ useVueI18nImportName?: boolean;
9
14
  }
10
15
 
11
16
  export { PluginOptions, SFCLangFormat };
package/lib/vite.cjs CHANGED
@@ -12,6 +12,7 @@ require('@vue/compiler-sfc');
12
12
  require('json5');
13
13
  require('js-yaml');
14
14
  require('fs');
15
+ require('picocolors');
15
16
 
16
17
  const vite = index.unplugin.vite;
17
18
 
package/lib/vite.mjs CHANGED
@@ -10,6 +10,7 @@ import '@vue/compiler-sfc';
10
10
  import 'json5';
11
11
  import 'js-yaml';
12
12
  import 'fs';
13
+ import 'picocolors';
13
14
 
14
15
  const vite = unplugin.vite;
15
16
 
package/lib/webpack.cjs CHANGED
@@ -12,6 +12,7 @@ require('@vue/compiler-sfc');
12
12
  require('json5');
13
13
  require('js-yaml');
14
14
  require('fs');
15
+ require('picocolors');
15
16
 
16
17
  const webpack = index.unplugin.webpack;
17
18
 
package/lib/webpack.mjs CHANGED
@@ -10,6 +10,7 @@ import '@vue/compiler-sfc';
10
10
  import 'json5';
11
11
  import 'js-yaml';
12
12
  import 'fs';
13
+ import 'picocolors';
13
14
 
14
15
  const webpack = unplugin.webpack;
15
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlify/unplugin-vue-i18n",
3
- "version": "0.2.1",
3
+ "version": "0.5.0",
4
4
  "description": "unplugin for Vue I18n",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
@@ -31,15 +31,16 @@
31
31
  "js-yaml": "^4.1.0",
32
32
  "json5": "^2.2.0",
33
33
  "pathe": "^0.2.0",
34
+ "picocolors": "^1.0.0",
34
35
  "source-map": "0.6.1",
35
- "unplugin": "^0.6.2"
36
+ "unplugin": "^0.8.0"
36
37
  },
37
38
  "devDependencies": {
38
39
  "mlly": "^0.5.2",
39
40
  "unbuild": "^0.7.4"
40
41
  },
41
42
  "engines": {
42
- "node": ">= 12"
43
+ "node": ">= 14.16"
43
44
  },
44
45
  "files": [
45
46
  "lib",