295f9da393
## Feature Change server components convention from using `.server.js` / `.client.js` file extension to determine it's a server or client component to using `'client'` js literal as a directive for determine client components boundary. React RFC: https://github.com/reactjs/rfcs/pull/189 New behavior doesn't consume `.server.js` as server components any more, if you're enabling `serverComponents` flag, every `page.js` in app dir will become server components by default. If you adding a `'client'` directive to the page, then that page will become a client component. This rule also applies to the normal js components, client components will require a `'client'` directive to indicate its identity, instead of having a `.client.js` extension. - [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` Co-authored-by: Shu Ding <3676859+shuding@users.noreply.github.com>
102 lines
3.6 KiB
TypeScript
102 lines
3.6 KiB
TypeScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
// Modified from https://github.com/facebook/react/blob/main/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js
|
|
|
|
const MODULE_REFERENCE = Symbol.for('react.module.reference')
|
|
const PROMISE_PROTOTYPE = Promise.prototype
|
|
|
|
const proxyHandlers: ProxyHandler<object> = {
|
|
get: function (target: any, name: string, _receiver: any) {
|
|
switch (name) {
|
|
// These names are read by the Flight runtime if you end up using the exports object.
|
|
case '$$typeof':
|
|
// These names are a little too common. We should probably have a way to
|
|
// have the Flight runtime extract the inner target instead.
|
|
return target.$$typeof
|
|
case 'filepath':
|
|
return target.filepath
|
|
case 'name':
|
|
return target.name
|
|
case 'async':
|
|
return target.async
|
|
// We need to special case this because createElement reads it if we pass this
|
|
// reference.
|
|
case 'defaultProps':
|
|
return undefined
|
|
case '__esModule':
|
|
// Something is conditionally checking which export to use. We'll pretend to be
|
|
// an ESM compat module but then we'll check again on the client.
|
|
target.default = {
|
|
$$typeof: MODULE_REFERENCE,
|
|
filepath: target.filepath,
|
|
// This a placeholder value that tells the client to conditionally use the
|
|
// whole object or just the default export.
|
|
name: '',
|
|
async: target.async,
|
|
}
|
|
return true
|
|
case 'then':
|
|
if (!target.async) {
|
|
// If this module is expected to return a Promise (such as an AsyncModule) then
|
|
// we should resolve that with a client reference that unwraps the Promise on
|
|
// the client.
|
|
const then = function then(
|
|
resolve: (res: any) => void,
|
|
_reject: (err: any) => void
|
|
) {
|
|
const moduleReference: Record<string, any> = {
|
|
$$typeof: MODULE_REFERENCE,
|
|
filepath: target.filepath,
|
|
name: '*', // Represents the whole object instead of a particular import.
|
|
async: true,
|
|
}
|
|
return Promise.resolve(
|
|
resolve(new Proxy(moduleReference, proxyHandlers))
|
|
)
|
|
}
|
|
// If this is not used as a Promise but is treated as a reference to a `.then`
|
|
// export then we should treat it as a reference to that name.
|
|
then.$$typeof = MODULE_REFERENCE
|
|
then.filepath = target.filepath
|
|
// then.name is conveniently already "then" which is the export name we need.
|
|
// This will break if it's minified though.
|
|
return then
|
|
}
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
let cachedReference = target[name]
|
|
if (!cachedReference) {
|
|
cachedReference = target[name] = {
|
|
$$typeof: MODULE_REFERENCE,
|
|
filepath: target.filepath,
|
|
name: name,
|
|
async: target.async,
|
|
}
|
|
}
|
|
return cachedReference
|
|
},
|
|
getPrototypeOf(_target: object) {
|
|
// Pretend to be a Promise in case anyone asks.
|
|
return PROMISE_PROTOTYPE
|
|
},
|
|
set: function () {
|
|
throw new Error('Cannot assign to a client module from a server module.')
|
|
},
|
|
}
|
|
|
|
export function createProxy(moduleId: string) {
|
|
const moduleReference = {
|
|
$$typeof: MODULE_REFERENCE,
|
|
filepath: moduleId,
|
|
name: '*', // Represents the whole object instead of a particular import.
|
|
async: false,
|
|
}
|
|
return new Proxy(moduleReference, proxyHandlers)
|
|
}
|