@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.
- package/dist/api/kv-cache.d.ts +22 -0
- package/dist/api/kv-cache.js +123 -0
- package/dist/api/kvCache.d.ts +3 -20
- package/dist/api/kvCache.js +3 -106
- package/dist/cli/build/bundle-server.js +12 -17
- package/dist/cli/build/open-next/copyCacheAssets.js +1 -1
- package/dist/cli/build/patches/ast/webpack-runtime.d.ts +23 -0
- package/dist/cli/build/patches/ast/webpack-runtime.js +57 -0
- package/dist/cli/build/patches/ast/webpack-runtime.spec.js +57 -0
- package/dist/cli/build/patches/investigated/index.d.ts +0 -1
- package/dist/cli/build/patches/investigated/index.js +0 -1
- package/dist/cli/build/patches/plugins/content-updater.d.ts +34 -0
- package/dist/cli/build/patches/plugins/content-updater.js +52 -0
- package/dist/cli/build/patches/plugins/load-instrumentation.d.ts +6 -0
- package/dist/cli/build/patches/plugins/load-instrumentation.js +20 -0
- package/dist/cli/build/patches/plugins/load-instrumentation.spec.js +45 -0
- package/dist/cli/build/patches/plugins/require-hook.d.ts +3 -0
- package/dist/cli/build/patches/plugins/require-hook.js +13 -0
- package/dist/cli/build/patches/plugins/require-page.d.ts +2 -5
- package/dist/cli/build/patches/plugins/require-page.js +30 -29
- package/dist/cli/build/patches/plugins/require.d.ts +2 -5
- package/dist/cli/build/patches/plugins/require.js +36 -43
- package/dist/cli/build/patches/to-investigate/index.d.ts +0 -1
- package/dist/cli/build/patches/to-investigate/index.js +0 -1
- package/dist/cli/build/utils/ensure-cf-config.js +7 -2
- package/dist/cli/build/utils/index.d.ts +0 -1
- package/dist/cli/build/utils/index.js +0 -1
- package/package.json +3 -4
- package/templates/defaults/open-next.config.ts +1 -1
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.d.ts +0 -13
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.js +0 -82
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.js +0 -20
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.d.ts +0 -19
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.js +0 -84
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.js +0 -25
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.d.ts +0 -14
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.js +0 -22
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.d.ts +0 -1
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.js +0 -15
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/index.d.ts +0 -5
- package/dist/cli/build/patches/investigated/update-webpack-chunks-file/index.js +0 -22
- package/dist/cli/build/patches/to-investigate/patch-load-instrumentation-module.d.ts +0 -14
- package/dist/cli/build/patches/to-investigate/patch-load-instrumentation-module.js +0 -34
- package/dist/cli/build/utils/ts-parse-file.d.ts +0 -8
- package/dist/cli/build/utils/ts-parse-file.js +0 -12
- /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
- /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,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 {
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
.
|
|
41
|
-
|
|
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, $
|
|
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, $
|
|
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 {
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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@
|
|
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.
|
|
72
|
+
"wrangler": "^3.107.3"
|
|
74
73
|
},
|
|
75
74
|
"scripts": {
|
|
76
75
|
"clean": "rimraf dist",
|
|
@@ -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>;
|