Skip to content

Commit eb602d4

Browse files
authored
Turbopack: Document the reasons for the current design of parse_segment_config_from_source (#83919)
Document all my learnings from my saga of trying to get Flow syntax to work in entrypoints with the babel loader and ultimately giving up, so that hopefully others don't waste their time like I did. https://vercel.slack.com/archives/C03EWR7LGEN/p1757982766169869
1 parent 21f4c9f commit eb602d4

File tree

1 file changed

+63
-0
lines changed

1 file changed

+63
-0
lines changed

crates/next-core/src/segment_config.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,67 @@ pub enum ParseSegmentMode {
300300
App,
301301
}
302302

303+
/// Parse the raw source code of a file to get the segment config local to that file.
304+
///
305+
/// See [the Next.js documentation for Route Segment
306+
/// Configs](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config).
307+
///
308+
/// Pages router and middleware use this directly. App router uses
309+
/// `parse_segment_config_from_loader_tree` instead, which aggregates configuration information
310+
/// across multiple files.
311+
///
312+
/// ## A Note on Parsing the Raw Source Code
313+
///
314+
/// A better API would use `ModuleAssetContext::process` to convert the `Source` to a `Module`,
315+
/// instead of parsing the raw source code. That would ensure that things like webpack loaders can
316+
/// run before SWC tries to parse the file, e.g. to strip unsupported syntax using Babel. However,
317+
/// because the config includes `runtime`, we can't know which context to use until after parsing
318+
/// the file.
319+
///
320+
/// This could be solved with speculative parsing:
321+
/// 1. Speculatively process files and extract route segment configs using the Node.js
322+
/// `ModuleAssetContext` first. This is the common/happy codepath.
323+
/// 2. If we get a config specifying `runtime = "edge"`, we should use the Edge runtime's
324+
/// `ModuleAssetContext` and re-process the file(s), extracting the segment config again.
325+
/// 3. If we failed to get a configuration (e.g. a parse error), we need speculatively process with
326+
/// the Edge runtime and look for a `runtime = "edge"` configuration key. If that also fails,
327+
/// then we should report any issues/errors from the first attempt using the Node.js context.
328+
///
329+
/// While a speculative parsing algorithm is straightforward, there are a few factors that make it
330+
/// impractical to implement:
331+
///
332+
/// - The app router config is loaded across many different files (page, layout, or route handler,
333+
/// including an arbitrary number of those files in parallel routes), and once we discover that
334+
/// something specified edge runtime, we must restart that entire loop, so try/reparse logic can't
335+
/// be cleanly encapsulated to an operation over a single file.
336+
///
337+
/// - There's a lot of tracking that needs to happen to later suppress `Issue` collectibles on
338+
/// speculatively-executed `OperationVc`s.
339+
///
340+
/// - Most things default to the node.js runtime and can be overridden to edge runtime, but
341+
/// middleware is an exception, so different codepaths have different defaults.
342+
///
343+
/// The `runtime` option is going to be deprecated, and we may eventually remove edge runtime
344+
/// completely (in Next 18?), so it doesn't make sense to spend a ton of time improving logic around
345+
/// that. In the future, doing this the right way with the `ModuleAssetContext` will be easy (there
346+
/// will only be one, no speculative parsing is needed), and I think it's okay to use a hacky
347+
/// solution for a couple years until that day comes.
348+
///
349+
/// ## What does webpack do?
350+
///
351+
/// The logic is in `packages/next/src/build/analysis/get-page-static-info.ts`, but it's very
352+
/// similar to what we do here.
353+
///
354+
/// There are a couple of notable differences:
355+
///
356+
/// - The webpack implementation uses a regexp (`PARSE_PATTERN`) to skip parsing some files, but
357+
/// this regexp is imperfect and may also suppress some lints that we have. The performance
358+
/// benefit is small, so we're not currently doing this (but we could revisit that decision in the
359+
/// future).
360+
///
361+
/// - The `parseModule` helper function swallows errors (!) returning a `null` ast value when
362+
/// parsing fails. This seems bad, as it may lead to silently-ignored segment configs, so we don't
363+
/// want to do this.
303364
#[turbo_tasks::function]
304365
pub async fn parse_segment_config_from_source(
305366
source: ResolvedVc<Box<dyn Source>>,
@@ -1229,6 +1290,8 @@ async fn parse_route_matcher_from_js_value(
12291290
})
12301291
}
12311292

1293+
/// A wrapper around [`parse_segment_config_from_source`] that merges route segment configuration
1294+
/// information from all relevant files (page, layout, parallel routes, etc).
12321295
#[turbo_tasks::function]
12331296
pub async fn parse_segment_config_from_loader_tree(
12341297
loader_tree: Vc<AppPageLoaderTree>,

0 commit comments

Comments
 (0)