Skip to content

Commit

Permalink
feat(unplugin-vue-i18n): exclude unused locales from the bundle (#301)
Browse files Browse the repository at this point in the history
* feat(unplugin-vue-i18n): excluding locales from bundle

* feat(unplugin-vue-i18n): excluding locales from bundle

* feat(unplugin-vue-i18n): excluding locales from bundle
  • Loading branch information
imslepov committed Sep 28, 2023
1 parent e6fbc82 commit 06dcfac
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 9 deletions.
19 changes: 19 additions & 0 deletions packages/bundle-utils/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface CodeGenOptions {
legacy?: boolean
bridge?: boolean
exportESM?: boolean
onlyLocales?: string[]
source?: string
sourceMap?: boolean
filename?: string
Expand Down Expand Up @@ -448,3 +449,21 @@ export function generateResourceAst(
const code = !occured ? `${friendlyJSONstringify(ast)}` : `\`${_msg}\``
return { code, ast, map, errors }
}

export function excludeLocales({
messages,
onlyLocales
}: {
messages: Record<string, unknown>
onlyLocales: string[]
}) {
const _messages = { ...messages }

Object.keys(_messages).forEach(locale => {
if (!onlyLocales.includes(locale)) {
delete _messages[locale]
}
})

return _messages
}
28 changes: 21 additions & 7 deletions packages/bundle-utils/src/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
createCodeGenerator,
generateMessageFunction,
generateResourceAst,
mapLinesColumns
mapLinesColumns,
excludeLocales
} from './codegen'
import { generateLegacyCode } from './legacy'
import MagicString from 'magic-string'
Expand All @@ -32,6 +33,7 @@ export function generate(
type = 'plain',
legacy = false,
bridge = false,
onlyLocales = [],
exportESM = false,
filename = 'vue-i18n-loader.json',
inSourceMap = undefined,
Expand All @@ -48,13 +50,12 @@ export function generate(
}: CodeGenOptions,
injector?: () => string
): CodeGenResult<JSONProgram> {
const target = Buffer.isBuffer(targetSource)
let value = Buffer.isBuffer(targetSource)
? targetSource.toString()
: targetSource
// const value = JSON.stringify(JSON.parse(target))
// .replace(/\u2028/g, '\\u2028') // line separator
// .replace(/\u2029/g, '\\u2029') // paragraph separator
const value = target

const options = {
type,
Expand All @@ -76,7 +77,19 @@ export function generate(
} as CodeGenOptions
const generator = createCodeGenerator(options)

const ast = parseJSON(value, { filePath: filename })
let ast = parseJSON(value, { filePath: filename })

if (!locale && type === 'sfc' && onlyLocales?.length) {
const messages = getStaticJSONValue(ast) as Record<string, unknown>

value = JSON.stringify(
excludeLocales({
messages,
onlyLocales
})
)
ast = parseJSON(value, { filePath: filename })
}

// for vue 2.x
if (legacy && type === 'sfc') {
Expand Down Expand Up @@ -104,9 +117,10 @@ export function generate(
// })
// }
// prettier-ignore
const newMap = map && !jit
? mapLinesColumns((map as any).toJSON(), codeMaps, inSourceMap) || null // eslint-disable-line @typescript-eslint/no-explicit-any
: null
const newMap =
map && !jit
? mapLinesColumns((map as any).toJSON(), codeMaps, inSourceMap) || null // eslint-disable-line @typescript-eslint/no-explicit-any
: null
return {
ast,
code,
Expand Down
18 changes: 16 additions & 2 deletions packages/bundle-utils/src/yaml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { isString, friendlyJSONstringify } from '@intlify/shared'
import {
createCodeGenerator,
excludeLocales,
generateMessageFunction,
generateResourceAst,
mapLinesColumns
Expand Down Expand Up @@ -35,6 +36,7 @@ export function generate(
type = 'plain',
legacy = false,
bridge = false,
onlyLocales = [],
exportESM = false,
useClassComponent = false,
filename = 'vue-i18n-loader.yaml',
Expand All @@ -51,7 +53,7 @@ export function generate(
}: CodeGenOptions,
injector?: () => string
): CodeGenResult<YAMLProgram> {
const value = Buffer.isBuffer(targetSource)
let value = Buffer.isBuffer(targetSource)
? targetSource.toString()
: targetSource

Expand All @@ -75,7 +77,19 @@ export function generate(
} as CodeGenOptions
const generator = createCodeGenerator(options)

const ast = parseYAML(value, { filePath: filename })
let ast = parseYAML(value, { filePath: filename })

if (!locale && type === 'sfc' && onlyLocales?.length) {
const messages = getStaticYAMLValue(ast) as Record<string, unknown>

value = JSON.stringify(
excludeLocales({
messages,
onlyLocales
})
)
ast = parseYAML(value, { filePath: filename })
}

// for vue 2.x
if (legacy && type === 'sfc') {
Expand Down
7 changes: 7 additions & 0 deletions packages/unplugin-vue-i18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,13 @@ If you enable this option, **you should check resources in your application are

This option that to use i18n custom blocks in `vue-class-component`.

### `onlyLocales`

- **Type:** `string | string[]`
- **Default:** `[]`

By using it you can exclude from the bundle those localizations that are not specified in this option.

### `useVueI18nImportName` (Experimental)

- **Type:** `boolean`
Expand Down
17 changes: 17 additions & 0 deletions packages/unplugin-vue-i18n/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ export const unplugin = createUnplugin<PluginOptions>((options = {}, meta) => {
raiseError(`This plugin is supported 'vite' and 'webpack' only`)
}

// normalize for `options.onlyLocales`
let onlyLocales: string[] = []
if (options.onlyLocales) {
onlyLocales = Array.isArray(options.onlyLocales)
? options.onlyLocales
: [options.onlyLocales]
}

// normalize for `options.include`
let include = options.include
let exclude = null
Expand Down Expand Up @@ -328,6 +336,7 @@ export const unplugin = createUnplugin<PluginOptions>((options = {}, meta) => {
escapeHtml,
bridge,
jit: jitCompilation,
onlyLocales,
exportESM: esm,
forceStringify
}
Expand Down Expand Up @@ -544,6 +553,7 @@ export const unplugin = createUnplugin<PluginOptions>((options = {}, meta) => {
escapeHtml,
bridge,
jit: jitCompilation,
onlyLocales,
exportESM: esm,
forceStringify
}
Expand Down Expand Up @@ -606,6 +616,7 @@ export const unplugin = createUnplugin<PluginOptions>((options = {}, meta) => {
jit: jitCompilation,
strictMessage,
escapeHtml,
onlyLocales,
exportESM: esm,
forceStringify
}
Expand Down Expand Up @@ -699,6 +710,7 @@ async function generateBundleResources(
forceStringify = false,
isGlobal = false,
bridge = false,
onlyLocales = [],
exportESM = true,
strictMessage = true,
escapeHtml = false,
Expand All @@ -708,6 +720,7 @@ async function generateBundleResources(
forceStringify?: boolean
isGlobal?: boolean
bridge?: boolean
onlyLocales?: string[]
exportESM?: boolean
strictMessage?: boolean
escapeHtml?: boolean
Expand All @@ -728,6 +741,7 @@ async function generateBundleResources(
useClassComponent,
bridge,
jit,
onlyLocales,
exportESM,
strictMessage,
escapeHtml,
Expand Down Expand Up @@ -806,6 +820,7 @@ function getOptions(
forceStringify = false,
isGlobal = false,
bridge = false,
onlyLocales = [],
exportESM = true,
useClassComponent = false,
allowDynamic = false,
Expand All @@ -817,6 +832,7 @@ function getOptions(
forceStringify?: boolean
isGlobal?: boolean
bridge?: boolean
onlyLocales?: string[]
exportESM?: boolean
useClassComponent?: boolean
allowDynamic?: boolean
Expand All @@ -838,6 +854,7 @@ function getOptions(
escapeHtml,
bridge,
jit,
onlyLocales,
exportESM,
env: mode,
onWarn: (msg: string): void => {
Expand Down
1 change: 1 addition & 0 deletions packages/unplugin-vue-i18n/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type SFCLangFormat = 'json' | 'json5' | 'yml' | 'yaml'
export interface PluginOptions {
include?: string | string[]
onlyLocales?: string | string[]
allowDynamic?: boolean
jitCompilation?: boolean
dropMessageCompiler?: boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ exports[`jitCompilation 1`] = `
]
`;

exports[`json: exclude locales 1`] = `
[
{
"locale": "",
"resource": {},
},
]
`;

exports[`json5 1`] = `
[
{
Expand All @@ -187,6 +196,15 @@ exports[`json5 1`] = `
]
`;

exports[`json5: exclude locales 1`] = `
[
{
"locale": "",
"resource": {},
},
]
`;

exports[`locale attr 1`] = `
[
{
Expand Down Expand Up @@ -278,3 +296,20 @@ exports[`yaml 1`] = `
},
]
`;

exports[`yaml: exclude locales 1`] = `
[
{
"locale": "en",
"resource": {
"hello": [Function],
},
},
{
"locale": "ja",
"resource": {
"hello": [Function],
},
},
]
`;
27 changes: 27 additions & 0 deletions packages/unplugin-vue-i18n/test/vite/custom-block.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@ test('basic', async () => {
expect(i18n.resource.en.hello(createMessageContext())).toEqual('hello world!')
})

test('json: exclude locales', async () => {
const { module } = await bundleAndRun('basic.vue', bundleVite, {
onlyLocales: ['ja']
})
expect(module.__i18n).toMatchSnapshot()
const i18n = module.__i18n.pop()
expect(i18n.resource.en).toBeUndefined()
})

test('yaml: exclude locales', async () => {
const { module } = await bundleAndRun('yaml.vue', bundleVite, {
onlyLocales: ['ja']
})
expect(module.__i18n).toMatchSnapshot()
const i18n = module.__i18n.pop()
expect(i18n.resource.en).toBeUndefined()
})

test('json5: exclude locales', async () => {
const { module } = await bundleAndRun('json5.vue', bundleVite, {
onlyLocales: ['ja']
})
expect(module.__i18n).toMatchSnapshot()
const i18n = module.__i18n.pop()
expect(i18n.resource.en).toBeUndefined()
})

test('yaml', async () => {
const { module } = await bundleAndRun('yaml.vue', bundleVite)
expect(module.__i18n).toMatchSnapshot()
Expand Down

0 comments on commit 06dcfac

Please sign in to comment.