Skip to content

Commit

Permalink
Fix ESM woes (#417)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjie authored Dec 29, 2023
2 parents 188c330 + d69140a commit 383cc41
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 7 deletions.
5 changes: 5 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ to make sure the system as a whole remains consistent.
Read more:
[Worker Pro Migration](https://worker.graphile.org/docs/pro/migration).

## v0.16.1

- Fixes issue importing task files that were written in TypeScript ESM format
but exported as CommonJS.

## v0.16.0

_There's a breakdown of these release notes available on the new
Expand Down
7 changes: 7 additions & 0 deletions __tests__/fixtures/tasks/wouldyoulike_ts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const blah_1 = require("../blah");
exports.default = (_payload, helpers) => {
helpers.logger.debug((0, blah_1.rand)());
return "some TS sausages";
};
7 changes: 7 additions & 0 deletions __tests__/fixtures/tasksFile-ts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.task2 = exports.task1 = void 0;
const task1 = () => "hi";
exports.task1 = task1;
const task2 = () => "hello from TS";
exports.task2 = task2;
6 changes: 6 additions & 0 deletions __tests__/fixtures/tasksFile_default-ts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = {
t1: () => "come with me, TS",
t2: () => "if you want to live",
};
62 changes: 62 additions & 0 deletions __tests__/getTasks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe("commonjs", () => {
Array [
"wouldyoulike",
"wouldyoulike_default",
"wouldyoulike_ts",
]
`);
const helpers = makeJobHelpers(
Expand All @@ -37,6 +38,9 @@ Array [
expect(
await tasks.wouldyoulike_default!(helpers.job.payload, helpers),
).toEqual("some more sausages");
expect(
await tasks.wouldyoulike_ts!(helpers.job.payload, helpers),
).toEqual("some TS sausages");
await release();
}));

Expand Down Expand Up @@ -68,6 +72,36 @@ Array [
await release();
}));

test("get tasks from file (vanilla-ts)", () =>
withPgClient(async (client) => {
const { tasks, release, compiledSharedOptions } = (await getTasks(
options,
`${__dirname}/fixtures/tasksFile-ts.js`,
)) as WatchedTaskList & { compiledSharedOptions: CompiledSharedOptions };
expect(tasks).toBeTruthy();
expect(Object.keys(tasks).sort()).toMatchInlineSnapshot(`
Array [
"task1",
"task2",
]
`);

const helpers = makeJobHelpers(
compiledSharedOptions,
makeMockJob("task1"),
{
withPgClient: makeWithPgClientFromClient(client),
abortSignal: undefined,
},
);
expect(await tasks.task1!(helpers.job.payload, helpers)).toEqual("hi");
expect(await tasks.task2!(helpers.job.payload, helpers)).toEqual(
"hello from TS",
);

await release();
}));

test("get tasks from file (default)", () =>
withPgClient(async (client) => {
const { tasks, release, compiledSharedOptions } = (await getTasks(
Expand All @@ -93,6 +127,34 @@ Array [
"if you want to live",
);

await release();
}));

test("get tasks from file (default-ts)", () =>
withPgClient(async (client) => {
const { tasks, release, compiledSharedOptions } = (await getTasks(
options,
`${__dirname}/fixtures/tasksFile_default-ts.js`,
)) as WatchedTaskList & { compiledSharedOptions: CompiledSharedOptions };
expect(tasks).toBeTruthy();
expect(Object.keys(tasks).sort()).toMatchInlineSnapshot(`
Array [
"t1",
"t2",
]
`);

const helpers = makeJobHelpers(compiledSharedOptions, makeMockJob("t1"), {
withPgClient: makeWithPgClientFromClient(client),
abortSignal: undefined,
});
expect(await tasks.t1!(helpers.job.payload, helpers)).toEqual(
"come with me, TS",
);
expect(await tasks.t2!(helpers.job.payload, helpers)).toEqual(
"if you want to live",
);

await release();
}));
});
Expand Down
17 changes: 14 additions & 3 deletions src/getTasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,25 @@ async function loadFileIntoTasks(
name: string | null = null,
) {
const rawMod = await import(filename);

// Normally, import() of a commonJS module with `module.exports` write
// would result in `{ default: module.exports }`.
// TypeScript in CommonJS mode when imported with Node ESM can lead to
// two levels of `__esModule: true`; so we try and grab the inner one
// if we can.
const mod =
Object.keys(rawMod).length === 1 &&
typeof rawMod.default === "object" &&
rawMod.default !== null
rawMod.default?.default?.__esModule === true
? rawMod.default.default
: rawMod.default?.__esModule === true
? rawMod.default
: Object.keys(rawMod).length === 1 &&
typeof rawMod.default === "object" &&
rawMod.default !== null
? rawMod.default
: rawMod;

if (name) {
// Always take the default export if there is one
const task = mod.default || mod;
if (isValidTask(task)) {
tasks[name] = task;
Expand Down
23 changes: 19 additions & 4 deletions src/plugins/LoadTaskFromJsPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,33 @@ export const LoadTaskFromJsPlugin: GraphileConfig.Plugin = {

try {
const rawMod = await import(jsFile.fullPath);

// Normally, import() of a commonJS module with `module.exports` write
// would result in `{ default: module.exports }`.
// TypeScript in CommonJS mode when imported with Node ESM can lead to
// two levels of `__esModule: true`; so we try and grab the inner one
// if we can.
const mod =
Object.keys(rawMod).length === 1 &&
typeof rawMod.default === "object" &&
rawMod.default !== null
rawMod.default?.default?.__esModule === true
? rawMod.default.default
: rawMod.default?.__esModule === true
? rawMod.default
: Object.keys(rawMod).length === 1 &&
typeof rawMod.default === "object" &&
rawMod.default !== null
? rawMod.default
: rawMod;

// Always take the default export if there is one
const task = mod.default || mod;

if (isValidTask(task)) {
details.handler = task;
} else {
throw new Error(
`Invalid task '${name}' - expected function, received ${
`Invalid task '${
jsFile.fullPath
}' - expected function, received ${
task ? typeof task : String(task)
}.`,
);
Expand Down

0 comments on commit 383cc41

Please sign in to comment.