next
Version:
The React Framework
225 lines (224 loc) • 9.71 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = transformSource;
var _module = require("module");
var _swc = require("../../swc");
var _utils = require("./utils");
async function transformSource(source) {
const { client: isClientCompilation , extensions } = this.getOptions();
const { resourcePath , resolve: resolveFn , context } = this;
const resolver = (req)=>{
return new Promise((resolve, reject)=>{
resolveFn(context, req, (err, result)=>{
if (err) return reject(err);
resolve(result);
});
});
};
if (typeof source !== 'string') {
throw new Error('Expected source to have been transformed to a string.');
}
const isServerComponent = (0, _utils).createServerComponentFilter(extensions);
const isClientComponent = (0, _utils).createClientComponentFilter(extensions);
const hasAppliedFlightServerLoader = this.loaders.some((loader)=>{
return hasFlightLoader(loader.path, 'server');
});
const isServerExt = isServerComponent(resourcePath);
if (!isClientCompilation) {
// We only apply the loader to server components, or shared components that
// are imported by a server component.
if (!isServerExt && !hasAppliedFlightServerLoader) {
return source;
}
}
const { source: transformedSource , imports , isEsm , __N_SSP , pageRuntime , } = await parseModuleInfo({
resourcePath,
source,
extensions,
isClientCompilation,
isServerComponent,
isClientComponent,
resolver
});
/**
* 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__, _: () => { ... }, server: true }
*
* Client compilation output:
* (The content of the Server Component module will be removed.)
* export const __next_rsc__ = { __webpack_require__, _: () => { ... }, server: false }
*/ const rscExports = {
__next_rsc__: `{
__webpack_require__,
_: () => {
${imports.map((importSource)=>`import(/* webpackMode: "eager" */ ${JSON.stringify(importSource)});`
).join('\n')}
},
server: ${isServerExt ? 'true' : 'false'}
}`
};
if (isClientCompilation) {
rscExports.default = 'function RSC() {}';
if (pageRuntime === 'edge') {
// Currently for the Edge runtime, we treat all RSC pages as SSR pages.
rscExports.__N_SSP = 'true';
} else {
if (__N_SSP) {
rscExports.__N_SSP = 'true';
} else {
// Server component pages are always considered as SSG by default because
// the flight data is needed for client navigation.
rscExports.__N_SSG = 'true';
}
}
}
const output = transformedSource + '\n' + (0, _utils).buildExports(rscExports, isEsm);
return output;
}
function createFlightServerRequest(request, options) {
return `next-flight-server-loader?${JSON.stringify(options)}!${request}`;
}
function hasFlightLoader(request, type) {
return request.includes(`next-flight-${type}-loader`);
}
async function parseModuleInfo({ resourcePath , source , extensions , isClientCompilation , isServerComponent , isClientComponent , resolver }) {
const ast = await (0, _swc).parse(source, {
filename: resourcePath,
isModule: 'unknown'
});
const { type , body } = ast;
let transformedSource = '';
let lastIndex = 0;
let imports = [];
let __N_SSP = false;
let pageRuntime = null;
let isBuiltinModule;
let isNodeModuleImport;
const isEsm = type === 'Module';
async function getModuleType(path) {
const isBuiltinModule_ = _module.builtinModules.includes(path);
const resolvedPath = isBuiltinModule_ ? path : await resolver(path);
const isNodeModuleImport_ = resolvedPath.includes('/node_modules/');
return [
isBuiltinModule_,
isNodeModuleImport_
];
}
function addClientImport(path) {
if (isServerComponent(path) || hasFlightLoader(path, 'server')) {
// If it's a server component, we recursively import its dependencies.
imports.push(path);
} else if (isClientComponent(path)) {
// Client component.
imports.push(path);
} else {
// Shared component.
imports.push(createFlightServerRequest(path, {
extensions,
client: 1
}));
}
}
for(let i = 0; i < body.length; i++){
const node = body[i];
switch(node.type){
case 'ImportDeclaration':
const importSource = node.source.value;
[isBuiltinModule, isNodeModuleImport] = await getModuleType(importSource);
// matching node_module package but excluding react cores since react is required to be shared
const isReactImports = [
'react',
'react/jsx-runtime',
'react/jsx-dev-runtime',
].includes(importSource);
if (!isClientCompilation) {
// Server compilation for .server.js.
if (isServerComponent(importSource)) {
continue;
}
const importDeclarations = source.substring(lastIndex, node.source.span.start);
if (isClientComponent(importSource)) {
transformedSource += importDeclarations;
transformedSource += JSON.stringify(`next-flight-client-loader!${importSource}`);
imports.push(importSource);
} else {
// A shared component. It should be handled as a server component.
const serverImportSource = isReactImports || isBuiltinModule ? importSource : createFlightServerRequest(importSource, {
extensions
});
transformedSource += importDeclarations;
transformedSource += JSON.stringify(serverImportSource);
// TODO: support handling RSC components from node_modules
if (!isNodeModuleImport) {
imports.push(importSource);
}
}
} else {
// For now we assume there is no .client.js inside node_modules.
// TODO: properly handle this.
if (isNodeModuleImport || isBuiltinModule) continue;
addClientImport(importSource);
}
lastIndex = node.source.span.end;
break;
case 'ExportDeclaration':
if (isClientCompilation) {
var ref;
// Keep `__N_SSG` and `__N_SSP` exports.
if (((ref = node.declaration) === null || ref === void 0 ? void 0 : ref.type) === 'VariableDeclaration') {
for (const declaration of node.declaration.declarations){
if (declaration.type === 'VariableDeclarator') {
var ref1;
if (((ref1 = declaration.id) === null || ref1 === void 0 ? void 0 : ref1.type) === 'Identifier') {
const value = declaration.id.value;
if (value === '__N_SSP') {
__N_SSP = true;
} else if (value === 'config') {
var ref2;
const props = declaration.init.properties;
const runtimeKeyValue = props.find((prop)=>prop.key.value === 'runtime'
);
const runtime = runtimeKeyValue === null || runtimeKeyValue === void 0 ? void 0 : (ref2 = runtimeKeyValue.value) === null || ref2 === void 0 ? void 0 : ref2.value;
if (runtime === 'nodejs' || runtime === 'edge') {
pageRuntime = runtime;
}
}
}
}
}
}
}
break;
case 'ExportNamedDeclaration':
if (isClientCompilation) {
if (node.source) {
// export { ... } from '...'
const path = node.source.value;
[isBuiltinModule, isNodeModuleImport] = await getModuleType(path);
if (!isBuiltinModule && !isNodeModuleImport) {
addClientImport(path);
}
}
}
break;
default:
break;
}
}
if (!isClientCompilation) {
transformedSource += source.substring(lastIndex);
}
return {
source: transformedSource,
imports,
isEsm,
__N_SSP,
pageRuntime
};
}
//# sourceMappingURL=next-flight-server-loader.js.map
;