Repository: lingui/js-lingui
Pull request #1085 (2021-06-14 17:49:47)

fix: remoteLoader pure functional on browser env
Fine tunning of #1080, now we release a new package @lingui/remote-loader, instead of using the @lingui/loader.

Loader dependencies are so much oriented to node environments, for a safer usage and extendability I decided to extract the remoteLoader to a new package with esm, and cjs exports usable on node and browser.

Pending to add documentatio to this new tool, the API it's simple:

```
remoteLoader({ messages, fallbackMessages?, format: 'minimal' }}
```

vercel[bot]: This pull request is being automatically deployed with Vercel ([learn more](https://vercel.link/github-learn-more)).  
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: [https://vercel.com/lingui-js/js-lingui/96YNbkoPo4oMjtG8bvBHeAti7cU2](https://vercel.com/lingui-js/js-lingui/96YNbkoPo4oMjtG8bvBHeAti7cU2)  
✅ Preview: *In Progress*


github-actions[bot]: ## size-limit report 📦 
| Path                                                               | Size               |
| ------------------------------------------------------------------ | ------------------ |
| ./packages/core/build/cjs/core.production.min.js                   | 2.78 KB (0%)       |
| ./packages/detect-locale/build/cjs/detect-locale.production.min.js | 798 B (0%)         |
| ./packages/react/build/cjs/react.production.min.js                 | 4.85 KB (0%)       |
| ./packages/remote-loader/build/cjs/remote-loader.production.min.js | 83.1 KB (+100% 🔺) |
codecov[bot]: # [Codecov](https://codecov.io/gh/lingui/js-lingui/pull/1085?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui) Report
> Merging [#1085](https://codecov.io/gh/lingui/js-lingui/pull/1085?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui) (dc0c935) into [main](https://codecov.io/gh/lingui/js-lingui/commit/d46169598e4b43c5fb5cb686f9b1e8811292e9c6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui) (d461695) will **decrease** coverage by `1.03%`.
> The diff coverage is `53.84%`.

[![Impacted file tree graph](https://codecov.io/gh/lingui/js-lingui/pull/1085/graphs/tree.svg?width=650&height=150&src=pr&token=CuJ1VD5BY5&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui)](https://codecov.io/gh/lingui/js-lingui/pull/1085?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui)

```diff
@@            Coverage Diff             @@
##             main    #1085      +/-   ##
==========================================
- Coverage   82.91%   81.88%   -1.04%     
==========================================
  Files          54       56       +2     
  Lines        1639     1678      +39     
  Branches      448      458      +10     
==========================================
+ Hits         1359     1374      +15     
- Misses        163      182      +19     
- Partials      117      122       +5     
```


| [Impacted Files](https://codecov.io/gh/lingui/js-lingui/pull/1085?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui) | Coverage Δ | |
|---|---|---|
| [packages/remote-loader/src/browserCompiler.ts](https://codecov.io/gh/lingui/js-lingui/pull/1085/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui#diff-cGFja2FnZXMvcmVtb3RlLWxvYWRlci9zcmMvYnJvd3NlckNvbXBpbGVyLnRz) | `45.94% <45.94%> (ø)` | |
| [packages/remote-loader/src/index.ts](https://codecov.io/gh/lingui/js-lingui/pull/1085/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui#diff-cGFja2FnZXMvcmVtb3RlLWxvYWRlci9zcmMvaW5kZXgudHM=) | `66.66% <66.66%> (ø)` | |
| [packages/remote-loader/src/minimalParser.ts](https://codecov.io/gh/lingui/js-lingui/pull/1085/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui#diff-cGFja2FnZXMvcmVtb3RlLWxvYWRlci9zcmMvbWluaW1hbFBhcnNlci50cw==) | `100.00% <100.00%> (ø)` | |
| [packages/cli/src/api/formats/minimal.ts](https://codecov.io/gh/lingui/js-lingui/pull/1085/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui#diff-cGFja2FnZXMvY2xpL3NyYy9hcGkvZm9ybWF0cy9taW5pbWFsLnRz) | `25.00% <0.00%> (-8.34%)` | :arrow_down: |
| [packages/cli/src/api/compile.ts](https://codecov.io/gh/lingui/js-lingui/pull/1085/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui#diff-cGFja2FnZXMvY2xpL3NyYy9hcGkvY29tcGlsZS50cw==) | `96.00% <0.00%> (-4.00%)` | :arrow_down: |
| [packages/conf/src/index.ts](https://codecov.io/gh/lingui/js-lingui/pull/1085/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui#diff-cGFja2FnZXMvY29uZi9zcmMvaW5kZXgudHM=) | `80.76% <0.00%> (ø)` | |
| [packages/cli/src/api/formats/index.ts](https://codecov.io/gh/lingui/js-lingui/pull/1085/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui#diff-cGFja2FnZXMvY2xpL3NyYy9hcGkvZm9ybWF0cy9pbmRleC50cw==) | `60.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/lingui/js-lingui/pull/1085?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/lingui/js-lingui/pull/1085?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui). Last update [8077d99...dc0c935](https://codecov.io/gh/lingui/js-lingui/pull/1085?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=lingui).


Patch:
diff --git a/package.json b/package.json
index 1c330bc7..e694612a 100644
--- a/package.json
+++ b/package.json
@@ -122,6 +122,10 @@
     {
       "path": "./packages/react/build/cjs/react.production.min.js",
       "limit": "5 kB"
+    },
+    {
+      "path": "./packages/remote-loader/build/cjs/remote-loader.production.min.js",
+      "limit": "5 kB"
     }
   ]
 }
diff --git a/packages/loader/index.d.ts b/packages/loader/index.d.ts
index d19498dd..e69de29b 100644
--- a/packages/loader/index.d.ts
+++ b/packages/loader/index.d.ts
@@ -1,3 +0,0 @@
-declare type RemoteLoaderMessages<T> = string | Record<string, any> | T;
-export declare function remoteLoader<T>(locale: string, messages: RemoteLoaderMessages<T>, fallbackMessages?: RemoteLoaderMessages<T>): any;
-export {};
diff --git a/packages/loader/index.js b/packages/loader/index.js
index 39ac9ca5..8b3f0bdc 100644
--- a/packages/loader/index.js
+++ b/packages/loader/index.js
@@ -1,2 +1,1 @@
-export { default } from "./src"
-export { remoteLoader } from "./src/remoteLoader"
\ No newline at end of file
+export { default } from "./src"
\ No newline at end of file
diff --git a/packages/loader/src/index.ts b/packages/loader/src/index.ts
index a4df3dc3..4a141521 100644
--- a/packages/loader/src/index.ts
+++ b/packages/loader/src/index.ts
@@ -1,2 +1,8 @@
 export { default } from "./webpackLoader"
-export { remoteLoader } from "./remoteLoader"
\ No newline at end of file
+
+export function remoteLoader() {
+  console.error(`
+    DEPRECATED: { remoteLoader } from "@lingui/loader"
+    Use instead: { remoteLoader } from "@lingui/remote-loader"
+  `);
+}
\ No newline at end of file
diff --git a/packages/loader/src/remoteLoader.ts b/packages/loader/src/remoteLoader.ts
deleted file mode 100644
index 56de77ae..00000000
--- a/packages/loader/src/remoteLoader.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import R from "ramda"
-import { getConfig } from "@lingui/conf"
-import { createCompiledCatalog, getFormat } from "@lingui/cli/api"
-
-type RemoteLoaderMessages<T> = string | Record<string, any> | T
-
-export function remoteLoader<T>(locale: string, messages: RemoteLoaderMessages<T>, fallbackMessages?: RemoteLoaderMessages<T>) {
-  const config = getConfig()
-
-  // When format is minimal, everything works fine because are .json files,
-  // but when is csv, .po, .po-gettext needs to be parsed to something interpretable
-  let parsedMessages;
-  let parsedFallbackMessages;
-  if (config.format) {
-    const formatter = getFormat(config.format)
-    if (fallbackMessages) {
-      // we do this because, people could just import the fallback and import the ./en/messages.js
-      // generated by lingui and the use case of format .po but fallback as .json module could be perfectly valid
-      parsedFallbackMessages = typeof fallbackMessages === "object" ? getFormat("minimal").parse(fallbackMessages) : formatter.parse(fallbackMessages)
-    }
-
-    parsedMessages = formatter.parse(messages)
-  } else {
-    throw new Error(`
-        *format* value in the Lingui configuration is required to make this loader 100% functional
-        Read more about this here: https://lingui.js.org/ref/conf.html#format
-      `)
-  }
-
-
-  const mapTranslationsToInterporlatedString = R.mapObjIndexed(
-    (_, key) => {
-      // if there's fallback and translation is empty, return the fallback
-      if (parsedMessages[key].translation === "" && parsedFallbackMessages?.[key]?.translation) {
-        return parsedFallbackMessages[key].translation
-      }
-
-      return parsedMessages[key].translation
-    },
-    parsedMessages
-  )
-
-  // In production we don't want untranslated strings. It's better to use message
-  // keys as a last resort.
-  // In development, however, we want to catch missing strings with `missing` parameter
-  // 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, mapTranslationsToInterporlatedString, {
-    strict,
-    ...config,
-    namespace: config.compileNamespace,
-    pseudoLocale: config.pseudoLocale,
-    // important, because without it returns an export statement
-    pure: true,
-  })
-
-}
-
diff --git a/packages/remote-loader/CHANGELOG.md b/packages/remote-loader/CHANGELOG.md
new file mode 100644
index 00000000..767840ad
--- /dev/null
+++ b/packages/remote-loader/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+
diff --git a/packages/remote-loader/README.md b/packages/remote-loader/README.md
new file mode 100644
index 00000000..56b1d20a
--- /dev/null
+++ b/packages/remote-loader/README.md
@@ -0,0 +1,5 @@
+[![License][badge-license]][license]
+[![Version][badge-version]][package]
+[![Downloads][badge-downloads]][package]
+
+# @lingui/remote-loader
diff --git a/packages/remote-loader/index.js b/packages/remote-loader/index.js
new file mode 100644
index 00000000..46e72b16
--- /dev/null
+++ b/packages/remote-loader/index.js
@@ -0,0 +1,1 @@
+export * from "./src"
diff --git a/packages/remote-loader/npm/index.js b/packages/remote-loader/npm/index.js
new file mode 100644
index 00000000..9e12b1ca
--- /dev/null
+++ b/packages/remote-loader/npm/index.js
@@ -0,0 +1,5 @@
+if (process.env.NODE_ENV === "production") {
+  module.exports = require("./cjs/remote-loader.production.min.js")
+} else {
+  module.exports = require("./cjs/remote-loader.development.js")
+}
diff --git a/packages/remote-loader/package.json b/packages/remote-loader/package.json
new file mode 100644
index 00000000..2f68f8d7
--- /dev/null
+++ b/packages/remote-loader/package.json
@@ -0,0 +1,48 @@
+{
+  "name": "@lingui/remote-loader",
+  "version": "3.10.2",
+  "description": "Load remotely your translations",
+  "main": "index.js",
+  "module": "esm/remote-loader.production.min.js",
+  "types": "cjs/index.d.ts",
+  "author": {
+    "name": "Sergio Moreno",
+    "email": "sergiomorenoalbert@gmail.com"
+  },
+  "license": "MIT",
+  "keywords": [
+    "remotely",
+    "component",
+    "i18n",
+    "internationalization",
+    "i9n",
+    "translation",
+    "icu",
+    "messageformat"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/lingui/js-lingui.git"
+  },
+  "bugs": {
+    "url": "https://github.com/lingui/js-lingui/issues"
+  },
+  "engines": {
+    "node": ">=10.0.0"
+  },
+  "files": [
+    "LICENSE",
+    "README.md",
+    "index.js",
+    "cjs/",
+    "esm/"
+  ],
+  "peerDependencies": {},
+  "dependencies": {
+    "ramda": "^0.27.1",
+    "@babel/generator": "^7.14.5",
+    "@babel/types": "^7.14.5",
+    "messageformat-parser": "^4.1.3"
+  },
+  "devDependencies": {}
+}
diff --git a/packages/remote-loader/src/browserCompiler.ts b/packages/remote-loader/src/browserCompiler.ts
new file mode 100644
index 00000000..a2ef03e8
--- /dev/null
+++ b/packages/remote-loader/src/browserCompiler.ts
@@ -0,0 +1,111 @@
+import * as R from "ramda"
+import * as t from "@babel/types"
+import generate from "@babel/generator"
+import { parse } from "messageformat-parser"
+
+export function createBrowserCompiledCatalog(messages: Record<string, any>) {
+  const compiledMessages = R.keys(messages).map((key) => {
+    let translation = messages[key] || key
+
+    return t.objectProperty(t.stringLiteral(key.toString()), compile(translation))
+  })
+
+  const ast = t.objectExpression(compiledMessages)
+  const code = generate(ast as any, {
+    minified: true,
+    jsescOption: {
+      minimal: true,
+    }
+  }).code
+
+  return JSON.parse(code)
+}
+
+/**
+ * Compile string message into AST tree. Message format is parsed/compiled into
+ * JS arrays, which are handled in client.
+ */
+export function compile(message: string) {
+  let tokens: any
+
+  try {
+    tokens = parse(message)
+  } catch (e) {
+    throw new Error(
+      `Can't parse message. Please check correct syntax: "${message}" \n \n Messageformat-parser trace: ${e.message}`,
+    )
+  }
+  const ast = processTokens(tokens)
+
+  if (isString(ast)) return t.stringLiteral(ast)
+
+  return ast
+}
+
+function processTokens(tokens: any) {
+  // Shortcut - if the message doesn't include any formatting,
+  // simply join all string chunks into one message
+  if (!tokens.filter((token: any) => !isString(token)).length) {
+    return tokens.join("")
+  }
+
+  return t.arrayExpression(
+    tokens.map((token: any) => {
+      if (isString(token)) {
+        return t.stringLiteral(token)
+
+        // # in plural case
+      } else if (token.type === "octothorpe") {
+        return t.stringLiteral("#")
+
+        // simple argument
+      } else if (token.type === "argument") {
+        return t.arrayExpression([t.stringLiteral(token.arg)])
+
+        // argument with custom format (date, number)
+      } else if (token.type === "function") {
+        const params = [t.stringLiteral(token.arg), t.stringLiteral(token.key)]
+
+        const format = token.param && token.param.tokens[0]
+        if (format) {
+          params.push(t.stringLiteral(format.trim()))
+        }
+        return t.arrayExpression(params)
+      }
+
+      // complex argument with cases
+      const formatProps = []
+
+      if (token.offset) {
+        formatProps.push(
+          t.objectProperty(
+            t.identifier("offset"),
+            t.numericLiteral(parseInt(token.offset))
+          )
+        )
+      }
+
+      token.cases.forEach((item: any) => {
+        const inlineTokens = processTokens(item.tokens)
+        formatProps.push(
+          t.objectProperty(
+            t.identifier(item.key),
+            isString(inlineTokens)
+              ? t.stringLiteral(inlineTokens)
+              : inlineTokens
+          )
+        )
+      })
+
+      const params = [
+        t.stringLiteral(token.arg),
+        t.stringLiteral(token.type),
+        t.objectExpression(formatProps),
+      ]
+
+      return t.arrayExpression(params)
+    })
+  )
+}
+
+const isString = (s: any) => typeof s === "string"
diff --git a/packages/remote-loader/src/index.ts b/packages/remote-loader/src/index.ts
new file mode 100644
index 00000000..e3b8b7f9
--- /dev/null
+++ b/packages/remote-loader/src/index.ts
@@ -0,0 +1,46 @@
+import * as R from "ramda"
+import { createBrowserCompiledCatalog } from "./browserCompiler"
+import PARSERS from "./minimalParser"
+
+type RemoteLoaderOpts<T> = {
+  format?: "minimal"
+  fallbackMessages?: string | Record<string, any> | T
+  messages: string | Record<string, any> | T
+}
+
+function remoteLoader<T>({ format = "minimal", fallbackMessages, messages}: RemoteLoaderOpts<T>) {
+  let parsedMessages: any;
+  let parsedFallbackMessages: any;
+
+  if (format) {
+    const formatter = PARSERS[format]
+    if (fallbackMessages) {
+      // we do this because, people could just import the fallback and import the ./en/messages.js
+      // generated by lingui and the use case of format .po but fallback as .json module could be perfectly valid
+      parsedFallbackMessages = typeof fallbackMessages === "object" ? PARSERS.minimal(fallbackMessages) : formatter(fallbackMessages)
+    }
+
+    parsedMessages = formatter(messages)
+  } else {
+    throw new Error(`
+        *format* value in the Lingui configuration is required to make this loader 100% functional
+        Read more about this here: https://lingui.js.org/ref/conf.html#format
+      `)
+  }
+
+  const mapTranslationsToInterporlatedString = R.mapObjIndexed(
+    (_, key) => {
+      // if there's fallback and translation is empty, return the fallback
+      if (parsedMessages[key].translation === "" && parsedFallbackMessages?.[key]?.translation) {
+        return parsedFallbackMessages[key].translation
+      }
+
+      return parsedMessages[key].translation
+    },
+    parsedMessages,
+  )
+
+  return createBrowserCompiledCatalog(mapTranslationsToInterporlatedString)
+}
+
+export { remoteLoader }
diff --git a/packages/remote-loader/src/minimalParser.ts b/packages/remote-loader/src/minimalParser.ts
new file mode 100644
index 00000000..ca8a660b
--- /dev/null
+++ b/packages/remote-loader/src/minimalParser.ts
@@ -0,0 +1,16 @@
+import * as R from "ramda"
+
+const deserialize = (R.map((translation: string) => ({
+  translation,
+  obsolete: false,
+  message: null,
+  origin: [],
+})) as unknown) as (minimalCatalog: any) => any
+
+const PARSERS = {
+  minimal: <T>(content: Record<string, any> | T) => {
+    return deserialize(content)
+  }
+}
+
+export default PARSERS
diff --git a/scripts/build/bundles.js b/scripts/build/bundles.js
index 8dde6e6b..f21d98b0 100644
--- a/scripts/build/bundles.js
+++ b/scripts/build/bundles.js
@@ -57,6 +57,11 @@ const bundles = [
   {
     type: bundleTypes.NODE,
     entry: "@lingui/loader"
+  },
+
+  {
+    type: bundleTypes.UNIVERSAL,
+    entry: "@lingui/remote-loader"
   }
 ]
 
diff --git a/yarn.lock b/yarn.lock
index 5071209f..4a78c7e9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -87,6 +87,15 @@
     jsesc "^2.5.1"
     source-map "^0.5.0"
 
+"@babel/generator@^7.14.5":
+  version "7.14.5"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785"
+  integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==
+  dependencies:
+    "@babel/types" "^7.14.5"
+    jsesc "^2.5.1"
+    source-map "^0.5.0"
+
 "@babel/helper-annotate-as-pure@^7.10.4":
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3"
@@ -285,6 +294,11 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
   integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
 
+"@babel/helper-validator-identifier@^7.14.5":
+  version "7.14.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8"
+  integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==
+
 "@babel/helper-wrap-function@^7.10.4":
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87"
@@ -1069,6 +1083,14 @@
     lodash "^4.17.19"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.14.5":
+  version "7.14.5"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff"
+  integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.14.5"
+    to-fast-properties "^2.0.0"
+
 "@bcoe/v8-coverage@^0.2.3":
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"


Test patch:
diff --git a/packages/loader/src/remoteLoader.test.ts b/packages/remote-loader/test/index.test.ts
similarity index 53%
rename from packages/loader/src/remoteLoader.test.ts
rename to packages/remote-loader/test/index.test.ts
index 1e80f1aa..d344f5a7 100644
--- a/packages/loader/src/remoteLoader.test.ts
+++ b/packages/remote-loader/test/index.test.ts
@@ -1,12 +1,11 @@
-import { remoteLoader } from "./remoteLoader"
+import { remoteLoader } from "../src"
 import fs from "fs"
-import path from "path"
 
 describe("remote-loader", () => {
   it("should compile correctly JSON messages coming from the fly", async () => {
     const unlink = createConfig("minimal")
     const messages = await simulatedJsonResponse()
-    const remoteMessages = remoteLoader("en", messages)
+    const remoteMessages = remoteLoader({ format: "minimal", messages})
     expect(remoteMessages).toMatchInlineSnapshot(`
       Object {
         property.key: value,
@@ -28,30 +27,13 @@ describe("remote-loader", () => {
     unlink()
   })
 
-  it("should compile correctly .po messages coming from the fly", async () => {
-    const unlink = createConfig("po")
-    const messages = await simulatedPoResponse()
-    expect(remoteLoader("en", messages)).toMatchInlineSnapshot(`
-      Object {
-        Hello World: Hello World,
-        My name is {name}: Array [
-          My name is ,
-          Array [
-            name,
-          ],
-        ],
-      }
-    `)
-    unlink()
-  })
-
   describe("fallbacks", () => {
     it("should fallback correctly to the fallback collection", async () => {
       const unlink = createConfig("minimal")
       const messages = await simulatedJsonResponse(true)
       const fallbackMessages = await simulatedJsonResponse()
 
-      expect(remoteLoader("en", messages, fallbackMessages))
+      expect(remoteLoader({ format: "minimal", messages, fallbackMessages }))
         .toMatchInlineSnapshot(`
         Object {
           property.key: value,
@@ -71,26 +53,6 @@ describe("remote-loader", () => {
       `)
       unlink()
     })
-
-    it("should fallback to compiled fallback", async () => {
-      const unlink = createConfig("po")
-      const messages = await simulatedPoResponse("es")
-      const fallbackMessages = await simulatedPoCompiledFile()
-
-      expect(remoteLoader("en", messages, fallbackMessages))
-        .toMatchInlineSnapshot(`
-        Object {
-          Hello World: Hello World,
-          My name is {name}: Array [
-            My name is ,
-            Array [
-              name,
-            ],
-          ],
-        }
-      `)
-      unlink()
-    })
   })
 })
 
@@ -104,24 +66,6 @@ function simulatedJsonResponse(nully?: boolean) {
   })
 }
 
-function simulatedPoResponse(locale = "en") {
-  return new Promise((resolve) => {
-    const file = fs.readFileSync(
-      path.join(__dirname, "..", "test/locale/" + locale + "/messages.po"),
-      "utf-8"
-    )
-    resolve(file)
-  })
-}
-
-function simulatedPoCompiledFile() {
-  return new Promise((resolve) => {
-    resolve({
-      "Hello World": "Hello World",
-      "My name is {name}": ["My name is ", ["name"]],
-    })
-  })
-}
 
 function createConfig(format: string) {
   const filename = `${process.cwd()}/.linguirc`