From 1d7785a80da5d9567c8d85988714f10153cdce6b Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Mon, 13 Mar 2023 17:19:42 +0900 Subject: [PATCH 1/2] feat(unplugin-vue-i18n): support dynamic resource construction for js / ts formats --- packages/unplugin-vue-i18n/README.md | 19 +++++++++++++++++-- packages/unplugin-vue-i18n/package.json | 2 +- packages/unplugin-vue-i18n/src/index.ts | 16 +++++++++------- packages/unplugin-vue-i18n/src/types.ts | 1 + packages/unplugin-vue-i18n/test/utils.ts | 1 + .../test/vite/resource-compilation.test.ts | 9 +++++++++ yarn.lock | 4 ++-- 7 files changed, 40 insertions(+), 12 deletions(-) diff --git a/packages/unplugin-vue-i18n/README.md b/packages/unplugin-vue-i18n/README.md index d60fbff..27aea31 100644 --- a/packages/unplugin-vue-i18n/README.md +++ b/packages/unplugin-vue-i18n/README.md @@ -277,6 +277,8 @@ This plugin will automatically select and bundle `petite-vue-i18n` build accordi - ts ``` + If nothing is specified for this option, i.e. `undefined`, nothing is done to the resource in the above format. + > ⚠️ 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. @@ -284,7 +286,8 @@ This plugin will automatically select and bundle `petite-vue-i18n` build accordi `yaml` resources don't support multi documentation with `|`, alias with `&` and `*`, tags with `! `, `@`, etc. Only simple data structures. > ⚠️ NOTE: - `js` and `ts` resources are limited to **simple export (`export default`) as locale messages object only**, such as programmatically dynamic resource construction is not guaranteed to work currently. + `js` and `ts` resources are limited to **simple export (`export default`) as locale messages object as default** + , such as programmatically dynamic resource construction is not guaranteed to work currently. ```js export default { @@ -293,7 +296,19 @@ This plugin will automatically select and bundle `petite-vue-i18n` build accordi } ``` - If nothing is specified for this option, i.e. `undefined`, nothing is done to the resource in the above format. + if you need to use programmatically dynamic resource construction, you would be enable `allowDynamic` option. In this case, you need to export the function with `export default` and construct the resource with the function as follow: + + ```js + import resources from './locales/all.json' + + export default async function loadResource(url) { + const res = await import(url).then(r => r.default || r) + return { ...resources, ...res } + } + ``` + + If you fetch some resources from a backend, the data **must be pre-compiled** for production. exmaple is [here](https://github.com/intlify/vue-i18n-next/tree/master/examples/backend). + ### `runtimeOnly` diff --git a/packages/unplugin-vue-i18n/package.json b/packages/unplugin-vue-i18n/package.json index 050a2e3..2cc3390 100644 --- a/packages/unplugin-vue-i18n/package.json +++ b/packages/unplugin-vue-i18n/package.json @@ -26,7 +26,7 @@ } }, "dependencies": { - "@intlify/bundle-utils": "^5.0.0", + "@intlify/bundle-utils": "^5.1.2", "@intlify/shared": "next", "@rollup/pluginutils": "^5.0.2", "@vue/compiler-sfc": "^3.2.47", diff --git a/packages/unplugin-vue-i18n/src/index.ts b/packages/unplugin-vue-i18n/src/index.ts index 47a4618..2e15fe1 100644 --- a/packages/unplugin-vue-i18n/src/index.ts +++ b/packages/unplugin-vue-i18n/src/index.ts @@ -27,12 +27,7 @@ import { parseVueRequest, VueQuery } from './query' import { createBridgeCodeGenerator } from './legacy' import { getRaw, warn, error, raiseError } from './utils' -import type { - UnpluginContextMeta, - UnpluginOptions, - SourceMapCompact -} from 'unplugin' -import type { SourceMapInput } from 'rollup' +import type { UnpluginContextMeta, UnpluginOptions } from 'unplugin' import type { PluginOptions } from './types' import type { CodeGenOptions, DevEnv } from '@intlify/bundle-utils' @@ -125,6 +120,8 @@ export const unplugin = createUnplugin((options = {}, meta) => { const esm = isBoolean(options.esm) ? options.esm : true debug('esm', esm) + const allowDynamic = !!options.allowDynamic + let isProduction = false let sourceMap = false @@ -296,6 +293,7 @@ export const unplugin = createUnplugin((options = {}, meta) => { inSourceMap, isGlobal: globalSFCScope, useClassComponent, + allowDynamic, bridge, exportESM: esm, forceStringify @@ -501,6 +499,7 @@ export const unplugin = createUnplugin((options = {}, meta) => { inSourceMap, isGlobal: globalSFCScope, useClassComponent, + allowDynamic, bridge, exportESM: esm, forceStringify @@ -743,7 +742,8 @@ function getOptions( isGlobal = false, bridge = false, exportESM = true, - useClassComponent = false + useClassComponent = false, + allowDynamic = false }: { inSourceMap?: RawSourceMap forceStringify?: boolean @@ -751,6 +751,7 @@ function getOptions( bridge?: boolean exportESM?: boolean useClassComponent?: boolean + allowDynamic?: boolean } ): Record { const mode: DevEnv = isProduction ? 'production' : 'development' @@ -761,6 +762,7 @@ function getOptions( inSourceMap, forceStringify, useClassComponent, + allowDynamic, bridge, exportESM, env: mode, diff --git a/packages/unplugin-vue-i18n/src/types.ts b/packages/unplugin-vue-i18n/src/types.ts index 120c918..de766dc 100644 --- a/packages/unplugin-vue-i18n/src/types.ts +++ b/packages/unplugin-vue-i18n/src/types.ts @@ -1,6 +1,7 @@ export type SFCLangFormat = 'json' | 'json5' | 'yml' | 'yaml' export interface PluginOptions { include?: string | string[] + allowDynamic?: boolean runtimeOnly?: boolean compositionOnly?: boolean fullInstall?: boolean diff --git a/packages/unplugin-vue-i18n/test/utils.ts b/packages/unplugin-vue-i18n/test/utils.ts index cdb72db..abe5cb5 100644 --- a/packages/unplugin-vue-i18n/test/utils.ts +++ b/packages/unplugin-vue-i18n/test/utils.ts @@ -153,6 +153,7 @@ export async function bundleAndRun( options.sourcemap = isBoolean(options.sourcemap) || false options.useClassComponent = isBoolean(options.useClassComponent) || false options.bridge = isBoolean(options.bridge) || false + options.allowDynamic = isBoolean(options.allowDynamic) || false const { code, map } = await bundler(fixture, options) diff --git a/packages/unplugin-vue-i18n/test/vite/resource-compilation.test.ts b/packages/unplugin-vue-i18n/test/vite/resource-compilation.test.ts index c3db15c..f9c6303 100644 --- a/packages/unplugin-vue-i18n/test/vite/resource-compilation.test.ts +++ b/packages/unplugin-vue-i18n/test/vite/resource-compilation.test.ts @@ -1,5 +1,6 @@ import { resolve } from 'pathe' import { bundleVite, bundleAndRun } from '../utils' +import { isFunction } from '@intlify/shared' import { createMessageContext } from '@intlify/core-base' const options = { @@ -48,3 +49,11 @@ test('ts resource', async () => { // expect(fn.source).toEqual(`@.caml:{'no apples'} | {0} apple | {n} apples`) expect(fn(createMessageContext({ named: { n: 3 } }))).toEqual(`3 apples`) }) + +test('dynamical resource with js / ts', async () => { + const { module } = await bundleAndRun('ka-JP.ts', bundleVite, { + allowDynamic: true, + ...options + }) + expect(isFunction(module)).toBe(true) +}) diff --git a/yarn.lock b/yarn.lock index e09ee60..f16e8a0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -969,7 +969,7 @@ __metadata: languageName: unknown linkType: soft -"@intlify/bundle-utils@^5.0.0, @intlify/bundle-utils@workspace:packages/bundle-utils": +"@intlify/bundle-utils@^5.1.2, @intlify/bundle-utils@workspace:packages/bundle-utils": version: 0.0.0-use.local resolution: "@intlify/bundle-utils@workspace:packages/bundle-utils" dependencies: @@ -1094,7 +1094,7 @@ __metadata: version: 0.0.0-use.local resolution: "@intlify/unplugin-vue-i18n@workspace:packages/unplugin-vue-i18n" dependencies: - "@intlify/bundle-utils": ^5.0.0 + "@intlify/bundle-utils": ^5.1.2 "@intlify/shared": next "@rollup/pluginutils": ^5.0.2 "@vue/compiler-sfc": ^3.2.47 From 50cdfc41a85717bb14ad20ba1217a723673d8a54 Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Mon, 13 Mar 2023 17:30:33 +0900 Subject: [PATCH 2/2] update docs --- packages/unplugin-vue-i18n/README.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/unplugin-vue-i18n/README.md b/packages/unplugin-vue-i18n/README.md index 27aea31..03f878b 100644 --- a/packages/unplugin-vue-i18n/README.md +++ b/packages/unplugin-vue-i18n/README.md @@ -286,8 +286,7 @@ This plugin will automatically select and bundle `petite-vue-i18n` build accordi `yaml` resources don't support multi documentation with `|`, alias with `&` and `*`, tags with `! `, `@`, etc. Only simple data structures. > ⚠️ NOTE: - `js` and `ts` resources are limited to **simple export (`export default`) as locale messages object as default** - , such as programmatically dynamic resource construction is not guaranteed to work currently. + `js` and `ts` resources are set **simple export (`export default`) as locale messages object, as default**. ```js export default { @@ -296,7 +295,19 @@ This plugin will automatically select and bundle `petite-vue-i18n` build accordi } ``` - if you need to use programmatically dynamic resource construction, you would be enable `allowDynamic` option. In this case, you need to export the function with `export default` and construct the resource with the function as follow: + If you need to use programmatically dynamic resource construction, you would be enable `allowDynamic` option. about details, see the section. + + > ⚠️ NOTE: + If you use the `js` and `ts` resources formats, set the paths, so your application code is not targeted. We recommend that resources be isolated from the application code. + +### `allowDynamic` + +- **Type:** `boolean` +- **Default:** `false` + + Whether or not programmatically dynamic resource construction for `js` or `ts` resource format. + + In this case, you need to export the function with `export default` and construct the resource with the function: ```js import resources from './locales/all.json' @@ -307,7 +318,7 @@ This plugin will automatically select and bundle `petite-vue-i18n` build accordi } ``` - If you fetch some resources from a backend, the data **must be pre-compiled** for production. exmaple is [here](https://github.com/intlify/vue-i18n-next/tree/master/examples/backend). + If you fetch some resources from the backend, the data **must be pre-compiled** for production. exmaple is [here](https://github.com/intlify/vue-i18n-next/tree/master/examples/backend). ### `runtimeOnly`