diff --git a/packages/babel-plugin-extract-messages/src/index.ts b/packages/babel-plugin-extract-messages/src/index.ts index 91d9f6a17..358529bc8 100644 --- a/packages/babel-plugin-extract-messages/src/index.ts +++ b/packages/babel-plugin-extract-messages/src/index.ts @@ -109,6 +109,28 @@ function extractStringContatentation(t, node, error): string { } } +function extractCommentString(t, path, valuePath, valueObj): string { + if (t.isStringLiteral(valueObj)) { + // Comment is a single line string + return valueObj.value; + } + + // Comment is a multi-line string. + const errorIfNotAString = path + .get(valuePath) + .buildCodeFrameError("Only strings are supported as comments.") + + if (t.isBinaryExpression(valueObj)) { + return extractStringContatentation( + t, + valueObj, + errorIfNotAString + ) + } else { + throw errorIfNotAString + } +} + export default function ({ types: t }) { let localTransComponentName @@ -220,25 +242,32 @@ export default function ({ types: t }) { ) } ) - if (!hasComment) return - + + const isNonMacroI18n = isI18nMethod(path.node.callee) && !hasComment && path.node.arguments[0] && !path.node.arguments[0].leadingComments; + if (!hasComment && !isNonMacroI18n) return; const props = { - id: path.node.arguments[0].value, - } + id: path.node.arguments[0].value + }; if (!props.id) { - console.warn("Missing message ID, skipping.") + console.warn("Missing message ID, skipping."); console.warn(generate(path.node).code) - return + return; } const copyOptions = ["message", "comment", "context"] if (t.isObjectExpression(path.node.arguments[2])) { - path.node.arguments[2].properties.forEach((property) => { - if (!copyOptions.includes(property.key.name)) return + path.node.arguments[2].properties.forEach(({key, value}, i) => { + if (!copyOptions.includes(key.name)) return + + let valueToExtract = value.value; + + if (key.name === "comment") { + valueToExtract = extractCommentString(t, path, `arguments.2.properties.${i}.value`, value); + } - props[property.key.name] = property.value.value + props[key.name] = valueToExtract }) } @@ -298,21 +327,8 @@ export default function ({ types: t }) { // By default, the value is just the string value of the object property. let valueToExtract = value.value; - if (key.name === "comment" && !t.isStringLiteral(value)) { - // Comments can be single or multi-line strings. - const errorIfNotAString = path - .get(`properties.${i}.value`) - .buildCodeFrameError("Only strings are supported as comments.") - - if (t.isBinaryExpression(value)) { - valueToExtract = extractStringContatentation( - t, - value, - errorIfNotAString - ) - } else { - throw errorIfNotAString - } + if (key.name === "comment") { + valueToExtract = extractCommentString(t, path, `properties.${i}.value`, value); } else if (key.name === "id") { const isIdLiteral = !value.value && t.isTemplateLiteral(value) if (isIdLiteral) { diff --git a/packages/babel-plugin-extract-messages/test/__snapshots__/index.ts.snap b/packages/babel-plugin-extract-messages/test/__snapshots__/index.ts.snap index bbb46d841..da852e90f 100644 --- a/packages/babel-plugin-extract-messages/test/__snapshots__/index.ts.snap +++ b/packages/babel-plugin-extract-messages/test/__snapshots__/index.ts.snap @@ -123,6 +123,72 @@ Object { } `; +exports[`@lingui/babel-plugin-extract-messages should extract all messages from JS files (without macros or i18n comments) 1`] = ` +Object { + Context1: Object { + Some id: Object { + extractedComments: Array [], + origin: Array [ + Array [ + js-without-macros-or-comments.js, + 11, + ], + ], + }, + }, + Description: Object { + extractedComments: Array [ + description, + ], + origin: Array [ + Array [ + js-without-macros-or-comments.js, + 3, + ], + ], + }, + ID: Object { + extractedComments: Array [], + message: Message with id, + origin: Array [ + Array [ + js-without-macros-or-comments.js, + 7, + ], + ], + }, + Message: Object { + extractedComments: Array [], + origin: Array [ + Array [ + js-without-macros-or-comments.js, + 1, + ], + ], + }, + Multiline: Object { + extractedComments: Array [ + this is 2 lines long, + ], + origin: Array [ + Array [ + js-without-macros-or-comments.js, + 5, + ], + ], + }, + Values {param}: Object { + extractedComments: Array [], + origin: Array [ + Array [ + js-without-macros-or-comments.js, + 9, + ], + ], + }, +} +`; + exports[`@lingui/babel-plugin-extract-messages should extract all messages from JS files 1`] = ` Object { Context1: Object { @@ -321,3 +387,30 @@ Object { }, } `; + +exports[`@lingui/babel-plugin-extract-messages should handle duplicate ids 1`] = ` +Object { + msg: Object { + extractedComments: Array [], + message: Hello World!, + origin: Array [ + Array [ + duplicate-id-valid.js, + 5, + ], + Array [ + duplicate-id-valid.js, + 8, + ], + Array [ + duplicate-id-valid.js, + 11, + ], + Array [ + duplicate-id-valid.js, + 14, + ], + ], + }, +} +`; diff --git a/packages/babel-plugin-extract-messages/test/fixtures/js-without-macros-or-comments.js b/packages/babel-plugin-extract-messages/test/fixtures/js-without-macros-or-comments.js new file mode 100644 index 000000000..e73602ccf --- /dev/null +++ b/packages/babel-plugin-extract-messages/test/fixtures/js-without-macros-or-comments.js @@ -0,0 +1,11 @@ +const msg = i18n._('Message') + +const withDescription = i18n._('Description', {}, { comment: "description"}); + +const withMultilineComment = i18n._('Multiline', {}, { comment: "this is " + "2 lines long" }); + +const withId = i18n._('ID', {}, { message: 'Message with id' }); + +const withValues = i18n._('Values {param}', { param: param }); + +const withContext = i18n._('Some id', {},{ context: 'Context1'}); diff --git a/packages/babel-plugin-extract-messages/test/index.ts b/packages/babel-plugin-extract-messages/test/index.ts index 3a7fc29be..f7db9aab8 100644 --- a/packages/babel-plugin-extract-messages/test/index.ts +++ b/packages/babel-plugin-extract-messages/test/index.ts @@ -113,6 +113,11 @@ describe("@lingui/babel-plugin-extract-messages", function () { ).toBeFalsy() }) + testCase( + "should handle duplicate ids", + "duplicate-id-valid.js" + ) + testCase( "should extract all messages from JSX files", "jsx-without-macros.js" @@ -134,4 +139,9 @@ describe("@lingui/babel-plugin-extract-messages", function () { "should extract all messages from JS files (macros)", "js-with-macros.js" ) + + testCase( + "should extract all messages from JS files (without macros or i18n comments)", + "js-without-macros-or-comments.js" + ) })