Repository: lingui/js-lingui
Pull request #1355 (2023-01-22 12:25:28)

feat(macro): throw useful error message if macro used without a plugin

vercel[bot]: [vc]: #eTXFYQAZWK3daxrKDbhER+VI20Bxiolqr0R2qWd7Ico=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJqcy1saW5ndWkiLCJyb290RGlyZWN0b3J5IjpudWxsLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vbGluZ3VpL2pzLWxpbmd1aS9BQ3l2ZTNVVlFjN2kxdFVkRW85Wjh0TWEzeVduIiwicHJldmlld1VybCI6IiIsIm5leHRDb21taXRTdGF0dXMiOiJQRU5ESU5HIn1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Updated |
| :--- | :----- | :------ | :------ |
| **js-lingui** | 🔄 Building ([Inspect](https://vercel.com/lingui/js-lingui/ACyve3UVQc7i1tUdEo9Z8tMa3yWn)) |  | Jan 22, 2023 at 0:25AM (UTC) |


github-actions[bot]: ## size-limit report 📦 
| Path                                        | Size         |
| ------------------------------------------- | ------------ |
| ./packages/core/build/esm/index.js          | 2.89 KB (0%) |
| ./packages/detect-locale/build/esm/index.js | 804 B (0%)   |
| ./packages/react/build/esm/index.js         | 2.61 KB (0%) |
| ./packages/remote-loader/build/esm/index.js | 7.37 KB (0%) |
thekip: @Martin005 done

Patch:
diff --git a/packages/macro/src/index.ts b/packages/macro/src/index.ts
index 643436e5..83974660 100644
--- a/packages/macro/src/index.ts
+++ b/packages/macro/src/index.ts
@@ -27,6 +27,22 @@ const getSymbolSource = (name: 'i18n' | 'Trans'): [source: string, identifier?:
 const [i18nImportModule, i18nImportName = "i18n"] = getSymbolSource("i18n")
 const [TransImportModule, TransImportName = "Trans"] = getSymbolSource("Trans")
 
+const jsMacroTags = new Set([
+  'defineMessage',
+  'arg',
+  't',
+  'plural',
+  'select',
+  'selectOrdinal'
+])
+
+const jsxMacroTags = new Set([
+  'Trans',
+  'Plural',
+  'Select',
+  'SelectOrdinal',
+])
+
 function macro({ references, state, babel }: MacroParams) {
   const jsxNodes: NodePath[] = []
   const jsNodes: NodePath[] = []
@@ -34,20 +50,18 @@ function macro({ references, state, babel }: MacroParams) {
 
   Object.keys(references).forEach((tagName) => {
     const nodes = references[tagName]
-    const macroType = getMacroType(tagName)
-    if (macroType == null) {
-      throw nodes[0].buildCodeFrameError(`Unknown macro ${tagName}`)
-    }
 
-    if (macroType === "js") {
+    if (jsMacroTags.has(tagName)) {
       nodes.forEach((node) => {
         jsNodes.push(node.parentPath)
       })
-    } else {
+    } else if(jsxMacroTags.has(tagName)) {
       nodes.forEach((node) => {
         // identifier.openingElement.jsxElement
         jsxNodes.push(node.parentPath.parentPath)
       })
+    } else {
+      throw nodes[0].buildCodeFrameError(`Unknown macro ${tagName}`)
     }
   })
 
@@ -131,21 +145,15 @@ const alreadyVisited = (path: NodePath) => {
   }
 }
 
-function getMacroType(tagName: string): string {
-  switch (tagName) {
-    case "defineMessage":
-    case "arg":
-    case "t":
-    case "plural":
-    case "select":
-    case "selectOrdinal":
-      return "js"
-    case "Trans":
-    case "Plural":
-    case "Select":
-    case "SelectOrdinal":
-      return "jsx"
-  }
-}
+[...jsMacroTags, ...jsxMacroTags].forEach((name) => {
+  Object.defineProperty(module.exports, name, {
+    get() {
+      throw new Error(`The macro you imported from "@lingui/macro" is being executed outside the context of compilation with babel-plugin-macros. `
+        + `This indicates that you don't have the babel plugin "babel-plugin-macros" configured correctly. `
+        + `Please see the documentation for how to configure babel-plugin-macros properly: `
+        + 'https://github.com/kentcdodds/babel-plugin-macros/blob/main/other/docs/user.md');
+    }
+  })
+})
 
 export default createMacro(macro)
diff --git a/scripts/verdaccio-release.js b/scripts/verdaccio-release.js
index 5372a634..db02f6af 100644
--- a/scripts/verdaccio-release.js
+++ b/scripts/verdaccio-release.js
@@ -1,7 +1,6 @@
 const { exec: _exec } = require("child_process")
 const chalk = require("chalk")
 const ora = require("ora")
-const os = require("os")
 
 async function releaseInVerdaccio() {
   const spinner = ora()
@@ -27,13 +26,8 @@ async function releaseInVerdaccio() {
   spinner.start(
     "Dont't mark files as changed to let publish, but not create a git history"
   )
-  if (isWindows()) {
-    await exec(
-      `for /f "" %f in ('git ls-files -m') do git update-index --assume-unchanged %f`
-    )
-  } else {
-    await exec("git ls-files -m | xargs git update-index --assume-unchanged")
-  }
+  await exec("git commit --all -m \"TEMP: verdaccio release\"")
+
   spinner.succeed()
 
   spinner.start("Publishing packages to local registry")
@@ -44,15 +38,7 @@ async function releaseInVerdaccio() {
   spinner.succeed()
 
   spinner.start("Reverting the changed files from index")
-  if (isWindows()) {
-    await exec(
-      `for /f "tokens=2" %f in ('git ls-files -v ^| findstr /R /C:"^[abcdefghijklmnopqrstuvwxyz]"') do git update-index --no-assume-unchanged %f --"`
-    )
-  } else {
-    await exec(
-      "git ls-files -v | grep '^[a-z]' | cut -c3- | xargs git update-index --no-assume-unchanged --"
-    )
-  }
+  await exec("git reset --soft HEAD~1")
   spinner.succeed()
 
   console.log()
@@ -63,10 +49,6 @@ async function releaseInVerdaccio() {
   )
 }
 
-function isWindows() {
-  return os.platform() === "win32"
-}
-
 function exec(cmd, options) {
   const _options = {
     env: {


Test patch:
diff --git a/packages/macro/test/index.ts b/packages/macro/test/index.ts
index 7698d6bd..038b065d 100644
--- a/packages/macro/test/index.ts
+++ b/packages/macro/test/index.ts
@@ -28,6 +28,8 @@ const testCases: Record<string, TestCase[]> = {
 }
 
 describe("macro", function () {
+  process.env.LINGUI_CONFIG = path.join(__dirname, "lingui.config.js")
+
   const babelOptions: TransformOptions = {
     filename: "<filename>",
     configFile: false,
@@ -54,7 +56,16 @@ describe("macro", function () {
 
       cases.forEach(
         (
-          { name, input, expected, filename, production, useTypescriptPreset, only, skip },
+          {
+            name,
+            input,
+            expected,
+            filename,
+            production,
+            useTypescriptPreset,
+            only,
+            skip,
+          },
           index
         ) => {
           let run = it
@@ -73,8 +84,6 @@ describe("macro", function () {
               babelOptions.presets.push("@babel/preset-typescript")
             }
 
-            process.env.LINGUI_CONFIG = path.join(__dirname, "lingui.config.js")
-
             try {
               if (filename) {
                 const inputPath = path.relative(
@@ -111,6 +120,17 @@ describe("macro", function () {
     })
   })
 
+  it("Should throw error if used without babel-macro-plugin", async () => {
+    await expect(
+      async () => {
+        const mod = await import("../src/index");
+        return (mod as unknown as typeof import('@lingui/macro')).Trans
+      }
+    ).rejects.toThrow(
+      'The macro you imported from "@lingui/macro"'
+    )
+  })
+
   describe.skip("validation", function () {
     describe("plural/select/selectordinal", function () {
       it("value is missing", function () {