Skip to content

Commit 4b3867b

Browse files
committed
feat: solidjs integration
1 parent 5339152 commit 4b3867b

29 files changed

Lines changed: 2365 additions & 54 deletions

packages/babel-plugin-extract-messages/src/index.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,12 @@ export default function ({ types: t }: { types: BabelTypes }): PluginObj {
202202
function isTransComponent(path: NodePath) {
203203
return (
204204
path.isJSXElement() &&
205-
path
206-
.get("openingElement")
207-
.get("name")
208-
.referencesImport("@lingui/react", "Trans")
205+
["@lingui/react", "@lingui/solid"].some((moduleName) =>
206+
path
207+
.get("openingElement")
208+
.get("name")
209+
.referencesImport(moduleName, "Trans"),
210+
)
209211
)
210212
}
211213

packages/babel-plugin-lingui-macro/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { MacroJSX } from "./macroJsx"
55
import type { NodePath } from "@babel/traverse"
66
import { MacroJs } from "./macroJs"
77
import { JsMacroName } from "./constants"
8+
import { wrapJsxElementAsComponent } from "./messageDescriptorUtils"
89
import {
910
type LinguiConfigNormalized,
1011
getConfig as loadConfig,
@@ -242,6 +243,10 @@ export default function ({
242243
stripMessageProp: shouldStripMessageProp(
243244
state.opts as LinguiPluginOpts,
244245
),
246+
transformElement:
247+
config.macro.jsxRuntime === "solid"
248+
? wrapJsxElementAsComponent
249+
: undefined,
245250
isLinguiIdentifier: (node: Identifier, macro) =>
246251
isLinguiIdentifier(path, node, macro),
247252
},

packages/babel-plugin-lingui-macro/src/macroJsx.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export type MacroJsxOpts = {
5555
stripNonEssentialProps: boolean
5656
stripMessageProp: boolean
5757
transImportName: string
58+
transformElement?: (value: Expression) => Expression
5859
isLinguiIdentifier: (node: Identifier, macro: JsMacroName) => boolean
5960
}
6061

@@ -74,9 +75,11 @@ const choiceComponentAttributesWhitelist = [
7475
export class MacroJSX {
7576
types: typeof babelTypes
7677
ctx: MacroJsxContext
78+
transformElement?: (value: Expression) => Expression
7779

7880
constructor({ types }: { types: typeof babelTypes }, opts: MacroJsxOpts) {
7981
this.types = types
82+
this.transformElement = opts.transformElement
8083

8184
this.ctx = {
8285
...createMacroJsContext(
@@ -118,6 +121,9 @@ export class MacroJSX {
118121
context,
119122
comment,
120123
},
124+
{
125+
transformElement: this.transformElement,
126+
},
121127
)
122128

123129
attributes.push(this.types.jsxSpreadAttribute(messageDescriptor))

packages/babel-plugin-lingui-macro/src/messageDescriptorUtils.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ type TextWithLoc = {
1919
loc?: SourceLocation
2020
}
2121

22+
type MessageDescriptorElementTransforms = {
23+
transformElement?: (value: Expression) => Expression
24+
}
25+
2226
function isObjectProperty(
2327
node: TextWithLoc | ObjectProperty,
2428
): node is ObjectProperty {
@@ -35,9 +39,21 @@ export function createMessageDescriptorFromTokens(
3539
context?: TextWithLoc | ObjectProperty
3640
comment?: TextWithLoc | ObjectProperty
3741
} = {},
42+
transforms: MessageDescriptorElementTransforms = {},
3843
) {
44+
const result = buildICUFromTokens(tokens)
45+
46+
if (result.elements && transforms.transformElement) {
47+
result.elements = Object.fromEntries(
48+
Object.entries(result.elements).map(([key, value]) => [
49+
key,
50+
transforms.transformElement(value),
51+
]),
52+
)
53+
}
54+
3955
return createMessageDescriptor(
40-
buildICUFromTokens(tokens),
56+
result,
4157
oldLoc,
4258
stripNonEssentialProps,
4359
stripMessageProp,
@@ -150,6 +166,33 @@ function createValuesProperty(key: string, values: Record<string, Expression>) {
150166
)
151167
}
152168

169+
export function wrapJsxElementAsComponent(value: Expression): Expression {
170+
if (!types.isJSXElement(value)) {
171+
return value
172+
}
173+
174+
const props = types.identifier("props")
175+
176+
return types.arrowFunctionExpression(
177+
[props],
178+
types.jsxElement(
179+
types.jsxOpeningElement(
180+
types.cloneNode(value.openingElement.name),
181+
[
182+
...value.openingElement.attributes.map((attribute) =>
183+
types.cloneNode(attribute),
184+
),
185+
types.jsxSpreadAttribute(props),
186+
],
187+
true,
188+
),
189+
null,
190+
[],
191+
true,
192+
),
193+
)
194+
}
195+
153196
export function createStringObjectProperty(
154197
key: string,
155198
value: string,

packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,30 @@ import { Trans as _Trans } from "@lingui/react";
10031003
10041004
`;
10051005
1006+
exports[`should generate Solid components 1`] = `
1007+
import { Trans } from "@lingui/solid/macro";
1008+
<Trans>
1009+
Hello <a href="/docs">docs</a>.
1010+
</Trans>;
1011+
1012+
↓ ↓ ↓ ↓ ↓ ↓
1013+
1014+
import { Trans as _Trans } from "@lingui/solid";
1015+
<_Trans
1016+
{
1017+
/*i18n*/
1018+
...{
1019+
id: "lqG909",
1020+
message: "Hello <0>docs</0>.",
1021+
components: {
1022+
0: (props) => <a href="/docs" {...props} />,
1023+
},
1024+
}
1025+
}
1026+
/>;
1027+
1028+
`;
1029+
10061030
exports[`should respect runtimeConfigModule 1`] = `
10071031
import { Trans } from "@lingui/react/macro";
10081032
<Trans>Hello World</Trans>;

packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,27 @@ macroTester({
364364
<Trans>Hello World</Trans>;
365365
`,
366366
},
367+
{
368+
name: "should generate Solid components",
369+
macroOpts: {
370+
linguiConfig: makeConfig(
371+
{
372+
macro: {
373+
jsxPackage: ["@lingui/solid/macro"],
374+
jsxRuntime: "solid",
375+
},
376+
runtimeConfigModule: {
377+
Trans: ["@lingui/solid", "Trans"],
378+
},
379+
},
380+
{ skipValidation: true },
381+
),
382+
},
383+
code: `
384+
import { Trans } from '@lingui/solid/macro';
385+
<Trans>Hello <a href="/docs">docs</a>.</Trans>;
386+
`,
387+
},
367388
{
368389
name: "should detects macro imported from config.macro.jsxPackage",
369390
macroOpts: {

packages/conf/src/makeConfig.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ export const defaultConfig = {
8181

8282
export const exampleConfig = {
8383
...defaultConfig,
84+
macro: {
85+
...defaultConfig.macro,
86+
jsxRuntime: multipleValidOptions("react", "solid"),
87+
},
8488
format: multipleValidOptions({}, {}),
8589
extractors: multipleValidOptions([], ["babel"], [Object]),
8690
runtimeConfigModule: multipleValidOptions(

packages/conf/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,12 @@ export type LinguiConfig = {
364364
* @default ["@lingui/react/macro"]
365365
*/
366366
jsxPackage?: string[]
367+
/**
368+
* Controls which JSX runtime semantics the Lingui JSX macro emit.
369+
*
370+
* @default undefined
371+
*/
372+
jsxRuntime?: "react" | "solid"
367373
}
368374
experimental?: {
369375
extractor?: ExperimentalExtractorOptions

packages/solid/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[![License][badge-license]][license]
2+
[![Version][badge-version]][package]
3+
[![Downloads][badge-downloads]][package]
4+
5+
# @lingui/solid
6+
7+
> SolidJS components for internationalization
8+
9+
`@lingui/solid` is part of [LinguiJS][linguijs]. See the [documentation][documentation] for all information, tutorials and examples.
10+
11+
## Installation & Usage
12+
13+
See the [documentation][documentation].
14+
15+
## License
16+
17+
[MIT][license]
18+
19+
[license]: https://github.com/lingui/js-lingui/blob/main/LICENSE
20+
[linguijs]: https://github.com/lingui/js-lingui
21+
[documentation]: https://lingui.dev
22+
[package]: https://www.npmjs.com/package/@lingui/solid
23+
[badge-downloads]: https://img.shields.io/npm/dw/@lingui/solid.svg
24+
[badge-version]: https://img.shields.io/npm/v/@lingui/solid.svg
25+
[badge-license]: https://img.shields.io/npm/l/@lingui/solid.svg

packages/solid/build.config.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { babel } from "@rollup/plugin-babel"
2+
import { defineBuildConfig } from "unbuild"
3+
4+
export default defineBuildConfig({
5+
entries: ["src/index.ts", "src/config.ts"],
6+
declaration: "node16",
7+
rollup: {
8+
commonjs: false,
9+
esbuild: {
10+
jsx: "preserve",
11+
},
12+
},
13+
hooks: {
14+
"rollup:options": (_, options) => {
15+
options.plugins.push(
16+
babel({
17+
babelHelpers: "bundled",
18+
babelrc: false,
19+
configFile: false,
20+
extensions: [".js", ".jsx", ".ts", ".tsx"],
21+
include: ["src/**/*"],
22+
presets: [["babel-preset-solid", { generate: "dom" }]],
23+
}),
24+
)
25+
},
26+
},
27+
})

0 commit comments

Comments
 (0)