diff --git a/packages/unplugin-vue-i18n/README.md b/packages/unplugin-vue-i18n/README.md index 93581ab..e493e3c 100644 --- a/packages/unplugin-vue-i18n/README.md +++ b/packages/unplugin-vue-i18n/README.md @@ -273,10 +273,21 @@ This plugin will automatically select and bundle `petite-vue-i18n` build accordi - json5 - yaml - yml + - js + - ts ``` 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. + Note that `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 + export default { + hello: 'Hello, {name}!', + // ... + } + ``` + If nothing is specified for this option, i.e. `undefined`, nothing is done to the resource in the above format. ### `runtimeOnly` diff --git a/packages/unplugin-vue-i18n/package.json b/packages/unplugin-vue-i18n/package.json index e39e570..7d5a1a3 100644 --- a/packages/unplugin-vue-i18n/package.json +++ b/packages/unplugin-vue-i18n/package.json @@ -26,18 +26,18 @@ } }, "dependencies": { - "@intlify/bundle-utils": "^4.0.0", + "@intlify/bundle-utils": "^5.0.0", "@intlify/shared": "next", - "@rollup/pluginutils": "^4.2.0", - "@vue/compiler-sfc": "^3.2.45", - "debug": "^4.3.1", - "fast-glob": "^3.2.5", + "@rollup/pluginutils": "^5.0.2", + "@vue/compiler-sfc": "^3.2.47", + "debug": "^4.3.3", + "fast-glob": "^3.2.12", "js-yaml": "^4.1.0", - "json5": "^2.2.0", + "json5": "^2.2.3", "pathe": "^1.0.0", "picocolors": "^1.0.0", "source-map": "0.6.1", - "unplugin": "^1.0.0" + "unplugin": "^1.1.0" }, "devDependencies": { "mlly": "^1.0.0", diff --git a/packages/unplugin-vue-i18n/src/index.ts b/packages/unplugin-vue-i18n/src/index.ts index 7ed2c68..1549be8 100644 --- a/packages/unplugin-vue-i18n/src/index.ts +++ b/packages/unplugin-vue-i18n/src/index.ts @@ -1,4 +1,4 @@ -import { createUnplugin } from 'unplugin' +import { createUnplugin, TransformResult } from 'unplugin' import { normalize, parse as parsePath } from 'pathe' import createDebug from 'debug' import fg from 'fast-glob' @@ -16,6 +16,7 @@ import { createFilter } from '@rollup/pluginutils' import { generateJSON, generateYAML, + generateJavaScript, checkInstallPackage, checkVueI18nBridgeInstallPackage, getVueI18nVersion @@ -26,7 +27,12 @@ import { parseVueRequest, VueQuery } from './query' import { createBridgeCodeGenerator } from './legacy' import { getRaw, warn, error, raiseError } from './utils' -import type { UnpluginContextMeta, UnpluginOptions } from 'unplugin' +import type { + UnpluginContextMeta, + UnpluginOptions, + SourceMapCompact +} from 'unplugin' +import type { SourceMapInput } from 'rollup' import type { PluginOptions } from './types' import type { CodeGenOptions, DevEnv } from '@intlify/bundle-utils' @@ -247,6 +253,76 @@ export const unplugin = createUnplugin((options = {}, meta) => { return orgTransform!.apply(this, [code, id]) } } + + /** + * typescript transform handling + * + * NOTE: + * Typescript resources are handled using the already existing `vite:esbuild` plugin. + */ + const esbuildPlugin = config.plugins.find( + p => p.name === 'vite:esbuild' + ) + if (esbuildPlugin) { + const orgTransform = esbuildPlugin.transform // backup @rollup/plugin-json + // @ts-ignore + esbuildPlugin.transform = async function (code: string, id: string) { + const result = (await orgTransform!.apply(this, [ + code, + id + ])) as TransformResult + if (result == null) { + return result + } + + const { filename, query } = parseVueRequest(id) + if (!query.vue && filter(id) && /\.[c|m]?ts$/.test(id)) { + const [_code, inSourceMap]: [string, RawSourceMap | undefined] = + isString(result) + ? [result, undefined] + : [result.code, result.map as RawSourceMap] + + let langInfo = defaultSFCLang + langInfo = parsePath(filename) + .ext as Required['defaultSFCLang'] + + const generate = getGenerator(langInfo) + const parseOptions = getOptions( + filename, + isProduction, + query as Record, + sourceMap, + { + inSourceMap, + isGlobal: globalSFCScope, + useClassComponent, + bridge, + exportESM: esm, + forceStringify + } + ) as CodeGenOptions + debug('parseOptions', parseOptions) + + const { code: generatedCode, map } = generate( + _code, + parseOptions, + bridge ? createBridgeCodeGenerator(_code, query) : undefined + ) + debug('generated code', generatedCode) + console.log('generated code', generatedCode) + debug('sourcemap', map, sourceMap) + + if (_code === generatedCode) return + + return { + code: generatedCode, + map: (sourceMap ? map : { mappings: '' }) as any // eslint-disable-line @typescript-eslint/no-explicit-any + } + } else { + return result + } + } + } }, async handleHotUpdate({ file, server }) { @@ -412,14 +488,11 @@ export const unplugin = createUnplugin((options = {}, meta) => { let inSourceMap: RawSourceMap | undefined if (!query.vue) { - if (/\.(json5?|ya?ml)$/.test(id) && filter(id)) { + if (/\.(json5?|ya?ml|[c|m]?js)$/.test(id) && filter(id)) { langInfo = parsePath(filename) .ext as Required['defaultSFCLang'] - const generate = /\.?json5?/.test(langInfo) - ? generateJSON - : generateYAML - + const generate = getGenerator(langInfo) const parseOptions = getOptions( filename, isProduction, @@ -517,6 +590,17 @@ export const unplugin = createUnplugin((options = {}, meta) => { } as UnpluginOptions }) +function getGenerator(ext: string, defaultGen = generateJSON) { + // prettier-ignore + return /\.?json5?$/.test(ext) + ? generateJSON + : /\.ya?ml$/.test(ext) + ? generateYAML + : /\.([c|m]?js|[c|m]?ts)$/.test(ext) + ? generateJavaScript + : defaultGen +} + function normalizeConfigResolveAlias( resolve: any, // eslint-disable-line @typescript-eslint/no-explicit-any framework: UnpluginContextMeta['framework'] diff --git a/packages/unplugin-vue-i18n/test/fixtures/locales/en-GB.ts b/packages/unplugin-vue-i18n/test/fixtures/locales/en-GB.ts new file mode 100644 index 0000000..374bab1 --- /dev/null +++ b/packages/unplugin-vue-i18n/test/fixtures/locales/en-GB.ts @@ -0,0 +1,4 @@ +export default { + // comment + message: "@.caml:{'no apples'} | {0} apple | {n} apples" +} as Record diff --git a/packages/unplugin-vue-i18n/test/fixtures/locales/en-KK.mjs b/packages/unplugin-vue-i18n/test/fixtures/locales/en-KK.mjs new file mode 100644 index 0000000..7d39326 --- /dev/null +++ b/packages/unplugin-vue-i18n/test/fixtures/locales/en-KK.mjs @@ -0,0 +1,4 @@ +export default { + // comment + message: "@.caml:{'no apples'} | {0} apple | {n} apples" +} diff --git a/packages/unplugin-vue-i18n/test/fixtures/locales/en-US.js b/packages/unplugin-vue-i18n/test/fixtures/locales/en-US.js new file mode 100644 index 0000000..7d39326 --- /dev/null +++ b/packages/unplugin-vue-i18n/test/fixtures/locales/en-US.js @@ -0,0 +1,4 @@ +export default { + // comment + message: "@.caml:{'no apples'} | {0} apple | {n} apples" +} diff --git a/packages/unplugin-vue-i18n/test/utils.ts b/packages/unplugin-vue-i18n/test/utils.ts index 2946a3f..cdb72db 100644 --- a/packages/unplugin-vue-i18n/test/utils.ts +++ b/packages/unplugin-vue-i18n/test/utils.ts @@ -31,7 +31,7 @@ export async function bundleVite( const input = (options.input as string) || './fixtures/entry.ts' const target = (options.target as string) || './fixtures' const include = (options.include as string[]) || [ - resolve(__dirname, './fixtures/**') + resolve(__dirname, './fixtures/locales/**') ] const silent = isBoolean(options.silent) ? options.silent === false 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 dc28f52..c3db15c 100644 --- a/packages/unplugin-vue-i18n/test/vite/resource-compilation.test.ts +++ b/packages/unplugin-vue-i18n/test/vite/resource-compilation.test.ts @@ -34,3 +34,17 @@ test('yml resource', async () => { // expect(fn.source).toEqual(`@.caml:{'no apples'} | {0} apple | {n} apples`) expect(fn(createMessageContext({ named: { n: 3 } }))).toEqual(`3 apples`) }) + +test('js resource', async () => { + const { module } = await bundleAndRun('en-KK.mjs', bundleVite, options) + const fn = module.message + // expect(fn.source).toEqual(`@.caml:{'no apples'} | {0} apple | {n} apples`) + expect(fn(createMessageContext({ named: { n: 3 } }))).toEqual(`3 apples`) +}) + +test('ts resource', async () => { + const { module } = await bundleAndRun('en-GB.ts', bundleVite, options) + const fn = module.message + // expect(fn.source).toEqual(`@.caml:{'no apples'} | {0} apple | {n} apples`) + expect(fn(createMessageContext({ named: { n: 3 } }))).toEqual(`3 apples`) +}) diff --git a/yarn.lock b/yarn.lock index 18ee08a..bf53b81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1040,10 +1040,13 @@ __metadata: languageName: unknown linkType: soft -"@intlify/bundle-utils@npm:3.4.0, @intlify/bundle-utils@npm:^3.1.2": - version: 3.4.0 - resolution: "@intlify/bundle-utils@npm:3.4.0" +"@intlify/bundle-utils@^5.0.0, @intlify/bundle-utils@workspace:packages/bundle-utils": + version: 0.0.0-use.local + resolution: "@intlify/bundle-utils@workspace:packages/bundle-utils" dependencies: + "@babel/parser": ^7.21.2 + "@babel/traverse": ^7.21.2 + "@babel/types": ^7.21.2 "@intlify/message-compiler": next "@intlify/shared": next jsonc-eslint-parser: ^1.0.1 @@ -1054,13 +1057,12 @@ __metadata: optional: true vue-i18n: optional: true - checksum: eb8bcec99fea495ac1011867f23e7a05aa2ba9b89366c6a1a8b7ecb0f1c4ef551e68aeabeb43bb01353a36a87791805c48290d0b847989cfb0c8fc07e2969b2e - languageName: node - linkType: hard + languageName: unknown + linkType: soft -"@intlify/bundle-utils@npm:^4.0.0": - version: 4.0.0 - resolution: "@intlify/bundle-utils@npm:4.0.0" +"@intlify/bundle-utils@npm:3.4.0, @intlify/bundle-utils@npm:^3.1.2": + version: 3.4.0 + resolution: "@intlify/bundle-utils@npm:3.4.0" dependencies: "@intlify/message-compiler": next "@intlify/shared": next @@ -1072,7 +1074,7 @@ __metadata: optional: true vue-i18n: optional: true - checksum: 765cd8d599635dd2dee163e4bf0914cf6351603a5e2c6f679a06f93fe5ef14ada84b45cf2f9bc2c0f83e620cf3d394f71b2d9c9eb7ee374d1fea764062da6184 + checksum: eb8bcec99fea495ac1011867f23e7a05aa2ba9b89366c6a1a8b7ecb0f1c4ef551e68aeabeb43bb01353a36a87791805c48290d0b847989cfb0c8fc07e2969b2e languageName: node linkType: hard @@ -1094,26 +1096,6 @@ __metadata: languageName: node linkType: hard -"@intlify/bundle-utils@workspace:packages/bundle-utils": - version: 0.0.0-use.local - resolution: "@intlify/bundle-utils@workspace:packages/bundle-utils" - dependencies: - "@babel/parser": ^7.21.2 - "@babel/traverse": ^7.21.2 - "@babel/types": ^7.21.2 - "@intlify/message-compiler": next - "@intlify/shared": next - jsonc-eslint-parser: ^1.0.1 - source-map: 0.6.1 - yaml-eslint-parser: ^0.3.2 - peerDependenciesMeta: - petite-vue-i18n: - optional: true - vue-i18n: - optional: true - languageName: unknown - linkType: soft - "@intlify/core-base@npm:9.2.0-beta.35, @intlify/core-base@npm:beta": version: 9.2.0-beta.35 resolution: "@intlify/core-base@npm:9.2.0-beta.35" @@ -1183,20 +1165,20 @@ __metadata: version: 0.0.0-use.local resolution: "@intlify/unplugin-vue-i18n@workspace:packages/unplugin-vue-i18n" dependencies: - "@intlify/bundle-utils": ^4.0.0 + "@intlify/bundle-utils": ^5.0.0 "@intlify/shared": next - "@rollup/pluginutils": ^4.2.0 - "@vue/compiler-sfc": ^3.2.45 - debug: ^4.3.1 - fast-glob: ^3.2.5 + "@rollup/pluginutils": ^5.0.2 + "@vue/compiler-sfc": ^3.2.47 + debug: ^4.3.3 + fast-glob: ^3.2.12 js-yaml: ^4.1.0 - json5: ^2.2.0 + json5: ^2.2.3 mlly: ^1.0.0 pathe: ^1.0.0 picocolors: ^1.0.0 source-map: 0.6.1 unbuild: ^1.0.2 - unplugin: ^1.0.0 + unplugin: ^1.1.0 peerDependencies: petite-vue-i18n: "*" vue-i18n: "*" @@ -2860,15 +2842,15 @@ __metadata: languageName: node linkType: hard -"@vue/compiler-core@npm:3.2.45": - version: 3.2.45 - resolution: "@vue/compiler-core@npm:3.2.45" +"@vue/compiler-core@npm:3.2.47": + version: 3.2.47 + resolution: "@vue/compiler-core@npm:3.2.47" dependencies: "@babel/parser": ^7.16.4 - "@vue/shared": 3.2.45 + "@vue/shared": 3.2.47 estree-walker: ^2.0.2 source-map: ^0.6.1 - checksum: e3c687b24c16c2b320c02ed38960f8bee7dcb88bddb09e60a80d2d4dc004070cbbd4eccbc99cc168d48d753ff60d0b9eefba835e1dec3b7f233a98c89af31c07 + checksum: 9ccc2a0b897b59eea56ca4f92ed29c14cd1184f68532edf5fb0fe5cb2833bcf9e4836029effb6eb9a7c872e9e0350fafdcd96ff00c0b5b79e17ded0c068b5f84 languageName: node linkType: hard @@ -2892,13 +2874,13 @@ __metadata: languageName: node linkType: hard -"@vue/compiler-dom@npm:3.2.45": - version: 3.2.45 - resolution: "@vue/compiler-dom@npm:3.2.45" +"@vue/compiler-dom@npm:3.2.47": + version: 3.2.47 + resolution: "@vue/compiler-dom@npm:3.2.47" dependencies: - "@vue/compiler-core": 3.2.45 - "@vue/shared": 3.2.45 - checksum: 89115538635f0da9cce615de5488d2759256fa573976a09a049536dbb94e9b5086b46f2f11e743cf0a7b14837161b3191c67611e0493054a5d4c4b96a322c901 + "@vue/compiler-core": 3.2.47 + "@vue/shared": 3.2.47 + checksum: 1eced735f865e6df0c2d7fa041f9f27996ff4c0d4baf5fad0f67e65e623215f4394c49bba337b78427c6e71f2cc2db12b19ec6b865b4c057c0a15ccedeb20752 languageName: node linkType: hard @@ -2938,21 +2920,21 @@ __metadata: languageName: node linkType: hard -"@vue/compiler-sfc@npm:^3.2.45": - version: 3.2.45 - resolution: "@vue/compiler-sfc@npm:3.2.45" +"@vue/compiler-sfc@npm:^3.2.47": + version: 3.2.47 + resolution: "@vue/compiler-sfc@npm:3.2.47" dependencies: "@babel/parser": ^7.16.4 - "@vue/compiler-core": 3.2.45 - "@vue/compiler-dom": 3.2.45 - "@vue/compiler-ssr": 3.2.45 - "@vue/reactivity-transform": 3.2.45 - "@vue/shared": 3.2.45 + "@vue/compiler-core": 3.2.47 + "@vue/compiler-dom": 3.2.47 + "@vue/compiler-ssr": 3.2.47 + "@vue/reactivity-transform": 3.2.47 + "@vue/shared": 3.2.47 estree-walker: ^2.0.2 magic-string: ^0.25.7 postcss: ^8.1.10 source-map: ^0.6.1 - checksum: bec375faa0012e953dc0887482cc01d52003ad424b6a8a9c8a2506fd4f0197ad62be22f77ce5691c2306068ae7bc0028399f25399e7d4beee668285d431f4d8f + checksum: 4588a513310b9319a00adfdbe789cfe60d5ec19c51e8f2098152b9e81f54be170e16c40463f6b5e4c7ab79796fc31e2de93587a9dd1af136023fa03712b62e68 languageName: node linkType: hard @@ -2976,13 +2958,13 @@ __metadata: languageName: node linkType: hard -"@vue/compiler-ssr@npm:3.2.45": - version: 3.2.45 - resolution: "@vue/compiler-ssr@npm:3.2.45" +"@vue/compiler-ssr@npm:3.2.47": + version: 3.2.47 + resolution: "@vue/compiler-ssr@npm:3.2.47" dependencies: - "@vue/compiler-dom": 3.2.45 - "@vue/shared": 3.2.45 - checksum: 830c475506d2b6d1a6872b3fde1024ef5132f725121fd9c34832c5cefcc8cfddf0dcaa3acc9b2da4754162fccdff48b3275b9ff31415a7793b224c04355dc632 + "@vue/compiler-dom": 3.2.47 + "@vue/shared": 3.2.47 + checksum: 91bc6e46744d5405713c08d8e576971aa6d13a0cde84ec592d3221bf6ee228e49ce12233af8c18dc39723455b420df2951f3616ceb99758eb432485475fa7bc2 languageName: node linkType: hard @@ -3026,16 +3008,16 @@ __metadata: languageName: node linkType: hard -"@vue/reactivity-transform@npm:3.2.45": - version: 3.2.45 - resolution: "@vue/reactivity-transform@npm:3.2.45" +"@vue/reactivity-transform@npm:3.2.47": + version: 3.2.47 + resolution: "@vue/reactivity-transform@npm:3.2.47" dependencies: "@babel/parser": ^7.16.4 - "@vue/compiler-core": 3.2.45 - "@vue/shared": 3.2.45 + "@vue/compiler-core": 3.2.47 + "@vue/shared": 3.2.47 estree-walker: ^2.0.2 magic-string: ^0.25.7 - checksum: 401040818947eb04c782487a7861d3ba20f95c9f3ca14282b3d7624002bfe6000547bb48c561afe87ae6d302143fec71a7e0bc3ed3ae2bfad8a228adf7fd90d6 + checksum: 6fe54374aa8c080c0c421e18134e84e723e2d3e53178cf084c1cd75bc8b1ffaaf07756801f3aa4e1e7ad1ba76356c28bbab4bc1b676159db8fc10f10f2cbd405 languageName: node linkType: hard @@ -3108,10 +3090,10 @@ __metadata: languageName: node linkType: hard -"@vue/shared@npm:3.2.45": - version: 3.2.45 - resolution: "@vue/shared@npm:3.2.45" - checksum: ff3205056caed2a965aa0980e21319515ce13c859a9b269fdab0ee8b3c9f3d8eec7eefdb7fd6c6b47c12acdc7bf23c6c187b6191054221b4a29108139b20c221 +"@vue/shared@npm:3.2.47": + version: 3.2.47 + resolution: "@vue/shared@npm:3.2.47" + checksum: 0aa711dc9160fa0e476e6e94eea4e019398adf2211352d0e4a672cfb6b65b104bbd5d234807d1c091107bdc0f5d818d0f12378987eb7861d39be3aa9f6cd6e3e languageName: node linkType: hard @@ -3572,6 +3554,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.8.2": + version: 8.8.2 + resolution: "acorn@npm:8.8.2" + bin: + acorn: bin/acorn + checksum: f790b99a1bf63ef160c967e23c46feea7787e531292bb827126334612c234ed489a0dc2c7ba33156416f0ffa8d25bf2b0fdb7f35c2ba60eb3e960572bece4001 + languageName: node + linkType: hard + "agent-base@npm:5": version: 5.1.1 resolution: "agent-base@npm:5.1.1" @@ -7630,7 +7621,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.11": +"fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.12": version: 3.2.12 resolution: "fast-glob@npm:3.2.12" dependencies: @@ -10345,6 +10336,15 @@ __metadata: languageName: node linkType: hard +"json5@npm:^2.2.3": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 2a7436a93393830bce797d4626275152e37e877b265e94ca69c99e3d20c2b9dab021279146a39cdb700e71b2dd32a4cebd1514cd57cee102b1af906ce5040349 + languageName: node + linkType: hard + "jsonc-eslint-parser@npm:^1.0.1": version: 1.4.1 resolution: "jsonc-eslint-parser@npm:1.4.1" @@ -15222,15 +15222,15 @@ __metadata: languageName: node linkType: hard -"unplugin@npm:^1.0.0": - version: 1.0.0 - resolution: "unplugin@npm:1.0.0" +"unplugin@npm:^1.1.0": + version: 1.1.0 + resolution: "unplugin@npm:1.1.0" dependencies: - acorn: ^8.8.1 + acorn: ^8.8.2 chokidar: ^3.5.3 webpack-sources: ^3.2.3 - webpack-virtual-modules: ^0.4.6 - checksum: eaaaf738d8707f2965702eb4a196731516e115ea5bee8fcc75f7a4538266eb0e5e727379db3cb55e4b0fd7d4bdbc4fc957b79bc3b757f2607bf1e84f1fd3bacd + webpack-virtual-modules: ^0.5.0 + checksum: 85b2916e038a3b30943e7cdd6b454999817ad19a45ccd5fa7eaa5e85dc2f6ad8f04cf2296d5cb240c7a6258829ad75501d9238e387c4512d14208ab7a213f805 languageName: node linkType: hard @@ -15848,10 +15848,10 @@ __metadata: languageName: node linkType: hard -"webpack-virtual-modules@npm:^0.4.6": - version: 0.4.6 - resolution: "webpack-virtual-modules@npm:0.4.6" - checksum: cb056ba8c50b35436ae43149554b051b80065b0cf79f2d528ca692ddf344a422ac71c415adb9da83dc3acc6e7e58f518388cc1cd11cb4fa29dc04f2c4494afe3 +"webpack-virtual-modules@npm:^0.5.0": + version: 0.5.0 + resolution: "webpack-virtual-modules@npm:0.5.0" + checksum: 22b59257b55c89d11ae295b588b683ee9fdf3aeb591bc7b6f88ac1d69cb63f4fcb507666ea986866dfae161a1fa534ad6fb4e2ea91bbcd0a6d454368d7d4c64b languageName: node linkType: hard