From 89646f22b232095bb2e52af00e63eb13ef53db3b Mon Sep 17 00:00:00 2001 From: Ben Yap Date: Fri, 22 Oct 2021 21:38:36 +1100 Subject: [PATCH] fix: resolve exports (esm) --- README.md | 8 +- src/steps/generateChanges.ts | 51 ++++--- test/fixtures/change/out/exports.d.ts | 5 + test/fixtures/change/out/exports.js | 1 + .../change/out/{sample.d.ts => imports.d.ts} | 0 .../out/{nested/sample.js => imports.js} | 0 .../out/nested/{sample.d.ts => index.d.ts} | 0 .../change/out/{sample.js => nested/index.js} | 0 test/fixtures/change/src/exports.ts | 5 + .../src/{nested/sample.ts => imports.ts} | 0 .../change/src/{sample.ts => nested/index.ts} | 2 +- test/steps/generateChanges.test.ts | 141 ++++++++++++++---- 12 files changed, 157 insertions(+), 56 deletions(-) create mode 100644 test/fixtures/change/out/exports.d.ts create mode 100644 test/fixtures/change/out/exports.js rename test/fixtures/change/out/{sample.d.ts => imports.d.ts} (100%) rename test/fixtures/change/out/{nested/sample.js => imports.js} (100%) rename test/fixtures/change/out/nested/{sample.d.ts => index.d.ts} (100%) rename test/fixtures/change/out/{sample.js => nested/index.js} (100%) create mode 100644 test/fixtures/change/src/exports.ts rename test/fixtures/change/src/{nested/sample.ts => imports.ts} (100%) rename test/fixtures/change/src/{sample.ts => nested/index.ts} (81%) diff --git a/README.md b/README.md index da5825c..075b701 100644 --- a/README.md +++ b/README.md @@ -49,22 +49,22 @@ convenient solution._ _`resolve-tspaths` uses some reasonable defaults. For most cases, you probably won't need to specify any options._ -#### `--project, -p` +#### `--project , -p ` Specify the `tsconfig` that the program should use. If not provided, it defaults to `tsconfig.json`. -#### `--src, -s` +#### `--src , -s ` Specify the source directory. If not provided, it defaults to `./src`. -#### `--out, -o` +#### `--out , -o ` Specify the output directory of the compiled code where `resolve-tspaths` should perform its changes. If not provided, it will default to `compilerOptions.outDir` from your `tsconfig`. -#### `--ext` +#### `--ext ` Provide a comma separated list of file extensions in the output directory that the program should process. Defaults to `js,d.ts`, which will process `.js` and diff --git a/src/steps/generateChanges.ts b/src/steps/generateChanges.ts index e020916..fa626bc 100644 --- a/src/steps/generateChanges.ts +++ b/src/steps/generateChanges.ts @@ -3,8 +3,8 @@ import { dirname, relative, resolve } from "path"; import { FileNotFoundError } from "~/utils/errors"; import type { Alias, Change, ProgramPaths, TextChange } from "~/types"; -export const IMPORT_REGEX = - /(?:require\(|import (?:.*from )?)['"]([^'"]*)['"]\)?/g; +export const IMPORT_EXPORT_REGEX = + /(?:require\(|(?:import|export) (?:.*from )?)['"]([^'"]*)['"]\)?/g; const PATHS = [ ".js", @@ -73,28 +73,31 @@ export function replaceAliasPathsInFile( const originalText = readFileSync(filePath, "utf-8"); const changes: TextChange[] = []; - const newText = originalText.replace(IMPORT_REGEX, (original, matched) => { - const result = aliasToRelativePath( - matched, - filePath, - aliases, - programPaths - ); - - if (!result.replacement) return original; - - const index = original.indexOf(matched); - changes.push({ - original: result.original, - modified: result.replacement, - }); - - return ( - original.substring(0, index) + - result.replacement + - original.substring(index + matched.length) - ); - }); + const newText = originalText.replace( + IMPORT_EXPORT_REGEX, + (original, matched) => { + const result = aliasToRelativePath( + matched, + filePath, + aliases, + programPaths + ); + + if (!result.replacement) return original; + + const index = original.indexOf(matched); + changes.push({ + original: result.original, + modified: result.replacement, + }); + + return ( + original.substring(0, index) + + result.replacement + + original.substring(index + matched.length) + ); + } + ); return { changed: originalText !== newText, text: newText, changes }; } diff --git a/test/fixtures/change/out/exports.d.ts b/test/fixtures/change/out/exports.d.ts new file mode 100644 index 0000000..6ce18f7 --- /dev/null +++ b/test/fixtures/change/out/exports.d.ts @@ -0,0 +1,5 @@ +export {} from "~/root"; +export {} from "package"; +export {} from "~/nested/non-existent"; +export {} from "~/nested/nested-path"; +export {} from "@/non-existent"; diff --git a/test/fixtures/change/out/exports.js b/test/fixtures/change/out/exports.js new file mode 100644 index 0000000..f053ebf --- /dev/null +++ b/test/fixtures/change/out/exports.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/test/fixtures/change/out/sample.d.ts b/test/fixtures/change/out/imports.d.ts similarity index 100% rename from test/fixtures/change/out/sample.d.ts rename to test/fixtures/change/out/imports.d.ts diff --git a/test/fixtures/change/out/nested/sample.js b/test/fixtures/change/out/imports.js similarity index 100% rename from test/fixtures/change/out/nested/sample.js rename to test/fixtures/change/out/imports.js diff --git a/test/fixtures/change/out/nested/sample.d.ts b/test/fixtures/change/out/nested/index.d.ts similarity index 100% rename from test/fixtures/change/out/nested/sample.d.ts rename to test/fixtures/change/out/nested/index.d.ts diff --git a/test/fixtures/change/out/sample.js b/test/fixtures/change/out/nested/index.js similarity index 100% rename from test/fixtures/change/out/sample.js rename to test/fixtures/change/out/nested/index.js diff --git a/test/fixtures/change/src/exports.ts b/test/fixtures/change/src/exports.ts new file mode 100644 index 0000000..6ce18f7 --- /dev/null +++ b/test/fixtures/change/src/exports.ts @@ -0,0 +1,5 @@ +export {} from "~/root"; +export {} from "package"; +export {} from "~/nested/non-existent"; +export {} from "~/nested/nested-path"; +export {} from "@/non-existent"; diff --git a/test/fixtures/change/src/nested/sample.ts b/test/fixtures/change/src/imports.ts similarity index 100% rename from test/fixtures/change/src/nested/sample.ts rename to test/fixtures/change/src/imports.ts diff --git a/test/fixtures/change/src/sample.ts b/test/fixtures/change/src/nested/index.ts similarity index 81% rename from test/fixtures/change/src/sample.ts rename to test/fixtures/change/src/nested/index.ts index d544d04..6a33c77 100644 --- a/test/fixtures/change/src/sample.ts +++ b/test/fixtures/change/src/nested/index.ts @@ -1,7 +1,7 @@ import {} from "~/root"; import {} from "package"; import {} from "~/nested/non-existent"; -import {} from "~/nested/path"; +import {} from "~/nested/nested-path"; import {} from "@/non-existent"; // Module code diff --git a/test/steps/generateChanges.test.ts b/test/steps/generateChanges.test.ts index aa13c82..746d31c 100644 --- a/test/steps/generateChanges.test.ts +++ b/test/steps/generateChanges.test.ts @@ -1,5 +1,5 @@ import { - IMPORT_REGEX, + IMPORT_EXPORT_REGEX, aliasToRelativePath, replaceAliasPathsInFile, generateChanges, @@ -7,11 +7,11 @@ import { import type { Alias, ProgramPaths } from "~/types"; describe("steps/generateChanges", () => { - describe("IMPORT_REGEX", () => { + describe("IMPORT_EXPORT_REGEX", () => { let regex: RegExp; beforeEach(() => { - regex = new RegExp(IMPORT_REGEX); + regex = new RegExp(IMPORT_EXPORT_REGEX); }); it("matches import * statements", () => { @@ -56,6 +56,48 @@ describe("steps/generateChanges", () => { `); }); + it("matches export * statements", () => { + const result = regex.exec(`export * as package from 'package';`); + expect(result).toMatchInlineSnapshot(` + Array [ + "export * as package from 'package'", + "package", + ] + `); + }); + + it("matches export {} statements", () => { + const result = regex.exec(`export { package } from '~/package';`); + expect(result).toMatchInlineSnapshot(` + Array [ + "export { package } from '~/package'", + "~/package", + ] + `); + }); + + it("matches export { as } statements", () => { + const result = regex.exec( + `export { package as myPackage } from '../package';` + ); + expect(result).toMatchInlineSnapshot(` + Array [ + "export { package as myPackage } from '../package'", + "../package", + ] + `); + }); + + it("matches export statements", () => { + const result = regex.exec(`export 'package';`); + expect(result).toMatchInlineSnapshot(` + Array [ + "export 'package'", + "package", + ] + `); + }); + it("matches require statements", () => { const result = regex.exec(`require('package');`); expect(result).toMatchInlineSnapshot(` @@ -106,14 +148,14 @@ describe("steps/generateChanges", () => { it("returns the original path for a non-aliased path", () => { const result = aliasToRelativePath( "path", - "test/fixtures/change/out/sample.js", + "test/fixtures/change/out/imports.js", aliases, programPaths ); expect(result).toMatchInlineSnapshot(` Object { - "file": "test/fixtures/change/out/sample.js", + "file": "test/fixtures/change/out/imports.js", "original": "path", } `); @@ -122,14 +164,14 @@ describe("steps/generateChanges", () => { it("returns the original path for an alias path that does not exist", () => { const result = aliasToRelativePath( "~/non-existent", - "test/fixtures/change/out/sample.js", + "test/fixtures/change/out/imports.js", aliases, programPaths ); expect(result).toMatchInlineSnapshot(` Object { - "file": "test/fixtures/change/out/sample.js", + "file": "test/fixtures/change/out/imports.js", "original": "~/non-existent", } `); @@ -138,14 +180,14 @@ describe("steps/generateChanges", () => { it("returns the correct relative path for an aliased path at the root", () => { const result = aliasToRelativePath( "~/root", - "test/fixtures/change/out/sample.js", + "test/fixtures/change/out/imports.js", aliases, programPaths ); expect(result).toMatchInlineSnapshot(` Object { - "file": "test/fixtures/change/out/sample.js", + "file": "test/fixtures/change/out/imports.js", "original": "~/root", "replacement": "./root", } @@ -155,14 +197,14 @@ describe("steps/generateChanges", () => { it("returns the correct relative path for an aliased path at the root using a secondary alias", () => { const result = aliasToRelativePath( "~/alternate", - "test/fixtures/change/out/sample.js", + "test/fixtures/change/out/imports.js", aliases, programPaths ); expect(result).toMatchInlineSnapshot(` Object { - "file": "test/fixtures/change/out/sample.js", + "file": "test/fixtures/change/out/imports.js", "original": "~/alternate", "replacement": "./alternateSrc/alternate", } @@ -172,14 +214,14 @@ describe("steps/generateChanges", () => { it("returns the correct relative path for a nested aliased path", () => { const result = aliasToRelativePath( "~/nested/nested-path", - "test/fixtures/change/out/sample.js", + "test/fixtures/change/out/imports.js", aliases, programPaths ); expect(result).toMatchInlineSnapshot(` Object { - "file": "test/fixtures/change/out/sample.js", + "file": "test/fixtures/change/out/imports.js", "original": "~/nested/nested-path", "replacement": "./nested/nested-path", } @@ -189,14 +231,14 @@ describe("steps/generateChanges", () => { it("returns the correct relative path for an aliased path from a nested directory", () => { const result = aliasToRelativePath( "~/root", - "test/fixtures/change/out/nested/sample.js", + "test/fixtures/change/out/nested/imports.js", aliases, programPaths ); expect(result).toMatchInlineSnapshot(` Object { - "file": "test/fixtures/change/out/nested/sample.js", + "file": "test/fixtures/change/out/nested/imports.js", "original": "~/root", "replacement": "../root", } @@ -206,14 +248,14 @@ describe("steps/generateChanges", () => { it("returns the correct relative path for an aliased path from a nested directory using a secondary alias", () => { const result = aliasToRelativePath( "~/alternate", - "test/fixtures/change/out/nested/sample.js", + "test/fixtures/change/out/nested/imports.js", aliases, programPaths ); expect(result).toMatchInlineSnapshot(` Object { - "file": "test/fixtures/change/out/nested/sample.js", + "file": "test/fixtures/change/out/nested/imports.js", "original": "~/alternate", "replacement": "../alternateSrc/alternate", } @@ -246,9 +288,9 @@ describe("steps/generateChanges", () => { expect(results.changes).toHaveLength(0); }); - it("generates replacements for a file at the root level correctly", () => { + it("generates replacements for a file with imports at the root level correctly", () => { const results = replaceAliasPathsInFile( - `${root}/out/sample.js`, + `${root}/out/imports.js`, aliases, programPaths ); @@ -281,7 +323,7 @@ describe("steps/generateChanges", () => { it("generates replacements for a file at a nested directory correctly", () => { const results = replaceAliasPathsInFile( - `${root}/out/nested/sample.js`, + `${root}/out/nested/index.js`, aliases, programPaths ); @@ -324,9 +366,9 @@ describe("steps/generateChanges", () => { expect(results.changes).toHaveLength(0); }); - it("generates replacements for a file at the root level correctly", () => { + it("generates replacements for a file with imports at the root level correctly", () => { const results = replaceAliasPathsInFile( - `${root}/out/sample.d.ts`, + `${root}/out/imports.d.ts`, aliases, programPaths ); @@ -354,9 +396,38 @@ describe("steps/generateChanges", () => { `); }); + it("generates replacements for a file with exports at the root level correctly", () => { + const results = replaceAliasPathsInFile( + `${root}/out/exports.d.ts`, + aliases, + programPaths + ); + expect(results.changed).toBe(true); + expect(results.changes).toMatchInlineSnapshot(` + Array [ + Object { + "modified": "./root", + "original": "~/root", + }, + Object { + "modified": "./nested/nested-path", + "original": "~/nested/nested-path", + }, + ] + `); + expect(results.text).toMatchInlineSnapshot(` + "export {} from \\"./root\\"; + export {} from \\"package\\"; + export {} from \\"~/nested/non-existent\\"; + export {} from \\"./nested/nested-path\\"; + export {} from \\"@/non-existent\\"; + " + `); + }); + it("generates replacements for a file at a nested directory correctly", () => { const results = replaceAliasPathsInFile( - `${root}/out/nested/sample.d.ts`, + `${root}/out/nested/index.d.ts`, aliases, programPaths ); @@ -405,13 +476,15 @@ describe("steps/generateChanges", () => { const results = generateChanges( [ `${root}/out/alternateSrc/alternate/index.js`, - `${root}/out/nested/sample.js`, - `${root}/out/sample.js`, + `${root}/out/nested/index.js`, + `${root}/out/imports.js`, + `${root}/out/exports.js`, `${root}/out/no-change.js`, ], aliases, programPaths ); + expect(results).toHaveLength(3); expect(results[0].changes).toMatchInlineSnapshot(` Array [ Object { @@ -454,13 +527,15 @@ describe("steps/generateChanges", () => { const results = generateChanges( [ `${root}/out/alternateSrc/alternate/index.d.ts`, - `${root}/out/nested/sample.d.ts`, - `${root}/out/sample.d.ts`, + `${root}/out/nested/index.d.ts`, + `${root}/out/imports.d.ts`, + `${root}/out/exports.d.ts`, `${root}/out/no-change.d.ts`, ], aliases, programPaths ); + expect(results).toHaveLength(4); expect(results[0].changes).toMatchInlineSnapshot(` Array [ Object { @@ -497,6 +572,18 @@ describe("steps/generateChanges", () => { }, ] `); + expect(results[3].changes).toMatchInlineSnapshot(` + Array [ + Object { + "modified": "./root", + "original": "~/root", + }, + Object { + "modified": "./nested/nested-path", + "original": "~/nested/nested-path", + }, + ] + `); }); }); });