Skip to content

Commit 334069d

Browse files
authored
Merge pull request #106 from Codex-/jiti
Add ESM support and change to jiti
2 parents fbea639 + 56f9e65 commit 334069d

20 files changed

+487
-172
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
node-version: [14.x, 16.x, 18.x]
13+
node-version: [16.x, 18.x, 20.x]
1414
steps:
1515
- uses: actions/checkout@v3
1616
- name: Use Node.js ${{ matrix.node-version }}
@@ -31,7 +31,7 @@ jobs:
3131
run: npm run test
3232
- name: Import with CJS
3333
if: ${{ always() }}
34-
run: node smoke-tests/smoke-test-cjs.js
34+
run: node smoke-tests/smoke-test-cjs.cjs
3535
- name: Import with ESM
3636
if: ${{ always() }}
3737
run: node smoke-tests/smoke-test-esm.mjs

.github/workflows/release.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ jobs:
1717
env:
1818
CI: true
1919
GITHUB_TOKEN: ${{ secrets.ACTION_GITHUB_TOKEN }}
20-
NPM_OTP_TOKEN: ${{ github.event.inputs.otp }}
2120
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
2221
runs-on: ubuntu-latest
2322
steps:

.gitignore

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
.idea
2-
.npmrc
2+
.vscode
3+
4+
node_modules
5+
36
coverage
47
dist
5-
node_modules
8+
9+
.npmrc

esbuild.config.mjs

Lines changed: 0 additions & 54 deletions
This file was deleted.

jest.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const config: Config.InitialOptions = {
66
"!<rootDir>/lib/__fixtures__/**/*",
77
],
88
moduleFileExtensions: ["ts", "js"],
9+
moduleNameMapper: {
10+
"^(\\.{1,2}/.*)\\.js$": "$1",
11+
},
912
transform: {
1013
"^.+\\.ts$": "@swc/jest",
1114
},

lib/index.spec.ts

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,68 @@ describe("TypeScriptLoader", () => {
1414
});
1515

1616
describe("cosmiconfig", () => {
17-
it("should load a valid TS file", async () => {
18-
const cfg = cosmiconfig("test", {
19-
loaders: {
20-
".ts": TypeScriptLoader(),
21-
},
17+
describe("synchronous", () => {
18+
it("should load a valid TS file", () => {
19+
const cfg = cosmiconfigSync("test", {
20+
loaders: {
21+
".ts": TypeScriptLoader(),
22+
},
23+
});
24+
const loadedCfg = cfg.load(
25+
path.resolve(fixturesPath, "valid.fixture.ts")
26+
);
27+
28+
expect(typeof loadedCfg!.config).toStrictEqual("object");
29+
expect(typeof loadedCfg!.config.test).toStrictEqual("object");
30+
expect(loadedCfg!.config.test.cake).toStrictEqual("a lie");
2231
});
23-
const loadedCfg = await cfg.load(
24-
path.resolve(fixturesPath, "valid.fixture.ts")
25-
);
2632

27-
expect(typeof loadedCfg!.config).toStrictEqual("object");
28-
expect(typeof loadedCfg!.config.test).toStrictEqual("object");
29-
expect(loadedCfg!.config.test.cake).toStrictEqual("a lie");
33+
it("should throw an error on loading an invalid TS file", () => {
34+
const cfg = cosmiconfigSync("test", {
35+
loaders: {
36+
".ts": TypeScriptLoader(),
37+
},
38+
});
39+
40+
try {
41+
cfg.load(path.resolve(fixturesPath, "invalid.fixture.ts"));
42+
fail("Should fail to load invalid TS");
43+
} catch (error: any) {
44+
expect(error?.name).toStrictEqual("TypeScriptCompileError");
45+
}
46+
});
3047
});
3148

32-
it("should throw an error on loading an invalid TS file", async () => {
33-
const cfg = cosmiconfig("test", {
34-
loaders: {
35-
".ts": TypeScriptLoader(),
36-
},
49+
describe("asynchronous", () => {
50+
it("should load a valid TS file", async () => {
51+
const cfg = cosmiconfig("test", {
52+
loaders: {
53+
".ts": TypeScriptLoader(),
54+
},
55+
});
56+
const loadedCfg = await cfg.load(
57+
path.resolve(fixturesPath, "valid.fixture.ts")
58+
);
59+
60+
expect(typeof loadedCfg!.config).toStrictEqual("object");
61+
expect(typeof loadedCfg!.config.test).toStrictEqual("object");
62+
expect(loadedCfg!.config.test.cake).toStrictEqual("a lie");
3763
});
3864

39-
try {
40-
await cfg.load(path.resolve(fixturesPath, "invalid.fixture.ts"));
41-
fail("Should fail to load invalid TS");
42-
} catch (error: any) {
43-
expect(error?.name).toStrictEqual("TypeScriptCompileError");
44-
}
65+
it("should throw an error on loading an invalid TS file", async () => {
66+
const cfg = cosmiconfig("test", {
67+
loaders: {
68+
".ts": TypeScriptLoader(),
69+
},
70+
});
71+
72+
try {
73+
await cfg.load(path.resolve(fixturesPath, "invalid.fixture.ts"));
74+
fail("Should fail to load invalid TS");
75+
} catch (error: any) {
76+
expect(error?.name).toStrictEqual("TypeScriptCompileError");
77+
}
78+
});
4579
});
4680
});
4781

lib/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export { TypeScriptLoader } from "./loader";
2-
export type { TypeScriptCompileError } from "./typescript-compile-error";
1+
export { TypeScriptLoader } from "./loader.js";
2+
export type { TypeScriptCompileError } from "./typescript-compile-error.js";

lib/loader.spec.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,23 @@ import fs from "node:fs";
22
import path from "node:path";
33

44
import { Loader } from "cosmiconfig";
5-
import * as tsnode from "ts-node";
5+
import * as jiti from "jiti";
66

77
import { TypeScriptLoader } from "./loader";
88
import { TypeScriptCompileError } from "./typescript-compile-error";
99

10+
// Handle jiti using `export default`
11+
jest.mock("jiti", () => {
12+
const actual = jest.requireActual("jiti");
13+
return {
14+
__esModule: true,
15+
default: jest.fn(actual),
16+
};
17+
});
18+
1019
describe("TypeScriptLoader", () => {
1120
const fixturesPath = path.resolve(__dirname, "__fixtures__");
12-
const tsNodeSpy = jest.spyOn(tsnode, "register");
21+
const jitiSpy = jest.spyOn(jiti, "default");
1322

1423
let loader: Loader;
1524

@@ -31,11 +40,11 @@ describe("TypeScriptLoader", () => {
3140
expect(() => loader(filePath, readFixtureContent(filePath))).toThrowError();
3241
});
3342

34-
it("should use the same instance of ts-node across multiple calls", () => {
43+
it("should use the same instance of jiti across multiple calls", () => {
3544
const filePath = path.resolve(fixturesPath, "valid.fixture.ts");
3645
loader(filePath, readFixtureContent(filePath));
3746
loader(filePath, readFixtureContent(filePath));
38-
expect(tsNodeSpy).toHaveBeenCalledTimes(1);
47+
expect(jitiSpy).toHaveBeenCalledTimes(1);
3948
});
4049

4150
it("should throw a TypeScriptCompileError on error", () => {
@@ -50,21 +59,17 @@ describe("TypeScriptLoader", () => {
5059
}
5160
});
5261

53-
describe("ts-node", () => {
62+
describe("jiti", () => {
5463
const unknownError = "Test Error";
5564

56-
let stub: jest.SpyInstance<tsnode.Service, [service: tsnode.Service]>;
65+
let stub: jest.SpyInstance;
5766

5867
beforeEach(() => {
59-
stub = jest.spyOn(tsnode, "register").mockImplementation(
60-
() =>
61-
({
62-
compile: (): string => {
63-
// eslint-disable-next-line @typescript-eslint/no-throw-literal
64-
throw unknownError;
65-
},
66-
} as any)
67-
);
68+
stub = jest.spyOn(jiti, "default").mockImplementation((() => () => {
69+
// eslint-disable-next-line @typescript-eslint/no-throw-literal
70+
throw unknownError;
71+
}) as any);
72+
6873
loader = TypeScriptLoader();
6974
});
7075

lib/loader.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
import type { Loader } from "cosmiconfig";
2-
import { register, RegisterOptions } from "ts-node";
2+
import jiti, { type JITIOptions } from "jiti";
33

4-
import { TypeScriptCompileError } from "./typescript-compile-error";
4+
import { TypeScriptCompileError } from "./typescript-compile-error.js";
55

6-
export function TypeScriptLoader(options?: RegisterOptions): Loader {
7-
const tsNodeInstance = register({
8-
...options,
9-
compilerOptions: { module: "commonjs" },
10-
});
11-
return (path: string, content: string) => {
6+
export function TypeScriptLoader(options?: JITIOptions): Loader {
7+
const loader = jiti("", { interopDefault: true, ...options });
8+
return (path: string) => {
129
try {
13-
// cosmiconfig requires the transpiled configuration to be CJS
14-
tsNodeInstance.compile(content, path);
15-
const result = require(path);
10+
const result = loader(path);
1611

1712
// `default` is used when exporting using export default, some modules
1813
// may still use `module.exports` or if in TS `export = `

lib/typescript-compile-error.spec.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,14 @@ describe("TypeScriptCompileError", () => {
1717
expect(tscError.stack).toBe(testError.stack);
1818
});
1919

20-
it("should replace the legacy tsc error string", () => {
21-
const testMsg =
22-
"TypeScript compiler encountered syntax errors while transpiling. Errors: ";
20+
it("should prefix the jiti parser error", () => {
21+
const testMsg = 'ParseError: Unexpected token, expected ","';
2322
const legacyError = new Error(testMsg);
2423
const tscError = TypeScriptCompileError.fromError(legacyError);
2524

26-
expect(tscError).not.toContainEqual(testMsg);
27-
});
28-
29-
it("should replace the tsc error string", () => {
30-
const testMsg = "⨯ Unable to compile TypeScript:";
31-
const newError = new Error(testMsg);
32-
const tscError = TypeScriptCompileError.fromError(newError);
33-
34-
expect(tscError).not.toContainEqual(testMsg);
25+
expect(tscError.message).toContain(
26+
"TypeScriptLoader failed to compile TypeScript:"
27+
);
3528
});
3629
});
3730
});

0 commit comments

Comments
 (0)