Skip to content
fluenti

@fluenti/core/transform

Stable, semver-guaranteed API for bundler plugin authors. Use this to build Fluenti integrations for Vite, webpack, Parcel, or any other bundler.

Create a reusable transform pipeline that encapsulates the full Fluenti code-transform chain.

import { createTransformPipeline } from '@fluenti/core/transform'
const pipeline = createTransformPipeline({
framework: 'react',
scope: { serverModuleImport: '@fluenti/next' },
})
// In your bundler's transform hook / loader:
const result = pipeline.transform(source, filePath)
if (result.transformed) {
return result.code
}
ParameterTypeDescription
frameworkstringFramework identifier: 'vue', 'solid', 'react', 'svelte'
scopeOmit<ScopeTransformOptions, 'framework'>Default scope-transform options (merged with per-call overrides)
MethodSignatureDescription
transform(code: string, fileId: string) => TransformResultFull chain: <Trans> optimization → scope transform
transformTrans(code: string) => TransTransformResultOnly <Trans> component optimization
transformScope(code: string, overrides?) => ScopeTransformResultOnly scope-aware t rewriting, with optional per-call overrides

The transform method automatically:

  1. Detects JSX/TSX files and runs <Trans> optimization when <Trans is found
  2. Runs a quick regex pre-check (hasScopeTransformCandidate) to skip files without Fluenti patterns
  3. Runs the full scope-aware AST transform

Quick regex pre-check that returns true when source code may contain Fluenti patterns (t(), t` `, import { t }). Use this to skip expensive AST parsing on irrelevant files.

import { hasScopeTransformCandidate } from '@fluenti/core/transform'
// In a webpack loader:
if (!hasScopeTransformCandidate(source)) {
return source // skip transform
}

False positives are acceptable (the AST transform is authoritative). False negatives are not.

Low-level scope-aware transform. Rewrites t`Hello ${name}` to t({ id, message }, { name }) based on AST analysis of variable bindings.

import { scopeTransform } from '@fluenti/core/transform'
const result = scopeTransform(source, {
framework: 'react',
serverModuleImport: '@fluenti/next',
treatFrameworkDirectImportsAsServer: true,
})
OptionTypeDescription
frameworkstringRequired. Framework identifier
allowTopLevelImportedTbooleanVue SFC: allow top-level import { t } without useI18n()
serverModuleImportstringNext.js: module that provides server-side t (e.g. '@fluenti/next')
treatFrameworkDirectImportsAsServerbooleanNext.js: treat direct framework imports as server-side
rerouteServerAuthoringImportsbooleanNext.js: reroute server authoring imports
errorOnServerUseI18nbooleanNext.js: error when useI18n() is used in server context

Optimizes <Trans>, <Plural>, <Select> JSX components at compile time by injecting __id, __message, and __components props.

import { transformTransComponents } from '@fluenti/core/transform'
const result = transformTransComponents(jsxSource)
if (result.transformed) {
// result.code has optimized components
}

Create a RuntimeGenerator from framework-specific reactive primitives. This eliminates duplication across framework packages by parameterizing only the reactivity API differences.

import { createRuntimeGenerator } from '@fluenti/core/transform'
const generator = createRuntimeGenerator({
imports: "import { ref, shallowReactive } from 'vue'",
catalogInit: 'const __catalog = shallowReactive({ ...__defaultMsgs })',
localeInit: (d) => `const __currentLocale = ref('${d}')`,
loadingInit: 'const __loading = ref(false)',
catalogUpdate: (msgs) => `Object.assign(__catalog, ${msgs})`,
localeUpdate: (locale) => `__currentLocale.value = ${locale}`,
loadingUpdate: (value) => `__loading.value = ${value}`,
localeRead: '__currentLocale.value',
runtimeKey: 'fluenti.runtime.vue.v1',
})
PropertyTypeDescription
importsstringImport statements for framework reactivity
catalogInitstringExpression to create reactive catalog object
localeInit(defaultLocale: string) => stringExpression to create reactive locale variable
loadingInitstringExpression to create reactive loading flag
catalogUpdate(msgs: string) => stringStatement to replace catalog with new messages
localeUpdate(locale: string) => stringStatement to update current locale
loadingUpdate(value: string) => stringStatement to set loading state
localeReadstringExpression to read current locale value
runtimeKeystringSymbol.for() key for globalThis registration
MethodSignatureDescription
generateRuntime(options: RuntimeGeneratorOptions) => stringGenerate the main reactive runtime module
FunctionDescription
createMessageId(message, context?)Generate a deterministic hash-based message ID
canonicalizeMessageIdentity(message, context?)Canonicalize message + context for hashing
resolveDescriptorId(descriptor)Resolve a descriptor’s effective ID (explicit or hash)
isGeneratedMessageId(id)Check if an ID was auto-generated (vs explicit)
FunctionDescription
parseSourceModule(code)Parse source code to Babel AST
walkSourceAst(ast, visitor)Walk AST with visitor pattern
isSourceNode(node)Type guard for AST nodes

Conceptual example showing how to use these APIs in a webpack plugin:

import { createTransformPipeline, hasScopeTransformCandidate } from '@fluenti/core/transform'
import { loadConfigSync } from '@fluenti/core/config'
import { resolveLocaleCodes } from '@fluenti/core'
const config = loadConfigSync()
const pipeline = createTransformPipeline({ framework: 'react' })
// Webpack loader
export default function fluentLoader(source: string): string {
if (!hasScopeTransformCandidate(source)) return source
const result = pipeline.transform(source, this.resourcePath)
return result.transformed ? result.code : source
}
// Webpack plugin
class FluentWebpackPlugin {
apply(compiler) {
// Pre-build: compile catalogs
compiler.hooks.beforeCompile.tapPromise('fluenti', async () => {
const { runCompile } = await import('@fluenti/cli')
await runCompile(process.cwd())
})
// Register loader
compiler.options.module.rules.push({
test: /\.[jt]sx?$/,
exclude: /node_modules/,
use: [{ loader: require.resolve('./loader') }],
})
// Alias compiled catalogs
const locales = resolveLocaleCodes(config.locales)
for (const locale of locales) {
compiler.options.resolve.alias[`@fluenti/messages/${locale}`] =
resolve(config.compileOutDir, `${locale}.js`)
}
}
}