Skip to content

Commit

Permalink
feat(workers): add support for workers style environemnt. See precons…
Browse files Browse the repository at this point in the history
  • Loading branch information
nicksrandall authored and emmatown committed Jul 4, 2022
1 parent 4e04c59 commit 04e06b5
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 7 deletions.
35 changes: 35 additions & 0 deletions packages/cli/src/build/config.ts
Expand Up @@ -207,5 +207,40 @@ export function getRollupConfigs(pkg: Package, aliases: Aliases) {
});
}

let hasWorkerField = pkg.entrypoints[0].json.worker !== undefined;

if (hasWorkerField) {
configs.push({
config: getRollupConfig(
pkg,
pkg.entrypoints,
aliases,
"worker",
() => {}
),
outputs: [
{
format: "cjs" as const,
entryFileNames: "[name].worker.cjs.js",
chunkFileNames: "dist/[name]-[hash].worker.cjs.js",
dir: pkg.directory,
exports: "named" as const,
interop,
plugins: cjsPlugins,
},
...(hasModuleField
? [
{
format: "es" as const,
entryFileNames: "[name].worker.esm.js",
chunkFileNames: "dist/[name]-[hash].worker.esm.js",
dir: pkg.directory,
},
]
: []),
],
});
}

return configs;
}
16 changes: 15 additions & 1 deletion packages/cli/src/build/rollup.ts
Expand Up @@ -31,7 +31,12 @@ const makeExternalPredicate = (externalArr: string[]) => {
return (id: string) => pattern.test(id);
};

export type RollupConfigType = "umd" | "browser" | "node-dev" | "node-prod";
export type RollupConfigType =
| "umd"
| "browser"
| "node-dev"
| "node-prod"
| "worker";

export let getRollupConfig = (
pkg: Package,
Expand Down Expand Up @@ -193,6 +198,15 @@ export let getRollupConfig = (
},
preventAssignment: true,
}),
type === "worker" &&
replace({
values: {
["typeof " + "document"]: JSON.stringify("undefined"),
["typeof " + "window"]: JSON.stringify("undefined"),
["typeof " + "process"]: JSON.stringify("undefined"),
},
preventAssignment: true,
}),
].filter((x): x is Plugin => !!x),
};

Expand Down
11 changes: 11 additions & 0 deletions packages/cli/src/dev.ts
Expand Up @@ -218,6 +218,17 @@ unregister();
);
}
}
if (entrypoint.json.worker) {
let workerField = validFields.worker(entrypoint);
for (let key of Object.keys(workerField)) {
promises.push(
fs.symlink(
entrypoint.source,
path.join(entrypoint.directory, workerField[key])
)
);
}
}

return Promise.all(promises);
})
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/entrypoint.ts
Expand Up @@ -9,6 +9,7 @@ export class Entrypoint extends Item<{
module?: JSONValue;
"umd:main"?: JSONValue;
browser?: JSONValue;
worker?: JSONValue;
preconstruct: {
source?: JSONValue;
umdName?: JSONValue;
Expand Down
24 changes: 24 additions & 0 deletions packages/cli/src/init.ts
Expand Up @@ -93,6 +93,30 @@ async function doInit(pkg: Package) {
}
}

let someEntrypointsHaveAWorkerField = pkg.entrypoints.some(
(entrypoint) => entrypoint.json.worker !== undefined
);

let someEntrypointsHaveAnInvalidWorkerField = pkg.entrypoints.some(
(entrypoint) => !isFieldValid.worker(entrypoint)
);
if (
someEntrypointsHaveAWorkerField &&
someEntrypointsHaveAnInvalidWorkerField
) {
let shouldFixWorkerField = await confirms.fixWorkerField(pkg);
if (shouldFixWorkerField) {
pkg.setFieldOnEntrypoints("worker");
} else {
throw new FixableError(
errors.fieldMustExistInAllEntrypointsIfExistsDeclinedFixDuringInit(
"worker"
),
pkg.name
);
}
}

await Promise.all(pkg.entrypoints.map((x) => x.save()));
}

Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/messages.ts
Expand Up @@ -2,7 +2,7 @@ import { PKG_JSON_CONFIG_FIELD } from "./constants";
import { createPromptConfirmLoader } from "./prompt";
import chalk from "chalk";

type Field = "main" | "module" | "browser" | "umd:main";
type Field = "main" | "module" | "browser" | "umd:main" | "worker";

export let errors = {
noSource: (source: string) =>
Expand Down Expand Up @@ -38,6 +38,9 @@ export let confirms = {
fixBrowserField: createPromptConfirmLoader(
"would you like to fix the browser build?"
),
fixWorkerField: createPromptConfirmLoader(
"would you like to fix the worker build?"
),
createEntrypointPkgJson: createPromptConfirmLoader(
"A package.json file does not exist for this entrypoint, would you like to create one automatically?"
),
Expand Down
8 changes: 5 additions & 3 deletions packages/cli/src/package.ts
Expand Up @@ -26,7 +26,7 @@ function getFieldsUsedInEntrypoints(
for (let descriptor of descriptors) {
if (descriptor.contents !== undefined) {
let parsed = jsonParse(descriptor.contents, descriptor.filename);
for (let field of ["module", "umd:main", "browser"] as const) {
for (let field of ["module", "umd:main", "browser", "worker"] as const) {
if (parsed[field] !== undefined) {
fields.add(field);
}
Expand All @@ -47,7 +47,7 @@ function getPlainEntrypointContent(
string | Record<string, string>
>> = {};
for (const field of fields) {
if (field === "browser") {
if (field === "browser" || field === "worker") {
obj[field] = validFieldsFromPkg[field](
pkg,
fields.has("module"),
Expand Down Expand Up @@ -264,7 +264,9 @@ export class Package extends Item<{
return pkg;
}

setFieldOnEntrypoints(field: "main" | "browser" | "module" | "umd:main") {
setFieldOnEntrypoints(
field: "main" | "browser" | "module" | "umd:main" | "worker"
) {
this.entrypoints.forEach((entrypoint) => {
entrypoint.json = setFieldInOrder(
entrypoint.json,
Expand Down
27 changes: 26 additions & 1 deletion packages/cli/src/utils.ts
Expand Up @@ -15,11 +15,12 @@ let fields = [
"module",
"umd:main",
"browser",
"worker",
];

export function setFieldInOrder<
Obj extends { [key: string]: any },
Key extends "main" | "module" | "umd:main" | "browser",
Key extends "main" | "module" | "umd:main" | "browser" | "worker",
Val extends any
>(obj: Obj, field: Key, value: Val): Obj & { [k in Key]: Val } {
if (field in obj) {
Expand Down Expand Up @@ -129,6 +130,22 @@ export const validFieldsFromPkg = {
}
return obj;
},
worker(
pkg: Package,
hasModuleBuild: boolean,
entrypointName: string,
forceStrategy?: DistFilenameStrategy
) {
let safeName = getDistName(pkg, entrypointName, forceStrategy);

let obj = {
[`./dist/${safeName}.cjs.js`]: `./dist/${safeName}.worker.cjs.js`,
};
if (hasModuleBuild) {
obj[`./dist/${safeName}.esm.js`] = `./dist/${safeName}.worker.esm.js`;
}
return obj;
},
};

export const validFields = {
Expand All @@ -148,6 +165,14 @@ export const validFields = {
entrypoint.name
);
},
worker(entrypoint: Entrypoint, forceStrategy?: DistFilenameStrategy) {
return validFieldsFromPkg.worker(
entrypoint.package,
entrypoint.json.module !== undefined,
entrypoint.name,
forceStrategy
);
},
};

export function flowTemplate(hasDefaultExport: boolean, relativePath: string) {
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/validate-package.ts
Expand Up @@ -15,6 +15,7 @@ export async function fixPackage(pkg: Package) {
module: pkg.entrypoints.some((x) => x.json.module !== undefined),
"umd:main": pkg.entrypoints.some((x) => x.json["umd:main"] !== undefined),
browser: pkg.entrypoints.some((x) => x.json.browser !== undefined),
worker: pkg.entrypoints.some((x) => x.json.worker !== undefined),
};

keys(fields)
Expand All @@ -41,6 +42,7 @@ export function validatePackage(pkg: Package) {
module: pkg.entrypoints[0].json.module !== undefined,
"umd:main": pkg.entrypoints[0].json["umd:main"] !== undefined,
browser: pkg.entrypoints[0].json.browser !== undefined,
worker: pkg.entrypoints[0].json.worker !== undefined,
};

pkg.entrypoints.forEach((entrypoint) => {
Expand Down
11 changes: 10 additions & 1 deletion packages/cli/src/validate.ts
Expand Up @@ -25,6 +25,9 @@ export const isFieldValid = {
browser(entrypoint: Entrypoint): boolean {
return equal(entrypoint.json.browser, validFields.browser(entrypoint));
},
worker(entrypoint: Entrypoint): boolean {
return equal(entrypoint.json.worker, validFields.worker(entrypoint));
},
};

export function isUmdNameSpecified(entrypoint: Entrypoint) {
Expand All @@ -38,7 +41,13 @@ function validateEntrypoint(entrypoint: Entrypoint, log: boolean) {
logger.info(infos.validEntrypoint, entrypoint.name);
}
const fatalErrors: FatalError[] = [];
for (const field of ["main", "module", "umd:main", "browser"] as const) {
for (const field of [
"main",
"module",
"umd:main",
"browser",
"worker",
] as const) {
if (field !== "main" && entrypoint.json[field] === undefined) {
continue;
}
Expand Down
6 changes: 6 additions & 0 deletions site/src/pages/configuration.mdx
Expand Up @@ -183,3 +183,9 @@ Example:
The `browser` field specifies alias files exclusive to browsers. This allows you to create different bundles from your source code based on `typeof window` and `typeof document` checks - thanks to that you can, for example, remove server-only code (just for those bundles).

**Note:** Those files are not meant to be consumed by browsers "as is". They just assume browser-like environment, but they still can contain for example references to `process.env.NODE_ENV` as that is meant to be replaced by a consuming bundler.

### `worker`

The `worker` field specifies alias files exclusive to DOM-less javascript environments like CloudFlare Workers. This allows you to create different bundles from your source code based on `typeof window` and `typeof document` and `typeof process` checks - thanks to that you can, for example, remove broser-only or Node.js-only code (just for those bundles).

**Note:** Those files are not meant to be consumed by workers "as is". They just assume worker-like environment, but they still can contain for example references to `process.env.NODE_ENV` as that is meant to be replaced by a consuming bundler.

0 comments on commit 04e06b5

Please sign in to comment.