Repository: lingui/js-lingui
Pull request #509 (2019-05-21 13:01:22)

Fixes --config option processing
### Overview

Processing process.argv inside getConfig can cause problems when used in conjunction with @lingui/loader since the --config arg is also used by webpack

Instead of processing process.argv, getConfig now accepts the path to a custom config file. Each command now passes the config option from commander into getConfig. Webpack loader now accepts an option "config" that can be used to specify a config file.

### Notes

Should all lingui options be allowed via webpack loader options? loader could be modified to assign all options to getConfig output.

I think I fixed an unrelated bug with lingui/loader. compileNamespace and pseudoLocale config options  were not being passed to createCompiledCatalog, so I assume that all loader compiled output was being exported as cjs.

### Testing

I smoke tested the fix by implementing lingui/loader with a config option while simultaneously using the --config option with webpack. I also verified that `lingui extract --config` continued to function as expected.


Patch:
diff --git a/docs/rfc/003_per_locale_bundles.rst b/docs/rfc/003_per_locale_bundles.rst
index 0270e764..0f19b490 100644
--- a/docs/rfc/003_per_locale_bundles.rst
+++ b/docs/rfc/003_per_locale_bundles.rst
@@ -107,7 +107,7 @@ specific plural rules:
                value={count}
                one={<>Máte <strong>#</strong> nepřečtenou zprávu</>}
                few={<>Máte <strong>#</strong> nepřečtené zprávy</>}
-               other={<>Máte <strong>#</strong> nepřečtené zprávy</>}
+               other={<>Máte <strong>#</strong> nepřečtených zpráv</>}
             />
          </p>
       </div>
diff --git a/packages/cli/src/lingui-add-locale.js b/packages/cli/src/lingui-add-locale.js
index 3be4428a..f300bf74 100644
--- a/packages/cli/src/lingui-add-locale.js
+++ b/packages/cli/src/lingui-add-locale.js
@@ -55,7 +55,7 @@ if (require.main === module) {
 
   if (!program.args.length) program.help()
 
-  const config = getConfig()
+  const config = getConfig({ configPath: program.config })
   if (program.format) {
     const msg =
       "--format option is deprecated and will be removed in @lingui/cli@3.0.0." +
diff --git a/packages/cli/src/lingui-compile.js b/packages/cli/src/lingui-compile.js
index 8ae5fe23..6a7a6cf5 100644
--- a/packages/cli/src/lingui-compile.js
+++ b/packages/cli/src/lingui-compile.js
@@ -140,7 +140,7 @@ if (require.main === module) {
     })
     .parse(process.argv)
 
-  const config = getConfig()
+  const config = getConfig({ configPath: program.config })
 
   if (program.format) {
     const msg =
diff --git a/packages/cli/src/lingui-extract.js b/packages/cli/src/lingui-extract.js
index 73907b50..31715419 100644
--- a/packages/cli/src/lingui-extract.js
+++ b/packages/cli/src/lingui-extract.js
@@ -149,7 +149,7 @@ if (require.main === module) {
     )
     .parse(process.argv)
 
-  const config = getConfig()
+  const config = getConfig({ configPath: program.config })
 
   if (program.format) {
     const msg =
diff --git a/packages/conf/src/index.js b/packages/conf/src/index.js
index 6a688861..aa51670a 100644
--- a/packages/conf/src/index.js
+++ b/packages/conf/src/index.js
@@ -75,29 +75,17 @@ const configValidation = {
   comment: "See https://lingui.js.org/ref/conf.html for a list of valid options"
 }
 
-function configFilePathFromArgs() {
-  const configIndex = process.argv.indexOf("--config")
-
-  if (
-    configIndex >= 0 &&
-    process.argv.length > configIndex &&
-    fs.existsSync(process.argv[configIndex + 1])
-  ) {
-    return process.argv[configIndex + 1]
-  }
-
-  return null
+function configExists(path) {
+  return path && fs.existsSync(path)
 }
 
-export function getConfig({ cwd } = {}) {
+export function getConfig({ cwd, configPath } = {}) {
   const configExplorer = cosmiconfig("lingui")
   const defaultRootDir = cwd || process.cwd()
-  const configPath = configFilePathFromArgs()
 
-  const result =
-    configPath == null
-      ? configExplorer.searchSync(defaultRootDir)
-      : configExplorer.loadSync(configPath)
+  const result = configExists(configPath)
+    ? configExplorer.loadSync(configPath)
+    : configExplorer.searchSync(defaultRootDir)
 
   const raw = { ...defaultConfig, ...(result ? result.config : {}) }
 
diff --git a/packages/loader/package.json b/packages/loader/package.json
index f6ac2950..d6f23fd4 100644
--- a/packages/loader/package.json
+++ b/packages/loader/package.json
@@ -32,6 +32,7 @@
     "@lingui/cli": "0.0.0-managed-by-release-script",
     "@lingui/conf": "0.0.0-managed-by-release-script",
     "babel-runtime": "^6.26.0",
-    "ramda": "^0.26.1"
+    "ramda": "^0.26.1",
+    "loader-utils": "^1.1.0"
   }
 }
diff --git a/packages/loader/src/index.js b/packages/loader/src/index.js
index 59041162..be49ac3d 100644
--- a/packages/loader/src/index.js
+++ b/packages/loader/src/index.js
@@ -2,6 +2,7 @@ import path from "path"
 import * as R from "ramda"
 import { getConfig } from "@lingui/conf"
 import { createCompiledCatalog, configureCatalog } from "@lingui/cli/api"
+import loaderUtils from "loader-utils"
 
 // Check if JavascriptParser and JavascriptGenerator exists -> Webpack 4
 let JavascriptParser
@@ -16,6 +17,8 @@ try {
 }
 
 export default function(source) {
+  const options = loaderUtils.getOptions(this)
+
   // Webpack 4 uses json-loader automatically, which breaks this loader because it
   // doesn't return JSON, but JS module. This is a temporary workaround before
   // official API is added (https://github.com/webpack/webpack/issues/7057#issuecomment-381883220)
@@ -26,7 +29,10 @@ export default function(source) {
     this._module.generator = new JavascriptGenerator()
   }
 
-  const config = getConfig({ cwd: path.dirname(this.resourcePath) })
+  const config = getConfig({
+    configPath: options.config,
+    cwd: path.dirname(this.resourcePath)
+  })
   const catalog = configureCatalog(config)
 
   const locale = catalog.getLocale(this.resourcePath)
@@ -47,5 +53,11 @@ export default function(source) {
   // of I18nProvider (React) or setupI18n (core) and therefore we need to get
   // empty translations if missing.
   const strict = process.env.NODE_ENV !== "production"
-  return createCompiledCatalog(locale, messages, strict)
+  return createCompiledCatalog(
+    locale,
+    messages,
+    strict,
+    config.compileNamespace,
+    config.pseudoLocale
+  )
 }


Test patch:
diff --git a/packages/conf/src/index.test.js b/packages/conf/src/index.test.js
index 8e822139..65efef8b 100644
--- a/packages/conf/src/index.test.js
+++ b/packages/conf/src/index.test.js
@@ -58,18 +58,9 @@ describe("lingui-conf", function() {
     expect(cosmiconfig().searchSync).toHaveBeenCalled()
   })
 
-  describe("with --config command line argument", function() {
-    beforeEach(function() {
-      process.argv.push("--config")
-      process.argv.push("./lingui/myconfig")
-    })
-
-    afterEach(function() {
-      process.argv.splice(process.argv.length - 2, 2)
-    })
-
+  describe("with configPath parameter", function() {
     it("allows specific config file to be loaded", function() {
-      getConfig()
+      getConfig({ configPath: "./lingui/myconfig" })
       expect(cosmiconfig().searchSync).not.toHaveBeenCalled()
       expect(cosmiconfig().loadSync).toHaveBeenCalledWith("./lingui/myconfig")
     })
diff --git a/packages/loader/test/compiler.js b/packages/loader/test/compiler.js
index f635bc4d..fac42137 100644
--- a/packages/loader/test/compiler.js
+++ b/packages/loader/test/compiler.js
@@ -15,7 +15,8 @@ export default (fixture, options = {}) => {
         {
           test: /\.json$/,
           use: {
-            loader: path.resolve(__dirname, "../src/index.js")
+            loader: path.resolve(__dirname, "../src/index.js"),
+            options
           }
         }
       ]
diff --git a/packages/loader/test/customconfig b/packages/loader/test/customconfig
new file mode 100644
index 00000000..a9b01222
--- /dev/null
+++ b/packages/loader/test/customconfig
@@ -0,0 +1,4 @@
+{
+  "localeDir": "<rootDir>/locale",
+  "compileNamespace": "window.really_long_namespace"
+}
diff --git a/packages/loader/test/loader.test.js b/packages/loader/test/loader.test.js
index 7ba61b69..27c8374b 100644
--- a/packages/loader/test/loader.test.js
+++ b/packages/loader/test/loader.test.js
@@ -20,4 +20,16 @@ describe("lingui-loader", function() {
     expect(output.errors).toEqual([])
     expect(output.modules[0].source).toMatchSnapshot()
   })
+
+  skipOnWindows("should allow config option", async () => {
+    const stats = await compiler(
+      path.join(".", "locale", "en", "messages.json"),
+      { config: `${path.dirname(module.filename)}/customconfig` }
+    )
+
+    const output = stats.toJson()
+
+    // customconfig contains this namespace
+    expect(output.modules[0].source).toMatch(/window\.really_long_namespace=/)
+  })
 })