diff --git a/jest.config.js b/jest.config.js index 8e1b73e8..7c6bcc9f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -35,10 +35,6 @@ export default { ], }, }, - { - displayName: '@nkzw/babel-plugin-fbtee-runtime', - roots: ['/packages/babel-plugin-fbtee-runtime'], - }, { displayName: 'fbtee', modulePaths: ['/packages/fbtee/src'], diff --git a/packages/babel-plugin-fbtee-runtime/.npmignore b/packages/babel-plugin-fbtee-runtime/.npmignore deleted file mode 100644 index 85de9cf9..00000000 --- a/packages/babel-plugin-fbtee-runtime/.npmignore +++ /dev/null @@ -1 +0,0 @@ -src diff --git a/packages/babel-plugin-fbtee-runtime/package.json b/packages/babel-plugin-fbtee-runtime/package.json deleted file mode 100644 index b6323a66..00000000 --- a/packages/babel-plugin-fbtee-runtime/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "@nkzw/babel-plugin-fbtee-runtime", - "version": "0.1.4", - "description": "The JavaScript & React Internationalization Framework.", - "keywords": [ - "fbt", - "globalization", - "i18n", - "internationalization", - "l10n", - "localization", - "react", - "translation" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/nkzw-tech/fbtee.git", - "directory": "packages/babel-plugin-fbtee-runtime" - }, - "license": "MIT", - "type": "module", - "main": "lib/index.js", - "scripts": { - "build": "tsup src/index.tsx -d lib --target=node22 --format=esm --clean --no-splitting --dts" - }, - "dependencies": { - "@babel/core": "^7.26.0", - "@babel/preset-react": "^7.26.3", - "@babel/traverse": "^7.26.4", - "@babel/types": "^7.26.3", - "invariant": "^2.2.4" - }, - "devDependencies": { - "@nkzw/babel-plugin-fbtee-auto-import": "workspace:^" - }, - "peerDependencies": { - "@nkzw/babel-plugin-fbtee": "workspace:^" - } -} diff --git a/packages/babel-plugin-fbtee-runtime/src/index.tsx b/packages/babel-plugin-fbtee-runtime/src/index.tsx deleted file mode 100644 index 3e61f3f0..00000000 --- a/packages/babel-plugin-fbtee-runtime/src/index.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { NodePath } from '@babel/core'; -import { - identifier, - isCallExpression, - isObjectExpression, - nullLiteral, - objectExpression, - objectProperty, - stringLiteral, - StringLiteral, -} from '@babel/types'; -import { - fbtHashKey, - mapLeaves, - replaceClearTokensWithTokenAliases, - SENTINEL, -} from '@nkzw/babel-plugin-fbtee'; -import type { ObjectWithJSFBT } from '@nkzw/babel-plugin-fbtee'; -import invariant from 'invariant'; - -export default function BabelPluginFbtRuntime() { - return { - name: 'fbt-runtime', - visitor: { - /** - * Transform the following: - * fbt._( - * SENTINEL + - * JSON.strinfigy({ - * type: "text", - * jsfbt: "jsfbt test" | { - * "t": {... jsfbt table} - * ... - * }, - * desc: "desc", - * project: "project", - * }) + - * SENTINEL - * ); - * to: - * fbt._("jsfbt test") or fbt._({... jsfbt table}) - */ - StringLiteral(path: NodePath) { - const sentinelLength = SENTINEL.length; - const phrase = path.node.value; - if ( - !phrase.startsWith(SENTINEL) || - !phrase.endsWith(SENTINEL) || - phrase.length <= sentinelLength * 2 - ) { - return; - } - - const parsedPhrase = JSON.parse( - phrase.slice(sentinelLength, phrase.length - sentinelLength), - ) as ObjectWithJSFBT; - - const runtimeInput = mapLeaves(parsedPhrase.jsfbt.t, (leaf) => - replaceClearTokensWithTokenAliases(leaf.text, leaf.tokenAliases), - ); - path.replaceWithSourceString(JSON.stringify(runtimeInput)); - - const parentNode = path.parentPath && path.parentPath.node; - invariant( - isCallExpression(parentNode), - `Expected parent node to be a 'CallExpression'`, - ); - - // Append runtime options - key for runtime dictionary lookup - if (parentNode.arguments.length === 1) { - // Second param 'args' could be omitted sometimes. Use null here. - parentNode.arguments.push(nullLiteral()); - } - - // Append hash key to the options argument - const optionsNode = parentNode.arguments[2]; - invariant( - optionsNode == null || isObjectExpression(optionsNode), - 'Expect options node to be either null or an object expression but got %s (%s)', - optionsNode, - typeof optionsNode, - ); - - parentNode.arguments[2] = objectExpression([ - ...(optionsNode == null ? [] : [...optionsNode.properties]), - objectProperty( - identifier('hk'), - stringLiteral(fbtHashKey(parsedPhrase.jsfbt.t)), - ), - ]); - }, - }, - }; -} diff --git a/packages/babel-plugin-fbtee-runtime/src/__tests__/index-test.tsx b/packages/babel-plugin-fbtee/src/__tests__/fbtRuntime-test.tsx similarity index 95% rename from packages/babel-plugin-fbtee-runtime/src/__tests__/index-test.tsx rename to packages/babel-plugin-fbtee/src/__tests__/fbtRuntime-test.tsx index 33477919..0f8b9089 100644 --- a/packages/babel-plugin-fbtee-runtime/src/__tests__/index-test.tsx +++ b/packages/babel-plugin-fbtee/src/__tests__/fbtRuntime-test.tsx @@ -1,18 +1,17 @@ import { transformSync } from '@babel/core'; import presetReact from '@babel/preset-react'; import { describe, it } from '@jest/globals'; -import fbtee from '@nkzw/babel-plugin-fbtee'; import fbtAutoImport from '@nkzw/babel-plugin-fbtee-auto-import'; +import fbtee from '../index.tsx'; import { assertSourceAstEqual, withFbtRequireStatement, -} from '@nkzw/babel-plugin-fbtee/src/__tests__/FbtTestUtil.tsx'; -import fbteeRuntime from '../index.tsx'; +} from './FbtTestUtil.tsx'; const transform = (source: string) => transformSync(source, { ast: false, - plugins: [fbtAutoImport, fbtee, fbteeRuntime], + plugins: [fbtAutoImport, fbtee], presets: [presetReact], sourceType: 'module', })?.code || ''; diff --git a/packages/babel-plugin-fbtee/src/index.tsx b/packages/babel-plugin-fbtee/src/index.tsx index 93e73d1e..0f40aec3 100644 --- a/packages/babel-plugin-fbtee/src/index.tsx +++ b/packages/babel-plugin-fbtee/src/index.tsx @@ -1,10 +1,19 @@ import type { NodePath } from '@babel/core'; import { CallExpression, + identifier, ImportDeclaration, + isCallExpression, + isObjectExpression, JSXElement, Node, + nullLiteral, + objectExpression, + objectProperty, + stringLiteral, + StringLiteral, } from '@babel/types'; +import invariant from 'invariant'; import { parse as parseDocblock } from 'jest-docblock'; import FbtCommonFunctionCallProcessor from './babel-processors/FbtCommonFunctionCallProcessor.tsx'; import type { MetaPhrase } from './babel-processors/FbtFunctionCallProcessor.tsx'; @@ -16,11 +25,14 @@ import { toPlainFbtNodeTree } from './fbt-nodes/FbtNodeUtil.tsx'; import type { FbtCommonMap } from './FbtCommon.tsx'; import { init } from './FbtCommon.tsx'; import type { FbtCallSiteOptions, FbtOptionConfig } from './FbtConstants.tsx'; -import { ValidFbtOptions } from './FbtConstants.tsx'; +import { SENTINEL, ValidFbtOptions } from './FbtConstants.tsx'; import type { EnumManifest } from './FbtEnumRegistrar.tsx'; import FbtEnumRegistrar from './FbtEnumRegistrar.tsx'; +import fbtHashKey from './fbtHashKey.tsx'; import FbtNodeChecker from './FbtNodeChecker.tsx'; import { checkOption, errorAt } from './FbtUtil.tsx'; +import { mapLeaves } from './JSFbtUtil.tsx'; +import replaceClearTokensWithTokenAliases from './replaceClearTokensWithTokenAliases.tsx'; import { FbtVariationType } from './translate/IntlVariations.tsx'; import type { FbtTableKey, PatternHash, PatternString } from './Types.d.ts'; @@ -71,6 +83,7 @@ export type PluginOptions = { filename?: string | null; // If true, generate the `outerTokenName` property on the JSFbt tree leaves. generateOuterTokenName?: boolean; + runtime?: boolean; }; /** * Token alias (aka mangled token name) @@ -316,6 +329,74 @@ export default function transform() { }); }, }, + + /** + * Transform the following: + * fbt._( + * SENTINEL + + * JSON.strinfigy({ + * type: "text", + * jsfbt: "jsfbt test" | { + * "t": {... jsfbt table} + * ... + * }, + * desc: "desc", + * project: "project", + * }) + + * SENTINEL + * ); + * to: + * fbt._("jsfbt test") or fbt._({... jsfbt table}) + */ + StringLiteral(path: NodePath) { + const sentinelLength = SENTINEL.length; + const phrase = path.node.value; + if ( + !phrase.startsWith(SENTINEL) || + !phrase.endsWith(SENTINEL) || + phrase.length <= sentinelLength * 2 + ) { + return; + } + + const parsedPhrase = JSON.parse( + phrase.slice(sentinelLength, phrase.length - sentinelLength), + ) as ObjectWithJSFBT; + + const runtimeInput = mapLeaves(parsedPhrase.jsfbt.t, (leaf) => + replaceClearTokensWithTokenAliases(leaf.text, leaf.tokenAliases), + ); + path.replaceWithSourceString(JSON.stringify(runtimeInput)); + + const parentNode = path.parentPath && path.parentPath.node; + invariant( + isCallExpression(parentNode), + `Expected parent node to be a 'CallExpression'`, + ); + + // Append runtime options - key for runtime dictionary lookup + if (parentNode.arguments.length === 1) { + // Second param 'args' could be omitted sometimes. Use null here. + parentNode.arguments.push(nullLiteral()); + } + + // Append hash key to the options argument + const optionsNode = parentNode.arguments[2]; + invariant( + optionsNode == null || isObjectExpression(optionsNode), + 'Expect options node to be either null or an object expression but got %s (%s)', + optionsNode, + typeof optionsNode, + ); + + parentNode.arguments[2] = objectExpression([ + ...(optionsNode == null ? [] : [...optionsNode.properties]), + objectProperty( + identifier('hk'), + stringLiteral(fbtHashKey(parsedPhrase.jsfbt.t)), + ), + ]); + }, }, }; } diff --git a/packages/babel-preset-fbtee/package.json b/packages/babel-preset-fbtee/package.json index 045098bf..a9c0aee2 100644 --- a/packages/babel-preset-fbtee/package.json +++ b/packages/babel-preset-fbtee/package.json @@ -28,7 +28,6 @@ }, "dependencies": { "@nkzw/babel-plugin-fbtee": "workspace:^", - "@nkzw/babel-plugin-fbtee-auto-import": "workspace:^", - "@nkzw/babel-plugin-fbtee-runtime": "workspace:^" + "@nkzw/babel-plugin-fbtee-auto-import": "workspace:^" } } diff --git a/packages/babel-preset-fbtee/src/index.tsx b/packages/babel-preset-fbtee/src/index.tsx index 52a450f5..7af19046 100644 --- a/packages/babel-preset-fbtee/src/index.tsx +++ b/packages/babel-preset-fbtee/src/index.tsx @@ -1,6 +1,5 @@ import fbt, { PluginOptions } from '@nkzw/babel-plugin-fbtee'; import autoImport from '@nkzw/babel-plugin-fbtee-auto-import'; -import fbtRuntime from '@nkzw/babel-plugin-fbtee-runtime'; export default function preset( _: unknown, @@ -10,7 +9,6 @@ export default function preset( plugins: [ ...(options?.disableAutoImport ? [] : [autoImport]), [fbt, options], - fbtRuntime, ], }; } diff --git a/packages/fbtee/package.json b/packages/fbtee/package.json index 180e4acb..068a9339 100644 --- a/packages/fbtee/package.json +++ b/packages/fbtee/package.json @@ -29,7 +29,6 @@ "devDependencies": {}, "peerDependencies": { "@nkzw/babel-plugin-fbtee": "workspace:^", - "@nkzw/babel-plugin-fbtee-runtime": "workspace:^", "react": "^19.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56e325d5..917699ea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -218,31 +218,6 @@ importers: specifier: workspace:^ version: link:../babel-plugin-fbtee - packages/babel-plugin-fbtee-runtime: - dependencies: - '@babel/core': - specifier: ^7.26.0 - version: 7.26.0 - '@babel/preset-react': - specifier: ^7.26.3 - version: 7.26.3(@babel/core@7.26.0) - '@babel/traverse': - specifier: ^7.26.4 - version: 7.26.4 - '@babel/types': - specifier: ^7.26.3 - version: 7.26.3 - '@nkzw/babel-plugin-fbtee': - specifier: workspace:^ - version: link:../babel-plugin-fbtee - invariant: - specifier: ^2.2.4 - version: 2.2.4 - devDependencies: - '@nkzw/babel-plugin-fbtee-auto-import': - specifier: workspace:^ - version: link:../babel-plugin-fbtee-auto-import - packages/babel-preset-fbtee: dependencies: '@nkzw/babel-plugin-fbtee': @@ -251,9 +226,6 @@ importers: '@nkzw/babel-plugin-fbtee-auto-import': specifier: workspace:^ version: link:../babel-plugin-fbtee-auto-import - '@nkzw/babel-plugin-fbtee-runtime': - specifier: workspace:^ - version: link:../babel-plugin-fbtee-runtime packages/eslint-plugin-fbtee: dependencies: @@ -270,9 +242,6 @@ importers: '@nkzw/babel-plugin-fbtee': specifier: workspace:^ version: link:../babel-plugin-fbtee - '@nkzw/babel-plugin-fbtee-runtime': - specifier: workspace:^ - version: link:../babel-plugin-fbtee-runtime invariant: specifier: ^2.2.4 version: 2.2.4 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 02e2ffbd..196bc981 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,7 +1,6 @@ packages: - 'example' - 'packages/babel-plugin-fbtee-auto-import' - - 'packages/babel-plugin-fbtee-runtime' - 'packages/babel-plugin-fbtee' - 'packages/babel-preset-fbtee' - 'packages/fbtee'