Skip to content

Commit

Permalink
feat: configurable generated file and directory paths (#3235)
Browse files Browse the repository at this point in the history
  • Loading branch information
BobbieGoede authored Nov 18, 2024
1 parent 04553ae commit abc3d7b
Show file tree
Hide file tree
Showing 9 changed files with 661 additions and 196 deletions.
11 changes: 11 additions & 0 deletions docs/content/docs/4.api/0.options.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,17 @@ This feature relies on [Nuxt's `experimental.typedRoutes`](https://nuxt.com/docs
- default: `false`{lang="ts"}
- Generate `vue-i18n` and message types used in translation functions and `vue-i18n` configuration. Can be configured to use the `defaultLocale` (better performance) or all locales for type generation.

### `generatedLocaleFilePathFormat`

- type: `'absolute' | 'relative'`{lang="ts-type"}
- `'absolute'`{lang="ts-type"} - locale file and langDir paths contain the full absolute path
- `'relative'`{lang="ts-type"} - locale file and langDir paths are converted to be relative to the `rootDir`
- default: `'absolute'`{lang="ts"}
- 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.

::callout{icon="i-heroicons-exclamation-triangle" color="amber"}
Changing this will also change the paths in `locales` returned by `useI18n()`{lang="ts"}.
::

## customBlocks

Expand Down
3 changes: 2 additions & 1 deletion playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export default defineNuxtConfig({
switchLocalePathLinkSSR: true,
autoImportTranslationFunctions: true,
typedPages: true,
typedOptionsAndMessages: 'default'
typedOptionsAndMessages: 'default',
generatedLocaleFilePathFormat: 'relative'
},
compilation: {
strictMessage: false,
Expand Down
3 changes: 2 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ export const DEFAULT_OPTIONS = {
switchLocalePathLinkSSR: false,
autoImportTranslationFunctions: false,
typedPages: true,
typedOptionsAndMessages: false
typedOptionsAndMessages: false,
generatedLocaleFilePathFormat: 'absolute'
},
bundle: {
compositionOnly: true,
Expand Down
50 changes: 45 additions & 5 deletions src/gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import { getLayerI18n, getLocalePaths, getNormalizedLocales, toCode } from './ut

import type { Nuxt } from '@nuxt/schema'
import type { PrerenderTarget } from './utils'
import type { NuxtI18nOptions, LocaleInfo, VueI18nConfigPathInfo, FileMeta, LocaleObject } from './types'
import type { NuxtI18nOptions, LocaleInfo, VueI18nConfigPathInfo, FileMeta, LocaleObject, LocaleFile } from './types'
import type { Locale } from 'vue-i18n'

export type LoaderOptions = {
vueI18nConfigPaths: Required<VueI18nConfigPathInfo>[]
localeInfo: LocaleInfo[]
nuxtI18nOptions: NuxtI18nOptions
isServer: boolean
normalizedLocales: LocaleObject<string>[]
}

const debug = createDebug('@nuxtjs/i18n:gen')
Expand All @@ -31,14 +32,18 @@ const generateVueI18nConfiguration = (config: Required<VueI18nConfigPathInfo>, i
)
}

export function simplifyLocaleOptions(nuxt: Nuxt, options: NuxtI18nOptions) {
export function simplifyLocaleOptions(
nuxt: Nuxt,
options: Pick<NuxtI18nOptions, 'locales' | 'experimental' | 'i18nModules'>
) {
const isLocaleObjectsArray = (locales?: Locale[] | LocaleObject[]) => locales?.some(x => typeof x !== 'string')

const hasLocaleObjects =
nuxt.options._layers.some(layer => isLocaleObjectsArray(getLayerI18n(layer)?.locales)) ||
options?.i18nModules?.some(module => isLocaleObjectsArray(module?.locales))

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

return locales.map(({ meta, ...locale }) => {
if (!hasLocaleObjects) {
Expand All @@ -47,6 +52,10 @@ export function simplifyLocaleOptions(nuxt: Nuxt, options: NuxtI18nOptions) {

if (locale.file || (locale.files?.length ?? 0) > 0) {
locale.files = getLocalePaths(locale)

if (pathFormat === 'relative') {
locale.files = locale.files.map(x => relative(nuxt.options.rootDir, x))
}
} else {
delete locale.files
}
Expand All @@ -58,7 +67,7 @@ export function simplifyLocaleOptions(nuxt: Nuxt, options: NuxtI18nOptions) {

export function generateLoaderOptions(
nuxt: Nuxt,
{ nuxtI18nOptions, vueI18nConfigPaths, localeInfo, isServer }: LoaderOptions
{ nuxtI18nOptions, vueI18nConfigPaths, localeInfo, isServer, normalizedLocales }: LoaderOptions
) {
debug('generateLoaderOptions: lazy', nuxtI18nOptions.lazy)

Expand Down Expand Up @@ -100,18 +109,49 @@ export function generateLoaderOptions(
.map(config => generateVueI18nConfiguration(config, isServer))

const localeLoaders = localeInfo.map(locale => [locale.code, locale.meta?.map(meta => importMapper.get(meta.key))])
const pathFormat = nuxtI18nOptions.experimental?.generatedLocaleFilePathFormat ?? 'absolute'

const generatedNuxtI18nOptions = {
...nuxtI18nOptions,
locales: simplifyLocaleOptions(nuxt, nuxtI18nOptions)
locales: simplifyLocaleOptions(nuxt, nuxtI18nOptions),
i18nModules:
nuxtI18nOptions.i18nModules?.map(x => {
if (pathFormat === 'absolute') return x
if (x.langDir == null) return x
return {
...x,
langDir: relative(nuxt.options.rootDir, x.langDir)
}
}) ?? []
}
delete nuxtI18nOptions.vueI18n

/**
* Process locale file paths in `normalizedLocales`
*/
const processedNormalizedLocales = normalizedLocales.map(x => {
if (pathFormat === 'absolute') return x
if (x.files == null) return x

return {
...x,
files: x.files.map(f => {
if (typeof f === 'string') return relative(nuxt.options.rootDir, f)

return {
...f,
path: relative(nuxt.options.rootDir, f.path)
}
}) as string[] | LocaleFile[]
}
})

const generated = {
importStrings,
localeLoaders,
nuxtI18nOptions: generatedNuxtI18nOptions,
vueI18nConfigs: vueI18nConfigImports
vueI18nConfigs: vueI18nConfigImports,
normalizedLocales: processedNormalizedLocales
}

debug('generate code', generated)
Expand Down
4 changes: 2 additions & 2 deletions src/prepare/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ export function prepareRuntime(ctx: I18nNuxtContext, nuxt: Nuxt) {
vueI18nConfigPaths,
localeInfo,
nuxtI18nOptions,
isServer
isServer,
normalizedLocales
}),
localeCodes,
normalizedLocales,
dev,
isSSG,
parallelPlugin: options.parallelPlugin
Expand Down
10 changes: 10 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@ export interface ExperimentalFeatures {
* @remark `'all'` to generate types based on all locales
*/
typedOptionsAndMessages?: false | 'default' | 'all'

/**
* Locale file and langDir paths can be formatted differently to prevent exposing sensitive paths in production.
*
* @defaultValue `'absolute'`
*
* @remark `'absolute'` locale file and langDir paths contain the full absolute path
* @remark `'relative'` locale file and langDir paths are converted to be relative to the `rootDir`
*/
generatedLocaleFilePathFormat?: 'absolute' | 'relative'
}

export interface BundleOptions
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ export const getLocaleFiles = (locale: LocaleObject | LocaleInfo): LocaleFile[]
return []
}

function resolveRelativeLocales(locale: LocaleObject, config: LocaleConfig) {
export function resolveRelativeLocales(locale: LocaleObject, config: LocaleConfig) {
const fileEntries = getLocaleFiles(locale)

return fileEntries.map(file => ({
Expand Down
Loading

0 comments on commit abc3d7b

Please sign in to comment.