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

Pages Route Module #50070

Merged
merged 25 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
52e46f8
feat: use definition instead of pathanme for route module
wyattjoh May 19, 2023
5485e6f
feat: add pages route module stub
wyattjoh May 19, 2023
dcbdb58
fix: support commonjs exports
wyattjoh May 19, 2023
64025d2
Merge branch 'canary' into wyattjoh/route-definition-embedding
wyattjoh May 19, 2023
11d68d6
fix: exclude loader from trace
wyattjoh May 19, 2023
671c20f
fix: updated snapshots
wyattjoh May 19, 2023
c9b68af
Merge branch 'canary' into wyattjoh/route-definition-embedding
wyattjoh May 19, 2023
e672488
fix: updated test snapshots
wyattjoh May 23, 2023
ed55595
fix: exclude instrumentation hook file
wyattjoh May 23, 2023
a51a1ad
Merge branch 'canary' into wyattjoh/route-definition-embedding
wyattjoh May 23, 2023
d59b9b6
fix: export unstable hook
wyattjoh May 23, 2023
d759dbe
fix: export `reportWebVitals` from module
wyattjoh May 23, 2023
5cd3d90
Merge branch 'canary' into wyattjoh/route-definition-embedding
wyattjoh May 23, 2023
934f81d
fix: update snapshots
wyattjoh May 23, 2023
9f03cf9
Merge branch 'canary' into wyattjoh/route-definition-embedding
wyattjoh May 23, 2023
1add3c7
fix: adjust named export detection
wyattjoh May 24, 2023
194961b
fix: updated snapshots
wyattjoh May 24, 2023
6790968
Merge branch 'canary' into wyattjoh/route-definition-embedding
wyattjoh May 24, 2023
f84a7b2
Merge branch 'canary' into wyattjoh/route-definition-embedding
wyattjoh May 24, 2023
f741043
fix: updated snapshots
wyattjoh May 24, 2023
a8f3414
fix: update snapshots
wyattjoh May 24, 2023
d83f9de
Merge branch 'canary' into wyattjoh/route-definition-embedding
wyattjoh May 24, 2023
905203d
feat: move app-route hooks into app-route module
wyattjoh May 24, 2023
6a8c56e
Merge branch 'canary' into wyattjoh/route-definition-embedding
wyattjoh May 24, 2023
2ac549b
Merge branch 'canary' into wyattjoh/route-definition-embedding
timneutkens May 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ import { EdgeRouteModuleWrapper } from 'next/dist/server/web/edge-route-module-w

import RouteModule from 'ROUTE_MODULE'
import * as userland from 'ENTRY'
import { PAGE, PATHNAME } from 'BOOTSTRAP_CONFIG'
import { PAGE, PATHNAME, KIND } from 'BOOTSTRAP_CONFIG'

// TODO: (wyattjoh) - perform the option construction in Rust to allow other modules to accept different options
const routeModule = new RouteModule({
userland,
pathname: PATHNAME,
definition: {
page: PAGE,
kind: KIND,
pathname: PATHNAME,
// The following aren't used in production.
filename: '',
bundlePath: '',
},
resolvedPagePath: `app/${PAGE}`,
nextConfigOutput: undefined,
})
Expand Down
11 changes: 9 additions & 2 deletions packages/next-swc/crates/next-core/js/src/entry/app/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@ import { attachRequestMeta } from '../../internal/next-request-helpers'

import RouteModule from 'ROUTE_MODULE'
import * as userland from 'ENTRY'
import { PAGE, PATHNAME } from 'BOOTSTRAP_CONFIG'
import { PAGE, PATHNAME, KIND } from 'BOOTSTRAP_CONFIG'

const routeModule = new RouteModule({
userland,
pathname: PATHNAME,
definition: {
page: PAGE,
kind: KIND,
pathname: PATHNAME,
// The following aren't used in production.
filename: '',
bundlePath: '',
},
resolvedPagePath: `app/${PAGE}`,
nextConfigOutput: undefined,
})
Expand Down
3 changes: 3 additions & 0 deletions packages/next-swc/crates/next-core/js/types/rust.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,12 @@ declare module 'ROUTE_MODULE' {
}

declare module 'BOOTSTRAP_CONFIG' {
import type { RouteKind } from 'next/dist/server/future/route-kind'

export const NAME: string
export const PAGE: string
export const PATHNAME: string
export const KIND: RouteKind
}

declare module 'CLIENT_MODULE' {
Expand Down
1 change: 1 addition & 0 deletions packages/next-swc/crates/next-core/src/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub async fn bootstrap(
let mut config = config.await?.clone_value();
config.insert("PAGE".to_string(), path.to_string());
config.insert("PATHNAME".to_string(), pathname);
config.insert("KIND".to_string(), "APP_ROUTE".to_string());

let config_asset = as_es_module_asset(
VirtualAssetVc::new(
Expand Down
12 changes: 8 additions & 4 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1193,16 +1193,20 @@ export default async function build(
? [
'hasCustomGetInitialProps',
'isPageStatic',
'getNamedExports',
'getDefinedNamedExports',
'exportPage',
]
: ['hasCustomGetInitialProps', 'isPageStatic', 'getNamedExports'],
: [
'hasCustomGetInitialProps',
'isPageStatic',
'getDefinedNamedExports',
],
}) as Worker &
Pick<
typeof import('./worker'),
| 'hasCustomGetInitialProps'
| 'isPageStatic'
| 'getNamedExports'
| 'getDefinedNamedExports'
| 'exportPage'
>
}
Expand Down Expand Up @@ -1275,7 +1279,7 @@ export default async function build(
true
)

const namedExportsPromise = pagesStaticWorkers.getNamedExports(
const namedExportsPromise = pagesStaticWorkers.getDefinedNamedExports(
appPageToCheck,
distDir,
runtimeEnvConfig
Expand Down
9 changes: 5 additions & 4 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1677,21 +1677,22 @@ export async function hasCustomGetInitialProps(
return mod.getInitialProps !== mod.origGetInitialProps
}

export async function getNamedExports(
export async function getDefinedNamedExports(
page: string,
distDir: string,
runtimeEnvConfig: any
): Promise<Array<string>> {
): Promise<ReadonlyArray<string>> {
require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig)
const components = await loadComponents({
distDir,
pathname: page,
hasServerComponents: false,
isAppPath: false,
})
let mod = components.ComponentMod

return Object.keys(mod)
return Object.keys(components.ComponentMod).filter((key) => {
return typeof components.ComponentMod[key] !== 'undefined'
})
}

export function detectConflictingPaths(
Expand Down
30 changes: 20 additions & 10 deletions packages/next/src/build/webpack/loaders/next-app-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { isAppRouteRoute } from '../../../lib/is-app-route-route'
import { isMetadataRoute } from '../../../lib/metadata/is-metadata-route'
import { NextConfig } from '../../../server/config-shared'
import { AppPathnameNormalizer } from '../../../server/future/normalizers/built/app/app-pathname-normalizer'
import { RouteKind } from '../../../server/future/route-kind'
import { AppRouteRouteModuleOptions } from '../../../server/future/route-modules/app-route/module'
import { AppBundlePathNormalizer } from '../../../server/future/normalizers/built/app/app-bundle-path-normalizer'

export type AppLoaderOptions = {
name: string
Expand Down Expand Up @@ -108,21 +111,24 @@ async function createAppRouteCode({
// References the route handler file to load found in `./routes/${kind}.ts`.
// TODO: allow switching to the different kinds of routes
const kind = 'app-route'
const normalizer = new AppPathnameNormalizer()
const pathname = normalizer.normalize(page)
const pathname = new AppPathnameNormalizer().normalize(page)
const bundlePath = new AppBundlePathNormalizer().normalize(page)

// This is providing the options defined by the route options type found at
// ./routes/${kind}.ts. This is stringified here so that the literal for
// `userland` can reference the variable for `userland` that's in scope for
// the loader code.
const options = `{
userland,
pathname: ${JSON.stringify(pathname)},
resolvedPagePath: ${JSON.stringify(resolvedPagePath)},
nextConfigOutput: ${
nextConfigOutput ? JSON.stringify(nextConfigOutput) : 'undefined'
const options: Omit<AppRouteRouteModuleOptions, 'userland'> = {
definition: {
kind: RouteKind.APP_ROUTE,
page,
pathname,
filename,
bundlePath,
},
}`
resolvedPagePath,
nextConfigOutput,
}

return `
import 'next/dist/server/node-polyfill-headers'
Expand All @@ -131,7 +137,11 @@ async function createAppRouteCode({

import * as userland from ${JSON.stringify(resolvedPagePath)}

const routeModule = new RouteModule(${options})
const options = ${JSON.stringify(options)}
const routeModule = new RouteModule({
...options,
userland,
})

// Pull out the exports that we need to expose from the module. This should
// be eliminated when we've moved the other routes to the new format. These
Expand Down
64 changes: 0 additions & 64 deletions packages/next/src/build/webpack/loaders/next-route-loader.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Hoists a name from a module or promised module.
*
* @param module the module to hoist the name from
* @param name the name to hoist
* @returns the value on the module (or promised module)
*/
export function hoist(module: any, name: string) {
// If the name is available in the module, return it.
if (name in module) {
return module[name]
}

// If a property called `then` exists, assume it's a promise and
// return a promise that resolves to the name.
if ('then' in module && typeof module.then === 'function') {
return module.then((mod: any) => hoist(mod, name))
}

// If we're trying to hoise the default export, and the module is a function,
// return the module itself.
if (typeof module === 'function' && name === 'default') {
return module
}

// Otherwise, return undefined.
return undefined
}
104 changes: 104 additions & 0 deletions packages/next/src/build/webpack/loaders/next-route-loader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import type { webpack } from 'next/dist/compiled/webpack/webpack'

import { stringify } from 'querystring'
import { getModuleBuildInfo } from '../get-module-build-info'
import { PagesRouteModuleOptions } from '../../../../server/future/route-modules/pages/module'
import { RouteKind } from '../../../../server/future/route-kind'
import { normalizePagePath } from '../../../../shared/lib/page-path/normalize-page-path'

/**
* The options for the route loader.
*/
type RouteLoaderOptions = {
/**
* The page name for this particular route.
*/
page: string

/**
* The preferred region for this route.
*/
preferredRegion: string | string[] | undefined

/**
* The absolute path to the userland page file.
*/
absolutePagePath: string
}

/**
* Returns the loader entry for a given page.
*
* @param query the options to create the loader entry
* @returns the encoded loader entry
*/
export function getRouteLoaderEntry(query: RouteLoaderOptions): string {
return `next-route-loader?${stringify(query)}!`
}

/**
* Handles the `next-route-loader` options.
* @returns the loader definition function
*/
const loader: webpack.LoaderDefinitionFunction<RouteLoaderOptions> =
function () {
const { page, preferredRegion, absolutePagePath } = this.getOptions()

// Ensure we only run this loader for as a module.
if (!this._module) {
throw new Error('Invariant: expected this to reference a module')
}

// Attach build info to the module.
const buildInfo = getModuleBuildInfo(this._module)
buildInfo.route = {
page,
absolutePagePath,
preferredRegion,
}

const options: Omit<PagesRouteModuleOptions, 'userland'> = {
definition: {
kind: RouteKind.PAGES,
page: normalizePagePath(page),
pathname: page,
// The following aren't used in production.
bundlePath: '',
filename: '',
},
}

return `
// Next.js Route Loader
import RouteModule from "next/dist/server/future/route-modules/pages/module"
import { hoist } from "next/dist/build/webpack/loaders/next-route-loader/helpers"

// Import the userland code.
import * as userland from ${JSON.stringify(absolutePagePath)}

// Re-export the component (should be the default export).
export default hoist(userland, "default")

// Re-export methods.
export const getStaticProps = hoist(userland, "getStaticProps")
export const getStaticPaths = hoist(userland, "getStaticPaths")
export const getServerSideProps = hoist(userland, "getServerSideProps")
export const config = hoist(userland, "config")
export const reportWebVitals = hoist(userland, "reportWebVitals")

// Re-export legacy methods.
export const unstable_getStaticProps = hoist(userland, "unstable_getStaticProps")
export const unstable_getStaticPaths = hoist(userland, "unstable_getStaticPaths")
export const unstable_getStaticParams = hoist(userland, "unstable_getStaticParams")
export const unstable_getServerProps = hoist(userland, "unstable_getServerProps")
export const unstable_getServerSideProps = hoist(userland, "unstable_getServerSideProps")

// Create and export the route module that will be consumed.
const options = ${JSON.stringify(options)}
const routeModule = new RouteModule({ ...options, userland })

export { routeModule }
`
}

export default loader
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export async function getNotFoundError(
!/next-(app|middleware|client-pages|route|flight-(client|server|client-entry))-loader\.js/.test(
name
) &&
!/next-route-loader\/index\.js/.test(name) &&
!/css-loader.+\.js/.test(name)
)
if (moduleTrace.length === 0) return ''
Expand Down