@opennextjs/cloudflare 0.4.6 → 0.4.7

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.
Files changed (47) hide show
  1. package/dist/api/kv-cache.d.ts +22 -0
  2. package/dist/api/kv-cache.js +123 -0
  3. package/dist/api/kvCache.d.ts +3 -20
  4. package/dist/api/kvCache.js +3 -106
  5. package/dist/cli/build/bundle-server.js +12 -17
  6. package/dist/cli/build/open-next/copyCacheAssets.js +1 -1
  7. package/dist/cli/build/patches/ast/webpack-runtime.d.ts +23 -0
  8. package/dist/cli/build/patches/ast/webpack-runtime.js +57 -0
  9. package/dist/cli/build/patches/ast/webpack-runtime.spec.js +57 -0
  10. package/dist/cli/build/patches/investigated/index.d.ts +0 -1
  11. package/dist/cli/build/patches/investigated/index.js +0 -1
  12. package/dist/cli/build/patches/plugins/content-updater.d.ts +34 -0
  13. package/dist/cli/build/patches/plugins/content-updater.js +52 -0
  14. package/dist/cli/build/patches/plugins/load-instrumentation.d.ts +6 -0
  15. package/dist/cli/build/patches/plugins/load-instrumentation.js +20 -0
  16. package/dist/cli/build/patches/plugins/load-instrumentation.spec.js +45 -0
  17. package/dist/cli/build/patches/plugins/require-hook.d.ts +3 -0
  18. package/dist/cli/build/patches/plugins/require-hook.js +13 -0
  19. package/dist/cli/build/patches/plugins/require-page.d.ts +2 -5
  20. package/dist/cli/build/patches/plugins/require-page.js +30 -29
  21. package/dist/cli/build/patches/plugins/require.d.ts +2 -5
  22. package/dist/cli/build/patches/plugins/require.js +36 -43
  23. package/dist/cli/build/patches/to-investigate/index.d.ts +0 -1
  24. package/dist/cli/build/patches/to-investigate/index.js +0 -1
  25. package/dist/cli/build/utils/ensure-cf-config.js +7 -2
  26. package/dist/cli/build/utils/index.d.ts +0 -1
  27. package/dist/cli/build/utils/index.js +0 -1
  28. package/package.json +3 -4
  29. package/templates/defaults/open-next.config.ts +1 -1
  30. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.d.ts +0 -13
  31. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.js +0 -82
  32. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.js +0 -20
  33. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.d.ts +0 -19
  34. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.js +0 -84
  35. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.js +0 -25
  36. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.d.ts +0 -14
  37. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.js +0 -22
  38. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.d.ts +0 -1
  39. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.js +0 -15
  40. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/index.d.ts +0 -5
  41. package/dist/cli/build/patches/investigated/update-webpack-chunks-file/index.js +0 -22
  42. package/dist/cli/build/patches/to-investigate/patch-load-instrumentation-module.d.ts +0 -14
  43. package/dist/cli/build/patches/to-investigate/patch-load-instrumentation-module.js +0 -34
  44. package/dist/cli/build/utils/ts-parse-file.d.ts +0 -8
  45. package/dist/cli/build/utils/ts-parse-file.js +0 -12
  46. /package/dist/cli/build/patches/{investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.d.ts → ast/webpack-runtime.spec.d.ts} +0 -0
  47. /package/dist/cli/build/patches/{investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.d.ts → plugins/load-instrumentation.spec.d.ts} +0 -0
@@ -0,0 +1,45 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { patchCode } from "../ast/util.js";
3
+ import { instrumentationRule } from "./load-instrumentation.js";
4
+ describe("LoadInstrumentationModule", () => {
5
+ test("patch", () => {
6
+ const code = `
7
+ export default class NextNodeServer extends BaseServer<
8
+ Options,
9
+ NodeNextRequest,
10
+ NodeNextResponse
11
+ > {
12
+ protected async loadInstrumentationModule() {
13
+ if (!this.serverOptions.dev) {
14
+ try {
15
+ this.instrumentation = await dynamicRequire(
16
+ resolve(
17
+ this.serverOptions.dir || '.',
18
+ this.serverOptions.conf.distDir!,
19
+ 'server',
20
+ INSTRUMENTATION_HOOK_FILENAME
21
+ )
22
+ )
23
+ } catch (err: any) {
24
+ if (err.code !== 'MODULE_NOT_FOUND') {
25
+ throw new Error(
26
+ 'An error occurred while loading the instrumentation hook',
27
+ { cause: err }
28
+ )
29
+ }
30
+ }
31
+ }
32
+ return this.instrumentation
33
+ }
34
+ }`;
35
+ expect(patchCode(code, instrumentationRule)).toMatchInlineSnapshot(`
36
+ "export default class NextNodeServer extends BaseServer<
37
+ Options,
38
+ NodeNextRequest,
39
+ NodeNextResponse
40
+ > {
41
+ async loadInstrumentationModule() { }
42
+ }"
43
+ `);
44
+ });
45
+ });
@@ -0,0 +1,3 @@
1
+ import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
2
+ import type { Plugin } from "esbuild";
3
+ export declare function shimRequireHook(options: BuildOptions): Plugin;
@@ -0,0 +1,13 @@
1
+ import { join } from "node:path";
2
+ import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
3
+ export function shimRequireHook(options) {
4
+ return {
5
+ name: "replaceRelative",
6
+ setup(build) {
7
+ // Note: we (empty) shim require-hook modules as they generate problematic code that uses requires
8
+ build.onResolve({ filter: getCrossPlatformPathRegex(String.raw `^\./require-hook$`, { escape: false }) }, () => ({
9
+ path: join(options.outputDir, "cloudflare-templates/shims/empty.js"),
10
+ }));
11
+ },
12
+ };
13
+ }
@@ -1,6 +1,3 @@
1
1
  import { type BuildOptions } from "@opennextjs/aws/build/helper.js";
2
- import type { PluginBuild } from "esbuild";
3
- export declare function inlineRequirePagePlugin(buildOpts: BuildOptions): {
4
- name: string;
5
- setup: (build: PluginBuild) => Promise<void>;
6
- };
2
+ import type { ContentUpdater } from "./content-updater.js";
3
+ export declare function inlineRequirePagePlugin(updater: ContentUpdater, buildOpts: BuildOptions): import("esbuild").Plugin;
@@ -1,46 +1,47 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
1
  import { readFile } from "node:fs/promises";
3
2
  import { join } from "node:path";
4
3
  import { getPackagePath } from "@opennextjs/aws/build/helper.js";
5
4
  import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
6
5
  import { patchCode } from "../ast/util.js";
7
- export function inlineRequirePagePlugin(buildOpts) {
8
- return {
9
- name: "inline-require-page",
10
- setup: async (build) => {
11
- build.onLoad({
12
- filter: getCrossPlatformPathRegex(String.raw `/next/dist/server/require\.js$`, { escape: false }),
13
- }, async ({ path }) => {
14
- const jsCode = await readFile(path, "utf8");
15
- if (/function requirePage\(/.test(jsCode)) {
16
- return { contents: patchCode(jsCode, getRule(buildOpts)) };
17
- }
18
- });
19
- },
20
- };
6
+ export function inlineRequirePagePlugin(updater, buildOpts) {
7
+ return updater.updateContent("inline-require-page", {
8
+ filter: getCrossPlatformPathRegex(String.raw `/next/dist/server/require\.js$`, { escape: false }),
9
+ }, async ({ contents }) => {
10
+ if (/function requirePage\(/.test(contents)) {
11
+ return patchCode(contents, await getRule(buildOpts));
12
+ }
13
+ });
21
14
  }
22
- function getRule(buildOpts) {
15
+ async function getRule(buildOpts) {
23
16
  const { outputDir } = buildOpts;
24
17
  const serverDir = join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next/server");
25
18
  const pagesManifestFile = join(serverDir, "pages-manifest.json");
26
19
  const appPathsManifestFile = join(serverDir, "app-paths-manifest.json");
27
- const pagesManifests = existsSync(pagesManifestFile)
28
- ? Object.values(JSON.parse(readFileSync(pagesManifestFile, "utf-8")))
29
- : [];
30
- const appPathsManifests = existsSync(appPathsManifestFile)
31
- ? Object.values(JSON.parse(readFileSync(appPathsManifestFile, "utf-8")))
32
- : [];
20
+ let pagesManifests = [];
21
+ try {
22
+ pagesManifests = Object.values(JSON.parse(await readFile(pagesManifestFile, "utf-8")));
23
+ }
24
+ catch {
25
+ // The file does not exists
26
+ pagesManifests = [];
27
+ }
28
+ let appPathsManifests;
29
+ try {
30
+ appPathsManifests = Object.values(JSON.parse(await readFile(appPathsManifestFile, "utf-8")));
31
+ }
32
+ catch {
33
+ // The file does not exists
34
+ appPathsManifests = [];
35
+ }
33
36
  const manifests = pagesManifests.concat(appPathsManifests);
34
37
  const htmlFiles = manifests.filter((file) => file.endsWith(".html"));
35
38
  const jsFiles = manifests.filter((file) => file.endsWith(".js"));
36
39
  // Inline fs access and dynamic require that are not supported by workerd.
37
40
  const fnBody = `
38
41
  // html
39
- ${htmlFiles
40
- .map((file) => `if (pagePath.endsWith("${file}")) {
41
- return ${JSON.stringify(readFileSync(join(serverDir, file), "utf-8"))};
42
- }`)
43
- .join("\n")}
42
+ ${(await Promise.all(htmlFiles.map(async (file) => `if (pagePath.endsWith("${file}")) {
43
+ return ${JSON.stringify(await readFile(join(serverDir, file), "utf-8"))};
44
+ }`))).join("\n")}
44
45
  // js
45
46
  process.env.__NEXT_PRIVATE_RUNTIME_TYPE = isAppPath ? 'app' : 'pages';
46
47
  try {
@@ -56,13 +57,13 @@ try {
56
57
  return {
57
58
  rule: {
58
59
  pattern: `
59
- function requirePage($PAGE, $DIST_DIR, $IS_APPP_ATH) {
60
+ function requirePage($PAGE, $DIST_DIR, $IS_APP_PATH) {
60
61
  const $_ = getPagePath($$$ARGS);
61
62
  $$$_BODY
62
63
  }`,
63
64
  },
64
65
  fix: `
65
- function requirePage($PAGE, $DIST_DIR, $IS_APPP_ATH) {
66
+ function requirePage($PAGE, $DIST_DIR, $IS_APP_PATH) {
66
67
  const pagePath = getPagePath($$$ARGS);
67
68
  ${fnBody}
68
69
  }`,
@@ -1,5 +1,2 @@
1
- import type { PluginBuild } from "esbuild";
2
- export declare function fixRequire(): {
3
- name: string;
4
- setup: (build: PluginBuild) => Promise<void>;
5
- };
1
+ import type { ContentUpdater } from "./content-updater";
2
+ export declare function fixRequire(updater: ContentUpdater): import("esbuild").Plugin;
@@ -1,44 +1,37 @@
1
- import fs from "node:fs/promises";
2
- export function fixRequire() {
3
- return {
4
- name: "fix-require",
5
- setup: async (build) => {
6
- build.onLoad({ filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/ }, async ({ path }) => {
7
- let contents = await fs.readFile(path, "utf-8");
8
- // `eval(...)` is not supported by workerd.
9
- contents = contents.replaceAll(`eval("require")`, "require");
10
- // `@opentelemetry` has a few issues.
11
- //
12
- // Next.js has the following code in `next/dist/server/lib/trace/tracer.js`:
13
- //
14
- // try {
15
- // api = require('@opentelemetry/api');
16
- // } catch (err) {
17
- // api = require('next/dist/compiled/@opentelemetry/api');
18
- // }
19
- //
20
- // The intent is to allow users to install their own version of `@opentelemetry/api`.
21
- //
22
- // The problem is that even when users do not explicitely install `@opentelemetry/api`,
23
- // `require('@opentelemetry/api')` resolves to the package which is a dependency
24
- // of Next.
25
- //
26
- // The second problem is that when Next traces files, it would not copy the `api/build/esm`
27
- // folder (used by the `module` conditions in package.json) it would only copy `api/build/src`.
28
- // This could be solved by updating the next config:
29
- //
30
- // const nextConfig: NextConfig = {
31
- // // ...
32
- // outputFileTracingIncludes: {
33
- // "*": ["./node_modules/@opentelemetry/api/build/**/*"],
34
- // },
35
- // };
36
- //
37
- // We can consider doing that when we want to enable users to install their own version
38
- // of `@opentelemetry/api`. For now we simply use the pre-compiled version.
39
- contents = contents.replace(/require\(.@opentelemetry\/api.\)/g, `require("next/dist/compiled/@opentelemetry/api")`);
40
- return { contents };
41
- });
42
- },
43
- };
1
+ export function fixRequire(updater) {
2
+ return updater.updateContent("fix-require", { filter: /\.(js|mjs|cjs|jsx|ts|tsx)$/ }, ({ contents }) => {
3
+ // `eval(...)` is not supported by workerd.
4
+ contents = contents.replaceAll(`eval("require")`, "require");
5
+ // `@opentelemetry` has a few issues.
6
+ //
7
+ // Next.js has the following code in `next/dist/server/lib/trace/tracer.js`:
8
+ //
9
+ // try {
10
+ // api = require('@opentelemetry/api');
11
+ // } catch (err) {
12
+ // api = require('next/dist/compiled/@opentelemetry/api');
13
+ // }
14
+ //
15
+ // The intent is to allow users to install their own version of `@opentelemetry/api`.
16
+ //
17
+ // The problem is that even when users do not explicitely install `@opentelemetry/api`,
18
+ // `require('@opentelemetry/api')` resolves to the package which is a dependency
19
+ // of Next.
20
+ //
21
+ // The second problem is that when Next traces files, it would not copy the `api/build/esm`
22
+ // folder (used by the `module` conditions in package.json) it would only copy `api/build/src`.
23
+ // This could be solved by updating the next config:
24
+ //
25
+ // const nextConfig: NextConfig = {
26
+ // // ...
27
+ // outputFileTracingIncludes: {
28
+ // "*": ["./node_modules/@opentelemetry/api/build/**/*"],
29
+ // },
30
+ // };
31
+ //
32
+ // We can consider doing that when we want to enable users to install their own version
33
+ // of `@opentelemetry/api`. For now we simply use the pre-compiled version.
34
+ contents = contents.replace(/require\(.@opentelemetry\/api.\)/g, `require("next/dist/compiled/@opentelemetry/api")`);
35
+ return contents;
36
+ });
44
37
  }
@@ -2,5 +2,4 @@ export * from "./inline-eval-manifest.js";
2
2
  export * from "./inline-middleware-manifest-require.js";
3
3
  export * from "./patch-exception-bubbling.js";
4
4
  export * from "./patch-find-dir.js";
5
- export * from "./patch-load-instrumentation-module.js";
6
5
  export * from "./patch-read-file.js";
@@ -2,5 +2,4 @@ export * from "./inline-eval-manifest.js";
2
2
  export * from "./inline-middleware-manifest-require.js";
3
3
  export * from "./patch-exception-bubbling.js";
4
4
  export * from "./patch-find-dir.js";
5
- export * from "./patch-load-instrumentation-module.js";
6
5
  export * from "./patch-read-file.js";
@@ -1,3 +1,4 @@
1
+ import logger from "@opennextjs/aws/logger.js";
1
2
  /**
2
3
  * Ensures open next is configured for cloudflare.
3
4
  *
@@ -9,13 +10,17 @@ export function ensureCloudflareConfig(config) {
9
10
  dftUseEdgeConverter: config.default?.override?.converter === "edge",
10
11
  dftMaybeUseCache: config.default?.override?.incrementalCache === "dummy" ||
11
12
  typeof config.default?.override?.incrementalCache === "function",
12
- dftUseDummyTagCacheAndQueue: config.default?.override?.tagCache === "dummy" && config.default?.override?.queue === "dummy",
13
+ dftUseDummyTagCache: config.default?.override?.tagCache === "dummy",
14
+ dftMaybeUseQueue: config.default?.override?.queue === "dummy" || config.default?.override?.queue === "direct",
13
15
  disableCacheInterception: config.dangerous?.enableCacheInterception !== true,
14
16
  mwIsMiddlewareExternal: config.middleware?.external == true,
15
17
  mwUseCloudflareWrapper: config.middleware?.override?.wrapper === "cloudflare-edge",
16
18
  mwUseEdgeConverter: config.middleware?.override?.converter === "edge",
17
19
  mwUseFetchProxy: config.middleware?.override?.proxyExternalRequest === "fetch",
18
20
  };
21
+ if (config.default?.override?.queue === "direct") {
22
+ logger.warn("The direct mode queue is not recommended for use in production.");
23
+ }
19
24
  if (Object.values(requirements).some((satisfied) => !satisfied)) {
20
25
  throw new Error("The `open-next.config.ts` should have a default export like this:\n\n" +
21
26
  `{
@@ -25,7 +30,7 @@ export function ensureCloudflareConfig(config) {
25
30
  converter: "edge",
26
31
  incrementalCache: "dummy" | function,
27
32
  tagCache: "dummy",
28
- queue: "dummy",
33
+ queue: "dummy" | "direct",
29
34
  },
30
35
  },
31
36
 
@@ -3,4 +3,3 @@ export * from "./create-config-files.js";
3
3
  export * from "./ensure-cf-config.js";
4
4
  export * from "./extract-project-env-vars.js";
5
5
  export * from "./normalize-path.js";
6
- export * from "./ts-parse-file.js";
@@ -3,4 +3,3 @@ export * from "./create-config-files.js";
3
3
  export * from "./ensure-cf-config.js";
4
4
  export * from "./extract-project-env-vars.js";
5
5
  export * from "./normalize-path.js";
6
- export * from "./ts-parse-file.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@opennextjs/cloudflare",
3
3
  "description": "Cloudflare builder for next apps",
4
- "version": "0.4.6",
4
+ "version": "0.4.7",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "opennextjs-cloudflare": "dist/cli/index.js"
@@ -63,14 +63,13 @@
63
63
  "dependencies": {
64
64
  "@ast-grep/napi": "^0.34.1",
65
65
  "@dotenvx/dotenvx": "1.31.0",
66
- "@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@724",
66
+ "@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@727",
67
67
  "enquirer": "^2.4.1",
68
68
  "glob": "^11.0.0",
69
- "ts-morph": "^23.0.0",
70
69
  "yaml": "^2.7.0"
71
70
  },
72
71
  "peerDependencies": {
73
- "wrangler": "^3.107.0"
72
+ "wrangler": "^3.107.3"
74
73
  },
75
74
  "scripts": {
76
75
  "clean": "rimraf dist",
@@ -1,6 +1,6 @@
1
1
  // default open-next.config.ts file created by @opennextjs/cloudflare
2
2
 
3
- import cache from "@opennextjs/cloudflare/kvCache";
3
+ import cache from "@opennextjs/cloudflare/kv-cache";
4
4
 
5
5
  const config = {
6
6
  default: {
@@ -1,13 +0,0 @@
1
- import * as ts from "ts-morph";
2
- /**
3
- * Gets the names of the variables that in the unminified webpack runtime file are called `installedChunks` and `installChunk`.
4
- *
5
- * Variables example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L256-L282
6
- *
7
- * @param sourceFile the webpack runtime file parsed with ts-morph
8
- * @returns an object containing the two variable names
9
- */
10
- export declare function getChunkInstallationIdentifiers(sourceFile: ts.SourceFile): Promise<{
11
- installedChunks: string;
12
- installChunk: string;
13
- }>;
@@ -1,82 +0,0 @@
1
- import * as ts from "ts-morph";
2
- /**
3
- * Gets the names of the variables that in the unminified webpack runtime file are called `installedChunks` and `installChunk`.
4
- *
5
- * Variables example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L256-L282
6
- *
7
- * @param sourceFile the webpack runtime file parsed with ts-morph
8
- * @returns an object containing the two variable names
9
- */
10
- export async function getChunkInstallationIdentifiers(sourceFile) {
11
- const installChunkDeclaration = getInstallChunkDeclaration(sourceFile);
12
- const installedChunksDeclaration = getInstalledChunksDeclaration(sourceFile, installChunkDeclaration);
13
- return {
14
- installChunk: installChunkDeclaration.getName(),
15
- installedChunks: installedChunksDeclaration.getName(),
16
- };
17
- }
18
- /**
19
- * Gets the declaration for what in the unminified webpack runtime file is called `installChunk`(which is a function that registers the various chunks.
20
- *
21
- * `installChunk` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L263-L282
22
- *
23
- * @param sourceFile the webpack runtime file parsed with ts-morph
24
- * @returns the `installChunk` declaration
25
- */
26
- function getInstallChunkDeclaration(sourceFile) {
27
- const installChunkDeclaration = sourceFile
28
- .getDescendantsOfKind(ts.SyntaxKind.VariableDeclaration)
29
- .find((declaration) => {
30
- const arrowFunction = declaration.getInitializerIfKind(ts.SyntaxKind.ArrowFunction);
31
- // we're looking for an arrow function
32
- if (!arrowFunction)
33
- return false;
34
- const functionParameters = arrowFunction.getParameters();
35
- // the arrow function we're looking for has a single parameter (the chunkId)
36
- if (functionParameters.length !== 1)
37
- return false;
38
- const arrowFunctionBodyBlock = arrowFunction.getFirstChildByKind(ts.SyntaxKind.Block);
39
- // the arrow function we're looking for has a block body
40
- if (!arrowFunctionBodyBlock)
41
- return false;
42
- const statementKinds = arrowFunctionBodyBlock.getStatements().map((statement) => statement.getKind());
43
- // the function we're looking for has 2 for loops (a standard one and a for-in one)
44
- const forInStatements = statementKinds.filter((s) => s === ts.SyntaxKind.ForInStatement);
45
- const forStatements = statementKinds.filter((s) => s === ts.SyntaxKind.ForStatement);
46
- if (forInStatements.length !== 1 || forStatements.length !== 1)
47
- return false;
48
- // the function we're looking for accesses its parameter three times, and it
49
- // accesses its `modules`, `ids` and `runtime` properties (in this order)
50
- const parameterName = functionParameters[0].getText();
51
- const functionParameterAccessedProperties = arrowFunctionBodyBlock
52
- .getDescendantsOfKind(ts.SyntaxKind.PropertyAccessExpression)
53
- .filter((propertyAccessExpression) => propertyAccessExpression.getExpression().getText() === parameterName)
54
- .map((propertyAccessExpression) => propertyAccessExpression.getName());
55
- if (functionParameterAccessedProperties.join(", ") !== "modules, ids, runtime")
56
- return false;
57
- return true;
58
- });
59
- if (!installChunkDeclaration) {
60
- throw new Error("ERROR: unable to find the installChunk function declaration");
61
- }
62
- return installChunkDeclaration;
63
- }
64
- /**
65
- * Gets the declaration for what in the unminified webpack runtime file is called `installedChunks` which is an object that holds the various registered chunks.
66
- *
67
- * `installedChunks` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L256-L261
68
- *
69
- * @param sourceFile the webpack runtime file parsed with ts-morph
70
- * @param installChunkDeclaration the declaration for the `installChunk` variable
71
- * @returns the `installedChunks` declaration
72
- */
73
- function getInstalledChunksDeclaration(sourceFile, installChunkDeclaration) {
74
- const allVariableDeclarations = sourceFile.getDescendantsOfKind(ts.SyntaxKind.VariableDeclaration);
75
- const installChunkDeclarationIdx = allVariableDeclarations.findIndex((declaration) => declaration === installChunkDeclaration);
76
- // the installedChunks declaration comes right before the installChunk one
77
- const installedChunksDeclaration = allVariableDeclarations[installChunkDeclarationIdx - 1];
78
- if (!installedChunksDeclaration?.getInitializer()?.isKind(ts.SyntaxKind.ObjectLiteralExpression)) {
79
- throw new Error("ERROR: unable to find the installedChunks declaration");
80
- }
81
- return installedChunksDeclaration;
82
- }
@@ -1,20 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { describe, expect, test } from "vitest";
3
- import { tsParseFile } from "../../../utils/index.js";
4
- import { getChunkInstallationIdentifiers } from "./get-chunk-installation-identifiers.js";
5
- describe("getChunkInstallationIdentifiers", () => {
6
- test("gets chunk identifiers from unminified code", async () => {
7
- const fileContent = await readFile(`${import.meta.dirname}/test-fixtures/unminified-webpacks-file.js`, "utf8");
8
- const tsSourceFile = tsParseFile(fileContent);
9
- const { installChunk, installedChunks } = await getChunkInstallationIdentifiers(tsSourceFile);
10
- expect(installChunk).toEqual("installChunk");
11
- expect(installedChunks).toEqual("installedChunks");
12
- });
13
- test("gets chunk identifiers from minified code", async () => {
14
- const fileContent = await readFile(`${import.meta.dirname}/test-fixtures/minified-webpacks-file.js`, "utf8");
15
- const tsSourceFile = tsParseFile(fileContent);
16
- const { installChunk, installedChunks } = await getChunkInstallationIdentifiers(tsSourceFile);
17
- expect(installChunk).toEqual("r");
18
- expect(installedChunks).toEqual("e");
19
- });
20
- });
@@ -1,19 +0,0 @@
1
- import * as ts from "ts-morph";
2
- /**
3
- * Updates the function that in the unminified webpack runtime file appears as `__webpack_require__.f.require` which is a function that
4
- * installs chunks by importing/requiring them at runtime.
5
- *
6
- * `__webpack_require__.f.require` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L284-L304
7
- *
8
- * This function needs to be updated so that it requires chunks using the standard `require` function and not webpack's custom `require` logic
9
- * which fails in the workerd runtime.
10
- *
11
- * @param sourceFile the webpack runtime file parsed with ts-morph (note: this gets side-effectfully updated)
12
- * @param chunkInstallationIdentifiers the names of the `installedChunks` and `installChunk` variables
13
- * @param chunks the identifiers of the chunks (found on the filesystem)
14
- * @returns the content of the sourceFile but with the require function updated
15
- */
16
- export declare function getFileContentWithUpdatedWebpackFRequireCode(sourceFile: ts.SourceFile, { installedChunks, installChunk }: {
17
- installedChunks: string;
18
- installChunk: string;
19
- }, chunks: string[]): Promise<string>;
@@ -1,84 +0,0 @@
1
- import * as ts from "ts-morph";
2
- /**
3
- * Updates the function that in the unminified webpack runtime file appears as `__webpack_require__.f.require` which is a function that
4
- * installs chunks by importing/requiring them at runtime.
5
- *
6
- * `__webpack_require__.f.require` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L284-L304
7
- *
8
- * This function needs to be updated so that it requires chunks using the standard `require` function and not webpack's custom `require` logic
9
- * which fails in the workerd runtime.
10
- *
11
- * @param sourceFile the webpack runtime file parsed with ts-morph (note: this gets side-effectfully updated)
12
- * @param chunkInstallationIdentifiers the names of the `installedChunks` and `installChunk` variables
13
- * @param chunks the identifiers of the chunks (found on the filesystem)
14
- * @returns the content of the sourceFile but with the require function updated
15
- */
16
- export async function getFileContentWithUpdatedWebpackFRequireCode(sourceFile, { installedChunks, installChunk }, chunks) {
17
- const webpackFRequireFunction = sourceFile
18
- .getDescendantsOfKind(ts.SyntaxKind.ArrowFunction)
19
- .find((arrowFunction) => {
20
- // the function is declared assigned in a binary expression, so let's check the binary expression
21
- // to get more confidence that we've got the right arrow function
22
- const binaryExpression = arrowFunction.getFirstAncestorByKind(ts.SyntaxKind.BinaryExpression);
23
- if (!binaryExpression)
24
- return false;
25
- // the variable being assigned always ends with .f.require (even in unminified files)
26
- const binaryExpressionLeft = binaryExpression.getLeft();
27
- if (!binaryExpressionLeft.getText().endsWith(".f.require"))
28
- return false;
29
- // the binary expression is an assignment
30
- const binaryExpressionOperator = binaryExpression.getOperatorToken();
31
- if (binaryExpressionOperator.getText() !== "=")
32
- return false;
33
- // the right side of the expression is our arrow function
34
- const binaryExpressionRight = binaryExpression.getRight();
35
- if (binaryExpressionRight !== arrowFunction)
36
- return false;
37
- const arrowFunctionBody = arrowFunction.getBody();
38
- if (!arrowFunctionBody.isKind(ts.SyntaxKind.Block))
39
- return false;
40
- const arrowFunctionBodyText = arrowFunctionBody.getText();
41
- const functionUsesChunkInstallationVariables = arrowFunctionBodyText.includes(installChunk) && arrowFunctionBodyText.includes(installedChunks);
42
- if (!functionUsesChunkInstallationVariables)
43
- return false;
44
- const functionParameters = arrowFunction.getParameters();
45
- if (functionParameters.length !== 2)
46
- return false;
47
- const callsInstallChunk = arrowFunctionBody
48
- .getDescendantsOfKind(ts.SyntaxKind.CallExpression)
49
- .some((callExpression) => callExpression.getExpression().getText() === installChunk);
50
- if (!callsInstallChunk)
51
- return false;
52
- const functionFirstParameterName = functionParameters[0]?.getName();
53
- const accessesInstalledChunksUsingItsFirstParameter = arrowFunctionBody
54
- .getDescendantsOfKind(ts.SyntaxKind.ElementAccessExpression)
55
- .some((elementAccess) => {
56
- return (elementAccess.getExpression().getText() === installedChunks &&
57
- elementAccess.getArgumentExpression()?.getText() === functionFirstParameterName);
58
- });
59
- if (!accessesInstalledChunksUsingItsFirstParameter)
60
- return false;
61
- return true;
62
- });
63
- if (!webpackFRequireFunction) {
64
- throw new Error("ERROR: unable to find the webpack f require function declaration");
65
- }
66
- const functionParameterNames = webpackFRequireFunction
67
- .getParameters()
68
- .map((parameter) => parameter.getName());
69
- const chunkId = functionParameterNames[0];
70
- const functionBody = webpackFRequireFunction.getBody();
71
- functionBody.replaceWithText(`
72
- {
73
- if (${installedChunks}[${chunkId}]) {
74
- return;
75
- }${chunks
76
- .map((chunk) => `
77
- if(${chunkId} === ${chunk}) {
78
- return ${installChunk}(require("./chunks/${chunk}.js"));
79
- }`)
80
- .join("")}
81
- throw new Error(\`Unknown chunk \${${chunkId}}\`);
82
- }`);
83
- return sourceFile.print();
84
- }
@@ -1,25 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { describe, expect, test } from "vitest";
3
- import { tsParseFile } from "../../../utils/index.js";
4
- import { getFileContentWithUpdatedWebpackFRequireCode } from "./get-file-content-with-updated-webpack-f-require-code";
5
- describe("getFileContentWithUpdatedWebpackFRequireCode", () => {
6
- test("returns the updated content of the f.require function from unminified webpack runtime code", async () => {
7
- const fileContent = await readFile(`${import.meta.dirname}/test-fixtures/unminified-webpacks-file.js`, "utf8");
8
- const tsSourceFile = tsParseFile(fileContent);
9
- const updatedFCode = await getFileContentWithUpdatedWebpackFRequireCode(tsSourceFile, { installChunk: "installChunk", installedChunks: "installedChunks" }, ["658"]);
10
- expect(unstyleCode(updatedFCode)).toContain(`if (installedChunks[chunkId]) { return; }`);
11
- expect(unstyleCode(updatedFCode)).toContain(`if (chunkId === 658) { return installChunk(require("./chunks/658.js")); }`);
12
- expect(unstyleCode(updatedFCode)).not.toContain(`require("./chunks/" +`);
13
- });
14
- test("returns the updated content of the f.require function from minified webpack runtime code", async () => {
15
- const fileContent = await readFile(`${import.meta.dirname}/test-fixtures/minified-webpacks-file.js`, "utf8");
16
- const tsSourceFile = tsParseFile(fileContent);
17
- const updatedFCode = await getFileContentWithUpdatedWebpackFRequireCode(tsSourceFile, { installChunk: "r", installedChunks: "e" }, ["658"]);
18
- expect(unstyleCode(updatedFCode)).toContain("if (e[o]) { return; }");
19
- expect(unstyleCode(updatedFCode)).toContain(`if (o === 658) { return r(require("./chunks/658.js")); }`);
20
- expect(unstyleCode(updatedFCode)).not.toContain(`require("./chunks/" +`);
21
- });
22
- });
23
- function unstyleCode(text) {
24
- return text.replace(/\n\s+/g, "\n").replace(/\n/g, " ");
25
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Updates the content of the webpack runtime file in a manner so that it doesn't perform runtime dynamic `require` calls which fail in our runtime.
3
- *
4
- * It does so by appropriately updating a function that in the unminified webpack runtime file appears as `__webpack_require__.f.require` which is
5
- * the one that normally would cause dynamic requires to happen at runtime.
6
- *
7
- * `__webpack_require__.f.require` example: https://github.com/webpack/webpack/blob/dae16ad11e/examples/module-worker/README.md?plain=1#L284-L304
8
- *
9
- *
10
- * @param fileContent the content of the webpack runtime file
11
- * @param chunks the identifiers of the chunks (found on the filesystem)
12
- * @returns the content of the webpack runtime file updated with our custom logic
13
- */
14
- export declare function getUpdatedWebpackChunksFileContent(fileContent: string, chunks: string[]): Promise<string>;