@verbaly/next 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/server.js.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -29,9 +29,7 @@ pnpm add verbaly @verbaly/next @verbaly/react
|
|
|
29
29
|
// next.config.ts
|
|
30
30
|
import { withVerbaly } from '@verbaly/next';
|
|
31
31
|
|
|
32
|
-
export default withVerbaly({
|
|
33
|
-
/* your Next config */
|
|
34
|
-
});
|
|
32
|
+
export default withVerbaly({/* your Next config */});
|
|
35
33
|
```
|
|
36
34
|
|
|
37
35
|
Locales live in your `verbaly.config` (created by `npx verbaly init`), or inline:
|
|
@@ -101,11 +99,11 @@ That's it. `next dev` extracts your messages live (catalogs + `verbaly.d.ts` sta
|
|
|
101
99
|
|
|
102
100
|
Second argument of `withVerbaly`: every [`verbaly.config`](https://www.npmjs.com/package/@verbaly/compiler) option, plus:
|
|
103
101
|
|
|
104
|
-
| Option
|
|
105
|
-
|
|
|
106
|
-
| `cookie`
|
|
107
|
-
| `fallback`
|
|
108
|
-
| `failOnMissing` | `false` opts out of the build gate.
|
|
102
|
+
| Option | What it does |
|
|
103
|
+
| --------------- | ------------------------------------------------------------------------------------------------------- |
|
|
104
|
+
| `cookie` | Cookie read/written for the user's choice (default `verbaly-locale`); `false` = `Accept-Language` only. |
|
|
105
|
+
| `fallback` | Locale when nothing matches (defaults to the source locale). |
|
|
106
|
+
| `failOnMissing` | `false` opts out of the build gate. |
|
|
109
107
|
|
|
110
108
|
- Negotiation reads `headers()`/`cookies()`, so negotiated routes render dynamically; for fully static output use [`verbaly render`](https://www.npmjs.com/package/@verbaly/compiler) per locale.
|
|
111
109
|
- The generated `.verbaly/` directory is build output (it ships its own `.gitignore`).
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/codegen.ts","../src/watch.ts","../src/index.ts"],"sourcesContent":["import type { Catalogs, ResolvedConfig } from '@verbaly/compiler';\nimport { mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type Compiler = typeof import('@verbaly/compiler');\n\nexport interface RequestOptions {\n cookie?: string | false;\n fallback?: string;\n}\n\nexport const GENERATED_DIR = '.verbaly';\n\nexport function generatedDir(root: string): string {\n return join(root, GENERATED_DIR);\n}\n\n// content compare: identical rewrites must not retrigger the bundler\nfunction writeIfChanged(file: string, content: string): boolean {\n try {\n if (readFileSync(file, 'utf8') === content) return false;\n } catch {\n // new file\n }\n writeFileSync(file, content);\n return true;\n}\n\n// dev-phase pipeline shared by withVerbaly and the watcher: sync catalogs to\n// the sources, refresh verbaly.d.ts, regenerate the runtime modules\nexport function syncAndWrite(\n compiler: Compiler,\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: Awaited<ReturnType<Compiler['extractProject']>>,\n requestOptions: RequestOptions,\n): void {\n const { added } = compiler.syncCatalogs(cfg, catalogs, registry);\n for (const locale of Object.keys(added)) {\n compiler.writeCatalog(cfg, locale, catalogs[locale] ?? {});\n }\n compiler.writeDts(cfg, catalogs[cfg.sourceLocale] ?? {});\n writeGeneratedModules(compiler, cfg, catalogs, requestOptions);\n}\n\n// real-file replacement for virtual:verbaly: Turbopack has no virtual modules\nexport function writeGeneratedModules(\n compiler: Compiler,\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n requestOptions: RequestOptions = {},\n): boolean {\n const dir = generatedDir(cfg.root);\n const localeDir = join(dir, 'locale');\n mkdirSync(localeDir, { recursive: true });\n\n let changed = writeIfChanged(join(dir, '.gitignore'), '*\\n');\n\n const runtime = compiler.generateRuntimeModule(cfg, {\n localeImport: (locale) => `./locale/${locale}.js`,\n extraExports: `export const requestOptions = ${JSON.stringify(requestOptions)};\\n`,\n });\n changed = writeIfChanged(join(dir, 'index.js'), runtime) || changed;\n\n const expected = new Set(cfg.locales.map((locale) => `${locale}.js`));\n for (const locale of cfg.locales) {\n changed =\n writeIfChanged(\n join(localeDir, `${locale}.js`),\n compiler.generateLocaleModule(catalogs[locale] ?? {}),\n ) || changed;\n }\n for (const file of readdirSync(localeDir)) {\n if (!expected.has(file)) {\n rmSync(join(localeDir, file));\n changed = true;\n }\n }\n return changed;\n}\n","import type { ResolvedConfig } from '@verbaly/compiler';\nimport { watch } from 'node:fs';\nimport { relative } from 'node:path';\nimport { GENERATED_DIR, syncAndWrite, type Compiler, type RequestOptions } from './codegen';\n\n// one watcher per project root: next.config can be evaluated more than once\nconst active = new Map<string, () => void>();\n\nexport function startWatcher(\n compiler: Compiler,\n cfg: ResolvedConfig,\n requestOptions: RequestOptions,\n): () => void {\n const existing = active.get(cfg.root);\n if (existing) return existing;\n\n const catalogDir = relative(cfg.root, cfg.dir).replaceAll('\\\\', '/');\n let timer: ReturnType<typeof setTimeout> | undefined;\n let running = false;\n let queued = false;\n\n async function refresh(): Promise<void> {\n if (running) {\n queued = true;\n return;\n }\n running = true;\n try {\n const catalogs = compiler.loadCatalogs(cfg);\n const registry = await compiler.extractProject(cfg);\n syncAndWrite(compiler, cfg, catalogs, registry, requestOptions);\n } catch (error) {\n console.warn('[verbaly] live extraction failed:', error);\n } finally {\n running = false;\n if (queued) {\n queued = false;\n schedule();\n }\n }\n }\n\n function schedule(): void {\n clearTimeout(timer);\n timer = setTimeout(() => void refresh(), 150);\n }\n\n const watcher = watch(cfg.root, { recursive: true }, (_event, filename) => {\n if (!filename) return;\n const file = filename.replaceAll('\\\\', '/');\n if (\n file.startsWith(`${GENERATED_DIR}/`) ||\n file.startsWith('.next/') ||\n file.includes('node_modules/') ||\n file.endsWith('.d.ts')\n ) {\n return;\n }\n const isCatalog = file.startsWith(`${catalogDir}/`) && file.endsWith('.json');\n if (isCatalog || compiler.SOURCE_FILE_RE.test(file)) schedule();\n });\n watcher.unref?.();\n\n const dispose = (): void => {\n clearTimeout(timer);\n watcher.close();\n active.delete(cfg.root);\n };\n active.set(cfg.root, dispose);\n return dispose;\n}\n\n// test hook\nexport function stopWatcher(root: string): void {\n active.get(root)?.();\n}\n","import type { PluginOptions } from '@verbaly/compiler';\nimport { join } from 'node:path';\nimport {\n GENERATED_DIR,\n generatedDir,\n syncAndWrite,\n writeGeneratedModules,\n type Compiler,\n type RequestOptions,\n} from './codegen';\nimport { startWatcher } from './watch';\n\nexport type { VerbalyConfig } from '@verbaly/compiler';\nexport type { RequestOptions } from './codegen';\n\nexport interface NextVerbalyOptions extends PluginOptions {\n cookie?: string | false;\n fallback?: string;\n}\n\n// structural NextConfig subset: compat asserted against next's real type in tests.\n// No index signatures: Next's real interfaces have none and a target index\n// signature would reject them.\nexport interface WebpackConfigLike {\n resolve?: { alias?: Record<string, unknown>; [key: string]: unknown };\n module?: { rules?: unknown[]; [key: string]: unknown };\n plugins?: unknown[];\n [key: string]: unknown;\n}\n\ninterface WebpackContextLike {\n webpack?: {\n NormalModuleReplacementPlugin: new (test: RegExp, resource: string) => unknown;\n };\n}\n\n// context param is `never` so Next's real webpack fn (specific context type) stays assignable\nexport type WebpackFn = (config: WebpackConfigLike, context: never) => unknown;\n\nexport interface TurbopackLike {\n resolveAlias?: Record<string, unknown>;\n rules?: Record<string, unknown>;\n}\n\nexport interface NextConfigLike {\n webpack?: WebpackFn | null;\n turbopack?: TurbopackLike;\n}\n\n// constraint is `object` (not NextConfigLike): an all-optional target would\n// reject configs sharing no keys with it (TS weak-type rule)\nexport type NextConfigInput<C extends object> =\n | C\n | ((phase: string, context: { defaultConfig?: unknown }) => C | Promise<C>);\n\n// next/constants values: literal to keep this module import-free of next\nconst DEV_PHASE = 'phase-development-server';\nconst BUILD_PHASE = 'phase-production-build';\n\nconst LOADER = '@verbaly/next/loader';\n// compiler's SOURCE_FILE_RE (inlined: the compiler is only dynamically imported here).\n// Matching happens via `condition.path`: a bare extension glob also matches Next's\n// internal App Router entry and Turbopack panics reading it as a file.\nconst SOURCE_PATH_RE = /\\.[cm]?[jt]sx?$/;\n\nexport function withVerbaly<C extends object>(\n nextConfig?: NextConfigInput<C>,\n options: NextVerbalyOptions = {},\n): (phase: string, context?: { defaultConfig?: unknown }) => Promise<C> {\n const { failOnMissing, cookie, fallback, ...verbalyConfig } = options;\n const requestOptions: RequestOptions = { cookie, fallback };\n\n return async (phase, context = {}) => {\n const base: C =\n typeof nextConfig === 'function' ? await nextConfig(phase, context) : (nextConfig ?? ({} as C));\n const root = verbalyConfig.root ?? process.cwd();\n\n // production server / export: everything is bundled, so no FS work, config only\n if (phase !== DEV_PHASE && phase !== BUILD_PHASE) {\n return composeConfig(base, root);\n }\n\n // dynamic: the compiler is ESM-only and this entry is also consumed as CJS\n const compiler: Compiler = await import('@verbaly/compiler');\n\n const cfg = await compiler.loadConfig(root, verbalyConfig);\n const catalogs = compiler.loadCatalogs(cfg);\n const registry = await compiler.extractProject(cfg);\n\n if (phase === BUILD_PHASE) {\n compiler.runBuildGate(cfg, registry, failOnMissing);\n writeGeneratedModules(compiler, cfg, catalogs, requestOptions);\n } else {\n syncAndWrite(compiler, cfg, catalogs, registry, requestOptions);\n startWatcher(compiler, cfg, requestOptions);\n }\n\n return composeConfig(base, cfg.root);\n };\n}\n\nfunction composeConfig<C extends object>(base: C, root: string): C {\n const runtimeModule = join(generatedDir(root), 'index.js');\n const { webpack: userWebpack, turbopack } = base as NextConfigLike;\n\n const rules: Record<string, unknown> = { ...turbopack?.rules };\n const verbalyRule = {\n condition: { all: [{ not: 'foreign' }, { path: SOURCE_PATH_RE }] },\n loaders: [LOADER],\n };\n const existing = rules['*'];\n rules['*'] = existing\n ? [...(Array.isArray(existing) ? existing : [existing]), verbalyRule]\n : verbalyRule;\n\n return {\n ...base,\n turbopack: {\n ...turbopack,\n resolveAlias: {\n ...turbopack?.resolveAlias,\n 'virtual:verbaly': `./${GENERATED_DIR}/index.js`,\n },\n rules,\n },\n webpack(config: WebpackConfigLike, context: unknown) {\n // webpack 5 treats 'virtual:' as a URI scheme: resolve.alias never fires,\n // module replacement does; the alias stays as a fallback for odd setups\n const { webpack: webpackInstance } = (context ?? {}) as WebpackContextLike;\n if (webpackInstance?.NormalModuleReplacementPlugin) {\n config.plugins ??= [];\n config.plugins.push(\n new webpackInstance.NormalModuleReplacementPlugin(/^virtual:verbaly$/, runtimeModule),\n );\n }\n config.resolve ??= {};\n config.resolve.alias ??= {};\n (config.resolve.alias as Record<string, unknown>)['virtual:verbaly'] = runtimeModule;\n config.module ??= {};\n config.module.rules ??= [];\n config.module.rules.push({\n test: SOURCE_PATH_RE,\n exclude: /node_modules/,\n enforce: 'pre',\n use: [{ loader: LOADER }],\n });\n return userWebpack ? userWebpack(config, context as never) : config;\n },\n } as C;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/codegen.ts","../src/watch.ts","../src/index.ts"],"sourcesContent":["import type { Catalogs, ResolvedConfig } from '@verbaly/compiler';\nimport { mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type Compiler = typeof import('@verbaly/compiler');\n\nexport interface RequestOptions {\n cookie?: string | false;\n fallback?: string;\n}\n\nexport const GENERATED_DIR = '.verbaly';\n\nexport function generatedDir(root: string): string {\n return join(root, GENERATED_DIR);\n}\n\n// content compare: identical rewrites must not retrigger the bundler\nfunction writeIfChanged(file: string, content: string): boolean {\n try {\n if (readFileSync(file, 'utf8') === content) return false;\n } catch {\n // new file\n }\n writeFileSync(file, content);\n return true;\n}\n\n// dev-phase pipeline shared by withVerbaly and the watcher: sync catalogs to\n// the sources, refresh verbaly.d.ts, regenerate the runtime modules\nexport function syncAndWrite(\n compiler: Compiler,\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: Awaited<ReturnType<Compiler['extractProject']>>,\n requestOptions: RequestOptions,\n): void {\n const { added } = compiler.syncCatalogs(cfg, catalogs, registry);\n for (const locale of Object.keys(added)) {\n compiler.writeCatalog(cfg, locale, catalogs[locale] ?? {});\n }\n compiler.writeDts(cfg, catalogs[cfg.sourceLocale] ?? {});\n writeGeneratedModules(compiler, cfg, catalogs, requestOptions);\n}\n\n// real-file replacement for virtual:verbaly: Turbopack has no virtual modules\nexport function writeGeneratedModules(\n compiler: Compiler,\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n requestOptions: RequestOptions = {},\n): boolean {\n const dir = generatedDir(cfg.root);\n const localeDir = join(dir, 'locale');\n mkdirSync(localeDir, { recursive: true });\n\n let changed = writeIfChanged(join(dir, '.gitignore'), '*\\n');\n\n const runtime = compiler.generateRuntimeModule(cfg, {\n localeImport: (locale) => `./locale/${locale}.js`,\n extraExports: `export const requestOptions = ${JSON.stringify(requestOptions)};\\n`,\n });\n changed = writeIfChanged(join(dir, 'index.js'), runtime) || changed;\n\n const expected = new Set(cfg.locales.map((locale) => `${locale}.js`));\n for (const locale of cfg.locales) {\n changed =\n writeIfChanged(\n join(localeDir, `${locale}.js`),\n compiler.generateLocaleModule(catalogs[locale] ?? {}),\n ) || changed;\n }\n for (const file of readdirSync(localeDir)) {\n if (!expected.has(file)) {\n rmSync(join(localeDir, file));\n changed = true;\n }\n }\n return changed;\n}\n","import type { ResolvedConfig } from '@verbaly/compiler';\nimport { watch } from 'node:fs';\nimport { relative } from 'node:path';\nimport { GENERATED_DIR, syncAndWrite, type Compiler, type RequestOptions } from './codegen';\n\n// one watcher per project root: next.config can be evaluated more than once\nconst active = new Map<string, () => void>();\n\nexport function startWatcher(\n compiler: Compiler,\n cfg: ResolvedConfig,\n requestOptions: RequestOptions,\n): () => void {\n const existing = active.get(cfg.root);\n if (existing) return existing;\n\n const catalogDir = relative(cfg.root, cfg.dir).replaceAll('\\\\', '/');\n let timer: ReturnType<typeof setTimeout> | undefined;\n let running = false;\n let queued = false;\n\n async function refresh(): Promise<void> {\n if (running) {\n queued = true;\n return;\n }\n running = true;\n try {\n const catalogs = compiler.loadCatalogs(cfg);\n const registry = await compiler.extractProject(cfg);\n syncAndWrite(compiler, cfg, catalogs, registry, requestOptions);\n } catch (error) {\n console.warn('[verbaly] live extraction failed:', error);\n } finally {\n running = false;\n if (queued) {\n queued = false;\n schedule();\n }\n }\n }\n\n function schedule(): void {\n clearTimeout(timer);\n timer = setTimeout(() => void refresh(), 150);\n }\n\n const watcher = watch(cfg.root, { recursive: true }, (_event, filename) => {\n if (!filename) return;\n const file = filename.replaceAll('\\\\', '/');\n if (\n file.startsWith(`${GENERATED_DIR}/`) ||\n file.startsWith('.next/') ||\n file.includes('node_modules/') ||\n file.endsWith('.d.ts')\n ) {\n return;\n }\n const isCatalog = file.startsWith(`${catalogDir}/`) && file.endsWith('.json');\n if (isCatalog || compiler.SOURCE_FILE_RE.test(file)) schedule();\n });\n watcher.unref?.();\n\n const dispose = (): void => {\n clearTimeout(timer);\n watcher.close();\n active.delete(cfg.root);\n };\n active.set(cfg.root, dispose);\n return dispose;\n}\n\n// test hook\nexport function stopWatcher(root: string): void {\n active.get(root)?.();\n}\n","import type { PluginOptions } from '@verbaly/compiler';\nimport { join } from 'node:path';\nimport {\n GENERATED_DIR,\n generatedDir,\n syncAndWrite,\n writeGeneratedModules,\n type Compiler,\n type RequestOptions,\n} from './codegen';\nimport { startWatcher } from './watch';\n\nexport type { VerbalyConfig } from '@verbaly/compiler';\nexport type { RequestOptions } from './codegen';\n\nexport interface NextVerbalyOptions extends PluginOptions {\n cookie?: string | false;\n fallback?: string;\n}\n\n// structural NextConfig subset: compat asserted against next's real type in tests.\n// No index signatures: Next's real interfaces have none and a target index\n// signature would reject them.\nexport interface WebpackConfigLike {\n resolve?: { alias?: Record<string, unknown>; [key: string]: unknown };\n module?: { rules?: unknown[]; [key: string]: unknown };\n plugins?: unknown[];\n [key: string]: unknown;\n}\n\ninterface WebpackContextLike {\n webpack?: {\n NormalModuleReplacementPlugin: new (test: RegExp, resource: string) => unknown;\n };\n}\n\n// context param is `never` so Next's real webpack fn (specific context type) stays assignable\nexport type WebpackFn = (config: WebpackConfigLike, context: never) => unknown;\n\nexport interface TurbopackLike {\n resolveAlias?: Record<string, unknown>;\n rules?: Record<string, unknown>;\n}\n\nexport interface NextConfigLike {\n webpack?: WebpackFn | null;\n turbopack?: TurbopackLike;\n}\n\n// constraint is `object` (not NextConfigLike): an all-optional target would\n// reject configs sharing no keys with it (TS weak-type rule)\nexport type NextConfigInput<C extends object> =\n C | ((phase: string, context: { defaultConfig?: unknown }) => C | Promise<C>);\n\n// next/constants values: literal to keep this module import-free of next\nconst DEV_PHASE = 'phase-development-server';\nconst BUILD_PHASE = 'phase-production-build';\n\nconst LOADER = '@verbaly/next/loader';\n// compiler's SOURCE_FILE_RE (inlined: the compiler is only dynamically imported here).\n// Matching happens via `condition.path`: a bare extension glob also matches Next's\n// internal App Router entry and Turbopack panics reading it as a file.\nconst SOURCE_PATH_RE = /\\.[cm]?[jt]sx?$/;\n\nexport function withVerbaly<C extends object>(\n nextConfig?: NextConfigInput<C>,\n options: NextVerbalyOptions = {},\n): (phase: string, context?: { defaultConfig?: unknown }) => Promise<C> {\n const { failOnMissing, cookie, fallback, ...verbalyConfig } = options;\n const requestOptions: RequestOptions = { cookie, fallback };\n\n return async (phase, context = {}) => {\n const base: C =\n typeof nextConfig === 'function'\n ? await nextConfig(phase, context)\n : (nextConfig ?? ({} as C));\n const root = verbalyConfig.root ?? process.cwd();\n\n // production server / export: everything is bundled, so no FS work, config only\n if (phase !== DEV_PHASE && phase !== BUILD_PHASE) {\n return composeConfig(base, root);\n }\n\n // dynamic: the compiler is ESM-only and this entry is also consumed as CJS\n const compiler: Compiler = await import('@verbaly/compiler');\n\n const cfg = await compiler.loadConfig(root, verbalyConfig);\n const catalogs = compiler.loadCatalogs(cfg);\n const registry = await compiler.extractProject(cfg);\n\n if (phase === BUILD_PHASE) {\n compiler.runBuildGate(cfg, registry, failOnMissing);\n writeGeneratedModules(compiler, cfg, catalogs, requestOptions);\n } else {\n syncAndWrite(compiler, cfg, catalogs, registry, requestOptions);\n startWatcher(compiler, cfg, requestOptions);\n }\n\n return composeConfig(base, cfg.root);\n };\n}\n\nfunction composeConfig<C extends object>(base: C, root: string): C {\n const runtimeModule = join(generatedDir(root), 'index.js');\n const { webpack: userWebpack, turbopack } = base as NextConfigLike;\n\n const rules: Record<string, unknown> = { ...turbopack?.rules };\n const verbalyRule = {\n condition: { all: [{ not: 'foreign' }, { path: SOURCE_PATH_RE }] },\n loaders: [LOADER],\n };\n const existing = rules['*'];\n rules['*'] = existing\n ? [...(Array.isArray(existing) ? existing : [existing]), verbalyRule]\n : verbalyRule;\n\n return {\n ...base,\n turbopack: {\n ...turbopack,\n resolveAlias: {\n ...turbopack?.resolveAlias,\n 'virtual:verbaly': `./${GENERATED_DIR}/index.js`,\n },\n rules,\n },\n webpack(config: WebpackConfigLike, context: unknown) {\n // webpack 5 treats 'virtual:' as a URI scheme: resolve.alias never fires,\n // module replacement does; the alias stays as a fallback for odd setups\n const { webpack: webpackInstance } = (context ?? {}) as WebpackContextLike;\n if (webpackInstance?.NormalModuleReplacementPlugin) {\n config.plugins ??= [];\n config.plugins.push(\n new webpackInstance.NormalModuleReplacementPlugin(/^virtual:verbaly$/, runtimeModule),\n );\n }\n config.resolve ??= {};\n config.resolve.alias ??= {};\n (config.resolve.alias as Record<string, unknown>)['virtual:verbaly'] = runtimeModule;\n config.module ??= {};\n config.module.rules ??= [];\n config.module.rules.push({\n test: SOURCE_PATH_RE,\n exclude: /node_modules/,\n enforce: 'pre',\n use: [{ loader: LOADER }],\n });\n return userWebpack ? userWebpack(config, context as never) : config;\n },\n } as C;\n}\n"],"mappings":"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"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/codegen.ts","../src/watch.ts","../src/index.ts"],"sourcesContent":["import type { Catalogs, ResolvedConfig } from '@verbaly/compiler';\nimport { mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type Compiler = typeof import('@verbaly/compiler');\n\nexport interface RequestOptions {\n cookie?: string | false;\n fallback?: string;\n}\n\nexport const GENERATED_DIR = '.verbaly';\n\nexport function generatedDir(root: string): string {\n return join(root, GENERATED_DIR);\n}\n\n// content compare: identical rewrites must not retrigger the bundler\nfunction writeIfChanged(file: string, content: string): boolean {\n try {\n if (readFileSync(file, 'utf8') === content) return false;\n } catch {\n // new file\n }\n writeFileSync(file, content);\n return true;\n}\n\n// dev-phase pipeline shared by withVerbaly and the watcher: sync catalogs to\n// the sources, refresh verbaly.d.ts, regenerate the runtime modules\nexport function syncAndWrite(\n compiler: Compiler,\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: Awaited<ReturnType<Compiler['extractProject']>>,\n requestOptions: RequestOptions,\n): void {\n const { added } = compiler.syncCatalogs(cfg, catalogs, registry);\n for (const locale of Object.keys(added)) {\n compiler.writeCatalog(cfg, locale, catalogs[locale] ?? {});\n }\n compiler.writeDts(cfg, catalogs[cfg.sourceLocale] ?? {});\n writeGeneratedModules(compiler, cfg, catalogs, requestOptions);\n}\n\n// real-file replacement for virtual:verbaly: Turbopack has no virtual modules\nexport function writeGeneratedModules(\n compiler: Compiler,\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n requestOptions: RequestOptions = {},\n): boolean {\n const dir = generatedDir(cfg.root);\n const localeDir = join(dir, 'locale');\n mkdirSync(localeDir, { recursive: true });\n\n let changed = writeIfChanged(join(dir, '.gitignore'), '*\\n');\n\n const runtime = compiler.generateRuntimeModule(cfg, {\n localeImport: (locale) => `./locale/${locale}.js`,\n extraExports: `export const requestOptions = ${JSON.stringify(requestOptions)};\\n`,\n });\n changed = writeIfChanged(join(dir, 'index.js'), runtime) || changed;\n\n const expected = new Set(cfg.locales.map((locale) => `${locale}.js`));\n for (const locale of cfg.locales) {\n changed =\n writeIfChanged(\n join(localeDir, `${locale}.js`),\n compiler.generateLocaleModule(catalogs[locale] ?? {}),\n ) || changed;\n }\n for (const file of readdirSync(localeDir)) {\n if (!expected.has(file)) {\n rmSync(join(localeDir, file));\n changed = true;\n }\n }\n return changed;\n}\n","import type { ResolvedConfig } from '@verbaly/compiler';\nimport { watch } from 'node:fs';\nimport { relative } from 'node:path';\nimport { GENERATED_DIR, syncAndWrite, type Compiler, type RequestOptions } from './codegen';\n\n// one watcher per project root: next.config can be evaluated more than once\nconst active = new Map<string, () => void>();\n\nexport function startWatcher(\n compiler: Compiler,\n cfg: ResolvedConfig,\n requestOptions: RequestOptions,\n): () => void {\n const existing = active.get(cfg.root);\n if (existing) return existing;\n\n const catalogDir = relative(cfg.root, cfg.dir).replaceAll('\\\\', '/');\n let timer: ReturnType<typeof setTimeout> | undefined;\n let running = false;\n let queued = false;\n\n async function refresh(): Promise<void> {\n if (running) {\n queued = true;\n return;\n }\n running = true;\n try {\n const catalogs = compiler.loadCatalogs(cfg);\n const registry = await compiler.extractProject(cfg);\n syncAndWrite(compiler, cfg, catalogs, registry, requestOptions);\n } catch (error) {\n console.warn('[verbaly] live extraction failed:', error);\n } finally {\n running = false;\n if (queued) {\n queued = false;\n schedule();\n }\n }\n }\n\n function schedule(): void {\n clearTimeout(timer);\n timer = setTimeout(() => void refresh(), 150);\n }\n\n const watcher = watch(cfg.root, { recursive: true }, (_event, filename) => {\n if (!filename) return;\n const file = filename.replaceAll('\\\\', '/');\n if (\n file.startsWith(`${GENERATED_DIR}/`) ||\n file.startsWith('.next/') ||\n file.includes('node_modules/') ||\n file.endsWith('.d.ts')\n ) {\n return;\n }\n const isCatalog = file.startsWith(`${catalogDir}/`) && file.endsWith('.json');\n if (isCatalog || compiler.SOURCE_FILE_RE.test(file)) schedule();\n });\n watcher.unref?.();\n\n const dispose = (): void => {\n clearTimeout(timer);\n watcher.close();\n active.delete(cfg.root);\n };\n active.set(cfg.root, dispose);\n return dispose;\n}\n\n// test hook\nexport function stopWatcher(root: string): void {\n active.get(root)?.();\n}\n","import type { PluginOptions } from '@verbaly/compiler';\nimport { join } from 'node:path';\nimport {\n GENERATED_DIR,\n generatedDir,\n syncAndWrite,\n writeGeneratedModules,\n type Compiler,\n type RequestOptions,\n} from './codegen';\nimport { startWatcher } from './watch';\n\nexport type { VerbalyConfig } from '@verbaly/compiler';\nexport type { RequestOptions } from './codegen';\n\nexport interface NextVerbalyOptions extends PluginOptions {\n cookie?: string | false;\n fallback?: string;\n}\n\n// structural NextConfig subset: compat asserted against next's real type in tests.\n// No index signatures: Next's real interfaces have none and a target index\n// signature would reject them.\nexport interface WebpackConfigLike {\n resolve?: { alias?: Record<string, unknown>; [key: string]: unknown };\n module?: { rules?: unknown[]; [key: string]: unknown };\n plugins?: unknown[];\n [key: string]: unknown;\n}\n\ninterface WebpackContextLike {\n webpack?: {\n NormalModuleReplacementPlugin: new (test: RegExp, resource: string) => unknown;\n };\n}\n\n// context param is `never` so Next's real webpack fn (specific context type) stays assignable\nexport type WebpackFn = (config: WebpackConfigLike, context: never) => unknown;\n\nexport interface TurbopackLike {\n resolveAlias?: Record<string, unknown>;\n rules?: Record<string, unknown>;\n}\n\nexport interface NextConfigLike {\n webpack?: WebpackFn | null;\n turbopack?: TurbopackLike;\n}\n\n// constraint is `object` (not NextConfigLike): an all-optional target would\n// reject configs sharing no keys with it (TS weak-type rule)\nexport type NextConfigInput<C extends object> =\n | C\n | ((phase: string, context: { defaultConfig?: unknown }) => C | Promise<C>);\n\n// next/constants values: literal to keep this module import-free of next\nconst DEV_PHASE = 'phase-development-server';\nconst BUILD_PHASE = 'phase-production-build';\n\nconst LOADER = '@verbaly/next/loader';\n// compiler's SOURCE_FILE_RE (inlined: the compiler is only dynamically imported here).\n// Matching happens via `condition.path`: a bare extension glob also matches Next's\n// internal App Router entry and Turbopack panics reading it as a file.\nconst SOURCE_PATH_RE = /\\.[cm]?[jt]sx?$/;\n\nexport function withVerbaly<C extends object>(\n nextConfig?: NextConfigInput<C>,\n options: NextVerbalyOptions = {},\n): (phase: string, context?: { defaultConfig?: unknown }) => Promise<C> {\n const { failOnMissing, cookie, fallback, ...verbalyConfig } = options;\n const requestOptions: RequestOptions = { cookie, fallback };\n\n return async (phase, context = {}) => {\n const base: C =\n typeof nextConfig === 'function' ? await nextConfig(phase, context) : (nextConfig ?? ({} as C));\n const root = verbalyConfig.root ?? process.cwd();\n\n // production server / export: everything is bundled, so no FS work, config only\n if (phase !== DEV_PHASE && phase !== BUILD_PHASE) {\n return composeConfig(base, root);\n }\n\n // dynamic: the compiler is ESM-only and this entry is also consumed as CJS\n const compiler: Compiler = await import('@verbaly/compiler');\n\n const cfg = await compiler.loadConfig(root, verbalyConfig);\n const catalogs = compiler.loadCatalogs(cfg);\n const registry = await compiler.extractProject(cfg);\n\n if (phase === BUILD_PHASE) {\n compiler.runBuildGate(cfg, registry, failOnMissing);\n writeGeneratedModules(compiler, cfg, catalogs, requestOptions);\n } else {\n syncAndWrite(compiler, cfg, catalogs, registry, requestOptions);\n startWatcher(compiler, cfg, requestOptions);\n }\n\n return composeConfig(base, cfg.root);\n };\n}\n\nfunction composeConfig<C extends object>(base: C, root: string): C {\n const runtimeModule = join(generatedDir(root), 'index.js');\n const { webpack: userWebpack, turbopack } = base as NextConfigLike;\n\n const rules: Record<string, unknown> = { ...turbopack?.rules };\n const verbalyRule = {\n condition: { all: [{ not: 'foreign' }, { path: SOURCE_PATH_RE }] },\n loaders: [LOADER],\n };\n const existing = rules['*'];\n rules['*'] = existing\n ? [...(Array.isArray(existing) ? existing : [existing]), verbalyRule]\n : verbalyRule;\n\n return {\n ...base,\n turbopack: {\n ...turbopack,\n resolveAlias: {\n ...turbopack?.resolveAlias,\n 'virtual:verbaly': `./${GENERATED_DIR}/index.js`,\n },\n rules,\n },\n webpack(config: WebpackConfigLike, context: unknown) {\n // webpack 5 treats 'virtual:' as a URI scheme: resolve.alias never fires,\n // module replacement does; the alias stays as a fallback for odd setups\n const { webpack: webpackInstance } = (context ?? {}) as WebpackContextLike;\n if (webpackInstance?.NormalModuleReplacementPlugin) {\n config.plugins ??= [];\n config.plugins.push(\n new webpackInstance.NormalModuleReplacementPlugin(/^virtual:verbaly$/, runtimeModule),\n );\n }\n config.resolve ??= {};\n config.resolve.alias ??= {};\n (config.resolve.alias as Record<string, unknown>)['virtual:verbaly'] = runtimeModule;\n config.module ??= {};\n config.module.rules ??= [];\n config.module.rules.push({\n test: SOURCE_PATH_RE,\n exclude: /node_modules/,\n enforce: 'pre',\n use: [{ loader: LOADER }],\n });\n return userWebpack ? userWebpack(config, context as never) : config;\n },\n } as C;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/codegen.ts","../src/watch.ts","../src/index.ts"],"sourcesContent":["import type { Catalogs, ResolvedConfig } from '@verbaly/compiler';\nimport { mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type Compiler = typeof import('@verbaly/compiler');\n\nexport interface RequestOptions {\n cookie?: string | false;\n fallback?: string;\n}\n\nexport const GENERATED_DIR = '.verbaly';\n\nexport function generatedDir(root: string): string {\n return join(root, GENERATED_DIR);\n}\n\n// content compare: identical rewrites must not retrigger the bundler\nfunction writeIfChanged(file: string, content: string): boolean {\n try {\n if (readFileSync(file, 'utf8') === content) return false;\n } catch {\n // new file\n }\n writeFileSync(file, content);\n return true;\n}\n\n// dev-phase pipeline shared by withVerbaly and the watcher: sync catalogs to\n// the sources, refresh verbaly.d.ts, regenerate the runtime modules\nexport function syncAndWrite(\n compiler: Compiler,\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: Awaited<ReturnType<Compiler['extractProject']>>,\n requestOptions: RequestOptions,\n): void {\n const { added } = compiler.syncCatalogs(cfg, catalogs, registry);\n for (const locale of Object.keys(added)) {\n compiler.writeCatalog(cfg, locale, catalogs[locale] ?? {});\n }\n compiler.writeDts(cfg, catalogs[cfg.sourceLocale] ?? {});\n writeGeneratedModules(compiler, cfg, catalogs, requestOptions);\n}\n\n// real-file replacement for virtual:verbaly: Turbopack has no virtual modules\nexport function writeGeneratedModules(\n compiler: Compiler,\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n requestOptions: RequestOptions = {},\n): boolean {\n const dir = generatedDir(cfg.root);\n const localeDir = join(dir, 'locale');\n mkdirSync(localeDir, { recursive: true });\n\n let changed = writeIfChanged(join(dir, '.gitignore'), '*\\n');\n\n const runtime = compiler.generateRuntimeModule(cfg, {\n localeImport: (locale) => `./locale/${locale}.js`,\n extraExports: `export const requestOptions = ${JSON.stringify(requestOptions)};\\n`,\n });\n changed = writeIfChanged(join(dir, 'index.js'), runtime) || changed;\n\n const expected = new Set(cfg.locales.map((locale) => `${locale}.js`));\n for (const locale of cfg.locales) {\n changed =\n writeIfChanged(\n join(localeDir, `${locale}.js`),\n compiler.generateLocaleModule(catalogs[locale] ?? {}),\n ) || changed;\n }\n for (const file of readdirSync(localeDir)) {\n if (!expected.has(file)) {\n rmSync(join(localeDir, file));\n changed = true;\n }\n }\n return changed;\n}\n","import type { ResolvedConfig } from '@verbaly/compiler';\nimport { watch } from 'node:fs';\nimport { relative } from 'node:path';\nimport { GENERATED_DIR, syncAndWrite, type Compiler, type RequestOptions } from './codegen';\n\n// one watcher per project root: next.config can be evaluated more than once\nconst active = new Map<string, () => void>();\n\nexport function startWatcher(\n compiler: Compiler,\n cfg: ResolvedConfig,\n requestOptions: RequestOptions,\n): () => void {\n const existing = active.get(cfg.root);\n if (existing) return existing;\n\n const catalogDir = relative(cfg.root, cfg.dir).replaceAll('\\\\', '/');\n let timer: ReturnType<typeof setTimeout> | undefined;\n let running = false;\n let queued = false;\n\n async function refresh(): Promise<void> {\n if (running) {\n queued = true;\n return;\n }\n running = true;\n try {\n const catalogs = compiler.loadCatalogs(cfg);\n const registry = await compiler.extractProject(cfg);\n syncAndWrite(compiler, cfg, catalogs, registry, requestOptions);\n } catch (error) {\n console.warn('[verbaly] live extraction failed:', error);\n } finally {\n running = false;\n if (queued) {\n queued = false;\n schedule();\n }\n }\n }\n\n function schedule(): void {\n clearTimeout(timer);\n timer = setTimeout(() => void refresh(), 150);\n }\n\n const watcher = watch(cfg.root, { recursive: true }, (_event, filename) => {\n if (!filename) return;\n const file = filename.replaceAll('\\\\', '/');\n if (\n file.startsWith(`${GENERATED_DIR}/`) ||\n file.startsWith('.next/') ||\n file.includes('node_modules/') ||\n file.endsWith('.d.ts')\n ) {\n return;\n }\n const isCatalog = file.startsWith(`${catalogDir}/`) && file.endsWith('.json');\n if (isCatalog || compiler.SOURCE_FILE_RE.test(file)) schedule();\n });\n watcher.unref?.();\n\n const dispose = (): void => {\n clearTimeout(timer);\n watcher.close();\n active.delete(cfg.root);\n };\n active.set(cfg.root, dispose);\n return dispose;\n}\n\n// test hook\nexport function stopWatcher(root: string): void {\n active.get(root)?.();\n}\n","import type { PluginOptions } from '@verbaly/compiler';\nimport { join } from 'node:path';\nimport {\n GENERATED_DIR,\n generatedDir,\n syncAndWrite,\n writeGeneratedModules,\n type Compiler,\n type RequestOptions,\n} from './codegen';\nimport { startWatcher } from './watch';\n\nexport type { VerbalyConfig } from '@verbaly/compiler';\nexport type { RequestOptions } from './codegen';\n\nexport interface NextVerbalyOptions extends PluginOptions {\n cookie?: string | false;\n fallback?: string;\n}\n\n// structural NextConfig subset: compat asserted against next's real type in tests.\n// No index signatures: Next's real interfaces have none and a target index\n// signature would reject them.\nexport interface WebpackConfigLike {\n resolve?: { alias?: Record<string, unknown>; [key: string]: unknown };\n module?: { rules?: unknown[]; [key: string]: unknown };\n plugins?: unknown[];\n [key: string]: unknown;\n}\n\ninterface WebpackContextLike {\n webpack?: {\n NormalModuleReplacementPlugin: new (test: RegExp, resource: string) => unknown;\n };\n}\n\n// context param is `never` so Next's real webpack fn (specific context type) stays assignable\nexport type WebpackFn = (config: WebpackConfigLike, context: never) => unknown;\n\nexport interface TurbopackLike {\n resolveAlias?: Record<string, unknown>;\n rules?: Record<string, unknown>;\n}\n\nexport interface NextConfigLike {\n webpack?: WebpackFn | null;\n turbopack?: TurbopackLike;\n}\n\n// constraint is `object` (not NextConfigLike): an all-optional target would\n// reject configs sharing no keys with it (TS weak-type rule)\nexport type NextConfigInput<C extends object> =\n C | ((phase: string, context: { defaultConfig?: unknown }) => C | Promise<C>);\n\n// next/constants values: literal to keep this module import-free of next\nconst DEV_PHASE = 'phase-development-server';\nconst BUILD_PHASE = 'phase-production-build';\n\nconst LOADER = '@verbaly/next/loader';\n// compiler's SOURCE_FILE_RE (inlined: the compiler is only dynamically imported here).\n// Matching happens via `condition.path`: a bare extension glob also matches Next's\n// internal App Router entry and Turbopack panics reading it as a file.\nconst SOURCE_PATH_RE = /\\.[cm]?[jt]sx?$/;\n\nexport function withVerbaly<C extends object>(\n nextConfig?: NextConfigInput<C>,\n options: NextVerbalyOptions = {},\n): (phase: string, context?: { defaultConfig?: unknown }) => Promise<C> {\n const { failOnMissing, cookie, fallback, ...verbalyConfig } = options;\n const requestOptions: RequestOptions = { cookie, fallback };\n\n return async (phase, context = {}) => {\n const base: C =\n typeof nextConfig === 'function'\n ? await nextConfig(phase, context)\n : (nextConfig ?? ({} as C));\n const root = verbalyConfig.root ?? process.cwd();\n\n // production server / export: everything is bundled, so no FS work, config only\n if (phase !== DEV_PHASE && phase !== BUILD_PHASE) {\n return composeConfig(base, root);\n }\n\n // dynamic: the compiler is ESM-only and this entry is also consumed as CJS\n const compiler: Compiler = await import('@verbaly/compiler');\n\n const cfg = await compiler.loadConfig(root, verbalyConfig);\n const catalogs = compiler.loadCatalogs(cfg);\n const registry = await compiler.extractProject(cfg);\n\n if (phase === BUILD_PHASE) {\n compiler.runBuildGate(cfg, registry, failOnMissing);\n writeGeneratedModules(compiler, cfg, catalogs, requestOptions);\n } else {\n syncAndWrite(compiler, cfg, catalogs, registry, requestOptions);\n startWatcher(compiler, cfg, requestOptions);\n }\n\n return composeConfig(base, cfg.root);\n };\n}\n\nfunction composeConfig<C extends object>(base: C, root: string): C {\n const runtimeModule = join(generatedDir(root), 'index.js');\n const { webpack: userWebpack, turbopack } = base as NextConfigLike;\n\n const rules: Record<string, unknown> = { ...turbopack?.rules };\n const verbalyRule = {\n condition: { all: [{ not: 'foreign' }, { path: SOURCE_PATH_RE }] },\n loaders: [LOADER],\n };\n const existing = rules['*'];\n rules['*'] = existing\n ? [...(Array.isArray(existing) ? existing : [existing]), verbalyRule]\n : verbalyRule;\n\n return {\n ...base,\n turbopack: {\n ...turbopack,\n resolveAlias: {\n ...turbopack?.resolveAlias,\n 'virtual:verbaly': `./${GENERATED_DIR}/index.js`,\n },\n rules,\n },\n webpack(config: WebpackConfigLike, context: unknown) {\n // webpack 5 treats 'virtual:' as a URI scheme: resolve.alias never fires,\n // module replacement does; the alias stays as a fallback for odd setups\n const { webpack: webpackInstance } = (context ?? {}) as WebpackContextLike;\n if (webpackInstance?.NormalModuleReplacementPlugin) {\n config.plugins ??= [];\n config.plugins.push(\n new webpackInstance.NormalModuleReplacementPlugin(/^virtual:verbaly$/, runtimeModule),\n );\n }\n config.resolve ??= {};\n config.resolve.alias ??= {};\n (config.resolve.alias as Record<string, unknown>)['virtual:verbaly'] = runtimeModule;\n config.module ??= {};\n config.module.rules ??= [];\n config.module.rules.push({\n test: SOURCE_PATH_RE,\n exclude: /node_modules/,\n enforce: 'pre',\n use: [{ loader: LOADER }],\n });\n return userWebpack ? userWebpack(config, context as never) : config;\n },\n } as C;\n}\n"],"mappings":";;;AAWA,MAAa,gBAAgB;AAE7B,SAAgB,aAAa,MAAsB;CACjD,OAAO,KAAK,MAAM,aAAa;AACjC;AAGA,SAAS,eAAe,MAAc,SAA0B;CAC9D,IAAI;EACF,IAAI,aAAa,MAAM,MAAM,MAAM,SAAS,OAAO;CACrD,QAAQ,CAER;CACA,cAAc,MAAM,OAAO;CAC3B,OAAO;AACT;AAIA,SAAgB,aACd,UACA,KACA,UACA,UACA,gBACM;CACN,MAAM,EAAE,UAAU,SAAS,aAAa,KAAK,UAAU,QAAQ;CAC/D,KAAK,MAAM,UAAU,OAAO,KAAK,KAAK,GACpC,SAAS,aAAa,KAAK,QAAQ,SAAS,WAAW,CAAC,CAAC;CAE3D,SAAS,SAAS,KAAK,SAAS,IAAI,iBAAiB,CAAC,CAAC;CACvD,sBAAsB,UAAU,KAAK,UAAU,cAAc;AAC/D;AAGA,SAAgB,sBACd,UACA,KACA,UACA,iBAAiC,CAAC,GACzB;CACT,MAAM,MAAM,aAAa,IAAI,IAAI;CACjC,MAAM,YAAY,KAAK,KAAK,QAAQ;CACpC,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;CAExC,IAAI,UAAU,eAAe,KAAK,KAAK,YAAY,GAAG,KAAK;CAE3D,MAAM,UAAU,SAAS,sBAAsB,KAAK;EAClD,eAAe,WAAW,YAAY,OAAO;EAC7C,cAAc,iCAAiC,KAAK,UAAU,cAAc,EAAE;CAChF,CAAC;CACD,UAAU,eAAe,KAAK,KAAK,UAAU,GAAG,OAAO,KAAK;CAE5D,MAAM,WAAW,IAAI,IAAI,IAAI,QAAQ,KAAK,WAAW,GAAG,OAAO,IAAI,CAAC;CACpE,KAAK,MAAM,UAAU,IAAI,SACvB,UACE,eACE,KAAK,WAAW,GAAG,OAAO,IAAI,GAC9B,SAAS,qBAAqB,SAAS,WAAW,CAAC,CAAC,CACtD,KAAK;CAET,KAAK,MAAM,QAAQ,YAAY,SAAS,GACtC,IAAI,CAAC,SAAS,IAAI,IAAI,GAAG;EACvB,OAAO,KAAK,WAAW,IAAI,CAAC;EAC5B,UAAU;CACZ;CAEF,OAAO;AACT;;;ACzEA,MAAM,yBAAS,IAAI,IAAwB;AAE3C,SAAgB,aACd,UACA,KACA,gBACY;CACZ,MAAM,WAAW,OAAO,IAAI,IAAI,IAAI;CACpC,IAAI,UAAU,OAAO;CAErB,MAAM,aAAa,SAAS,IAAI,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,MAAM,GAAG;CACnE,IAAI;CACJ,IAAI,UAAU;CACd,IAAI,SAAS;CAEb,eAAe,UAAyB;EACtC,IAAI,SAAS;GACX,SAAS;GACT;EACF;EACA,UAAU;EACV,IAAI;GAGF,aAAa,UAAU,KAFN,SAAS,aAAa,GAEJ,GAAG,MADf,SAAS,eAAe,GAAG,GACF,cAAc;EAChE,SAAS,OAAO;GACd,QAAQ,KAAK,qCAAqC,KAAK;EACzD,UAAU;GACR,UAAU;GACV,IAAI,QAAQ;IACV,SAAS;IACT,SAAS;GACX;EACF;CACF;CAEA,SAAS,WAAiB;EACxB,aAAa,KAAK;EAClB,QAAQ,iBAAiB,KAAK,QAAQ,GAAG,GAAG;CAC9C;CAEA,MAAM,UAAU,MAAM,IAAI,MAAM,EAAE,WAAW,KAAK,IAAI,QAAQ,aAAa;EACzE,IAAI,CAAC,UAAU;EACf,MAAM,OAAO,SAAS,WAAW,MAAM,GAAG;EAC1C,IACE,KAAK,WAAW,WAAmB,KACnC,KAAK,WAAW,QAAQ,KACxB,KAAK,SAAS,eAAe,KAC7B,KAAK,SAAS,OAAO,GAErB;EAGF,IADkB,KAAK,WAAW,GAAG,WAAW,EAAE,KAAK,KAAK,SAAS,OAAO,KAC3D,SAAS,eAAe,KAAK,IAAI,GAAG,SAAS;CAChE,CAAC;CACD,QAAQ,QAAQ;CAEhB,MAAM,gBAAsB;EAC1B,aAAa,KAAK;EAClB,QAAQ,MAAM;EACd,OAAO,OAAO,IAAI,IAAI;CACxB;CACA,OAAO,IAAI,IAAI,MAAM,OAAO;CAC5B,OAAO;AACT;;;ACfA,MAAM,YAAY;AAClB,MAAM,cAAc;AAEpB,MAAM,SAAS;AAIf,MAAM,iBAAiB;AAEvB,SAAgB,YACd,YACA,UAA8B,CAAC,GACuC;CACtE,MAAM,EAAE,eAAe,QAAQ,UAAU,GAAG,kBAAkB;CAC9D,MAAM,iBAAiC;EAAE;EAAQ;CAAS;CAE1D,OAAO,OAAO,OAAO,UAAU,CAAC,MAAM;EACpC,MAAM,OACJ,OAAO,eAAe,aAClB,MAAM,WAAW,OAAO,OAAO,IAC9B,cAAe,CAAC;EACvB,MAAM,OAAO,cAAc,QAAQ,QAAQ,IAAI;EAG/C,IAAI,UAAU,aAAa,UAAU,aACnC,OAAO,cAAc,MAAM,IAAI;EAIjC,MAAM,WAAqB,MAAM,OAAO;EAExC,MAAM,MAAM,MAAM,SAAS,WAAW,MAAM,aAAa;EACzD,MAAM,WAAW,SAAS,aAAa,GAAG;EAC1C,MAAM,WAAW,MAAM,SAAS,eAAe,GAAG;EAElD,IAAI,UAAU,aAAa;GACzB,SAAS,aAAa,KAAK,UAAU,aAAa;GAClD,sBAAsB,UAAU,KAAK,UAAU,cAAc;EAC/D,OAAO;GACL,aAAa,UAAU,KAAK,UAAU,UAAU,cAAc;GAC9D,aAAa,UAAU,KAAK,cAAc;EAC5C;EAEA,OAAO,cAAc,MAAM,IAAI,IAAI;CACrC;AACF;AAEA,SAAS,cAAgC,MAAS,MAAiB;CACjE,MAAM,gBAAgB,KAAK,aAAa,IAAI,GAAG,UAAU;CACzD,MAAM,EAAE,SAAS,aAAa,cAAc;CAE5C,MAAM,QAAiC,EAAE,GAAG,WAAW,MAAM;CAC7D,MAAM,cAAc;EAClB,WAAW,EAAE,KAAK,CAAC,EAAE,KAAK,UAAU,GAAG,EAAE,MAAM,eAAe,CAAC,EAAE;EACjE,SAAS,CAAC,MAAM;CAClB;CACA,MAAM,WAAW,MAAM;CACvB,MAAM,OAAO,WACT,CAAC,GAAI,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,GAAI,WAAW,IAClE;CAEJ,OAAO;EACL,GAAG;EACH,WAAW;GACT,GAAG;GACH,cAAc;IACZ,GAAG,WAAW;IACd,mBAAmB,KAAK,cAAc;GACxC;GACA;EACF;EACA,QAAQ,QAA2B,SAAkB;GAGnD,MAAM,EAAE,SAAS,oBAAqB,WAAW,CAAC;GAClD,IAAI,iBAAiB,+BAA+B;IAClD,OAAO,YAAY,CAAC;IACpB,OAAO,QAAQ,KACb,IAAI,gBAAgB,8BAA8B,qBAAqB,aAAa,CACtF;GACF;GACA,OAAO,YAAY,CAAC;GACpB,OAAO,QAAQ,UAAU,CAAC;GAC1B,OAAQ,QAAQ,MAAkC,qBAAqB;GACvE,OAAO,WAAW,CAAC;GACnB,OAAO,OAAO,UAAU,CAAC;GACzB,OAAO,OAAO,MAAM,KAAK;IACvB,MAAM;IACN,SAAS;IACT,SAAS;IACT,KAAK,CAAC,EAAE,QAAQ,OAAO,CAAC;GAC1B,CAAC;GACD,OAAO,cAAc,YAAY,QAAQ,OAAgB,IAAI;EAC/D;CACF;AACF"}
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["import { cookies, headers } from 'next/headers';\nimport * as React from 'react';\nimport {
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["import { cookies, headers } from 'next/headers';\nimport * as React from 'react';\nimport { LOCALE_STORAGE_KEY, resolveRequestLocale, type TFunction, type Verbaly } from 'verbaly';\nimport {\n createRequestInstance,\n loadMessages,\n locales,\n requestOptions,\n sourceLocale,\n} from 'virtual:verbaly';\n\ninterface RequestState {\n locale: string;\n instance: Verbaly;\n}\n\n// React.cache = per-request dedupe in RSC; identity fallback keeps plain-node tests runnable\nconst perRequest: <T extends () => Promise<RequestState>>(fn: T) => T =\n (React as { cache?: <T>(fn: T) => T }).cache ?? ((fn) => fn);\n\nconst getRequestState = perRequest(async (): Promise<RequestState> => {\n const cookieName = requestOptions?.cookie ?? LOCALE_STORAGE_KEY;\n const headerStore = await headers();\n const cookieValue = cookieName === false ? undefined : (await cookies()).get(cookieName)?.value;\n const locale = resolveRequestLocale({\n supported: locales,\n cookie: cookieValue,\n header: headerStore.get('accept-language'),\n fallback: requestOptions?.fallback ?? sourceLocale,\n });\n return { locale, instance: await createRequestInstance(locale) };\n});\n\n// request-scoped instance: never the virtual:verbaly singleton (locale would leak between requests)\nexport async function getVerbaly(): Promise<Verbaly> {\n return (await getRequestState()).instance;\n}\n\nexport async function getT(): Promise<TFunction> {\n return (await getRequestState()).instance.t;\n}\n\nexport async function getRequestLocale(): Promise<string> {\n return (await getRequestState()).locale;\n}\n\nexport interface VerbalyProviderProps {\n locale: string;\n messages?: Record<string, string>;\n}\n\n// serializable props for <VerbalyProvider>: messages omitted when locale is the\n// source locale (it ships inline in the client bundle already)\nexport async function getVerbalyProps(): Promise<VerbalyProviderProps> {\n const { locale } = await getRequestState();\n if (locale === sourceLocale) return { locale };\n return { locale, messages: await loadMessages(locale) };\n}\n"],"mappings":";;;;;AAoBA,MAAM,mBAFH,MAAsC,WAAW,OAAO,IAAA,CAExB,YAAmC;CACpE,MAAM,aAAa,gBAAgB,UAAU;CAC7C,MAAM,cAAc,MAAM,QAAQ;CAElC,MAAM,SAAS,qBAAqB;EAClC,WAAW;EACX,QAHkB,eAAe,QAAQ,KAAA,KAAa,MAAM,QAAQ,EAAA,CAAG,IAAI,UAAU,CAAC,EAAE;EAIxF,QAAQ,YAAY,IAAI,iBAAiB;EACzC,UAAU,gBAAgB,YAAY;CACxC,CAAC;CACD,OAAO;EAAE;EAAQ,UAAU,MAAM,sBAAsB,MAAM;CAAE;AACjE,CAAC;AAGD,eAAsB,aAA+B;CACnD,QAAQ,MAAM,gBAAgB,EAAA,CAAG;AACnC;AAEA,eAAsB,OAA2B;CAC/C,QAAQ,MAAM,gBAAgB,EAAA,CAAG,SAAS;AAC5C;AAEA,eAAsB,mBAAoC;CACxD,QAAQ,MAAM,gBAAgB,EAAA,CAAG;AACnC;AASA,eAAsB,kBAAiD;CACrE,MAAM,EAAE,WAAW,MAAM,gBAAgB;CACzC,IAAI,WAAW,cAAc,OAAO,EAAE,OAAO;CAC7C,OAAO;EAAE;EAAQ,UAAU,MAAM,aAAa,MAAM;CAAE;AACxD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verbaly/next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Next.js integration for Verbaly: App Router/RSC with per-request locale negotiation, Turbopack and webpack support, flash-free hydration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n",
|
|
@@ -64,13 +64,13 @@
|
|
|
64
64
|
"access": "public"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@verbaly/compiler": "^0.
|
|
67
|
+
"@verbaly/compiler": "^0.24.0"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"next": "^15.0.0 || ^16.0.0",
|
|
71
71
|
"react": "^18.0.0 || ^19.0.0",
|
|
72
|
-
"@verbaly/react": "^0.
|
|
73
|
-
"verbaly": "^0.
|
|
72
|
+
"@verbaly/react": "^0.24.0",
|
|
73
|
+
"verbaly": "^0.24.0"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@types/react": "^19.2.17",
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
"next": "^16.2.10",
|
|
80
80
|
"react": "^19.2.7",
|
|
81
81
|
"react-dom": "^19.2.7",
|
|
82
|
-
"
|
|
83
|
-
"verbaly": "0.
|
|
82
|
+
"verbaly": "0.24.0",
|
|
83
|
+
"@verbaly/react": "0.24.0"
|
|
84
84
|
},
|
|
85
85
|
"scripts": {
|
|
86
86
|
"build": "tsdown",
|