Skip to content

Commit 25eb4b4

Browse files
authored
fix: fix the build of examples/e2e/app-router (#275)
1 parent baf8ff4 commit 25eb4b4

File tree

8 files changed

+234
-92
lines changed

8 files changed

+234
-92
lines changed

examples/e2e/app-router/next.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ const nextConfig: NextConfig = {
66
transpilePackages: ["@example/shared"],
77
output: "standalone",
88
// outputFileTracingRoot: "../sst",
9+
typescript: {
10+
ignoreBuildErrors: true,
11+
},
912
eslint: {
1013
ignoreDuringBuilds: true,
1114
},

examples/e2e/app-router/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"start": "next start --port 3001",
1010
"lint": "next lint",
1111
"clean": "rm -rf .turbo node_modules .next .open-next",
12-
"build:worker-tofix": "pnpm opennextjs-cloudflare",
12+
"build:worker": "pnpm opennextjs-cloudflare",
1313
"dev:worker": "wrangler dev --port 8770 --inspector-port 9330",
1414
"preview": "pnpm build:worker && pnpm dev:worker"
1515
},

packages/cloudflare/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,26 @@
5858
"@types/mock-fs": "catalog:",
5959
"@types/node": "catalog:",
6060
"esbuild": "catalog:",
61+
"eslint": "catalog:",
6162
"eslint-plugin-import": "catalog:",
6263
"eslint-plugin-simple-import-sort": "catalog:",
6364
"eslint-plugin-unicorn": "catalog:",
64-
"eslint": "catalog:",
6565
"globals": "catalog:",
6666
"mock-fs": "catalog:",
6767
"next": "catalog:",
6868
"rimraf": "catalog:",
69-
"typescript-eslint": "catalog:",
7069
"typescript": "catalog:",
70+
"typescript-eslint": "catalog:",
7171
"vitest": "catalog:"
7272
},
7373
"dependencies": {
74+
"@ast-grep/napi": "^0.33.1",
7475
"@dotenvx/dotenvx": "catalog:",
7576
"@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@704",
77+
"enquirer": "^2.4.1",
7678
"glob": "catalog:",
7779
"ts-morph": "catalog:",
78-
"enquirer": "^2.4.1"
80+
"yaml": "^2.7.0"
7981
},
8082
"peerDependencies": {
8183
"wrangler": "catalog:"

packages/cloudflare/src/cli/build/bundle-server.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { readFile, writeFile } from "node:fs/promises";
33
import path from "node:path";
44
import { fileURLToPath } from "node:url";
55

6+
import { Lang, parse } from "@ast-grep/napi";
67
import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
78
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
89
import { build, Plugin } from "esbuild";
910

1011
import { Config } from "../config.js";
12+
import { patchOptionalDependencies } from "./patches/ast/optional-deps.js";
1113
import * as patches from "./patches/index.js";
1214
import { normalizePath, patchCodeWithValidations } from "./utils/index.js";
1315

@@ -44,7 +46,7 @@ export async function bundleServer(config: Config, openNextOptions: BuildOptions
4446
target: "esnext",
4547
minify: false,
4648
plugins: [createFixRequiresESBuildPlugin(config)],
47-
external: ["./middleware/handler.mjs"],
49+
external: ["./middleware/handler.mjs", "caniuse-lite"],
4850
alias: {
4951
// Note: we apply an empty shim to next/dist/compiled/ws because it generates two `eval`s:
5052
// eval("require")("bufferutil");
@@ -176,7 +178,11 @@ async function updateWorkerBundledCode(
176178
],
177179
]);
178180

179-
await writeFile(workerOutputFile, patchedCode);
181+
const bundle = parse(Lang.TypeScript, patchedCode).root();
182+
183+
const edits = patchOptionalDependencies(bundle);
184+
185+
await writeFile(workerOutputFile, bundle.commitEdits(edits));
180186
}
181187

182188
function createFixRequiresESBuildPlugin(config: Config): Plugin {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { type SgNode } from "@ast-grep/napi";
2+
3+
import { applyRule } from "./util.js";
4+
5+
/**
6+
* Handle optional dependencies.
7+
*
8+
* A top level `require(dep)` would throw when the dep is not installed.
9+
*
10+
* So we wrap any of
11+
* - `t = require("dep")`
12+
* - `t = require("dep/sub/path")`
13+
* - `t = require("dep/sub/path/" + var)`
14+
* - `e.exports = require("dep")`
15+
*
16+
* in a try/catch (only if not already).
17+
*/
18+
const rule = `
19+
rule:
20+
pattern: $$$LHS = require($$$REQ)
21+
has:
22+
pattern: $MOD
23+
kind: string_fragment
24+
stopBy: end
25+
regex: ^caniuse-lite(/|$)
26+
not:
27+
inside:
28+
kind: try_statement
29+
stopBy: end
30+
31+
fix: |-
32+
try {
33+
$$$LHS = require($$$REQ);
34+
} catch {
35+
throw new Error('The optional dependency "$MOD" is not installed');
36+
}
37+
`;
38+
39+
export function patchOptionalDependencies(root: SgNode) {
40+
return applyRule(rule, root);
41+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { type Edit, type NapiConfig, type SgNode } from "@ast-grep/napi";
2+
import yaml from "yaml";
3+
4+
/**
5+
* Returns the `Edit`s for an ast-grep rule in yaml format
6+
*
7+
* The rule must have a `fix` to rewrite the matched node.
8+
*
9+
* Tip: use https://ast-grep.github.io/playground.html to create rules.
10+
*
11+
* @param yamlRule The rule in yaml format
12+
* @param root The root node
13+
* @param once only apply once
14+
* @returns A list of edits.
15+
*/
16+
export function applyRule(yamlRule: string, root: SgNode, { once = false } = {}) {
17+
const rule: NapiConfig & { fix?: string } = yaml.parse(yamlRule);
18+
if (rule.transform) {
19+
throw new Error("transform is not supported");
20+
}
21+
if (!rule.fix) {
22+
throw new Error("no fix to apply");
23+
}
24+
25+
const fix = rule.fix;
26+
27+
const matches = once ? [root.find(rule)].filter((m) => m !== null) : root.findAll(rule);
28+
29+
const edits: Edit[] = [];
30+
31+
matches.forEach((match) => {
32+
edits.push(
33+
match.replace(
34+
// Replace known placeholders by their value
35+
fix
36+
.replace(/\$\$\$([A-Z0-9_]+)/g, (_m, name) =>
37+
match
38+
.getMultipleMatches(name)
39+
.map((n) => n.text())
40+
.join("")
41+
)
42+
.replace(/\$([A-Z0-9_]+)/g, (m, name) => match.getMatch(name)?.text() ?? m)
43+
)
44+
);
45+
});
46+
47+
return edits;
48+
}

0 commit comments

Comments
 (0)