Skip to content

Commit

Permalink
feat: support --experimental-public in local mode
Browse files Browse the repository at this point in the history
`--experimental-public` is an abstraction over Workers Sites, and we can leverage miniflare's inbuilt support for Sites to serve assets in local mode.
  • Loading branch information
threepointone committed Jun 14, 2022
1 parent db8a0bb commit f101422
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 51 deletions.
7 changes: 7 additions & 0 deletions .changeset/loud-meals-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": patch
---

feat: support `--experimental-public` in local mode

`--experimental-public` is an abstraction over Workers Sites, and we can leverage miniflare's inbuilt support for Sites to serve assets in local mode.
27 changes: 27 additions & 0 deletions packages/wrangler/src/__tests__/dev.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,33 @@ describe("wrangler dev", () => {
}
`);
});

it("should error if --experimental-public and --site are used together", async () => {
writeWranglerToml({
main: "./index.js",
});
fs.writeFileSync("index.js", `export default {};`);
await expect(
runWrangler("publish --experimental-public abc --site xyz")
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Cannot use --experimental-public and a Site configuration together."`
);
});

it("should error if --experimental-public and config.site are used together", async () => {
writeWranglerToml({
main: "./index.js",
site: {
bucket: "xyz",
},
});
fs.writeFileSync("index.js", `export default {};`);
await expect(
runWrangler("publish --experimental-public abc")
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Cannot use --experimental-public and a Site configuration together."`
);
});
});

describe("--inspect", () => {
Expand Down
31 changes: 30 additions & 1 deletion packages/wrangler/src/__tests__/publish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1404,6 +1404,33 @@ addEventListener('fetch', event => {});`
expect(std.err).toMatchInlineSnapshot(`""`);
});

it("should error if --experimental-public and --site are used together", async () => {
writeWranglerToml({
main: "./index.js",
});
writeWorkerSource();
await expect(
runWrangler("publish --experimental-public abc --site xyz")
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Cannot use --experimental-public and a Site configuration together."`
);
});

it("should error if --experimental-public and config.site are used together", async () => {
writeWranglerToml({
main: "./index.js",
site: {
bucket: "xyz",
},
});
writeWorkerSource();
await expect(
runWrangler("publish --experimental-public abc")
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Cannot use --experimental-public and a Site configuration together."`
);
});

it("should not contain backslash for assets with nested directories", async () => {
const assets = [
{ filePath: "subdir/file-1.txt", content: "Content of file-1" },
Expand Down Expand Up @@ -5311,6 +5338,7 @@ function mockUploadWorkerRequest(
options: {
available_on_subdomain?: boolean;
expectedEntry?: string;
expectedMainModule?: string;
expectedType?: "esm" | "sw";
expectedBindings?: unknown;
expectedModules?: Record<string, string>;
Expand All @@ -5324,6 +5352,7 @@ function mockUploadWorkerRequest(
const {
available_on_subdomain = true,
expectedEntry,
expectedMainModule = "index.js",
expectedType = "esm",
expectedBindings,
expectedModules = {},
Expand Down Expand Up @@ -5358,7 +5387,7 @@ function mockUploadWorkerRequest(
formBody.get("metadata") as string
) as WorkerMetadata;
if (expectedType === "esm") {
expect(metadata.main_module).toEqual("index.js");
expect(metadata.main_module).toEqual(expectedMainModule);
} else {
expect(metadata.body_part).toEqual("index.js");
}
Expand Down
3 changes: 2 additions & 1 deletion packages/wrangler/src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ function getEntryPoint(
path.join(__dirname, "../templates/static-asset-facade.js"),
"utf8"
)
.replace("__ENTRY_POINT__", entryFile),
// on windows, escape backslashes in the path (`\`)
.replace("__ENTRY_POINT__", entryFile.replaceAll("\\", "\\\\")),
sourcefile: "static-asset-facade.js",
resolveDir: path.dirname(entryFile),
},
Expand Down
58 changes: 26 additions & 32 deletions packages/wrangler/src/dev/dev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import type { Config } from "../config";
import type { Entry } from "../entry";
import type { AssetPaths } from "../sites";
import type { CfWorkerInit } from "../worker";
import type { EsbuildBundle } from "./use-esbuild";

export type DevProps = {
name?: string;
Expand Down Expand Up @@ -54,10 +53,6 @@ export type DevProps = {
};

export function DevImplementation(props: DevProps): JSX.Element {
const directory = useTmpDir();

useCustomBuild(props.entry, props.build);

if (props.public && props.entry.format === "service-worker") {
throw new Error(
"You cannot use the service-worker format with a `public` directory."
Expand All @@ -82,37 +77,16 @@ export function DevImplementation(props: DevProps): JSX.Element {
);
}

const bundle = useEsbuild({
entry: props.entry,
destination: directory,
staticRoot: props.public,
jsxFactory: props.jsxFactory,
rules: props.rules,
jsxFragment: props.jsxFragment,
serveAssetsFromWorker: !!props.public,
tsconfig: props.tsconfig,
minify: props.minify,
nodeCompat: props.nodeCompat,
});

// only load the UI if we're running in a supported environment
const { isRawModeSupported } = useStdin();
return isRawModeSupported ? (
<InteractiveDevSession {...props} bundle={bundle} />
<InteractiveDevSession {...props} />
) : (
<DevSession
{...props}
bundle={bundle}
local={props.initialMode === "local"}
/>
<DevSession {...props} local={props.initialMode === "local"} />
);
}

type InteractiveDevSessionProps = DevProps & {
bundle: EsbuildBundle | undefined;
};

function InteractiveDevSession(props: InteractiveDevSessionProps) {
function InteractiveDevSession(props: DevProps) {
const toggles = useHotkeys(
{
local: props.initialMode === "local",
Expand Down Expand Up @@ -145,13 +119,33 @@ function InteractiveDevSession(props: InteractiveDevSessionProps) {
);
}

type DevSessionProps = InteractiveDevSessionProps & { local: boolean };
type DevSessionProps = DevProps & {
local: boolean;
};

function DevSession(props: DevSessionProps) {
useCustomBuild(props.entry, props.build);

const directory = useTmpDir();

const bundle = useEsbuild({
entry: props.entry,
destination: directory,
staticRoot: props.public,
jsxFactory: props.jsxFactory,
rules: props.rules,
jsxFragment: props.jsxFragment,
// In dev for remote mode, we serve --experimental-assets from the local proxy before we send the request to the worker.
serveAssetsFromWorker: !!props.public && !!props.local,
tsconfig: props.tsconfig,
minify: props.minify,
nodeCompat: props.nodeCompat,
});

return props.local ? (
<Local
name={props.name}
bundle={props.bundle}
bundle={bundle}
format={props.entry.format}
compatibilityDate={props.compatibilityDate}
localProtocol={props.localProtocol}
Expand All @@ -169,7 +163,7 @@ function DevSession(props: DevSessionProps) {
) : (
<Remote
name={props.name}
bundle={props.bundle}
bundle={bundle}
format={props.entry.format}
accountId={props.accountId}
bindings={props.bindings}
Expand Down
5 changes: 0 additions & 5 deletions packages/wrangler/src/dev/local.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,6 @@ function useLocalWorker({
abortSignal: abortController.signal,
});

if (publicDirectory) {
throw new Error(
'⎔ A "public" folder is not yet supported in local mode.'
);
}
if (bindings.services && bindings.services.length > 0) {
throw new Error(
"⎔ Service bindings are not yet supported in local mode."
Expand Down
5 changes: 4 additions & 1 deletion packages/wrangler/src/dev/remote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function Remote(props: {
accountId: props.accountId,
bindings: props.bindings,
assetPaths: props.assetPaths,
public: props.public,
port: props.port,
compatibilityDate: props.compatibilityDate,
compatibilityFlags: props.compatibilityFlags,
Expand Down Expand Up @@ -74,6 +75,7 @@ export function useWorker(props: {
accountId: string | undefined;
bindings: CfWorkerInit["bindings"];
assetPaths: AssetPaths | undefined;
public: string | undefined;
port: number;
compatibilityDate: string | undefined;
compatibilityFlags: string[] | undefined;
Expand Down Expand Up @@ -131,7 +133,7 @@ export function useWorker(props: {
// include it in the kv namespace name regardless (since there's no
// concept of service environments for kv namespaces yet).
name + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
assetPaths,
props.public ? undefined : assetPaths,
true,
false
); // TODO: cancellable?
Expand Down Expand Up @@ -223,6 +225,7 @@ export function useWorker(props: {
accountId,
port,
assetPaths,
props.public,
compatibilityDate,
compatibilityFlags,
usageModel,
Expand Down
5 changes: 1 addition & 4 deletions packages/wrangler/src/dev/use-esbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export type EsbuildBundle = {
entry: Entry;
type: "esm" | "commonjs";
modules: CfModule[];
serveAssetsFromWorker: boolean;
};

export function useEsbuild({
Expand Down Expand Up @@ -67,8 +66,7 @@ export function useEsbuild({

const { resolvedEntryPointPath, bundleType, modules, stop } =
await bundleWorker(entry, destination, {
// In dev, we serve assets from the local proxy before we send the request to the worker.
serveAssetsFromWorker: false,
serveAssetsFromWorker,
jsxFactory,
jsxFragment,
rules,
Expand All @@ -87,7 +85,6 @@ export function useEsbuild({
path: resolvedEntryPointPath,
type: bundleType,
modules,
serveAssetsFromWorker,
});
}

Expand Down
26 changes: 19 additions & 7 deletions packages/wrangler/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,12 @@ function createCLIParser(argv: string[]) {
);
}

if (args["experimental-public"] && (args.site || config.site)) {
throw new Error(
"Cannot use --experimental-public and a Site configuration together."
);
}

if (args.public) {
throw new Error(
"The --public field has been renamed to --experimental-public, and will change behaviour in the future."
Expand Down Expand Up @@ -1229,7 +1235,7 @@ function createCLIParser(argv: string[]) {
accountId={config.account_id}
assetPaths={getAssetPaths(
config,
args.site,
args["experimental-public"] || args.site,
args.siteInclude,
args.siteExclude
)}
Expand Down Expand Up @@ -1384,23 +1390,29 @@ function createCLIParser(argv: string[]) {
},
async (args) => {
await printWranglerBanner();

const configPath =
(args.config as ConfigPath) ||
(args.script && findWranglerToml(path.dirname(args.script)));
const config = readConfig(configPath, args);
const entry = await getEntry(args, config, "publish");

if (args["experimental-public"]) {
logger.warn(
"The --experimental-public field is experimental and will change in the future."
);
}
if (args["experimental-public"] && (args.site || config.site)) {
throw new Error(
"Cannot use --experimental-public and a Site configuration together."
);
}
if (args.public) {
throw new Error(
"The --public field has been renamed to --experimental-public, and will change behaviour in the future."
);
}

const configPath =
(args.config as ConfigPath) ||
(args.script && findWranglerToml(path.dirname(args.script)));
const config = readConfig(configPath, args);
const entry = await getEntry(args, config, "publish");

if (args.latest) {
logger.warn(
"Using the latest version of the Workers runtime. To silence this warning, please choose a specific version of the runtime with --compatibility-date, or add a compatibility_date to your wrangler.toml.\n"
Expand Down

0 comments on commit f101422

Please sign in to comment.