Skip to content

Commit 7a1e387

Browse files
feat: add support for the "new" deno bundle command (#116)
1 parent 4caedc8 commit 7a1e387

File tree

7 files changed

+310
-34
lines changed

7 files changed

+310
-34
lines changed

src/build.ts

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { Protocol } from "jsr:@slack/protocols@0.0.3/types";
66

77
import { cleanManifest, getManifest } from "./get_manifest.ts";
88
import { validateManifestFunctions } from "./utilities.ts";
9-
import { DenoBundler, EsbuildBundler } from "./bundler/mods.ts";
9+
import { Deno2Bundler, DenoBundler, EsbuildBundler } from "./bundler/mods.ts";
1010
import { BundleError } from "./errors.ts";
1111

1212
export const validateAndCreateFunctions = async (
@@ -62,7 +62,7 @@ async function resolveDenoConfigPath(
6262
}
6363
}
6464
throw new Error(
65-
`Could not find a deno.json or deno.jsonc file in the current directory.`,
65+
"Could not find a deno.json or deno.jsonc file in the current directory.",
6666
);
6767
}
6868

@@ -77,29 +77,48 @@ const createFunctionFile = async (
7777
const fnBundledPath = path.join(outputDirectory, fnFileRelative);
7878

7979
try {
80+
// TODO: Remove this try/catch block once Deno 1.x is no longer supported
8081
await DenoBundler.bundle({
8182
entrypoint: fnFilePath,
8283
outFile: fnBundledPath,
8384
});
85+
return;
8486
} catch (denoBundleErr) {
8587
if (!(denoBundleErr instanceof BundleError)) {
86-
protocol.error(`Error bundling function file "${fnId}" with Deno`);
88+
protocol.error(`Failed to bundle function "${fnId}" using Deno bundler`);
8789
throw denoBundleErr;
8890
}
91+
// TODO: once Protocol can handle debug add a debug statement for the error
92+
}
8993

90-
// TODO: once Protocol can handle debug add a debug statement here
91-
92-
try {
93-
const bundle = await EsbuildBundler.bundle({
94-
entrypoint: fnFilePath,
95-
absWorkingDir: workingDirectory,
96-
configPath: await resolveDenoConfigPath(workingDirectory),
97-
});
98-
await Deno.writeFile(fnBundledPath, bundle);
99-
} catch (esbuildError) {
100-
protocol.error(`Error bundling function file "${fnId}" with esbuild`);
101-
throw esbuildError;
94+
try {
95+
await Deno2Bundler.bundle({
96+
entrypoint: fnFilePath,
97+
outFile: fnBundledPath,
98+
});
99+
return;
100+
} catch (denoBundleErr) {
101+
if (!(denoBundleErr instanceof BundleError)) {
102+
protocol.error(
103+
`Failed to bundle function "${fnId}" using Deno2 bundler`,
104+
);
105+
throw denoBundleErr;
102106
}
107+
// TODO: once Protocol can handle debug add a debug statement for the error
108+
}
109+
110+
try {
111+
const bundle = await EsbuildBundler.bundle({
112+
entrypoint: fnFilePath,
113+
absWorkingDir: workingDirectory,
114+
configPath: await resolveDenoConfigPath(workingDirectory),
115+
});
116+
await Deno.writeFile(fnBundledPath, bundle);
117+
} catch (esbuildError) {
118+
protocol.error(
119+
`Failed to bundle function "${fnId}": Attempt with Deno bundle and esbuild - all failed`,
120+
);
121+
throw esbuildError;
103122
}
104123
};
105124

src/bundler/deno2_bundler.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { BundleError } from "../errors.ts";
2+
3+
type DenoBundleOptions = {
4+
/** The path to the file being bundled */
5+
entrypoint: string;
6+
/** The path where the bundled file should be written. */
7+
outFile: string;
8+
};
9+
10+
export const Deno2Bundler = {
11+
bundle: async (options: DenoBundleOptions): Promise<void> => {
12+
// call out to deno to handle bundling
13+
const command = new Deno.Command(Deno.execPath(), {
14+
args: [
15+
"bundle",
16+
"--quiet",
17+
options.entrypoint,
18+
"--output",
19+
options.outFile,
20+
],
21+
});
22+
23+
const { code, stderr } = await command.output();
24+
if (code !== 0) {
25+
throw new BundleError({
26+
cause: new TextDecoder().decode(stderr),
27+
});
28+
}
29+
},
30+
};

src/bundler/deno2_bundler_test.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { assertRejects, assertSpyCall, stub } from "../dev_deps.ts";
2+
import { BundleError } from "../errors.ts";
3+
import { Deno2Bundler } from "./deno2_bundler.ts";
4+
5+
Deno.test("Deno2 Bundler tests", async (t) => {
6+
await t.step(Deno2Bundler.bundle.name, async (tt) => {
7+
const expectedEntrypoint = "./function.ts";
8+
const expectedOutFile = "./dist/bundle.ts";
9+
10+
await tt.step(
11+
"should invoke 'deno bundle' successfully",
12+
async () => {
13+
const commandResp = {
14+
output: () => Promise.resolve({ code: 0 }),
15+
} as Deno.Command;
16+
17+
const commandStub = stub(
18+
Deno,
19+
"Command",
20+
() => commandResp,
21+
);
22+
23+
try {
24+
await Deno2Bundler.bundle(
25+
{ entrypoint: expectedEntrypoint, outFile: expectedOutFile },
26+
);
27+
assertSpyCall(commandStub, 0, {
28+
args: [
29+
Deno.execPath(),
30+
{
31+
args: [
32+
"bundle",
33+
"--quiet",
34+
expectedEntrypoint,
35+
"--output",
36+
expectedOutFile,
37+
],
38+
},
39+
],
40+
});
41+
} finally {
42+
commandStub.restore();
43+
}
44+
},
45+
);
46+
47+
await tt.step(
48+
"should throw an exception if the 'deno bundle' command fails",
49+
async () => {
50+
const commandResp = {
51+
output: () =>
52+
Promise.resolve({
53+
code: 1,
54+
stderr: new TextEncoder().encode(
55+
"error: unrecognized subcommand 'bundle'",
56+
),
57+
}),
58+
} as Deno.Command;
59+
60+
const commandStub = stub(
61+
Deno,
62+
"Command",
63+
() => commandResp,
64+
);
65+
66+
try {
67+
await assertRejects(
68+
() =>
69+
Deno2Bundler.bundle(
70+
{
71+
entrypoint: expectedEntrypoint,
72+
outFile: expectedOutFile,
73+
},
74+
),
75+
BundleError,
76+
"Error bundling function file",
77+
);
78+
assertSpyCall(commandStub, 0, {
79+
args: [
80+
Deno.execPath(),
81+
{
82+
args: [
83+
"bundle",
84+
"--quiet",
85+
expectedEntrypoint,
86+
"--output",
87+
expectedOutFile,
88+
],
89+
},
90+
],
91+
});
92+
} finally {
93+
commandStub.restore();
94+
}
95+
},
96+
);
97+
});
98+
});

src/bundler/deno_bundler.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ type DenoBundleOptions = {
77
outFile: string;
88
};
99

10+
/**
11+
* @deprecated This bundler only works with Deno 1.x versions. For Deno 2.x and newer,
12+
* use the Deno2Bundler or EsbuildBundler instead.
13+
*/
14+
1015
export const DenoBundler = {
1116
bundle: async (options: DenoBundleOptions): Promise<void> => {
1217
// call out to deno to handle bundling

src/bundler/mods.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { EsbuildBundler } from "./esbuild_bundler.ts";
22
export { DenoBundler } from "./deno_bundler.ts";
3+
export { Deno2Bundler } from "./deno2_bundler.ts";

src/get_manifest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ async function readImportedManifestFile(filename: string) {
115115

116116
// `getDefaultExport` will throw if no default export present
117117
const manifest = await getDefaultExport(filename);
118-
if (typeof manifest != "object") {
118+
if (typeof manifest !== "object") {
119119
throw new Error(
120120
`Manifest file: ${filename} default export is not an object!`,
121121
);

0 commit comments

Comments
 (0)