@verbaly/next 0.20.0 → 0.22.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/dist/client.d.ts CHANGED
@@ -1,10 +1,9 @@
1
+ import { VerbalyProviderProps as VerbalyProviderProps$1 } from "./server.js";
1
2
  import { ReactElement, ReactNode } from "react";
2
3
  import { SwitchLocaleOptions } from "verbaly";
3
4
  import { Trans, useLocale, useT, useVerbaly } from "@verbaly/react";
4
5
  //#region src/client.d.ts
5
- interface VerbalyProviderProps {
6
- locale: string;
7
- messages?: Record<string, string>;
6
+ interface VerbalyProviderProps extends VerbalyProviderProps$1 {
8
7
  children?: ReactNode;
9
8
  }
10
9
  declare function VerbalyProvider(props: VerbalyProviderProps): ReactElement;
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["ReactVerbalyProvider","useVerbaly"],"sources":["../src/client.ts"],"sourcesContent":["'use client';\n\nimport { useRouter } from 'next/navigation';\nimport {\n createElement,\n useCallback,\n useEffect,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { VerbalyProvider as ReactVerbalyProvider, useVerbaly } from '@verbaly/react';\nimport { switchLocale, type SwitchLocaleOptions } from 'verbaly';\nimport { createInstance, requestOptions, sourceLocale } from 'virtual:verbaly';\n\nexport { Trans, useLocale, useT, useVerbaly } from '@verbaly/react';\n\nexport interface VerbalyProviderProps {\n locale: string;\n messages?: Record<string, string>;\n children?: ReactNode;\n}\n\nexport function VerbalyProvider(props: VerbalyProviderProps): ReactElement {\n const [instance] = useState(() => {\n const created = createInstance({ locale: props.locale });\n if (props.messages && props.locale !== sourceLocale) {\n created.addMessages(props.locale, props.messages);\n }\n return created;\n });\n\n // server changed the locale out-of-band (cookie edit + refresh): follow it\n useEffect(() => {\n if (props.locale === instance.locale) return;\n if (props.messages) {\n instance.addMessages(props.locale, props.messages);\n instance.setLocale(props.locale);\n } else {\n void instance.loadLocale(props.locale).then(() => instance.setLocale(props.locale));\n }\n }, [props.locale, props.messages, instance]);\n\n return createElement(ReactVerbalyProvider, { instance }, props.children);\n}\n\n// core switchLocale (catalog → locale → cookie + <html lang>) + RSC re-render\nexport function useSwitchLocale(): (\n locale: string,\n options?: SwitchLocaleOptions,\n) => Promise<void> {\n const instance = useVerbaly();\n const router = useRouter();\n return useCallback(\n async (locale, options) => {\n await switchLocale(instance, locale, { cookie: requestOptions?.cookie, ...options });\n router.refresh();\n },\n [instance, router],\n );\n}\n"],"mappings":";;;;;;;AAuBA,SAAgB,gBAAgB,OAA2C;CACzE,MAAM,CAAC,YAAY,eAAe;EAChC,MAAM,UAAU,eAAe,EAAE,QAAQ,MAAM,OAAO,CAAC;EACvD,IAAI,MAAM,YAAY,MAAM,WAAW,cACrC,QAAQ,YAAY,MAAM,QAAQ,MAAM,QAAQ;EAElD,OAAO;CACT,CAAC;CAGD,gBAAgB;EACd,IAAI,MAAM,WAAW,SAAS,QAAQ;EACtC,IAAI,MAAM,UAAU;GAClB,SAAS,YAAY,MAAM,QAAQ,MAAM,QAAQ;GACjD,SAAS,UAAU,MAAM,MAAM;EACjC,OACE,SAAc,WAAW,MAAM,MAAM,CAAC,CAAC,WAAW,SAAS,UAAU,MAAM,MAAM,CAAC;CAEtF,GAAG;EAAC,MAAM;EAAQ,MAAM;EAAU;CAAQ,CAAC;CAE3C,OAAO,cAAcA,mBAAsB,EAAE,SAAS,GAAG,MAAM,QAAQ;AACzE;AAGA,SAAgB,kBAGG;CACjB,MAAM,WAAWC,aAAW;CAC5B,MAAM,SAAS,UAAU;CACzB,OAAO,YACL,OAAO,QAAQ,YAAY;EACzB,MAAM,aAAa,UAAU,QAAQ;GAAE,QAAQ,gBAAgB;GAAQ,GAAG;EAAQ,CAAC;EACnF,OAAO,QAAQ;CACjB,GACA,CAAC,UAAU,MAAM,CACnB;AACF"}
1
+ {"version":3,"file":"client.js","names":["ReactVerbalyProvider","useVerbaly"],"sources":["../src/client.ts"],"sourcesContent":["'use client';\n\nimport { useRouter } from 'next/navigation';\nimport {\n createElement,\n useCallback,\n useEffect,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { VerbalyProvider as ReactVerbalyProvider, useVerbaly } from '@verbaly/react';\nimport { switchLocale, type SwitchLocaleOptions } from 'verbaly';\nimport { createInstance, requestOptions, sourceLocale } from 'virtual:verbaly';\nimport type { VerbalyProviderProps as SerializableProps } from './server';\n\nexport { Trans, useLocale, useT, useVerbaly } from '@verbaly/react';\n\n// the serializable props getVerbalyProps() produces, plus the client-side children\nexport interface VerbalyProviderProps extends SerializableProps {\n children?: ReactNode;\n}\n\nexport function VerbalyProvider(props: VerbalyProviderProps): ReactElement {\n const [instance] = useState(() => {\n const created = createInstance({ locale: props.locale });\n if (props.messages && props.locale !== sourceLocale) {\n created.addMessages(props.locale, props.messages);\n }\n return created;\n });\n\n // server changed the locale out-of-band (cookie edit + refresh): follow it\n useEffect(() => {\n if (props.locale === instance.locale) return;\n if (props.messages) {\n instance.addMessages(props.locale, props.messages);\n instance.setLocale(props.locale);\n } else {\n void instance.loadLocale(props.locale).then(() => instance.setLocale(props.locale));\n }\n }, [props.locale, props.messages, instance]);\n\n return createElement(ReactVerbalyProvider, { instance }, props.children);\n}\n\n// core switchLocale (catalog → locale → cookie + <html lang>) + RSC re-render\nexport function useSwitchLocale(): (\n locale: string,\n options?: SwitchLocaleOptions,\n) => Promise<void> {\n const instance = useVerbaly();\n const router = useRouter();\n return useCallback(\n async (locale, options) => {\n await switchLocale(instance, locale, { cookie: requestOptions?.cookie, ...options });\n router.refresh();\n },\n [instance, router],\n );\n}\n"],"mappings":";;;;;;;AAuBA,SAAgB,gBAAgB,OAA2C;CACzE,MAAM,CAAC,YAAY,eAAe;EAChC,MAAM,UAAU,eAAe,EAAE,QAAQ,MAAM,OAAO,CAAC;EACvD,IAAI,MAAM,YAAY,MAAM,WAAW,cACrC,QAAQ,YAAY,MAAM,QAAQ,MAAM,QAAQ;EAElD,OAAO;CACT,CAAC;CAGD,gBAAgB;EACd,IAAI,MAAM,WAAW,SAAS,QAAQ;EACtC,IAAI,MAAM,UAAU;GAClB,SAAS,YAAY,MAAM,QAAQ,MAAM,QAAQ;GACjD,SAAS,UAAU,MAAM,MAAM;EACjC,OACE,SAAc,WAAW,MAAM,MAAM,CAAC,CAAC,WAAW,SAAS,UAAU,MAAM,MAAM,CAAC;CAEtF,GAAG;EAAC,MAAM;EAAQ,MAAM;EAAU;CAAQ,CAAC;CAE3C,OAAO,cAAcA,mBAAsB,EAAE,SAAS,GAAG,MAAM,QAAQ;AACzE;AAGA,SAAgB,kBAGG;CACjB,MAAM,WAAWC,aAAW;CAC5B,MAAM,SAAS,UAAU;CACzB,OAAO,YACL,OAAO,QAAQ,YAAY;EACzB,MAAM,aAAa,UAAU,QAAQ;GAAE,QAAQ,gBAAgB;GAAQ,GAAG;EAAQ,CAAC;EACnF,OAAO,QAAQ;CACjB,GACA,CAAC,UAAU,MAAM,CACnB;AACF"}
package/dist/index.cjs CHANGED
@@ -13,6 +13,12 @@ function writeIfChanged(file, content) {
13
13
  (0, node_fs.writeFileSync)(file, content);
14
14
  return true;
15
15
  }
16
+ function syncAndWrite(compiler, cfg, catalogs, registry, requestOptions) {
17
+ const { added } = compiler.syncCatalogs(cfg, catalogs, registry);
18
+ for (const locale of Object.keys(added)) compiler.writeCatalog(cfg, locale, catalogs[locale] ?? {});
19
+ compiler.writeDts(cfg, catalogs[cfg.sourceLocale] ?? {});
20
+ writeGeneratedModules(compiler, cfg, catalogs, requestOptions);
21
+ }
16
22
  function writeGeneratedModules(compiler, cfg, catalogs, requestOptions = {}) {
17
23
  const dir = generatedDir(cfg.root);
18
24
  const localeDir = (0, node_path.join)(dir, "locale");
@@ -48,12 +54,7 @@ function startWatcher(compiler, cfg, requestOptions) {
48
54
  }
49
55
  running = true;
50
56
  try {
51
- const catalogs = compiler.loadCatalogs(cfg);
52
- const registry = await compiler.extractProject(cfg);
53
- const { added } = compiler.syncCatalogs(cfg, catalogs, registry);
54
- for (const locale of Object.keys(added)) compiler.writeCatalog(cfg, locale, catalogs[locale] ?? {});
55
- compiler.writeDts(cfg, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));
56
- writeGeneratedModules(compiler, cfg, catalogs, requestOptions);
57
+ syncAndWrite(compiler, cfg, compiler.loadCatalogs(cfg), await compiler.extractProject(cfg), requestOptions);
57
58
  } catch (error) {
58
59
  console.warn("[verbaly] live extraction failed:", error);
59
60
  } finally {
@@ -104,14 +105,12 @@ function withVerbaly(nextConfig, options = {}) {
104
105
  const catalogs = compiler.loadCatalogs(cfg);
105
106
  const registry = await compiler.extractProject(cfg);
106
107
  if (phase === BUILD_PHASE) {
107
- if (failOnMissing !== false) compiler.runBuildGate(cfg, registry);
108
+ compiler.runBuildGate(cfg, registry, failOnMissing);
109
+ writeGeneratedModules(compiler, cfg, catalogs, requestOptions);
108
110
  } else {
109
- const { added } = compiler.syncCatalogs(cfg, catalogs, registry);
110
- for (const locale of Object.keys(added)) compiler.writeCatalog(cfg, locale, catalogs[locale] ?? {});
111
- compiler.writeDts(cfg, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));
111
+ syncAndWrite(compiler, cfg, catalogs, registry, requestOptions);
112
112
  startWatcher(compiler, cfg, requestOptions);
113
113
  }
114
- writeGeneratedModules(compiler, cfg, catalogs, requestOptions);
115
114
  return composeConfig(base, cfg.root);
116
115
  };
117
116
  }
@@ -147,7 +146,7 @@ function composeConfig(base, root) {
147
146
  config.module ??= {};
148
147
  config.module.rules ??= [];
149
148
  config.module.rules.push({
150
- test: /\.[cm]?[jt]sx?$/,
149
+ test: SOURCE_PATH_RE,
151
150
  exclude: /node_modules/,
152
151
  enforce: "pre",
153
152
  use: [{ loader: LOADER }]
@@ -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// 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, writeGeneratedModules, 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 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, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));\n writeGeneratedModules(compiler, cfg, catalogs, 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 { VerbalyConfig } from '@verbaly/compiler';\nimport { join } from 'node:path';\nimport {\n GENERATED_DIR,\n generatedDir,\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 VerbalyConfig {\n failOnMissing?: boolean;\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 if (failOnMissing !== false) compiler.runBuildGate(cfg, registry);\n } else {\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, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));\n startWatcher(compiler, cfg, requestOptions);\n }\n\n writeGeneratedModules(compiler, cfg, catalogs, requestOptions);\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: /\\.[cm]?[jt]sx?$/,\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,QAAA,GAAA,UAAA,KAAA,CAAY,MAAM,aAAa;AACjC;AAGA,SAAS,eAAe,MAAc,SAA0B;CAC9D,IAAI;EACF,KAAA,GAAA,QAAA,aAAA,CAAiB,MAAM,MAAM,MAAM,SAAS,OAAO;CACrD,QAAQ,CAER;CACA,CAAA,GAAA,QAAA,cAAA,CAAc,MAAM,OAAO;CAC3B,OAAO;AACT;AAGA,SAAgB,sBACd,UACA,KACA,UACA,iBAAiC,CAAC,GACzB;CACT,MAAM,MAAM,aAAa,IAAI,IAAI;CACjC,MAAM,aAAA,GAAA,UAAA,KAAA,CAAiB,KAAK,QAAQ;CACpC,CAAA,GAAA,QAAA,UAAA,CAAU,WAAW,EAAE,WAAW,KAAK,CAAC;CAExC,IAAI,UAAU,gBAAA,GAAA,UAAA,KAAA,CAAoB,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,gBAAA,GAAA,UAAA,KAAA,CAAoB,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,gBAAA,GAAA,UAAA,KAAA,CACO,WAAW,GAAG,OAAO,IAAI,GAC9B,SAAS,qBAAqB,SAAS,WAAW,CAAC,CAAC,CACtD,KAAK;CAET,KAAK,MAAM,SAAA,GAAA,QAAA,YAAA,CAAoB,SAAS,GACtC,IAAI,CAAC,SAAS,IAAI,IAAI,GAAG;EACvB,CAAA,GAAA,QAAA,OAAA,EAAA,GAAA,UAAA,KAAA,CAAY,WAAW,IAAI,CAAC;EAC5B,UAAU;CACZ;CAEF,OAAO;AACT;;;ACxDA,MAAM,yBAAS,IAAI,IAAwB;AAE3C,SAAgB,aACd,UACA,KACA,gBACY;CACZ,MAAM,WAAW,OAAO,IAAI,IAAI,IAAI;CACpC,IAAI,UAAU,OAAO;CAErB,MAAM,cAAA,GAAA,UAAA,SAAA,CAAsB,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;GACF,MAAM,WAAW,SAAS,aAAa,GAAG;GAC1C,MAAM,WAAW,MAAM,SAAS,eAAe,GAAG;GAClD,MAAM,EAAE,UAAU,SAAS,aAAa,KAAK,UAAU,QAAQ;GAC/D,KAAK,MAAM,UAAU,OAAO,KAAK,KAAK,GACpC,SAAS,aAAa,KAAK,QAAQ,SAAS,WAAW,CAAC,CAAC;GAE3D,SAAS,SAAS,KAAK,IAAI,IAAI,OAAO,QAAQ,SAAS,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC;GAChF,sBAAsB,UAAU,KAAK,UAAU,cAAc;EAC/D,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,WAAA,GAAA,QAAA,MAAA,CAAgB,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;;;ACnBA,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,aAAa,MAAM,WAAW,OAAO,OAAO,IAAK,cAAe,CAAC;EACzF,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;OACR,kBAAkB,OAAO,SAAS,aAAa,KAAK,QAAQ;EAAA,OAC3D;GACL,MAAM,EAAE,UAAU,SAAS,aAAa,KAAK,UAAU,QAAQ;GAC/D,KAAK,MAAM,UAAU,OAAO,KAAK,KAAK,GACpC,SAAS,aAAa,KAAK,QAAQ,SAAS,WAAW,CAAC,CAAC;GAE3D,SAAS,SAAS,KAAK,IAAI,IAAI,OAAO,QAAQ,SAAS,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC;GAChF,aAAa,UAAU,KAAK,cAAc;EAC5C;EAEA,sBAAsB,UAAU,KAAK,UAAU,cAAc;EAC7D,OAAO,cAAc,MAAM,IAAI,IAAI;CACrC;AACF;AAEA,SAAS,cAAgC,MAAS,MAAiB;CACjE,MAAM,iBAAA,GAAA,UAAA,KAAA,CAAqB,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"}
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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"}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { VerbalyConfig, VerbalyConfig as VerbalyConfig$1 } from "@verbaly/compiler";
1
+ import { PluginOptions, VerbalyConfig } from "@verbaly/compiler";
2
2
  //#region src/codegen.d.ts
3
3
  interface RequestOptions {
4
4
  cookie?: string | false;
@@ -6,8 +6,7 @@ interface RequestOptions {
6
6
  }
7
7
  //#endregion
8
8
  //#region src/index.d.ts
9
- interface NextVerbalyOptions extends VerbalyConfig$1 {
10
- failOnMissing?: boolean;
9
+ interface NextVerbalyOptions extends PluginOptions {
11
10
  cookie?: string | false;
12
11
  fallback?: string;
13
12
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { VerbalyConfig, VerbalyConfig as VerbalyConfig$1 } from "@verbaly/compiler";
1
+ import { PluginOptions, VerbalyConfig } from "@verbaly/compiler";
2
2
  //#region src/codegen.d.ts
3
3
  interface RequestOptions {
4
4
  cookie?: string | false;
@@ -6,8 +6,7 @@ interface RequestOptions {
6
6
  }
7
7
  //#endregion
8
8
  //#region src/index.d.ts
9
- interface NextVerbalyOptions extends VerbalyConfig$1 {
10
- failOnMissing?: boolean;
9
+ interface NextVerbalyOptions extends PluginOptions {
11
10
  cookie?: string | false;
12
11
  fallback?: string;
13
12
  }
package/dist/index.js CHANGED
@@ -12,6 +12,12 @@ function writeIfChanged(file, content) {
12
12
  writeFileSync(file, content);
13
13
  return true;
14
14
  }
15
+ function syncAndWrite(compiler, cfg, catalogs, registry, requestOptions) {
16
+ const { added } = compiler.syncCatalogs(cfg, catalogs, registry);
17
+ for (const locale of Object.keys(added)) compiler.writeCatalog(cfg, locale, catalogs[locale] ?? {});
18
+ compiler.writeDts(cfg, catalogs[cfg.sourceLocale] ?? {});
19
+ writeGeneratedModules(compiler, cfg, catalogs, requestOptions);
20
+ }
15
21
  function writeGeneratedModules(compiler, cfg, catalogs, requestOptions = {}) {
16
22
  const dir = generatedDir(cfg.root);
17
23
  const localeDir = join(dir, "locale");
@@ -47,12 +53,7 @@ function startWatcher(compiler, cfg, requestOptions) {
47
53
  }
48
54
  running = true;
49
55
  try {
50
- const catalogs = compiler.loadCatalogs(cfg);
51
- const registry = await compiler.extractProject(cfg);
52
- const { added } = compiler.syncCatalogs(cfg, catalogs, registry);
53
- for (const locale of Object.keys(added)) compiler.writeCatalog(cfg, locale, catalogs[locale] ?? {});
54
- compiler.writeDts(cfg, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));
55
- writeGeneratedModules(compiler, cfg, catalogs, requestOptions);
56
+ syncAndWrite(compiler, cfg, compiler.loadCatalogs(cfg), await compiler.extractProject(cfg), requestOptions);
56
57
  } catch (error) {
57
58
  console.warn("[verbaly] live extraction failed:", error);
58
59
  } finally {
@@ -103,14 +104,12 @@ function withVerbaly(nextConfig, options = {}) {
103
104
  const catalogs = compiler.loadCatalogs(cfg);
104
105
  const registry = await compiler.extractProject(cfg);
105
106
  if (phase === BUILD_PHASE) {
106
- if (failOnMissing !== false) compiler.runBuildGate(cfg, registry);
107
+ compiler.runBuildGate(cfg, registry, failOnMissing);
108
+ writeGeneratedModules(compiler, cfg, catalogs, requestOptions);
107
109
  } else {
108
- const { added } = compiler.syncCatalogs(cfg, catalogs, registry);
109
- for (const locale of Object.keys(added)) compiler.writeCatalog(cfg, locale, catalogs[locale] ?? {});
110
- compiler.writeDts(cfg, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));
110
+ syncAndWrite(compiler, cfg, catalogs, registry, requestOptions);
111
111
  startWatcher(compiler, cfg, requestOptions);
112
112
  }
113
- writeGeneratedModules(compiler, cfg, catalogs, requestOptions);
114
113
  return composeConfig(base, cfg.root);
115
114
  };
116
115
  }
@@ -146,7 +145,7 @@ function composeConfig(base, root) {
146
145
  config.module ??= {};
147
146
  config.module.rules ??= [];
148
147
  config.module.rules.push({
149
- test: /\.[cm]?[jt]sx?$/,
148
+ test: SOURCE_PATH_RE,
150
149
  exclude: /node_modules/,
151
150
  enforce: "pre",
152
151
  use: [{ loader: LOADER }]
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// 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, writeGeneratedModules, 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 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, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));\n writeGeneratedModules(compiler, cfg, catalogs, 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 { VerbalyConfig } from '@verbaly/compiler';\nimport { join } from 'node:path';\nimport {\n GENERATED_DIR,\n generatedDir,\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 VerbalyConfig {\n failOnMissing?: boolean;\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 if (failOnMissing !== false) compiler.runBuildGate(cfg, registry);\n } else {\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, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));\n startWatcher(compiler, cfg, requestOptions);\n }\n\n writeGeneratedModules(compiler, cfg, catalogs, requestOptions);\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: /\\.[cm]?[jt]sx?$/,\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\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verbaly/next",
3
- "version": "0.20.0",
3
+ "version": "0.22.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.20.0"
67
+ "@verbaly/compiler": "^0.22.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.20.0",
73
- "verbaly": "^0.20.0"
72
+ "@verbaly/react": "^0.22.0",
73
+ "verbaly": "^0.22.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
- "verbaly": "0.20.0",
83
- "@verbaly/react": "0.20.0"
82
+ "verbaly": "0.22.0",
83
+ "@verbaly/react": "0.22.0"
84
84
  },
85
85
  "scripts": {
86
86
  "build": "tsdown",