Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/e2e/app-router/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ const nextConfig: NextConfig = {
transpilePackages: ["@example/shared"],
output: "standalone",
// outputFileTracingRoot: "../sst",
typescript: {
ignoreBuildErrors: true,
},
eslint: {
ignoreDuringBuilds: true,
},
Expand Down
2 changes: 1 addition & 1 deletion examples/e2e/app-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"start": "next start --port 3001",
"lint": "next lint",
"clean": "rm -rf .turbo node_modules .next .open-next",
"build:worker-tofix": "pnpm opennextjs-cloudflare",
"build:worker": "pnpm opennextjs-cloudflare",
"dev:worker": "wrangler dev --port 8770 --inspector-port 9330",
"preview": "pnpm build:worker && pnpm dev:worker"
},
Expand Down
8 changes: 5 additions & 3 deletions packages/cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,26 @@
"@types/mock-fs": "catalog:",
"@types/node": "catalog:",
"esbuild": "catalog:",
"eslint": "catalog:",
"eslint-plugin-import": "catalog:",
"eslint-plugin-simple-import-sort": "catalog:",
"eslint-plugin-unicorn": "catalog:",
"eslint": "catalog:",
"globals": "catalog:",
"mock-fs": "catalog:",
"next": "catalog:",
"rimraf": "catalog:",
"typescript-eslint": "catalog:",
"typescript": "catalog:",
"typescript-eslint": "catalog:",
"vitest": "catalog:"
},
"dependencies": {
"@ast-grep/napi": "^0.33.1",
"@dotenvx/dotenvx": "catalog:",
"@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@704",
"enquirer": "^2.4.1",
"glob": "catalog:",
"ts-morph": "catalog:",
"enquirer": "^2.4.1"
"yaml": "^2.7.0"
},
"peerDependencies": {
"wrangler": "catalog:"
Expand Down
10 changes: 8 additions & 2 deletions packages/cloudflare/src/cli/build/bundle-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

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

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

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

await writeFile(workerOutputFile, patchedCode);
const bundle = parse(Lang.TypeScript, patchedCode).root();

const edits = patchOptionalDependencies(bundle);

await writeFile(workerOutputFile, bundle.commitEdits(edits));
}

function createFixRequiresESBuildPlugin(config: Config): Plugin {
Expand Down
41 changes: 41 additions & 0 deletions packages/cloudflare/src/cli/build/patches/ast/optional-deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { type SgNode } from "@ast-grep/napi";

import { applyRule } from "./util.js";

/**
* Handle optional dependencies.
*
* A top level `require(dep)` would throw when the dep is not installed.
*
* So we wrap any of
* - `t = require("dep")`
* - `t = require("dep/sub/path")`
* - `t = require("dep/sub/path/" + var)`
* - `e.exports = require("dep")`
*
* in a try/catch (only if not already).
*/
const rule = `
rule:
pattern: $$$LHS = require($$$REQ)
has:
pattern: $MOD
kind: string_fragment
stopBy: end
regex: ^caniuse-lite(/|$)
not:
inside:
kind: try_statement
stopBy: end

fix: |-
try {
$$$LHS = require($$$REQ);
} catch {
throw new Error('The optional dependency "$MOD" is not installed');
}
`;

export function patchOptionalDependencies(root: SgNode) {
return applyRule(rule, root);
}
48 changes: 48 additions & 0 deletions packages/cloudflare/src/cli/build/patches/ast/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { type Edit, type NapiConfig, type SgNode } from "@ast-grep/napi";
import yaml from "yaml";

/**
* Returns the `Edit`s for an ast-grep rule in yaml format
*
* The rule must have a `fix` to rewrite the matched node.
*
* Tip: use https://ast-grep.github.io/playground.html to create rules.
*
* @param yamlRule The rule in yaml format
* @param root The root node
* @param once only apply once
* @returns A list of edits.
*/
export function applyRule(yamlRule: string, root: SgNode, { once = false } = {}) {
const rule: NapiConfig & { fix?: string } = yaml.parse(yamlRule);
if (rule.transform) {
throw new Error("transform is not supported");
}
if (!rule.fix) {
throw new Error("no fix to apply");
}

const fix = rule.fix;

const matches = once ? [root.find(rule)].filter((m) => m !== null) : root.findAll(rule);

const edits: Edit[] = [];

matches.forEach((match) => {
edits.push(
match.replace(
// Replace known placeholders by their value
fix
.replace(/\$\$\$([A-Z0-9_]+)/g, (_m, name) =>
match
.getMultipleMatches(name)
.map((n) => n.text())
.join()
)
.replace(/\$([A-Z0-9_]+)/g, (m, name) => match.getMatch(name)?.text() ?? m)
)
);
});

return edits;
}
Loading
Loading