Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(macro): macro to plugin #1867

Merged
merged 16 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
}
}