Skip to content

Commit

Permalink
refactor(macro): use message descriptor for Trans
Browse files Browse the repository at this point in the history
  • Loading branch information
timofei-iatsenko committed Apr 12, 2024
1 parent afc962c commit 94d9c78
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 103 deletions.
27 changes: 4 additions & 23 deletions packages/babel-plugin-lingui-macro/src/macroJs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
MACRO_LEGACY_PACKAGE,
} from "./constants"
import { generateMessageId } from "@lingui/message-utils/generateMessageId"
import { createMessageDescriptorFromTokens } from "./messageDescriptorUtils"

function buildICUFromTokens(tokens: Tokens) {
const messageFormat = new ICUMessageFormat()
Expand Down Expand Up @@ -76,7 +77,7 @@ export class MacroJs {
linguiInstance?: babelTypes.Expression
) => {
return this.createI18nCall(
this.createMessageDescriptorFromTokens(tokens, path.node.loc),
createMessageDescriptorFromTokens(tokens, path.node.loc),
linguiInstance
)
}
Expand All @@ -102,7 +103,7 @@ export class MacroJs {
this.isDefineMessage(path.get("tag"))
) {
const tokens = this.tokenizeTemplateLiteral(path.get("quasi"))
return this.createMessageDescriptorFromTokens(tokens, path.node.loc)
return createMessageDescriptorFromTokens(tokens, path.node.loc)
}

if (path.isTaggedTemplateExpression()) {
Expand Down Expand Up @@ -255,7 +256,7 @@ export class MacroJs {
if (currentPath.isTaggedTemplateExpression()) {
const tokens = this.tokenizeTemplateLiteral(currentPath)

const descriptor = this.createMessageDescriptorFromTokens(
const descriptor = createMessageDescriptorFromTokens(
tokens,
currentPath.node.loc
)
Expand Down Expand Up @@ -543,26 +544,6 @@ export class MacroJs {
)
}

createMessageDescriptorFromTokens(tokens: Tokens, oldLoc?: SourceLocation) {
const { message, values } = buildICUFromTokens(tokens)

const properties: ObjectProperty[] = [
this.createIdProperty(message),

!this.stripNonEssentialProps
? this.createObjectProperty(MESSAGE, this.types.stringLiteral(message))
: null,

this.createValuesProperty(values),
]

return this.createMessageDescriptor(
properties,
// preserve line numbers for extractor
oldLoc
)
}

createMessageDescriptor(
properties: ObjectProperty[],
oldLoc?: SourceLocation
Expand Down
97 changes: 17 additions & 80 deletions packages/babel-plugin-lingui-macro/src/macroJsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@ import {
} from "@babel/types"
import { NodePath } from "@babel/traverse"

import ICUMessageFormat, {
ArgToken,
ElementToken,
TextToken,
Token,
} from "./icu"
import { ArgToken, ElementToken, TextToken, Token } from "./icu"
import { makeCounter } from "./utils"
import {
COMMENT,
Expand All @@ -30,8 +25,11 @@ import {
MACRO_REACT_PACKAGE,
MACRO_LEGACY_PACKAGE,
} from "./constants"
import { generateMessageId } from "@lingui/message-utils/generateMessageId"
import cleanJSXElementLiteralChild from "./utils/cleanJSXElementLiteralChild"
import {
createMessageDescriptorFromTokens,
createStringObjectProperty,
} from "./messageDescriptorUtils"

const pluralRuleRe = /(_[\d\w]+|zero|one|two|few|many|other)/
const jsx2icuExactChoice = (value: string) =>
Expand Down Expand Up @@ -87,87 +85,26 @@ export class MacroJSX {
return false
}

const messageFormat = new ICUMessageFormat()
const { message, values, jsxElements } = messageFormat.fromTokens(tokens)
const { attributes, id, comment, context } = this.stripMacroAttributes(
path as NodePath<JSXElement>
)

if (!id && !message) {
throw new Error("Incorrect usage of Trans")
}

if (id) {
attributes.push(
this.types.jsxAttribute(
this.types.jsxIdentifier(ID),
this.types.stringLiteral(id)
)
)
} else {
attributes.push(
this.createStringJsxAttribute(ID, generateMessageId(message, context))
)
}

if (!this.stripNonEssentialProps) {
if (message) {
attributes.push(this.createStringJsxAttribute(MESSAGE, message))
}

if (comment) {
attributes.push(
this.types.jsxAttribute(
this.types.jsxIdentifier(COMMENT),
this.types.stringLiteral(comment)
)
)
}

if (context) {
attributes.push(
this.types.jsxAttribute(
this.types.jsxIdentifier(CONTEXT),
this.types.stringLiteral(context)
)
)
}
}

// Parameters for variable substitution
const valuesObject = Object.keys(values).map((key) =>
this.types.objectProperty(this.types.identifier(key), values[key])
const messageDescriptor = createMessageDescriptorFromTokens(
tokens,
path.node.loc,
{
id,
context,
comment,
},
this.stripNonEssentialProps
)

if (valuesObject.length) {
attributes.push(
this.types.jsxAttribute(
this.types.jsxIdentifier("values"),
this.types.jsxExpressionContainer(
this.types.objectExpression(valuesObject)
)
)
)
if (!id && !tokens) {
throw new Error("Incorrect usage of Trans")
}

// Inline elements
if (Object.keys(jsxElements).length) {
attributes.push(
this.types.jsxAttribute(
this.types.jsxIdentifier("components"),
this.types.jsxExpressionContainer(
this.types.objectExpression(
Object.keys(jsxElements).map((key) =>
this.types.objectProperty(
this.types.identifier(key),
jsxElements[key]
)
)
)
)
)
)
}
attributes.push(this.types.jsxSpreadAttribute(messageDescriptor))

const newNode = this.types.jsxElement(
this.types.jsxOpeningElement(
Expand Down
112 changes: 112 additions & 0 deletions packages/babel-plugin-lingui-macro/src/messageDescriptorUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import ICUMessageFormat, { Tokens, ParsedResult } from "./icu"
import { SourceLocation, ObjectProperty, ObjectExpression } from "@babel/types"
import { MESSAGE, ID, EXTRACT_MARK, COMMENT, CONTEXT } from "./constants"
import * as types from "@babel/types"
import { generateMessageId } from "@lingui/message-utils/generateMessageId"

function buildICUFromTokens(tokens: Tokens) {
const messageFormat = new ICUMessageFormat()
return messageFormat.fromTokens(tokens)
}

export function createMessageDescriptorFromTokens(
tokens: Tokens,
oldLoc?: SourceLocation,
defaults: { id?: string; context?: string; comment?: string } = {},
stripNonEssentialProps = true
) {
const { message, values, jsxElements } = buildICUFromTokens(tokens)

const properties: ObjectProperty[] = []

properties.push(
defaults.id
? createStringObjectProperty(ID, defaults.id)
: createIdProperty(message, defaults.context)
)

if (!stripNonEssentialProps) {
properties.push(createStringObjectProperty(MESSAGE, message))

if (defaults.comment) {
properties.push(createStringObjectProperty(COMMENT, defaults.comment))
}

if (defaults.context) {
properties.push(createStringObjectProperty(CONTEXT, defaults.context))
}
}

properties.push(createValuesProperty(values))
properties.push(createComponentsProperty(jsxElements))

return createMessageDescriptor(
properties,
// preserve line numbers for extractor
oldLoc
)
}

function createIdProperty(message: string, context?: string) {
return createStringObjectProperty(ID, generateMessageId(message, context))
}

function createValuesProperty(values: ParsedResult["values"]) {
const valuesObject = Object.keys(values).map((key) =>
types.objectProperty(types.identifier(key), values[key])
)

if (!valuesObject.length) return

return types.objectProperty(
types.identifier("values"),
types.objectExpression(valuesObject)
)
}

function createComponentsProperty(values: ParsedResult["jsxElements"]) {
const valuesObject = Object.keys(values).map((key) =>
types.objectProperty(types.identifier(key), values[key])
)

if (!valuesObject.length) return

return types.objectProperty(
types.identifier("components"),
types.objectExpression(valuesObject)
)
}

// if (Object.keys(jsxElements).length) {
// attributes.push(
// this.types.jsxAttribute(
// this.types.jsxIdentifier("components"),
// this.types.jsxExpressionContainer(
// this.types.objectExpression(
// Object.keys(jsxElements).map((key) =>
// this.types.objectProperty(
// this.types.identifier(key),
// jsxElements[key]
// )
// )
// )
// )
// )
// )
// }
export function createStringObjectProperty(key: string, value: string) {
return types.objectProperty(types.identifier(key), types.stringLiteral(value))
}

function createMessageDescriptor(
properties: ObjectProperty[],
oldLoc?: SourceLocation
): ObjectExpression {
const newDescriptor = types.objectExpression(properties.filter(Boolean))
types.addComment(newDescriptor, "leading", EXTRACT_MARK)
if (oldLoc) {
newDescriptor.loc = oldLoc
}

return newDescriptor
}

0 comments on commit 94d9c78

Please sign in to comment.