Skip to content

Commit

Permalink
refactor(macro): macro to plugin (#1867)
Browse files Browse the repository at this point in the history
  • Loading branch information
thekip authored and andrii-bodnar committed Mar 18, 2024
1 parent 8b1d0a3 commit 01405f8
Show file tree
Hide file tree
Showing 35 changed files with 1,247 additions and 974 deletions.
3 changes: 2 additions & 1 deletion packages/babel-plugin-extract-messages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"@babel/core": "^7.21.0",
"@babel/traverse": "7.20.12",
"@babel/types": "^7.20.7",
"@lingui/jest-mocks": "*",
"@lingui/jest-mocks": "workspace:*",
"@lingui/macro": "workspace:*",
"unbuild": "2.0.0"
}
}
42 changes: 8 additions & 34 deletions packages/babel-plugin-extract-messages/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import type * as BabelTypesNamespace from "@babel/types"
import {
Expression,
Identifier,
ImportSpecifier,
JSXAttribute,
Node,
ObjectExpression,
ObjectProperty,
} from "@babel/types"
import type { PluginObj, PluginPass } from "@babel/core"
import type { NodePath } from "@babel/core"
import type { PluginObj, PluginPass, NodePath } from "@babel/core"
import type { Hub } from "@babel/traverse"

type BabelTypes = typeof BabelTypesNamespace
Expand Down Expand Up @@ -148,14 +146,13 @@ function hasI18nComment(node: Node): boolean {
}

export default function ({ types: t }: { types: BabelTypes }): PluginObj {
let localTransComponentName: string

function isTransComponent(node: Node) {
function isTransComponent(path: NodePath) {
return (
t.isJSXElement(node) &&
t.isJSXIdentifier(node.openingElement.name, {
name: localTransComponentName,
})
path.isJSXElement() &&
path
.get("openingElement")
.get("name")
.referencesImport("@lingui/react", "Trans")
)
}

Expand Down Expand Up @@ -190,33 +187,10 @@ export default function ({ types: t }: { types: BabelTypes }): PluginObj {

return {
visitor: {
// Get the local name of Trans component. Usually it's just `Trans`, but
// it might be different when the import is aliased:
// import { Trans as T } from '@lingui/react';
ImportDeclaration(path) {
const { node } = path

const moduleName = node.source.value
if (!["@lingui/react", "@lingui/core"].includes(moduleName)) return

const importDeclarations: Record<string, string> = {}
if (moduleName === "@lingui/react") {
node.specifiers.forEach((specifier) => {
specifier = specifier as ImportSpecifier
importDeclarations[(specifier.imported as Identifier).name] =
specifier.local.name
})

// Trans import might be missing if there's just Plural or similar macro.
// If there's no alias, consider it was imported as Trans.
localTransComponentName = importDeclarations["Trans"] || "Trans"
}
},

// Extract translation from <Trans /> component.
JSXElement(path, ctx) {
const { node } = path
if (!localTransComponentName || !isTransComponent(node)) return
if (!isTransComponent(path)) return

const attrs = (node.openingElement.attributes as JSXAttribute[]) || []

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { t, defineMessage, msg, useLingui } from "@lingui/macro"
import { t, defineMessage, msg, useLingui, plural } from "@lingui/macro"

t`Message`

Expand Down
12 changes: 4 additions & 8 deletions packages/babel-plugin-extract-messages/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fs from "fs"
import { transform as babelTransform } from "@babel/core"
import plugin, { ExtractedMessage, ExtractPluginOpts } from "../src/index"
import { mockConsole } from "@lingui/jest-mocks"
import linguiMacroPlugin, { type LinguiPluginOpts } from "@lingui/macro/plugin"

const transform = (filename: string) => {
const rootDir = path.join(__dirname, "fixtures")
Expand Down Expand Up @@ -42,15 +43,10 @@ const transformCode = (
plugins: [
"@babel/plugin-syntax-jsx",
[
"macros",
linguiMacroPlugin,
{
lingui: { extract: true },
// macro plugin uses package `resolve` to find a path of macro file
// this will not follow jest pathMapping and will resolve path from ./build
// instead of ./src which makes testing & developing hard.
// here we override resolve and provide correct path for testing
resolvePath: (source: string) => require.resolve(source),
},
extract: true,
} satisfies LinguiPluginOpts,
],
[plugin, pluginOpts],
],
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@lingui/conf": "4.8.0-next.0",
"@lingui/core": "4.8.0-next.0",
"@lingui/format-po": "4.8.0-next.0",
"@lingui/macro": "4.8.0-next.0",
"@lingui/message-utils": "4.8.0-next.0",
"babel-plugin-macros": "^3.0.1",
"chalk": "^4.1.0",
Expand All @@ -80,7 +81,6 @@
},
"devDependencies": {
"@lingui/jest-mocks": "*",
"@lingui/macro": "4.8.0-next.0",
"@types/convert-source-map": "^2.0.0",
"@types/glob": "^8.1.0",
"@types/micromatch": "^4.0.1",
Expand Down
18 changes: 6 additions & 12 deletions packages/cli/src/api/extractors/babel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import linguiExtractMessages from "@lingui/babel-plugin-extract-messages"
import type { ExtractorType } from "@lingui/conf"
import { ParserPlugin } from "@babel/parser"

import { LinguiMacroOpts } from "@lingui/macro/node"
import { type LinguiPluginOpts } from "@lingui/macro/plugin"
import linguiMacroPlugin from "@lingui/macro/plugin"
import { ExtractedMessage, ExtractorCtx } from "@lingui/conf"

const babelRe = new RegExp(
Expand Down Expand Up @@ -120,18 +121,11 @@ export async function extractFromFileWithBabel(

plugins: [
[
"macros",
linguiMacroPlugin,
{
// macro plugin uses package `resolve` to find a path of macro file
// this will not follow jest pathMapping and will resolve path from ./build
// instead of ./src which makes testing & developing hard.
// here we override resolve and provide correct path for testing
resolvePath: (source: string) => require.resolve(source),
lingui: {
extract: true,
linguiConfig: ctx.linguiConfig,
} satisfies LinguiMacroOpts,
},
extract: true,
linguiConfig: ctx.linguiConfig,
} satisfies LinguiPluginOpts,
],
[
linguiExtractMessages,
Expand Down
18 changes: 12 additions & 6 deletions packages/conf/src/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,18 @@ exports[`@lingui/conf should return default config 1`] = `
pseudoLocale: ,
rootDir: .,
runtimeConfigModule: {
TransImportModule: @lingui/react,
TransImportName: Trans,
i18nImportModule: @lingui/core,
i18nImportName: i18n,
useLinguiImportModule: @lingui/react,
useLinguiImportName: useLingui,
Trans: [
@lingui/react,
Trans,
],
i18n: [
@lingui/core,
i18n,
],
useLingui: [
@lingui/react,
useLingui,
],
},
service: {
apiKey: ,
Expand Down
72 changes: 48 additions & 24 deletions packages/conf/src/migrations/normalizeRuntimeConfigModule.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ describe("normalizeRuntimeConfigModule", () => {

expect(actual.runtimeConfigModule).toMatchInlineSnapshot(`
{
TransImportModule: @lingui/react,
TransImportName: Trans,
i18nImportModule: @lingui/core,
i18nImportName: i18n,
useLinguiImportModule: @lingui/react,
useLinguiImportName: useLingui,
Trans: [
@lingui/react,
Trans,
],
i18n: [
@lingui/core,
i18n,
],
useLingui: [
@lingui/react,
useLingui,
],
}
`)
})
Expand All @@ -23,12 +29,18 @@ describe("normalizeRuntimeConfigModule", () => {

expect(actual.runtimeConfigModule).toMatchInlineSnapshot(`
{
TransImportModule: @lingui/react,
TransImportName: Trans,
i18nImportModule: ../my-i18n,
i18nImportName: myI18n,
useLinguiImportModule: @lingui/react,
useLinguiImportName: useLingui,
Trans: [
@lingui/react,
Trans,
],
i18n: [
../my-i18n,
myI18n,
],
useLingui: [
@lingui/react,
useLingui,
],
}
`)
})
Expand All @@ -44,12 +56,18 @@ describe("normalizeRuntimeConfigModule", () => {

expect(actual.runtimeConfigModule).toMatchInlineSnapshot(`
{
TransImportModule: ./custom-trans,
TransImportName: myTrans,
i18nImportModule: ./custom-i18n,
i18nImportName: myI18n,
useLinguiImportModule: ./custom-use-lingui,
useLinguiImportName: myLingui,
Trans: [
./custom-trans,
myTrans,
],
i18n: [
./custom-i18n,
myI18n,
],
useLingui: [
./custom-use-lingui,
myLingui,
],
}
`)
})
Expand All @@ -65,12 +83,18 @@ describe("normalizeRuntimeConfigModule", () => {

expect(actual.runtimeConfigModule).toMatchInlineSnapshot(`
{
TransImportModule: ./custom-trans,
TransImportName: Trans,
i18nImportModule: ./custom-i18n,
i18nImportName: i18n,
useLinguiImportModule: ./custom-use-lingui,
useLinguiImportName: useLingui,
Trans: [
./custom-trans,
Trans,
],
i18n: [
./custom-i18n,
i18n,
],
useLingui: [
./custom-use-lingui,
useLingui,
],
}
`)
})
Expand Down
33 changes: 13 additions & 20 deletions packages/conf/src/migrations/normalizeRuntimeConfigModule.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { LinguiConfig, LinguiConfigNormalized } from "../types"

type ModuleSrc = [source: string, identifier?: string]
type ModuleSrc = [source: string, identifier: string]

const getSymbolSource = (
defaults: ModuleSrc,
Expand All @@ -26,28 +26,21 @@ const getSymbolSource = (
export function normalizeRuntimeConfigModule(
config: Pick<LinguiConfig, "runtimeConfigModule">
) {
const [i18nImportModule, i18nImportName] = getSymbolSource(
["@lingui/core", "i18n"],
config.runtimeConfigModule
)
const [TransImportModule, TransImportName] = getSymbolSource(
["@lingui/react", "Trans"],
config.runtimeConfigModule
)
const [useLinguiImportModule, useLinguiImportName] = getSymbolSource(
["@lingui/react", "useLingui"],
config.runtimeConfigModule
)

return {
...config,
runtimeConfigModule: {
i18nImportModule,
i18nImportName,
TransImportModule,
TransImportName,
useLinguiImportModule,
useLinguiImportName,
i18n: getSymbolSource(
["@lingui/core", "i18n"],
config.runtimeConfigModule
),
useLingui: getSymbolSource(
["@lingui/react", "useLingui"],
config.runtimeConfigModule
),
Trans: getSymbolSource(
["@lingui/react", "Trans"],
config.runtimeConfigModule
),
} satisfies LinguiConfigNormalized["runtimeConfigModule"],
}
}
11 changes: 5 additions & 6 deletions packages/conf/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,17 +217,16 @@ export type LinguiConfig = {
}
}

type ModuleSourceNormalized = readonly [module: string, specifier: string]

export type LinguiConfigNormalized = Omit<
LinguiConfig,
"runtimeConfigModule"
> & {
fallbackLocales?: FallbackLocales
runtimeConfigModule: {
i18nImportModule: string
i18nImportName: string
TransImportModule: string
TransImportName: string
useLinguiImportModule: string
useLinguiImportName: string
i18n: ModuleSourceNormalized
useLingui: ModuleSourceNormalized
Trans: ModuleSourceNormalized
}
}

0 comments on commit 01405f8

Please sign in to comment.