diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/js-defineMessage.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-defineMessage.test.ts.snap new file mode 100644 index 000000000..7e2d1c419 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-defineMessage.test.ts.snap @@ -0,0 +1,203 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Production - only essential props are kept 1`] = ` +import { defineMessage } from "@lingui/core/macro"; +const msg = defineMessage({ + message: \`Hello \${name}\`, + id: "msgId", + comment: "description for translators", + context: "My Context", +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +const msg = + /*i18n*/ + { + id: "msgId", + values: { + name: name, + }, + }; + +`; + +exports[`Production - only essential props are kept, without id 1`] = ` +import { defineMessage } from "@lingui/core/macro"; +const msg = defineMessage({ + message: \`Hello \${name}\`, + comment: "description for translators", + context: "My Context", +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +const msg = + /*i18n*/ + { + values: { + name: name, + }, + id: "oT92lS", + }; + +`; + +exports[`defineMessage can be called by alias \`msg\` 1`] = ` +import { msg } from "@lingui/core/macro"; +const message1 = msg\`Message\`; +const message2 = msg({ message: "Message" }); + +↓ ↓ ↓ ↓ ↓ ↓ + +const message1 = + /*i18n*/ + { + id: "xDAtGP", + message: "Message", + }; +const message2 = + /*i18n*/ + { + message: "Message", + id: "xDAtGP", + }; + +`; + +exports[`defineMessage macro could be renamed 1`] = ` +import { + defineMessage as defineMessage2, + plural as plural2, +} from "@lingui/core/macro"; +const message = defineMessage2({ + comment: "Description", + message: plural2(value, { one: "book", other: "books" }), +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +const message = + /*i18n*/ + { + values: { + value: value, + }, + message: "{value, plural, one {book} other {books}}", + id: "SlmyxX", + comment: "Description", + }; + +`; + +exports[`defineMessage should support template literal 1`] = ` +import { defineMessage } from "@lingui/core/macro"; +const message = defineMessage\`Message\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +const message = + /*i18n*/ + { + id: "xDAtGP", + message: "Message", + }; + +`; + +exports[`should expand macros in message property 1`] = ` +import { defineMessage, plural, arg } from "@lingui/core/macro"; +const message = defineMessage({ + comment: "Description", + message: plural(value, { one: "book", other: "books" }), +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +const message = + /*i18n*/ + { + values: { + value: value, + }, + message: "{value, plural, one {book} other {books}}", + id: "SlmyxX", + comment: "Description", + }; + +`; + +exports[`should left string message intact 1`] = ` +import { defineMessage } from "@lingui/core/macro"; +const message = defineMessage({ + message: "Message", +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +const message = + /*i18n*/ + { + message: "Message", + id: "xDAtGP", + }; + +`; + +exports[`should preserve custom id 1`] = ` +import { defineMessage } from "@lingui/core/macro"; +const message = defineMessage({ + id: "msg.id", + message: "Message", +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +const message = + /*i18n*/ + { + id: "msg.id", + message: "Message", + }; + +`; + +exports[`should preserve values 1`] = ` +import { defineMessage, t } from "@lingui/core/macro"; +const message = defineMessage({ + message: t\`Hello \${name}\`, +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +const message = + /*i18n*/ + { + values: { + name: name, + }, + message: "Hello {name}", + id: "OVaF9k", + }; + +`; + +exports[`should transform template literals 1`] = ` +import { defineMessage } from "@lingui/core/macro"; +const message = defineMessage({ + message: \`Message \${name}\`, +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +const message = + /*i18n*/ + { + values: { + name: name, + }, + message: "Message {name}", + id: "A2aVLF", + }; + +`; diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/js-plural.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-plural.test.ts.snap new file mode 100644 index 000000000..925fb504a --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-plural.test.ts.snap @@ -0,0 +1,99 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Macro is used in expression assignment 1`] = ` +import { plural } from "@lingui/core/macro"; +const a = plural(count, { + one: \`# book\`, + other: "# books", +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const a = _i18n._( + /*i18n*/ + { + id: "esnaQO", + message: "{count, plural, one {# book} other {# books}}", + values: { + count: count, + }, + } +); + +`; + +exports[`Macro with expression only choice 1`] = ` +import { plural } from "@lingui/core/macro"; +plural(users.length, { + offset: 1, + 0: "No books", + 1: "1 book", + other: someOtherExp, +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "0mcXIe", + message: + "{0, plural, offset:1 =0 {No books} =1 {1 book} other {{someOtherExp}}}", + values: { + 0: users.length, + someOtherExp: someOtherExp, + }, + } +); + +`; + +exports[`Macro with offset and exact matches 1`] = ` +import { plural } from "@lingui/core/macro"; +plural(users.length, { + offset: 1, + 0: "No books", + 1: "1 book", + other: "# books", +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "CF5t+7", + message: "{0, plural, offset:1 =0 {No books} =1 {1 book} other {# books}}", + values: { + 0: users.length, + }, + } +); + +`; + +exports[`plural macro could be renamed 1`] = ` +import { plural as plural2 } from "@lingui/core/macro"; +const a = plural2(count, { + one: \`# book\`, + other: "# books", +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const a = _i18n._( + /*i18n*/ + { + id: "esnaQO", + message: "{count, plural, one {# book} other {# books}}", + values: { + count: count, + }, + } +); + +`; diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/js-select.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-select.test.ts.snap new file mode 100644 index 000000000..7130a5755 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-select.test.ts.snap @@ -0,0 +1,60 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Nested macros 1`] = ` +import { select, plural } from "@lingui/core/macro"; +select(gender, { + male: plural(numOfGuests, { + one: "He invites one guest", + other: "He invites # guests", + }), + female: \`She is \${gender}\`, + other: \`They is \${gender}\`, +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "G8xqGf", + message: + "{gender, select, male {{numOfGuests, plural, one {He invites one guest} other {He invites # guests}}} female {She is {gender}} other {They is {gender}}}", + values: { + gender: gender, + numOfGuests: numOfGuests, + }, + } +); + +`; + +exports[`Nested macros with pure expressions option 1`] = ` +import { select, plural } from "@lingui/core/macro"; +select(gender, { + male: plural(numOfGuests, { + one: "He invites one guest", + other: "He invites # guests", + }), + female: \`She is \${gender}\`, + other: someOtherExp, +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "j9PNNm", + message: + "{gender, select, male {{numOfGuests, plural, one {He invites one guest} other {He invites # guests}}} female {She is {gender}} other {{someOtherExp}}}", + values: { + gender: gender, + numOfGuests: numOfGuests, + someOtherExp: someOtherExp, + }, + } +); + +`; diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/js-selectOrdinal.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-selectOrdinal.test.ts.snap new file mode 100644 index 000000000..63ca7915d --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-selectOrdinal.test.ts.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#1 1`] = ` +import { t, selectOrdinal } from "@lingui/core/macro"; +t\`This is my \${selectOrdinal(count, { + one: "#st", + two: \`#nd\`, + other: "#rd", +})} cat\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "dJXd3T", + message: + "This is my {count, selectordinal, one {#st} two {#nd} other {#rd}} cat", + values: { + count: count, + }, + } +); + +`; diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/js-t.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-t.test.ts.snap new file mode 100644 index 000000000..84b1a27b4 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-t.test.ts.snap @@ -0,0 +1,566 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Anything variables except simple identifiers are used as positional arguments 1`] = ` +import { t } from "@lingui/core/macro"; +t\` Property \${props.name}, function \${random()}, array \${ + array[index] +}, constant \${42}, object \${new Date()} anything \${props.messages[ + index +].value()}\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "vVZNZ5", + message: + " Property {0}, function {1}, array {2}, constant {3}, object {4} anything {5}", + values: { + 0: props.name, + 1: random(), + 2: array[index], + 3: 42, + 4: new Date(), + 5: props.messages[index].value(), + }, + } +); + +`; + +exports[`Context might be passed as template literal 1`] = ` +import { t } from "@lingui/core/macro"; +t({ message: "Hello", context: "my custom" }); +t({ message: "Hello", context: \`my custom\` }); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + context: "my custom", + message: "Hello", + id: "BYqAaU", + } +); +_i18n._( + /*i18n*/ + { + context: \`my custom\`, + message: "Hello", + id: "BYqAaU", + } +); + +`; + +exports[`Macro is used in call expression 1`] = ` +import { t } from "@lingui/core/macro"; +const msg = message.error(t({ message: "dasd" })); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const msg = message.error( + _i18n._( + /*i18n*/ + { + message: "dasd", + id: "9ZMZjU", + } + ) +); + +`; + +exports[`Macro is used in expression assignment 1`] = ` +import { t } from "@lingui/core/macro"; +const a = t\`Expression assignment\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const a = _i18n._( + /*i18n*/ + { + id: "mjnlP0", + message: "Expression assignment", + } +); + +`; + +exports[`Macro is used in expression assignment, with custom lingui instance 1`] = ` +import { t } from "@lingui/core/macro"; +import { customI18n } from "./lingui"; +const a = t(customI18n)\`Expression assignment\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { customI18n } from "./lingui"; +const a = customI18n._( + /*i18n*/ + { + id: "mjnlP0", + message: "Expression assignment", + } +); + +`; + +exports[`Newlines are preserved 1`] = ` +import { t } from "@lingui/core/macro"; +t\`Multiline + string\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "+8iwDA", + message: "Multiline\\n string", + } +); + +`; + +exports[`Production - all props kept if extract: true 1`] = ` +import { t } from "@lingui/core/macro"; +const msg = t({ + message: \`Hello \${name}\`, + id: "msgId", + comment: "description for translators", + context: "My Context", +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const msg = _i18n._( + /*i18n*/ + { + id: "msgId", + context: "My Context", + values: { + name: name, + }, + message: "Hello {name}", + comment: "description for translators", + } +); + +`; + +exports[`Production - only essential props are kept 1`] = ` +import { t } from "@lingui/core/macro"; +const msg = t\`Message\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const msg = _i18n._( + /*i18n*/ + { + id: "xDAtGP", + } +); + +`; + +exports[`Production - only essential props are kept 2`] = ` +import { t } from "@lingui/core/macro"; +const msg = t({ + message: \`Hello \${name}\`, + id: "msgId", + comment: "description for translators", + context: "My Context", +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const msg = _i18n._( + /*i18n*/ + { + id: "msgId", + values: { + name: name, + }, + } +); + +`; + +exports[`Production - only essential props are kept, with custom i18n instance 1`] = ` +import { t } from "@lingui/core/macro"; +import { i18n } from "./lingui"; +const msg = t(i18n)({ + message: \`Hello \${name}\`, + id: "msgId", + comment: "description for translators", + context: "My Context", +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n } from "./lingui"; +const msg = i18n._( + /*i18n*/ + { + id: "msgId", + values: { + name: name, + }, + } +); + +`; + +exports[`Production - only essential props are kept, with plural, with custom i18n instance 1`] = ` +import { t, plural } from "@lingui/core/macro"; +const msg = t({ + id: "msgId", + comment: "description for translators", + context: "some context", + message: plural(val, { one: "...", other: "..." }), +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const msg = _i18n._( + /*i18n*/ + { + id: "msgId", + values: { + val: val, + }, + } +); + +`; + +exports[`Should generate different id when context provided 1`] = ` +import { t } from "@lingui/core/macro"; +t({ message: "Hello" }); +t({ message: "Hello", context: "my custom" }); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + message: "Hello", + id: "uzTaYi", + } +); +_i18n._( + /*i18n*/ + { + context: "my custom", + message: "Hello", + id: "BYqAaU", + } +); + +`; + +exports[`Support id and comment in t macro as callExpression 1`] = ` +import { t, plural } from "@lingui/core/macro"; +const msg = t({ + id: "msgId", + comment: "description for translators", + message: plural(val, { one: "...", other: "..." }), +}); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const msg = _i18n._( + /*i18n*/ + { + id: "msgId", + values: { + val: val, + }, + message: "{val, plural, one {...} other {...}}", + comment: "description for translators", + } +); + +`; + +exports[`Support id in template literal 1`] = ` +import { t } from "@lingui/core/macro"; +const msg = t({ id: \`msgId\` }); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const msg = _i18n._( + /*i18n*/ + { + id: \`msgId\`, + } +); + +`; + +exports[`Support id with message interpolation 1`] = ` +import { t } from "@lingui/core/macro"; +const msg = t({ id: "msgId", message: \`Some \${value}\` }); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const msg = _i18n._( + /*i18n*/ + { + id: "msgId", + values: { + value: value, + }, + message: "Some {value}", + } +); + +`; + +exports[`Support t in t 1`] = ` +import { t } from "@lingui/core/macro"; +t\`Field \${t\`First Name\`} is required\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "O8dJMg", + message: "Field {0} is required", + values: { + 0: _i18n._( + /*i18n*/ + { + id: "kODvZJ", + message: "First Name", + } + ), + }, + } +); + +`; + +exports[`Support template strings in t macro message 1`] = ` +import { t } from "@lingui/core/macro"; +const msg = t({ message: \`Hello \${name}\` }); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const msg = _i18n._( + /*i18n*/ + { + values: { + name: name, + }, + message: "Hello {name}", + id: "OVaF9k", + } +); + +`; + +exports[`Support template strings in t macro message, with custom i18n instance 1`] = ` +import { t } from "@lingui/core/macro"; +import { i18n } from "./lingui"; +const msg = t(i18n)({ message: \`Hello \${name}\` }); + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n } from "./lingui"; +const msg = i18n._( + /*i18n*/ + { + values: { + name: name, + }, + message: "Hello {name}", + id: "OVaF9k", + } +); + +`; + +exports[`Support template strings in t macro message, with custom i18n instance object property 1`] = ` +import { t } from "@lingui/core/macro"; +const msg = t(global.i18n)({ message: \`Hello \${name}\` }); + +↓ ↓ ↓ ↓ ↓ ↓ + +const msg = global.i18n._( + /*i18n*/ + { + values: { + name: name, + }, + message: "Hello {name}", + id: "OVaF9k", + } +); + +`; + +exports[`Variables are replaced with named arguments 1`] = ` +import { t } from "@lingui/core/macro"; +t\`Variable \${name}\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "xRRkAE", + message: "Variable {name}", + values: { + name: name, + }, + } +); + +`; + +exports[`Variables should be deduplicated 1`] = ` +import { t } from "@lingui/core/macro"; +t\`\${duplicate} variable \${duplicate}\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "+nhkwg", + message: "{duplicate} variable {duplicate}", + values: { + duplicate: duplicate, + }, + } +); + +`; + +exports[`Variables with escaped double quotes are correctly formatted 1`] = ` +import { t } from "@lingui/core/macro"; +t\`Variable "name"\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "CcPIZW", + message: 'Variable "name"', + } +); + +`; + +exports[`Variables with escaped template literals are correctly formatted 1`] = ` +import { t } from "@lingui/core/macro"; +t\`Variable \\\`\${name}\\\`\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "ICBco+", + message: "Variable \`{name}\`", + values: { + name: name, + }, + } +); + +`; + +exports[`should correctly process nested macro when referenced from different imports 1`] = ` +import { t } from "@lingui/core/macro"; +import { plural } from "@lingui/core/macro"; +t\`Ola! \${plural(count, { one: "1 user", many: "# users" })} is required\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "EUO+Gb", + message: "Ola! {count, plural, one {1 user} many {# users}} is required", + values: { + count: count, + }, + } +); + +`; + +exports[`should correctly process nested macro when referenced from different imports 2 1`] = ` +import { t as t1, plural as plural1 } from "@lingui/core/macro"; +import { plural as plural2, t as t2 } from "@lingui/core/macro"; +t1\`Ola! \${plural2(count, { one: "1 user", many: "# users" })} Ola!\`; +t2\`Ola! \${plural1(count, { one: "1 user", many: "# users" })} Ola!\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +_i18n._( + /*i18n*/ + { + id: "aui5Gr", + message: "Ola! {count, plural, one {1 user} many {# users}} Ola!", + values: { + count: count, + }, + } +); +_i18n._( + /*i18n*/ + { + id: "wJ7AD9", + message: "Ola! {count, plural, one {1 user} many {# users}} Ola!", + values: { + count: count, + }, + } +); + +`; + +exports[`t\`\` macro could be renamed 1`] = ` +import { t as t2 } from "@lingui/core/macro"; +const a = t2\`Expression assignment\`; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +const a = _i18n._( + /*i18n*/ + { + id: "mjnlP0", + message: "Expression assignment", + } +); + +`; diff --git a/packages/babel-plugin-lingui-macro/test/js-useLingui.ts b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-useLingui.test.ts.snap similarity index 60% rename from packages/babel-plugin-lingui-macro/test/js-useLingui.ts rename to packages/babel-plugin-lingui-macro/test/__snapshots__/js-useLingui.test.ts.snap index d4832a674..a4540ff99 100644 --- a/packages/babel-plugin-lingui-macro/test/js-useLingui.ts +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/js-useLingui.test.ts.snap @@ -1,60 +1,44 @@ -import { TestCase } from "./index" -import { makeConfig } from "@lingui/conf" - -const cases: TestCase[] = [ - { - name: "tagged template literal style", - input: ` -import { useLingui } from '@lingui/react/macro'; - -function MyComponent() { - const { t } = useLingui(); - const a = t\`Text\`; -} - `, - expected: ` -import { useLingui as _useLingui } from "@lingui/react"; - -function MyComponent() { - const { _: _t } = _useLingui(); - const a = _t( - /*i18n*/ - { - id: \"xeiujy\", - message: \"Text\", - } - ); -}`, - }, - { - name: "support renamed destructuring", - input: ` -import { useLingui } from '@lingui/react/macro'; +// Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`inserted statement should not clash with existing variables 1`] = ` +import { useLingui } from "@lingui/react/macro"; function MyComponent() { + const _t = "i'm here"; const { t: _ } = useLingui(); const a = _\`Text\`; } - `, - expected: ` -import { useLingui as _useLingui } from "@lingui/react"; +↓ ↓ ↓ ↓ ↓ ↓ + +import { useLingui as _useLingui } from "@lingui/react"; function MyComponent() { - const { _: _t } = _useLingui(); - const a = _t( + const _t = "i'm here"; + const { _: _t2 } = _useLingui(); + const a = _t2( /*i18n*/ { - id: \"xeiujy\", - message: \"Text\", + id: "xeiujy", + message: "Text", } ); -}`, - }, - { - name: "should process macro with matching name in correct scopes", - input: ` -import { useLingui } from '@lingui/react/macro'; +} + +`; +exports[`should not break on function currying 1`] = ` +import { useLingui } from "@lingui/core/macro"; +const result = curryingFoo()(); +console.log("curryingFoo", result); + +↓ ↓ ↓ ↓ ↓ ↓ + +const result = curryingFoo()(); +console.log("curryingFoo", result); + +`; + +exports[`should process macro with matching name in correct scopes 1`] = ` +import { useLingui } from "@lingui/react/macro"; function MyComponent() { const { t } = useLingui(); const a = t\`Text\`; @@ -69,8 +53,9 @@ function MyComponent() { t\`Text\`; } } - `, - expected: ` + +↓ ↓ ↓ ↓ ↓ ↓ + import { useLingui as _useLingui } from "@lingui/react"; function MyComponent() { const { _: _t } = _useLingui(); @@ -98,25 +83,21 @@ function MyComponent() { } } -`, - }, - { - name: "inserted statement should not clash with existing variables", - input: ` -import { useLingui } from '@lingui/react/macro'; +`; +exports[`support configuring runtime module import using LinguiConfig.runtimeConfigModule 1`] = ` +import { useLingui } from "@lingui/react/macro"; function MyComponent() { - const _t = "i'm here"; - const { t: _ } = useLingui(); - const a = _\`Text\`; + const { t } = useLingui(); + const a = t\`Text\`; } - `, - expected: ` -import { useLingui as _useLingui } from "@lingui/react"; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { myUselingui as _useLingui } from "@my/lingui-react"; function MyComponent() { - const _t = "i'm here"; - const { _: _t2 } = _useLingui(); - const a = _t2( + const { _: _t } = _useLingui(); + const a = _t( /*i18n*/ { id: "xeiujy", @@ -124,78 +105,76 @@ function MyComponent() { } ); } -`, - }, - { - name: "support nested macro", - input: ` -import { useLingui } from '@lingui/react/macro'; -import { plural } from '@lingui/core/macro'; +`; + +exports[`support message descriptor 1`] = ` +import { useLingui } from "@lingui/react/macro"; function MyComponent() { const { t } = useLingui(); - const a = t\`Text \${plural(users.length, { - offset: 1, - 0: "No books", - 1: "1 book", - other: "# books" - })}\`; + const a = t({ message: "Hello", context: "my custom" }); } - `, - expected: ` +↓ ↓ ↓ ↓ ↓ ↓ + import { useLingui as _useLingui } from "@lingui/react"; function MyComponent() { const { _: _t } = _useLingui(); const a = _t( /*i18n*/ { - id: "hJRCh6", - message: - "Text {0, plural, offset:1 =0 {No books} =1 {1 book} other {# books}}", - values: { - 0: users.length, - }, + context: "my custom", + message: "Hello", + id: "BYqAaU", } ); } -`, - }, - { - name: "support message descriptor", - input: ` -import { useLingui } from '@lingui/react/macro'; + +`; + +exports[`support nested macro 1`] = ` +import { useLingui } from "@lingui/react/macro"; +import { plural } from "@lingui/core/macro"; function MyComponent() { const { t } = useLingui(); - const a = t({ message: "Hello", context: "my custom" }); + const a = t\`Text \${plural(users.length, { + offset: 1, + 0: "No books", + 1: "1 book", + other: "# books", + })}\`; } - `, - expected: ` - import { useLingui as _useLingui } from "@lingui/react"; - function MyComponent() { + +↓ ↓ ↓ ↓ ↓ ↓ + +import { useLingui as _useLingui } from "@lingui/react"; +function MyComponent() { const { _: _t } = _useLingui(); const a = _t( /*i18n*/ { - context: "my custom", - message: "Hello", - id: "BYqAaU", + id: "hJRCh6", + message: + "Text {0, plural, offset:1 =0 {No books} =1 {1 book} other {# books}}", + values: { + 0: users.length, + }, } ); -}`, - }, - { - name: "support passing t variable as dependency", - input: ` -import { useLingui } from '@lingui/react/macro'; +} + +`; +exports[`support passing t variable as dependency 1`] = ` +import { useLingui } from "@lingui/react/macro"; function MyComponent() { const { t } = useLingui(); const a = useMemo(() => t\`Text\`, [t]); } - `, - expected: ` + +↓ ↓ ↓ ↓ ↓ ↓ + import { useLingui as _useLingui } from "@lingui/react"; function MyComponent() { const { _: _t } = _useLingui(); @@ -211,25 +190,44 @@ function MyComponent() { [_t] ); } -`, - }, - { - name: "transform to standard useLingui statement", - input: ` -import { useLingui } from '@lingui/react/macro'; +`; + +exports[`support renamed destructuring 1`] = ` +import { useLingui } from "@lingui/react/macro"; function MyComponent() { - const { i18n, t } = useLingui(); + const { t: _ } = useLingui(); + const a = _\`Text\`; +} - console.log(i18n); +↓ ↓ ↓ ↓ ↓ ↓ + +import { useLingui as _useLingui } from "@lingui/react"; +function MyComponent() { + const { _: _t } = _useLingui(); + const a = _t( + /*i18n*/ + { + id: "xeiujy", + message: "Text", + } + ); +} + +`; + +exports[`tagged template literal style 1`] = ` +import { useLingui } from "@lingui/react/macro"; +function MyComponent() { + const { t } = useLingui(); const a = t\`Text\`; } - `, - expected: ` + +↓ ↓ ↓ ↓ ↓ ↓ + import { useLingui as _useLingui } from "@lingui/react"; function MyComponent() { - const { i18n, _: _t } = _useLingui(); - console.log(i18n); + const { _: _t } = _useLingui(); const a = _t( /*i18n*/ { @@ -238,29 +236,24 @@ function MyComponent() { } ); } -`, - }, - { - name: "work with existing useLingui statement", - input: ` -import { useLingui as useLinguiMacro } from '@lingui/react/macro'; -import { useLingui } from '@lingui/react'; +`; + +exports[`transform to standard useLingui statement 1`] = ` +import { useLingui } from "@lingui/react/macro"; function MyComponent() { - const { _ } = useLingui(); + const { i18n, t } = useLingui(); - console.log(_); - const { t } = useLinguiMacro(); + console.log(i18n); const a = t\`Text\`; } - `, - expected: ` + +↓ ↓ ↓ ↓ ↓ ↓ + import { useLingui as _useLingui } from "@lingui/react"; -import { useLingui } from "@lingui/react"; function MyComponent() { - const { _ } = useLingui(); - console.log(_); - const { _: _t } = _useLingui(); + const { i18n, _: _t } = _useLingui(); + console.log(i18n); const a = _t( /*i18n*/ { @@ -269,28 +262,27 @@ function MyComponent() { } ); } -`, - }, - { - name: "work with renamed existing useLingui statement", - input: ` -import { useLingui as useLinguiRenamed } from '@lingui/react'; -import { useLingui as useLinguiMacro } from '@lingui/react/macro'; +`; + +exports[`work with existing useLingui statement 1`] = ` +import { useLingui as useLinguiMacro } from "@lingui/react/macro"; +import { useLingui } from "@lingui/react"; function MyComponent() { - const { _ } = useLinguiRenamed(); + const { _ } = useLingui(); console.log(_); const { t } = useLinguiMacro(); const a = t\`Text\`; } - `, - expected: ` -import { useLingui as useLinguiRenamed } from '@lingui/react'; + +↓ ↓ ↓ ↓ ↓ ↓ + import { useLingui as _useLingui } from "@lingui/react"; +import { useLingui } from "@lingui/react"; function MyComponent() { - const { _ } = useLinguiRenamed(); + const { _ } = useLingui(); console.log(_); const { _: _t } = _useLingui(); const a = _t( @@ -301,26 +293,11 @@ function MyComponent() { } ); } -`, - }, - { - name: "should not break on function currying", - input: ` - import { useLingui } from '@lingui/core/macro'; - - const result = curryingFoo()() - console.log('curryingFoo', result) - `, - expected: ` - const result = curryingFoo()() - console.log('curryingFoo', result) -`, - }, - { - name: "work with multiple react components", - input: ` -import { useLingui } from '@lingui/react/macro'; +`; + +exports[`work with multiple react components 1`] = ` +import { useLingui } from "@lingui/react/macro"; function MyComponent() { const { t } = useLingui(); const a = t\`Text\`; @@ -329,8 +306,10 @@ function MyComponent() { function MyComponent2() { const { t } = useLingui(); const b = t\`Text\`; -}`, - expected: ` +} + +↓ ↓ ↓ ↓ ↓ ↓ + import { useLingui as _useLingui } from "@lingui/react"; function MyComponent() { const { _: _t } = _useLingui(); @@ -352,31 +331,28 @@ function MyComponent2() { } ); } -`, - }, - { - name: "support configuring runtime module import using LinguiConfig.runtimeConfigModule", - macroOpts: { - linguiConfig: makeConfig( - { - runtimeConfigModule: { - useLingui: ["@my/lingui-react", "myUselingui"], - }, - }, - { skipValidation: true } - ), - }, - input: ` -import { useLingui } from '@lingui/react/macro'; + +`; + +exports[`work with renamed existing useLingui statement 1`] = ` +import { useLingui as useLinguiRenamed } from "@lingui/react"; +import { useLingui as useLinguiMacro } from "@lingui/react/macro"; function MyComponent() { - const { t } = useLingui(); + const { _ } = useLinguiRenamed(); + + console.log(_); + const { t } = useLinguiMacro(); const a = t\`Text\`; } -`, - expected: ` -import { myUselingui as _useLingui } from "@my/lingui-react"; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { useLingui as useLinguiRenamed } from "@lingui/react"; +import { useLingui as _useLingui } from "@lingui/react"; function MyComponent() { + const { _ } = useLinguiRenamed(); + console.log(_); const { _: _t } = _useLingui(); const a = _t( /*i18n*/ @@ -386,8 +362,5 @@ function MyComponent() { } ); } -`, - }, -] -export default cases +`; diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-plural.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-plural.test.ts.snap new file mode 100644 index 000000000..78ce0b0ed --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-plural.test.ts.snap @@ -0,0 +1,229 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#1 1`] = ` +import { Plural } from "@lingui/react/macro"; +A lot of them} +/>; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"tYX0sm"} + message={ + "{count, plural, offset:1 =0 {Zero items} few {{count} items} other {<0>A lot of them}}" + } + values={{ + count: count, + }} + components={{ + 0: , + }} +/>; + +`; + +exports[`#4 1`] = ` +import { Trans, Plural } from "@lingui/react/macro"; + + # slot added + + } + other={ + + # slots added + + } +/>; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"X8eyr1"} + message={ + "{count, plural, one {<0># slot added} other {<1># slots added}}" + } + values={{ + count: count, + }} + components={{ + 0: , + 1: , + }} +/>; + +`; + +exports[`#6 1`] = ` +import { Plural } from "@lingui/react/macro"; +A lot of them} +/>; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + render="strong" + id="msg.plural" + message={ + "{count, plural, offset:1 =0 {Zero items} few {{count} items} other {<0>A lot of them}}" + } + values={{ + count: count, + }} + components={{ + 0: , + }} +/>; + +`; + +exports[`#7 1`] = ` +import { Trans, Plural } from "@lingui/react/macro"; + + Looking for{" "} + a lot of them} + /> +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id="inner-id-removed" + message={ + "Looking for {0, plural, offset:1 =0 {zero items} few {{1} items {2}} other {<0>a lot of them}}" + } + values={{ + 0: items.length, + 1: items.length, + 2: 42, + }} + components={{ + 0: , + }} +/>; + +`; + +exports[`#8 1`] = ` +import { Plural } from "@lingui/react/macro"; +A lot of them} +/>; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"EQvNfC"} + message={ + "{count, plural, =0 {Zero items} one {{oneText}} other {<0>A lot of them}}" + } + values={{ + count: count, + oneText: oneText, + }} + components={{ + 0: , + }} +/>; + +`; + +exports[`Plural macro could be renamed 1`] = ` +import { Plural as Plural2 } from "@lingui/react/macro"; +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"EMgKyP"} + message={"{count, plural, one {...} other {...}}"} + values={{ + count: count, + }} +/>; + +`; + +exports[`Should preserve reserved props: \`comment\`, \`context\`, \`render\`, \`id\` 1`] = ` +import { Plural } from "@lingui/react/macro"; + {}} + value={count} + offset="1" + _0="Zero items" + few={\`\${count} items\`} + other={A lot of them} +/>; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + render={() => {}} + id="custom.id" + message={ + "{count, plural, offset:1 =0 {Zero items} few {{count} items} other {<0>A lot of them}}" + } + comment="Comment for translator" + context="translation context" + values={{ + count: count, + }} + components={{ + 0: , + }} +/>; + +`; + +exports[`Should return cases without leading or trailing spaces for nested Trans inside Plural 1`] = ` +import { Trans, Plural } from "@lingui/react/macro"; +One hello} + other={Other hello} + value={count} +/>; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"oukcm6"} + message={"{count, plural, one {One hello} other {Other hello}}"} + values={{ + count: count, + }} +/>; + +`; diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-select.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-select.test.ts.snap new file mode 100644 index 000000000..b1e47e5c9 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-select.test.ts.snap @@ -0,0 +1,114 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#1 1`] = ` +import { Select } from "@lingui/react/macro"; +Other} +/>; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + render="strong" + id="msg.select" + message={"{0, select, male {He} female {She} other {<0>Other}}"} + values={{ + 0: user.gender, + }} + components={{ + 0: , + }} +/>; + +`; + +exports[`#4 1`] = ` +import { Select } from "@lingui/react/macro"; + + Hooray! + + } + _sad={ + + Oh no! + + } + other="Dunno" +/>; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"f1ZLwG"} + message={"{0, select, happy {Hooray! <0/>} sad {Oh no! <1/>} other {Dunno}}"} + values={{ + 0: "happy", + }} + components={{ + 0: , + 1: , + }} +/>; + +`; diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-selectOrdinal.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-selectOrdinal.test.ts.snap new file mode 100644 index 000000000..9ab615e8a --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-selectOrdinal.test.ts.snap @@ -0,0 +1,94 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#1 1`] = ` +import { Trans, SelectOrdinal } from "@lingui/react/macro"; + + This is my{" "} + #rd} + />{" "} + cat. +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"3YEV8L"} + message={ + "This is my {count, selectordinal, one {#st} two {#nd} other {<0>#rd}} cat." + } + values={{ + count: count, + }} + components={{ + 0: , + }} +/>; + +`; + +exports[`#2 1`] = ` +import { Trans, SelectOrdinal } from "@lingui/react/macro"; + + This is my + #rd} + />{" "} + cat. +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"Dz3XK1"} + message={ + "This is my{count, selectordinal, one {#st} two {#nd} other {<0>#rd}} cat." + } + values={{ + count: count, + }} + components={{ + 0: , + }} +/>; + +`; + +exports[`#3 1`] = ` +import { Trans, SelectOrdinal } from "@lingui/react/macro"; + + This is my{" "} + #rd} + />{" "} + cat. +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"CDpzE+"} + message={ + "This is my {0, selectordinal, one {#st} two {#nd} other {<0>#rd}} cat." + } + values={{ + 0: user.numCats, + }} + components={{ + 0: , + }} +/>; + +`; diff --git a/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap new file mode 100644 index 000000000..e536169c9 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/__snapshots__/jsx-trans.test.ts.snap @@ -0,0 +1,553 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Elements are replaced with placeholders 1`] = ` +import { Trans } from "@lingui/react/macro"; + + Hello World! +
+

+ My name is{" "} + + {" "} + {name} + +

+; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"k9gsHO"} + message={"Hello <0>World!<1/><2>My name is <3> <4>{name}"} + values={{ + name: name, + }} + components={{ + 0: , + 1:
, + 2:

, + 3: , + 4: , + }} +/>; + +`; + +exports[`Elements inside expression container 1`] = ` +import { Trans } from "@lingui/react/macro"; +{Component inside expression container}; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"1cZQQW"} + message={"<0>Component inside expression container"} + components={{ + 0: , + }} +/>; + +`; + +exports[`Elements without children 1`] = ` +import { Trans } from "@lingui/react/macro"; +{
}
; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"SCJtqt"} + message={"<0/>"} + components={{ + 0:
, + }} +/>; + +`; + +exports[`Expressions are converted to positional arguments 1`] = ` +import { Trans } from "@lingui/react/macro"; + + Property {props.name}, function {random()}, array {array[index]}, constant{" "} + {42}, object {new Date()}, everything {props.messages[index].value()} +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"HjKDmx"} + message={ + "Property {0}, function {1}, array {2}, constant {3}, object {4}, everything {5}" + } + values={{ + 0: props.name, + 1: random(), + 2: array[index], + 3: 42, + 4: new Date(), + 5: props.messages[index].value(), + }} +/>; + +`; + +exports[`Generate ID from message 1`] = ` +import { Trans } from "@lingui/react/macro"; +Hello World; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id={"mY42CM"} message={"Hello World"} />; + +`; + +exports[`Generate different id when context provided 1`] = ` +import { Trans } from "@lingui/react/macro"; +Hello World; +Hello World; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id={"mY42CM"} message={"Hello World"} />; +<_Trans id={"SO/WB8"} message={"Hello World"} context="my context" />; + +`; + +exports[`HTML attributes are handled 1`] = ` +import { Trans } from "@lingui/react/macro"; + + This should work   +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"K/1Xpr"} + message={"<0>This should work \\xA0"} + components={{ + 0: , + }} +/>; + +`; + +exports[`Ignore JSXEmptyExpression 1`] = ` +import { Trans } from "@lingui/react/macro"; +Hello {/* and I cannot stress this enough */} World; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id={"i0M2R8"} message={"Hello World"} />; + +`; + +exports[`JSX Macro inside JSX conditional expressions 1`] = ` +import { Trans } from "@lingui/react/macro"; + + Hello, {props.world ? world : guys} +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"UT5PlM"} + message={"Hello, {0}"} + values={{ + 0: props.world ? ( + <_Trans id={"ELi2P3"} message={"world"} /> + ) : ( + <_Trans id={"39nd+2"} message={"guys"} /> + ), + }} +/>; + +`; + +exports[`JSX Macro inside JSX multiple nested conditional expressions 1`] = ` +import { Trans } from "@lingui/react/macro"; + + Hello,{" "} + {props.world ? ( + world + ) : props.b ? ( + nested + ) : ( + guys + )} +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"UT5PlM"} + message={"Hello, {0}"} + values={{ + 0: props.world ? ( + <_Trans id={"ELi2P3"} message={"world"} /> + ) : props.b ? ( + <_Trans id={"lV+268"} message={"nested"} /> + ) : ( + <_Trans id={"39nd+2"} message={"guys"} /> + ), + }} +/>; + +`; + +exports[`Preserve custom ID (literal expression) 1`] = ` +import { Trans } from "@lingui/react/macro"; +Hello World; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id="msg.hello" message={"Hello World"} />; + +`; + +exports[`Preserve custom ID (string literal) 1`] = ` +import { Trans } from "@lingui/react/macro"; +Hello World; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id="msg.hello" message={"Hello World"} />; + +`; + +exports[`Preserve custom ID (template expression) 1`] = ` +import { Trans } from "@lingui/react/macro"; +Hello World; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id="msg.hello" message={"Hello World"} />; + +`; + +exports[`Production - all props kept if extract: true 1`] = ` +import { Trans } from "@lingui/react/macro"; + + Hello World +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id="msg.hello" message={"Hello World"} comment="Hello World" />; + +`; + +exports[`Production - import type doesn't interference on normal import 1`] = ` +import type { withI18nProps } from "@lingui/react"; +import { Trans } from "@lingui/react/macro"; + + Hello World +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id="msg.hello" />; + +`; + +exports[`Production - only essential props are kept 1`] = ` +import { Trans } from "@lingui/react/macro"; + + Hello World +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id="msg.hello" />; + +`; + +exports[`Quoted JSX attributes are handled 1`] = ` +import { Trans } from "@lingui/react/macro"; +Speak "friend"!; +Speak "friend"!; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id={"NWmRwM"} message={'Speak "friend"!'} />; +<_Trans id="custom-id" message={'Speak "friend"!'} />; + +`; + +exports[`Should not process non JSXElement nodes 1`] = ` +import { Trans } from "@lingui/react/macro"; +type X = typeof Trans; +const cmp = Hello; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +const cmp = <_Trans id={"uzTaYi"} message={"Hello"} />; + +`; + +exports[`Should preserve reserved props: \`comment\`, \`context\`, \`render\`, \`id\` 1`] = ` +import { Trans } from "@lingui/react/macro"; + {}} +> + Hello World +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + render={() => {}} + id="custom.id" + message={"Hello World"} + comment="Comment for translator" + context="translation context" +/>; + +`; + +exports[`Strings as children are preserved 1`] = ` +import { Trans } from "@lingui/react/macro"; +{"hello {count, plural, one {world} other {worlds}}"}; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"U8dd/d"} + message={"hello {count, plural, one {world} other {worlds}}"} +/>; + +`; + +exports[`Strip whitespace around arguments 1`] = ` +import { Trans } from "@lingui/react/macro"; +Strip whitespace around arguments: '{name}'; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"tRMgLt"} + message={"Strip whitespace around arguments: '{name}'"} + values={{ + name: name, + }} +/>; + +`; + +exports[`Strip whitespace around tags but keep forced spaces 1`] = ` +import { Trans } from "@lingui/react/macro"; + + Strip whitespace around tags, but keep forced spaces! +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"Ud4KOf"} + message={"Strip whitespace around tags, but keep <0>forced spaces!"} + components={{ + 0: , + }} +/>; + +`; + +exports[`Strip whitespace around tags but keep whitespaces in JSX containers 1`] = ` +import { Trans } from "@lingui/react/macro"; + + {"Wonderful framework "} +
Next.js + {" say hi. And "} + Next.js + {" say hi."} +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"3YVd0H"} + message={ + "Wonderful framework <0>Next.js say hi. And <1>Next.js say hi." + } + components={{ + 0: , + 1: , + }} +/>; + +`; + +exports[`Template literals as children 1`] = ` +import { Trans } from "@lingui/react/macro"; +{\`How much is \${expression}? \${count}\`}; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"exe3kM"} + message={"How much is {expression}? {count}"} + values={{ + expression: expression, + count: count, + }} +/>; + +`; + +exports[`Trans macro could be renamed 1`] = ` +import { Trans as Trans2 } from "@lingui/react/macro"; +Hello World; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id={"mY42CM"} message={"Hello World"} />; + +`; + +exports[`Use a js macro inside a JSX Attribute of a component handled by JSX macro 1`] = ` +import { Trans } from "@lingui/react/macro"; +import { t } from "@lingui/core/macro"; + + Read{" "} + + more + +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"QZyANg"} + message={"Read <0>more"} + components={{ + 0: ( + + ), + }} +/>; + +`; + +exports[`Use a js macro inside a JSX Attribute of a non macro JSX component 1`] = ` +import { plural } from "@lingui/core/macro"; + + About +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { i18n as _i18n } from "@lingui/core"; + + About +; + +`; + +exports[`Use decoded html entities 1`] = ` +import { Trans } from "@lingui/react/macro"; +&; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans id={"EwTON7"} message={"&"} />; + +`; + +exports[`Variables are converted to named arguments 1`] = ` +import { Trans } from "@lingui/react/macro"; + + Hi {yourName}, my name is {myName} +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"y10VRI"} + message={"Hi {yourName}, my name is {myName}"} + values={{ + yourName: yourName, + myName: myName, + }} +/>; + +`; + +exports[`Variables are deduplicated 1`] = ` +import { Trans } from "@lingui/react/macro"; + + {duplicate} variable {duplicate} +; + +↓ ↓ ↓ ↓ ↓ ↓ + +import { Trans as _Trans } from "@lingui/react"; +<_Trans + id={"+nhkwg"} + message={"{duplicate} variable {duplicate}"} + values={{ + duplicate: duplicate, + }} +/>; + +`; diff --git a/packages/babel-plugin-lingui-macro/test/fixtures/jsx-keep-forced-newlines.expected.js b/packages/babel-plugin-lingui-macro/test/fixtures/jsx-keep-forced-newlines.expected.js index b7c382107..4bf775382 100644 --- a/packages/babel-plugin-lingui-macro/test/fixtures/jsx-keep-forced-newlines.expected.js +++ b/packages/babel-plugin-lingui-macro/test/fixtures/jsx-keep-forced-newlines.expected.js @@ -1,2 +1,2 @@ import { Trans as _Trans } from "@lingui/react" -;<_Trans id={""} message={"Keep multiple\nforced\nnewlines!"} /> +;<_Trans id={"9xE5pD"} message={"Keep multiple\nforced\nnewlines!"} /> diff --git a/packages/babel-plugin-lingui-macro/test/index.ts b/packages/babel-plugin-lingui-macro/test/index.ts index 794791dd2..fee464964 100644 --- a/packages/babel-plugin-lingui-macro/test/index.ts +++ b/packages/babel-plugin-lingui-macro/test/index.ts @@ -1,109 +1,10 @@ -import fs from "fs" import path from "path" -import { - PluginObj, - transformFileSync, - TransformOptions, - transformSync, -} from "@babel/core" -import prettier from "prettier" -// use package path instead relative because we want -// to test it in from /dist folder in integration tests -import linguiMacroPlugin, { - LinguiPluginOpts, -} from "@lingui/babel-plugin-lingui-macro" -import { - JSXAttribute, - jsxExpressionContainer, - JSXIdentifier, - stringLiteral, -} from "@babel/types" -import { NodePath } from "@babel/traverse" - -export type TestCase = { - name?: string - input?: string - expected?: string - filename?: string - production?: boolean - useTypescriptPreset?: boolean - macroOpts?: LinguiPluginOpts - /** Remove hash id from snapshot for more stable testing */ - stripId?: boolean - only?: boolean - skip?: boolean -} - -const testCases: Record = { - "js-t": require("./js-t").default, - "js-plural": require("./js-plural").default, - "js-select": require("./js-select").default, - "js-selectOrdinal": require("./js-selectOrdinal").default, - "jsx-trans": require("./jsx-trans").default, - "jsx-select": require("./jsx-select").default, - "jsx-plural": require("./jsx-plural").default, - "jsx-selectOrdinal": require("./jsx-selectOrdinal").default, - "js-defineMessage": require("./js-defineMessage").default, - "js-useLingui": require("./js-useLingui").default, -} - -function stripIdPlugin(): PluginObj { - return { - visitor: { - JSXOpeningElement: (path) => { - const idAttr = path - .get("attributes") - .find( - (attr) => - attr.isJSXAttribute() && - (attr.node.name as JSXIdentifier).name === "id" - ) as NodePath - - if (idAttr) { - idAttr - .get("value") - .replaceWith(jsxExpressionContainer(stringLiteral(""))) - } - }, - }, - } -} +import { transformSync } from "@babel/core" +import { getDefaultBabelOptions } from "./macroTester" describe("macro", function () { process.env.LINGUI_CONFIG = path.join(__dirname, "lingui.config.js") - const getDefaultBabelOptions = ( - transformType: "plugin" | "macro" = "plugin", - macroOpts: LinguiPluginOpts = {}, - isTs: boolean = false, - stripId = false - ): TransformOptions => { - return { - filename: "" + (isTs ? ".tsx" : "jsx"), - configFile: false, - babelrc: false, - presets: [], - plugins: [ - "@babel/plugin-syntax-jsx", - transformType === "plugin" - ? [linguiMacroPlugin, macroOpts] - : [ - "macros", - { - lingui: macroOpts, - // 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), - }, - ], - - ...(stripId ? [stripIdPlugin] : []), - ], - } - } - const transformTypes = ["plugin", "macro"] as const function forTransforms( @@ -132,97 +33,6 @@ describe("macro", function () { } } - Object.keys(testCases).forEach((suiteName) => { - describe(`${suiteName}`, () => { - const cases = testCases[suiteName] - - const clean = (value: string) => - prettier.format(value, { parser: "babel-ts" }).replace(/\n+/, "\n") - - cases.forEach( - ( - { - name, - input, - expected, - filename, - production, - useTypescriptPreset, - only, - skip, - macroOpts, - stripId, - }, - index - ) => { - let group = describe - if (only) group = describe.only - if (skip) group = describe.skip - group(name != null ? name : `${suiteName} #${index + 1}`, () => { - transformTypes.forEach((transformType) => { - it(transformType, () => { - const babelOptions = getDefaultBabelOptions( - transformType, - macroOpts, - useTypescriptPreset, - stripId - ) - expect(input || filename).toBeDefined() - - const originalEnv = process.env.NODE_ENV - - if (production) { - process.env.NODE_ENV = "production" - } - - if (useTypescriptPreset) { - babelOptions.presets.push("@babel/preset-typescript") - } - - try { - if (filename) { - const inputPath = path.relative( - process.cwd(), - path.join(__dirname, "fixtures", filename) - ) - const expectedPath = inputPath.replace( - /\.js$/, - ".expected.js" - ) - const expected = fs - .readFileSync(expectedPath, "utf8") - .replace(/\r/g, "") - .trim() - - const _babelOptions = { - ...babelOptions, - cwd: path.dirname(inputPath), - } - - const actual = transformFileSync(inputPath, _babelOptions) - .code.replace(/\r/g, "") - .trim() - expect(clean(actual)).toEqual(clean(expected)) - } else { - const actual = transformSync( - input, - babelOptions - ).code.trim() - - expect(clean(actual)).toEqual(clean(expected)) - } - } finally { - process.env.LINGUI_CONFIG = "" - process.env.NODE_ENV = originalEnv - } - }) - }) - }) - } - ) - }) - }) - it("Should throw error if used without babel-macro-plugin", async () => { await expect(async () => { const mod = await import("../src/macro") diff --git a/packages/babel-plugin-lingui-macro/test/js-defineMessage.test.ts b/packages/babel-plugin-lingui-macro/test/js-defineMessage.test.ts new file mode 100644 index 000000000..d6f15bba6 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/js-defineMessage.test.ts @@ -0,0 +1,103 @@ +import { macroTester } from "./macroTester" + +macroTester({ + cases: [ + { + name: "defineMessage should support template literal", + code: ` + import { defineMessage } from '@lingui/core/macro'; + const message = defineMessage\`Message\` + `, + }, + { + name: "defineMessage can be called by alias `msg`", + code: ` + import { msg } from '@lingui/core/macro'; + const message1 = msg\`Message\` + const message2 = msg({message: "Message"}) + `, + }, + { + name: "should expand macros in message property", + code: ` + import { defineMessage, plural, arg } from '@lingui/core/macro'; + const message = defineMessage({ + comment: "Description", + message: plural(value, { one: "book", other: "books" }) + }) + `, + }, + { + name: "defineMessage macro could be renamed", + code: ` + import { defineMessage as defineMessage2, plural as plural2 } from '@lingui/core/macro'; + const message = defineMessage2({ + comment: "Description", + message: plural2(value, { one: "book", other: "books" }) + }) + `, + }, + { + name: "should left string message intact", + code: ` + import { defineMessage } from '@lingui/core/macro'; + const message = defineMessage({ + message: "Message" + }) + `, + }, + { + name: "should transform template literals", + code: ` + import { defineMessage } from '@lingui/core/macro'; + const message = defineMessage({ + message: \`Message \${name}\` + }) + `, + }, + { + name: "should preserve custom id", + code: ` + import { defineMessage } from '@lingui/core/macro'; + const message = defineMessage({ + id: "msg.id", + message: "Message" + }) + `, + }, + { + name: "Production - only essential props are kept, without id", + production: true, + code: ` + import { defineMessage } from '@lingui/core/macro'; + const msg = defineMessage({ + message: \`Hello $\{name\}\`, + comment: 'description for translators', + context: 'My Context', + }) + `, + }, + { + name: "Production - only essential props are kept", + production: true, + code: ` + import { defineMessage } from '@lingui/core/macro'; + const msg = defineMessage({ + message: \`Hello $\{name\}\`, + id: 'msgId', + comment: 'description for translators', + context: 'My Context', + }) + `, + }, + { + name: "should preserve values", + code: ` + import { defineMessage, t } from '@lingui/core/macro'; + const message = defineMessage({ + message: t\`Hello $\{name\}\` + }) + `, + }, + ], +}) diff --git a/packages/babel-plugin-lingui-macro/test/js-defineMessage.ts b/packages/babel-plugin-lingui-macro/test/js-defineMessage.ts deleted file mode 100644 index 10d25877a..000000000 --- a/packages/babel-plugin-lingui-macro/test/js-defineMessage.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { TestCase } from "./index" - -const cases: TestCase[] = [ - { - name: "defineMessage should support template literal", - input: ` - import { defineMessage } from '@lingui/core/macro'; - const message = defineMessage\`Message\` - `, - expected: ` - const message = - /*i18n*/ - { - id: "xDAtGP", - message: "Message", - }; - `, - }, - { - name: "defineMessage can be called by alias `msg`", - input: ` - import { msg } from '@lingui/core/macro'; - const message1 = msg\`Message\` - const message2 = msg({message: "Message"}) - `, - expected: ` - const message1 = - /*i18n*/ - { - id: "xDAtGP", - message: "Message", - }; - const message2 = - /*i18n*/ - { - message: "Message", - id: "xDAtGP", - }; - `, - }, - { - name: "should expand macros in message property", - input: ` - import { defineMessage, plural, arg } from '@lingui/core/macro'; - const message = defineMessage({ - comment: "Description", - message: plural(value, { one: "book", other: "books" }) - }) - `, - expected: ` - const message = - /*i18n*/ - { - values: { - value: value, - }, - message: "{value, plural, one {book} other {books}}", - id: "SlmyxX", - comment: "Description", - }; - `, - }, - { - name: "defineMessage macro could be renamed", - input: ` - import { defineMessage as defineMessage2, plural as plural2 } from '@lingui/core/macro'; - const message = defineMessage2({ - comment: "Description", - message: plural2(value, { one: "book", other: "books" }) - }) - `, - expected: ` - const message = - /*i18n*/ - { - values: { - value: value, - }, - message: "{value, plural, one {book} other {books}}", - id: "SlmyxX", - comment: "Description", - }; - `, - }, - { - name: "should left string message intact", - input: ` - import { defineMessage } from '@lingui/core/macro'; - const message = defineMessage({ - message: "Message" - }) - `, - expected: ` - const message = - /*i18n*/ - { - message: "Message", - id: "xDAtGP", - }; - `, - }, - { - name: "should transform template literals", - input: ` - import { defineMessage } from '@lingui/core/macro'; - const message = defineMessage({ - message: \`Message \${name}\` - }) - `, - expected: ` - const message = - /*i18n*/ - { - values: { - name: name - }, - message: "Message {name}", - id: "A2aVLF", - }; - `, - }, - { - name: "should preserve custom id", - input: ` - import { defineMessage } from '@lingui/core/macro'; - const message = defineMessage({ - id: "msg.id", - message: "Message" - }) - `, - expected: ` - const message = - /*i18n*/ - { - id: "msg.id", - message: "Message" - }; - `, - }, - { - name: "Production - only essential props are kept, without id", - production: true, - input: ` - import { defineMessage } from '@lingui/core/macro'; - const msg = defineMessage({ - message: \`Hello $\{name\}\`, - comment: 'description for translators', - context: 'My Context', - }) - `, - expected: ` - const msg = - /*i18n*/ - { - values: { - name: name, - }, - id: "oT92lS", - }; - `, - }, - { - name: "Production - only essential props are kept", - production: true, - input: ` - import { defineMessage } from '@lingui/core/macro'; - const msg = defineMessage({ - message: \`Hello $\{name\}\`, - id: 'msgId', - comment: 'description for translators', - context: 'My Context', - }) - `, - expected: ` - const msg = - /*i18n*/ - { - id: 'msgId', - values: { - name: name, - }, - }; - `, - }, - { - name: "should preserve values", - input: ` - import { defineMessage, t } from '@lingui/core/macro'; - const message = defineMessage({ - message: t\`Hello $\{name\}\` - }) - `, - expected: ` - const message = - /*i18n*/ - { - values: { - name: name - }, - message: "Hello {name}", - id: "OVaF9k", - }; - `, - }, -] - -export default cases diff --git a/packages/babel-plugin-lingui-macro/test/js-plural.test.ts b/packages/babel-plugin-lingui-macro/test/js-plural.test.ts new file mode 100644 index 000000000..9e89a9065 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/js-plural.test.ts @@ -0,0 +1,50 @@ +import { macroTester } from "./macroTester" + +macroTester({ + cases: [ + { + name: "Macro is used in expression assignment", + code: ` + import { plural } from '@lingui/core/macro' + const a = plural(count, { + "one": \`# book\`, + other: "# books" + }); + `, + }, + { + name: "plural macro could be renamed", + code: ` + import { plural as plural2 } from '@lingui/core/macro' + const a = plural2(count, { + "one": \`# book\`, + other: "# books" + }); + `, + }, + { + name: "Macro with offset and exact matches", + code: ` + import { plural } from '@lingui/core/macro' + plural(users.length, { + offset: 1, + 0: "No books", + 1: "1 book", + other: "# books" + }); + `, + }, + { + name: "Macro with expression only choice", + code: ` + import { plural } from '@lingui/core/macro' + plural(users.length, { + offset: 1, + 0: "No books", + 1: "1 book", + other: someOtherExp + }); + `, + }, + ], +}) diff --git a/packages/babel-plugin-lingui-macro/test/js-plural.ts b/packages/babel-plugin-lingui-macro/test/js-plural.ts deleted file mode 100644 index 67e184c05..000000000 --- a/packages/babel-plugin-lingui-macro/test/js-plural.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { TestCase } from "./index" - -const cases: TestCase[] = [ - { - name: "Macro is used in expression assignment", - input: ` - import { plural } from '@lingui/core/macro' - const a = plural(count, { - "one": \`# book\`, - other: "# books" - }); - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const a = _i18n._( - /*i18n*/ - { - id: "esnaQO", - message: "{count, plural, one {# book} other {# books}}", - values: { - count: count, - }, - } - ); - - `, - }, - { - name: "plural macro could be renamed", - input: ` - import { plural as plural2 } from '@lingui/core/macro' - const a = plural2(count, { - "one": \`# book\`, - other: "# books" - }); - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const a = _i18n._( - /*i18n*/ - { - id: "esnaQO", - message: "{count, plural, one {# book} other {# books}}", - values: { - count: count, - }, - } - ); - - `, - }, - { - name: "Macro with offset and exact matches", - input: ` - import { plural } from '@lingui/core/macro' - plural(users.length, { - offset: 1, - 0: "No books", - 1: "1 book", - other: "# books" - }); - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "CF5t+7", - message: "{0, plural, offset:1 =0 {No books} =1 {1 book} other {# books}}", - values: { - 0: users.length, - }, - } - ); - `, - }, - { - name: "Macro with expression only choice", - input: ` - import { plural } from '@lingui/core/macro' - plural(users.length, { - offset: 1, - 0: "No books", - 1: "1 book", - other: someOtherExp - }); - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "0mcXIe", - message: "{0, plural, offset:1 =0 {No books} =1 {1 book} other {{someOtherExp}}}", - values: { - 0: users.length, - someOtherExp: someOtherExp, - }, - } - ); - `, - }, -] -export default cases diff --git a/packages/babel-plugin-lingui-macro/test/js-select.test.ts b/packages/babel-plugin-lingui-macro/test/js-select.test.ts new file mode 100644 index 000000000..300466a72 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/js-select.test.ts @@ -0,0 +1,34 @@ +import { macroTester } from "./macroTester" + +macroTester({ + cases: [ + { + name: "Nested macros", + code: ` + import { select, plural } from '@lingui/core/macro' + select(gender, { + "male": plural(numOfGuests, { + one: "He invites one guest", + other: "He invites # guests" + }), + female: \`She is \${gender}\`, + other: \`They is \${gender}\` + }); + `, + }, + { + name: "Nested macros with pure expressions option", + code: ` + import { select, plural } from '@lingui/core/macro' + select(gender, { + "male": plural(numOfGuests, { + one: "He invites one guest", + other: "He invites # guests" + }), + female: \`She is \${gender}\`, + other: someOtherExp + }); + `, + }, + ], +}) diff --git a/packages/babel-plugin-lingui-macro/test/js-select.ts b/packages/babel-plugin-lingui-macro/test/js-select.ts deleted file mode 100644 index ad9f3c24f..000000000 --- a/packages/babel-plugin-lingui-macro/test/js-select.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { TestCase } from "./index" - -const cases: TestCase[] = [ - { - name: "Nested macros", - input: ` - import { select, plural } from '@lingui/core/macro' - select(gender, { - "male": plural(numOfGuests, { - one: "He invites one guest", - other: "He invites # guests" - }), - female: \`She is \${gender}\`, - other: \`They is \${gender}\` - }); - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "G8xqGf", - message: "{gender, select, male {{numOfGuests, plural, one {He invites one guest} other {He invites # guests}}} female {She is {gender}} other {They is {gender}}}", - values: { - gender: gender, - numOfGuests: numOfGuests, - }, - } - ); - `, - }, - { - name: "Nested macros with pure expressions option", - input: ` - import { select, plural } from '@lingui/core/macro' - select(gender, { - "male": plural(numOfGuests, { - one: "He invites one guest", - other: "He invites # guests" - }), - female: \`She is \${gender}\`, - other: someOtherExp - }); - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "j9PNNm", - message: "{gender, select, male {{numOfGuests, plural, one {He invites one guest} other {He invites # guests}}} female {She is {gender}} other {{someOtherExp}}}", - values: { - gender: gender, - numOfGuests: numOfGuests, - someOtherExp: someOtherExp, - }, - } - ); - `, - }, -] -export default cases diff --git a/packages/babel-plugin-lingui-macro/test/js-selectOrdinal.test.ts b/packages/babel-plugin-lingui-macro/test/js-selectOrdinal.test.ts new file mode 100644 index 000000000..0ca4d07b4 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/js-selectOrdinal.test.ts @@ -0,0 +1,16 @@ +import { macroTester } from "./macroTester" + +macroTester({ + cases: [ + { + code: ` + import { t, selectOrdinal } from '@lingui/core/macro' + t\`This is my \${selectOrdinal(count, { + one: "#st", + "two": \`#nd\`, + other: ("#rd") + })} cat\` + `, + }, + ], +}) diff --git a/packages/babel-plugin-lingui-macro/test/js-selectOrdinal.ts b/packages/babel-plugin-lingui-macro/test/js-selectOrdinal.ts deleted file mode 100644 index 0b6fa3949..000000000 --- a/packages/babel-plugin-lingui-macro/test/js-selectOrdinal.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { TestCase } from "./index" - -const cases: TestCase[] = [ - { - input: ` - import { t, selectOrdinal } from '@lingui/core/macro' - t\`This is my \${selectOrdinal(count, { - one: "#st", - "two": \`#nd\`, - other: ("#rd") - })} cat\` - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "dJXd3T", - message: "This is my {count, selectordinal, one {#st} two {#nd} other {#rd}} cat", - values: { - count: count, - }, - } - ); - `, - }, -] -export default cases diff --git a/packages/babel-plugin-lingui-macro/test/js-t.test.ts b/packages/babel-plugin-lingui-macro/test/js-t.test.ts new file mode 100644 index 000000000..9aee0a771 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/js-t.test.ts @@ -0,0 +1,239 @@ +import { macroTester } from "./macroTester" + +macroTester({ + cases: [ + { + name: "Macro is used in expression assignment", + code: ` + import { t } from '@lingui/core/macro'; + const a = t\`Expression assignment\`; + `, + }, + { + name: "Macro is used in call expression", + code: ` + import { t } from '@lingui/core/macro'; + const msg = message.error(t({message: "dasd"})) + `, + }, + { + name: "t`` macro could be renamed", + code: ` + import { t as t2 } from '@lingui/core/macro'; + const a = t2\`Expression assignment\`; + `, + }, + { + name: "Macro is used in expression assignment, with custom lingui instance", + code: ` + import { t } from '@lingui/core/macro'; + import { customI18n } from './lingui'; + const a = t(customI18n)\`Expression assignment\`; + `, + }, + { + name: "Variables are replaced with named arguments", + code: ` + import { t } from '@lingui/core/macro'; + t\`Variable \${name}\`; + `, + }, + { + name: "Variables with escaped template literals are correctly formatted", + code: ` + import { t } from '@lingui/core/macro'; + t\`Variable \\\`\${name}\\\`\`; + `, + }, + { + name: "Variables with escaped double quotes are correctly formatted", + code: ` + import { t } from '@lingui/core/macro'; + t\`Variable \"name\"\`; + `, + }, + { + name: "Variables should be deduplicated", + code: ` + import { t } from '@lingui/core/macro'; + t\`\${duplicate} variable \${duplicate}\`; + `, + }, + { + name: "Anything variables except simple identifiers are used as positional arguments", + code: ` + import { t } from '@lingui/core/macro'; + t\`\ + Property \${props.name},\ + function \${random()},\ + array \${array[index]},\ + constant \${42},\ + object \${new Date()}\ + anything \${props.messages[index].value()}\ +\` +`, + }, + { + name: "Newlines are preserved", + code: ` + import { t } from '@lingui/core/macro'; + t\`Multiline + string\` + `, + }, + { + name: "Support template strings in t macro message", + code: ` + import { t } from '@lingui/core/macro' + const msg = t({ message: \`Hello \${name}\` }) + `, + }, + { + name: "Support template strings in t macro message, with custom i18n instance", + code: ` + import { t } from '@lingui/core/macro' + import { i18n } from './lingui' + const msg = t(i18n)({ message: \`Hello \${name}\` }) + `, + }, + { + name: "Support template strings in t macro message, with custom i18n instance object property", + code: ` + import { t } from '@lingui/core/macro' + const msg = t(global.i18n)({ message: \`Hello \${name}\` }) + `, + }, + { + name: "Should generate different id when context provided", + code: ` + import { t } from '@lingui/core/macro' + t({ message: "Hello" }) + t({ message: "Hello", context: "my custom" }) + `, + }, + { + name: "Context might be passed as template literal", + code: ` + import { t } from '@lingui/core/macro' + t({ message: "Hello", context: "my custom" }) + t({ message: "Hello", context: \`my custom\` }) + `, + }, + { + name: "Support id and comment in t macro as callExpression", + code: ` + import { t, plural } from '@lingui/core/macro' + const msg = t({ id: 'msgId', comment: 'description for translators', message: plural(val, { one: '...', other: '...' }) }) + `, + }, + { + name: "Support id with message interpolation", + code: ` + import { t } from '@lingui/core/macro' + const msg = t({ id: 'msgId', message: \`Some \${value}\` }) + `, + }, + { + name: "Support id in template literal", + code: ` + import { t } from '@lingui/core/macro' + const msg = t({ id: \`msgId\` }) + `, + }, + { + name: "Production - only essential props are kept", + production: true, + code: ` + import { t } from '@lingui/core/macro'; + const msg = t\`Message\` + `, + }, + { + name: "Production - only essential props are kept, with plural, with custom i18n instance", + production: true, + code: ` + import { t, plural } from '@lingui/core/macro'; + const msg = t({ + id: 'msgId', + comment: 'description for translators', + context: 'some context', + message: plural(val, { one: '...', other: '...' }) + }) + `, + }, + { + name: "Production - only essential props are kept, with custom i18n instance", + production: true, + code: ` + import { t } from '@lingui/core/macro'; + import { i18n } from './lingui'; + const msg = t(i18n)({ + message: \`Hello $\{name\}\`, + id: 'msgId', + comment: 'description for translators', + context: 'My Context', + }) + `, + }, + { + name: "Production - only essential props are kept", + production: true, + code: ` + import { t } from '@lingui/core/macro'; + const msg = t({ + message: \`Hello $\{name\}\`, + id: 'msgId', + comment: 'description for translators', + context: 'My Context', + }) + `, + }, + { + name: "Production - all props kept if extract: true", + production: true, + macroOpts: { + extract: true, + }, + code: ` + import { t } from '@lingui/core/macro'; + const msg = t({ + message: \`Hello $\{name\}\`, + id: 'msgId', + comment: 'description for translators', + context: 'My Context', + }) + `, + }, + { + name: "Newlines after continuation character are removed", + filename: "js-t-continuation-character.js", + }, + { + filename: "js-t-var/js-t-var.js", + }, + { + name: "Support t in t", + code: ` + import { t } from '@lingui/core/macro' + t\`Field \${t\`First Name\`} is required\` + `, + }, + { + name: "should correctly process nested macro when referenced from different imports", + code: ` + import { t } from '@lingui/core/macro' + import { plural } from '@lingui/core/macro' + t\`Ola! \${plural(count, {one: "1 user", many: "# users"})} is required\` + `, + }, + { + name: "should correctly process nested macro when referenced from different imports 2", + code: ` + import { t as t1, plural as plural1 } from '@lingui/core/macro' + import { plural as plural2, t as t2 } from '@lingui/core/macro' + t1\`Ola! \${plural2(count, {one: "1 user", many: "# users"})} Ola!\` + t2\`Ola! \${plural1(count, {one: "1 user", many: "# users"})} Ola!\` + `, + }, + ], +}) diff --git a/packages/babel-plugin-lingui-macro/test/js-t.ts b/packages/babel-plugin-lingui-macro/test/js-t.ts deleted file mode 100644 index 3160e81e5..000000000 --- a/packages/babel-plugin-lingui-macro/test/js-t.ts +++ /dev/null @@ -1,578 +0,0 @@ -import { TestCase } from "./index" - -const cases: TestCase[] = [ - { - name: "Macro is used in expression assignment", - input: ` - import { t } from '@lingui/core/macro'; - const a = t\`Expression assignment\`; - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const a = _i18n._( - /*i18n*/ - { - id: "mjnlP0", - message: "Expression assignment", - } - ); - `, - }, - { - name: "Macro is used in call expression", - input: ` - import { t } from '@lingui/core/macro'; - const msg = message.error(t({message: "dasd"})) - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const msg = message.error( - _i18n._( - /*i18n*/ - { - message: "dasd", - id: "9ZMZjU", - } - ) - ); - `, - }, - { - name: "t`` macro could be renamed", - input: ` - import { t as t2 } from '@lingui/core/macro'; - const a = t2\`Expression assignment\`; - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const a = _i18n._( - /*i18n*/ - { - id: "mjnlP0", - message: "Expression assignment", - } - ); - `, - }, - { - name: "Macro is used in expression assignment, with custom lingui instance", - input: ` - import { t } from '@lingui/core/macro'; - import { customI18n } from './lingui'; - const a = t(customI18n)\`Expression assignment\`; - `, - expected: ` - import { customI18n } from './lingui'; - const a = customI18n._( - /*i18n*/ - { - id: "mjnlP0", - message: "Expression assignment", - } - ); - `, - }, - { - name: "Variables are replaced with named arguments", - input: ` - import { t } from '@lingui/core/macro'; - t\`Variable \${name}\`; - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "xRRkAE", - message: "Variable {name}", - values: { - name: name, - }, - } - ); - `, - }, - { - name: "Variables with escaped template literals are correctly formatted", - input: ` - import { t } from '@lingui/core/macro'; - t\`Variable \\\`\${name}\\\`\`; - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "ICBco+", - message: "Variable \`{name}\`", - values: { - name: name, - }, - } - ); - `, - }, - { - name: "Variables with escaped double quotes are correctly formatted", - input: ` - import { t } from '@lingui/core/macro'; - t\`Variable \"name\"\`; - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "CcPIZW", - message: 'Variable "name"', - } - ); - `, - }, - { - name: "Variables should be deduplicated", - input: ` - import { t } from '@lingui/core/macro'; - t\`\${duplicate} variable \${duplicate}\`; - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "+nhkwg", - message: "{duplicate} variable {duplicate}", - values: { - duplicate: duplicate, - }, - } - ); - `, - }, - { - name: "Anything variables except simple identifiers are used as positional arguments", - input: ` - import { t } from '@lingui/core/macro'; - t\`\ - Property \${props.name},\ - function \${random()},\ - array \${array[index]},\ - constant \${42},\ - object \${new Date()}\ - anything \${props.messages[index].value()}\ -\` -`, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "vVZNZ5", - message: - " Property {0}, function {1}, array {2}, constant {3}, object {4} anything {5}", - values: { - 0: props.name, - 1: random(), - 2: array[index], - 3: 42, - 4: new Date(), - 5: props.messages[index].value(), - }, - } - ); - `, - }, - { - name: "Newlines are preserved", - input: ` - import { t } from '@lingui/core/macro'; - t\`Multiline - string\` - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - id: "+8iwDA", - message: "Multiline\\n string", - } - ); - `, - }, - { - name: "Support template strings in t macro message", - input: ` - import { t } from '@lingui/core/macro' - const msg = t({ message: \`Hello \${name}\` }) - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const msg = _i18n._( - /*i18n*/ - { - values: { - name: name, - }, - message: "Hello {name}", - id: "OVaF9k", - } - ); - `, - }, - { - name: "Support template strings in t macro message, with custom i18n instance", - input: ` - import { t } from '@lingui/core/macro' - import { i18n } from './lingui' - const msg = t(i18n)({ message: \`Hello \${name}\` }) - `, - expected: ` - import { i18n } from "./lingui"; - const msg = i18n._( - /*i18n*/ - { - values: { - name: name, - }, - message: "Hello {name}", - id: "OVaF9k", - } - ); - `, - }, - { - name: "Support template strings in t macro message, with custom i18n instance object property", - input: ` - import { t } from '@lingui/core/macro' - const msg = t(global.i18n)({ message: \`Hello \${name}\` }) - `, - expected: `const msg = global.i18n._( - /*i18n*/ - { - values: { - name: name, - }, - message: "Hello {name}", - id: "OVaF9k", - } - ); - `, - }, - { - name: "Should generate different id when context provided", - input: ` - import { t } from '@lingui/core/macro' - t({ message: "Hello" }) - t({ message: "Hello", context: "my custom" }) - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - message: "Hello", - id: "uzTaYi", - } - ); - _i18n._( - /*i18n*/ - { - context: "my custom", - message: "Hello", - id: "BYqAaU", - } - ); - `, - }, - { - name: "Context might be passed as template literal", - input: ` - import { t } from '@lingui/core/macro' - t({ message: "Hello", context: "my custom" }) - t({ message: "Hello", context: \`my custom\` }) - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - _i18n._( - /*i18n*/ - { - context: "my custom", - message: "Hello", - id: "BYqAaU", - } - ); - _i18n._( - /*i18n*/ - { - context: \`my custom\`, - message: "Hello", - id: "BYqAaU", - } - ); - `, - }, - { - name: "Support id and comment in t macro as callExpression", - input: ` - import { t, plural } from '@lingui/core/macro' - const msg = t({ id: 'msgId', comment: 'description for translators', message: plural(val, { one: '...', other: '...' }) }) - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const msg = _i18n._( - /*i18n*/ - { - id: "msgId", - values: { - val: val, - }, - message: "{val, plural, one {...} other {...}}", - comment: "description for translators", - } - ); - `, - }, - { - name: "Support id with message interpolation", - input: ` - import { t } from '@lingui/core/macro' - const msg = t({ id: 'msgId', message: \`Some \${value}\` }) - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const msg = _i18n._( - /*i18n*/ - { - id: "msgId", - values: { - value: value, - }, - message: "Some {value}", - } - ); - `, - }, - { - name: "Support id in template literal", - input: ` - import { t } from '@lingui/core/macro' - const msg = t({ id: \`msgId\` }) - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const msg = - _i18n._(/*i18n*/ - { - id: \`msgId\` - }); - `, - }, - { - name: "Production - only essential props are kept", - production: true, - input: ` - import { t } from '@lingui/core/macro'; - const msg = t\`Message\` - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const msg = _i18n._(/*i18n*/ - { - id: "xDAtGP", - }); - `, - }, - { - name: "Production - only essential props are kept, with plural, with custom i18n instance", - production: true, - input: ` - import { t, plural } from '@lingui/core/macro'; - const msg = t({ - id: 'msgId', - comment: 'description for translators', - context: 'some context', - message: plural(val, { one: '...', other: '...' }) - }) - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const msg = - _i18n._(/*i18n*/ - { - id: "msgId", - values: { - val: val, - }, - }); - `, - }, - { - name: "Production - only essential props are kept, with custom i18n instance", - production: true, - input: ` - import { t } from '@lingui/core/macro'; - import { i18n } from './lingui'; - const msg = t(i18n)({ - message: \`Hello $\{name\}\`, - id: 'msgId', - comment: 'description for translators', - context: 'My Context', - }) - `, - expected: ` - import { i18n } from "./lingui"; - const msg = - i18n._(/*i18n*/ - { - id: 'msgId', - values: { - name: name, - }, - }); - `, - }, - { - name: "Production - only essential props are kept", - production: true, - input: ` - import { t } from '@lingui/core/macro'; - const msg = t({ - message: \`Hello $\{name\}\`, - id: 'msgId', - comment: 'description for translators', - context: 'My Context', - }) - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const msg = - _i18n._(/*i18n*/ - { - id: 'msgId', - values: { - name: name, - }, - }); - `, - }, - { - name: "Production - all props kept if extract: true", - production: true, - macroOpts: { - extract: true, - }, - input: ` - import { t } from '@lingui/core/macro'; - const msg = t({ - message: \`Hello $\{name\}\`, - id: 'msgId', - comment: 'description for translators', - context: 'My Context', - }) - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - const msg = - _i18n._(/*i18n*/ - { - id: 'msgId', - context: 'My Context', - values: { - name: name, - }, - message: "Hello {name}", - comment: "description for translators", - }); - `, - }, - { - name: "Newlines after continuation character are removed", - filename: "js-t-continuation-character.js", - }, - { - filename: "js-t-var/js-t-var.js", - }, - { - name: "Support t in t", - input: ` - import { t } from '@lingui/core/macro' - t\`Field \${t\`First Name\`} is required\` - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; -_i18n._( - /*i18n*/ - { - id: "O8dJMg", - message: "Field {0} is required", - values: { - 0: _i18n._( - /*i18n*/ - { - id: "kODvZJ", - message: "First Name", - } - ), - }, - } -); - - `, - }, - { - name: "should correctly process nested macro when referenced from different imports", - input: ` - import { t } from '@lingui/core/macro' - import { plural } from '@lingui/core/macro' - t\`Ola! \${plural(count, {one: "1 user", many: "# users"})} is required\` - `, - expected: ` -import { i18n as _i18n } from "@lingui/core"; -_i18n._( - /*i18n*/ - { - id: "EUO+Gb", - message: "Ola! {count, plural, one {1 user} many {# users}} is required", - values: { - count: count, - }, - } -); - `, - }, - { - name: "should correctly process nested macro when referenced from different imports 2", - input: ` - import { t as t1, plural as plural1 } from '@lingui/core/macro' - import { plural as plural2, t as t2 } from '@lingui/core/macro' - t1\`Ola! \${plural2(count, {one: "1 user", many: "# users"})} Ola!\` - t2\`Ola! \${plural1(count, {one: "1 user", many: "# users"})} Ola!\` - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; -_i18n._( - /*i18n*/ - { - id: "aui5Gr", - message: "Ola! {count, plural, one {1 user} many {# users}} Ola!", - values: { - count: count, - }, - } -); -_i18n._( - /*i18n*/ - { - id: "wJ7AD9", - message: "Ola! {count, plural, one {1 user} many {# users}} Ola!", - values: { - count: count, - }, - } -); -`, - }, -] - -export default cases diff --git a/packages/babel-plugin-lingui-macro/test/js-useLingui.test.ts b/packages/babel-plugin-lingui-macro/test/js-useLingui.test.ts new file mode 100644 index 000000000..83ecec067 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/js-useLingui.test.ts @@ -0,0 +1,191 @@ +import { makeConfig } from "@lingui/conf" +import { macroTester } from "./macroTester" + +macroTester({ + cases: [ + { + name: "tagged template literal style", + code: ` +import { useLingui } from '@lingui/react/macro'; + +function MyComponent() { + const { t } = useLingui(); + const a = t\`Text\`; +} + `, + }, + { + name: "support renamed destructuring", + code: ` +import { useLingui } from '@lingui/react/macro'; + +function MyComponent() { + const { t: _ } = useLingui(); + const a = _\`Text\`; +} + `, + }, + { + name: "should process macro with matching name in correct scopes", + code: ` +import { useLingui } from '@lingui/react/macro'; + +function MyComponent() { + const { t } = useLingui(); + const a = t\`Text\`; + + { + // here is child scope with own "t" binding, shouldn't be processed + const t = () => {}; + t\`Text\`; + } + { + // here is child scope which should be processed, since 't' relates to outer scope + t\`Text\`; + } +} + `, + }, + { + name: "inserted statement should not clash with existing variables", + code: ` +import { useLingui } from '@lingui/react/macro'; + +function MyComponent() { + const _t = "i'm here"; + const { t: _ } = useLingui(); + const a = _\`Text\`; +} + `, + }, + { + name: "support nested macro", + code: ` +import { useLingui } from '@lingui/react/macro'; +import { plural } from '@lingui/core/macro'; + +function MyComponent() { + const { t } = useLingui(); + const a = t\`Text \${plural(users.length, { + offset: 1, + 0: "No books", + 1: "1 book", + other: "# books" + })}\`; +} + + `, + }, + { + name: "support message descriptor", + code: ` +import { useLingui } from '@lingui/react/macro'; + +function MyComponent() { + const { t } = useLingui(); + const a = t({ message: "Hello", context: "my custom" }); +} + `, + }, + { + name: "support passing t variable as dependency", + code: ` +import { useLingui } from '@lingui/react/macro'; + +function MyComponent() { + const { t } = useLingui(); + const a = useMemo(() => t\`Text\`, [t]); +} + `, + }, + { + name: "transform to standard useLingui statement", + code: ` +import { useLingui } from '@lingui/react/macro'; + +function MyComponent() { + const { i18n, t } = useLingui(); + + console.log(i18n); + const a = t\`Text\`; +} + `, + }, + { + name: "work with existing useLingui statement", + code: ` +import { useLingui as useLinguiMacro } from '@lingui/react/macro'; +import { useLingui } from '@lingui/react'; + +function MyComponent() { + const { _ } = useLingui(); + + console.log(_); + const { t } = useLinguiMacro(); + const a = t\`Text\`; +} + `, + }, + + { + name: "work with renamed existing useLingui statement", + code: ` +import { useLingui as useLinguiRenamed } from '@lingui/react'; +import { useLingui as useLinguiMacro } from '@lingui/react/macro'; + +function MyComponent() { + const { _ } = useLinguiRenamed(); + + console.log(_); + const { t } = useLinguiMacro(); + const a = t\`Text\`; +} + `, + }, + { + name: "should not break on function currying", + code: ` + import { useLingui } from '@lingui/core/macro'; + + const result = curryingFoo()() + console.log('curryingFoo', result) + `, + }, + { + name: "work with multiple react components", + code: ` +import { useLingui } from '@lingui/react/macro'; + +function MyComponent() { + const { t } = useLingui(); + const a = t\`Text\`; +} + +function MyComponent2() { + const { t } = useLingui(); + const b = t\`Text\`; +}`, + }, + { + name: "support configuring runtime module import using LinguiConfig.runtimeConfigModule", + macroOpts: { + linguiConfig: makeConfig( + { + runtimeConfigModule: { + useLingui: ["@my/lingui-react", "myUselingui"], + }, + }, + { skipValidation: true } + ), + }, + code: ` +import { useLingui } from '@lingui/react/macro'; + +function MyComponent() { + const { t } = useLingui(); + const a = t\`Text\`; +} +`, + }, + ], +}) diff --git a/packages/babel-plugin-lingui-macro/test/jsx-plural.test.ts b/packages/babel-plugin-lingui-macro/test/jsx-plural.test.ts new file mode 100644 index 000000000..ce53b1515 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/jsx-plural.test.ts @@ -0,0 +1,126 @@ +import { macroTester } from "./macroTester" + +macroTester({ + cases: [ + { + code: ` + import { Plural } from '@lingui/react/macro'; + A lot of them} + />; + `, + }, + { + name: "Plural macro could be renamed", + code: ` + import { Plural as Plural2 } from '@lingui/react/macro'; + ; + `, + }, + { + name: "Should preserve reserved props: `comment`, `context`, `render`, `id`", + code: ` + import { Plural } from '@lingui/react/macro'; + {}} + value={count} + offset="1" + _0="Zero items" + few={\`\${count} items\`} + other={A lot of them} + />; + `, + }, + { + code: ` + import { Trans, Plural } from '@lingui/react/macro'; + + # slot added + + } + other={ + + # slots added + + } + />; + `, + }, + { + name: "Should return cases without leading or trailing spaces for nested Trans inside Plural", + code: ` + import { Trans, Plural } from '@lingui/react/macro'; + + One hello + + } + other={ + + Other hello + + } + value={count} + />; + `, + }, + { + code: ` + import { Plural } from '@lingui/react/macro'; + A lot of them} + />; + `, + }, + { + code: ` + import { Trans, Plural } from '@lingui/react/macro'; + + Looking for{" "} + a lot of them} + /> + + `, + }, + { + code: ` + import { Plural } from '@lingui/react/macro'; + A lot of them} + />; + `, + }, + { + filename: `jsx-plural-select-nested.js`, + }, + ], +}) diff --git a/packages/babel-plugin-lingui-macro/test/jsx-plural.ts b/packages/babel-plugin-lingui-macro/test/jsx-plural.ts deleted file mode 100644 index 15d5997bf..000000000 --- a/packages/babel-plugin-lingui-macro/test/jsx-plural.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { TestCase } from "./index" - -const cases: TestCase[] = [ - { - input: ` - import { Plural } from '@lingui/react/macro'; - A lot of them} - />; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={"tYX0sm"} - message={ - "{count, plural, offset:1 =0 {Zero items} few {{count} items} other {<0>A lot of them}}" - } - values={{ - count: count - }} - components={{ - 0: - }} - />; - `, - }, - { - stripId: true, - name: "Plural macro could be renamed", - input: ` - import { Plural as Plural2 } from '@lingui/react/macro'; - ; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"{count, plural, one {...} other {...}}"} - values={{ - count: count, - }} - />; - `, - }, - { - name: "Should preserve reserved props: `comment`, `context`, `render`, `id`", - input: ` - import { Plural } from '@lingui/react/macro'; - {}} - value={count} - offset="1" - _0="Zero items" - few={\`\${count} items\`} - other={A lot of them} - />; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - render={() => {}} - id="custom.id" - message={ - "{count, plural, offset:1 =0 {Zero items} few {{count} items} other {<0>A lot of them}}" - } - comment="Comment for translator" - context="translation context" - values={{ - count: count - }} - components={{ - 0: - }} - />; - `, - }, - { - stripId: true, - input: ` - import { Trans, Plural } from '@lingui/react/macro'; - - # slot added - - } - other={ - - # slots added - - } - />; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={ - "{count, plural, one {<0># slot added} other {<1># slots added}}" - } - values={{ - count: count - }} - components={{ - 0: , - 1: - }} - />; - `, - }, - { - stripId: true, - name: "Should return cases without leading or trailing spaces for nested Trans inside Plural", - input: ` - import { Trans, Plural } from '@lingui/react/macro'; - - One hello - - } - other={ - - Other hello - - } - value={count} - />; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={ - "{count, plural, one {One hello} other {Other hello}}" - } - values={{ - count: count - }} - />; - `, - }, - { - input: ` - import { Plural } from '@lingui/react/macro'; - A lot of them} - />; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans render="strong" id="msg.plural" message={"{count, plural, offset:1 =0 {Zero items} few {{count} items} other {<0>A lot of them}}"} values={{ - count: count - }} components={{ - 0: - }} />; - `, - }, - { - input: ` - import { Trans, Plural } from '@lingui/react/macro'; - - Looking for{" "} - a lot of them} - /> - - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id="inner-id-removed" message={ - "Looking for {0, plural, offset:1 =0 {zero items} few {{1} items {2}} other {<0>a lot of them}}" - } - values={{ - 0: items.length, - 1: items.length, - 2: 42 - }} components={{ - 0: - }} />; - `, - }, - { - input: ` - import { Plural } from '@lingui/react/macro'; - A lot of them} - />; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={"EQvNfC"} - message={ - "{count, plural, =0 {Zero items} one {{oneText}} other {<0>A lot of them}}" - } - values={{ - count: count, - oneText: oneText, - }} - components={{ - 0: - }} - />; - `, - }, - { - filename: `jsx-plural-select-nested.js`, - }, -] -export default cases diff --git a/packages/babel-plugin-lingui-macro/test/jsx-select.test.ts b/packages/babel-plugin-lingui-macro/test/jsx-select.test.ts new file mode 100644 index 000000000..fc5ab05b6 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/jsx-select.test.ts @@ -0,0 +1,59 @@ +import { macroTester } from "./macroTester" + +macroTester({ + cases: [ + { + code: ` + import { Select } from '@lingui/react/macro'; + Other} + />; + `, + }, + { + name: "Select should support JSX elements in cases", + code: ` + import { Select, Trans } from '@lingui/react/macro'; + ; + `, + }, + ], +}) diff --git a/packages/babel-plugin-lingui-macro/test/jsx-select.ts b/packages/babel-plugin-lingui-macro/test/jsx-select.ts deleted file mode 100644 index 6b8175cd4..000000000 --- a/packages/babel-plugin-lingui-macro/test/jsx-select.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { TestCase } from "./index" - -const cases: TestCase[] = [ - { - stripId: true, - input: ` - import { Select } from '@lingui/react/macro'; - Other} - />; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans render="strong" id="msg.select" message={"{0, select, male {He} female {She} other {<0>Other}}"} values={{ - 0: user.gender - }} components={{ - 0: - }} />; - `, - }, - { - stripId: true, - name: "Select should support JSX elements in cases", - input: ` - import { Select, Trans } from '@lingui/react/macro'; - ; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans render="strong" id="msg.select" message={"{0, select, male {He} female {She} other {{otherText}}}"} values={{ - 0: user.gender, - otherText: otherText - }} />; - `, - }, -] -export default cases diff --git a/packages/babel-plugin-lingui-macro/test/jsx-selectOrdinal.test.ts b/packages/babel-plugin-lingui-macro/test/jsx-selectOrdinal.test.ts new file mode 100644 index 000000000..1c2432d69 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/jsx-selectOrdinal.test.ts @@ -0,0 +1,47 @@ +import { macroTester } from "./macroTester" + +macroTester({ + cases: [ + { + code: ` + import { Trans, SelectOrdinal } from '@lingui/react/macro'; + + This is my #rd} + /> cat. + ; + `, + }, + { + // without trailing whitespace ICU expression on the next line will not have a space + code: ` + import { Trans, SelectOrdinal } from '@lingui/react/macro'; + + This is my + #rd} + /> cat. + ; + `, + }, + { + code: ` + import { Trans, SelectOrdinal } from '@lingui/react/macro'; + + This is my #rd} + /> cat. + ; + `, + }, + ], +}) diff --git a/packages/babel-plugin-lingui-macro/test/jsx-selectOrdinal.ts b/packages/babel-plugin-lingui-macro/test/jsx-selectOrdinal.ts deleted file mode 100644 index 8b1814527..000000000 --- a/packages/babel-plugin-lingui-macro/test/jsx-selectOrdinal.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { TestCase } from "./index" - -const cases: TestCase[] = [ - { - stripId: true, - input: ` - import { Trans, SelectOrdinal } from '@lingui/react/macro'; - - This is my #rd} - /> cat. - ; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={""} message={ - "This is my {count, selectordinal, one {#st} two {#nd} other {<0>#rd}} cat." - } - values={{ - count: count - }} components={{ - 0: - }} />; - `, - }, - { - // without trailing whitespace ICU expression on the next line will not have a space - stripId: true, - input: ` - import { Trans, SelectOrdinal } from '@lingui/react/macro'; - - This is my - #rd} - /> cat. - ; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={""} message={ - "This is my{count, selectordinal, one {#st} two {#nd} other {<0>#rd}} cat." - } - values={{ - count: count - }} components={{ - 0: - }} />; - `, - }, - { - stripId: true, - input: ` - import { Trans, SelectOrdinal } from '@lingui/react/macro'; - - This is my #rd} - /> cat. - ; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={"" } - message={ - "This is my {0, selectordinal, one {#st} two {#nd} other {<0>#rd}} cat." - } - values={{ - 0: user.numCats - }} - components={{ - 0: - }} - />; - `, - }, -] -export default cases diff --git a/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts b/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts new file mode 100644 index 000000000..f8f3dcb15 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/jsx-trans.test.ts @@ -0,0 +1,275 @@ +import { macroTester } from "./macroTester" + +macroTester({ + cases: [ + { + name: "Generate ID from message", + code: ` + import { Trans } from '@lingui/react/macro'; + Hello World; + `, + }, + { + name: "Generate different id when context provided", + code: ` + import { Trans } from '@lingui/react/macro'; + Hello World; + Hello World; + `, + }, + { + name: "Preserve custom ID (string literal)", + code: ` + import { Trans } from '@lingui/react/macro'; + Hello World; + `, + }, + { + name: "Preserve custom ID (literal expression)", + code: ` + import { Trans } from '@lingui/react/macro'; + Hello World; + `, + }, + { + name: "Preserve custom ID (template expression)", + code: ` + import { Trans } from '@lingui/react/macro'; + Hello World; + `, + }, + { + name: "Should preserve reserved props: `comment`, `context`, `render`, `id`", + code: ` + import { Trans } from '@lingui/react/macro'; + {}} + >Hello World; + `, + }, + { + name: "Trans macro could be renamed", + code: ` + import { Trans as Trans2 } from '@lingui/react/macro'; + Hello World; + `, + }, + { + name: "Variables are converted to named arguments", + code: ` + import { Trans } from '@lingui/react/macro'; + Hi {yourName}, my name is {myName}; + `, + }, + { + name: "Variables are deduplicated", + code: ` + import { Trans } from '@lingui/react/macro'; + {duplicate} variable {duplicate}; + `, + }, + { + name: "Quoted JSX attributes are handled", + code: ` + import { Trans } from '@lingui/react/macro'; + Speak "friend"!; + Speak "friend"!; + `, + }, + { + name: "HTML attributes are handled", + code: ` + import { Trans } from '@lingui/react/macro'; + + This should work   + ; + `, + }, + { + name: "Template literals as children", + code: ` + import { Trans } from '@lingui/react/macro'; + {\`How much is \${expression}? \${count}\`}; + `, + }, + { + name: "Strings as children are preserved", + code: ` + import { Trans } from '@lingui/react/macro'; + {"hello {count, plural, one {world} other {worlds}}"}; + `, + }, + { + name: "Expressions are converted to positional arguments", + code: ` + import { Trans } from '@lingui/react/macro'; + + Property {props.name}, + function {random()}, + array {array[index]}, + constant {42}, + object {new Date()}, + everything {props.messages[index].value()} + ; + `, + }, + { + name: "JSX Macro inside JSX conditional expressions", + code: ` + import { Trans } from '@lingui/react/macro'; + Hello, {props.world ? world : guys} + `, + }, + { + name: "JSX Macro inside JSX multiple nested conditional expressions", + code: ` + import { Trans } from '@lingui/react/macro'; + Hello, {props.world ? world : ( + props.b + ? nested + : guys + ) + } + `, + }, + { + name: "Elements are replaced with placeholders", + code: ` + import { Trans } from '@lingui/react/macro'; + + Hello World!
+

+ My name is {" "} + {name} +

+ ; + `, + }, + { + name: "Elements inside expression container", + code: ` + import { Trans } from '@lingui/react/macro'; + {Component inside expression container}; + `, + }, + { + name: "Elements without children", + code: ` + import { Trans } from '@lingui/react/macro'; + {
}
; + `, + }, + { + name: "Production - only essential props are kept", + production: true, + code: ` + import { Trans } from '@lingui/react/macro'; + Hello World + `, + }, + { + name: "Production - all props kept if extract: true", + production: true, + macroOpts: { + extract: true, + }, + code: ` + import { Trans } from '@lingui/react/macro'; + Hello World + `, + }, + { + name: "Production - import type doesn't interference on normal import", + production: true, + useTypescriptPreset: true, + code: ` + import type { withI18nProps } from '@lingui/react' + import { Trans } from '@lingui/react/macro'; + Hello World + `, + }, + { + name: "Strip whitespace around arguments", + code: ` + import { Trans } from "@lingui/react/macro"; + + Strip whitespace around arguments: ' + {name} + ' + + `, + }, + { + name: "Strip whitespace around tags but keep forced spaces", + code: ` + import { Trans } from "@lingui/react/macro"; + + Strip whitespace around tags, but keep{" "} + forced spaces + ! + + `, + }, + { + name: "Strip whitespace around tags but keep whitespaces in JSX containers", + code: ` + import { Trans } from "@lingui/react/macro"; + + {"Wonderful framework "} + Next.js + {" say hi. And "} + Next.js + {" say hi."} + + `, + }, + { + name: "Keep forced newlines", + filename: "./jsx-keep-forced-newlines.js", + }, + { + name: "Use a js macro inside a JSX Attribute of a component handled by JSX macro", + code: ` + import { Trans } from '@lingui/react/macro'; + import { t } from '@lingui/core/macro'; + Read more + `, + }, + { + name: "Use a js macro inside a JSX Attribute of a non macro JSX component", + code: ` + import { plural } from '@lingui/core/macro'; + About + `, + }, + { + name: "Ignore JSXEmptyExpression", + code: ` + import { Trans } from '@lingui/react/macro'; + Hello {/* and I cannot stress this enough */} World; + `, + }, + { + name: "Use decoded html entities", + code: ` + import { Trans } from "@lingui/react/macro"; + & + `, + }, + { + name: "Should not process non JSXElement nodes", + useTypescriptPreset: true, + code: ` + import { Trans } from "@lingui/react/macro"; + type X = typeof Trans; + const cmp = Hello + `, + }, + ], +}) diff --git a/packages/babel-plugin-lingui-macro/test/jsx-trans.ts b/packages/babel-plugin-lingui-macro/test/jsx-trans.ts deleted file mode 100644 index 6c84fd564..000000000 --- a/packages/babel-plugin-lingui-macro/test/jsx-trans.ts +++ /dev/null @@ -1,551 +0,0 @@ -import { TestCase } from "./index" - -const cases: TestCase[] = [ - { - name: "Generate ID from message", - input: ` - import { Trans } from '@lingui/react/macro'; - Hello World; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={"mY42CM"} message={"Hello World"} />; - `, - }, - { - name: "Generate different id when context provided", - input: ` - import { Trans } from '@lingui/react/macro'; - Hello World; - Hello World; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={"mY42CM"} message={"Hello World"} />; - <_Trans id={"SO/WB8"} message={"Hello World"} context="my context" />; - `, - }, - { - name: "Preserve custom ID (string literal)", - input: ` - import { Trans } from '@lingui/react/macro'; - Hello World; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id="msg.hello" message={"Hello World"} />; - `, - }, - { - name: "Preserve custom ID (literal expression)", - input: ` - import { Trans } from '@lingui/react/macro'; - Hello World; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id="msg.hello" message={"Hello World"} />; - `, - }, - { - name: "Preserve custom ID (template expression)", - input: ` - import { Trans } from '@lingui/react/macro'; - Hello World; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id="msg.hello" message={"Hello World"} />; - `, - }, - { - name: "Should preserve reserved props: `comment`, `context`, `render`, `id`", - input: ` - import { Trans } from '@lingui/react/macro'; - {}} - >Hello World; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - render={() => {}} - id="custom.id" - message={"Hello World"} - comment="Comment for translator" - context="translation context" - />; - `, - }, - { - stripId: true, - name: "Trans macro could be renamed", - input: ` - import { Trans as Trans2 } from '@lingui/react/macro'; - Hello World; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={""} message={"Hello World"} />; - `, - }, - { - name: "Variables are converted to named arguments", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - Hi {yourName}, my name is {myName}; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"Hi {yourName}, my name is {myName}"} - values={{ - yourName: yourName, - myName: myName, - }} - />; - `, - }, - { - name: "Variables are deduplicated", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - {duplicate} variable {duplicate}; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"{duplicate} variable {duplicate}"} - values={{ - duplicate: duplicate - }} - />; - `, - }, - { - name: "Quoted JSX attributes are handled", - input: ` - import { Trans } from '@lingui/react/macro'; - Speak "friend"!; - Speak "friend"!; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={"NWmRwM"} message={'Speak "friend"!'} />; - <_Trans id="custom-id" message={'Speak "friend"!'} />; - `, - }, - { - name: "HTML attributes are handled", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - - This should work   - ; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"<0>This should work \\xA0"} - components={{ - 0: , - }} - />; - `, - }, - { - name: "Template literals as children", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - {\`How much is \${expression}? \${count}\`}; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"How much is {expression}? {count}"} values={{ - expression: expression, - count: count - }} - />; - `, - }, - { - name: "Strings as children are preserved", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - {"hello {count, plural, one {world} other {worlds}}"}; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"hello {count, plural, one {world} other {worlds}}"} - />; - `, - }, - { - name: "Expressions are converted to positional arguments", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - - Property {props.name}, - function {random()}, - array {array[index]}, - constant {42}, - object {new Date()}, - everything {props.messages[index].value()} - ; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"Property {0}, function {1}, array {2}, constant {3}, object {4}, everything {5}"} - values={{ - 0: props.name, - 1: random(), - 2: array[index], - 3: 42, - 4: new Date(), - 5: props.messages[index].value() - }} - />; - `, - }, - { - name: "JSX Macro inside JSX conditional expressions", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - Hello, {props.world ? world : guys} - `, - expected: ` - import { Trans as _Trans } from '@lingui/react'; - - <_Trans - id={''} - message={"Hello, {0}"} - values={{ - 0: props.world ? <_Trans id={""} message={'world'} /> : <_Trans id={''} message={'guys'} /> - }} - /> - `, - }, - { - name: "JSX Macro inside JSX multiple nested conditional expressions", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - Hello, {props.world ? world : ( - props.b - ? nested - : guys - ) - } - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"Hello, {0}"} - values={{ - 0: props.world ? ( - <_Trans id={""} message={"world"} /> - ) : props.b ? ( - <_Trans id={""} message={"nested"} /> - ) : ( - <_Trans id={""} message={"guys"} /> - ), - }} - />; - `, - }, - { - name: "Elements are replaced with placeholders", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - - Hello World!
-

- My name is {" "} - {name} -

-
; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"Hello <0>World!<1/><2>My name is <3> <4>{name}"} - values={{ - name: name - }} - components={{ - 0: , - 1:
, - 2:

, - 3: , - 4: - }} - />; - `, - }, - { - name: "Elements inside expression container", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - {Component inside expression container}; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"<0>Component inside expression container"} - components={{ - 0: - }} - />; - `, - }, - { - name: "Elements without children", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - {
}
; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={""} message={"<0/>"} components={{ - 0:
- }} />; - `, - }, - { - name: "Production - only essential props are kept", - production: true, - input: ` - import { Trans } from '@lingui/react/macro'; - Hello World - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id="msg.hello" />; - `, - }, - { - name: "Production - all props kept if extract: true", - production: true, - macroOpts: { - extract: true, - }, - input: ` - import { Trans } from '@lingui/react/macro'; - Hello World - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id="msg.hello" message={"Hello World"} comment="Hello World"/>; - `, - }, - { - name: "Production - import type doesn't interference on normal import", - production: true, - useTypescriptPreset: true, - input: ` - import type { withI18nProps } from '@lingui/react' - import { Trans } from '@lingui/react/macro'; - Hello World - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id="msg.hello" />; - `, - }, - { - name: "Strip whitespace around arguments", - stripId: true, - input: ` - import { Trans } from "@lingui/react/macro"; - - Strip whitespace around arguments: ' - {name} - ' - - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={""} message={"Strip whitespace around arguments: '{name}'"} values={{ - name: name - }} />; - `, - }, - { - name: "Strip whitespace around tags but keep forced spaces", - stripId: true, - input: ` - import { Trans } from "@lingui/react/macro"; - - Strip whitespace around tags, but keep{" "} - forced spaces - ! - - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={""} message={"Strip whitespace around tags, but keep <0>forced spaces!"} components={{ - 0: - }} />; - `, - }, - { - name: "Strip whitespace around tags but keep whitespaces in JSX containers", - stripId: true, - input: ` - import { Trans } from "@lingui/react/macro"; - - {"Wonderful framework "} -
Next.js - {" say hi. And "} - Next.js - {" say hi."} - - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; -<_Trans - id={""} - message={ - "Wonderful framework <0>Next.js say hi. And <1>Next.js say hi." - } - components={{ - 0: , - 1: , - }} -/>; -; - `, - }, - { - name: "Keep forced newlines", - stripId: true, - filename: "./jsx-keep-forced-newlines.js", - }, - { - name: "Use a js macro inside a JSX Attribute of a component handled by JSX macro", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - import { t } from '@lingui/core/macro'; - Read more - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - import { Trans as _Trans } from "@lingui/react"; - <_Trans - id={""} - message={"Read <0>more"} - components={{ - 0: ( - - ), - }} - />; - - `, - }, - { - name: "Use a js macro inside a JSX Attribute of a non macro JSX component", - input: ` - import { plural } from '@lingui/core/macro'; - About - `, - expected: ` - import { i18n as _i18n } from "@lingui/core"; - - About - ; - `, - }, - { - name: "Ignore JSXEmptyExpression", - stripId: true, - input: ` - import { Trans } from '@lingui/react/macro'; - Hello {/* and I cannot stress this enough */} World; - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={""} message={"Hello World"} />; - `, - }, - { - name: "Use decoded html entities", - stripId: true, - input: ` - import { Trans } from "@lingui/react/macro"; - & - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - <_Trans id={""} message={"&"} />; - `, - }, - { - name: "Should not process non JSXElement nodes", - useTypescriptPreset: true, - stripId: true, - input: ` - import { Trans } from "@lingui/react/macro"; - type X = typeof Trans; - const cmp = Hello - `, - expected: ` - import { Trans as _Trans } from "@lingui/react"; - const cmp = <_Trans id={""} message={"Hello"} />; - `, - }, -] -export default cases diff --git a/packages/babel-plugin-lingui-macro/test/macroTester.ts b/packages/babel-plugin-lingui-macro/test/macroTester.ts new file mode 100644 index 000000000..02f0a1637 --- /dev/null +++ b/packages/babel-plugin-lingui-macro/test/macroTester.ts @@ -0,0 +1,150 @@ +// use package path instead relative because we want +// to test it in from /dist folder in integration tests +import linguiMacroPlugin, { + LinguiPluginOpts, +} from "@lingui/babel-plugin-lingui-macro" +import { transformFileSync, transformSync, TransformOptions } from "@babel/core" +import prettier from "prettier" +import path from "path" +import fs from "fs" + +export type TestCase = TestCaseInline | TestCaseFixture + +type TestCaseInline = { + /** + * Input code for testing + */ + code: string + /** + * If not set, snapshot testing will be used + */ + expected?: string +} & TestCaseCommon + +type TestCaseFixture = { + filename: string +} & TestCaseCommon + +type TestCaseCommon = { + name?: string + production?: boolean + useTypescriptPreset?: boolean + macroOpts?: LinguiPluginOpts + only?: boolean + skip?: boolean +} + +export type MacroTesterOptions = { + cases: TestCase[] +} + +export function macroTester(opts: MacroTesterOptions) { + process.env.LINGUI_CONFIG = path.join(__dirname, "lingui.config.js") + + const clean = (value: string) => + prettier.format(value, { parser: "babel-ts" }).replace(/\n+/, "\n") + + opts.cases.forEach((testCase, index) => { + const { name, production, only, skip, useTypescriptPreset, macroOpts } = + testCase + + let group = test + if (only) group = test.only + if (skip) group = test.skip + const groupName = name != null ? name : `#${index + 1}` + + group(groupName, () => { + const originalEnv = process.env.NODE_ENV + + if (production) { + process.env.NODE_ENV = "production" + } + + try { + if ("filename" in testCase) { + const inputPath = path.relative( + process.cwd(), + path.join(__dirname, "fixtures", testCase.filename) + ) + const expectedPath = inputPath.replace(/\.js$/, ".expected.js") + const expected = fs + .readFileSync(expectedPath, "utf8") + .replace(/\r/g, "") + .trim() + + const actualPlugin = transformFileSync(inputPath, { + ...getDefaultBabelOptions("plugin", macroOpts, useTypescriptPreset), + cwd: path.dirname(inputPath), + }) + .code.replace(/\r/g, "") + .trim() + + const actualMacro = transformFileSync(inputPath, { + ...getDefaultBabelOptions("plugin", macroOpts, useTypescriptPreset), + cwd: path.dirname(inputPath), + }) + .code.replace(/\r/g, "") + .trim() + + // output from plugin transformation should be the same to macro transformation + expect(actualPlugin).toBe(actualMacro) + + expect(clean(actualPlugin)).toEqual(clean(expected)) + } else { + const actualPlugin = transformSync( + testCase.code, + getDefaultBabelOptions("plugin", macroOpts, useTypescriptPreset) + ).code.trim() + + const actualMacro = transformSync( + testCase.code, + getDefaultBabelOptions("macro", macroOpts, useTypescriptPreset) + ).code.trim() + + // output from plugin transformation should be the same to macro transformation + expect(actualPlugin).toBe(actualMacro) + + if (testCase.expected) { + expect(clean(actualPlugin)).toEqual(clean(testCase.expected)) + } else { + expect( + clean(testCase.code) + "\n↓ ↓ ↓ ↓ ↓ ↓\n\n" + clean(actualPlugin) + ).toMatchSnapshot() + } + } + } finally { + process.env.LINGUI_CONFIG = "" + process.env.NODE_ENV = originalEnv + } + }) + }) +} + +export const getDefaultBabelOptions = ( + transformType: "plugin" | "macro" = "plugin", + macroOpts: LinguiPluginOpts = {}, + isTs: boolean = false +): TransformOptions => { + return { + filename: "" + (isTs ? ".tsx" : "jsx"), + configFile: false, + babelrc: false, + presets: [...(isTs ? ["@babel/preset-typescript"] : [])], + plugins: [ + "@babel/plugin-syntax-jsx", + transformType === "plugin" + ? [linguiMacroPlugin, macroOpts] + : [ + "macros", + { + lingui: macroOpts, + // 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), + }, + ], + ], + } +}