402 lines
10 KiB
TypeScript
402 lines
10 KiB
TypeScript
import path from "path"
|
|
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-solid/babel-plugin-lingui-macro"
|
|
|
|
const transform = (filename: string) => {
|
|
const rootDir = path.join(__dirname, "fixtures")
|
|
|
|
const filePath = path.join(rootDir, filename)
|
|
const code = fs.readFileSync(filePath).toString()
|
|
|
|
return transformCode(code, filePath, rootDir)
|
|
}
|
|
|
|
const transformCode = (
|
|
code: string,
|
|
filename = "test-case.js",
|
|
rootDir = "."
|
|
) => {
|
|
process.env.LINGUI_CONFIG = path.join(
|
|
__dirname,
|
|
"fixtures",
|
|
"lingui.config.js"
|
|
)
|
|
const messages: ExtractedMessage[] = []
|
|
|
|
try {
|
|
const pluginOpts: ExtractPluginOpts = {
|
|
onMessageExtracted(msg: ExtractedMessage) {
|
|
const filename = path.relative(rootDir, msg.origin[0])
|
|
messages.push({
|
|
...msg,
|
|
origin: [filename, msg.origin[1]],
|
|
})
|
|
},
|
|
}
|
|
|
|
babelTransform(code, {
|
|
configFile: false,
|
|
filename,
|
|
plugins: [
|
|
"@babel/plugin-syntax-jsx",
|
|
[
|
|
linguiMacroPlugin,
|
|
{
|
|
extract: true,
|
|
} satisfies LinguiPluginOpts,
|
|
],
|
|
[plugin, pluginOpts],
|
|
],
|
|
})
|
|
} finally {
|
|
process.env.LINGUI_CONFIG = null
|
|
}
|
|
|
|
return messages
|
|
}
|
|
|
|
function expectNoConsole(cb: () => void) {
|
|
return mockConsole((console) => {
|
|
cb()
|
|
|
|
expect(console.warn).not.toBeCalled()
|
|
expect(console.error).not.toBeCalled()
|
|
})
|
|
}
|
|
|
|
describe("@lingui-solid/babel-plugin-extract-messages", function () {
|
|
it("should ignore files without lingui import", () => {
|
|
expectNoConsole(() => {
|
|
const messages = transform("without-lingui.js")
|
|
expect(messages.length).toBe(0)
|
|
})
|
|
})
|
|
|
|
it("should extract all messages from JSX files", () => {
|
|
expectNoConsole(() => {
|
|
const messages = transform("jsx-without-macros.js")
|
|
expect(messages).toMatchSnapshot()
|
|
})
|
|
})
|
|
|
|
describe("JSX", () => {
|
|
it("Should not rise warning when translation from variable", () => {
|
|
const code = `
|
|
import { Trans } from "@lingui/react";
|
|
|
|
<Trans id={message} />;
|
|
<Trans id={message.field} />;
|
|
`
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
expect(messages.length).toBe(0)
|
|
})
|
|
})
|
|
|
|
it("Should not rise warning when `key` used with macro", () => {
|
|
const code = `
|
|
import { Trans } from "@lingui/react/macro";
|
|
|
|
<Trans context="Context2" key={1}>
|
|
Some message
|
|
</Trans>
|
|
`
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
expect(messages.length).toBe(1)
|
|
})
|
|
})
|
|
|
|
it("Should log error when no ID provided", () => {
|
|
const code = `
|
|
import { Trans } from "@lingui/react";
|
|
|
|
<Trans />;
|
|
<Trans message="Missing ID" />;
|
|
`
|
|
mockConsole((console) => {
|
|
const messages = transformCode(code)
|
|
|
|
expect(messages.length).toBe(0)
|
|
expect(console.error).not.toBeCalled()
|
|
expect(console.warn).toBeCalledWith(
|
|
expect.stringContaining(`Missing message ID`)
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("CallExpression i18n._()", () => {
|
|
it("should extract messages from i18n._ call expressions", () => {
|
|
expectNoConsole(() => {
|
|
const messages = transform("js-call-expression.js")
|
|
expect(messages).toMatchSnapshot()
|
|
})
|
|
})
|
|
|
|
it("should extract from member access expressions", () => {
|
|
const code = `
|
|
// member access
|
|
ctx.i18n._("Message")
|
|
|
|
// member access any depth
|
|
ctx.req.i18n._("Message")
|
|
`
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
expect(messages.length).toBe(2)
|
|
})
|
|
})
|
|
|
|
it("should not extract from random i18n members", () => {
|
|
const code = `
|
|
i18n.load("Message")
|
|
`
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
expect(messages.length).toBe(0)
|
|
})
|
|
})
|
|
|
|
it("should not extract if disabled via annotation", () => {
|
|
const code = `
|
|
/* lingui-extract-ignore */
|
|
i18n._("Message")
|
|
|
|
/* lingui-extract-ignore */
|
|
ctx.i18n._("Message")
|
|
|
|
/* lingui-extract-ignore */
|
|
ctx.req.i18n._("Message")
|
|
`
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
expect(messages.length).toBe(0)
|
|
})
|
|
})
|
|
|
|
it("Should not rise warning when translation from variable", () => {
|
|
const code = `
|
|
i18n._(message);
|
|
// member expression
|
|
i18n._(foo.bar);
|
|
// function call
|
|
i18n._(getMessage());
|
|
`
|
|
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
expect(messages.length).toBe(0)
|
|
})
|
|
})
|
|
|
|
it("Should throw error when not a string provided as comment", () => {
|
|
const code = `const msg = i18n._('message.id', {}, {comment: variable})`
|
|
|
|
return mockConsole((console) => {
|
|
transformCode(code)
|
|
|
|
expect(console.error).not.toBeCalled()
|
|
expect(console.warn).toBeCalledWith(
|
|
expect.stringContaining(
|
|
"Only strings or template literals could be extracted."
|
|
)
|
|
)
|
|
})
|
|
})
|
|
|
|
it("Should support extract id from TplLiteral and Concatenation", () => {
|
|
const code = `
|
|
const msg = i18n._(\`message.id\`);
|
|
const msg2 = i18n._("second" + '.' + "id")
|
|
`
|
|
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
expect(messages[0]).toMatchObject({
|
|
id: "message.id",
|
|
})
|
|
expect(messages[1]).toMatchObject({
|
|
id: "second.id",
|
|
})
|
|
})
|
|
})
|
|
|
|
it("Should support string concatenation", () => {
|
|
const code = `const msg = i18n._('message.id', {}, {comment: "first " + "second " + "third"})`
|
|
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
expect(messages[0]).toMatchObject({
|
|
comment: "first second third",
|
|
})
|
|
})
|
|
})
|
|
|
|
it("Should not double extract from call generated by macro and dont rise warnings", () => {
|
|
const code = `import { i18n } from "@lingui/core";
|
|
const msg =
|
|
i18n._(/*i18n*/
|
|
{
|
|
id: "Hello {name}",
|
|
values: {
|
|
name: name,
|
|
},
|
|
});
|
|
`
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
expect(messages.length).toBe(1)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("StringLiteral", () => {
|
|
it("Should extract from marked StringLiteral", () => {
|
|
const code = `const t = /*i18n*/'Message'`
|
|
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
|
|
expect(messages[0]).toMatchObject({
|
|
id: "Message",
|
|
})
|
|
})
|
|
})
|
|
|
|
it("Should log error when empty StringLiteral marked for extraction", () => {
|
|
const code = `const t = /*i18n*/''`
|
|
|
|
return mockConsole((console) => {
|
|
const messages = transformCode(code)
|
|
|
|
expect(messages.length).toBe(0)
|
|
expect(console.error).not.toBeCalled()
|
|
expect(console.warn).toBeCalledWith(
|
|
expect.stringContaining(`Empty StringLiteral`)
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("MessageDescriptor", () => {
|
|
it("should extract messages from MessageDescriptors", () => {
|
|
expectNoConsole(() => {
|
|
const messages = transform("js-message-descriptor.js")
|
|
expect(messages).toMatchSnapshot()
|
|
})
|
|
})
|
|
|
|
it("Should extract id from TemplateLiteral", () => {
|
|
const code = "const msg = /*i18n*/{id: `Message`}"
|
|
|
|
expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
|
|
expect(messages[0]).toMatchObject({
|
|
id: "Message",
|
|
})
|
|
})
|
|
})
|
|
|
|
it("Should log error if TemplateLiteral in id has expressions", () => {
|
|
const code = "const msg = /*i18n*/{id: `Hello ${name}`}"
|
|
|
|
return mockConsole((console) => {
|
|
const messages = transformCode(code)
|
|
|
|
expect(messages.length).toBe(0)
|
|
|
|
expect(console.error).not.toBeCalled()
|
|
expect(console.warn).toBeCalledWith(
|
|
expect.stringContaining(
|
|
`Could not extract from template literal with expressions.`
|
|
)
|
|
)
|
|
})
|
|
})
|
|
|
|
it("Should log error when no ID provided", () => {
|
|
const code = "const msg = /*i18n*/ {message: `Hello ${name}`}"
|
|
|
|
return mockConsole((console) => {
|
|
const messages = transformCode(code)
|
|
|
|
expect(messages.length).toBe(0)
|
|
expect(console.error).not.toBeCalled()
|
|
|
|
expect(console.warn).toBeCalledWith(
|
|
expect.stringContaining(`Missing message ID`)
|
|
)
|
|
})
|
|
})
|
|
|
|
it("Should log error when not a string provided as ID", () => {
|
|
const code = "const msg = /*i18n*/ {id: id}"
|
|
|
|
return mockConsole((console) => {
|
|
transformCode(code)
|
|
|
|
expect(console.error).not.toBeCalled()
|
|
expect(console.warn).toBeCalledWith(
|
|
expect.stringContaining(
|
|
"Only strings or template literals could be extracted."
|
|
)
|
|
)
|
|
})
|
|
})
|
|
|
|
it("Should log error when not a string provided as comment", () => {
|
|
const code = `const msg = /*i18n*/ {id: "msg.id", comment: variable}`
|
|
|
|
return mockConsole((console) => {
|
|
transformCode(code)
|
|
|
|
expect(console.error).not.toBeCalled()
|
|
expect(console.warn).toBeCalledWith(
|
|
expect.stringContaining(
|
|
"Only strings or template literals could be extracted."
|
|
)
|
|
)
|
|
})
|
|
})
|
|
|
|
it("Should support string concatenation in comment", () => {
|
|
const code = `const msg = /*i18n*/ {id: "msg.id", comment: "first " + "second " + "third"}`
|
|
|
|
return expectNoConsole(() => {
|
|
const messages = transformCode(code)
|
|
expect(messages[0]).toMatchObject({
|
|
comment: "first second third",
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
it("should extract all messages from JSX files (macros)", () => {
|
|
return expectNoConsole(() => {
|
|
const messages = transform("jsx-with-macros.js")
|
|
expect(messages).toMatchSnapshot()
|
|
})
|
|
})
|
|
|
|
it("should extract Plural messages from JSX files when there's no Trans tag (integration)", () => {
|
|
return expectNoConsole(() => {
|
|
const messages = transform("jsx-without-trans.js")
|
|
expect(messages).toMatchSnapshot()
|
|
})
|
|
})
|
|
|
|
it("should extract all messages from JS files (macros)", () => {
|
|
return expectNoConsole(() => {
|
|
const messages = transform("js-with-macros.js")
|
|
expect(messages).toMatchSnapshot()
|
|
})
|
|
})
|
|
})
|