Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experimental: unstable_after #65038

Merged
merged 9 commits into from
May 20, 2024
1 change: 1 addition & 0 deletions packages/next-swc/crates/next-core/src/next_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ pub struct ExperimentalConfig {
// ---
adjust_font_fallbacks: Option<bool>,
adjust_font_fallbacks_with_size_adjust: Option<bool>,
after: Option<bool>,
amp: Option<serde_json::Value>,
app_document_preloading: Option<bool>,
case_sensitive_routes: Option<bool>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub fn get_next_cjs_optimizer_rule(enable_mdx_rs: bool) -> ModuleRule {
"userAgent".into(),
"next/dist/server/web/spec-extension/user-agent".into(),
),
("unstable_after".into(), "next/dist/server/after".into()),
]),
},
)]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -472,8 +472,9 @@ struct ReactServerComponentValidator {
filepath: String,
app_dir: Option<PathBuf>,
invalid_server_imports: Vec<JsWord>,
invalid_client_imports: Vec<JsWord>,
invalid_server_lib_apis_mapping: HashMap<&'static str, Vec<&'static str>>,
invalid_client_imports: Vec<JsWord>,
invalid_client_lib_apis_mapping: HashMap<&'static str, Vec<&'static str>>,
pub directive_import_collection: Option<(bool, bool, Vec<ModuleImports>, Vec<String>)>,
}

Expand Down Expand Up @@ -540,7 +541,10 @@ impl ReactServerComponentValidator {
JsWord::from("react-dom/server"),
JsWord::from("next/router"),
],

invalid_client_imports: vec![JsWord::from("server-only"), JsWord::from("next/headers")],

invalid_client_lib_apis_mapping: [("next/server", vec!["unstable_after"])].into(),
}
}

Expand Down Expand Up @@ -627,14 +631,31 @@ impl ReactServerComponentValidator {
return;
}
for import in imports {
let source = import.source.0.clone();
if self.invalid_client_imports.contains(&source) {
let source = &import.source.0;

if self.invalid_client_imports.contains(source) {
report_error(
&self.app_dir,
&self.filepath,
RSCErrorKind::NextRscErrClientImport((source.to_string(), import.source.1)),
);
}

let invalid_apis = self.invalid_client_lib_apis_mapping.get(source.as_str());
if let Some(invalid_apis) = invalid_apis {
for specifier in &import.specifiers {
if invalid_apis.contains(&specifier.0.as_str()) {
report_error(
&self.app_dir,
&self.filepath,
RSCErrorKind::NextRscErrClientImport((
specifier.0.to_string(),
specifier.1,
)),
);
}
}
}
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/next/server.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export { userAgent } from 'next/dist/server/web/spec-extension/user-agent'
export { URLPattern } from 'next/dist/compiled/@edge-runtime/primitives/url'
export { ImageResponse } from 'next/dist/server/web/spec-extension/image-response'
export type { ImageResponseOptions } from 'next/dist/compiled/@vercel/og/types'
export { unstable_after } from 'next/dist/server/after'
2 changes: 2 additions & 0 deletions packages/next/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const serverExports = {
.userAgent,
URLPattern: require('next/dist/server/web/spec-extension/url-pattern')
.URLPattern,
unstable_after: require('next/dist/server/after').unstable_after,
}

// https://nodejs.org/api/esm.html#commonjs-namespaces
Expand All @@ -24,3 +25,4 @@ exports.ImageResponse = serverExports.ImageResponse
exports.userAgentFromString = serverExports.userAgentFromString
exports.userAgent = serverExports.userAgent
exports.URLPattern = serverExports.URLPattern
exports.unstable_after = serverExports.unstable_after
3 changes: 3 additions & 0 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,9 @@ export async function buildAppStaticPaths({
incrementalCache,
supportsDynamicHTML: true,
isRevalidate: false,
experimental: {
after: false,
Copy link
Member Author

@lubieowoce lubieowoce May 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in theory this might yield a confusing error message ("enable experimental.after in next.config.js") even if it's enabled, because there's currently no good way to encode "enabled, but not allowed in this context".

in practice, it shouldn't happen, because after() should trigger a static bailout before it gets a chance to print that other message, but i need to double-check that.

},
},
},
async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,19 @@ export function getRender({
event?: NextFetchEvent
) {
const extendedReq = new WebNextRequest(request)
const extendedRes = new WebNextResponse()
const extendedRes = new WebNextResponse(
undefined,
// tracking onClose adds overhead, so only do it if `experimental.after` is on.
!!process.env.__NEXT_AFTER
)

handler(extendedReq, extendedRes)
const result = await extendedRes.toResponse()

if (event?.waitUntil) {
// TODO(after):
// remove `internal_runWithWaitUntil` and the `internal-edge-wait-until` module
// when consumers switch to `unstable_after`.
const waitUntilPromise = internal_getCurrentFunctionWaitUntil()
if (waitUntilPromise) {
event.waitUntil(waitUntilPromise)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export function getDefineEnv({
: '',
'process.env.NEXT_MINIMAL': '',
'process.env.__NEXT_PPR': checkIsAppPPREnabled(config.experimental.ppr),
'process.env.__NEXT_AFTER': config.experimental.after ?? false,
'process.env.NEXT_DEPLOYMENT_ID': config.deploymentId || false,
'process.env.__NEXT_FETCH_CACHE_KEY_PREFIX': fetchCacheKeyPrefix ?? '',
'process.env.__NEXT_MIDDLEWARE_MATCHERS': middlewareMatchers ?? [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { ReadonlyRequestCookies } from '../../server/web/spec-extension/ada
// Share the instance module in the next-shared layer
import { requestAsyncStorage } from './request-async-storage-instance' with { 'turbopack-transition': 'next-shared' }
import type { DeepReadonly } from '../../shared/lib/deep-readonly'
import type { AfterContext } from '../../server/after/after-context'

export interface RequestStore {
readonly headers: ReadonlyHeaders
Expand All @@ -17,6 +18,7 @@ export interface RequestStore {
Record<string, { files: string[] }>
>
readonly assetPrefix: string
readonly afterContext: AfterContext | undefined
}

export type RequestAsyncStorage = AsyncLocalStorage<RequestStore>
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ export async function exportAppImpl(
isAppPPREnabled: checkIsAppPPREnabled(nextConfig.experimental.ppr),
clientTraceMetadata: nextConfig.experimental.clientTraceMetadata,
swrDelta: nextConfig.experimental.swrDelta,
after: nextConfig.experimental.after ?? false,
},
}

Expand Down
7 changes: 6 additions & 1 deletion packages/next/src/export/routes/app-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type {
import { isDynamicUsageError } from '../helpers/is-dynamic-usage-error'
import { SERVER_DIRECTORY } from '../../shared/lib/constants'
import { hasNextSupport } from '../../telemetry/ci-info'
import type { ExperimentalConfig } from '../../server/config-shared'

export const enum ExportedAppRouteFiles {
BODY = 'BODY',
Expand All @@ -37,7 +38,8 @@ export async function exportAppRoute(
incrementalCache: IncrementalCache | undefined,
distDir: string,
htmlFilepath: string,
fileWriter: FileWriter
fileWriter: FileWriter,
experimental: Required<Pick<ExperimentalConfig, 'after'>>
): Promise<ExportRouteResult> {
// Ensure that the URL is absolute.
req.url = `http://localhost:3000${req.url}`
Expand All @@ -64,10 +66,13 @@ export async function exportAppRoute(
notFoundRoutes: [],
},
renderOpts: {
experimental: experimental,
originalPathname: page,
nextExport: true,
supportsDynamicHTML: false,
incrementalCache,
waitUntil: undefined,
onClose: undefined,
},
}

Expand Down
6 changes: 5 additions & 1 deletion packages/next/src/export/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import type { FontConfig } from '../server/font-utils'
import type { ExportPathMap, NextConfigComplete } from '../server/config-shared'
import type { Span } from '../trace'
import type { Revalidate } from '../server/lib/revalidate'
import type { NextEnabledDirectories } from '../server/base-server'
import type {
NextEnabledDirectories,
RequestLifecycleOpts,
} from '../server/base-server'
import type {
SerializableTurborepoAccessTraceResult,
TurborepoAccessTraceResult,
Expand Down Expand Up @@ -97,6 +100,7 @@ export type WorkerRenderOptsPartial = PagesRenderOptsPartial &
AppRenderOptsPartial

export type WorkerRenderOpts = WorkerRenderOptsPartial &
RequestLifecycleOpts &
LoadComponentsReturnType

export type ExportWorker = (
Expand Down
5 changes: 4 additions & 1 deletion packages/next/src/export/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@ async function exportPageImpl(
incrementalCache,
distDir,
htmlFilepath,
fileWriter
fileWriter,
input.renderOpts.experimental
)
}

Expand All @@ -274,6 +275,8 @@ async function exportPageImpl(
...input.renderOpts.experimental,
isRoutePPREnabled,
},
waitUntil: undefined,
onClose: undefined,
}

if (hasNextSupport) {
Expand Down