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
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/babel-plugin-extract-messages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@babel/traverse": "7.20.12",
"@babel/types": "^7.20.7",
"@lingui/jest-mocks": "*",
"@lingui/macro": "*",
"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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2023-03-15 10:00+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: en\n"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2023-03-15 10:00+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: pl\n"
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2023-03-15 10:00+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: en\n"

#: fixtures/pages/index.page.tsx:83
#: fixtures/pages/index.page.tsx:85
msgid "{0, plural, one {# Person} other {# Persons}}"
msgstr "{0, plural, one {# Person} other {# Persons}}"

#: fixtures/components/Developers.tsx:20
msgid "{selected, plural, one {Developer} other {Developers}}"
msgstr "{selected, plural, one {Developer} other {Developers}}"

#: fixtures/pages/index.page.tsx:62
msgid "<0>Next.js</0>say hi."
msgstr "<0>Next.js</0>say hi."

#: fixtures/components/Switcher.tsx:10
msgid "English"
msgstr "English"

#: fixtures/components/AboutText.tsx:6
msgid "Hello, world"
msgstr "Hello, world"

#. js-lingui-explicit-id
#: fixtures/components/AboutText.tsx:8
msgid "message.next-explanation"
msgstr "Next.js is an open-source React front-end development web framework that enables functionality such as server-side rendering and generating static websites for React based web applications. It is a production-ready framework that allows developers to quickly create static and dynamic JAMstack websites and is used widely by many large companies."

#: fixtures/pages/index.page.tsx:58
#: fixtures/pages/index.page.tsx:60
msgid "Plain text"
msgstr "Plain text"

#: fixtures/components/Developers.tsx:9
msgid "Plural Test: How many developers?"
msgstr "Plural Test: How many developers?"

#: fixtures/components/Switcher.tsx:11
msgid "Serbian"
msgstr "Serbian"

#: fixtures/components/Switcher.tsx:12
msgid "Spanish"
msgstr "Spanish"

#: fixtures/pages/index.page.tsx:46
msgid "Translation Demo"
msgstr "Translation Demo"

#: fixtures/pages/index.page.tsx:53
msgid "Welcome to <0>Next.js!</0>"
msgstr "Welcome to <0>Next.js!</0>"

#: fixtures/pages/index.page.tsx:67
msgid "Wonderful framework <0>Next.js</0>say hi."
msgstr "Wonderful framework <0>Next.js</0>say hi."

#: fixtures/pages/index.page.tsx:72
msgid "Wonderful framework <0>Next.js</0>say hi. And <1>Next.js</1>say hi."
msgstr "Wonderful framework <0>Next.js</0>say hi. And <1>Next.js</1>say hi."
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2023-03-15 10:00+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: pl\n"

#: fixtures/pages/index.page.tsx:83
#: fixtures/pages/index.page.tsx:85
msgid "{0, plural, one {# Person} other {# Persons}}"
msgstr ""

#: fixtures/components/Developers.tsx:20
msgid "{selected, plural, one {Developer} other {Developers}}"
msgstr ""

#: fixtures/pages/index.page.tsx:62
msgid "<0>Next.js</0>say hi."
msgstr ""

#: fixtures/components/Switcher.tsx:10
msgid "English"
msgstr ""

#: fixtures/components/AboutText.tsx:6
msgid "Hello, world"
msgstr ""

#. js-lingui-explicit-id
#: fixtures/components/AboutText.tsx:8
msgid "message.next-explanation"
msgstr ""

#: fixtures/pages/index.page.tsx:58
#: fixtures/pages/index.page.tsx:60
msgid "Plain text"
msgstr ""

#: fixtures/components/Developers.tsx:9
msgid "Plural Test: How many developers?"
msgstr ""

#: fixtures/components/Switcher.tsx:11
msgid "Serbian"
msgstr ""

#: fixtures/components/Switcher.tsx:12
msgid "Spanish"
msgstr ""

#: fixtures/pages/index.page.tsx:46
msgid "Translation Demo"
msgstr ""

#: fixtures/pages/index.page.tsx:53
msgid "Welcome to <0>Next.js!</0>"
msgstr ""

#: fixtures/pages/index.page.tsx:67
msgid "Wonderful framework <0>Next.js</0>say hi."
msgstr ""

#: fixtures/pages/index.page.tsx:72
msgid "Wonderful framework <0>Next.js</0>say hi. And <1>Next.js</1>say hi."
msgstr ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Trans } from '@lingui/macro'

export function AboutText() {
return (
<p>
<Trans>Hello, world</Trans>
<br />
<Trans id="message.next-explanation">
Next.js is an open-source React front-end development web framework that
enables functionality such as server-side rendering and generating
static websites for React based web applications. It is a
production-ready framework that allows developers to quickly create
static and dynamic JAMstack websites and is used widely by many large
companies.
</Trans>
</p>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState } from 'react'
import { Trans, Plural } from '@lingui/macro'

export default function Developers() {
const [selected, setSelected] = useState('1')
return (
<div>
<p>
<Trans>Plural Test: How many developers?</Trans>
</p>
<div style={{ display: 'flex', justifyContent: 'space-evenly' }}>
<select
value={selected}
onChange={(evt) => setSelected(evt.target.value)}
>
<option value={'1'}>1</option>
<option value={'2'}>2</option>
</select>
<p>
<Plural value={selected} one={'Developer'} other={`Developers`} />
</p>
</div>
</div>
)
}
Loading
Loading