@fluenti/next 0.2.0 → 0.2.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"generate-server-module.d.ts","sourceRoot":"","sources":["../src/generate-server-module.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAEnD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,oBAAoB,GAC3B,MAAM,CAiMR"}
1
+ {"version":3,"file":"generate-server-module.d.ts","sourceRoot":"","sources":["../src/generate-server-module.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAEnD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,oBAAoB,GAC3B,MAAM,CAmOR"}
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`node:fs`),t=require(`node:child_process`),n=require(`node:path`),r=require(`node:module`),i=require(`@fluenti/core`);var a=typeof __filename==`string`?__filename:{}.url,{createJiti:o}=(0,r.createRequire)(a)(`jiti`);function s(e,t){let r=c(e),i=t?.defaultLocale??r?.sourceLocale??`en`,a=t?.locales??r?.locales??[i],o=t?.compiledDir??r?.compileOutDir??`./src/locales/compiled`,s=t?.serverModuleOutDir??(0,n.join)(`node_modules`,`.fluenti`),l={locales:a,defaultLocale:i,compiledDir:o,serverModule:t?.serverModule??null,serverModuleOutDir:s};return t?.resolveLocale&&(l.resolveLocale=t.resolveLocale),t?.dateFormats&&(l.dateFormats=t.dateFormats),t?.numberFormats&&(l.numberFormats=t.numberFormats),t?.fallbackChain&&(l.fallbackChain=t.fallbackChain),l}function c(t){let r=o(a,{moduleCache:!1,interopDefault:!0});for(let i of[`fluenti.config.ts`,`fluenti.config.js`,`fluenti.config.mjs`]){let a=(0,n.resolve)(t,i);if((0,e.existsSync)(a))try{return u(r(a))}catch{return l(a,r)||null}}return null}function l(t,r){let i=(0,e.readFileSync)(t,`utf8`),a=i.match(/import\s*\{\s*defineConfig(?:\s+as\s+([A-Za-z_$][\w$]*))?\s*\}\s*from\s*['"]@fluenti\/cli['"]\s*;?/);if(!a)return null;let o=a[1]??`defineConfig`,s=i.replace(a[0],``),c=(0,n.join)((0,n.dirname)(t),`.${(0,n.basename)(t,(0,n.extname)(t))}.next-plugin-read-config${(0,n.extname)(t)||`.ts`}`);(0,e.writeFileSync)(c,`const ${o} = (config) => config\n${s}`,`utf8`);try{return u(r(c))}catch{return null}finally{(0,e.rmSync)(c,{force:!0})}}function u(e){return typeof e==`object`&&e&&`default`in e?e.default??{}:e}function d(t,r){if(r.serverModule)return(0,n.resolve)(t,r.serverModule);let a=(0,n.resolve)(t,r.serverModuleOutDir),o=(0,n.resolve)(a,`server.js`),s=(0,n.resolve)(a,`server.d.ts`);(0,e.existsSync)(a)||(0,e.mkdirSync)(a,{recursive:!0});for(let e of r.locales)(0,i.validateLocale)(e,`next-plugin`);let c=f((0,n.relative)(a,(0,n.resolve)(t,r.compiledDir))),l=r.locales.map(e=>` case '${e}': return import('${c}/${e}')`).join(`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`node:fs`),t=require(`node:child_process`),n=require(`node:path`),r=require(`node:module`),i=require(`@fluenti/core`);var a=typeof __filename==`string`?__filename:{}.url,{createJiti:o}=(0,r.createRequire)(a)(`jiti`);function s(e,t){let r=c(e),i=t?.defaultLocale??r?.sourceLocale??`en`,a=t?.locales??r?.locales??[i],o=t?.compiledDir??r?.compileOutDir??`./src/locales/compiled`,s=t?.serverModuleOutDir??(0,n.join)(`node_modules`,`.fluenti`),l=t?.cookieName??`locale`,u={locales:a,defaultLocale:i,compiledDir:o,serverModule:t?.serverModule??null,serverModuleOutDir:s,cookieName:l};return t?.resolveLocale&&(u.resolveLocale=t.resolveLocale),t?.dateFormats&&(u.dateFormats=t.dateFormats),t?.numberFormats&&(u.numberFormats=t.numberFormats),t?.fallbackChain&&(u.fallbackChain=t.fallbackChain),u}function c(t){let r=o(a,{moduleCache:!1,interopDefault:!0});for(let i of[`fluenti.config.ts`,`fluenti.config.js`,`fluenti.config.mjs`]){let a=(0,n.resolve)(t,i);if((0,e.existsSync)(a))try{return u(r(a))}catch{return l(a,r)||null}}return null}function l(t,r){let i=(0,e.readFileSync)(t,`utf8`),a=i.match(/import\s*\{\s*defineConfig(?:\s+as\s+([A-Za-z_$][\w$]*))?\s*\}\s*from\s*['"]@fluenti\/cli['"]\s*;?/);if(!a)return null;let o=a[1]??`defineConfig`,s=i.replace(a[0],``),c=(0,n.join)((0,n.dirname)(t),`.${(0,n.basename)(t,(0,n.extname)(t))}.next-plugin-read-config${(0,n.extname)(t)||`.ts`}`);(0,e.writeFileSync)(c,`const ${o} = (config) => config\n${s}`,`utf8`);try{return u(r(c))}catch{return null}finally{(0,e.rmSync)(c,{force:!0})}}function u(e){return typeof e==`object`&&e&&`default`in e?e.default??{}:e}function d(t,r){if(r.serverModule)return(0,n.resolve)(t,r.serverModule);let a=(0,n.resolve)(t,r.serverModuleOutDir),o=(0,n.resolve)(a,`server.js`),s=(0,n.resolve)(a,`server.d.ts`);(0,e.existsSync)(a)||(0,e.mkdirSync)(a,{recursive:!0});for(let e of r.locales)(0,i.validateLocale)(e,`next-plugin`);let c=f((0,n.relative)(a,(0,n.resolve)(t,r.compiledDir))),l=r.locales.map(e=>` case '${e}': return import('${c}/${e}')`).join(`
2
2
  `),u=r.fallbackChain?JSON.stringify(r.fallbackChain):`undefined`;(0,e.writeFileSync)((0,n.resolve)(a,`client-provider.js`),`"use client";
3
3
  // Auto-generated by @fluenti/next — do not edit
4
4
  import { createElement } from 'react'
@@ -13,15 +13,44 @@ export function ClientI18nProvider({ locale, fallbackLocale, fallbackChain, chil
13
13
  }
14
14
  `,`utf-8`);let d=r.resolveLocale?`import __resolveLocale from '${f((0,n.relative)(a,(0,n.resolve)(t,r.resolveLocale)))}'`:null,p=r.resolveLocale?`resolveLocale: __resolveLocale,`:`resolveLocale: async () => {
15
15
  try {
16
- const { cookies } = await import('next/headers')
17
- return (await cookies()).get('locale')?.value ?? '${r.defaultLocale}'
16
+ const { cookies, headers } = await import('next/headers')
17
+ const [cookieStore, headerStore] = await Promise.all([cookies(), headers()])
18
+
19
+ // 1. Referer URL path segment (available in Server Action context)
20
+ const referer = headerStore.get('referer')
21
+ if (referer) {
22
+ try {
23
+ const seg = new URL(referer).pathname.split('/')[1]
24
+ if (seg && __locales.includes(seg)) return seg
25
+ } catch {}
26
+ }
27
+
28
+ // 2. Cookie (configurable name)
29
+ const fromCookie = cookieStore.get('${r.cookieName}')?.value
30
+ if (fromCookie && __locales.includes(fromCookie)) return fromCookie
31
+
32
+ // 3. Accept-Language header
33
+ const acceptLang = headerStore.get('accept-language')
34
+ if (acceptLang) {
35
+ for (const part of acceptLang.split(',')) {
36
+ const lang = part.split(';')[0].trim()
37
+ if (__locales.includes(lang)) return lang
38
+ const prefix = lang.split('-')[0]
39
+ const match = __locales.find(l => l === prefix || l.startsWith(prefix + '-'))
40
+ if (match) return match
41
+ }
42
+ }
43
+
44
+ return '${r.defaultLocale}'
18
45
  } catch {
19
46
  return '${r.defaultLocale}'
20
47
  }
21
- },`,m=`// Auto-generated by @fluenti/next — do not edit
48
+ },`,m=JSON.stringify(r.locales),h=`// Auto-generated by @fluenti/next — do not edit
22
49
  import { createServerI18n } from '@fluenti/react/server'
23
50
  import { createElement } from 'react'
24
51
  ${d?`${d}\n`:``}
52
+ const __locales = ${m}
53
+
25
54
  const serverI18n = createServerI18n({
26
55
  loadMessages: async (locale) => {
27
56
  switch (locale) {
@@ -38,8 +67,11 @@ export const setLocale = serverI18n.setLocale
38
67
  export const getI18n = serverI18n.getI18n
39
68
  export const t = (..._args) => {
40
69
  throw new Error(
41
- "[fluenti] \`t\` imported from '@fluenti/next' is a compile-time API. " +
42
- 'Use it only with the Fluenti loader inside an async server scope.',
70
+ "[fluenti] \`t\` imported from '@fluenti/next' is a compile-time API replaced at build time.\\n" +
71
+ ' Ensure:\\n' +
72
+ ' 1. \`withFluenti()\` is configured in next.config.ts\\n' +
73
+ ' 2. The file is inside src/ (not node_modules)\\n' +
74
+ " 3. For client components, import from '@fluenti/react'",
43
75
  )
44
76
  }
45
77
  export const Trans = serverI18n.Trans
@@ -70,7 +102,7 @@ export async function I18nProvider({ locale, children }) {
70
102
  fallbackChain: ${u},
71
103
  }, children)
72
104
  }
73
- `;return(0,e.writeFileSync)(o,m,`utf-8`),(0,e.writeFileSync)(s,`// Auto-generated by @fluenti/next — do not edit
105
+ `;return(0,e.writeFileSync)(o,h,`utf-8`),(0,e.writeFileSync)(s,`// Auto-generated by @fluenti/next — do not edit
74
106
  import type { ReactNode, ReactElement } from 'react'
75
107
  import type { CompileTimeT, FluentInstanceExtended } from '@fluenti/core'
76
108
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/read-config.ts","../src/generate-server-module.ts","../src/dev-runner.ts","../src/with-fluenti.ts","../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, extname, join, resolve } from 'node:path'\nimport type { FluentiConfig } from '@fluenti/core'\nimport type { WithFluentConfig, ResolvedFluentConfig } from './types'\n\nconst runtimeModulePath = typeof __filename === 'string'\n ? __filename\n : import.meta.url\nconst require = createRequire(runtimeModulePath)\nconst { createJiti } = require('jiti') as {\n createJiti: (\n url: string,\n options?: { moduleCache?: boolean; interopDefault?: boolean },\n ) => (path: string) => unknown\n}\n\n/**\n * Read fluenti.config.ts and merge with withFluenti() overrides.\n */\nexport function resolveConfig(\n projectRoot: string,\n overrides?: WithFluentConfig,\n): ResolvedFluentConfig {\n const fileConfig = readFluentConfigSync(projectRoot)\n\n const defaultLocale = overrides?.defaultLocale\n ?? fileConfig?.sourceLocale\n ?? 'en'\n\n const locales = overrides?.locales\n ?? fileConfig?.locales\n ?? [defaultLocale]\n\n const compiledDir = overrides?.compiledDir\n ?? fileConfig?.compileOutDir\n ?? './src/locales/compiled'\n\n const serverModuleOutDir = overrides?.serverModuleOutDir\n ?? join('node_modules', '.fluenti')\n\n const resolved: ResolvedFluentConfig = {\n locales,\n defaultLocale,\n compiledDir,\n serverModule: overrides?.serverModule ?? null,\n serverModuleOutDir,\n }\n if (overrides?.resolveLocale) resolved.resolveLocale = overrides.resolveLocale\n if (overrides?.dateFormats) resolved.dateFormats = overrides.dateFormats\n if (overrides?.numberFormats) resolved.numberFormats = overrides.numberFormats\n if (overrides?.fallbackChain) resolved.fallbackChain = overrides.fallbackChain\n return resolved\n}\n\n/**\n * Attempt to read fluenti.config.ts synchronously.\n * Returns null if file doesn't exist or can't be parsed.\n */\nfunction readFluentConfigSync(projectRoot: string): FluentiConfig | null {\n const jiti = createJiti(runtimeModulePath, {\n moduleCache: false,\n interopDefault: true,\n })\n const candidates = [\n 'fluenti.config.ts',\n 'fluenti.config.js',\n 'fluenti.config.mjs',\n ]\n\n for (const name of candidates) {\n const configPath = resolve(projectRoot, name)\n if (existsSync(configPath)) {\n try {\n return normalizeLoadedConfig(jiti(configPath) as FluentiConfig | { default?: FluentiConfig })\n } catch {\n const rewritten = tryLoadConfigViaDefineConfigShim(configPath, jiti)\n if (rewritten) {\n return rewritten\n }\n return null\n }\n }\n }\n\n return null\n}\n\nfunction tryLoadConfigViaDefineConfigShim(\n configPath: string,\n jiti: (path: string) => unknown,\n): FluentiConfig | null {\n const source = readFileSync(configPath, 'utf8')\n const importMatch = source.match(\n /import\\s*\\{\\s*defineConfig(?:\\s+as\\s+([A-Za-z_$][\\w$]*))?\\s*\\}\\s*from\\s*['\"]@fluenti\\/cli['\"]\\s*;?/,\n )\n if (!importMatch) {\n return null\n }\n\n const helperName = importMatch[1] ?? 'defineConfig'\n const rewrittenSource = source.replace(importMatch[0], '')\n const tempPath = join(\n dirname(configPath),\n `.${basename(configPath, extname(configPath))}.next-plugin-read-config${extname(configPath) || '.ts'}`,\n )\n\n writeFileSync(tempPath, `const ${helperName} = (config) => config\\n${rewrittenSource}`, 'utf8')\n\n try {\n return normalizeLoadedConfig(jiti(tempPath) as FluentiConfig | { default?: FluentiConfig })\n } catch {\n return null\n } finally {\n rmSync(tempPath, { force: true })\n }\n}\n\nfunction normalizeLoadedConfig(\n mod: FluentiConfig | { default?: FluentiConfig },\n): FluentiConfig {\n return typeof mod === 'object' && mod !== null && 'default' in mod\n ? (mod.default ?? {}) as FluentiConfig\n : mod as FluentiConfig\n}\n","import { writeFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { resolve, relative } from 'node:path'\nimport { validateLocale } from '@fluenti/core'\nimport type { ResolvedFluentConfig } from './types'\n\n/**\n * Generate the server module that provides:\n * - setLocale / getI18n\n * - Trans / Plural / Select / DateTime / NumberFormat (server components)\n * - I18nProvider (async server component for layouts)\n *\n * @returns Absolute path to the generated server module.\n */\nexport function generateServerModule(\n projectRoot: string,\n config: ResolvedFluentConfig,\n): string {\n if (config.serverModule) {\n return resolve(projectRoot, config.serverModule)\n }\n\n const outDir = resolve(projectRoot, config.serverModuleOutDir)\n const outPath = resolve(outDir, 'server.js')\n const dtsPath = resolve(outDir, 'server.d.ts')\n\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true })\n }\n\n for (const locale of config.locales) {\n validateLocale(locale, 'next-plugin')\n }\n\n const compiledDirAbs = resolve(projectRoot, config.compiledDir)\n const compiledRelative = toForwardSlash(relative(outDir, compiledDirAbs))\n\n const localeImports = config.locales\n .map((locale) => ` case '${locale}': return import('${compiledRelative}/${locale}')`)\n .join('\\n')\n\n const fallbackChainStr = config.fallbackChain\n ? JSON.stringify(config.fallbackChain)\n : 'undefined'\n\n // Generate a 'use client' provider that imports messages statically.\n // Messages contain functions (interpolation) which can't cross the RSC boundary.\n const clientProviderPath = resolve(outDir, 'client-provider.js')\n\n const clientStaticImports = config.locales\n .map((locale) => {\n const safe = locale.replace(/[^a-zA-Z0-9]/g, '_')\n return `import ${safe} from '${compiledRelative}/${locale}'`\n })\n .join('\\n')\n\n const clientAllMessagesEntries = config.locales\n .map((locale) => {\n const safe = locale.replace(/[^a-zA-Z0-9]/g, '_')\n return `'${locale}': ${safe}`\n })\n .join(', ')\n\n const clientProviderSource = `\"use client\";\n// Auto-generated by @fluenti/next — do not edit\nimport { createElement } from 'react'\nimport { I18nProvider } from '@fluenti/react'\n${clientStaticImports}\n\nconst __allMessages = { ${clientAllMessagesEntries} }\n\nexport function ClientI18nProvider({ locale, fallbackLocale, fallbackChain, children }) {\n return createElement(I18nProvider, { locale, fallbackLocale, messages: __allMessages, fallbackChain }, children)\n}\n`\n writeFileSync(clientProviderPath, clientProviderSource, 'utf-8')\n\n const resolveLocaleImport = config.resolveLocale\n ? (() => {\n const absPath = resolve(projectRoot, config.resolveLocale)\n const relPath = toForwardSlash(relative(outDir, absPath))\n return `import __resolveLocale from '${relPath}'`\n })()\n : null\n\n const resolveLocaleFn = config.resolveLocale\n ? `resolveLocale: __resolveLocale,`\n : `resolveLocale: async () => {\n try {\n const { cookies } = await import('next/headers')\n return (await cookies()).get('locale')?.value ?? '${config.defaultLocale}'\n } catch {\n return '${config.defaultLocale}'\n }\n },`\n\n const moduleSource = `// Auto-generated by @fluenti/next — do not edit\nimport { createServerI18n } from '@fluenti/react/server'\nimport { createElement } from 'react'\n${resolveLocaleImport ? `${resolveLocaleImport}\\n` : ''}\nconst serverI18n = createServerI18n({\n loadMessages: async (locale) => {\n switch (locale) {\n${localeImports}\n default: return import('${compiledRelative}/${config.defaultLocale}')\n }\n },\n fallbackLocale: '${config.defaultLocale}',\n fallbackChain: ${fallbackChainStr},\n ${resolveLocaleFn}\n})\n\nexport const setLocale = serverI18n.setLocale\nexport const getI18n = serverI18n.getI18n\nexport const t = (..._args) => {\n throw new Error(\n \"[fluenti] \\`t\\` imported from '@fluenti/next' is a compile-time API. \" +\n 'Use it only with the Fluenti loader inside an async server scope.',\n )\n}\nexport const Trans = serverI18n.Trans\nexport const Plural = serverI18n.Plural\nexport const Select = serverI18n.Select\nexport const DateTime = serverI18n.DateTime\nexport const NumberFormat = serverI18n.NumberFormat\n\n/**\n * Async server component for root layouts.\n *\n * Sets up both server-side (React.cache) and client-side (I18nProvider) i18n.\n */\nexport async function I18nProvider({ locale, children }) {\n const activeLocale = locale ?? '${config.defaultLocale}'\n\n // 1. Initialize server-side i18n (React.cache scoped)\n serverI18n.setLocale(activeLocale)\n await serverI18n.getI18n()\n\n // 2. Import the local 'use client' provider that has messages statically bundled.\n // Messages contain functions (interpolation) which can't be serialized across the RSC boundary.\n const { ClientI18nProvider } = await import('./client-provider.js')\n\n return createElement(ClientI18nProvider, {\n locale: activeLocale,\n fallbackLocale: '${config.defaultLocale}',\n fallbackChain: ${fallbackChainStr},\n }, children)\n}\n`\n\n const dtsSource = `// Auto-generated by @fluenti/next — do not edit\nimport type { ReactNode, ReactElement } from 'react'\nimport type { CompileTimeT, FluentInstanceExtended } from '@fluenti/core'\n\nexport declare function setLocale(locale: string): void\nexport declare function getI18n(): Promise<FluentInstanceExtended & { locale: string }>\nexport declare const t: CompileTimeT\n\nexport declare function Trans(props: {\n children: ReactNode\n id?: string\n context?: string\n comment?: string\n render?: (translation: ReactNode) => ReactNode\n}): Promise<ReactElement>\n\nexport declare function Plural(props: {\n value: number\n id?: string\n context?: string\n comment?: string\n zero?: ReactNode\n one?: ReactNode\n two?: ReactNode\n few?: ReactNode\n many?: ReactNode\n other: ReactNode\n offset?: number\n}): Promise<ReactElement>\n\nexport declare function Select(props: {\n value: string\n id?: string\n context?: string\n comment?: string\n other: ReactNode\n options?: Record<string, ReactNode>\n [key: string]: ReactNode | Record<string, ReactNode> | string | undefined\n}): Promise<ReactElement>\n\nexport declare function DateTime(props: {\n value: Date | number\n style?: string\n}): Promise<ReactElement>\n\nexport declare function NumberFormat(props: {\n value: number\n style?: string\n}): Promise<ReactElement>\n\nexport declare function I18nProvider(props: {\n locale?: string\n children: ReactNode\n}): Promise<ReactElement>\n`\n\n writeFileSync(outPath, moduleSource, 'utf-8')\n writeFileSync(dtsPath, dtsSource, 'utf-8')\n\n return outPath\n}\n\nfunction toForwardSlash(p: string): string {\n return p.split('\\\\').join('/')\n}\n","import { exec } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { resolve, dirname, join } from 'node:path'\nimport { createRequire } from 'node:module'\n\nexport interface DevRunnerOptions {\n cwd: string\n onSuccess?: () => void\n onError?: (err: Error) => void\n /** If true, reject the promise on failure instead of swallowing the error */\n throwOnError?: boolean\n /** Run only compile (skip extract). Useful for production builds where source is unchanged. */\n compileOnly?: boolean\n}\n\n/**\n * Walk up from `cwd` to find `node_modules/.bin/fluenti`.\n * Returns the absolute path or null if not found.\n */\nexport function resolveCliBin(cwd: string): string | null {\n let dir = cwd\n for (;;) {\n const bin = resolve(dir, 'node_modules/.bin/fluenti')\n if (existsSync(bin)) return bin\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n return null\n}\n\n/**\n * Run compile in-process via `@fluenti/cli` (for compileOnly mode),\n * or fall back to shell-out for extract + compile (dev mode).\n */\nexport async function runExtractCompile(options: DevRunnerOptions): Promise<void> {\n if (options.compileOnly) {\n try {\n // Resolve @fluenti/cli from the project's cwd (not from this package's location)\n // using createRequire so pnpm's strict node_modules layout works correctly.\n // Use require() (not import()) to load @fluenti/cli — avoids CJS/ESM interop\n // issues when dynamic import() loads minified CJS with chunk requires.\n const projectRequire = createRequire(join(options.cwd, 'package.json'))\n const { runCompile } = projectRequire('@fluenti/cli')\n await runCompile(options.cwd)\n console.log('[fluenti] Compiling... done')\n options.onSuccess?.()\n return\n } catch (e) {\n const error = e instanceof Error ? e : new Error(String(e))\n if (options.throwOnError) throw error\n console.warn('[fluenti] Compile failed:', error.message)\n options.onError?.(error)\n return\n }\n }\n\n // Dev mode: shell out for extract + compile\n const bin = resolveCliBin(options.cwd)\n if (!bin) {\n const msg = '[fluenti] CLI not found — skipping auto-compile. Install @fluenti/cli as a devDependency.'\n if (options.throwOnError) {\n return Promise.reject(new Error(msg))\n }\n console.warn(msg)\n return Promise.resolve()\n }\n\n const command = `${bin} extract && ${bin} compile`\n return new Promise<void>((resolve, reject) => {\n exec(\n command,\n { cwd: options.cwd },\n (err, _stdout, stderr) => {\n if (err) {\n const error = new Error(stderr || err.message)\n if (options.throwOnError) {\n reject(error)\n return\n }\n console.warn('[fluenti] Extract/compile failed:', error.message)\n options.onError?.(error)\n } else {\n console.log('[fluenti] Extracting and compiling... done')\n options.onSuccess?.()\n }\n resolve()\n },\n )\n })\n}\n\n/**\n * Create a debounced runner that collapses rapid calls.\n *\n * - If called while idle, schedules a run after `delay` ms.\n * - If called while a run is in progress, marks a pending rerun.\n * - Never runs concurrently.\n */\nexport function createDebouncedRunner(\n options: DevRunnerOptions,\n delay = 300,\n): () => void {\n let timer: ReturnType<typeof setTimeout> | null = null\n let running = false\n let pendingRerun = false\n\n async function execute(): Promise<void> {\n running = true\n try {\n await runExtractCompile(options)\n } finally {\n running = false\n if (pendingRerun) {\n pendingRerun = false\n schedule()\n }\n }\n }\n\n function schedule(): void {\n if (timer !== null) {\n clearTimeout(timer)\n }\n timer = setTimeout(() => {\n timer = null\n if (running) {\n pendingRerun = true\n } else {\n execute()\n }\n }, delay)\n }\n\n return schedule\n}\n","import { existsSync } from 'node:fs'\nimport { execSync } from 'node:child_process'\nimport { resolve, dirname } from 'node:path'\nimport type { WithFluentConfig } from './types'\nimport { resolveConfig } from './read-config'\nimport { generateServerModule } from './generate-server-module'\nimport { createDebouncedRunner } from './dev-runner'\n\ntype NextConfig = Record<string, unknown>\n\n/**\n * Wrap your Next.js config with Fluenti support.\n *\n * Adds a webpack loader that transforms `t\\`\\`` and `t()` calls,\n * and generates a server module for RSC i18n.\n *\n * @example\n * ```ts\n * // next.config.ts — function style (recommended)\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti()({ reactStrictMode: true })\n * ```\n *\n * @example\n * ```ts\n * // next.config.ts — direct style\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti({ reactStrictMode: true })\n * ```\n */\nexport function withFluenti(fluentConfig?: WithFluentConfig): (nextConfig?: NextConfig) => NextConfig\nexport function withFluenti(nextConfig: NextConfig): NextConfig\nexport function withFluenti(\n configOrNext?: WithFluentConfig | NextConfig,\n): NextConfig | ((nextConfig?: NextConfig) => NextConfig) {\n if (configOrNext && isNextConfig(configOrNext as NextConfig)) {\n return applyFluenti({}, configOrNext as NextConfig)\n }\n\n const fluentConfig = (configOrNext ?? {}) as WithFluentConfig\n return function wrappedConfig(nextConfig?: NextConfig): NextConfig {\n return applyFluenti(fluentConfig, nextConfig ?? {})\n }\n}\n\nfunction isNextConfig(obj: NextConfig): boolean {\n const nextKeys = [\n 'reactStrictMode', 'experimental', 'images', 'env', 'webpack',\n 'rewrites', 'redirects', 'headers', 'pageExtensions', 'output',\n 'basePath', 'i18n', 'trailingSlash', 'compiler', 'transpilePackages',\n ]\n return nextKeys.some((key) => key in obj)\n}\n\nfunction applyFluenti(\n fluentConfig: WithFluentConfig,\n nextConfig: NextConfig,\n): NextConfig {\n const projectRoot = process.cwd()\n const resolved = resolveConfig(projectRoot, fluentConfig)\n\n // Warn if compiled catalogs directory doesn't exist yet\n const compiledDir = resolve(projectRoot, resolved.compiledDir)\n if (!existsSync(compiledDir)) {\n console.warn(\n `\\n[fluenti] Compiled catalogs not found at ${resolved.compiledDir}.\\n` +\n `Run: npx fluenti extract && npx fluenti compile\\n`,\n )\n }\n\n // Generate server module for RSC\n const serverModulePath = generateServerModule(projectRoot, resolved)\n\n // Resolve the loader path — use import.meta.url for ESM compatibility\n const thisDir = typeof __dirname !== 'undefined'\n ? __dirname\n : dirname(new URL(import.meta.url).pathname)\n const loaderPath = resolve(thisDir, 'loader.js')\n\n const existingWebpack = nextConfig['webpack'] as\n | ((config: WebpackConfig, options: WebpackOptions) => WebpackConfig)\n | undefined\n\n let buildCompileRan = false\n\n return {\n ...nextConfig,\n webpack(config: WebpackConfig, options: WebpackOptions) {\n // Add fluenti loader (enforce: pre — runs before other loaders)\n config.module.rules.push({\n test: /\\.[jt]sx?$/,\n enforce: 'pre' as const,\n exclude: [/node_modules/, /\\.next/],\n use: [\n {\n loader: loaderPath,\n options: {\n serverModulePath,\n },\n },\n ],\n })\n\n // Add resolve alias so loader can import from generated server module\n config.resolve = config.resolve ?? {} as WebpackConfig['resolve']\n config.resolve.alias = config.resolve.alias ?? {}\n config.resolve.alias['@fluenti/next$'] = serverModulePath\n\n // Auto compile before production build (run once across server+client passes)\n const buildAutoCompile = fluentConfig.buildAutoCompile ?? true\n if (!options.dev && buildAutoCompile && !buildCompileRan) {\n buildCompileRan = true\n try {\n // Use node -e with dynamic import — avoids CLI binary resolution issues.\n // webpack() is sync, so we use execSync to block until compile finishes.\n const escapedRoot = projectRoot.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")\n execSync(\n `node --input-type=module -e \"const { runCompile } = await import('@fluenti/cli'); await runCompile('${escapedRoot}')\"`,\n { cwd: projectRoot, stdio: 'inherit' },\n )\n } catch {\n // @fluenti/cli not available or compile failed — skip silently\n }\n }\n\n // Auto extract+compile in dev mode\n const devAutoCompile = fluentConfig.devAutoCompile ?? true\n if (options.dev && devAutoCompile) {\n const devDelay = fluentConfig.devAutoCompileDelay ?? 1000\n const debouncedRun = createDebouncedRunner({ cwd: projectRoot }, devDelay)\n const compiledDirResolved = resolve(projectRoot, resolved.compiledDir)\n\n config.plugins = config.plugins ?? []\n config.plugins.push({\n apply(compiler: WebpackCompiler) {\n let isFirstBuild = true\n compiler.hooks.watchRun.tapAsync('fluenti-dev', (_compiler: WebpackCompiler, callback: () => void) => {\n if (isFirstBuild) {\n isFirstBuild = false\n debouncedRun()\n }\n const modifiedFiles = _compiler.modifiedFiles\n if (modifiedFiles) {\n const hasSourceChange = [...modifiedFiles].some((f: string) =>\n /\\.[jt]sx?$/.test(f)\n && !f.includes('node_modules')\n && !f.includes('.next')\n && !f.startsWith(compiledDirResolved),\n )\n if (hasSourceChange) {\n debouncedRun()\n }\n }\n callback()\n })\n },\n })\n }\n\n // Call user's webpack config if provided\n if (existingWebpack) {\n return existingWebpack(config, options)\n }\n\n return config\n },\n }\n}\n\n// Minimal webpack types for the config function\ninterface WebpackConfig {\n module: {\n rules: Array<{\n test: RegExp\n enforce?: 'pre' | 'post'\n exclude?: Array<RegExp>\n use: Array<{ loader: string; options: Record<string, unknown> }>\n }>\n }\n resolve: {\n alias?: Record<string, string>\n }\n plugins?: Array<{ apply(compiler: WebpackCompiler): void }>\n}\n\ninterface WebpackOptions {\n isServer: boolean\n dev: boolean\n}\n\ninterface WebpackCompiler {\n hooks: {\n watchRun: {\n tapAsync(name: string, cb: (compiler: WebpackCompiler, callback: () => void) => void): void\n }\n }\n modifiedFiles?: Set<string>\n}\n","/**\n * @fluenti/next — Next.js plugin for Fluenti\n *\n * Provides:\n * - `withFluenti()` — wraps next.config.ts with t`` transform support\n * - I18nProvider — async server component (exported from generated module)\n * - Webpack loader for strict, binding-aware tagged-template optimization\n *\n * @example\n * ```ts\n * // next.config.ts\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti()({ reactStrictMode: true })\n * ```\n *\n * @example\n * ```tsx\n * // app/layout.tsx — resolved by webpack alias to the generated module\n * import { I18nProvider } from '@fluenti/next'\n * ```\n */\nexport { withFluenti } from './with-fluenti'\nexport type { WithFluentConfig, I18nProviderProps } from './types'\n\n// ── Runtime stubs ────────────────────────────────────────────────────\n// TypeScript resolves types from this file (via package.json exports).\n// At runtime, webpack `resolve.alias` redirects `@fluenti/next$` to the\n// generated server module, so these stubs are never actually called in\n// a correctly configured project. They exist only to provide helpful\n// errors if `withFluenti()` is not configured.\n\nimport type { ReactNode, ReactElement } from 'react'\nimport type { CompileTimeT, FluentInstanceExtended } from '@fluenti/core'\n\nconst NOT_CONFIGURED =\n '[fluenti] `withFluenti()` must be configured in next.config.ts before importing from \"@fluenti/next\".'\n\nfunction throwNotConfigured(): never {\n throw new Error(NOT_CONFIGURED)\n}\n\n/** @see Generated module for the real implementation. */\nexport const setLocale: (locale: string) => void = throwNotConfigured\n/** @see Generated module for the real implementation. */\nexport const getI18n: () => Promise<FluentInstanceExtended & { locale: string }> = throwNotConfigured as () => Promise<FluentInstanceExtended & { locale: string }>\n/** @see Generated module for the real implementation. */\nexport const t: CompileTimeT = throwNotConfigured as unknown as CompileTimeT\n/** @see Generated module for the real implementation. */\nexport const Trans: (props: { children: ReactNode; id?: string; context?: string; comment?: string; render?: (translation: ReactNode) => ReactNode }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Trans\n/** @see Generated module for the real implementation. */\nexport const Plural: (props: { value: number; id?: string; context?: string; comment?: string; zero?: ReactNode; one?: ReactNode; two?: ReactNode; few?: ReactNode; many?: ReactNode; other: ReactNode; offset?: number }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Plural\n/** @see Generated module for the real implementation. */\nexport const Select: (props: { value: string; id?: string; context?: string; comment?: string; other: ReactNode; options?: Record<string, ReactNode>; [key: string]: ReactNode | Record<string, ReactNode> | string | undefined }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Select\n/** @see Generated module for the real implementation. */\nexport const DateTime: (props: { value: Date | number; style?: string }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof DateTime\n/** @see Generated module for the real implementation. */\nexport const NumberFormat: (props: { value: number; style?: string }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof NumberFormat\n/** @see Generated module for the real implementation. */\nexport const I18nProvider: (props: { locale?: string; children: ReactNode }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof I18nProvider\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/read-config.ts","../src/generate-server-module.ts","../src/dev-runner.ts","../src/with-fluenti.ts","../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, extname, join, resolve } from 'node:path'\nimport type { FluentiConfig } from '@fluenti/core'\nimport type { WithFluentConfig, ResolvedFluentConfig } from './types'\n\nconst runtimeModulePath = typeof __filename === 'string'\n ? __filename\n : import.meta.url\nconst require = createRequire(runtimeModulePath)\nconst { createJiti } = require('jiti') as {\n createJiti: (\n url: string,\n options?: { moduleCache?: boolean; interopDefault?: boolean },\n ) => (path: string) => unknown\n}\n\n/**\n * Read fluenti.config.ts and merge with withFluenti() overrides.\n */\nexport function resolveConfig(\n projectRoot: string,\n overrides?: WithFluentConfig,\n): ResolvedFluentConfig {\n const fileConfig = readFluentConfigSync(projectRoot)\n\n const defaultLocale = overrides?.defaultLocale\n ?? fileConfig?.sourceLocale\n ?? 'en'\n\n const locales = overrides?.locales\n ?? fileConfig?.locales\n ?? [defaultLocale]\n\n const compiledDir = overrides?.compiledDir\n ?? fileConfig?.compileOutDir\n ?? './src/locales/compiled'\n\n const serverModuleOutDir = overrides?.serverModuleOutDir\n ?? join('node_modules', '.fluenti')\n\n const cookieName = overrides?.cookieName ?? 'locale'\n\n const resolved: ResolvedFluentConfig = {\n locales,\n defaultLocale,\n compiledDir,\n serverModule: overrides?.serverModule ?? null,\n serverModuleOutDir,\n cookieName,\n }\n if (overrides?.resolveLocale) resolved.resolveLocale = overrides.resolveLocale\n if (overrides?.dateFormats) resolved.dateFormats = overrides.dateFormats\n if (overrides?.numberFormats) resolved.numberFormats = overrides.numberFormats\n if (overrides?.fallbackChain) resolved.fallbackChain = overrides.fallbackChain\n return resolved\n}\n\n/**\n * Attempt to read fluenti.config.ts synchronously.\n * Returns null if file doesn't exist or can't be parsed.\n */\nfunction readFluentConfigSync(projectRoot: string): FluentiConfig | null {\n const jiti = createJiti(runtimeModulePath, {\n moduleCache: false,\n interopDefault: true,\n })\n const candidates = [\n 'fluenti.config.ts',\n 'fluenti.config.js',\n 'fluenti.config.mjs',\n ]\n\n for (const name of candidates) {\n const configPath = resolve(projectRoot, name)\n if (existsSync(configPath)) {\n try {\n return normalizeLoadedConfig(jiti(configPath) as FluentiConfig | { default?: FluentiConfig })\n } catch {\n const rewritten = tryLoadConfigViaDefineConfigShim(configPath, jiti)\n if (rewritten) {\n return rewritten\n }\n return null\n }\n }\n }\n\n return null\n}\n\nfunction tryLoadConfigViaDefineConfigShim(\n configPath: string,\n jiti: (path: string) => unknown,\n): FluentiConfig | null {\n const source = readFileSync(configPath, 'utf8')\n const importMatch = source.match(\n /import\\s*\\{\\s*defineConfig(?:\\s+as\\s+([A-Za-z_$][\\w$]*))?\\s*\\}\\s*from\\s*['\"]@fluenti\\/cli['\"]\\s*;?/,\n )\n if (!importMatch) {\n return null\n }\n\n const helperName = importMatch[1] ?? 'defineConfig'\n const rewrittenSource = source.replace(importMatch[0], '')\n const tempPath = join(\n dirname(configPath),\n `.${basename(configPath, extname(configPath))}.next-plugin-read-config${extname(configPath) || '.ts'}`,\n )\n\n writeFileSync(tempPath, `const ${helperName} = (config) => config\\n${rewrittenSource}`, 'utf8')\n\n try {\n return normalizeLoadedConfig(jiti(tempPath) as FluentiConfig | { default?: FluentiConfig })\n } catch {\n return null\n } finally {\n rmSync(tempPath, { force: true })\n }\n}\n\nfunction normalizeLoadedConfig(\n mod: FluentiConfig | { default?: FluentiConfig },\n): FluentiConfig {\n return typeof mod === 'object' && mod !== null && 'default' in mod\n ? (mod.default ?? {}) as FluentiConfig\n : mod as FluentiConfig\n}\n","import { writeFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { resolve, relative } from 'node:path'\nimport { validateLocale } from '@fluenti/core'\nimport type { ResolvedFluentConfig } from './types'\n\n/**\n * Generate the server module that provides:\n * - setLocale / getI18n\n * - Trans / Plural / Select / DateTime / NumberFormat (server components)\n * - I18nProvider (async server component for layouts)\n *\n * @returns Absolute path to the generated server module.\n */\nexport function generateServerModule(\n projectRoot: string,\n config: ResolvedFluentConfig,\n): string {\n if (config.serverModule) {\n return resolve(projectRoot, config.serverModule)\n }\n\n const outDir = resolve(projectRoot, config.serverModuleOutDir)\n const outPath = resolve(outDir, 'server.js')\n const dtsPath = resolve(outDir, 'server.d.ts')\n\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true })\n }\n\n for (const locale of config.locales) {\n validateLocale(locale, 'next-plugin')\n }\n\n const compiledDirAbs = resolve(projectRoot, config.compiledDir)\n const compiledRelative = toForwardSlash(relative(outDir, compiledDirAbs))\n\n const localeImports = config.locales\n .map((locale) => ` case '${locale}': return import('${compiledRelative}/${locale}')`)\n .join('\\n')\n\n const fallbackChainStr = config.fallbackChain\n ? JSON.stringify(config.fallbackChain)\n : 'undefined'\n\n // Generate a 'use client' provider that imports messages statically.\n // Messages contain functions (interpolation) which can't cross the RSC boundary.\n const clientProviderPath = resolve(outDir, 'client-provider.js')\n\n const clientStaticImports = config.locales\n .map((locale) => {\n const safe = locale.replace(/[^a-zA-Z0-9]/g, '_')\n return `import ${safe} from '${compiledRelative}/${locale}'`\n })\n .join('\\n')\n\n const clientAllMessagesEntries = config.locales\n .map((locale) => {\n const safe = locale.replace(/[^a-zA-Z0-9]/g, '_')\n return `'${locale}': ${safe}`\n })\n .join(', ')\n\n const clientProviderSource = `\"use client\";\n// Auto-generated by @fluenti/next — do not edit\nimport { createElement } from 'react'\nimport { I18nProvider } from '@fluenti/react'\n${clientStaticImports}\n\nconst __allMessages = { ${clientAllMessagesEntries} }\n\nexport function ClientI18nProvider({ locale, fallbackLocale, fallbackChain, children }) {\n return createElement(I18nProvider, { locale, fallbackLocale, messages: __allMessages, fallbackChain }, children)\n}\n`\n writeFileSync(clientProviderPath, clientProviderSource, 'utf-8')\n\n const resolveLocaleImport = config.resolveLocale\n ? (() => {\n const absPath = resolve(projectRoot, config.resolveLocale)\n const relPath = toForwardSlash(relative(outDir, absPath))\n return `import __resolveLocale from '${relPath}'`\n })()\n : null\n\n const resolveLocaleFn = config.resolveLocale\n ? `resolveLocale: __resolveLocale,`\n : `resolveLocale: async () => {\n try {\n const { cookies, headers } = await import('next/headers')\n const [cookieStore, headerStore] = await Promise.all([cookies(), headers()])\n\n // 1. Referer URL path segment (available in Server Action context)\n const referer = headerStore.get('referer')\n if (referer) {\n try {\n const seg = new URL(referer).pathname.split('/')[1]\n if (seg && __locales.includes(seg)) return seg\n } catch {}\n }\n\n // 2. Cookie (configurable name)\n const fromCookie = cookieStore.get('${config.cookieName}')?.value\n if (fromCookie && __locales.includes(fromCookie)) return fromCookie\n\n // 3. Accept-Language header\n const acceptLang = headerStore.get('accept-language')\n if (acceptLang) {\n for (const part of acceptLang.split(',')) {\n const lang = part.split(';')[0].trim()\n if (__locales.includes(lang)) return lang\n const prefix = lang.split('-')[0]\n const match = __locales.find(l => l === prefix || l.startsWith(prefix + '-'))\n if (match) return match\n }\n }\n\n return '${config.defaultLocale}'\n } catch {\n return '${config.defaultLocale}'\n }\n },`\n\n const localesArrayStr = JSON.stringify(config.locales)\n\n const moduleSource = `// Auto-generated by @fluenti/next — do not edit\nimport { createServerI18n } from '@fluenti/react/server'\nimport { createElement } from 'react'\n${resolveLocaleImport ? `${resolveLocaleImport}\\n` : ''}\nconst __locales = ${localesArrayStr}\n\nconst serverI18n = createServerI18n({\n loadMessages: async (locale) => {\n switch (locale) {\n${localeImports}\n default: return import('${compiledRelative}/${config.defaultLocale}')\n }\n },\n fallbackLocale: '${config.defaultLocale}',\n fallbackChain: ${fallbackChainStr},\n ${resolveLocaleFn}\n})\n\nexport const setLocale = serverI18n.setLocale\nexport const getI18n = serverI18n.getI18n\nexport const t = (..._args) => {\n throw new Error(\n \"[fluenti] \\`t\\` imported from '@fluenti/next' is a compile-time API replaced at build time.\\\\n\" +\n ' Ensure:\\\\n' +\n ' 1. \\`withFluenti()\\` is configured in next.config.ts\\\\n' +\n ' 2. The file is inside src/ (not node_modules)\\\\n' +\n \" 3. For client components, import from '@fluenti/react'\",\n )\n}\nexport const Trans = serverI18n.Trans\nexport const Plural = serverI18n.Plural\nexport const Select = serverI18n.Select\nexport const DateTime = serverI18n.DateTime\nexport const NumberFormat = serverI18n.NumberFormat\n\n/**\n * Async server component for root layouts.\n *\n * Sets up both server-side (React.cache) and client-side (I18nProvider) i18n.\n */\nexport async function I18nProvider({ locale, children }) {\n const activeLocale = locale ?? '${config.defaultLocale}'\n\n // 1. Initialize server-side i18n (React.cache scoped)\n serverI18n.setLocale(activeLocale)\n await serverI18n.getI18n()\n\n // 2. Import the local 'use client' provider that has messages statically bundled.\n // Messages contain functions (interpolation) which can't be serialized across the RSC boundary.\n const { ClientI18nProvider } = await import('./client-provider.js')\n\n return createElement(ClientI18nProvider, {\n locale: activeLocale,\n fallbackLocale: '${config.defaultLocale}',\n fallbackChain: ${fallbackChainStr},\n }, children)\n}\n`\n\n const dtsSource = `// Auto-generated by @fluenti/next — do not edit\nimport type { ReactNode, ReactElement } from 'react'\nimport type { CompileTimeT, FluentInstanceExtended } from '@fluenti/core'\n\nexport declare function setLocale(locale: string): void\nexport declare function getI18n(): Promise<FluentInstanceExtended & { locale: string }>\nexport declare const t: CompileTimeT\n\nexport declare function Trans(props: {\n children: ReactNode\n id?: string\n context?: string\n comment?: string\n render?: (translation: ReactNode) => ReactNode\n}): Promise<ReactElement>\n\nexport declare function Plural(props: {\n value: number\n id?: string\n context?: string\n comment?: string\n zero?: ReactNode\n one?: ReactNode\n two?: ReactNode\n few?: ReactNode\n many?: ReactNode\n other: ReactNode\n offset?: number\n}): Promise<ReactElement>\n\nexport declare function Select(props: {\n value: string\n id?: string\n context?: string\n comment?: string\n other: ReactNode\n options?: Record<string, ReactNode>\n [key: string]: ReactNode | Record<string, ReactNode> | string | undefined\n}): Promise<ReactElement>\n\nexport declare function DateTime(props: {\n value: Date | number\n style?: string\n}): Promise<ReactElement>\n\nexport declare function NumberFormat(props: {\n value: number\n style?: string\n}): Promise<ReactElement>\n\nexport declare function I18nProvider(props: {\n locale?: string\n children: ReactNode\n}): Promise<ReactElement>\n`\n\n writeFileSync(outPath, moduleSource, 'utf-8')\n writeFileSync(dtsPath, dtsSource, 'utf-8')\n\n return outPath\n}\n\nfunction toForwardSlash(p: string): string {\n return p.split('\\\\').join('/')\n}\n","import { exec } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { resolve, dirname, join } from 'node:path'\nimport { createRequire } from 'node:module'\n\nexport interface DevRunnerOptions {\n cwd: string\n onSuccess?: () => void\n onError?: (err: Error) => void\n /** If true, reject the promise on failure instead of swallowing the error */\n throwOnError?: boolean\n /** Run only compile (skip extract). Useful for production builds where source is unchanged. */\n compileOnly?: boolean\n}\n\n/**\n * Walk up from `cwd` to find `node_modules/.bin/fluenti`.\n * Returns the absolute path or null if not found.\n */\nexport function resolveCliBin(cwd: string): string | null {\n let dir = cwd\n for (;;) {\n const bin = resolve(dir, 'node_modules/.bin/fluenti')\n if (existsSync(bin)) return bin\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n return null\n}\n\n/**\n * Run compile in-process via `@fluenti/cli` (for compileOnly mode),\n * or fall back to shell-out for extract + compile (dev mode).\n */\nexport async function runExtractCompile(options: DevRunnerOptions): Promise<void> {\n if (options.compileOnly) {\n try {\n // Resolve @fluenti/cli from the project's cwd (not from this package's location)\n // using createRequire so pnpm's strict node_modules layout works correctly.\n // Use require() (not import()) to load @fluenti/cli — avoids CJS/ESM interop\n // issues when dynamic import() loads minified CJS with chunk requires.\n const projectRequire = createRequire(join(options.cwd, 'package.json'))\n const { runCompile } = projectRequire('@fluenti/cli')\n await runCompile(options.cwd)\n console.log('[fluenti] Compiling... done')\n options.onSuccess?.()\n return\n } catch (e) {\n const error = e instanceof Error ? e : new Error(String(e))\n if (options.throwOnError) throw error\n console.warn('[fluenti] Compile failed:', error.message)\n options.onError?.(error)\n return\n }\n }\n\n // Dev mode: shell out for extract + compile\n const bin = resolveCliBin(options.cwd)\n if (!bin) {\n const msg = '[fluenti] CLI not found — skipping auto-compile. Install @fluenti/cli as a devDependency.'\n if (options.throwOnError) {\n return Promise.reject(new Error(msg))\n }\n console.warn(msg)\n return Promise.resolve()\n }\n\n const command = `${bin} extract && ${bin} compile`\n return new Promise<void>((resolve, reject) => {\n exec(\n command,\n { cwd: options.cwd },\n (err, _stdout, stderr) => {\n if (err) {\n const error = new Error(stderr || err.message)\n if (options.throwOnError) {\n reject(error)\n return\n }\n console.warn('[fluenti] Extract/compile failed:', error.message)\n options.onError?.(error)\n } else {\n console.log('[fluenti] Extracting and compiling... done')\n options.onSuccess?.()\n }\n resolve()\n },\n )\n })\n}\n\n/**\n * Create a debounced runner that collapses rapid calls.\n *\n * - If called while idle, schedules a run after `delay` ms.\n * - If called while a run is in progress, marks a pending rerun.\n * - Never runs concurrently.\n */\nexport function createDebouncedRunner(\n options: DevRunnerOptions,\n delay = 300,\n): () => void {\n let timer: ReturnType<typeof setTimeout> | null = null\n let running = false\n let pendingRerun = false\n\n async function execute(): Promise<void> {\n running = true\n try {\n await runExtractCompile(options)\n } finally {\n running = false\n if (pendingRerun) {\n pendingRerun = false\n schedule()\n }\n }\n }\n\n function schedule(): void {\n if (timer !== null) {\n clearTimeout(timer)\n }\n timer = setTimeout(() => {\n timer = null\n if (running) {\n pendingRerun = true\n } else {\n execute()\n }\n }, delay)\n }\n\n return schedule\n}\n","import { existsSync } from 'node:fs'\nimport { execSync } from 'node:child_process'\nimport { resolve, dirname } from 'node:path'\nimport type { WithFluentConfig } from './types'\nimport { resolveConfig } from './read-config'\nimport { generateServerModule } from './generate-server-module'\nimport { createDebouncedRunner } from './dev-runner'\n\ntype NextConfig = Record<string, unknown>\n\n/**\n * Wrap your Next.js config with Fluenti support.\n *\n * Adds a webpack loader that transforms `t\\`\\`` and `t()` calls,\n * and generates a server module for RSC i18n.\n *\n * @example\n * ```ts\n * // next.config.ts — function style (recommended)\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti()({ reactStrictMode: true })\n * ```\n *\n * @example\n * ```ts\n * // next.config.ts — direct style\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti({ reactStrictMode: true })\n * ```\n */\nexport function withFluenti(fluentConfig?: WithFluentConfig): (nextConfig?: NextConfig) => NextConfig\nexport function withFluenti(nextConfig: NextConfig): NextConfig\nexport function withFluenti(\n configOrNext?: WithFluentConfig | NextConfig,\n): NextConfig | ((nextConfig?: NextConfig) => NextConfig) {\n if (configOrNext && isNextConfig(configOrNext as NextConfig)) {\n return applyFluenti({}, configOrNext as NextConfig)\n }\n\n const fluentConfig = (configOrNext ?? {}) as WithFluentConfig\n return function wrappedConfig(nextConfig?: NextConfig): NextConfig {\n return applyFluenti(fluentConfig, nextConfig ?? {})\n }\n}\n\nfunction isNextConfig(obj: NextConfig): boolean {\n const nextKeys = [\n 'reactStrictMode', 'experimental', 'images', 'env', 'webpack',\n 'rewrites', 'redirects', 'headers', 'pageExtensions', 'output',\n 'basePath', 'i18n', 'trailingSlash', 'compiler', 'transpilePackages',\n ]\n return nextKeys.some((key) => key in obj)\n}\n\nfunction applyFluenti(\n fluentConfig: WithFluentConfig,\n nextConfig: NextConfig,\n): NextConfig {\n const projectRoot = process.cwd()\n const resolved = resolveConfig(projectRoot, fluentConfig)\n\n // Warn if compiled catalogs directory doesn't exist yet\n const compiledDir = resolve(projectRoot, resolved.compiledDir)\n if (!existsSync(compiledDir)) {\n console.warn(\n `\\n[fluenti] Compiled catalogs not found at ${resolved.compiledDir}.\\n` +\n `Run: npx fluenti extract && npx fluenti compile\\n`,\n )\n }\n\n // Generate server module for RSC\n const serverModulePath = generateServerModule(projectRoot, resolved)\n\n // Resolve the loader path — use import.meta.url for ESM compatibility\n const thisDir = typeof __dirname !== 'undefined'\n ? __dirname\n : dirname(new URL(import.meta.url).pathname)\n const loaderPath = resolve(thisDir, 'loader.js')\n\n const existingWebpack = nextConfig['webpack'] as\n | ((config: WebpackConfig, options: WebpackOptions) => WebpackConfig)\n | undefined\n\n let buildCompileRan = false\n\n return {\n ...nextConfig,\n webpack(config: WebpackConfig, options: WebpackOptions) {\n // Add fluenti loader (enforce: pre — runs before other loaders)\n config.module.rules.push({\n test: /\\.[jt]sx?$/,\n enforce: 'pre' as const,\n exclude: [/node_modules/, /\\.next/],\n use: [\n {\n loader: loaderPath,\n options: {\n serverModulePath,\n },\n },\n ],\n })\n\n // Add resolve alias so loader can import from generated server module\n config.resolve = config.resolve ?? {} as WebpackConfig['resolve']\n config.resolve.alias = config.resolve.alias ?? {}\n config.resolve.alias['@fluenti/next$'] = serverModulePath\n\n // Auto compile before production build (run once across server+client passes)\n const buildAutoCompile = fluentConfig.buildAutoCompile ?? true\n if (!options.dev && buildAutoCompile && !buildCompileRan) {\n buildCompileRan = true\n try {\n // Use node -e with dynamic import — avoids CLI binary resolution issues.\n // webpack() is sync, so we use execSync to block until compile finishes.\n const escapedRoot = projectRoot.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")\n execSync(\n `node --input-type=module -e \"const { runCompile } = await import('@fluenti/cli'); await runCompile('${escapedRoot}')\"`,\n { cwd: projectRoot, stdio: 'inherit' },\n )\n } catch {\n // @fluenti/cli not available or compile failed — skip silently\n }\n }\n\n // Auto extract+compile in dev mode\n const devAutoCompile = fluentConfig.devAutoCompile ?? true\n if (options.dev && devAutoCompile) {\n const devDelay = fluentConfig.devAutoCompileDelay ?? 1000\n const debouncedRun = createDebouncedRunner({ cwd: projectRoot }, devDelay)\n const compiledDirResolved = resolve(projectRoot, resolved.compiledDir)\n\n config.plugins = config.plugins ?? []\n config.plugins.push({\n apply(compiler: WebpackCompiler) {\n let isFirstBuild = true\n compiler.hooks.watchRun.tapAsync('fluenti-dev', (_compiler: WebpackCompiler, callback: () => void) => {\n if (isFirstBuild) {\n isFirstBuild = false\n debouncedRun()\n }\n const modifiedFiles = _compiler.modifiedFiles\n if (modifiedFiles) {\n const hasSourceChange = [...modifiedFiles].some((f: string) =>\n /\\.[jt]sx?$/.test(f)\n && !f.includes('node_modules')\n && !f.includes('.next')\n && !f.startsWith(compiledDirResolved),\n )\n if (hasSourceChange) {\n debouncedRun()\n }\n }\n callback()\n })\n },\n })\n }\n\n // Call user's webpack config if provided\n if (existingWebpack) {\n return existingWebpack(config, options)\n }\n\n return config\n },\n }\n}\n\n// Minimal webpack types for the config function\ninterface WebpackConfig {\n module: {\n rules: Array<{\n test: RegExp\n enforce?: 'pre' | 'post'\n exclude?: Array<RegExp>\n use: Array<{ loader: string; options: Record<string, unknown> }>\n }>\n }\n resolve: {\n alias?: Record<string, string>\n }\n plugins?: Array<{ apply(compiler: WebpackCompiler): void }>\n}\n\ninterface WebpackOptions {\n isServer: boolean\n dev: boolean\n}\n\ninterface WebpackCompiler {\n hooks: {\n watchRun: {\n tapAsync(name: string, cb: (compiler: WebpackCompiler, callback: () => void) => void): void\n }\n }\n modifiedFiles?: Set<string>\n}\n","/**\n * @fluenti/next — Next.js plugin for Fluenti\n *\n * Provides:\n * - `withFluenti()` — wraps next.config.ts with t`` transform support\n * - I18nProvider — async server component (exported from generated module)\n * - Webpack loader for strict, binding-aware tagged-template optimization\n *\n * @example\n * ```ts\n * // next.config.ts\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti()({ reactStrictMode: true })\n * ```\n *\n * @example\n * ```tsx\n * // app/layout.tsx — resolved by webpack alias to the generated module\n * import { I18nProvider } from '@fluenti/next'\n * ```\n */\nexport { withFluenti } from './with-fluenti'\nexport type { WithFluentConfig, I18nProviderProps } from './types'\n\n// ── Runtime stubs ────────────────────────────────────────────────────\n// TypeScript resolves types from this file (via package.json exports).\n// At runtime, webpack `resolve.alias` redirects `@fluenti/next$` to the\n// generated server module, so these stubs are never actually called in\n// a correctly configured project. They exist only to provide helpful\n// errors if `withFluenti()` is not configured.\n\nimport type { ReactNode, ReactElement } from 'react'\nimport type { CompileTimeT, FluentInstanceExtended } from '@fluenti/core'\n\nconst NOT_CONFIGURED =\n '[fluenti] `withFluenti()` must be configured in next.config.ts before importing from \"@fluenti/next\".'\n\nfunction throwNotConfigured(): never {\n throw new Error(NOT_CONFIGURED)\n}\n\n/** @see Generated module for the real implementation. */\nexport const setLocale: (locale: string) => void = throwNotConfigured\n/** @see Generated module for the real implementation. */\nexport const getI18n: () => Promise<FluentInstanceExtended & { locale: string }> = throwNotConfigured as () => Promise<FluentInstanceExtended & { locale: string }>\n/** @see Generated module for the real implementation. */\nexport const t: CompileTimeT = throwNotConfigured as unknown as CompileTimeT\n/** @see Generated module for the real implementation. */\nexport const Trans: (props: { children: ReactNode; id?: string; context?: string; comment?: string; render?: (translation: ReactNode) => ReactNode }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Trans\n/** @see Generated module for the real implementation. */\nexport const Plural: (props: { value: number; id?: string; context?: string; comment?: string; zero?: ReactNode; one?: ReactNode; two?: ReactNode; few?: ReactNode; many?: ReactNode; other: ReactNode; offset?: number }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Plural\n/** @see Generated module for the real implementation. */\nexport const Select: (props: { value: string; id?: string; context?: string; comment?: string; other: ReactNode; options?: Record<string, ReactNode>; [key: string]: ReactNode | Record<string, ReactNode> | string | undefined }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Select\n/** @see Generated module for the real implementation. */\nexport const DateTime: (props: { value: Date | number; style?: string }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof DateTime\n/** @see Generated module for the real implementation. */\nexport const NumberFormat: (props: { value: number; style?: string }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof NumberFormat\n/** @see Generated module for the real implementation. */\nexport const I18nProvider: (props: { locale?: string; children: ReactNode }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof I18nProvider\n"],"mappings":"uMAMA,IAAM,EAAoB,OAAO,YAAe,SAC5C,WAAA,EAAA,CACY,IAEV,CAAE,eAAA,EAAA,EAAA,eADsB,EAAkB,CACjB,OAAO,CAUtC,SAAgB,EACd,EACA,EACsB,CACtB,IAAM,EAAa,EAAqB,EAAY,CAE9C,EAAgB,GAAW,eAC5B,GAAY,cACZ,KAEC,EAAU,GAAW,SACtB,GAAY,SACZ,CAAC,EAAc,CAEd,EAAc,GAAW,aAC1B,GAAY,eACZ,yBAEC,EAAqB,GAAW,qBAAA,EAAA,EAAA,MAC5B,eAAgB,WAAW,CAE/B,EAAa,GAAW,YAAc,SAEtC,EAAiC,CACrC,UACA,gBACA,cACA,aAAc,GAAW,cAAgB,KACzC,qBACA,aACD,CAKD,OAJI,GAAW,gBAAe,EAAS,cAAgB,EAAU,eAC7D,GAAW,cAAa,EAAS,YAAc,EAAU,aACzD,GAAW,gBAAe,EAAS,cAAgB,EAAU,eAC7D,GAAW,gBAAe,EAAS,cAAgB,EAAU,eAC1D,EAOT,SAAS,EAAqB,EAA2C,CACvE,IAAM,EAAO,EAAW,EAAmB,CACzC,YAAa,GACb,eAAgB,GACjB,CAAC,CAOF,IAAK,IAAM,IANQ,CACjB,oBACA,oBACA,qBACD,CAE8B,CAC7B,IAAM,GAAA,EAAA,EAAA,SAAqB,EAAa,EAAK,CAC7C,IAAA,EAAA,EAAA,YAAe,EAAW,CACxB,GAAI,CACF,OAAO,EAAsB,EAAK,EAAW,CAAgD,MACvF,CAKN,OAJkB,EAAiC,EAAY,EAAK,EAI7D,MAKb,OAAO,KAGT,SAAS,EACP,EACA,EACsB,CACtB,IAAM,GAAA,EAAA,EAAA,cAAsB,EAAY,OAAO,CACzC,EAAc,EAAO,MACzB,qGACD,CACD,GAAI,CAAC,EACH,OAAO,KAGT,IAAM,EAAa,EAAY,IAAM,eAC/B,EAAkB,EAAO,QAAQ,EAAY,GAAI,GAAG,CACpD,GAAA,EAAA,EAAA,OAAA,EAAA,EAAA,SACI,EAAW,CACnB,KAAA,EAAA,EAAA,UAAa,GAAA,EAAA,EAAA,SAAoB,EAAW,CAAC,CAAC,2BAAA,EAAA,EAAA,SAAkC,EAAW,EAAI,QAChG,EAED,EAAA,EAAA,eAAc,EAAU,SAAS,EAAW,yBAAyB,IAAmB,OAAO,CAE/F,GAAI,CACF,OAAO,EAAsB,EAAK,EAAS,CAAgD,MACrF,CACN,OAAO,YACC,EACR,EAAA,EAAA,QAAO,EAAU,CAAE,MAAO,GAAM,CAAC,EAIrC,SAAS,EACP,EACe,CACf,OAAO,OAAO,GAAQ,UAAY,GAAgB,YAAa,EAC1D,EAAI,SAAW,EAAE,CAClB,ECjHN,SAAgB,EACd,EACA,EACQ,CACR,GAAI,EAAO,aACT,OAAA,EAAA,EAAA,SAAe,EAAa,EAAO,aAAa,CAGlD,IAAM,GAAA,EAAA,EAAA,SAAiB,EAAa,EAAO,mBAAmB,CACxD,GAAA,EAAA,EAAA,SAAkB,EAAQ,YAAY,CACtC,GAAA,EAAA,EAAA,SAAkB,EAAQ,cAAc,EAE1C,EAAA,EAAA,YAAY,EAAO,GACrB,EAAA,EAAA,WAAU,EAAQ,CAAE,UAAW,GAAM,CAAC,CAGxC,IAAK,IAAM,KAAU,EAAO,SAC1B,EAAA,EAAA,gBAAe,EAAQ,cAAc,CAIvC,IAAM,EAAmB,GAAA,EAAA,EAAA,UAAwB,GAAA,EAAA,EAAA,SADlB,EAAa,EAAO,YAAY,CACS,CAAC,CAEnE,EAAgB,EAAO,QAC1B,IAAK,GAAW,eAAe,EAAO,oBAAoB,EAAiB,GAAG,EAAO,IAAI,CACzF,KAAK;EAAK,CAEP,EAAmB,EAAO,cAC5B,KAAK,UAAU,EAAO,cAAc,CACpC,aAgCJ,EAAA,EAAA,gBAAA,EAAA,EAAA,SA5BmC,EAAQ,qBAAqB,CAgBnC;;;;EAdD,EAAO,QAChC,IAAK,GAEG,UADM,EAAO,QAAQ,gBAAiB,IAAI,CAC3B,SAAS,EAAiB,GAAG,EAAO,GAC1D,CACD,KAAK;EAAK,CAaO;;0BAXa,EAAO,QACrC,IAAK,GAEG,IAAI,EAAO,KADL,EAAO,QAAQ,gBAAiB,IAAI,GAEjD,CACD,KAAK,KAAK,CAQoC;;;;;EAMO,QAAQ,CAEhE,IAAM,EAAsB,EAAO,cAItB,gCADS,GAAA,EAAA,EAAA,UAAwB,GAAA,EAAA,EAAA,SADhB,EAAa,EAAO,cAAc,CACF,CAAC,CACV,GAEjD,KAEE,EAAkB,EAAO,cAC3B,kCACA;;;;;;;;;;;;;;;4CAesC,EAAO,WAAW;;;;;;;;;;;;;;;gBAe9C,EAAO,cAAc;;gBAErB,EAAO,cAAc;;MAI7B,EAAkB,KAAK,UAAU,EAAO,QAAQ,CAEhD,EAAe;;;EAGrB,EAAsB,GAAG,EAAoB,IAAM,GAAG;oBACpC,EAAgB;;;;;EAKlC,EAAc;gCACgB,EAAiB,GAAG,EAAO,cAAc;;;qBAGpD,EAAO,cAAc;mBACvB,EAAiB;IAChC,EAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;oCA0BgB,EAAO,cAAc;;;;;;;;;;;;uBAYlC,EAAO,cAAc;qBACvB,EAAiB;;;EAgEpC,OAHA,EAAA,EAAA,eAAc,EAAS,EAAc,QAAQ,EAC7C,EAAA,EAAA,eAAc,EAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAW,QAAQ,CAEnC,EAGT,SAAS,EAAe,EAAmB,CACzC,OAAO,EAAE,MAAM,KAAK,CAAC,KAAK,IAAI,CCnOhC,SAAgB,EAAc,EAA4B,CACxD,IAAI,EAAM,EACV,OAAS,CACP,IAAM,GAAA,EAAA,EAAA,SAAc,EAAK,4BAA4B,CACrD,IAAA,EAAA,EAAA,YAAe,EAAI,CAAE,OAAO,EAC5B,IAAM,GAAA,EAAA,EAAA,SAAiB,EAAI,CAC3B,GAAI,IAAW,EAAK,MACpB,EAAM,EAER,OAAO,KAOT,eAAsB,EAAkB,EAA0C,CAChF,GAAI,EAAQ,YACV,GAAI,CAMF,GAAM,CAAE,eAAA,EAAA,EAAA,gBAAA,EAAA,EAAA,MADkC,EAAQ,IAAK,eAAe,CAAC,CACjC,eAAe,CACrD,MAAM,EAAW,EAAQ,IAAI,CAC7B,QAAQ,IAAI,8BAA8B,CAC1C,EAAQ,aAAa,CACrB,aACO,EAAG,CACV,IAAM,EAAQ,aAAa,MAAQ,EAAQ,MAAM,OAAO,EAAE,CAAC,CAC3D,GAAI,EAAQ,aAAc,MAAM,EAChC,QAAQ,KAAK,4BAA6B,EAAM,QAAQ,CACxD,EAAQ,UAAU,EAAM,CACxB,OAKJ,IAAM,EAAM,EAAc,EAAQ,IAAI,CACtC,GAAI,CAAC,EAAK,CACR,IAAM,EAAM,4FAKZ,OAJI,EAAQ,aACH,QAAQ,OAAW,MAAM,EAAI,CAAC,EAEvC,QAAQ,KAAK,EAAI,CACV,QAAQ,SAAS,EAG1B,IAAM,EAAU,GAAG,EAAI,cAAc,EAAI,UACzC,OAAO,IAAI,SAAe,EAAS,IAAW,EAC5C,EAAA,EAAA,MACE,EACA,CAAE,IAAK,EAAQ,IAAK,EACnB,EAAK,EAAS,IAAW,CACxB,GAAI,EAAK,CACP,IAAM,EAAY,MAAM,GAAU,EAAI,QAAQ,CAC9C,GAAI,EAAQ,aAAc,CACxB,EAAO,EAAM,CACb,OAEF,QAAQ,KAAK,oCAAqC,EAAM,QAAQ,CAChE,EAAQ,UAAU,EAAM,MAExB,QAAQ,IAAI,6CAA6C,CACzD,EAAQ,aAAa,CAEvB,GAAS,EAEZ,EACD,CAUJ,SAAgB,EACd,EACA,EAAQ,IACI,CACZ,IAAI,EAA8C,KAC9C,EAAU,GACV,EAAe,GAEnB,eAAe,GAAyB,CACtC,EAAU,GACV,GAAI,CACF,MAAM,EAAkB,EAAQ,QACxB,CACR,EAAU,GACN,IACF,EAAe,GACf,GAAU,GAKhB,SAAS,GAAiB,CACpB,IAAU,MACZ,aAAa,EAAM,CAErB,EAAQ,eAAiB,CACvB,EAAQ,KACJ,EACF,EAAe,GAEf,GAAS,EAEV,EAAM,CAGX,OAAO,ECtGT,SAAgB,EACd,EACwD,CACxD,GAAI,GAAgB,EAAa,EAA2B,CAC1D,OAAO,EAAa,EAAE,CAAE,EAA2B,CAGrD,IAAM,EAAgB,GAAgB,EAAE,CACxC,OAAO,SAAuB,EAAqC,CACjE,OAAO,EAAa,EAAc,GAAc,EAAE,CAAC,EAIvD,SAAS,EAAa,EAA0B,CAM9C,MALiB,CACf,kBAAmB,eAAgB,SAAU,MAAO,UACpD,WAAY,YAAa,UAAW,iBAAkB,SACtD,WAAY,OAAQ,gBAAiB,WAAY,oBAClD,CACe,KAAM,GAAQ,KAAO,EAAI,CAG3C,SAAS,EACP,EACA,EACY,CACZ,IAAM,EAAc,QAAQ,KAAK,CAC3B,EAAW,EAAc,EAAa,EAAa,EAIrD,EAAA,EAAA,aAAA,EAAA,EAAA,SADwB,EAAa,EAAS,YAAY,CAClC,EAC1B,QAAQ,KACN,8CAA8C,EAAS,YAAY,sDAEpE,CAIH,IAAM,EAAmB,EAAqB,EAAa,EAAS,CAM9D,GAAA,EAAA,EAAA,SAHU,OAAO,UAAc,IACjC,WAAA,EAAA,EAAA,SACQ,IAAI,IAAA,EAAA,CAAgB,IAAI,CAAC,SAAS,CACV,YAAY,CAE1C,EAAkB,EAAW,QAI/B,EAAkB,GAEtB,MAAO,CACL,GAAG,EACH,QAAQ,EAAuB,EAAyB,CAEtD,EAAO,OAAO,MAAM,KAAK,CACvB,KAAM,aACN,QAAS,MACT,QAAS,CAAC,eAAgB,SAAS,CACnC,IAAK,CACH,CACE,OAAQ,EACR,QAAS,CACP,mBACD,CACF,CACF,CACF,CAAC,CAGF,EAAO,QAAU,EAAO,SAAW,EAAE,CACrC,EAAO,QAAQ,MAAQ,EAAO,QAAQ,OAAS,EAAE,CACjD,EAAO,QAAQ,MAAM,kBAAoB,EAGzC,IAAM,EAAmB,EAAa,kBAAoB,GAC1D,GAAI,CAAC,EAAQ,KAAO,GAAoB,CAAC,EAAiB,CACxD,EAAkB,GAClB,GAAI,EAIF,EAAA,EAAA,UACE,uGAFkB,EAAY,QAAQ,MAAO,OAAO,CAAC,QAAQ,KAAM,MAAM,CAE0C,KACnH,CAAE,IAAK,EAAa,MAAO,UAAW,CACvC,MACK,GAMV,IAAM,EAAiB,EAAa,gBAAkB,GACtD,GAAI,EAAQ,KAAO,EAAgB,CACjC,IAAM,EAAW,EAAa,qBAAuB,IAC/C,EAAe,EAAsB,CAAE,IAAK,EAAa,CAAE,EAAS,CACpE,GAAA,EAAA,EAAA,SAA8B,EAAa,EAAS,YAAY,CAEtE,EAAO,QAAU,EAAO,SAAW,EAAE,CACrC,EAAO,QAAQ,KAAK,CAClB,MAAM,EAA2B,CAC/B,IAAI,EAAe,GACnB,EAAS,MAAM,SAAS,SAAS,eAAgB,EAA4B,IAAyB,CAChG,IACF,EAAe,GACf,GAAc,EAEhB,IAAM,EAAgB,EAAU,cAC5B,GACsB,CAAC,GAAG,EAAc,CAAC,KAAM,GAC/C,aAAa,KAAK,EAAE,EACjB,CAAC,EAAE,SAAS,eAAe,EAC3B,CAAC,EAAE,SAAS,QAAQ,EACpB,CAAC,EAAE,WAAW,EAAoB,CACtC,EAEC,GAAc,CAGlB,GAAU,EACV,EAEL,CAAC,CAQJ,OAJI,EACK,EAAgB,EAAQ,EAAQ,CAGlC,GAEV,CCpIH,IAAM,EACJ,wGAEF,SAAS,GAA4B,CACnC,MAAU,MAAM,EAAe,CAIjC,IAAa,EAAsC,EAEtC,EAAsE,EAEtE,EAAkB,EAElB,EAAoK,EAEpK,EAAyO,EAEzO,EAAiP,EAEjP,EAAuF,EAEvF,EAAoF,EAEpF,EAA2F"}
package/dist/index.js CHANGED
@@ -6,14 +6,15 @@ import { validateLocale as m } from "@fluenti/core";
6
6
  //#region src/read-config.ts
7
7
  var h = typeof __filename == "string" ? __filename : import.meta.url, { createJiti: g } = p(h)("jiti");
8
8
  function _(e, t) {
9
- let n = v(e), r = t?.defaultLocale ?? n?.sourceLocale ?? "en", i = t?.locales ?? n?.locales ?? [r], a = t?.compiledDir ?? n?.compileOutDir ?? "./src/locales/compiled", o = t?.serverModuleOutDir ?? u("node_modules", ".fluenti"), s = {
9
+ let n = v(e), r = t?.defaultLocale ?? n?.sourceLocale ?? "en", i = t?.locales ?? n?.locales ?? [r], a = t?.compiledDir ?? n?.compileOutDir ?? "./src/locales/compiled", o = t?.serverModuleOutDir ?? u("node_modules", ".fluenti"), s = t?.cookieName ?? "locale", c = {
10
10
  locales: i,
11
11
  defaultLocale: r,
12
12
  compiledDir: a,
13
13
  serverModule: t?.serverModule ?? null,
14
- serverModuleOutDir: o
14
+ serverModuleOutDir: o,
15
+ cookieName: s
15
16
  };
16
- return t?.resolveLocale && (s.resolveLocale = t.resolveLocale), t?.dateFormats && (s.dateFormats = t.dateFormats), t?.numberFormats && (s.numberFormats = t.numberFormats), t?.fallbackChain && (s.fallbackChain = t.fallbackChain), s;
17
+ return t?.resolveLocale && (c.resolveLocale = t.resolveLocale), t?.dateFormats && (c.dateFormats = t.dateFormats), t?.numberFormats && (c.numberFormats = t.numberFormats), t?.fallbackChain && (c.fallbackChain = t.fallbackChain), c;
17
18
  }
18
19
  function v(t) {
19
20
  let n = g(h, {
@@ -72,16 +73,45 @@ export function ClientI18nProvider({ locale, fallbackLocale, fallbackChain, chil
72
73
  `, "utf-8");
73
74
  let p = r.resolveLocale ? `import __resolveLocale from '${S(d(a, f(n, r.resolveLocale)))}'` : null, h = r.resolveLocale ? "resolveLocale: __resolveLocale," : `resolveLocale: async () => {
74
75
  try {
75
- const { cookies } = await import('next/headers')
76
- return (await cookies()).get('locale')?.value ?? '${r.defaultLocale}'
76
+ const { cookies, headers } = await import('next/headers')
77
+ const [cookieStore, headerStore] = await Promise.all([cookies(), headers()])
78
+
79
+ // 1. Referer URL path segment (available in Server Action context)
80
+ const referer = headerStore.get('referer')
81
+ if (referer) {
82
+ try {
83
+ const seg = new URL(referer).pathname.split('/')[1]
84
+ if (seg && __locales.includes(seg)) return seg
85
+ } catch {}
86
+ }
87
+
88
+ // 2. Cookie (configurable name)
89
+ const fromCookie = cookieStore.get('${r.cookieName}')?.value
90
+ if (fromCookie && __locales.includes(fromCookie)) return fromCookie
91
+
92
+ // 3. Accept-Language header
93
+ const acceptLang = headerStore.get('accept-language')
94
+ if (acceptLang) {
95
+ for (const part of acceptLang.split(',')) {
96
+ const lang = part.split(';')[0].trim()
97
+ if (__locales.includes(lang)) return lang
98
+ const prefix = lang.split('-')[0]
99
+ const match = __locales.find(l => l === prefix || l.startsWith(prefix + '-'))
100
+ if (match) return match
101
+ }
102
+ }
103
+
104
+ return '${r.defaultLocale}'
77
105
  } catch {
78
106
  return '${r.defaultLocale}'
79
107
  }
80
- },`;
108
+ },`, g = JSON.stringify(r.locales);
81
109
  return i(o, `// Auto-generated by @fluenti/next — do not edit
82
110
  import { createServerI18n } from '@fluenti/react/server'
83
111
  import { createElement } from 'react'
84
112
  ${p ? `${p}\n` : ""}
113
+ const __locales = ${g}
114
+
85
115
  const serverI18n = createServerI18n({
86
116
  loadMessages: async (locale) => {
87
117
  switch (locale) {
@@ -98,8 +128,11 @@ export const setLocale = serverI18n.setLocale
98
128
  export const getI18n = serverI18n.getI18n
99
129
  export const t = (..._args) => {
100
130
  throw new Error(
101
- "[fluenti] \`t\` imported from '@fluenti/next' is a compile-time API. " +
102
- 'Use it only with the Fluenti loader inside an async server scope.',
131
+ "[fluenti] \`t\` imported from '@fluenti/next' is a compile-time API replaced at build time.\\n" +
132
+ ' Ensure:\\n' +
133
+ ' 1. \`withFluenti()\` is configured in next.config.ts\\n' +
134
+ ' 2. The file is inside src/ (not node_modules)\\n' +
135
+ " 3. For client components, import from '@fluenti/react'",
103
136
  )
104
137
  }
105
138
  export const Trans = serverI18n.Trans
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/read-config.ts","../src/generate-server-module.ts","../src/dev-runner.ts","../src/with-fluenti.ts","../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, extname, join, resolve } from 'node:path'\nimport type { FluentiConfig } from '@fluenti/core'\nimport type { WithFluentConfig, ResolvedFluentConfig } from './types'\n\nconst runtimeModulePath = typeof __filename === 'string'\n ? __filename\n : import.meta.url\nconst require = createRequire(runtimeModulePath)\nconst { createJiti } = require('jiti') as {\n createJiti: (\n url: string,\n options?: { moduleCache?: boolean; interopDefault?: boolean },\n ) => (path: string) => unknown\n}\n\n/**\n * Read fluenti.config.ts and merge with withFluenti() overrides.\n */\nexport function resolveConfig(\n projectRoot: string,\n overrides?: WithFluentConfig,\n): ResolvedFluentConfig {\n const fileConfig = readFluentConfigSync(projectRoot)\n\n const defaultLocale = overrides?.defaultLocale\n ?? fileConfig?.sourceLocale\n ?? 'en'\n\n const locales = overrides?.locales\n ?? fileConfig?.locales\n ?? [defaultLocale]\n\n const compiledDir = overrides?.compiledDir\n ?? fileConfig?.compileOutDir\n ?? './src/locales/compiled'\n\n const serverModuleOutDir = overrides?.serverModuleOutDir\n ?? join('node_modules', '.fluenti')\n\n const resolved: ResolvedFluentConfig = {\n locales,\n defaultLocale,\n compiledDir,\n serverModule: overrides?.serverModule ?? null,\n serverModuleOutDir,\n }\n if (overrides?.resolveLocale) resolved.resolveLocale = overrides.resolveLocale\n if (overrides?.dateFormats) resolved.dateFormats = overrides.dateFormats\n if (overrides?.numberFormats) resolved.numberFormats = overrides.numberFormats\n if (overrides?.fallbackChain) resolved.fallbackChain = overrides.fallbackChain\n return resolved\n}\n\n/**\n * Attempt to read fluenti.config.ts synchronously.\n * Returns null if file doesn't exist or can't be parsed.\n */\nfunction readFluentConfigSync(projectRoot: string): FluentiConfig | null {\n const jiti = createJiti(runtimeModulePath, {\n moduleCache: false,\n interopDefault: true,\n })\n const candidates = [\n 'fluenti.config.ts',\n 'fluenti.config.js',\n 'fluenti.config.mjs',\n ]\n\n for (const name of candidates) {\n const configPath = resolve(projectRoot, name)\n if (existsSync(configPath)) {\n try {\n return normalizeLoadedConfig(jiti(configPath) as FluentiConfig | { default?: FluentiConfig })\n } catch {\n const rewritten = tryLoadConfigViaDefineConfigShim(configPath, jiti)\n if (rewritten) {\n return rewritten\n }\n return null\n }\n }\n }\n\n return null\n}\n\nfunction tryLoadConfigViaDefineConfigShim(\n configPath: string,\n jiti: (path: string) => unknown,\n): FluentiConfig | null {\n const source = readFileSync(configPath, 'utf8')\n const importMatch = source.match(\n /import\\s*\\{\\s*defineConfig(?:\\s+as\\s+([A-Za-z_$][\\w$]*))?\\s*\\}\\s*from\\s*['\"]@fluenti\\/cli['\"]\\s*;?/,\n )\n if (!importMatch) {\n return null\n }\n\n const helperName = importMatch[1] ?? 'defineConfig'\n const rewrittenSource = source.replace(importMatch[0], '')\n const tempPath = join(\n dirname(configPath),\n `.${basename(configPath, extname(configPath))}.next-plugin-read-config${extname(configPath) || '.ts'}`,\n )\n\n writeFileSync(tempPath, `const ${helperName} = (config) => config\\n${rewrittenSource}`, 'utf8')\n\n try {\n return normalizeLoadedConfig(jiti(tempPath) as FluentiConfig | { default?: FluentiConfig })\n } catch {\n return null\n } finally {\n rmSync(tempPath, { force: true })\n }\n}\n\nfunction normalizeLoadedConfig(\n mod: FluentiConfig | { default?: FluentiConfig },\n): FluentiConfig {\n return typeof mod === 'object' && mod !== null && 'default' in mod\n ? (mod.default ?? {}) as FluentiConfig\n : mod as FluentiConfig\n}\n","import { writeFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { resolve, relative } from 'node:path'\nimport { validateLocale } from '@fluenti/core'\nimport type { ResolvedFluentConfig } from './types'\n\n/**\n * Generate the server module that provides:\n * - setLocale / getI18n\n * - Trans / Plural / Select / DateTime / NumberFormat (server components)\n * - I18nProvider (async server component for layouts)\n *\n * @returns Absolute path to the generated server module.\n */\nexport function generateServerModule(\n projectRoot: string,\n config: ResolvedFluentConfig,\n): string {\n if (config.serverModule) {\n return resolve(projectRoot, config.serverModule)\n }\n\n const outDir = resolve(projectRoot, config.serverModuleOutDir)\n const outPath = resolve(outDir, 'server.js')\n const dtsPath = resolve(outDir, 'server.d.ts')\n\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true })\n }\n\n for (const locale of config.locales) {\n validateLocale(locale, 'next-plugin')\n }\n\n const compiledDirAbs = resolve(projectRoot, config.compiledDir)\n const compiledRelative = toForwardSlash(relative(outDir, compiledDirAbs))\n\n const localeImports = config.locales\n .map((locale) => ` case '${locale}': return import('${compiledRelative}/${locale}')`)\n .join('\\n')\n\n const fallbackChainStr = config.fallbackChain\n ? JSON.stringify(config.fallbackChain)\n : 'undefined'\n\n // Generate a 'use client' provider that imports messages statically.\n // Messages contain functions (interpolation) which can't cross the RSC boundary.\n const clientProviderPath = resolve(outDir, 'client-provider.js')\n\n const clientStaticImports = config.locales\n .map((locale) => {\n const safe = locale.replace(/[^a-zA-Z0-9]/g, '_')\n return `import ${safe} from '${compiledRelative}/${locale}'`\n })\n .join('\\n')\n\n const clientAllMessagesEntries = config.locales\n .map((locale) => {\n const safe = locale.replace(/[^a-zA-Z0-9]/g, '_')\n return `'${locale}': ${safe}`\n })\n .join(', ')\n\n const clientProviderSource = `\"use client\";\n// Auto-generated by @fluenti/next — do not edit\nimport { createElement } from 'react'\nimport { I18nProvider } from '@fluenti/react'\n${clientStaticImports}\n\nconst __allMessages = { ${clientAllMessagesEntries} }\n\nexport function ClientI18nProvider({ locale, fallbackLocale, fallbackChain, children }) {\n return createElement(I18nProvider, { locale, fallbackLocale, messages: __allMessages, fallbackChain }, children)\n}\n`\n writeFileSync(clientProviderPath, clientProviderSource, 'utf-8')\n\n const resolveLocaleImport = config.resolveLocale\n ? (() => {\n const absPath = resolve(projectRoot, config.resolveLocale)\n const relPath = toForwardSlash(relative(outDir, absPath))\n return `import __resolveLocale from '${relPath}'`\n })()\n : null\n\n const resolveLocaleFn = config.resolveLocale\n ? `resolveLocale: __resolveLocale,`\n : `resolveLocale: async () => {\n try {\n const { cookies } = await import('next/headers')\n return (await cookies()).get('locale')?.value ?? '${config.defaultLocale}'\n } catch {\n return '${config.defaultLocale}'\n }\n },`\n\n const moduleSource = `// Auto-generated by @fluenti/next — do not edit\nimport { createServerI18n } from '@fluenti/react/server'\nimport { createElement } from 'react'\n${resolveLocaleImport ? `${resolveLocaleImport}\\n` : ''}\nconst serverI18n = createServerI18n({\n loadMessages: async (locale) => {\n switch (locale) {\n${localeImports}\n default: return import('${compiledRelative}/${config.defaultLocale}')\n }\n },\n fallbackLocale: '${config.defaultLocale}',\n fallbackChain: ${fallbackChainStr},\n ${resolveLocaleFn}\n})\n\nexport const setLocale = serverI18n.setLocale\nexport const getI18n = serverI18n.getI18n\nexport const t = (..._args) => {\n throw new Error(\n \"[fluenti] \\`t\\` imported from '@fluenti/next' is a compile-time API. \" +\n 'Use it only with the Fluenti loader inside an async server scope.',\n )\n}\nexport const Trans = serverI18n.Trans\nexport const Plural = serverI18n.Plural\nexport const Select = serverI18n.Select\nexport const DateTime = serverI18n.DateTime\nexport const NumberFormat = serverI18n.NumberFormat\n\n/**\n * Async server component for root layouts.\n *\n * Sets up both server-side (React.cache) and client-side (I18nProvider) i18n.\n */\nexport async function I18nProvider({ locale, children }) {\n const activeLocale = locale ?? '${config.defaultLocale}'\n\n // 1. Initialize server-side i18n (React.cache scoped)\n serverI18n.setLocale(activeLocale)\n await serverI18n.getI18n()\n\n // 2. Import the local 'use client' provider that has messages statically bundled.\n // Messages contain functions (interpolation) which can't be serialized across the RSC boundary.\n const { ClientI18nProvider } = await import('./client-provider.js')\n\n return createElement(ClientI18nProvider, {\n locale: activeLocale,\n fallbackLocale: '${config.defaultLocale}',\n fallbackChain: ${fallbackChainStr},\n }, children)\n}\n`\n\n const dtsSource = `// Auto-generated by @fluenti/next — do not edit\nimport type { ReactNode, ReactElement } from 'react'\nimport type { CompileTimeT, FluentInstanceExtended } from '@fluenti/core'\n\nexport declare function setLocale(locale: string): void\nexport declare function getI18n(): Promise<FluentInstanceExtended & { locale: string }>\nexport declare const t: CompileTimeT\n\nexport declare function Trans(props: {\n children: ReactNode\n id?: string\n context?: string\n comment?: string\n render?: (translation: ReactNode) => ReactNode\n}): Promise<ReactElement>\n\nexport declare function Plural(props: {\n value: number\n id?: string\n context?: string\n comment?: string\n zero?: ReactNode\n one?: ReactNode\n two?: ReactNode\n few?: ReactNode\n many?: ReactNode\n other: ReactNode\n offset?: number\n}): Promise<ReactElement>\n\nexport declare function Select(props: {\n value: string\n id?: string\n context?: string\n comment?: string\n other: ReactNode\n options?: Record<string, ReactNode>\n [key: string]: ReactNode | Record<string, ReactNode> | string | undefined\n}): Promise<ReactElement>\n\nexport declare function DateTime(props: {\n value: Date | number\n style?: string\n}): Promise<ReactElement>\n\nexport declare function NumberFormat(props: {\n value: number\n style?: string\n}): Promise<ReactElement>\n\nexport declare function I18nProvider(props: {\n locale?: string\n children: ReactNode\n}): Promise<ReactElement>\n`\n\n writeFileSync(outPath, moduleSource, 'utf-8')\n writeFileSync(dtsPath, dtsSource, 'utf-8')\n\n return outPath\n}\n\nfunction toForwardSlash(p: string): string {\n return p.split('\\\\').join('/')\n}\n","import { exec } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { resolve, dirname, join } from 'node:path'\nimport { createRequire } from 'node:module'\n\nexport interface DevRunnerOptions {\n cwd: string\n onSuccess?: () => void\n onError?: (err: Error) => void\n /** If true, reject the promise on failure instead of swallowing the error */\n throwOnError?: boolean\n /** Run only compile (skip extract). Useful for production builds where source is unchanged. */\n compileOnly?: boolean\n}\n\n/**\n * Walk up from `cwd` to find `node_modules/.bin/fluenti`.\n * Returns the absolute path or null if not found.\n */\nexport function resolveCliBin(cwd: string): string | null {\n let dir = cwd\n for (;;) {\n const bin = resolve(dir, 'node_modules/.bin/fluenti')\n if (existsSync(bin)) return bin\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n return null\n}\n\n/**\n * Run compile in-process via `@fluenti/cli` (for compileOnly mode),\n * or fall back to shell-out for extract + compile (dev mode).\n */\nexport async function runExtractCompile(options: DevRunnerOptions): Promise<void> {\n if (options.compileOnly) {\n try {\n // Resolve @fluenti/cli from the project's cwd (not from this package's location)\n // using createRequire so pnpm's strict node_modules layout works correctly.\n // Use require() (not import()) to load @fluenti/cli — avoids CJS/ESM interop\n // issues when dynamic import() loads minified CJS with chunk requires.\n const projectRequire = createRequire(join(options.cwd, 'package.json'))\n const { runCompile } = projectRequire('@fluenti/cli')\n await runCompile(options.cwd)\n console.log('[fluenti] Compiling... done')\n options.onSuccess?.()\n return\n } catch (e) {\n const error = e instanceof Error ? e : new Error(String(e))\n if (options.throwOnError) throw error\n console.warn('[fluenti] Compile failed:', error.message)\n options.onError?.(error)\n return\n }\n }\n\n // Dev mode: shell out for extract + compile\n const bin = resolveCliBin(options.cwd)\n if (!bin) {\n const msg = '[fluenti] CLI not found — skipping auto-compile. Install @fluenti/cli as a devDependency.'\n if (options.throwOnError) {\n return Promise.reject(new Error(msg))\n }\n console.warn(msg)\n return Promise.resolve()\n }\n\n const command = `${bin} extract && ${bin} compile`\n return new Promise<void>((resolve, reject) => {\n exec(\n command,\n { cwd: options.cwd },\n (err, _stdout, stderr) => {\n if (err) {\n const error = new Error(stderr || err.message)\n if (options.throwOnError) {\n reject(error)\n return\n }\n console.warn('[fluenti] Extract/compile failed:', error.message)\n options.onError?.(error)\n } else {\n console.log('[fluenti] Extracting and compiling... done')\n options.onSuccess?.()\n }\n resolve()\n },\n )\n })\n}\n\n/**\n * Create a debounced runner that collapses rapid calls.\n *\n * - If called while idle, schedules a run after `delay` ms.\n * - If called while a run is in progress, marks a pending rerun.\n * - Never runs concurrently.\n */\nexport function createDebouncedRunner(\n options: DevRunnerOptions,\n delay = 300,\n): () => void {\n let timer: ReturnType<typeof setTimeout> | null = null\n let running = false\n let pendingRerun = false\n\n async function execute(): Promise<void> {\n running = true\n try {\n await runExtractCompile(options)\n } finally {\n running = false\n if (pendingRerun) {\n pendingRerun = false\n schedule()\n }\n }\n }\n\n function schedule(): void {\n if (timer !== null) {\n clearTimeout(timer)\n }\n timer = setTimeout(() => {\n timer = null\n if (running) {\n pendingRerun = true\n } else {\n execute()\n }\n }, delay)\n }\n\n return schedule\n}\n","import { existsSync } from 'node:fs'\nimport { execSync } from 'node:child_process'\nimport { resolve, dirname } from 'node:path'\nimport type { WithFluentConfig } from './types'\nimport { resolveConfig } from './read-config'\nimport { generateServerModule } from './generate-server-module'\nimport { createDebouncedRunner } from './dev-runner'\n\ntype NextConfig = Record<string, unknown>\n\n/**\n * Wrap your Next.js config with Fluenti support.\n *\n * Adds a webpack loader that transforms `t\\`\\`` and `t()` calls,\n * and generates a server module for RSC i18n.\n *\n * @example\n * ```ts\n * // next.config.ts — function style (recommended)\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti()({ reactStrictMode: true })\n * ```\n *\n * @example\n * ```ts\n * // next.config.ts — direct style\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti({ reactStrictMode: true })\n * ```\n */\nexport function withFluenti(fluentConfig?: WithFluentConfig): (nextConfig?: NextConfig) => NextConfig\nexport function withFluenti(nextConfig: NextConfig): NextConfig\nexport function withFluenti(\n configOrNext?: WithFluentConfig | NextConfig,\n): NextConfig | ((nextConfig?: NextConfig) => NextConfig) {\n if (configOrNext && isNextConfig(configOrNext as NextConfig)) {\n return applyFluenti({}, configOrNext as NextConfig)\n }\n\n const fluentConfig = (configOrNext ?? {}) as WithFluentConfig\n return function wrappedConfig(nextConfig?: NextConfig): NextConfig {\n return applyFluenti(fluentConfig, nextConfig ?? {})\n }\n}\n\nfunction isNextConfig(obj: NextConfig): boolean {\n const nextKeys = [\n 'reactStrictMode', 'experimental', 'images', 'env', 'webpack',\n 'rewrites', 'redirects', 'headers', 'pageExtensions', 'output',\n 'basePath', 'i18n', 'trailingSlash', 'compiler', 'transpilePackages',\n ]\n return nextKeys.some((key) => key in obj)\n}\n\nfunction applyFluenti(\n fluentConfig: WithFluentConfig,\n nextConfig: NextConfig,\n): NextConfig {\n const projectRoot = process.cwd()\n const resolved = resolveConfig(projectRoot, fluentConfig)\n\n // Warn if compiled catalogs directory doesn't exist yet\n const compiledDir = resolve(projectRoot, resolved.compiledDir)\n if (!existsSync(compiledDir)) {\n console.warn(\n `\\n[fluenti] Compiled catalogs not found at ${resolved.compiledDir}.\\n` +\n `Run: npx fluenti extract && npx fluenti compile\\n`,\n )\n }\n\n // Generate server module for RSC\n const serverModulePath = generateServerModule(projectRoot, resolved)\n\n // Resolve the loader path — use import.meta.url for ESM compatibility\n const thisDir = typeof __dirname !== 'undefined'\n ? __dirname\n : dirname(new URL(import.meta.url).pathname)\n const loaderPath = resolve(thisDir, 'loader.js')\n\n const existingWebpack = nextConfig['webpack'] as\n | ((config: WebpackConfig, options: WebpackOptions) => WebpackConfig)\n | undefined\n\n let buildCompileRan = false\n\n return {\n ...nextConfig,\n webpack(config: WebpackConfig, options: WebpackOptions) {\n // Add fluenti loader (enforce: pre — runs before other loaders)\n config.module.rules.push({\n test: /\\.[jt]sx?$/,\n enforce: 'pre' as const,\n exclude: [/node_modules/, /\\.next/],\n use: [\n {\n loader: loaderPath,\n options: {\n serverModulePath,\n },\n },\n ],\n })\n\n // Add resolve alias so loader can import from generated server module\n config.resolve = config.resolve ?? {} as WebpackConfig['resolve']\n config.resolve.alias = config.resolve.alias ?? {}\n config.resolve.alias['@fluenti/next$'] = serverModulePath\n\n // Auto compile before production build (run once across server+client passes)\n const buildAutoCompile = fluentConfig.buildAutoCompile ?? true\n if (!options.dev && buildAutoCompile && !buildCompileRan) {\n buildCompileRan = true\n try {\n // Use node -e with dynamic import — avoids CLI binary resolution issues.\n // webpack() is sync, so we use execSync to block until compile finishes.\n const escapedRoot = projectRoot.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")\n execSync(\n `node --input-type=module -e \"const { runCompile } = await import('@fluenti/cli'); await runCompile('${escapedRoot}')\"`,\n { cwd: projectRoot, stdio: 'inherit' },\n )\n } catch {\n // @fluenti/cli not available or compile failed — skip silently\n }\n }\n\n // Auto extract+compile in dev mode\n const devAutoCompile = fluentConfig.devAutoCompile ?? true\n if (options.dev && devAutoCompile) {\n const devDelay = fluentConfig.devAutoCompileDelay ?? 1000\n const debouncedRun = createDebouncedRunner({ cwd: projectRoot }, devDelay)\n const compiledDirResolved = resolve(projectRoot, resolved.compiledDir)\n\n config.plugins = config.plugins ?? []\n config.plugins.push({\n apply(compiler: WebpackCompiler) {\n let isFirstBuild = true\n compiler.hooks.watchRun.tapAsync('fluenti-dev', (_compiler: WebpackCompiler, callback: () => void) => {\n if (isFirstBuild) {\n isFirstBuild = false\n debouncedRun()\n }\n const modifiedFiles = _compiler.modifiedFiles\n if (modifiedFiles) {\n const hasSourceChange = [...modifiedFiles].some((f: string) =>\n /\\.[jt]sx?$/.test(f)\n && !f.includes('node_modules')\n && !f.includes('.next')\n && !f.startsWith(compiledDirResolved),\n )\n if (hasSourceChange) {\n debouncedRun()\n }\n }\n callback()\n })\n },\n })\n }\n\n // Call user's webpack config if provided\n if (existingWebpack) {\n return existingWebpack(config, options)\n }\n\n return config\n },\n }\n}\n\n// Minimal webpack types for the config function\ninterface WebpackConfig {\n module: {\n rules: Array<{\n test: RegExp\n enforce?: 'pre' | 'post'\n exclude?: Array<RegExp>\n use: Array<{ loader: string; options: Record<string, unknown> }>\n }>\n }\n resolve: {\n alias?: Record<string, string>\n }\n plugins?: Array<{ apply(compiler: WebpackCompiler): void }>\n}\n\ninterface WebpackOptions {\n isServer: boolean\n dev: boolean\n}\n\ninterface WebpackCompiler {\n hooks: {\n watchRun: {\n tapAsync(name: string, cb: (compiler: WebpackCompiler, callback: () => void) => void): void\n }\n }\n modifiedFiles?: Set<string>\n}\n","/**\n * @fluenti/next — Next.js plugin for Fluenti\n *\n * Provides:\n * - `withFluenti()` — wraps next.config.ts with t`` transform support\n * - I18nProvider — async server component (exported from generated module)\n * - Webpack loader for strict, binding-aware tagged-template optimization\n *\n * @example\n * ```ts\n * // next.config.ts\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti()({ reactStrictMode: true })\n * ```\n *\n * @example\n * ```tsx\n * // app/layout.tsx — resolved by webpack alias to the generated module\n * import { I18nProvider } from '@fluenti/next'\n * ```\n */\nexport { withFluenti } from './with-fluenti'\nexport type { WithFluentConfig, I18nProviderProps } from './types'\n\n// ── Runtime stubs ────────────────────────────────────────────────────\n// TypeScript resolves types from this file (via package.json exports).\n// At runtime, webpack `resolve.alias` redirects `@fluenti/next$` to the\n// generated server module, so these stubs are never actually called in\n// a correctly configured project. They exist only to provide helpful\n// errors if `withFluenti()` is not configured.\n\nimport type { ReactNode, ReactElement } from 'react'\nimport type { CompileTimeT, FluentInstanceExtended } from '@fluenti/core'\n\nconst NOT_CONFIGURED =\n '[fluenti] `withFluenti()` must be configured in next.config.ts before importing from \"@fluenti/next\".'\n\nfunction throwNotConfigured(): never {\n throw new Error(NOT_CONFIGURED)\n}\n\n/** @see Generated module for the real implementation. */\nexport const setLocale: (locale: string) => void = throwNotConfigured\n/** @see Generated module for the real implementation. */\nexport const getI18n: () => Promise<FluentInstanceExtended & { locale: string }> = throwNotConfigured as () => Promise<FluentInstanceExtended & { locale: string }>\n/** @see Generated module for the real implementation. */\nexport const t: CompileTimeT = throwNotConfigured as unknown as CompileTimeT\n/** @see Generated module for the real implementation. */\nexport const Trans: (props: { children: ReactNode; id?: string; context?: string; comment?: string; render?: (translation: ReactNode) => ReactNode }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Trans\n/** @see Generated module for the real implementation. */\nexport const Plural: (props: { value: number; id?: string; context?: string; comment?: string; zero?: ReactNode; one?: ReactNode; two?: ReactNode; few?: ReactNode; many?: ReactNode; other: ReactNode; offset?: number }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Plural\n/** @see Generated module for the real implementation. */\nexport const Select: (props: { value: string; id?: string; context?: string; comment?: string; other: ReactNode; options?: Record<string, ReactNode>; [key: string]: ReactNode | Record<string, ReactNode> | string | undefined }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Select\n/** @see Generated module for the real implementation. */\nexport const DateTime: (props: { value: Date | number; style?: string }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof DateTime\n/** @see Generated module for the real implementation. */\nexport const NumberFormat: (props: { value: number; style?: string }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof NumberFormat\n/** @see Generated module for the real implementation. */\nexport const I18nProvider: (props: { locale?: string; children: ReactNode }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof I18nProvider\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/read-config.ts","../src/generate-server-module.ts","../src/dev-runner.ts","../src/with-fluenti.ts","../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, extname, join, resolve } from 'node:path'\nimport type { FluentiConfig } from '@fluenti/core'\nimport type { WithFluentConfig, ResolvedFluentConfig } from './types'\n\nconst runtimeModulePath = typeof __filename === 'string'\n ? __filename\n : import.meta.url\nconst require = createRequire(runtimeModulePath)\nconst { createJiti } = require('jiti') as {\n createJiti: (\n url: string,\n options?: { moduleCache?: boolean; interopDefault?: boolean },\n ) => (path: string) => unknown\n}\n\n/**\n * Read fluenti.config.ts and merge with withFluenti() overrides.\n */\nexport function resolveConfig(\n projectRoot: string,\n overrides?: WithFluentConfig,\n): ResolvedFluentConfig {\n const fileConfig = readFluentConfigSync(projectRoot)\n\n const defaultLocale = overrides?.defaultLocale\n ?? fileConfig?.sourceLocale\n ?? 'en'\n\n const locales = overrides?.locales\n ?? fileConfig?.locales\n ?? [defaultLocale]\n\n const compiledDir = overrides?.compiledDir\n ?? fileConfig?.compileOutDir\n ?? './src/locales/compiled'\n\n const serverModuleOutDir = overrides?.serverModuleOutDir\n ?? join('node_modules', '.fluenti')\n\n const cookieName = overrides?.cookieName ?? 'locale'\n\n const resolved: ResolvedFluentConfig = {\n locales,\n defaultLocale,\n compiledDir,\n serverModule: overrides?.serverModule ?? null,\n serverModuleOutDir,\n cookieName,\n }\n if (overrides?.resolveLocale) resolved.resolveLocale = overrides.resolveLocale\n if (overrides?.dateFormats) resolved.dateFormats = overrides.dateFormats\n if (overrides?.numberFormats) resolved.numberFormats = overrides.numberFormats\n if (overrides?.fallbackChain) resolved.fallbackChain = overrides.fallbackChain\n return resolved\n}\n\n/**\n * Attempt to read fluenti.config.ts synchronously.\n * Returns null if file doesn't exist or can't be parsed.\n */\nfunction readFluentConfigSync(projectRoot: string): FluentiConfig | null {\n const jiti = createJiti(runtimeModulePath, {\n moduleCache: false,\n interopDefault: true,\n })\n const candidates = [\n 'fluenti.config.ts',\n 'fluenti.config.js',\n 'fluenti.config.mjs',\n ]\n\n for (const name of candidates) {\n const configPath = resolve(projectRoot, name)\n if (existsSync(configPath)) {\n try {\n return normalizeLoadedConfig(jiti(configPath) as FluentiConfig | { default?: FluentiConfig })\n } catch {\n const rewritten = tryLoadConfigViaDefineConfigShim(configPath, jiti)\n if (rewritten) {\n return rewritten\n }\n return null\n }\n }\n }\n\n return null\n}\n\nfunction tryLoadConfigViaDefineConfigShim(\n configPath: string,\n jiti: (path: string) => unknown,\n): FluentiConfig | null {\n const source = readFileSync(configPath, 'utf8')\n const importMatch = source.match(\n /import\\s*\\{\\s*defineConfig(?:\\s+as\\s+([A-Za-z_$][\\w$]*))?\\s*\\}\\s*from\\s*['\"]@fluenti\\/cli['\"]\\s*;?/,\n )\n if (!importMatch) {\n return null\n }\n\n const helperName = importMatch[1] ?? 'defineConfig'\n const rewrittenSource = source.replace(importMatch[0], '')\n const tempPath = join(\n dirname(configPath),\n `.${basename(configPath, extname(configPath))}.next-plugin-read-config${extname(configPath) || '.ts'}`,\n )\n\n writeFileSync(tempPath, `const ${helperName} = (config) => config\\n${rewrittenSource}`, 'utf8')\n\n try {\n return normalizeLoadedConfig(jiti(tempPath) as FluentiConfig | { default?: FluentiConfig })\n } catch {\n return null\n } finally {\n rmSync(tempPath, { force: true })\n }\n}\n\nfunction normalizeLoadedConfig(\n mod: FluentiConfig | { default?: FluentiConfig },\n): FluentiConfig {\n return typeof mod === 'object' && mod !== null && 'default' in mod\n ? (mod.default ?? {}) as FluentiConfig\n : mod as FluentiConfig\n}\n","import { writeFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { resolve, relative } from 'node:path'\nimport { validateLocale } from '@fluenti/core'\nimport type { ResolvedFluentConfig } from './types'\n\n/**\n * Generate the server module that provides:\n * - setLocale / getI18n\n * - Trans / Plural / Select / DateTime / NumberFormat (server components)\n * - I18nProvider (async server component for layouts)\n *\n * @returns Absolute path to the generated server module.\n */\nexport function generateServerModule(\n projectRoot: string,\n config: ResolvedFluentConfig,\n): string {\n if (config.serverModule) {\n return resolve(projectRoot, config.serverModule)\n }\n\n const outDir = resolve(projectRoot, config.serverModuleOutDir)\n const outPath = resolve(outDir, 'server.js')\n const dtsPath = resolve(outDir, 'server.d.ts')\n\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true })\n }\n\n for (const locale of config.locales) {\n validateLocale(locale, 'next-plugin')\n }\n\n const compiledDirAbs = resolve(projectRoot, config.compiledDir)\n const compiledRelative = toForwardSlash(relative(outDir, compiledDirAbs))\n\n const localeImports = config.locales\n .map((locale) => ` case '${locale}': return import('${compiledRelative}/${locale}')`)\n .join('\\n')\n\n const fallbackChainStr = config.fallbackChain\n ? JSON.stringify(config.fallbackChain)\n : 'undefined'\n\n // Generate a 'use client' provider that imports messages statically.\n // Messages contain functions (interpolation) which can't cross the RSC boundary.\n const clientProviderPath = resolve(outDir, 'client-provider.js')\n\n const clientStaticImports = config.locales\n .map((locale) => {\n const safe = locale.replace(/[^a-zA-Z0-9]/g, '_')\n return `import ${safe} from '${compiledRelative}/${locale}'`\n })\n .join('\\n')\n\n const clientAllMessagesEntries = config.locales\n .map((locale) => {\n const safe = locale.replace(/[^a-zA-Z0-9]/g, '_')\n return `'${locale}': ${safe}`\n })\n .join(', ')\n\n const clientProviderSource = `\"use client\";\n// Auto-generated by @fluenti/next — do not edit\nimport { createElement } from 'react'\nimport { I18nProvider } from '@fluenti/react'\n${clientStaticImports}\n\nconst __allMessages = { ${clientAllMessagesEntries} }\n\nexport function ClientI18nProvider({ locale, fallbackLocale, fallbackChain, children }) {\n return createElement(I18nProvider, { locale, fallbackLocale, messages: __allMessages, fallbackChain }, children)\n}\n`\n writeFileSync(clientProviderPath, clientProviderSource, 'utf-8')\n\n const resolveLocaleImport = config.resolveLocale\n ? (() => {\n const absPath = resolve(projectRoot, config.resolveLocale)\n const relPath = toForwardSlash(relative(outDir, absPath))\n return `import __resolveLocale from '${relPath}'`\n })()\n : null\n\n const resolveLocaleFn = config.resolveLocale\n ? `resolveLocale: __resolveLocale,`\n : `resolveLocale: async () => {\n try {\n const { cookies, headers } = await import('next/headers')\n const [cookieStore, headerStore] = await Promise.all([cookies(), headers()])\n\n // 1. Referer URL path segment (available in Server Action context)\n const referer = headerStore.get('referer')\n if (referer) {\n try {\n const seg = new URL(referer).pathname.split('/')[1]\n if (seg && __locales.includes(seg)) return seg\n } catch {}\n }\n\n // 2. Cookie (configurable name)\n const fromCookie = cookieStore.get('${config.cookieName}')?.value\n if (fromCookie && __locales.includes(fromCookie)) return fromCookie\n\n // 3. Accept-Language header\n const acceptLang = headerStore.get('accept-language')\n if (acceptLang) {\n for (const part of acceptLang.split(',')) {\n const lang = part.split(';')[0].trim()\n if (__locales.includes(lang)) return lang\n const prefix = lang.split('-')[0]\n const match = __locales.find(l => l === prefix || l.startsWith(prefix + '-'))\n if (match) return match\n }\n }\n\n return '${config.defaultLocale}'\n } catch {\n return '${config.defaultLocale}'\n }\n },`\n\n const localesArrayStr = JSON.stringify(config.locales)\n\n const moduleSource = `// Auto-generated by @fluenti/next — do not edit\nimport { createServerI18n } from '@fluenti/react/server'\nimport { createElement } from 'react'\n${resolveLocaleImport ? `${resolveLocaleImport}\\n` : ''}\nconst __locales = ${localesArrayStr}\n\nconst serverI18n = createServerI18n({\n loadMessages: async (locale) => {\n switch (locale) {\n${localeImports}\n default: return import('${compiledRelative}/${config.defaultLocale}')\n }\n },\n fallbackLocale: '${config.defaultLocale}',\n fallbackChain: ${fallbackChainStr},\n ${resolveLocaleFn}\n})\n\nexport const setLocale = serverI18n.setLocale\nexport const getI18n = serverI18n.getI18n\nexport const t = (..._args) => {\n throw new Error(\n \"[fluenti] \\`t\\` imported from '@fluenti/next' is a compile-time API replaced at build time.\\\\n\" +\n ' Ensure:\\\\n' +\n ' 1. \\`withFluenti()\\` is configured in next.config.ts\\\\n' +\n ' 2. The file is inside src/ (not node_modules)\\\\n' +\n \" 3. For client components, import from '@fluenti/react'\",\n )\n}\nexport const Trans = serverI18n.Trans\nexport const Plural = serverI18n.Plural\nexport const Select = serverI18n.Select\nexport const DateTime = serverI18n.DateTime\nexport const NumberFormat = serverI18n.NumberFormat\n\n/**\n * Async server component for root layouts.\n *\n * Sets up both server-side (React.cache) and client-side (I18nProvider) i18n.\n */\nexport async function I18nProvider({ locale, children }) {\n const activeLocale = locale ?? '${config.defaultLocale}'\n\n // 1. Initialize server-side i18n (React.cache scoped)\n serverI18n.setLocale(activeLocale)\n await serverI18n.getI18n()\n\n // 2. Import the local 'use client' provider that has messages statically bundled.\n // Messages contain functions (interpolation) which can't be serialized across the RSC boundary.\n const { ClientI18nProvider } = await import('./client-provider.js')\n\n return createElement(ClientI18nProvider, {\n locale: activeLocale,\n fallbackLocale: '${config.defaultLocale}',\n fallbackChain: ${fallbackChainStr},\n }, children)\n}\n`\n\n const dtsSource = `// Auto-generated by @fluenti/next — do not edit\nimport type { ReactNode, ReactElement } from 'react'\nimport type { CompileTimeT, FluentInstanceExtended } from '@fluenti/core'\n\nexport declare function setLocale(locale: string): void\nexport declare function getI18n(): Promise<FluentInstanceExtended & { locale: string }>\nexport declare const t: CompileTimeT\n\nexport declare function Trans(props: {\n children: ReactNode\n id?: string\n context?: string\n comment?: string\n render?: (translation: ReactNode) => ReactNode\n}): Promise<ReactElement>\n\nexport declare function Plural(props: {\n value: number\n id?: string\n context?: string\n comment?: string\n zero?: ReactNode\n one?: ReactNode\n two?: ReactNode\n few?: ReactNode\n many?: ReactNode\n other: ReactNode\n offset?: number\n}): Promise<ReactElement>\n\nexport declare function Select(props: {\n value: string\n id?: string\n context?: string\n comment?: string\n other: ReactNode\n options?: Record<string, ReactNode>\n [key: string]: ReactNode | Record<string, ReactNode> | string | undefined\n}): Promise<ReactElement>\n\nexport declare function DateTime(props: {\n value: Date | number\n style?: string\n}): Promise<ReactElement>\n\nexport declare function NumberFormat(props: {\n value: number\n style?: string\n}): Promise<ReactElement>\n\nexport declare function I18nProvider(props: {\n locale?: string\n children: ReactNode\n}): Promise<ReactElement>\n`\n\n writeFileSync(outPath, moduleSource, 'utf-8')\n writeFileSync(dtsPath, dtsSource, 'utf-8')\n\n return outPath\n}\n\nfunction toForwardSlash(p: string): string {\n return p.split('\\\\').join('/')\n}\n","import { exec } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { resolve, dirname, join } from 'node:path'\nimport { createRequire } from 'node:module'\n\nexport interface DevRunnerOptions {\n cwd: string\n onSuccess?: () => void\n onError?: (err: Error) => void\n /** If true, reject the promise on failure instead of swallowing the error */\n throwOnError?: boolean\n /** Run only compile (skip extract). Useful for production builds where source is unchanged. */\n compileOnly?: boolean\n}\n\n/**\n * Walk up from `cwd` to find `node_modules/.bin/fluenti`.\n * Returns the absolute path or null if not found.\n */\nexport function resolveCliBin(cwd: string): string | null {\n let dir = cwd\n for (;;) {\n const bin = resolve(dir, 'node_modules/.bin/fluenti')\n if (existsSync(bin)) return bin\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n return null\n}\n\n/**\n * Run compile in-process via `@fluenti/cli` (for compileOnly mode),\n * or fall back to shell-out for extract + compile (dev mode).\n */\nexport async function runExtractCompile(options: DevRunnerOptions): Promise<void> {\n if (options.compileOnly) {\n try {\n // Resolve @fluenti/cli from the project's cwd (not from this package's location)\n // using createRequire so pnpm's strict node_modules layout works correctly.\n // Use require() (not import()) to load @fluenti/cli — avoids CJS/ESM interop\n // issues when dynamic import() loads minified CJS with chunk requires.\n const projectRequire = createRequire(join(options.cwd, 'package.json'))\n const { runCompile } = projectRequire('@fluenti/cli')\n await runCompile(options.cwd)\n console.log('[fluenti] Compiling... done')\n options.onSuccess?.()\n return\n } catch (e) {\n const error = e instanceof Error ? e : new Error(String(e))\n if (options.throwOnError) throw error\n console.warn('[fluenti] Compile failed:', error.message)\n options.onError?.(error)\n return\n }\n }\n\n // Dev mode: shell out for extract + compile\n const bin = resolveCliBin(options.cwd)\n if (!bin) {\n const msg = '[fluenti] CLI not found — skipping auto-compile. Install @fluenti/cli as a devDependency.'\n if (options.throwOnError) {\n return Promise.reject(new Error(msg))\n }\n console.warn(msg)\n return Promise.resolve()\n }\n\n const command = `${bin} extract && ${bin} compile`\n return new Promise<void>((resolve, reject) => {\n exec(\n command,\n { cwd: options.cwd },\n (err, _stdout, stderr) => {\n if (err) {\n const error = new Error(stderr || err.message)\n if (options.throwOnError) {\n reject(error)\n return\n }\n console.warn('[fluenti] Extract/compile failed:', error.message)\n options.onError?.(error)\n } else {\n console.log('[fluenti] Extracting and compiling... done')\n options.onSuccess?.()\n }\n resolve()\n },\n )\n })\n}\n\n/**\n * Create a debounced runner that collapses rapid calls.\n *\n * - If called while idle, schedules a run after `delay` ms.\n * - If called while a run is in progress, marks a pending rerun.\n * - Never runs concurrently.\n */\nexport function createDebouncedRunner(\n options: DevRunnerOptions,\n delay = 300,\n): () => void {\n let timer: ReturnType<typeof setTimeout> | null = null\n let running = false\n let pendingRerun = false\n\n async function execute(): Promise<void> {\n running = true\n try {\n await runExtractCompile(options)\n } finally {\n running = false\n if (pendingRerun) {\n pendingRerun = false\n schedule()\n }\n }\n }\n\n function schedule(): void {\n if (timer !== null) {\n clearTimeout(timer)\n }\n timer = setTimeout(() => {\n timer = null\n if (running) {\n pendingRerun = true\n } else {\n execute()\n }\n }, delay)\n }\n\n return schedule\n}\n","import { existsSync } from 'node:fs'\nimport { execSync } from 'node:child_process'\nimport { resolve, dirname } from 'node:path'\nimport type { WithFluentConfig } from './types'\nimport { resolveConfig } from './read-config'\nimport { generateServerModule } from './generate-server-module'\nimport { createDebouncedRunner } from './dev-runner'\n\ntype NextConfig = Record<string, unknown>\n\n/**\n * Wrap your Next.js config with Fluenti support.\n *\n * Adds a webpack loader that transforms `t\\`\\`` and `t()` calls,\n * and generates a server module for RSC i18n.\n *\n * @example\n * ```ts\n * // next.config.ts — function style (recommended)\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti()({ reactStrictMode: true })\n * ```\n *\n * @example\n * ```ts\n * // next.config.ts — direct style\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti({ reactStrictMode: true })\n * ```\n */\nexport function withFluenti(fluentConfig?: WithFluentConfig): (nextConfig?: NextConfig) => NextConfig\nexport function withFluenti(nextConfig: NextConfig): NextConfig\nexport function withFluenti(\n configOrNext?: WithFluentConfig | NextConfig,\n): NextConfig | ((nextConfig?: NextConfig) => NextConfig) {\n if (configOrNext && isNextConfig(configOrNext as NextConfig)) {\n return applyFluenti({}, configOrNext as NextConfig)\n }\n\n const fluentConfig = (configOrNext ?? {}) as WithFluentConfig\n return function wrappedConfig(nextConfig?: NextConfig): NextConfig {\n return applyFluenti(fluentConfig, nextConfig ?? {})\n }\n}\n\nfunction isNextConfig(obj: NextConfig): boolean {\n const nextKeys = [\n 'reactStrictMode', 'experimental', 'images', 'env', 'webpack',\n 'rewrites', 'redirects', 'headers', 'pageExtensions', 'output',\n 'basePath', 'i18n', 'trailingSlash', 'compiler', 'transpilePackages',\n ]\n return nextKeys.some((key) => key in obj)\n}\n\nfunction applyFluenti(\n fluentConfig: WithFluentConfig,\n nextConfig: NextConfig,\n): NextConfig {\n const projectRoot = process.cwd()\n const resolved = resolveConfig(projectRoot, fluentConfig)\n\n // Warn if compiled catalogs directory doesn't exist yet\n const compiledDir = resolve(projectRoot, resolved.compiledDir)\n if (!existsSync(compiledDir)) {\n console.warn(\n `\\n[fluenti] Compiled catalogs not found at ${resolved.compiledDir}.\\n` +\n `Run: npx fluenti extract && npx fluenti compile\\n`,\n )\n }\n\n // Generate server module for RSC\n const serverModulePath = generateServerModule(projectRoot, resolved)\n\n // Resolve the loader path — use import.meta.url for ESM compatibility\n const thisDir = typeof __dirname !== 'undefined'\n ? __dirname\n : dirname(new URL(import.meta.url).pathname)\n const loaderPath = resolve(thisDir, 'loader.js')\n\n const existingWebpack = nextConfig['webpack'] as\n | ((config: WebpackConfig, options: WebpackOptions) => WebpackConfig)\n | undefined\n\n let buildCompileRan = false\n\n return {\n ...nextConfig,\n webpack(config: WebpackConfig, options: WebpackOptions) {\n // Add fluenti loader (enforce: pre — runs before other loaders)\n config.module.rules.push({\n test: /\\.[jt]sx?$/,\n enforce: 'pre' as const,\n exclude: [/node_modules/, /\\.next/],\n use: [\n {\n loader: loaderPath,\n options: {\n serverModulePath,\n },\n },\n ],\n })\n\n // Add resolve alias so loader can import from generated server module\n config.resolve = config.resolve ?? {} as WebpackConfig['resolve']\n config.resolve.alias = config.resolve.alias ?? {}\n config.resolve.alias['@fluenti/next$'] = serverModulePath\n\n // Auto compile before production build (run once across server+client passes)\n const buildAutoCompile = fluentConfig.buildAutoCompile ?? true\n if (!options.dev && buildAutoCompile && !buildCompileRan) {\n buildCompileRan = true\n try {\n // Use node -e with dynamic import — avoids CLI binary resolution issues.\n // webpack() is sync, so we use execSync to block until compile finishes.\n const escapedRoot = projectRoot.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\")\n execSync(\n `node --input-type=module -e \"const { runCompile } = await import('@fluenti/cli'); await runCompile('${escapedRoot}')\"`,\n { cwd: projectRoot, stdio: 'inherit' },\n )\n } catch {\n // @fluenti/cli not available or compile failed — skip silently\n }\n }\n\n // Auto extract+compile in dev mode\n const devAutoCompile = fluentConfig.devAutoCompile ?? true\n if (options.dev && devAutoCompile) {\n const devDelay = fluentConfig.devAutoCompileDelay ?? 1000\n const debouncedRun = createDebouncedRunner({ cwd: projectRoot }, devDelay)\n const compiledDirResolved = resolve(projectRoot, resolved.compiledDir)\n\n config.plugins = config.plugins ?? []\n config.plugins.push({\n apply(compiler: WebpackCompiler) {\n let isFirstBuild = true\n compiler.hooks.watchRun.tapAsync('fluenti-dev', (_compiler: WebpackCompiler, callback: () => void) => {\n if (isFirstBuild) {\n isFirstBuild = false\n debouncedRun()\n }\n const modifiedFiles = _compiler.modifiedFiles\n if (modifiedFiles) {\n const hasSourceChange = [...modifiedFiles].some((f: string) =>\n /\\.[jt]sx?$/.test(f)\n && !f.includes('node_modules')\n && !f.includes('.next')\n && !f.startsWith(compiledDirResolved),\n )\n if (hasSourceChange) {\n debouncedRun()\n }\n }\n callback()\n })\n },\n })\n }\n\n // Call user's webpack config if provided\n if (existingWebpack) {\n return existingWebpack(config, options)\n }\n\n return config\n },\n }\n}\n\n// Minimal webpack types for the config function\ninterface WebpackConfig {\n module: {\n rules: Array<{\n test: RegExp\n enforce?: 'pre' | 'post'\n exclude?: Array<RegExp>\n use: Array<{ loader: string; options: Record<string, unknown> }>\n }>\n }\n resolve: {\n alias?: Record<string, string>\n }\n plugins?: Array<{ apply(compiler: WebpackCompiler): void }>\n}\n\ninterface WebpackOptions {\n isServer: boolean\n dev: boolean\n}\n\ninterface WebpackCompiler {\n hooks: {\n watchRun: {\n tapAsync(name: string, cb: (compiler: WebpackCompiler, callback: () => void) => void): void\n }\n }\n modifiedFiles?: Set<string>\n}\n","/**\n * @fluenti/next — Next.js plugin for Fluenti\n *\n * Provides:\n * - `withFluenti()` — wraps next.config.ts with t`` transform support\n * - I18nProvider — async server component (exported from generated module)\n * - Webpack loader for strict, binding-aware tagged-template optimization\n *\n * @example\n * ```ts\n * // next.config.ts\n * import { withFluenti } from '@fluenti/next'\n * export default withFluenti()({ reactStrictMode: true })\n * ```\n *\n * @example\n * ```tsx\n * // app/layout.tsx — resolved by webpack alias to the generated module\n * import { I18nProvider } from '@fluenti/next'\n * ```\n */\nexport { withFluenti } from './with-fluenti'\nexport type { WithFluentConfig, I18nProviderProps } from './types'\n\n// ── Runtime stubs ────────────────────────────────────────────────────\n// TypeScript resolves types from this file (via package.json exports).\n// At runtime, webpack `resolve.alias` redirects `@fluenti/next$` to the\n// generated server module, so these stubs are never actually called in\n// a correctly configured project. They exist only to provide helpful\n// errors if `withFluenti()` is not configured.\n\nimport type { ReactNode, ReactElement } from 'react'\nimport type { CompileTimeT, FluentInstanceExtended } from '@fluenti/core'\n\nconst NOT_CONFIGURED =\n '[fluenti] `withFluenti()` must be configured in next.config.ts before importing from \"@fluenti/next\".'\n\nfunction throwNotConfigured(): never {\n throw new Error(NOT_CONFIGURED)\n}\n\n/** @see Generated module for the real implementation. */\nexport const setLocale: (locale: string) => void = throwNotConfigured\n/** @see Generated module for the real implementation. */\nexport const getI18n: () => Promise<FluentInstanceExtended & { locale: string }> = throwNotConfigured as () => Promise<FluentInstanceExtended & { locale: string }>\n/** @see Generated module for the real implementation. */\nexport const t: CompileTimeT = throwNotConfigured as unknown as CompileTimeT\n/** @see Generated module for the real implementation. */\nexport const Trans: (props: { children: ReactNode; id?: string; context?: string; comment?: string; render?: (translation: ReactNode) => ReactNode }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Trans\n/** @see Generated module for the real implementation. */\nexport const Plural: (props: { value: number; id?: string; context?: string; comment?: string; zero?: ReactNode; one?: ReactNode; two?: ReactNode; few?: ReactNode; many?: ReactNode; other: ReactNode; offset?: number }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Plural\n/** @see Generated module for the real implementation. */\nexport const Select: (props: { value: string; id?: string; context?: string; comment?: string; other: ReactNode; options?: Record<string, ReactNode>; [key: string]: ReactNode | Record<string, ReactNode> | string | undefined }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof Select\n/** @see Generated module for the real implementation. */\nexport const DateTime: (props: { value: Date | number; style?: string }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof DateTime\n/** @see Generated module for the real implementation. */\nexport const NumberFormat: (props: { value: number; style?: string }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof NumberFormat\n/** @see Generated module for the real implementation. */\nexport const I18nProvider: (props: { locale?: string; children: ReactNode }) => Promise<ReactElement> = throwNotConfigured as unknown as typeof I18nProvider\n"],"mappings":"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"}
package/dist/loader.cjs CHANGED
@@ -1,2 +1,2 @@
1
- let e=require(`@fluenti/core/internal`);function t(t){if(!/\.[jt]sx?$/.test(this.resourcePath)||/node_modules|\.next/.test(this.resourcePath))return t;let i=t,a=/^\s*['"]use client['"]/.test(i);if(/\.[jt]sx$/.test(this.resourcePath)&&/<Trans[\s>]/.test(i)){let t=(0,e.transformTransComponents)(i);t.transformed&&(i=t.code)}if(!r(i,a)&&!n(i))return i;let o=(0,e.scopeTransform)(i,{framework:`react`,serverModuleImport:`@fluenti/next`,treatFrameworkDirectImportsAsServer:!a,rerouteServerAuthoringImports:!a,errorOnServerUseI18n:!a});return o.transformed?o.code:i}function n(e){return/(?<![.\w$])t\(\s*['"]/.test(e)||/[A-Za-z_$][\w$]*\(\s*\{/.test(e)||/[A-Za-z_$][\w$]*`/.test(e)&&(e.includes(`useI18n`)||e.includes(`getI18n`))?!0:/import\s*\{\s*t(?:\s+as\s+[A-Za-z_$][\w$]*)?[\s,}]/.test(e)&&(e.includes(`@fluenti/react`)||e.includes(`@fluenti/next`))}function r(e,t){return t||!e.includes(`@fluenti/react`)&&!e.includes(`@fluenti/next`)?!1:/\b(useI18n|Trans|Plural|Select|DateTime|NumberFormat|t)\b/.test(e)}module.exports=t;
1
+ let e=require(`@fluenti/core/internal`);function t(t){if(!/\.[jt]sx?$/.test(this.resourcePath)||/node_modules|\.next/.test(this.resourcePath))return t;let i=t,a=/^\s*['"]use client['"]/.test(i);if(/\.[jt]sx$/.test(this.resourcePath)&&/<Trans[\s>]/.test(i)){let t=(0,e.transformTransComponents)(i);t.transformed&&(i=t.code)}if(!r(i,a)&&!n(i))return i;try{let t=(0,e.scopeTransform)(i,{framework:`react`,serverModuleImport:`@fluenti/next`,treatFrameworkDirectImportsAsServer:!a,rerouteServerAuthoringImports:!a,errorOnServerUseI18n:!a});if(t.transformed)return t.code}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`[fluenti] Transform failed in ${this.resourcePath}: ${t}`)}return i}function n(e){return/(?<![.\w$])t\(\s*['"]/.test(e)||/[A-Za-z_$][\w$]*\(\s*\{/.test(e)||/[A-Za-z_$][\w$]*`/.test(e)&&(e.includes(`useI18n`)||e.includes(`getI18n`))?!0:/import\s*\{\s*t(?:\s+as\s+[A-Za-z_$][\w$]*)?[\s,}]/.test(e)&&(e.includes(`@fluenti/react`)||e.includes(`@fluenti/next`))}function r(e,t){return t||!e.includes(`@fluenti/react`)&&!e.includes(`@fluenti/next`)?!1:/\b(useI18n|Trans|Plural|Select|DateTime|NumberFormat|t)\b/.test(e)}module.exports=t;
2
2
  //# sourceMappingURL=loader.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.cjs","names":[],"sources":["../src/loader.ts"],"sourcesContent":["/**\n * Webpack loader for t`` and t() transforms in Next.js.\n *\n * Runs as enforce: 'pre' to transform source before other loaders.\n * Only statically provable `t` bindings are optimized; runtime `t()` calls\n * continue to work without injected proxy globals.\n */\nimport { scopeTransform } from './scope-transform'\nimport { transformTransComponents } from './trans-transform'\n\n/**\n * Webpack loader function.\n * `this` is the webpack LoaderContext.\n */\nexport default function fluentLoader(this: LoaderContext, source: string): string {\n // Only process .tsx, .ts, .jsx, .js files\n if (!/\\.[jt]sx?$/.test(this.resourcePath)) {\n return source\n }\n\n // Skip node_modules and .next directory\n if (/node_modules|\\.next/.test(this.resourcePath)) {\n return source\n }\n\n let result = source\n const isClientModule = /^\\s*['\"]use client['\"]/.test(result)\n\n // ── <Trans> compile-time optimization (JSX/TSX only) ──────────────\n if (/\\.[jt]sx$/.test(this.resourcePath) && /<Trans[\\s>]/.test(result)) {\n const transResult = transformTransComponents(result)\n if (transResult.transformed) {\n result = transResult.code\n }\n }\n\n // Quick check: does this file contain any Fluenti authoring/runtime surface?\n if (!isServerFluentiFile(result, isClientModule) && !hasFluentPatterns(result)) {\n return result\n }\n\n // Try scope-aware transform first (AST-based, zero false positives)\n const scoped = scopeTransform(result, {\n framework: 'react',\n serverModuleImport: '@fluenti/next',\n treatFrameworkDirectImportsAsServer: !isClientModule,\n rerouteServerAuthoringImports: !isClientModule,\n errorOnServerUseI18n: !isClientModule,\n })\n if (scoped.transformed) {\n return scoped.code\n }\n\n return result\n}\n\n/**\n * Quick regex check to avoid full parsing on files without t`` or t().\n */\nfunction hasFluentPatterns(code: string): boolean {\n if (/(?<![.\\w$])t\\(\\s*['\"]/.test(code) || /[A-Za-z_$][\\w$]*\\(\\s*\\{/.test(code)) {\n return true\n }\n\n if (/[A-Za-z_$][\\w$]*`/.test(code) && (code.includes('useI18n') || code.includes('getI18n'))) {\n return true\n }\n\n return /import\\s*\\{\\s*t(?:\\s+as\\s+[A-Za-z_$][\\w$]*)?[\\s,}]/.test(code)\n && (code.includes('@fluenti/react') || code.includes('@fluenti/next'))\n}\n\nfunction isServerFluentiFile(code: string, isClientModule: boolean): boolean {\n if (isClientModule) return false\n if (!code.includes('@fluenti/react') && !code.includes('@fluenti/next')) {\n return false\n }\n\n return /\\b(useI18n|Trans|Plural|Select|DateTime|NumberFormat|t)\\b/.test(code)\n}\n\ninterface LoaderContext {\n resourcePath: string\n getOptions(): Record<string, unknown>\n}\n"],"mappings":"wCAcA,SAAwB,EAAkC,EAAwB,CAOhF,GALI,CAAC,aAAa,KAAK,KAAK,aAAa,EAKrC,sBAAsB,KAAK,KAAK,aAAa,CAC/C,OAAO,EAGT,IAAI,EAAS,EACP,EAAiB,yBAAyB,KAAK,EAAO,CAG5D,GAAI,YAAY,KAAK,KAAK,aAAa,EAAI,cAAc,KAAK,EAAO,CAAE,CACrE,IAAM,GAAA,EAAA,EAAA,0BAAuC,EAAO,CAChD,EAAY,cACd,EAAS,EAAY,MAKzB,GAAI,CAAC,EAAoB,EAAQ,EAAe,EAAI,CAAC,EAAkB,EAAO,CAC5E,OAAO,EAIT,IAAM,GAAA,EAAA,EAAA,gBAAwB,EAAQ,CACpC,UAAW,QACX,mBAAoB,gBACpB,oCAAqC,CAAC,EACtC,8BAA+B,CAAC,EAChC,qBAAsB,CAAC,EACxB,CAAC,CAKF,OAJI,EAAO,YACF,EAAO,KAGT,EAMT,SAAS,EAAkB,EAAuB,CAShD,MARI,wBAAwB,KAAK,EAAK,EAAI,0BAA0B,KAAK,EAAK,EAI1E,oBAAoB,KAAK,EAAK,GAAK,EAAK,SAAS,UAAU,EAAI,EAAK,SAAS,UAAU,EAClF,GAGF,qDAAqD,KAAK,EAAK,GAChE,EAAK,SAAS,iBAAiB,EAAI,EAAK,SAAS,gBAAgB,EAGzE,SAAS,EAAoB,EAAc,EAAkC,CAM3E,OALI,GACA,CAAC,EAAK,SAAS,iBAAiB,EAAI,CAAC,EAAK,SAAS,gBAAgB,CAC9D,GAGF,4DAA4D,KAAK,EAAK"}
1
+ {"version":3,"file":"loader.cjs","names":[],"sources":["../src/loader.ts"],"sourcesContent":["/**\n * Webpack loader for t`` and t() transforms in Next.js.\n *\n * Runs as enforce: 'pre' to transform source before other loaders.\n * Only statically provable `t` bindings are optimized; runtime `t()` calls\n * continue to work without injected proxy globals.\n */\nimport { scopeTransform } from './scope-transform'\nimport { transformTransComponents } from './trans-transform'\n\n/**\n * Webpack loader function.\n * `this` is the webpack LoaderContext.\n */\nexport default function fluentLoader(this: LoaderContext, source: string): string {\n // Only process .tsx, .ts, .jsx, .js files\n if (!/\\.[jt]sx?$/.test(this.resourcePath)) {\n return source\n }\n\n // Skip node_modules and .next directory\n if (/node_modules|\\.next/.test(this.resourcePath)) {\n return source\n }\n\n let result = source\n const isClientModule = /^\\s*['\"]use client['\"]/.test(result)\n\n // ── <Trans> compile-time optimization (JSX/TSX only) ──────────────\n if (/\\.[jt]sx$/.test(this.resourcePath) && /<Trans[\\s>]/.test(result)) {\n const transResult = transformTransComponents(result)\n if (transResult.transformed) {\n result = transResult.code\n }\n }\n\n // Quick check: does this file contain any Fluenti authoring/runtime surface?\n if (!isServerFluentiFile(result, isClientModule) && !hasFluentPatterns(result)) {\n return result\n }\n\n // Try scope-aware transform first (AST-based, zero false positives)\n try {\n const scoped = scopeTransform(result, {\n framework: 'react',\n serverModuleImport: '@fluenti/next',\n treatFrameworkDirectImportsAsServer: !isClientModule,\n rerouteServerAuthoringImports: !isClientModule,\n errorOnServerUseI18n: !isClientModule,\n })\n if (scoped.transformed) {\n return scoped.code\n }\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error)\n throw new Error(`[fluenti] Transform failed in ${this.resourcePath}: ${msg}`)\n }\n\n return result\n}\n\n/**\n * Quick regex check to avoid full parsing on files without t`` or t().\n */\nfunction hasFluentPatterns(code: string): boolean {\n if (/(?<![.\\w$])t\\(\\s*['\"]/.test(code) || /[A-Za-z_$][\\w$]*\\(\\s*\\{/.test(code)) {\n return true\n }\n\n if (/[A-Za-z_$][\\w$]*`/.test(code) && (code.includes('useI18n') || code.includes('getI18n'))) {\n return true\n }\n\n return /import\\s*\\{\\s*t(?:\\s+as\\s+[A-Za-z_$][\\w$]*)?[\\s,}]/.test(code)\n && (code.includes('@fluenti/react') || code.includes('@fluenti/next'))\n}\n\nfunction isServerFluentiFile(code: string, isClientModule: boolean): boolean {\n if (isClientModule) return false\n if (!code.includes('@fluenti/react') && !code.includes('@fluenti/next')) {\n return false\n }\n\n return /\\b(useI18n|Trans|Plural|Select|DateTime|NumberFormat|t)\\b/.test(code)\n}\n\ninterface LoaderContext {\n resourcePath: string\n getOptions(): Record<string, unknown>\n}\n"],"mappings":"wCAcA,SAAwB,EAAkC,EAAwB,CAOhF,GALI,CAAC,aAAa,KAAK,KAAK,aAAa,EAKrC,sBAAsB,KAAK,KAAK,aAAa,CAC/C,OAAO,EAGT,IAAI,EAAS,EACP,EAAiB,yBAAyB,KAAK,EAAO,CAG5D,GAAI,YAAY,KAAK,KAAK,aAAa,EAAI,cAAc,KAAK,EAAO,CAAE,CACrE,IAAM,GAAA,EAAA,EAAA,0BAAuC,EAAO,CAChD,EAAY,cACd,EAAS,EAAY,MAKzB,GAAI,CAAC,EAAoB,EAAQ,EAAe,EAAI,CAAC,EAAkB,EAAO,CAC5E,OAAO,EAIT,GAAI,CACF,IAAM,GAAA,EAAA,EAAA,gBAAwB,EAAQ,CACpC,UAAW,QACX,mBAAoB,gBACpB,oCAAqC,CAAC,EACtC,8BAA+B,CAAC,EAChC,qBAAsB,CAAC,EACxB,CAAC,CACF,GAAI,EAAO,YACT,OAAO,EAAO,WAET,EAAgB,CACvB,IAAM,EAAM,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,CAClE,MAAU,MAAM,iCAAiC,KAAK,aAAa,IAAI,IAAM,CAG/E,OAAO,EAMT,SAAS,EAAkB,EAAuB,CAShD,MARI,wBAAwB,KAAK,EAAK,EAAI,0BAA0B,KAAK,EAAK,EAI1E,oBAAoB,KAAK,EAAK,GAAK,EAAK,SAAS,UAAU,EAAI,EAAK,SAAS,UAAU,EAClF,GAGF,qDAAqD,KAAK,EAAK,GAChE,EAAK,SAAS,iBAAiB,EAAI,EAAK,SAAS,gBAAgB,EAGzE,SAAS,EAAoB,EAAc,EAAkC,CAM3E,OALI,GACA,CAAC,EAAK,SAAS,iBAAiB,EAAI,CAAC,EAAK,SAAS,gBAAgB,CAC9D,GAGF,4DAA4D,KAAK,EAAK"}
package/dist/loader.js CHANGED
@@ -8,14 +8,20 @@ function n(n) {
8
8
  e.transformed && (a = e.code);
9
9
  }
10
10
  if (!i(a, o) && !r(a)) return a;
11
- let s = e(a, {
12
- framework: "react",
13
- serverModuleImport: "@fluenti/next",
14
- treatFrameworkDirectImportsAsServer: !o,
15
- rerouteServerAuthoringImports: !o,
16
- errorOnServerUseI18n: !o
17
- });
18
- return s.transformed ? s.code : a;
11
+ try {
12
+ let t = e(a, {
13
+ framework: "react",
14
+ serverModuleImport: "@fluenti/next",
15
+ treatFrameworkDirectImportsAsServer: !o,
16
+ rerouteServerAuthoringImports: !o,
17
+ errorOnServerUseI18n: !o
18
+ });
19
+ if (t.transformed) return t.code;
20
+ } catch (e) {
21
+ let t = e instanceof Error ? e.message : String(e);
22
+ throw Error(`[fluenti] Transform failed in ${this.resourcePath}: ${t}`);
23
+ }
24
+ return a;
19
25
  }
20
26
  function r(e) {
21
27
  return /(?<![.\w$])t\(\s*['"]/.test(e) || /[A-Za-z_$][\w$]*\(\s*\{/.test(e) || /[A-Za-z_$][\w$]*`/.test(e) && (e.includes("useI18n") || e.includes("getI18n")) ? !0 : /import\s*\{\s*t(?:\s+as\s+[A-Za-z_$][\w$]*)?[\s,}]/.test(e) && (e.includes("@fluenti/react") || e.includes("@fluenti/next"));
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","names":[],"sources":["../src/loader.ts"],"sourcesContent":["/**\n * Webpack loader for t`` and t() transforms in Next.js.\n *\n * Runs as enforce: 'pre' to transform source before other loaders.\n * Only statically provable `t` bindings are optimized; runtime `t()` calls\n * continue to work without injected proxy globals.\n */\nimport { scopeTransform } from './scope-transform'\nimport { transformTransComponents } from './trans-transform'\n\n/**\n * Webpack loader function.\n * `this` is the webpack LoaderContext.\n */\nexport default function fluentLoader(this: LoaderContext, source: string): string {\n // Only process .tsx, .ts, .jsx, .js files\n if (!/\\.[jt]sx?$/.test(this.resourcePath)) {\n return source\n }\n\n // Skip node_modules and .next directory\n if (/node_modules|\\.next/.test(this.resourcePath)) {\n return source\n }\n\n let result = source\n const isClientModule = /^\\s*['\"]use client['\"]/.test(result)\n\n // ── <Trans> compile-time optimization (JSX/TSX only) ──────────────\n if (/\\.[jt]sx$/.test(this.resourcePath) && /<Trans[\\s>]/.test(result)) {\n const transResult = transformTransComponents(result)\n if (transResult.transformed) {\n result = transResult.code\n }\n }\n\n // Quick check: does this file contain any Fluenti authoring/runtime surface?\n if (!isServerFluentiFile(result, isClientModule) && !hasFluentPatterns(result)) {\n return result\n }\n\n // Try scope-aware transform first (AST-based, zero false positives)\n const scoped = scopeTransform(result, {\n framework: 'react',\n serverModuleImport: '@fluenti/next',\n treatFrameworkDirectImportsAsServer: !isClientModule,\n rerouteServerAuthoringImports: !isClientModule,\n errorOnServerUseI18n: !isClientModule,\n })\n if (scoped.transformed) {\n return scoped.code\n }\n\n return result\n}\n\n/**\n * Quick regex check to avoid full parsing on files without t`` or t().\n */\nfunction hasFluentPatterns(code: string): boolean {\n if (/(?<![.\\w$])t\\(\\s*['\"]/.test(code) || /[A-Za-z_$][\\w$]*\\(\\s*\\{/.test(code)) {\n return true\n }\n\n if (/[A-Za-z_$][\\w$]*`/.test(code) && (code.includes('useI18n') || code.includes('getI18n'))) {\n return true\n }\n\n return /import\\s*\\{\\s*t(?:\\s+as\\s+[A-Za-z_$][\\w$]*)?[\\s,}]/.test(code)\n && (code.includes('@fluenti/react') || code.includes('@fluenti/next'))\n}\n\nfunction isServerFluentiFile(code: string, isClientModule: boolean): boolean {\n if (isClientModule) return false\n if (!code.includes('@fluenti/react') && !code.includes('@fluenti/next')) {\n return false\n }\n\n return /\\b(useI18n|Trans|Plural|Select|DateTime|NumberFormat|t)\\b/.test(code)\n}\n\ninterface LoaderContext {\n resourcePath: string\n getOptions(): Record<string, unknown>\n}\n"],"mappings":";;AAcA,SAAwB,EAAkC,GAAwB;AAOhF,KALI,CAAC,aAAa,KAAK,KAAK,aAAa,IAKrC,sBAAsB,KAAK,KAAK,aAAa,CAC/C,QAAO;CAGT,IAAI,IAAS,GACP,IAAiB,yBAAyB,KAAK,EAAO;AAG5D,KAAI,YAAY,KAAK,KAAK,aAAa,IAAI,cAAc,KAAK,EAAO,EAAE;EACrE,IAAM,IAAc,EAAyB,EAAO;AACpD,EAAI,EAAY,gBACd,IAAS,EAAY;;AAKzB,KAAI,CAAC,EAAoB,GAAQ,EAAe,IAAI,CAAC,EAAkB,EAAO,CAC5E,QAAO;CAIT,IAAM,IAAS,EAAe,GAAQ;EACpC,WAAW;EACX,oBAAoB;EACpB,qCAAqC,CAAC;EACtC,+BAA+B,CAAC;EAChC,sBAAsB,CAAC;EACxB,CAAC;AAKF,QAJI,EAAO,cACF,EAAO,OAGT;;AAMT,SAAS,EAAkB,GAAuB;AAShD,QARI,wBAAwB,KAAK,EAAK,IAAI,0BAA0B,KAAK,EAAK,IAI1E,oBAAoB,KAAK,EAAK,KAAK,EAAK,SAAS,UAAU,IAAI,EAAK,SAAS,UAAU,IAClF,KAGF,qDAAqD,KAAK,EAAK,KAChE,EAAK,SAAS,iBAAiB,IAAI,EAAK,SAAS,gBAAgB;;AAGzE,SAAS,EAAoB,GAAc,GAAkC;AAM3E,QALI,KACA,CAAC,EAAK,SAAS,iBAAiB,IAAI,CAAC,EAAK,SAAS,gBAAgB,GAC9D,KAGF,4DAA4D,KAAK,EAAK"}
1
+ {"version":3,"file":"loader.js","names":[],"sources":["../src/loader.ts"],"sourcesContent":["/**\n * Webpack loader for t`` and t() transforms in Next.js.\n *\n * Runs as enforce: 'pre' to transform source before other loaders.\n * Only statically provable `t` bindings are optimized; runtime `t()` calls\n * continue to work without injected proxy globals.\n */\nimport { scopeTransform } from './scope-transform'\nimport { transformTransComponents } from './trans-transform'\n\n/**\n * Webpack loader function.\n * `this` is the webpack LoaderContext.\n */\nexport default function fluentLoader(this: LoaderContext, source: string): string {\n // Only process .tsx, .ts, .jsx, .js files\n if (!/\\.[jt]sx?$/.test(this.resourcePath)) {\n return source\n }\n\n // Skip node_modules and .next directory\n if (/node_modules|\\.next/.test(this.resourcePath)) {\n return source\n }\n\n let result = source\n const isClientModule = /^\\s*['\"]use client['\"]/.test(result)\n\n // ── <Trans> compile-time optimization (JSX/TSX only) ──────────────\n if (/\\.[jt]sx$/.test(this.resourcePath) && /<Trans[\\s>]/.test(result)) {\n const transResult = transformTransComponents(result)\n if (transResult.transformed) {\n result = transResult.code\n }\n }\n\n // Quick check: does this file contain any Fluenti authoring/runtime surface?\n if (!isServerFluentiFile(result, isClientModule) && !hasFluentPatterns(result)) {\n return result\n }\n\n // Try scope-aware transform first (AST-based, zero false positives)\n try {\n const scoped = scopeTransform(result, {\n framework: 'react',\n serverModuleImport: '@fluenti/next',\n treatFrameworkDirectImportsAsServer: !isClientModule,\n rerouteServerAuthoringImports: !isClientModule,\n errorOnServerUseI18n: !isClientModule,\n })\n if (scoped.transformed) {\n return scoped.code\n }\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error)\n throw new Error(`[fluenti] Transform failed in ${this.resourcePath}: ${msg}`)\n }\n\n return result\n}\n\n/**\n * Quick regex check to avoid full parsing on files without t`` or t().\n */\nfunction hasFluentPatterns(code: string): boolean {\n if (/(?<![.\\w$])t\\(\\s*['\"]/.test(code) || /[A-Za-z_$][\\w$]*\\(\\s*\\{/.test(code)) {\n return true\n }\n\n if (/[A-Za-z_$][\\w$]*`/.test(code) && (code.includes('useI18n') || code.includes('getI18n'))) {\n return true\n }\n\n return /import\\s*\\{\\s*t(?:\\s+as\\s+[A-Za-z_$][\\w$]*)?[\\s,}]/.test(code)\n && (code.includes('@fluenti/react') || code.includes('@fluenti/next'))\n}\n\nfunction isServerFluentiFile(code: string, isClientModule: boolean): boolean {\n if (isClientModule) return false\n if (!code.includes('@fluenti/react') && !code.includes('@fluenti/next')) {\n return false\n }\n\n return /\\b(useI18n|Trans|Plural|Select|DateTime|NumberFormat|t)\\b/.test(code)\n}\n\ninterface LoaderContext {\n resourcePath: string\n getOptions(): Record<string, unknown>\n}\n"],"mappings":";;AAcA,SAAwB,EAAkC,GAAwB;AAOhF,KALI,CAAC,aAAa,KAAK,KAAK,aAAa,IAKrC,sBAAsB,KAAK,KAAK,aAAa,CAC/C,QAAO;CAGT,IAAI,IAAS,GACP,IAAiB,yBAAyB,KAAK,EAAO;AAG5D,KAAI,YAAY,KAAK,KAAK,aAAa,IAAI,cAAc,KAAK,EAAO,EAAE;EACrE,IAAM,IAAc,EAAyB,EAAO;AACpD,EAAI,EAAY,gBACd,IAAS,EAAY;;AAKzB,KAAI,CAAC,EAAoB,GAAQ,EAAe,IAAI,CAAC,EAAkB,EAAO,CAC5E,QAAO;AAIT,KAAI;EACF,IAAM,IAAS,EAAe,GAAQ;GACpC,WAAW;GACX,oBAAoB;GACpB,qCAAqC,CAAC;GACtC,+BAA+B,CAAC;GAChC,sBAAsB,CAAC;GACxB,CAAC;AACF,MAAI,EAAO,YACT,QAAO,EAAO;UAET,GAAgB;EACvB,IAAM,IAAM,aAAiB,QAAQ,EAAM,UAAU,OAAO,EAAM;AAClE,QAAU,MAAM,iCAAiC,KAAK,aAAa,IAAI,IAAM;;AAG/E,QAAO;;AAMT,SAAS,EAAkB,GAAuB;AAShD,QARI,wBAAwB,KAAK,EAAK,IAAI,0BAA0B,KAAK,EAAK,IAI1E,oBAAoB,KAAK,EAAK,KAAK,EAAK,SAAS,UAAU,IAAI,EAAK,SAAS,UAAU,IAClF,KAGF,qDAAqD,KAAK,EAAK,KAChE,EAAK,SAAS,iBAAiB,IAAI,EAAK,SAAS,gBAAgB;;AAGzE,SAAS,EAAoB,GAAc,GAAkC;AAM3E,QALI,KACA,CAAC,EAAK,SAAS,iBAAiB,IAAI,CAAC,EAAK,SAAS,gBAAgB,GAC9D,KAGF,4DAA4D,KAAK,EAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"read-config.d.ts","sourceRoot":"","sources":["../src/read-config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAarE;;GAEG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,gBAAgB,GAC3B,oBAAoB,CA8BtB"}
1
+ {"version":3,"file":"read-config.d.ts","sourceRoot":"","sources":["../src/read-config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAarE;;GAEG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,gBAAgB,GAC3B,oBAAoB,CAiCtB"}
package/dist/types.d.ts CHANGED
@@ -27,6 +27,8 @@ export interface WithFluentConfig {
27
27
  * @example './lib/resolve-locale'
28
28
  */
29
29
  resolveLocale?: string;
30
+ /** Cookie name used for locale detection (default: 'locale') */
31
+ cookieName?: string;
30
32
  /** Custom date format styles */
31
33
  dateFormats?: DateFormatOptions;
32
34
  /** Custom number format styles */
@@ -50,6 +52,7 @@ export interface ResolvedFluentConfig {
50
52
  serverModule: string | null;
51
53
  serverModuleOutDir: string;
52
54
  resolveLocale?: string;
55
+ cookieName: string;
53
56
  dateFormats?: DateFormatOptions;
54
57
  numberFormats?: NumberFormatOptions;
55
58
  fallbackChain?: Record<string, Locale[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAEnF;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAE3B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,gCAAgC;IAChC,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,kCAAkC;IAClC,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,gCAAgC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACxC,uDAAuD;IACvD,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,8GAA8G;IAC9G,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAEnF;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAE3B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB,gCAAgC;IAChC,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,kCAAkC;IAClC,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,gCAAgC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACxC,uDAAuD;IACvD,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,8GAA8G;IAC9G,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluenti/next",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Next.js plugin for Fluenti — withFluenti, I18nProvider, t`` transforms for App Router and Pages Router",
6
6
  "homepage": "https://fluenti.dev",
@@ -71,8 +71,8 @@
71
71
  },
72
72
  "dependencies": {
73
73
  "jiti": "^2",
74
- "@fluenti/core": "0.2.0",
75
- "@fluenti/react": "0.2.0"
74
+ "@fluenti/core": "0.2.1",
75
+ "@fluenti/react": "0.2.1"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@types/node": "^22",