feat(rspack): add support for non-buildable libraries in external dependencies (#30606)

Parity with https://github.com/nrwl/nx/pull/30538
This commit is contained in:
Nicholas Cunningham 2025-04-04 07:41:22 -06:00 committed by GitHub
parent 4015d4cfe2
commit 32b48a3a04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 93 additions and 1 deletions

View File

@ -19,6 +19,7 @@ import { getTerserEcmaVersion } from './get-terser-ecma-version';
import nodeExternals = require('webpack-node-externals'); import nodeExternals = require('webpack-node-externals');
import { NormalizedNxAppRspackPluginOptions } from './models'; import { NormalizedNxAppRspackPluginOptions } from './models';
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup'; import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
import { isBuildableLibrary } from './is-lib-buildable';
const IGNORED_RSPACK_WARNINGS = [ const IGNORED_RSPACK_WARNINGS = [
/The comment file/i, /The comment file/i,
@ -350,7 +351,40 @@ function applyNxDependentConfig(
options.externalDependencies === 'all' options.externalDependencies === 'all'
) { ) {
const modulesDir = `${options.root}/node_modules`; const modulesDir = `${options.root}/node_modules`;
externals.push(nodeExternals({ modulesDir })); const graph = options.projectGraph;
const projectName = options.projectName;
const deps = graph?.dependencies?.[projectName] ?? [];
// Collect non-buildable TS project references so that they are bundled
// in the final output. This is needed for projects that are not buildable
// but are referenced by buildable projects. This is needed for the new TS
// solution setup.
const nonBuildableWorkspaceLibs = isUsingTsSolution
? deps
.filter((dep) => {
const node = graph.nodes?.[dep.target];
if (!node || node.type !== 'lib') return false;
const hasBuildTarget = 'build' in (node.data?.targets ?? {});
if (hasBuildTarget) {
return false;
}
// If there is no build target we check the package exports to see if they reference
// source files
return !isBuildableLibrary(node);
})
.map(
(dep) => graph.nodes?.[dep.target]?.data?.metadata?.js?.packageName
)
.filter((name): name is string => !!name)
: [];
externals.push(
nodeExternals({ modulesDir, allowlist: nonBuildableWorkspaceLibs })
);
} else if (Array.isArray(options.externalDependencies)) { } else if (Array.isArray(options.externalDependencies)) {
externals.push(function (ctx, callback: Function) { externals.push(function (ctx, callback: Function) {
if (options.externalDependencies.includes(ctx.request)) { if (options.externalDependencies.includes(ctx.request)) {

View File

@ -0,0 +1,58 @@
import { type ProjectGraphProjectNode } from '@nx/devkit';
function isSourceFile(path: string): boolean {
return ['.ts', '.tsx', '.mts', '.cts'].some((ext) => path.endsWith(ext));
}
function isBuildableExportMap(packageExports: any): boolean {
if (!packageExports || Object.keys(packageExports).length === 0) {
return false; // exports = {} → not buildable
}
const isCompiledExport = (value: unknown): boolean => {
if (typeof value === 'string') {
return !isSourceFile(value);
}
if (typeof value === 'object' && value !== null) {
return Object.entries(value).some(([key, subValue]) => {
if (
key === 'types' ||
key === 'development' ||
key === './package.json'
)
return false;
return typeof subValue === 'string' && !isSourceFile(subValue);
});
}
return false;
};
if (packageExports['.']) {
return isCompiledExport(packageExports['.']);
}
return Object.entries(packageExports).some(
([key, value]) => key !== '.' && isCompiledExport(value)
);
}
/**
* Check if the library is buildable.
* @param node from the project graph
* @returns boolean
*/
export function isBuildableLibrary(node: ProjectGraphProjectNode): boolean {
if (!node.data.metadata?.js) {
return false;
}
const { packageExports, packageMain } = node.data.metadata.js;
// if we have exports only check this else fallback to packageMain
if (packageExports) {
return isBuildableExportMap(packageExports);
}
return (
typeof packageMain === 'string' &&
packageMain !== '' &&
!isSourceFile(packageMain)
);
}