const acorn = require('acorn-loose');
const url = require('url');
const Module = require('module');
module.exports = function register() {
const Server: any = require('react-server-dom-webpack/server');
const registerServerReference = Server.registerServerReference;
const createClientModuleProxy = Server.createClientModuleProxy;
const originalCompile = Module.prototype._compile;
Module.prototype._compile = function (
this: any,
content: string,
filename: string,
): void {
if (
content.indexOf('use client') === -1 &&
content.indexOf('use server') === -1
) {
return originalCompile.apply(this, arguments);
}
let body;
try {
body = acorn.parse(content, {
ecmaVersion: '2024',
sourceType: 'source',
}).body;
} catch (x) {
console['error']('Error parsing %s %s', url, x.message);
return originalCompile.apply(this, arguments);
}
let useClient = false;
let useServer = false;
for (let i = 0; i < body.length; i++) {
const node = body[i];
if (node.type !== 'ExpressionStatement' || !node.directive) {
break;
}
if (node.directive === 'use client') {
useClient = true;
}
if (node.directive === 'use server') {
useServer = true;
}
}
if (!useClient && !useServer) {
return originalCompile.apply(this, arguments);
}
if (useClient && useServer) {
throw new Error(
'Cannot have both "use client" and "use server" directives in the same file.',
);
}
if (useClient) {
const moduleId: string = (url.pathToFileURL(filename).href: any);
this.exports = createClientModuleProxy(moduleId);
}
if (useServer) {
originalCompile.apply(this, arguments);
const moduleId: string = (url.pathToFileURL(filename).href: any);
const exports = this.exports;
if (typeof exports === 'function') {
registerServerReference(
(exports: any),
moduleId,
null,
);
} else {
const keys = Object.keys(exports);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = exports[keys[i]];
if (typeof value === 'function') {
registerServerReference((value: any), moduleId, key);
}
}
}
}
};
};