Repository: lingui/js-lingui (ts)

Issue:
Error: EPERM: operation not permitted, scandir 'C:/$Recycle.Bin/S-1-5-18' when running lingui extract
**Describe the bug**
Running `lingui extract` results in error

```
Error: EPERM: operation not permitted, scandir 'C:/$Recycle.Bin/S-1-5-18'
```

**To Reproduce**
Use `next-js` example, and run `lingui extract`

**Expected behavior**
The command should run successfully.

- jsLingui version ``3.0``
- Babel version ``@babel/core@7.7.7``
- Your Babel config (e.g. `.babelrc`):

```
{
    "presets": ["next/babel"],
    "plugins": [
      "macros",
      "jsx-control-statements",
      [
        "import",
        {
          "libraryName": "@geist-ui/react",
          "libraryDirectory": "esm"
        }
      ]
    ]
  }
  
```


Pull request:
Fix catalog include path on Windows
Closes #796 

Patch:
diff --git a/packages/cli/src/api/catalog.ts b/packages/cli/src/api/catalog.ts
index bc4dff1c..31e5096f 100644
--- a/packages/cli/src/api/catalog.ts
+++ b/packages/cli/src/api/catalog.ts
@@ -252,7 +252,7 @@ export class Catalog {
   }
 
   writeCompiled(locale: string, compiledCatalog: string, namespace?: string) {
-    const ext = `.${ namespace === "es" ? "mjs": "js"}`
+    const ext = `.${namespace === "es" ? "mjs" : "js"}`
     const filename = this.path.replace(LOCALE, locale) + ext
 
     const basedir = path.dirname(filename)
@@ -436,7 +436,9 @@ export function getCatalogForMerge(config: LinguiConfig) {
   // catalog name is the last directory of catalogPath.
   // If the last part is {locale}, then catalog doesn't have an explicit name
   const name = (function () {
-    const _name = path.basename(normalizeRelativePath(catalogConfig.catalogsMergePath))
+    const _name = path.basename(
+      normalizeRelativePath(catalogConfig.catalogsMergePath)
+    )
     return _name !== LOCALE ? _name : null
   })()
 
@@ -486,11 +488,11 @@ const ensureArray = <T>(value: Array<T> | T | null | undefined): Array<T> => {
  * Preserve directories:       ./relative/ => relative/
  * Preserve absolute paths:    /absolute/path => /absolute/path
  */
-function normalizeRelativePath(sourcePath: string): string {
+export function normalizeRelativePath(sourcePath: string): string {
   const sourcePathPosix = normalize(sourcePath)
 
   // absolute path, do nothing
-  if (sourcePathPosix.startsWith(PATHSEP)) return sourcePathPosix
+  if (path.isAbsolute(sourcePathPosix)) return sourcePathPosix
 
   // preserve trailing slash for directories
   const isDir = normalize(sourcePath, false).endsWith(PATHSEP)


Test patch:
diff --git a/packages/cli/src/api/catalog.test.ts b/packages/cli/src/api/catalog.test.ts
index 20378cff..b67be54f 100644
--- a/packages/cli/src/api/catalog.test.ts
+++ b/packages/cli/src/api/catalog.test.ts
@@ -10,6 +10,7 @@ import {
   Catalog,
   cleanObsolete,
   order,
+  normalizeRelativePath,
 } from "./catalog"
 import { createCompiledCatalog } from "./compile"
 
@@ -767,6 +768,21 @@ describe("getCatalogForMerge", function () {
   })
 })
 
+describe("normalizeRelativePath", function () {
+  it("should preserve absolute paths - posix", function () {
+    const absolute = "/my/directory"
+    expect(normalizeRelativePath(absolute)).toEqual(absolute)
+  })
+
+  it("should preserve absolute paths - win32", function () {
+    const absolute = "C:\\my\\directory"
+    // path remains the same, but separators are converted to posix
+    expect(normalizeRelativePath(absolute)).toEqual(
+      absolute.split("\\").join("/")
+    )
+  })
+})
+
 describe("cleanObsolete", function () {
   it("should remove obsolete messages from catalog", function () {
     const catalog = {