Skip to content

Commit

Permalink
Merge pull request #33 from unstubbable/source-maps
Browse files Browse the repository at this point in the history
Handle source maps in webpack loaders
  • Loading branch information
unstubbable authored Nov 17, 2023
2 parents cfe455b + 67e3777 commit 93adb2f
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 133 deletions.
23 changes: 10 additions & 13 deletions apps/cloudflare-app/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ export default function createConfigs(_env, argv) {
const rscSsrLoader = createWebpackRscSsrLoader();
const rscClientLoader = createWebpackRscClientLoader({serverReferencesMap});

/**
* @type {import('webpack').RuleSetUseItem}
*/
const serverSwcLoader = {
loader: `swc-loader`,
options: {env: {targets: {node: 18}}},
};

/**
* @type {import('webpack').Configuration}
*/
Expand Down Expand Up @@ -122,26 +130,16 @@ export default function createConfigs(_env, argv) {
{
issuerLayer: webpackRscLayerName,
test: /\.tsx?$/,
use: [rscServerLoader, `swc-loader`],
use: [rscServerLoader, serverSwcLoader],
exclude: [/node_modules/],
},
{
test: /\.tsx?$/,
use: [rscSsrLoader, `swc-loader`],
use: [rscSsrLoader, serverSwcLoader],
exclude: [/node_modules/],
},
],
},
{
oneOf: [
{
test: /\.js$/,
issuerLayer: webpackRscLayerName,
use: rscServerLoader,
},
{test: /\.js$/, use: rscSsrLoader},
],
},
cssRule,
],
},
Expand Down Expand Up @@ -177,7 +175,6 @@ export default function createConfigs(_env, argv) {
module: {
rules: [
{test: /\.js$/, loader: `source-map-loader`, enforce: `pre`},
{test: /\.js$/, use: rscClientLoader},
{
test: /\.tsx?$/,
use: [rscClientLoader, `swc-loader`],
Expand Down
23 changes: 10 additions & 13 deletions apps/vercel-app/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ export default function createConfigs(_env, argv) {
const rscSsrLoader = createWebpackRscSsrLoader();
const rscClientLoader = createWebpackRscClientLoader({serverReferencesMap});

/**
* @type {import('webpack').RuleSetUseItem}
*/
const serverSwcLoader = {
loader: `swc-loader`,
options: {env: {targets: {node: 18}}},
};

/**
* @type {import('webpack').Configuration}
*/
Expand Down Expand Up @@ -149,27 +157,17 @@ export default function createConfigs(_env, argv) {
{
issuerLayer: webpackRscLayerName,
test: /\.tsx?$/,
use: [rscServerLoader, `swc-loader`],
use: [rscServerLoader, serverSwcLoader],
exclude: [/node_modules/],
},
{
test: /\.tsx?$/,
use: [rscSsrLoader, `swc-loader`],
use: [rscSsrLoader, serverSwcLoader],
// use: `swc-loader`,
exclude: [/node_modules/],
},
],
},
{
oneOf: [
{
test: /\.js$/,
issuerLayer: webpackRscLayerName,
use: rscServerLoader,
},
{test: /\.js$/, use: rscSsrLoader},
],
},
cssRule,
],
},
Expand Down Expand Up @@ -218,7 +216,6 @@ export default function createConfigs(_env, argv) {
module: {
rules: [
{test: /\.js$/, loader: `source-map-loader`, enforce: `pre`},
{test: /\.js$/, use: rscClientLoader},
{
test: /\.tsx?$/,
use: [rscClientLoader, `swc-loader`],
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/webpack-rsc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mfng/webpack-rsc",
"version": "2.4.0",
"version": "2.5.0",
"description": "A set of Webpack loaders and plugins for React Server Components",
"repository": {
"type": "git",
Expand Down
187 changes: 96 additions & 91 deletions packages/webpack-rsc/src/webpack-rsc-server-loader.cts
Original file line number Diff line number Diff line change
Expand Up @@ -21,109 +21,114 @@ namespace webpackRscServerLoader {

type RegisterReferenceType = 'Server' | 'Client';

function webpackRscServerLoader(
this: webpack.LoaderContext<webpackRscServerLoader.WebpackRscServerLoaderOptions>,
source: string,
): void {
this.cacheable(true);

const {clientReferencesMap} = this.getOptions();
const clientReferences: webpackRscServerLoader.ClientReference[] = [];
const resourcePath = this.resourcePath;

const ast = parser.parse(source, {
sourceType: `module`,
sourceFilename: resourcePath,
});

let moduleDirective: 'use client' | 'use server' | undefined;
let addedRegisterReferenceCall: RegisterReferenceType | undefined;
const unshiftedNodes = new Set<t.Node>();

traverse.default(ast, {
enter(nodePath) {
const {node} = nodePath;

if (t.isProgram(node)) {
if (node.directives.some(isDirective(`use client`))) {
moduleDirective = `use client`;
} else if (node.directives.some(isDirective(`use server`))) {
moduleDirective = `use server`;
} else {
const webpackRscServerLoader: webpack.LoaderDefinitionFunction<webpackRscServerLoader.WebpackRscServerLoaderOptions> =
function (source, sourceMap) {
this.cacheable(true);

const {clientReferencesMap} = this.getOptions();
const clientReferences: webpackRscServerLoader.ClientReference[] = [];
const resourcePath = this.resourcePath;

const ast = parser.parse(source, {
sourceType: `module`,
sourceFilename: resourcePath,
});

let moduleDirective: 'use client' | 'use server' | undefined;
let addedRegisterReferenceCall: RegisterReferenceType | undefined;
const unshiftedNodes = new Set<t.Node>();

traverse.default(ast, {
enter(nodePath) {
const {node} = nodePath;

if (t.isProgram(node)) {
if (node.directives.some(isDirective(`use client`))) {
moduleDirective = `use client`;
} else if (node.directives.some(isDirective(`use server`))) {
moduleDirective = `use server`;
} else {
nodePath.skip();
}

return;
}

if (
!moduleDirective ||
(t.isDirective(node) && isDirective(`use client`)(node)) ||
unshiftedNodes.has(node)
) {
nodePath.skip();

return;
}

return;
}
const exportName = getExportName(node);

if (moduleDirective === `use client`) {
if (exportName) {
const id = `${path.relative(process.cwd(), resourcePath)}`;
clientReferences.push({id, exportName});
addedRegisterReferenceCall = `Client`;
nodePath.replaceWith(createExportedClientReference(id, exportName));
nodePath.skip();
} else {
nodePath.remove();
}
} else if (exportName) {
addedRegisterReferenceCall = `Server`;
nodePath.insertAfter(createRegisterServerReference(exportName));
nodePath.skip();
}
},
exit(nodePath) {
if (!t.isProgram(nodePath.node) || !addedRegisterReferenceCall) {
nodePath.skip();

if (
!moduleDirective ||
(t.isDirective(node) && isDirective(`use client`)(node)) ||
unshiftedNodes.has(node)
) {
nodePath.skip();
return;
}

return;
}
const nodes: t.Node[] = [
createRegisterReferenceImport(addedRegisterReferenceCall),
];

const exportName = getExportName(node);
if (addedRegisterReferenceCall === `Client`) {
nodes.push(createClientReferenceProxyImplementation());
}

if (moduleDirective === `use client`) {
if (exportName) {
const id = `${path.relative(process.cwd(), resourcePath)}`;
clientReferences.push({id, exportName});
addedRegisterReferenceCall = `Client`;
nodePath.replaceWith(createExportedClientReference(id, exportName));
nodePath.skip();
} else {
nodePath.remove();
for (const node of nodes) {
unshiftedNodes.add(node);
}
} else if (exportName) {
addedRegisterReferenceCall = `Server`;
nodePath.insertAfter(createRegisterServerReference(exportName));
nodePath.skip();
}
},
exit(nodePath) {
if (!t.isProgram(nodePath.node) || !addedRegisterReferenceCall) {
nodePath.skip();

return;
}

const nodes: t.Node[] = [
createRegisterReferenceImport(addedRegisterReferenceCall),
];

if (addedRegisterReferenceCall === `Client`) {
nodes.push(createClientReferenceProxyImplementation());
}

for (const node of nodes) {
unshiftedNodes.add(node);
}

(nodePath as traverse.NodePath<t.Program>).unshiftContainer(
`body`,
nodes,
);
},
});

if (!moduleDirective) {
return this.callback(null, source);
}

if (clientReferences.length > 0) {
clientReferencesMap.set(resourcePath, clientReferences);
}
(nodePath as traverse.NodePath<t.Program>).unshiftContainer(
`body`,
nodes,
);
},
});

const {code} = generate.default(ast, {sourceFileName: this.resourcePath});
if (!moduleDirective) {
return this.callback(null, source, sourceMap);
}

// TODO: Handle source maps.
if (clientReferences.length > 0) {
clientReferencesMap.set(resourcePath, clientReferences);
}

this.callback(null, code);
}
const {code, map} = generate.default(
ast,
{
sourceFileName: this.resourcePath,
sourceMaps: this.sourceMap,
// @ts-expect-error
inputSourceMap: sourceMap,
},
source,
);

this.callback(null, code, map ?? sourceMap);
};

function isDirective(
value: 'use client' | 'use server',
Expand Down
35 changes: 26 additions & 9 deletions packages/webpack-rsc/src/webpack-rsc-ssr-loader.cts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import generate = require('@babel/generator');
import parser = require('@babel/parser');
import traverse = require('@babel/traverse');
import t = require('@babel/types');
import type {LoaderContext} from 'webpack';
import webpack = require('webpack');

export = function webpackRscSsrLoader(
this: LoaderContext<{}>,
source: string,
): void {
const webpackRscSsrLoader: webpack.LoaderDefinitionFunction = function (
source,
sourceMap,
) {
this.cacheable(true);

const resourcePath = this.resourcePath;
Expand All @@ -17,12 +17,16 @@ export = function webpackRscSsrLoader(
sourceFilename: resourcePath,
});

let hasUseServerDirective = false;

traverse.default(ast, {
enter(path) {
const {node} = path;

if (t.isProgram(node)) {
if (!node.directives.some(isUseServerDirective)) {
if (node.directives.some(isUseServerDirective)) {
hasUseServerDirective = true;
} else {
path.skip();
}

Expand All @@ -46,11 +50,22 @@ export = function webpackRscSsrLoader(
},
});

const {code} = generate.default(ast, {sourceFileName: this.resourcePath});
if (!hasUseServerDirective) {
return this.callback(null, source, sourceMap);
}

// TODO: Handle source maps.
const {code, map} = generate.default(
ast,
{
sourceFileName: this.resourcePath,
sourceMaps: this.sourceMap,
// @ts-expect-error
inputSourceMap: sourceMap,
},
source,
);

this.callback(null, code);
this.callback(null, code, map ?? sourceMap);
};

function isUseServerDirective(directive: t.Directive): boolean {
Expand Down Expand Up @@ -104,3 +119,5 @@ function createExportedServerReferenceStub(
),
);
}

export = webpackRscSsrLoader;
Loading

1 comment on commit 93adb2f

@vercel
Copy link

@vercel vercel bot commented on 93adb2f Nov 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

mfng – ./

mfng-git-main-unstubbable.vercel.app
mfng-unstubbable.vercel.app
mfng.vercel.app

Please sign in to comment.