Skip to content

Commit e8e8d15

Browse files
authored
feat: support 'off' option for experimental.generatedLocaleFilePathFormat (#3508)
1 parent 8b2c650 commit e8e8d15

5 files changed

Lines changed: 193 additions & 34 deletions

File tree

docs/content/docs/04.api/00.options.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ This feature relies on [Nuxt's `experimental.typedRoutes`](https://nuxt.com/docs
500500
- type: `'absolute' | 'relative'`{lang="ts-type"}
501501
- `'absolute'`{lang="ts-type"} - locale file and langDir paths contain the full absolute path
502502
- `'relative'`{lang="ts-type"} - locale file and langDir paths are converted to be relative to the `rootDir`
503+
- `'off'`{lang="ts-type"} - locale file and langDir paths are used at build-time and stripped from the final build - this will be the default in v10 and will fully replace the absolute and relative configs
503504
- default: `'absolute'`{lang="ts"}
504505
- This changes the generated locale file and langDir paths, these paths are absolute by default in v9 (and lower) and could expose sensitive path information to production.
505506

src/gen.ts

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,26 @@ import { getLayerI18n, getLocaleFiles } from './utils'
77
import { asI18nVirtual } from './transform/utils'
88

99
import type { Nuxt } from '@nuxt/schema'
10-
import type { NuxtI18nOptions, LocaleInfo, LocaleObject, LocaleFile } from './types'
10+
import type { NuxtI18nOptions, LocaleInfo, LocaleObject, ExperimentalFeatures } from './types'
1111
import type { Locale } from 'vue-i18n'
1212
import type { I18nNuxtContext } from './context'
1313

1414
const debug = createDebug('@nuxtjs/i18n:gen')
1515

16+
function formatLocaleFiles(
17+
nuxt: Nuxt,
18+
locale: LocaleObject,
19+
format: ExperimentalFeatures['generatedLocaleFilePathFormat'] = 'absolute'
20+
) {
21+
if (format == 'off') {
22+
delete locale.files
23+
} else if (format === 'relative') {
24+
locale.files = getLocaleFiles(locale).map(x => assign(x, { path: relative(nuxt.options.rootDir, x.path) }))
25+
}
26+
delete locale.file
27+
return locale
28+
}
29+
1630
export function simplifyLocaleOptions(
1731
nuxt: Nuxt,
1832
options: Pick<NuxtI18nOptions, 'locales' | 'experimental' | 'i18nModules'>
@@ -24,19 +38,10 @@ export function simplifyLocaleOptions(
2438
options?.i18nModules?.some(module => isLocaleObjectsArray(module?.locales))
2539

2640
const locales = (options.locales ?? []) as LocaleObject[]
27-
const pathFormat = options.experimental?.generatedLocaleFilePathFormat ?? 'absolute'
2841

2942
return locales.map(({ meta, ...locale }) => {
30-
if (!hasLocaleObjects) {
31-
return locale.code
32-
}
33-
34-
locale.files = getLocaleFiles(locale).map(x =>
35-
pathFormat === 'relative' ? relative(nuxt.options.rootDir, x.path) : x.path
36-
)
37-
delete locale.file
38-
39-
return locale
43+
if (!hasLocaleObjects) locale.code
44+
return formatLocaleFiles(nuxt, locale, options.experimental?.generatedLocaleFilePathFormat)
4045
})
4146
}
4247

@@ -96,13 +101,20 @@ export function generateLoaderOptions(
96101
vueI18nConfigs.push({ specifier, importer, relative: config.meta.loadPath })
97102
}
98103

99-
const pathFormat = ctx.options.experimental?.generatedLocaleFilePathFormat ?? 'absolute'
100-
104+
const pathFormat = ctx.options.experimental?.generatedLocaleFilePathFormat
101105
const nuxtI18nOptions = assign({}, ctx.options, {
102106
locales: simplifyLocaleOptions(nuxt, ctx.options),
103107
i18nModules: (ctx.options.i18nModules ?? []).map(x => {
104108
if (pathFormat === 'absolute' || x.langDir == null) return x
105-
return assign({}, x, { langDir: relative(nuxt.options.rootDir, x.langDir) })
109+
if (pathFormat === 'off') {
110+
delete x.langDir
111+
} else {
112+
x.langDir = relative(nuxt.options.rootDir, x.langDir)
113+
}
114+
x.locales = (x.locales ?? []).map(locale =>
115+
isString(locale) ? locale : formatLocaleFiles(nuxt, locale, pathFormat)
116+
) as string[] | LocaleObject[]
117+
return x
106118
})
107119
})
108120
// @ts-expect-error is required
@@ -111,18 +123,7 @@ export function generateLoaderOptions(
111123
/**
112124
* Process locale file paths in `normalizedLocales`
113125
*/
114-
const normalizedLocales = ctx.normalizedLocales.map(x => {
115-
if (pathFormat === 'absolute') return x
116-
if (x.files == null) return x
117-
118-
return {
119-
...x,
120-
files: x.files.map(f => {
121-
if (isString(f)) return relative(nuxt.options.rootDir, f)
122-
return { ...f, path: relative(nuxt.options.rootDir, f.path) }
123-
}) as string[] | LocaleFile[]
124-
}
125-
})
126+
const normalizedLocales = ctx.normalizedLocales.map(x => formatLocaleFiles(nuxt, x, pathFormat))
126127

127128
return { localeLoaders, nuxtI18nOptions, vueI18nConfigs, normalizedLocales }
128129
}

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export interface ExperimentalFeatures {
9999
* @remark `'relative'` locale file and langDir paths are converted to be relative to the `rootDir`
100100
* @default 'absolute'
101101
*/
102-
generatedLocaleFilePathFormat?: 'absolute' | 'relative'
102+
generatedLocaleFilePathFormat?: 'absolute' | 'relative' | 'off'
103103
/**
104104
* Removes non-canonical query parameters from alternate link meta tags
105105
* @default false

test/__snapshots__/gen.test.ts.snap

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,104 @@ exports[`basic 1`] = `
7979
}
8080
`;
8181

82+
exports[`files with cache configuration (off) 1`] = `
83+
{
84+
"localeLoaders": {
85+
"en": [
86+
{
87+
"cache": true,
88+
"importString": "import locale_en_46json_e9231b3b from "#nuxt-i18n/e9231b3b";",
89+
"key": ""locale_en_46json_e9231b3b"",
90+
"load": "() => import("#nuxt-i18n/e9231b3b" /* webpackChunkName: "locale_en_46json_e9231b3b" */)",
91+
"relative": "srcDir/test/locales/en.json",
92+
"specifier": "#nuxt-i18n/e9231b3b",
93+
},
94+
],
95+
"es": [
96+
{
97+
"cache": false,
98+
"importString": "import locale_es_46json_4fad0583 from "#nuxt-i18n/4fad0583";",
99+
"key": ""locale_es_46json_4fad0583"",
100+
"load": "() => import("#nuxt-i18n/4fad0583" /* webpackChunkName: "locale_es_46json_4fad0583" */)",
101+
"relative": "srcDir/test/locales/es.json",
102+
"specifier": "#nuxt-i18n/4fad0583",
103+
},
104+
],
105+
"es-AR": [
106+
{
107+
"cache": false,
108+
"importString": "import locale_es_46json_4fad0583 from "#nuxt-i18n/4fad0583";",
109+
"key": ""locale_es_46json_4fad0583"",
110+
"load": "() => import("#nuxt-i18n/4fad0583" /* webpackChunkName: "locale_es_46json_4fad0583" */)",
111+
"relative": "srcDir/test/locales/es.json",
112+
"specifier": "#nuxt-i18n/4fad0583",
113+
},
114+
{
115+
"cache": true,
116+
"importString": "import locale_es_45AR_46json_5c77b60d from "#nuxt-i18n/5c77b60d";",
117+
"key": ""locale_es_45AR_46json_5c77b60d"",
118+
"load": "() => import("#nuxt-i18n/5c77b60d" /* webpackChunkName: "locale_es_45AR_46json_5c77b60d" */)",
119+
"relative": "srcDir/test/locales/es-AR.json",
120+
"specifier": "#nuxt-i18n/5c77b60d",
121+
},
122+
],
123+
"fr": [
124+
{
125+
"cache": true,
126+
"importString": "import locale_fr_46json_5627016f from "#nuxt-i18n/5627016f";",
127+
"key": ""locale_fr_46json_5627016f"",
128+
"load": "() => import("#nuxt-i18n/5627016f" /* webpackChunkName: "locale_fr_46json_5627016f" */)",
129+
"relative": "srcDir/test/locales/fr.json",
130+
"specifier": "#nuxt-i18n/5627016f",
131+
},
132+
],
133+
"ja": [
134+
{
135+
"cache": true,
136+
"importString": "import locale_ja_46json_354e4128 from "#nuxt-i18n/354e4128";",
137+
"key": ""locale_ja_46json_354e4128"",
138+
"load": "() => import("#nuxt-i18n/354e4128" /* webpackChunkName: "locale_ja_46json_354e4128" */)",
139+
"relative": "srcDir/test/locales/ja.json",
140+
"specifier": "#nuxt-i18n/354e4128",
141+
},
142+
],
143+
},
144+
"normalizedLocales": [
145+
{
146+
"code": "en",
147+
},
148+
{
149+
"code": "ja",
150+
},
151+
{
152+
"code": "fr",
153+
},
154+
{
155+
"code": "es",
156+
},
157+
{
158+
"code": "es-AR",
159+
},
160+
],
161+
"nuxtI18nOptions": {
162+
"defaultLocale": "en",
163+
"experimental": {
164+
"generatedLocaleFilePathFormat": "off",
165+
},
166+
"i18nModules": [],
167+
"lazy": true,
168+
"locales": [],
169+
},
170+
"vueI18nConfigs": [
171+
{
172+
"importer": "() => import("#nuxt-i18n/6354e9fa" /* webpackChunkName: "config_i18n_46config_46ts_6354e9fa" */)",
173+
"relative": "/test/i18n.config.ts",
174+
"specifier": "#nuxt-i18n/6354e9fa",
175+
},
176+
],
177+
}
178+
`;
179+
82180
exports[`files with cache configuration (relative) 1`] = `
83181
{
84182
"localeLoaders": {
@@ -639,21 +737,30 @@ exports[`toCode: function (arrow) 1`] = `
639737
{
640738
"code": "en",
641739
"files": [
642-
"en.json",
740+
{
741+
"cache": true,
742+
"path": "en.json",
743+
},
643744
],
644745
"testFunc": [Function],
645746
},
646747
{
647748
"code": "ja",
648749
"files": [
649-
"ja.json",
750+
{
751+
"cache": true,
752+
"path": "ja.json",
753+
},
650754
],
651755
"testFunc": [Function],
652756
},
653757
{
654758
"code": "fr",
655759
"files": [
656-
"fr.json",
760+
{
761+
"cache": true,
762+
"path": "fr.json",
763+
},
657764
],
658765
"testFunc": [Function],
659766
},
@@ -681,21 +788,30 @@ exports[`toCode: function (named) 1`] = `
681788
{
682789
"code": "en",
683790
"files": [
684-
"en.json",
791+
{
792+
"cache": true,
793+
"path": "en.json",
794+
},
685795
],
686796
"testFunc": [Function],
687797
},
688798
{
689799
"code": "ja",
690800
"files": [
691-
"ja.json",
801+
{
802+
"cache": true,
803+
"path": "ja.json",
804+
},
692805
],
693806
"testFunc": [Function],
694807
},
695808
{
696809
"code": "fr",
697810
"files": [
698-
"fr.json",
811+
{
812+
"cache": true,
813+
"path": "fr.json",
814+
},
699815
],
700816
"testFunc": [Function],
701817
},

test/gen.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,47 @@ test('files with cache configuration (relative)', async () => {
239239
expect(code).toMatchSnapshot()
240240
})
241241

242+
test('files with cache configuration (off)', async () => {
243+
const locales = getMockLocales([
244+
{
245+
code: 'es',
246+
files: [{ path: 'es.json', cache: false }]
247+
},
248+
{
249+
code: 'es-AR',
250+
files: [
251+
{ path: 'es.json', cache: false },
252+
{ path: 'es-AR.json', cache: true }
253+
]
254+
}
255+
])
256+
257+
for (const l of locales) {
258+
// @ts-ignore
259+
l.files = resolveRelativeLocales(l, { langDir: 'locales' })
260+
}
261+
const localeInfo = await resolveLocales('srcDir', locales, '.nuxt')
262+
const vueI18nConfig = await resolveVueI18nConfigInfo('/test', NUXT_I18N_VUE_I18N_CONFIG.meta.loadPath, '.nuxt')
263+
264+
const code = generateLoaderOptions(
265+
{
266+
vueI18nConfigPaths: [vueI18nConfig].filter((x): x is Required<VueI18nConfigPathInfo> => x != null),
267+
localeInfo,
268+
normalizedLocales: getNormalizedLocales(locales),
269+
options: {
270+
...NUXT_I18N_OPTIONS,
271+
lazy: true,
272+
experimental: {
273+
generatedLocaleFilePathFormat: 'off'
274+
}
275+
}
276+
},
277+
{ ...makeNuxtOptions(localeInfo), options: { ...makeNuxtOptions(localeInfo).options, rootDir: '/test' } }
278+
)
279+
280+
expect(code).toMatchSnapshot()
281+
})
282+
242283
test('locale file in nested', async () => {
243284
const locales = [
244285
{

0 commit comments

Comments
 (0)