alchemy 0.10.3 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/aws/function.d.ts +4 -3
- package/lib/aws/function.js +5 -6
- package/lib/cloudflare/auth.js +17 -3
- package/lib/cloudflare/d1-database.js +1 -0
- package/lib/cloudflare/worker.js +9 -1
- package/lib/cloudflare/wrangler.json.d.ts +1 -0
- package/lib/cloudflare/wrangler.json.js +1 -0
- package/lib/esbuild/bundle.d.ts +13 -5
- package/lib/esbuild/bundle.js +43 -31
- package/package.json +1 -1
- package/src/aws/function.ts +11 -9
- package/src/cloudflare/auth.ts +22 -5
- package/src/cloudflare/d1-database.ts +1 -0
- package/src/cloudflare/worker.ts +7 -1
- package/src/cloudflare/wrangler.json.ts +3 -0
- package/src/esbuild/bundle.ts +54 -46
package/lib/aws/function.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Architecture, Runtime } from "@aws-sdk/client-lambda";
|
|
2
2
|
import type { Context } from "../context";
|
|
3
|
+
import type { Bundle } from "../esbuild/bundle";
|
|
3
4
|
import { Resource } from "../resource";
|
|
4
5
|
/**
|
|
5
6
|
* Properties for creating or updating a Lambda function
|
|
@@ -10,9 +11,9 @@ export interface FunctionProps {
|
|
|
10
11
|
*/
|
|
11
12
|
functionName: string;
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
|
+
* Bundle for the function
|
|
14
15
|
*/
|
|
15
|
-
|
|
16
|
+
bundle: Bundle;
|
|
16
17
|
/**
|
|
17
18
|
* ARN of the IAM role that Lambda assumes when executing the function
|
|
18
19
|
*/
|
|
@@ -21,7 +22,7 @@ export interface FunctionProps {
|
|
|
21
22
|
* Function handler in the format 'file.function'
|
|
22
23
|
* For Node.js this is typically 'index.handler' or similar
|
|
23
24
|
*/
|
|
24
|
-
handler
|
|
25
|
+
handler: string;
|
|
25
26
|
/**
|
|
26
27
|
* Lambda runtime environment for the function
|
|
27
28
|
* @default nodejs20.x if not specified
|
package/lib/aws/function.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { AddPermissionCommand, Architecture, CreateFunctionCommand, CreateFunctionUrlConfigCommand, DeleteFunctionCommand, DeleteFunctionUrlConfigCommand, GetFunctionCommand, GetFunctionConfigurationCommand, GetFunctionUrlConfigCommand, LambdaClient, ResourceNotFoundException, Runtime, UpdateFunctionCodeCommand, UpdateFunctionConfigurationCommand, UpdateFunctionUrlConfigCommand, } from "@aws-sdk/client-lambda";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
2
|
import { Resource } from "../resource";
|
|
5
3
|
import { ignore } from "../util/ignore";
|
|
6
4
|
/**
|
|
@@ -60,7 +58,7 @@ import { ignore } from "../util/ignore";
|
|
|
60
58
|
export const Function = Resource("lambda::Function", async function (id, props) {
|
|
61
59
|
const client = new LambdaClient({});
|
|
62
60
|
const region = await resolveRegion(client);
|
|
63
|
-
const code = await zipCode(props
|
|
61
|
+
const code = await zipCode(props);
|
|
64
62
|
if (this.phase === "delete") {
|
|
65
63
|
// Delete function URL if it exists
|
|
66
64
|
if (this.output?.url) {
|
|
@@ -408,9 +406,10 @@ async function waitForFunctionStabilization(client, functionName) {
|
|
|
408
406
|
}
|
|
409
407
|
}
|
|
410
408
|
// Helper to zip the code
|
|
411
|
-
async function zipCode(
|
|
412
|
-
const fileContent =
|
|
413
|
-
const fileName =
|
|
409
|
+
async function zipCode(props) {
|
|
410
|
+
const fileContent = props.bundle.content;
|
|
411
|
+
const fileName = props.handler.split(".").reverse().slice(0, -1).reverse().join(".") +
|
|
412
|
+
(props.bundle.format === "cjs" ? ".cjs" : ".mjs");
|
|
414
413
|
// Create a zip buffer in memory
|
|
415
414
|
const zip = new (await import("jszip")).default();
|
|
416
415
|
zip.file(fileName, fileContent);
|
package/lib/cloudflare/auth.js
CHANGED
|
@@ -126,6 +126,8 @@ async function getRefreshedAuthConfig() {
|
|
|
126
126
|
return authConfig;
|
|
127
127
|
}
|
|
128
128
|
async function writeWranglerConfig(config) {
|
|
129
|
+
if (config.exists === false)
|
|
130
|
+
return;
|
|
129
131
|
const TOML = await import("@iarna/toml");
|
|
130
132
|
const configPath = await findWranglerConfig();
|
|
131
133
|
config = {
|
|
@@ -140,9 +142,21 @@ async function writeWranglerConfig(config) {
|
|
|
140
142
|
const authConfigCache = {};
|
|
141
143
|
async function readWranglerConfig() {
|
|
142
144
|
const configPath = await findWranglerConfig();
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
try {
|
|
146
|
+
const config = (authConfigCache[configPath] ??= await parseTOML(await fs.readFile(configPath, "utf-8")));
|
|
147
|
+
config.path = configPath;
|
|
148
|
+
return config;
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
if (e.code === "ENOENT") {
|
|
152
|
+
// The config doesn't exist
|
|
153
|
+
return {
|
|
154
|
+
path: configPath,
|
|
155
|
+
exists: false
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
throw e;
|
|
159
|
+
}
|
|
146
160
|
}
|
|
147
161
|
let wranglerConfigPath;
|
|
148
162
|
async function findWranglerConfig() {
|
|
@@ -139,6 +139,7 @@ export const D1DatabaseResource = Resource("cloudflare::D1Database", async funct
|
|
|
139
139
|
readReplication: dbData.result.read_replication,
|
|
140
140
|
primaryLocationHint: props.primaryLocationHint,
|
|
141
141
|
accountId: api.accountId,
|
|
142
|
+
migrationsDir: props.migrationsDir
|
|
142
143
|
});
|
|
143
144
|
}
|
|
144
145
|
});
|
package/lib/cloudflare/worker.js
CHANGED
|
@@ -456,7 +456,15 @@ async function bundleWorkerScript(props) {
|
|
|
456
456
|
external: [...(bundleOptions.external ?? []), "cloudflare:workers"],
|
|
457
457
|
});
|
|
458
458
|
try {
|
|
459
|
-
|
|
459
|
+
if (bundle.content) {
|
|
460
|
+
return bundle.content;
|
|
461
|
+
}
|
|
462
|
+
else if (bundle.path) {
|
|
463
|
+
return await fs.readFile(bundle.path, "utf-8");
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
throw new Error("Failed to create bundle");
|
|
467
|
+
}
|
|
460
468
|
}
|
|
461
469
|
catch (error) {
|
|
462
470
|
console.error("Error reading bundle:", error);
|
package/lib/esbuild/bundle.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import esbuild from "esbuild";
|
|
2
2
|
import type { Context } from "../context";
|
|
3
3
|
import { Resource } from "../resource";
|
|
4
4
|
/**
|
|
@@ -64,17 +64,25 @@ export interface BundleProps {
|
|
|
64
64
|
/**
|
|
65
65
|
* Output returned after bundle creation/update
|
|
66
66
|
*/
|
|
67
|
-
export interface Bundle extends Resource<"esbuild::Bundle"
|
|
67
|
+
export interface Bundle<P extends BundleProps = BundleProps> extends Resource<"esbuild::Bundle">, BundleProps {
|
|
68
68
|
/**
|
|
69
69
|
* Path to the bundled file
|
|
70
70
|
* Absolute or relative path to the generated bundle
|
|
71
71
|
*/
|
|
72
|
-
path:
|
|
72
|
+
path: P extends {
|
|
73
|
+
outdir: string;
|
|
74
|
+
} | {
|
|
75
|
+
outfile: string;
|
|
76
|
+
} ? string : undefined;
|
|
73
77
|
/**
|
|
74
78
|
* SHA-256 hash of the bundle contents
|
|
75
79
|
* Used for cache busting and content verification
|
|
76
80
|
*/
|
|
77
81
|
hash: string;
|
|
82
|
+
/**
|
|
83
|
+
* The content of the bundle (the .js or .mjs file)
|
|
84
|
+
*/
|
|
85
|
+
content: string;
|
|
78
86
|
}
|
|
79
87
|
/**
|
|
80
88
|
* esbuild Bundle Resource
|
|
@@ -92,8 +100,9 @@ export interface Bundle extends Resource<"esbuild::Bundle"> {
|
|
|
92
100
|
* target: "node18"
|
|
93
101
|
* });
|
|
94
102
|
*/
|
|
95
|
-
export declare const Bundle: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<Bundle
|
|
103
|
+
export declare const Bundle: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | (<Props extends BundleProps>(this: Context<Bundle<any>>, id: string, props: Props) => Promise<Bundle<Props>>);
|
|
96
104
|
export declare function bundle(props: BundleProps): Promise<esbuild.BuildResult<{
|
|
105
|
+
write: boolean;
|
|
97
106
|
entryPoints: string[];
|
|
98
107
|
outdir: string | undefined;
|
|
99
108
|
outfile: string | undefined;
|
|
@@ -105,7 +114,6 @@ export declare function bundle(props: BundleProps): Promise<esbuild.BuildResult<
|
|
|
105
114
|
external: string[];
|
|
106
115
|
platform: "browser" | "node" | "neutral" | undefined;
|
|
107
116
|
metafile: true;
|
|
108
|
-
write: true;
|
|
109
117
|
splitting?: boolean;
|
|
110
118
|
preserveSymlinks?: boolean;
|
|
111
119
|
outbase?: string;
|
package/lib/esbuild/bundle.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import esbuild from "esbuild";
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
|
-
import fs from "node:fs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { Resource } from "../resource";
|
|
6
6
|
/**
|
|
@@ -22,20 +22,25 @@ import { Resource } from "../resource";
|
|
|
22
22
|
export const Bundle = Resource("esbuild::Bundle", {
|
|
23
23
|
alwaysUpdate: true,
|
|
24
24
|
}, async function (id, props) {
|
|
25
|
-
// Determine output path
|
|
26
|
-
const outDirPath = props.outfile != null ? path.dirname(props.outfile) : props.outdir;
|
|
27
|
-
if (outDirPath == null) {
|
|
28
|
-
throw new Error(`You need to specify either outfile or outdir in your bundle configuration ${JSON.stringify(props)}`);
|
|
29
|
-
}
|
|
30
|
-
// Ensure output directory exists
|
|
31
|
-
await fs.promises.mkdir(path.dirname(outDirPath), { recursive: true });
|
|
32
25
|
if (this.phase === "delete") {
|
|
33
|
-
|
|
26
|
+
console.log("delete", this.output.path);
|
|
27
|
+
if (this.output.path) {
|
|
28
|
+
try {
|
|
29
|
+
await fs.rm(this.output.path, { force: true });
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (error instanceof Error && error.message.includes("ENOENT")) {
|
|
33
|
+
// File doesn't exist, so we can ignore the error
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return this.destroy();
|
|
34
41
|
}
|
|
35
42
|
const result = await bundle(props);
|
|
36
|
-
|
|
37
|
-
// Use bundle metada data to retrieve its content
|
|
38
|
-
const bundleOutputPath = Object.entries(result.metafile.outputs).find(([file, output]) => {
|
|
43
|
+
const bundlePath = Object.entries(result.metafile.outputs).find(([_, output]) => {
|
|
39
44
|
if (output.entryPoint === undefined) {
|
|
40
45
|
return false;
|
|
41
46
|
}
|
|
@@ -44,29 +49,37 @@ export const Bundle = Resource("esbuild::Bundle", {
|
|
|
44
49
|
return (relativeOutput ===
|
|
45
50
|
path.relative(process.cwd(), path.resolve(process.cwd(), props.entryPoint)));
|
|
46
51
|
})?.[0];
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
const outputFile = result.outputFiles?.[0];
|
|
53
|
+
if (outputFile === undefined && bundlePath === undefined) {
|
|
54
|
+
throw new Error("Failed to create bundle");
|
|
55
|
+
}
|
|
56
|
+
else if (outputFile) {
|
|
57
|
+
return this({
|
|
58
|
+
...props,
|
|
59
|
+
path: bundlePath,
|
|
60
|
+
hash: outputFile.hash,
|
|
61
|
+
content: outputFile.text,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const content = await fs.readFile(bundlePath, "utf-8");
|
|
66
|
+
return this({
|
|
67
|
+
...props,
|
|
68
|
+
path: bundlePath,
|
|
69
|
+
hash: crypto.createHash("sha256").update(content).digest("hex"),
|
|
70
|
+
content,
|
|
71
|
+
});
|
|
49
72
|
}
|
|
50
|
-
// Calculate hash of the output
|
|
51
|
-
const bundleOutputContent = await fs.promises.readFile(bundleOutputPath);
|
|
52
|
-
const hash = crypto
|
|
53
|
-
.createHash("sha256")
|
|
54
|
-
.update(bundleOutputContent)
|
|
55
|
-
.digest("hex");
|
|
56
|
-
// Store metadata in context
|
|
57
|
-
await this.set("metafile", result.metafile);
|
|
58
|
-
await this.set("hash", hash);
|
|
59
|
-
// Return output info
|
|
60
|
-
return this({
|
|
61
|
-
path: bundleOutputPath,
|
|
62
|
-
hash,
|
|
63
|
-
});
|
|
64
73
|
});
|
|
65
74
|
export async function bundle(props) {
|
|
66
75
|
return await esbuild.build({
|
|
67
76
|
...props.options,
|
|
77
|
+
write: !(props.outdir === undefined && props.outfile === undefined),
|
|
78
|
+
// write:
|
|
79
|
+
// props.outdir === undefined && props.outfile === undefined ? false : true,
|
|
80
|
+
// write: false,
|
|
68
81
|
entryPoints: [props.entryPoint],
|
|
69
|
-
outdir: props.outdir,
|
|
82
|
+
outdir: props.outdir ? props.outdir : props.outfile ? undefined : ".out",
|
|
70
83
|
outfile: props.outfile,
|
|
71
84
|
bundle: true,
|
|
72
85
|
format: props.format,
|
|
@@ -80,6 +93,5 @@ export async function bundle(props) {
|
|
|
80
93
|
],
|
|
81
94
|
platform: props.platform,
|
|
82
95
|
metafile: true,
|
|
83
|
-
write: true,
|
|
84
96
|
});
|
|
85
97
|
}
|
package/package.json
CHANGED
package/src/aws/function.ts
CHANGED
|
@@ -15,9 +15,8 @@ import {
|
|
|
15
15
|
UpdateFunctionConfigurationCommand,
|
|
16
16
|
UpdateFunctionUrlConfigCommand,
|
|
17
17
|
} from "@aws-sdk/client-lambda";
|
|
18
|
-
import fs from "node:fs";
|
|
19
|
-
import path from "node:path";
|
|
20
18
|
import type { Context } from "../context";
|
|
19
|
+
import type { Bundle } from "../esbuild/bundle";
|
|
21
20
|
import { Resource } from "../resource";
|
|
22
21
|
import { ignore } from "../util/ignore";
|
|
23
22
|
|
|
@@ -31,9 +30,9 @@ export interface FunctionProps {
|
|
|
31
30
|
functionName: string;
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
|
-
*
|
|
33
|
+
* Bundle for the function
|
|
35
34
|
*/
|
|
36
|
-
|
|
35
|
+
bundle: Bundle;
|
|
37
36
|
|
|
38
37
|
/**
|
|
39
38
|
* ARN of the IAM role that Lambda assumes when executing the function
|
|
@@ -44,7 +43,7 @@ export interface FunctionProps {
|
|
|
44
43
|
* Function handler in the format 'file.function'
|
|
45
44
|
* For Node.js this is typically 'index.handler' or similar
|
|
46
45
|
*/
|
|
47
|
-
handler
|
|
46
|
+
handler: string;
|
|
48
47
|
|
|
49
48
|
/**
|
|
50
49
|
* Lambda runtime environment for the function
|
|
@@ -301,7 +300,7 @@ export const Function = Resource(
|
|
|
301
300
|
const client = new LambdaClient({});
|
|
302
301
|
const region = await resolveRegion(client);
|
|
303
302
|
|
|
304
|
-
const code = await zipCode(props
|
|
303
|
+
const code = await zipCode(props);
|
|
305
304
|
|
|
306
305
|
if (this.phase === "delete") {
|
|
307
306
|
// Delete function URL if it exists
|
|
@@ -714,9 +713,12 @@ async function waitForFunctionStabilization(
|
|
|
714
713
|
}
|
|
715
714
|
|
|
716
715
|
// Helper to zip the code
|
|
717
|
-
async function zipCode(
|
|
718
|
-
const fileContent =
|
|
719
|
-
|
|
716
|
+
async function zipCode(props: FunctionProps): Promise<Buffer> {
|
|
717
|
+
const fileContent = props.bundle.content;
|
|
718
|
+
|
|
719
|
+
const fileName =
|
|
720
|
+
props.handler.split(".").reverse().slice(0, -1).reverse().join(".") +
|
|
721
|
+
(props.bundle.format === "cjs" ? ".cjs" : ".mjs");
|
|
720
722
|
|
|
721
723
|
// Create a zip buffer in memory
|
|
722
724
|
const zip = new (await import("jszip")).default();
|
package/src/cloudflare/auth.ts
CHANGED
|
@@ -239,6 +239,8 @@ interface WranglerConfig {
|
|
|
239
239
|
refresh_token?: string;
|
|
240
240
|
expiration_time?: string;
|
|
241
241
|
scopes?: string[];
|
|
242
|
+
/** exists is `false` if the config file doesn't exist, like in CI */
|
|
243
|
+
exists?: boolean;
|
|
242
244
|
/** @deprecated - this field was only provided by the deprecated v1 `wrangler config` command. */
|
|
243
245
|
api_token?: string;
|
|
244
246
|
}
|
|
@@ -258,6 +260,8 @@ async function getRefreshedAuthConfig(): Promise<WranglerConfig> {
|
|
|
258
260
|
}
|
|
259
261
|
|
|
260
262
|
async function writeWranglerConfig(config: WranglerConfig) {
|
|
263
|
+
if (config.exists === false) return
|
|
264
|
+
|
|
261
265
|
const TOML = await import("@iarna/toml");
|
|
262
266
|
const configPath = await findWranglerConfig();
|
|
263
267
|
config = {
|
|
@@ -274,11 +278,24 @@ const authConfigCache: Record<string, WranglerConfig> = {};
|
|
|
274
278
|
|
|
275
279
|
async function readWranglerConfig(): Promise<WranglerConfig> {
|
|
276
280
|
const configPath = await findWranglerConfig();
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
281
|
+
try {
|
|
282
|
+
const config = (authConfigCache[configPath] ??= await parseTOML(
|
|
283
|
+
await fs.readFile(configPath, "utf-8")
|
|
284
|
+
));
|
|
285
|
+
config.path = configPath;
|
|
286
|
+
|
|
287
|
+
return config
|
|
288
|
+
} catch (e: any) {
|
|
289
|
+
if (e.code === "ENOENT") {
|
|
290
|
+
// The config doesn't exist
|
|
291
|
+
return {
|
|
292
|
+
path: configPath,
|
|
293
|
+
exists: false
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
throw e
|
|
298
|
+
}
|
|
282
299
|
}
|
|
283
300
|
|
|
284
301
|
let wranglerConfigPath: string | undefined;
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -747,7 +747,13 @@ async function bundleWorkerScript<B extends Bindings>(props: WorkerProps) {
|
|
|
747
747
|
});
|
|
748
748
|
|
|
749
749
|
try {
|
|
750
|
-
|
|
750
|
+
if (bundle.content) {
|
|
751
|
+
return bundle.content;
|
|
752
|
+
} else if (bundle.path) {
|
|
753
|
+
return await fs.readFile(bundle.path, "utf-8");
|
|
754
|
+
} else {
|
|
755
|
+
throw new Error("Failed to create bundle");
|
|
756
|
+
}
|
|
751
757
|
} catch (error) {
|
|
752
758
|
console.error("Error reading bundle:", error);
|
|
753
759
|
throw new Error("Error reading bundle");
|
|
@@ -207,6 +207,7 @@ export interface WranglerJsonSpec {
|
|
|
207
207
|
binding: string;
|
|
208
208
|
database_id: string;
|
|
209
209
|
database_name: string;
|
|
210
|
+
migrations_dir?: string
|
|
210
211
|
}[];
|
|
211
212
|
|
|
212
213
|
/**
|
|
@@ -254,6 +255,7 @@ function processBindings(spec: WranglerJsonSpec, bindings: Bindings): void {
|
|
|
254
255
|
binding: string;
|
|
255
256
|
database_id: string;
|
|
256
257
|
database_name: string;
|
|
258
|
+
migrations_dir?: string;
|
|
257
259
|
}[] = [];
|
|
258
260
|
const queues: { binding: string; queue: string }[] = [];
|
|
259
261
|
const vectorizeIndexes: { binding: string; index_name: string }[] = [];
|
|
@@ -314,6 +316,7 @@ function processBindings(spec: WranglerJsonSpec, bindings: Bindings): void {
|
|
|
314
316
|
binding: bindingName,
|
|
315
317
|
database_id: binding.id,
|
|
316
318
|
database_name: binding.name,
|
|
319
|
+
migrations_dir: binding.migrationsDir,
|
|
317
320
|
});
|
|
318
321
|
} else if (binding.type === "queue") {
|
|
319
322
|
queues.push({
|
package/src/esbuild/bundle.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import esbuild from "esbuild";
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
|
-
import fs from "node:fs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import type { Context } from "../context";
|
|
6
6
|
import { Resource } from "../resource";
|
|
@@ -78,18 +78,25 @@ export interface BundleProps {
|
|
|
78
78
|
/**
|
|
79
79
|
* Output returned after bundle creation/update
|
|
80
80
|
*/
|
|
81
|
-
export interface Bundle extends
|
|
81
|
+
export interface Bundle<P extends BundleProps = BundleProps>
|
|
82
|
+
extends Resource<"esbuild::Bundle">,
|
|
83
|
+
BundleProps {
|
|
82
84
|
/**
|
|
83
85
|
* Path to the bundled file
|
|
84
86
|
* Absolute or relative path to the generated bundle
|
|
85
87
|
*/
|
|
86
|
-
path: string;
|
|
88
|
+
path: P extends { outdir: string } | { outfile: string } ? string : undefined;
|
|
87
89
|
|
|
88
90
|
/**
|
|
89
91
|
* SHA-256 hash of the bundle contents
|
|
90
92
|
* Used for cache busting and content verification
|
|
91
93
|
*/
|
|
92
94
|
hash: string;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The content of the bundle (the .js or .mjs file)
|
|
98
|
+
*/
|
|
99
|
+
content: string;
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
/**
|
|
@@ -113,32 +120,31 @@ export const Bundle = Resource(
|
|
|
113
120
|
{
|
|
114
121
|
alwaysUpdate: true,
|
|
115
122
|
},
|
|
116
|
-
async function (
|
|
117
|
-
this: Context<Bundle
|
|
123
|
+
async function <Props extends BundleProps>(
|
|
124
|
+
this: Context<Bundle<any>>,
|
|
118
125
|
id: string,
|
|
119
|
-
props:
|
|
120
|
-
): Promise<Bundle
|
|
121
|
-
// Determine output path
|
|
122
|
-
const outDirPath =
|
|
123
|
-
props.outfile != null ? path.dirname(props.outfile) : props.outdir;
|
|
124
|
-
|
|
125
|
-
if (outDirPath == null) {
|
|
126
|
-
throw new Error(
|
|
127
|
-
`You need to specify either outfile or outdir in your bundle configuration ${JSON.stringify(props)}`
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Ensure output directory exists
|
|
132
|
-
await fs.promises.mkdir(path.dirname(outDirPath), { recursive: true });
|
|
126
|
+
props: Props
|
|
127
|
+
): Promise<Bundle<Props>> {
|
|
133
128
|
if (this.phase === "delete") {
|
|
134
|
-
|
|
129
|
+
console.log("delete", this.output.path);
|
|
130
|
+
if (this.output.path) {
|
|
131
|
+
try {
|
|
132
|
+
await fs.rm(this.output.path, { force: true });
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (error instanceof Error && error.message.includes("ENOENT")) {
|
|
135
|
+
// File doesn't exist, so we can ignore the error
|
|
136
|
+
} else {
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return this.destroy();
|
|
135
142
|
}
|
|
136
143
|
|
|
137
144
|
const result = await bundle(props);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
([file, output]) => {
|
|
145
|
+
|
|
146
|
+
const bundlePath = Object.entries(result.metafile.outputs).find(
|
|
147
|
+
([_, output]) => {
|
|
142
148
|
if (output.entryPoint === undefined) {
|
|
143
149
|
return false;
|
|
144
150
|
}
|
|
@@ -157,34 +163,37 @@ export const Bundle = Resource(
|
|
|
157
163
|
}
|
|
158
164
|
)?.[0];
|
|
159
165
|
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
const outputFile = result.outputFiles?.[0];
|
|
167
|
+
if (outputFile === undefined && bundlePath === undefined) {
|
|
168
|
+
throw new Error("Failed to create bundle");
|
|
169
|
+
} else if (outputFile) {
|
|
170
|
+
return this({
|
|
171
|
+
...props,
|
|
172
|
+
path: bundlePath,
|
|
173
|
+
hash: outputFile.hash,
|
|
174
|
+
content: outputFile.text,
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
const content = await fs.readFile(bundlePath!, "utf-8");
|
|
178
|
+
return this({
|
|
179
|
+
...props,
|
|
180
|
+
path: bundlePath,
|
|
181
|
+
hash: crypto.createHash("sha256").update(content).digest("hex"),
|
|
182
|
+
content,
|
|
183
|
+
});
|
|
162
184
|
}
|
|
163
|
-
|
|
164
|
-
// Calculate hash of the output
|
|
165
|
-
const bundleOutputContent = await fs.promises.readFile(bundleOutputPath);
|
|
166
|
-
const hash = crypto
|
|
167
|
-
.createHash("sha256")
|
|
168
|
-
.update(bundleOutputContent)
|
|
169
|
-
.digest("hex");
|
|
170
|
-
|
|
171
|
-
// Store metadata in context
|
|
172
|
-
await this.set("metafile", result.metafile);
|
|
173
|
-
await this.set("hash", hash);
|
|
174
|
-
|
|
175
|
-
// Return output info
|
|
176
|
-
return this({
|
|
177
|
-
path: bundleOutputPath,
|
|
178
|
-
hash,
|
|
179
|
-
});
|
|
180
185
|
}
|
|
181
186
|
);
|
|
182
187
|
|
|
183
188
|
export async function bundle(props: BundleProps) {
|
|
184
189
|
return await esbuild.build({
|
|
185
190
|
...props.options,
|
|
191
|
+
write: !(props.outdir === undefined && props.outfile === undefined),
|
|
192
|
+
// write:
|
|
193
|
+
// props.outdir === undefined && props.outfile === undefined ? false : true,
|
|
194
|
+
// write: false,
|
|
186
195
|
entryPoints: [props.entryPoint],
|
|
187
|
-
outdir: props.outdir,
|
|
196
|
+
outdir: props.outdir ? props.outdir : props.outfile ? undefined : ".out",
|
|
188
197
|
outfile: props.outfile,
|
|
189
198
|
bundle: true,
|
|
190
199
|
format: props.format,
|
|
@@ -198,6 +207,5 @@ export async function bundle(props: BundleProps) {
|
|
|
198
207
|
],
|
|
199
208
|
platform: props.platform,
|
|
200
209
|
metafile: true,
|
|
201
|
-
write: true,
|
|
202
210
|
});
|
|
203
211
|
}
|