next
Version:
The React Framework
154 lines (153 loc) • 6.54 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = transformSource;
var _swc = require("../../swc");
var _utils = require("../../utils");
var _utils1 = require("./utils");
async function transformSource(source) {
const { client: isClientCompilation , pageExtensions } = this.getOptions();
const { resourcePath , resourceQuery } = this;
if (typeof source !== 'string') {
throw new Error('Expected source to have been transformed to a string.');
}
// We currently assume that all components are shared components (unsuffixed)
// from node_modules.
if (resourcePath.includes('/node_modules/')) {
return source;
}
const rawRawPageExtensions = (0, _utils).getRawPageExtensions(pageExtensions);
const isServerComponent = createServerComponentFilter(rawRawPageExtensions);
const isClientComponent = createClientComponentFilter(rawRawPageExtensions);
if (!isClientCompilation) {
// We only apply the loader to server components, or shared components that
// are imported by a server component.
if (!isServerComponent(resourcePath) && resourceQuery !== '?__sc_server__') {
return source;
}
}
const { source: transformedSource , imports , isEsm , } = await parseModuleInfo({
resourcePath,
source,
isClientCompilation,
isServerComponent,
isClientComponent
});
/**
* For .server.js files, we handle this loader differently.
*
* Server compilation output:
* (The content of the Server Component module will be kept.)
* export const __next_rsc__ = { __webpack_require__, _: () => { ... } }
*
* Client compilation output:
* (The content of the Server Component module will be removed.)
* export const __next_rsc__ = { __webpack_require__, _: () => { ... } }
*/ const rscExports = {
__next_rsc__: `{
__webpack_require__,
_: () => {\n${imports}\n}
}`,
__next_rsc_server__: isServerComponent(resourcePath) ? 'true' : 'false'
};
if (isClientCompilation) {
rscExports['default'] = 'function RSC() {}';
}
const output = transformedSource + '\n' + (0, _utils1).buildExports(rscExports, isEsm);
return output;
}
const imageExtensions = [
'jpg',
'jpeg',
'png',
'webp',
'avif'
];
const createClientComponentFilter = (pageExtensions)=>{
// Special cases for Next.js APIs that are considered as client components:
// - .client.[ext]
// - next/link, next/image
// - .[imageExt]
const regex = new RegExp('(' + `\\.client(\\.(${pageExtensions.join('|')}))?|` + `next/link|next/image|` + `\\.(${imageExtensions.join('|')})` + ')$');
return (importSource)=>regex.test(importSource)
;
};
const createServerComponentFilter = (pageExtensions)=>{
const regex = new RegExp(`\\.server(\\.(${pageExtensions.join('|')}))?$`);
return (importSource)=>regex.test(importSource)
;
};
async function parseModuleInfo({ resourcePath , source , isClientCompilation , isServerComponent , isClientComponent }) {
const ast = await (0, _swc).parse(source, {
filename: resourcePath,
isModule: 'unknown'
});
const { type , body } = ast;
let transformedSource = '';
let lastIndex = 0;
let imports = '';
const isEsm = type === 'Module';
for(let i = 0; i < body.length; i++){
const node = body[i];
switch(node.type){
case 'ImportDeclaration':
{
const importSource = node.source.value;
if (!isClientCompilation) {
// Server compilation for .server.js.
if (isServerComponent(importSource)) {
continue;
}
const importDeclarations = source.substring(lastIndex, node.source.span.start);
if (isClientComponent(importSource)) {
// A client component. It should be loaded as module reference.
transformedSource += importDeclarations;
transformedSource += JSON.stringify(`${importSource}?__sc_client__`);
imports += `require(${JSON.stringify(importSource)})\n`;
} else {
// FIXME
// case: 'react'
// Avoid module resolution error like Cannot find `./?__rsc_server__` in react/package.json
// cases: 'react/jsx-runtime', 'react/jsx-dev-runtime'
// This is a special case to avoid the Duplicate React error.
// Since we already include React in the SSR runtime,
// here we can't create a new module with the ?__rsc_server__ query.
if ([
'react',
'react/jsx-runtime',
'react/jsx-dev-runtime'
].includes(importSource)) {
continue;
}
// A shared component. It should be handled as a server
// component.
transformedSource += importDeclarations;
transformedSource += JSON.stringify(`${importSource}?__sc_server__`);
}
} else {
// For the client compilation, we skip all modules imports but
// always keep client components in the bundle. All client components
// have to be imported from either server or client components.
if (!(isClientComponent(importSource) || isServerComponent(importSource))) {
continue;
}
imports += `require(${JSON.stringify(importSource)})\n`;
}
lastIndex = node.source.span.end;
break;
}
default:
break;
}
}
if (!isClientCompilation) {
transformedSource += source.substring(lastIndex);
}
return {
source: transformedSource,
imports,
isEsm
};
}
//# sourceMappingURL=next-flight-server-loader.js.map
;