Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
63e3d4c
fix(app-router): ignore very dynamic requests during build analysis
NathanDrake2406 Jun 4, 2026
403d3ec
fix(plugins/ignore-dynamic-requests): tighten semantics for dynamic r…
NathanDrake2406 Jun 4, 2026
9d8e542
chore: trigger ci
NathanDrake2406 Jun 4, 2026
13b6589
test(build-optimization): fix catch-binding assertion for helper name…
NathanDrake2406 Jun 4, 2026
7006b69
fix(scope-analysis): two-phase hoisted declaration collection for req…
NathanDrake2406 Jun 5, 2026
4810c73
fix(scope-analysis): correct block-scoped let/const shadowing for TDZ…
NathanDrake2406 Jun 5, 2026
7d4b4a2
fix(scope-analysis): model for-loop lexical header and switch-shared …
NathanDrake2406 Jun 5, 2026
4b893bf
fix(scope-analysis): walk function parameter defaults and model class…
NathanDrake2406 Jun 5, 2026
5e460b0
fix(scope-analysis): add distinct static-block scope type for var sem…
NathanDrake2406 Jun 5, 2026
ada1fc3
fix(scope-analysis): model named class expressions and TS declare, de…
NathanDrake2406 Jun 5, 2026
34347e4
Merge remote-tracking branch 'upstream/main' into nathan/dynamic-requ…
NathanDrake2406 Jun 5, 2026
5b3e940
fix(build): handle remaining dynamic request AST edges
NathanDrake2406 Jun 5, 2026
41e776f
fix(build): preserve labelled var require and empty literals
NathanDrake2406 Jun 5, 2026
1933ae7
Merge branch 'main' into nathan/dynamic-requests
NathanDrake2406 Jun 5, 2026
8d4e93e
chore: retrigger CI
NathanDrake2406 Jun 5, 2026
eb71e05
fix(build): narrow dynamic request precheck
NathanDrake2406 Jun 6, 2026
857175e
Merge branch 'main' into nathan/dynamic-requests
NathanDrake2406 Jun 6, 2026
dafd2e1
fix(build): emit Next-like dynamic request failures
NathanDrake2406 Jun 6, 2026
c5193e1
fix(build): require one dynamic request argument
NathanDrake2406 Jun 6, 2026
eebfb4b
fix(build): skip vinext internals in dynamic request guard
NathanDrake2406 Jun 6, 2026
a3f506f
fix(build): skip spread require dynamic guard
NathanDrake2406 Jun 7, 2026
3a73461
Merge branch 'main' into nathan/dynamic-requests
NathanDrake2406 Jun 9, 2026
3688a70
refactor(plugins): dedup AST and module-ID helpers across transform p…
NathanDrake2406 Jun 13, 2026
225e5d4
fix(plugins): getObjectProperty handles functions; keep report/check …
NathanDrake2406 Jun 13, 2026
68a62f9
Merge remote-tracking branch 'origin/main' into codex/pr-1736-owner
james-elicx Jun 14, 2026
b9be15a
fix(build): align very dynamic request classification
james-elicx Jun 14, 2026
38977d6
fix(build): respect resource require bindings
james-elicx Jun 14, 2026
1cd8fd5
fix(build): complete require scope tracking
james-elicx Jun 14, 2026
8d6a1c4
fix(build): respect named class heritage bindings
james-elicx Jun 14, 2026
7bf52e8
fix(build): finish dynamic request pattern parity
james-elicx Jun 14, 2026
8798c14
fix(build): normalize dynamic request expressions
james-elicx Jun 14, 2026
cee2795
fix(build): preserve dynamic request scope parity
james-elicx Jun 14, 2026
224ea1c
fix(build): remove unused AST helper
james-elicx Jun 14, 2026
316c77e
fix(build): handle wrapped dynamic require calls
james-elicx Jun 14, 2026
e3563fa
fix(build): avoid dynamic import precheck backtracking
james-elicx Jun 14, 2026
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
5 changes: 5 additions & 0 deletions packages/vinext/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ import {
import { stripServerExports } from "./plugins/strip-server-exports.js";
import { removeConsoleCalls } from "./plugins/remove-console.js";
import { createImportMetaUrlPlugin } from "./plugins/import-meta-url.js";
import { createIgnoreDynamicRequestsPlugin } from "./plugins/ignore-dynamic-requests.js";
import { createRequireContextPlugin } from "./plugins/require-context.js";
import { createExtensionlessDynamicImportPlugin } from "./plugins/extensionless-dynamic-import.js";
import { createWasmModuleImportPlugin } from "./plugins/wasm-module-import.js";
Expand Down Expand Up @@ -1024,6 +1025,10 @@ export default function vinext(options: VinextOptions = {}): PluginOption[] {
...(viteMajorVersion >= 8 ? [] : [tsconfigPaths()]),
// React Fast Refresh + JSX transform for client components.
reactPluginPromise,
// Next.js/Turbopack ignores "very dynamic" requests with no static path
// part during graph analysis. Preserve that behaviour before the CJS
// plugin tries to expand require(dynamic) as a static glob.
createIgnoreDynamicRequestsPlugin(),
// Transform CJS require()/module.exports to ESM before other plugins
// analyze imports (RSC directive scanning, shim resolution, etc.)
commonjs(),
Expand Down
36 changes: 33 additions & 3 deletions packages/vinext/src/plugins/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,30 @@ export type AstRange = AstRecord & {

const SKIP_CHILD_KEYS = new Set(["type", "parent", "loc", "start", "end"]);

function getObjectProperty(value: unknown, key: string): unknown {
if (typeof value !== "object" || value === null) return null;
export function getObjectProperty(value: unknown, key: string): unknown {
if ((typeof value !== "object" || value === null) && typeof value !== "function") return null;
return Reflect.get(value, key);
}

export function isAstRecord(value: unknown): value is AstRecord {
return typeof getObjectProperty(value, "type") === "string";
}

function toAstRecord(value: unknown): AstRecord | null {
export function toAstRecord(value: unknown): AstRecord | null {
return isAstRecord(value) ? value : null;
}

export function nodeArray(value: unknown): unknown[] {
return Array.isArray(value) ? value : [];
}

export function astArray(value: unknown): AstRecord[] {
return nodeArray(value).flatMap((entry) => {
const node = toAstRecord(entry);
return node ? [node] : [];
});
}

export function hasRange(node: AstRecord | null): node is AstRange {
return node !== null && typeof node.start === "number" && typeof node.end === "number";
}
Expand Down Expand Up @@ -62,6 +69,26 @@ export function forEachAstChild(node: AstRecord, callback: (child: AstRecord) =>
}
}

/**
* Expression wrappers that are transparent at runtime. Unwrapping them returns
* the underlying expression so callers can analyze the real value.
*/
const TRANSPARENT_EXPRESSION_TYPES = new Set([
"TSAsExpression",
"TSTypeAssertion",
"TSNonNullExpression",
"TSInstantiationExpression",
"TSSatisfiesExpression",
"ParenthesizedExpression",
"ChainExpression",
]);

export function unwrapTransparentExpression(node: AstRecord | null): AstRecord | null {
if (!node) return null;
if (!TRANSPARENT_EXPRESSION_TYPES.has(node.type)) return node;
return unwrapTransparentExpression(toAstRecord(getObjectProperty(node, "expression")));
}

export function collectBindingNames(pattern: unknown, target: Set<string>): void {
const node = toAstRecord(pattern);
if (!node) return;
Expand All @@ -76,6 +103,9 @@ export function collectBindingNames(pattern: unknown, target: Set<string>): void
case "AssignmentPattern":
collectBindingNames(node.left, target);
return;
case "TSParameterProperty":
collectBindingNames(node.parameter, target);
return;
case "ArrayPattern":
for (const element of nodeArray(node.elements)) collectBindingNames(element, target);
return;
Expand Down
Loading
Loading