From ce6100066e0c20d010f5188402077e1bd1ab4005 Mon Sep 17 00:00:00 2001 From: Sunil Pai Date: Sat, 29 Jan 2022 20:34:02 +0000 Subject: [PATCH] feat: Workers Sites support for local mode (#336) * chore: example-sites-app An example app with a Workers Sites definition. * feat: inline text-like files into the worker bundle We were adding text-like modules (i.e. `.txt`, `.html` and `.pem` files) as separate modules in the Worker definition, but this only really 'works' with the ES module Worker format. This commit changes that to inline the text-like files into the Worker bundle directly. We still have to do something similar with `.wasm` modules, but that requires a different fix, and we'll do so in a subsequent commit. * feat: Sites support for local mode `wrangler dev` This adds support for Workers Sites in local mode when running wrangler `dev`. Further, it fixes a bug where we were sending the `__STATIC_CONTENT_MANIFEST` definition as a separate module even with service worker format, and a bug where we weren't uploading the namespace binding when other kv namespaces weren't present. * Update packages/wrangler/src/api/form_data.ts Co-authored-by: Pete Bacon Darwin Co-authored-by: Pete Bacon Darwin --- .changeset/nervous-pugs-lay.md | 9 + .changeset/red-pillows-grow.md | 7 + package-lock.json | 36 +- packages/example-sites-app/package.json | 8 + packages/example-sites-app/public/404.html | 50 +++ packages/example-sites-app/public/favicon.ico | Bin 0 -> 1150 bytes .../public/img/200-wrangler-ferris.gif | Bin 0 -> 45598 bytes .../public/img/404-wrangler-ferris.gif | Bin 0 -> 31606 bytes packages/example-sites-app/public/index.html | 50 +++ packages/example-sites-app/src/index.js | 66 ++++ packages/example-sites-app/wrangler.toml | 2 + packages/wrangler/src/__tests__/kv.test.ts | 41 +-- .../wrangler/src/__tests__/logout.test.ts | 2 +- packages/wrangler/src/__tests__/mock-kv.ts | 40 +++ packages/wrangler/src/__tests__/mock-user.ts | 27 ++ .../wrangler/src/__tests__/publish.test.ts | 321 +++++++++++++++--- .../wrangler/src/__tests__/whoami.test.tsx | 28 +- packages/wrangler/src/api/form_data.ts | 6 +- packages/wrangler/src/dev.tsx | 49 ++- packages/wrangler/src/module-collection.ts | 5 +- packages/wrangler/src/publish.ts | 18 +- 21 files changed, 636 insertions(+), 129 deletions(-) create mode 100644 .changeset/nervous-pugs-lay.md create mode 100644 .changeset/red-pillows-grow.md create mode 100644 packages/example-sites-app/package.json create mode 100644 packages/example-sites-app/public/404.html create mode 100644 packages/example-sites-app/public/favicon.ico create mode 100644 packages/example-sites-app/public/img/200-wrangler-ferris.gif create mode 100644 packages/example-sites-app/public/img/404-wrangler-ferris.gif create mode 100644 packages/example-sites-app/public/index.html create mode 100644 packages/example-sites-app/src/index.js create mode 100644 packages/example-sites-app/wrangler.toml create mode 100644 packages/wrangler/src/__tests__/mock-kv.ts create mode 100644 packages/wrangler/src/__tests__/mock-user.ts diff --git a/.changeset/nervous-pugs-lay.md b/.changeset/nervous-pugs-lay.md new file mode 100644 index 000000000000..135907fd6241 --- /dev/null +++ b/.changeset/nervous-pugs-lay.md @@ -0,0 +1,9 @@ +--- +"wrangler": patch +--- + +feat: inline text-like files into the worker bundle + +We were adding text-like modules (i.e. `.txt`, `.html` and `.pem` files) as separate modules in the Worker definition, but this only really 'works' with the ES module Worker format. This commit changes that to inline the text-like files into the Worker bundle directly. + +We still have to do something similar with `.wasm` modules, but that requires a different fix, and we'll do so in a subsequent commit. diff --git a/.changeset/red-pillows-grow.md b/.changeset/red-pillows-grow.md new file mode 100644 index 000000000000..dd3b84e60078 --- /dev/null +++ b/.changeset/red-pillows-grow.md @@ -0,0 +1,7 @@ +--- +"wrangler": patch +--- + +feat: Sites support for local mode `wrangler dev` + +This adds support for Workers Sites in local mode when running wrangler `dev`. Further, it fixes a bug where we were sending the `__STATIC_CONTENT_MANIFEST` definition as a separate module even with service worker format, and a bug where we weren't uploading the namespace binding when other kv namespaces weren't present. diff --git a/package-lock.json b/package-lock.json index 769bd29cfa86..8585abf29de8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -753,6 +753,14 @@ "node": ">=4" } }, + "node_modules/@cloudflare/kv-asset-handler": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", + "integrity": "sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==", + "dependencies": { + "mime": "^3.0.0" + } + }, "node_modules/@cnakazawa/watch": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", @@ -5578,6 +5586,10 @@ "node": ">= 0.6" } }, + "node_modules/example-sites-app": { + "resolved": "packages/example-sites-app", + "link": true + }, "node_modules/example-worker-app": { "resolved": "packages/example-worker-app", "link": true @@ -11033,7 +11045,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "dev": true, "bin": { "mime": "cli.js" }, @@ -15142,6 +15153,12 @@ "version": "1.0.0", "license": "ISC" }, + "packages/example-sites-app": { + "version": "1.0.0", + "dependencies": { + "@cloudflare/kv-asset-handler": "~0.2.0" + } + }, "packages/example-worker-app": { "version": "1.0.0", "license": "ISC" @@ -16036,6 +16053,14 @@ } } }, + "@cloudflare/kv-asset-handler": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.2.0.tgz", + "integrity": "sha512-MVbXLbTcAotOPUj0pAMhVtJ+3/kFkwJqc5qNOleOZTv6QkZZABDMS21dSrSlVswEHwrpWC03e4fWytjqKvuE2A==", + "requires": { + "mime": "^3.0.0" + } + }, "@cnakazawa/watch": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", @@ -19603,6 +19628,12 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, + "example-sites-app": { + "version": "file:packages/example-sites-app", + "requires": { + "@cloudflare/kv-asset-handler": "~0.2.0" + } + }, "example-worker-app": { "version": "file:packages/example-worker-app" }, @@ -23633,8 +23664,7 @@ "mime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "dev": true + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==" }, "mime-db": { "version": "1.51.0", diff --git a/packages/example-sites-app/package.json b/packages/example-sites-app/package.json new file mode 100644 index 000000000000..1b635fcbeb2b --- /dev/null +++ b/packages/example-sites-app/package.json @@ -0,0 +1,8 @@ +{ + "name": "example-sites-app", + "private": true, + "version": "1.0.0", + "dependencies": { + "@cloudflare/kv-asset-handler": "~0.2.0" + } +} diff --git a/packages/example-sites-app/public/404.html b/packages/example-sites-app/public/404.html new file mode 100644 index 000000000000..cc263a84c4a2 --- /dev/null +++ b/packages/example-sites-app/public/404.html @@ -0,0 +1,50 @@ + + + + + + + + + +
+

404 Not Found

+

Oh dang! We couldn't find that page.

+ a sad crab is unable to unable to lasso a paper airplane. 404 not found. +
+ + diff --git a/packages/example-sites-app/public/favicon.ico b/packages/example-sites-app/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..cc6c23bd37bb4fa58f810c5f9e36041d96b23462 GIT binary patch literal 1150 zcmXw&dr(wW9LJA6klCG0Y`fdtcD^W zZ;*%(!UqcotRgi|$B@@&|J#+okFh*l9>gNc5&sPx{583mU`;2{d&-7lcYdrHJ zV6g+cdI9tI3XjTu1YOoNblCzTvxM=;eZu<4eJ-{zQwSS$UpSd=@030w<3Vp+juRO@ zL9RJ=p9em-bEK6D+>qyj;VU=vxdL=KLJy%QSXABXQmS|xl??P zJ@7Pt0LZ-pWRqv}2_KI>X@^vI zWC%*ZH6Z^7?JS=1#1apWEAk-but)QRjD;RcQk6F;q1-dJti+9%zMe&N0Xe>WTv<9C ziZ1^JLq$V?^(WvUd3Ll&e918{;)}hAEAk@tuxD&#y;!xP#;Yb)dhtrB8}UW6lQD(U zsUO)SRzHfJ#4(kINyxrds67%0w(J347EvjXqa$gNRq_L{KiET#i=)tk@A^ z%{O7F8^u7yuR!StaO`K`gR=@xs1&ihLd47DdF`a%mD=RP>Ti5lRZpH;@%ZvG7poan znQa1wrkfb3{~f5l4pfro!!Cs<)%uWB<3nPVh_p7T>h)H?y0+PmlyAw?AWkNq5L5PC z4YH0~Ft?6FZu$dgya5~?QMswb&yySdSk>UCoPFw>XQZU`)6y7uu&%|A)bD(tJymqx z=F#1EFnD$XX!{dr9Roh;5Ad}oC1|8H`LVjuKW4JYY`smkywxs;(oadCT#(xAcb1KY zYWx0za_$b$c^jy?q4F&@nZI#brkrK0Yn4Lev25s)8DS(GlY z-2?1*>Hl8~a8suYQ-=ati{{A=%GtQ7M`3TDBf1qcA!%b-!vWRt2>8(n4Ezl=PpJI; z3ku(44`5@Df(>1YL>@D>;rm1TXM{fOl+?SuL&Ev{D!)Drj64Pgr|2Fzg8ZF}0c`6J zV(WzfHroR|w5EPz_gs98RbVUYM7E^dYswo8jQ>WlTKwn?V7nXQJNgxDzZ?wn?xo;3 mxlH6@Tl)fR@>#})7R+I#Y&5j;3=uJp_O2D{Jpa$vV*dkKub%Ay literal 0 HcmV?d00001 diff --git a/packages/example-sites-app/public/img/200-wrangler-ferris.gif b/packages/example-sites-app/public/img/200-wrangler-ferris.gif new file mode 100644 index 0000000000000000000000000000000000000000..8853751fac7b39559f140971700360fe249fcdc5 GIT binary patch literal 45598 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 Os#2HA)TVYVAOJf?`|0KY literal 0 HcmV?d00001 diff --git a/packages/example-sites-app/public/img/404-wrangler-ferris.gif b/packages/example-sites-app/public/img/404-wrangler-ferris.gif new file mode 100644 index 0000000000000000000000000000000000000000..0ac1479fcf6f4ded732b8b8ea7bfd9f98b5ca3e2 GIT binary patch literal 31606 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 + + + + + + + + +
+

200 Success

+

Hello World! Welcome to your Workers Site.

+ a happy crab is wearing a cowboy hat and holding a lasso. 200 success. +
+ + diff --git a/packages/example-sites-app/src/index.js b/packages/example-sites-app/src/index.js new file mode 100644 index 000000000000..f1f4dd27a5ab --- /dev/null +++ b/packages/example-sites-app/src/index.js @@ -0,0 +1,66 @@ +import { + getAssetFromKV, + mapRequestToAsset, +} from "@cloudflare/kv-asset-handler"; + +/** + * The DEBUG flag will do two things that help during development: + * 1. we will skip caching on the edge, which makes it easier to + * debug. + * 2. we will return an error message on exception in your Response rather + * than the default 404.html page. + */ +const DEBUG = false; + +addEventListener("fetch", (event) => { + event.respondWith(handleEvent(event)); +}); + +async function handleEvent(event) { + let options = {}; + + /** + * You can add custom logic to how we fetch your assets + * by configuring the function `mapRequestToAsset` + */ + // options.mapRequestToAsset = handlePrefix(/^\/docs/) + + try { + if (DEBUG) { + // customize caching + options.cacheControl = { + bypassCache: true, + }; + } + + const page = await getAssetFromKV(event, options); + + // allow headers to be altered + const response = new Response(page.body, page); + + response.headers.set("X-XSS-Protection", "1; mode=block"); + response.headers.set("X-Content-Type-Options", "nosniff"); + response.headers.set("X-Frame-Options", "DENY"); + response.headers.set("Referrer-Policy", "unsafe-url"); + response.headers.set("Feature-Policy", "none"); + + return response; + } catch (e) { + // if an error is thrown try to serve the asset at 404.html + if (!DEBUG) { + try { + let notFoundResponse = await getAssetFromKV(event, { + mapRequestToAsset: (req) => + new Request(`${new URL(req.url).origin}/404.html`, req), + }); + + return new Response(notFoundResponse.body, { + ...notFoundResponse, + status: 404, + }); + } catch (e) {} + } + + return new Response(e.message || e.toString(), { status: 500 }); + } +} diff --git a/packages/example-sites-app/wrangler.toml b/packages/example-sites-app/wrangler.toml new file mode 100644 index 000000000000..f294bc81852e --- /dev/null +++ b/packages/example-sites-app/wrangler.toml @@ -0,0 +1,2 @@ +name = "example-sites-app" +site = { bucket = "./public" } diff --git a/packages/wrangler/src/__tests__/kv.test.ts b/packages/wrangler/src/__tests__/kv.test.ts index b9ee7e5fafc5..2aab087e2792 100644 --- a/packages/wrangler/src/__tests__/kv.test.ts +++ b/packages/wrangler/src/__tests__/kv.test.ts @@ -4,11 +4,11 @@ import { setMockResponse, setMockRawResponse, unsetAllMocks, - createFetchResult, } from "./mock-cfetch"; import { runWrangler } from "./run-wrangler"; import { runInTempDir } from "./run-in-tmp"; import { mockConsoleMethods } from "./mock-console"; +import { mockKeyListRequest } from "./mock-kv"; describe("wrangler", () => { runInTempDir(); @@ -1222,42 +1222,3 @@ function writeWranglerConfig() { "utf-8" ); } - -export function mockKeyListRequest( - expectedNamespaceId: string, - expectedKeys: string[], - keysPerRequest = 1000, - blankCursorValue: "" | undefined | null = undefined -) { - const requests = { count: 0 }; - // See https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys - const expectedKeyObjects = expectedKeys.map((name) => ({ - name, - expiration: 123456789, - metadata: {}, - })); - setMockRawResponse( - "/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys", - "GET", - ([_url, accountId, namespaceId], _init, query) => { - requests.count++; - expect(accountId).toEqual("some-account-id"); - expect(namespaceId).toEqual(expectedNamespaceId); - if (expectedKeyObjects.length <= keysPerRequest) { - return createFetchResult(expectedKeyObjects); - } else { - const start = parseInt(query.get("cursor") ?? "0") || 0; - const end = start + keysPerRequest; - const cursor = end < expectedKeyObjects.length ? end : blankCursorValue; - return createFetchResult( - expectedKeyObjects.slice(start, end), - true, - [], - [], - { cursor } - ); - } - } - ); - return requests; -} diff --git a/packages/wrangler/src/__tests__/logout.test.ts b/packages/wrangler/src/__tests__/logout.test.ts index e58ff907778a..db12cd27a99d 100644 --- a/packages/wrangler/src/__tests__/logout.test.ts +++ b/packages/wrangler/src/__tests__/logout.test.ts @@ -6,7 +6,7 @@ import { runWrangler } from "./run-wrangler"; import { runInTempDir } from "./run-in-tmp"; import { initialise } from "../user"; import { mockConsoleMethods } from "./mock-console"; -import { writeUserConfig } from "./whoami.test"; +import { writeUserConfig } from "./mock-user"; const ORIGINAL_CF_API_TOKEN = process.env.CF_API_TOKEN; const ORIGINAL_CF_ACCOUNT_ID = process.env.CF_ACCOUNT_ID; diff --git a/packages/wrangler/src/__tests__/mock-kv.ts b/packages/wrangler/src/__tests__/mock-kv.ts new file mode 100644 index 000000000000..8c11122495ec --- /dev/null +++ b/packages/wrangler/src/__tests__/mock-kv.ts @@ -0,0 +1,40 @@ +import { createFetchResult, setMockRawResponse } from "./mock-cfetch"; + +export function mockKeyListRequest( + expectedNamespaceId: string, + expectedKeys: string[], + keysPerRequest = 1000, + blankCursorValue: "" | undefined | null = undefined +) { + const requests = { count: 0 }; + // See https://api.cloudflare.com/#workers-kv-namespace-list-a-namespace-s-keys + const expectedKeyObjects = expectedKeys.map((name) => ({ + name, + expiration: 123456789, + metadata: {}, + })); + setMockRawResponse( + "/accounts/:accountId/storage/kv/namespaces/:namespaceId/keys", + "GET", + ([_url, accountId, namespaceId], _init, query) => { + requests.count++; + expect(accountId).toEqual("some-account-id"); + expect(namespaceId).toEqual(expectedNamespaceId); + if (expectedKeyObjects.length <= keysPerRequest) { + return createFetchResult(expectedKeyObjects); + } else { + const start = parseInt(query.get("cursor") ?? "0") || 0; + const end = start + keysPerRequest; + const cursor = end < expectedKeyObjects.length ? end : blankCursorValue; + return createFetchResult( + expectedKeyObjects.slice(start, end), + true, + [], + [], + { cursor } + ); + } + } + ); + return requests; +} diff --git a/packages/wrangler/src/__tests__/mock-user.ts b/packages/wrangler/src/__tests__/mock-user.ts new file mode 100644 index 000000000000..f5b305d4c971 --- /dev/null +++ b/packages/wrangler/src/__tests__/mock-user.ts @@ -0,0 +1,27 @@ +import path from "path"; +import os from "os"; +import { mkdirSync, writeFileSync } from "node:fs"; + +export function writeUserConfig( + oauth_token?: string, + refresh_token?: string, + expiration_time?: string +) { + const lines: string[] = []; + if (oauth_token) { + lines.push(`oauth_token = "${oauth_token}"`); + } + if (refresh_token) { + lines.push(`refresh_token = "${refresh_token}"`); + } + if (expiration_time) { + lines.push(`expiration_time = "${expiration_time}"`); + } + const configPath = path.join(os.homedir(), ".wrangler/config"); + mkdirSync(configPath, { recursive: true }); + writeFileSync( + path.join(configPath, "default.toml"), + lines.join("\n"), + "utf-8" + ); +} diff --git a/packages/wrangler/src/__tests__/publish.test.ts b/packages/wrangler/src/__tests__/publish.test.ts index 29aabb85a828..3ee4713a0181 100644 --- a/packages/wrangler/src/__tests__/publish.test.ts +++ b/packages/wrangler/src/__tests__/publish.test.ts @@ -1,13 +1,14 @@ import * as fs from "node:fs"; import * as path from "node:path"; import type { KVNamespaceInfo } from "../kv"; -import { mockKeyListRequest } from "./kv.test"; +import { mockKeyListRequest } from "./mock-kv"; import { setMockResponse, unsetAllMocks } from "./mock-cfetch"; import { runInTempDir } from "./run-in-tmp"; import { runWrangler } from "./run-wrangler"; import { mockConsoleMethods } from "./mock-console"; import type { Config } from "../config"; import * as TOML from "@iarna/toml"; +import type { WorkerMetadata } from "../api/form_data"; describe("publish", () => { runInTempDir(); @@ -18,10 +19,30 @@ describe("publish", () => { }); describe("entry-points", () => { - it("should be able to use `index` with no extension as the entry-point", async () => { + it("should be able to use `index` with no extension as the entry-point (esm)", async () => { writeWranglerToml(); - writeEsmWorkerSource(); - mockUploadWorkerRequest(); + writeWorkerSource(); + mockUploadWorkerRequest({ expectedType: "esm" }); + mockSubDomainRequest(); + + await runWrangler("publish ./index"); + + expect(stripTimings(std.out)).toMatchInlineSnapshot(` + "Uploaded + test-name + (TIMINGS) + Deployed + test-name + (TIMINGS) + + test-name.test-sub-domain.workers.dev" + `); + expect(std.err).toMatchInlineSnapshot(`""`); + }); + it("should be able to use `index` with no extension as the entry-point (sw)", async () => { + writeWranglerToml(); + writeWorkerSource({ type: "sw" }); + mockUploadWorkerRequest({ expectedType: "sw" }); mockSubDomainRequest(); await runWrangler("publish ./index"); @@ -41,7 +62,7 @@ describe("publish", () => { it("should be able to use the `build.upload.main` config as the entry-point for ESM sources", async () => { writeWranglerToml({ build: { upload: { main: "./index.js" } } }); - writeEsmWorkerSource(); + writeWorkerSource(); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -60,10 +81,10 @@ describe("publish", () => { expect(std.err).toMatchInlineSnapshot(`""`); }); - it("should be able to transpile TypeScript", async () => { + it("should be able to transpile TypeScript (esm)", async () => { writeWranglerToml(); - writeEsmWorkerSource({ format: "ts" }); - mockUploadWorkerRequest({ expectedBody: "var foo = 100;" }); + writeWorkerSource({ format: "ts" }); + mockUploadWorkerRequest({ expectedEntry: "var foo = 100;" }); mockSubDomainRequest(); await runWrangler("publish index.ts"); @@ -80,10 +101,87 @@ describe("publish", () => { expect(std.err).toMatchInlineSnapshot(`""`); }); - it("should be able to transpile entry-points in sub-directories", async () => { + it("should be able to transpile TypeScript (sw)", async () => { writeWranglerToml(); - writeEsmWorkerSource({ basePath: "./src" }); - mockUploadWorkerRequest({ expectedBody: "var foo = 100;" }); + writeWorkerSource({ format: "ts", type: "sw" }); + mockUploadWorkerRequest({ + expectedEntry: "var foo = 100;", + expectedType: "sw", + }); + mockSubDomainRequest(); + await runWrangler("publish index.ts"); + + expect(stripTimings(std.out)).toMatchInlineSnapshot(` + "Uploaded + test-name + (TIMINGS) + Deployed + test-name + (TIMINGS) + + test-name.test-sub-domain.workers.dev" + `); + expect(std.err).toMatchInlineSnapshot(`""`); + }); + + it("should inline referenced text modules into the worker", async () => { + writeWranglerToml(); + fs.writeFileSync( + "./index.js", + ` +import txt from './textfile.txt'; +export default{ + fetch(){ + return new Response(txt); + } +} +` + ); + fs.writeFileSync("./textfile.txt", "Hello, World!"); + mockUploadWorkerRequest({ expectedEntry: "Hello, World!" }); + mockSubDomainRequest(); + await runWrangler("publish index.js"); + expect(stripTimings(std.out)).toMatchInlineSnapshot(` + "Uploaded + test-name + (TIMINGS) + Deployed + test-name + (TIMINGS) + + test-name.test-sub-domain.workers.dev" + `); + expect(std.err).toMatchInlineSnapshot(`""`); + }); + + it("should be able to transpile entry-points in sub-directories (esm)", async () => { + writeWranglerToml(); + writeWorkerSource({ basePath: "./src" }); + mockUploadWorkerRequest({ expectedEntry: "var foo = 100;" }); + mockSubDomainRequest(); + + await runWrangler("publish ./src/index.js"); + + expect(stripTimings(std.out)).toMatchInlineSnapshot(` + "Uploaded + test-name + (TIMINGS) + Deployed + test-name + (TIMINGS) + + test-name.test-sub-domain.workers.dev" + `); + expect(std.err).toMatchInlineSnapshot(`""`); + }); + + it("should be able to transpile entry-points in sub-directories (sw)", async () => { + writeWranglerToml(); + writeWorkerSource({ basePath: "./src", type: "sw" }); + mockUploadWorkerRequest({ + expectedEntry: "var foo = 100;", + expectedType: "sw", + }); mockSubDomainRequest(); await runWrangler("publish ./src/index.js"); @@ -108,7 +206,7 @@ describe("publish", () => { "entry-point": "./index.js", }, }); - writeEsmWorkerSource(); + writeWorkerSource(); mockUploadWorkerRequest(); mockSubDomainRequest(); let error: Error | undefined; @@ -151,7 +249,7 @@ describe("publish", () => { bucket: "assets", }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -185,7 +283,7 @@ describe("publish", () => { it("should error if there is no entry-point specified", async () => { writeWranglerToml(); - writeEsmWorkerSource(); + writeWorkerSource(); mockUploadWorkerRequest(); mockSubDomainRequest(); let error: Error | undefined; @@ -224,7 +322,7 @@ describe("publish", () => { bucket: "assets", }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -250,6 +348,112 @@ describe("publish", () => { expect(std.err).toMatchInlineSnapshot(`""`); }); + it("when using a service worker type, it should inline an asset manifest, and bind to a namespace", async () => { + const assets = [ + { filePath: "assets/file-1.txt", content: "Content of file-1" }, + { filePath: "assets/file-2.txt", content: "Content of file-2" }, + ]; + const kvNamespace = { + title: "__test-name_sites_assets", + id: "__test-name_sites_assets-id", + }; + writeWranglerToml({ + build: { upload: { main: "./index.js" } }, + site: { + bucket: "assets", + }, + }); + writeWorkerSource({ type: "sw" }); + writeAssets(assets); + mockUploadWorkerRequest({ + expectedType: "sw", + expectedEntry: `const __STATIC_CONTENT_MANIFEST = {"file-1.txt":"assets/file-1.2ca234f380.txt","file-2.txt":"assets/file-2.5938485188.txt"};`, + expectedBindings: [ + { + name: "__STATIC_CONTENT", + namespace_id: "__test-name_sites_assets-id", + type: "kv_namespace", + }, + ], + }); + mockSubDomainRequest(); + mockListKVNamespacesRequest(kvNamespace); + mockKeyListRequest(kvNamespace.id, []); + mockUploadAssetsToKVRequest(kvNamespace.id, assets); + + await runWrangler("publish"); + + expect(stripTimings(std.out)).toMatchInlineSnapshot(` + "reading assets/file-1.txt... + uploading as assets/file-1.2ca234f380.txt... + reading assets/file-2.txt... + uploading as assets/file-2.5938485188.txt... + Uploaded + test-name + (TIMINGS) + Deployed + test-name + (TIMINGS) + + test-name.test-sub-domain.workers.dev" + `); + expect(stripTimings(std.err)).toMatchInlineSnapshot(`""`); + }); + + it("when using a module worker type, it should add an asset manifest module, and bind to a namespace", async () => { + const assets = [ + { filePath: "assets/file-1.txt", content: "Content of file-1" }, + { filePath: "assets/file-2.txt", content: "Content of file-2" }, + ]; + const kvNamespace = { + title: "__test-name_sites_assets", + id: "__test-name_sites_assets-id", + }; + writeWranglerToml({ + build: { upload: { main: "./index.js" } }, + site: { + bucket: "assets", + }, + }); + writeWorkerSource({ type: "esm" }); + writeAssets(assets); + mockUploadWorkerRequest({ + expectedBindings: [ + { + name: "__STATIC_CONTENT", + namespace_id: "__test-name_sites_assets-id", + type: "kv_namespace", + }, + ], + expectedModules: { + __STATIC_CONTENT_MANIFEST: + '{"file-1.txt":"assets/file-1.2ca234f380.txt","file-2.txt":"assets/file-2.5938485188.txt"}', + }, + }); + mockSubDomainRequest(); + mockListKVNamespacesRequest(kvNamespace); + mockKeyListRequest(kvNamespace.id, []); + mockUploadAssetsToKVRequest(kvNamespace.id, assets); + + await runWrangler("publish"); + + expect(stripTimings(std.out)).toMatchInlineSnapshot(` + "reading assets/file-1.txt... + uploading as assets/file-1.2ca234f380.txt... + reading assets/file-2.txt... + uploading as assets/file-2.5938485188.txt... + Uploaded + test-name + (TIMINGS) + Deployed + test-name + (TIMINGS) + + test-name.test-sub-domain.workers.dev" + `); + expect(stripTimings(std.err)).toMatchInlineSnapshot(`""`); + }); + it("should only upload files that are not already in the KV namespace", async () => { const assets = [ { filePath: "assets/file-1.txt", content: "Content of file-1" }, @@ -265,7 +469,7 @@ describe("publish", () => { bucket: "assets", }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -311,7 +515,7 @@ describe("publish", () => { bucket: "assets", }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -354,7 +558,7 @@ describe("publish", () => { bucket: "assets", }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -398,7 +602,7 @@ describe("publish", () => { include: ["file-1.txt"], }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -442,7 +646,7 @@ describe("publish", () => { exclude: ["file-2.txt"], }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -486,7 +690,7 @@ describe("publish", () => { include: ["file-2.txt"], }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -530,7 +734,7 @@ describe("publish", () => { exclude: ["assets/file-1.txt"], }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -579,7 +783,7 @@ describe("publish", () => { bucket: "assets", }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -629,7 +833,7 @@ describe("publish", () => { bucket: "assets", }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -677,7 +881,7 @@ describe("publish", () => { exclude: ["assets/file-1.txt"], }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets(assets); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -720,7 +924,7 @@ describe("publish", () => { bucket: "assets", }, }); - writeEsmWorkerSource(); + writeWorkerSource(); writeAssets([longFilePathAsset]); mockUploadWorkerRequest(); mockSubDomainRequest(); @@ -758,7 +962,7 @@ describe("publish", () => { }); mockUploadWorkerRequest({ - expectedBody: "return new Response(123)", + expectedEntry: "return new Response(123)", }); mockSubDomainRequest(); @@ -799,23 +1003,31 @@ function writeWranglerToml(config: Omit = {}) { } /** Write a mock Worker script to disk. */ -function writeEsmWorkerSource({ +function writeWorkerSource({ basePath = ".", format = "js", -}: { basePath?: string; format?: "js" | "ts" | "jsx" | "tsx" | "mjs" } = {}) { + type = "esm", +}: { + basePath?: string; + format?: "js" | "ts" | "jsx" | "tsx" | "mjs"; + type?: "esm" | "sw"; +} = {}) { if (basePath !== ".") { fs.mkdirSync(basePath, { recursive: true }); } fs.writeFileSync( `${basePath}/index.${format}`, - [ - `import { foo } from "./another";`, - `export default {`, - ` async fetch(request) {`, - ` return new Response('Hello' + foo);`, - ` },`, - `};`, - ].join("\n") + type === "esm" + ? `import { foo } from "./another"; + export default { + async fetch(request) { + return new Response('Hello' + foo); + }, + };` + : `import { foo } from "./another"; + addEventListener('fetch', event => { + event.respondWith(new Response('Hello' + foo)); + })` ); fs.writeFileSync(`${basePath}/another.${format}`, `export const foo = 100;`); } @@ -831,10 +1043,16 @@ function writeAssets(assets: { filePath: string; content: string }[]) { /** Create a mock handler for the request to upload a worker script. */ function mockUploadWorkerRequest({ available_on_subdomain = true, - expectedBody, + expectedEntry, + expectedType = "esm", + expectedBindings, + expectedModules = {}, }: { available_on_subdomain?: boolean; - expectedBody?: string; + expectedEntry?: string; + expectedType?: "esm" | "sw"; + expectedBindings?: unknown; + expectedModules?: Record; } = {}) { setMockResponse( "/accounts/:accountId/workers/scripts/:scriptName", @@ -843,11 +1061,28 @@ function mockUploadWorkerRequest({ expect(accountId).toEqual("some-account-id"); expect(scriptName).toEqual("test-name"); expect(queryParams.get("available_on_subdomains")).toEqual("true"); - if (expectedBody !== undefined) { - expect( - await ((body as FormData).get("index.js") as File).text() - ).toMatch(expectedBody); + const formBody = body as FormData; + if (expectedEntry !== undefined) { + expect(await (formBody.get("index.js") as File).text()).toMatch( + expectedEntry + ); } + + const metadata = JSON.parse( + formBody.get("metadata") as string + ) as WorkerMetadata; + if (expectedType === "esm") { + expect(metadata.main_module).toEqual("index.js"); + } else { + expect(metadata.body_part).toEqual("index.js"); + } + if (expectedBindings !== undefined) { + expect(metadata.bindings).toEqual(expectedBindings); + } + for (const [name, content] of Object.entries(expectedModules)) { + expect(await (formBody.get(name) as File).text()).toMatch(content); + } + return { available_on_subdomain }; } ); diff --git a/packages/wrangler/src/__tests__/whoami.test.tsx b/packages/wrangler/src/__tests__/whoami.test.tsx index 1d54356c9e3f..e6c774aa0b4e 100644 --- a/packages/wrangler/src/__tests__/whoami.test.tsx +++ b/packages/wrangler/src/__tests__/whoami.test.tsx @@ -1,13 +1,11 @@ import React from "react"; -import os from "node:os"; -import path from "node:path"; import { render } from "ink-testing-library"; import type { UserInfo } from "../whoami"; import { getUserInfo, WhoAmI } from "../whoami"; import { runInTempDir } from "./run-in-tmp"; -import { mkdirSync, writeFileSync } from "node:fs"; import { setMockResponse } from "./mock-cfetch"; import { initialise } from "../user"; +import { writeUserConfig } from "./mock-user"; const ORIGINAL_CF_API_TOKEN = process.env.CF_API_TOKEN; const ORIGINAL_CF_ACCOUNT_ID = process.env.CF_ACCOUNT_ID; @@ -101,27 +99,3 @@ describe("WhoAmI component", () => { expect(lastFrame()).toMatch(/Account Three .+ account-3/); }); }); - -export function writeUserConfig( - oauth_token?: string, - refresh_token?: string, - expiration_time?: string -) { - const lines: string[] = []; - if (oauth_token) { - lines.push(`oauth_token = "${oauth_token}"`); - } - if (refresh_token) { - lines.push(`refresh_token = "${refresh_token}"`); - } - if (expiration_time) { - lines.push(`expiration_time = "${expiration_time}"`); - } - const configPath = path.join(os.homedir(), ".wrangler/config"); - mkdirSync(configPath, { recursive: true }); - writeFileSync( - path.join(configPath, "default.toml"), - lines.join("\n"), - "utf-8" - ); -} diff --git a/packages/wrangler/src/api/form_data.ts b/packages/wrangler/src/api/form_data.ts index 51eebb282db6..2efd13f2aafa 100644 --- a/packages/wrangler/src/api/form_data.ts +++ b/packages/wrangler/src/api/form_data.ts @@ -30,7 +30,11 @@ function toModule(module: CfModule, entryType: CfModuleType): Blob { return new Blob([content], { type }); } -interface WorkerMetadata { +export interface WorkerMetadata { + /** The name of the entry point module. Only exists when the worker is in the ES module format */ + main_module?: string; + /** The name of the entry point module. Only exists when the worker is in the Service Worker format */ + body_part?: string; compatibility_date?: string; compatibility_flags?: string[]; usage_model?: "bundled" | "unbound"; diff --git a/packages/wrangler/src/dev.tsx b/packages/wrangler/src/dev.tsx index 299e071a7bfb..a9f03d938164 100644 --- a/packages/wrangler/src/dev.tsx +++ b/packages/wrangler/src/dev.tsx @@ -100,7 +100,7 @@ function Dev(props: DevProps): JSX.Element { bundle={bundle} format={props.format} bindings={props.bindings} - site={props.assetPaths} + assetPaths={props.assetPaths} public={props.public} port={port} enableLocalPersistence={props.enableLocalPersistence} @@ -183,8 +183,8 @@ function Local(props: { bundle: EsbuildBundle | undefined; format: CfScriptFormat; bindings: CfWorkerInit["bindings"]; + assetPaths: undefined | AssetPaths; public: undefined | string; - site: undefined | AssetPaths; port: number; enableLocalPersistence: boolean; }) { @@ -193,6 +193,8 @@ function Local(props: { bundle: props.bundle, format: props.format, bindings: props.bindings, + assetPaths: props.assetPaths, + public: props.public, port: props.port, enableLocalPersistence: props.enableLocalPersistence, }); @@ -205,11 +207,13 @@ function useLocalWorker(props: { bundle: EsbuildBundle | undefined; format: CfScriptFormat; bindings: CfWorkerInit["bindings"]; + assetPaths: undefined | AssetPaths; + public: undefined | string; port: number; enableLocalPersistence: boolean; }) { // TODO: pass vars via command line - const { bundle, format, bindings, port } = props; + const { bundle, format, bindings, port, assetPaths } = props; const local = useRef>(); const removeSignalExitListener = useRef<() => void>(); const [inspectorUrl, setInspectorUrl] = useState(); @@ -228,6 +232,11 @@ function useLocalWorker(props: { } await waitForPortToBeAvailable(port, { retryPeriod: 200, timeout: 2000 }); + if (props.public) { + throw new Error( + '⎔ A "public" folder is not yet supported in local mode.' + ); + } console.log("⎔ Starting a local server..."); // TODO: just use execa for this @@ -245,6 +254,20 @@ function useLocalWorker(props: { path.join(__dirname, "../miniflare-config-stubs/package.empty.json"), "--port", port.toString(), + ...(assetPaths + ? [ + "--site", + assetPaths.baseDirectory, + ...assetPaths.includePatterns.map((pattern) => [ + "--site-include", + pattern, + ]), + ...assetPaths.excludePatterns.map((pattern) => [ + "--site-exclude", + pattern, + ]), + ].flatMap((x) => x) + : []), ...(props.enableLocalPersistence ? ["--kv-persist", "--cache-persist", "--do-persist"] : []), @@ -327,6 +350,8 @@ function useLocalWorker(props: { bindings.kv_namespaces, bindings.vars, props.enableLocalPersistence, + assetPaths, + props.public, ]); return { inspectorUrl }; } @@ -354,7 +379,7 @@ function useTmpDir(): string | undefined { return () => { dir.cleanup().catch(() => { // extremely unlikely, - // but it's 2021 after all + // but it's 2022 after all console.error("failed to cleanup tmp dir"); }); }; @@ -464,6 +489,9 @@ function useEsbuild(props: { sourcemap: true, loader: { ".js": "jsx", + ".html": "text", + ".pem": "text", + ".txt": "text", }, ...(jsxFactory && { jsxFactory }), ...(jsxFragment && { jsxFragment }), @@ -588,16 +616,23 @@ function useWorker(props: { true ); // TODO: cancellable? - const content = await readFile(bundle.path, "utf-8"); + const workerType = format || bundle.type === "esm" ? "esm" : "commonjs"; + let content = await readFile(bundle.path, "utf-8"); + if (workerType !== "esm" && assets.manifest) { + content = `const __STATIC_CONTENT_MANIFEST = ${JSON.stringify( + assets.manifest + )};\n${content}`; + } + const init: CfWorkerInit = { name, main: { name: path.basename(bundle.path), - type: format || bundle.type === "esm" ? "esm" : "commonjs", + type: workerType, content, }, modules: modules.concat( - assets.manifest + assets.manifest && workerType === "esm" ? { name: "__STATIC_CONTENT_MANIFEST", content: JSON.stringify(assets.manifest), diff --git a/packages/wrangler/src/module-collection.ts b/packages/wrangler/src/module-collection.ts index 1ebfa8e3073b..fbce76e299e1 100644 --- a/packages/wrangler/src/module-collection.ts +++ b/packages/wrangler/src/module-collection.ts @@ -30,11 +30,10 @@ export default function makeModuleCollector(): { build.onResolve( // filter on "known" file types, // we can expand this list later - { filter: /.*\.(pem|txt|html|wasm)$/ }, + { filter: /.*\.(wasm)$/ }, async (args: esbuild.OnResolveArgs) => { // take the file and massage it to a // transportable/manageable format - const fileExt = path.extname(args.path); const filePath = path.join(args.resolveDir, args.path); const fileContent = await readFile(filePath); const fileHash = crypto @@ -47,7 +46,7 @@ export default function makeModuleCollector(): { modules.push({ name: fileName, content: fileContent, - type: fileExt === ".wasm" ? "compiled-wasm" : "text", + type: "compiled-wasm", }); return { diff --git a/packages/wrangler/src/publish.ts b/packages/wrangler/src/publish.ts index bddcc9203cc5..119bc1c0bfb3 100644 --- a/packages/wrangler/src/publish.ts +++ b/packages/wrangler/src/publish.ts @@ -148,6 +148,9 @@ export default async function publish(props: Props): Promise { conditions: ["worker", "browser"], loader: { ".js": "jsx", + ".html": "text", + ".pem": "text", + ".txt": "text", }, plugins: [moduleCollector.plugin], ...(jsxFactory && { jsxFactory }), @@ -192,7 +195,7 @@ export default async function publish(props: Props): Promise { return; } - const content = await readFile(resolvedEntryPointPath, { encoding: "utf-8" }); + let content = await readFile(resolvedEntryPointPath, { encoding: "utf-8" }); await destination.cleanup(); // if config.migrations @@ -242,7 +245,7 @@ export default async function publish(props: Props): Promise { ); const bindings: CfWorkerInit["bindings"] = { - kv_namespaces: envRootObj.kv_namespaces?.concat( + kv_namespaces: (envRootObj.kv_namespaces || []).concat( assets.namespace ? { binding: "__STATIC_CONTENT", id: assets.namespace } : [] @@ -252,17 +255,24 @@ export default async function publish(props: Props): Promise { services: envRootObj.experimental_services, }; + const workerType = bundle.type === "esm" ? "esm" : "commonjs"; + if (workerType !== "esm" && assets.manifest) { + content = `const __STATIC_CONTENT_MANIFEST = ${JSON.stringify( + assets.manifest + )};\n${content}`; + } + const worker: CfWorkerInit = { name: scriptName, main: { name: path.basename(resolvedEntryPointPath), content: content, - type: bundle.type === "esm" ? "esm" : "commonjs", + type: workerType, }, bindings, ...(migrations && { migrations }), modules: moduleCollector.modules.concat( - assets.manifest + assets.manifest && workerType === "esm" ? { name: "__STATIC_CONTENT_MANIFEST", content: JSON.stringify(assets.manifest),