fix(webpack): handle package.json exports field for non-buildable libs (#31444)
Current Behavior
The webpack and rspack plugins for handling non-buildable libraries
don't properly process the exports field in package.json. They
incorrectly assume libraries have only a single entry point, typically
through a barrel file (index.ts).
When a library defines multiple export paths using the exports field
(e.g., "./*": "./src/*.ts"), the plugins fail to generate the correct
allowlist patterns for webpack externals. This causes build failures
when trying to use non-buildable libraries that expose multiple entry
points without barrel files.
Expected Behavior
The webpack and rspack plugins should properly parse the exports field
from package.json and generate appropriate allowlist patterns for all
exported subpaths. This includes:
Handling wildcard patterns ("./*": "./src/*.ts")
Processing conditional exports (import/require/development)
Supporting exact subpath exports ("./utils": "./src/utils.ts")
Escaping special characters in package names for regex patterns
Gracefully falling back to reading package.json directly when metadata
is unavailable
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
659149d87c
commit
f9c427a80b
154
packages/rspack/src/plugins/utils/get-non-buildable-libs.spec.ts
Normal file
154
packages/rspack/src/plugins/utils/get-non-buildable-libs.spec.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import { logger } from '@nx/devkit';
|
||||
import { createAllowlistFromExports } from './get-non-buildable-libs';
|
||||
|
||||
describe('createAllowlistFromExports', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(logger, 'warn').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should handle undefined exports', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', undefined);
|
||||
expect(result).toEqual(['@test/lib']);
|
||||
});
|
||||
|
||||
it('should handle string exports', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', './index.js');
|
||||
expect(result).toEqual(['@test/lib']);
|
||||
});
|
||||
|
||||
it('should handle wildcard exports', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./*': './src/*.ts',
|
||||
});
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]).toBe('@test/lib');
|
||||
expect(result[1]).toBeInstanceOf(RegExp);
|
||||
|
||||
const regex = result[1] as RegExp;
|
||||
expect(regex.test('@test/lib/utils')).toBe(true);
|
||||
expect(regex.test('@test/lib/nested/path')).toBe(true);
|
||||
expect(regex.test('@other/lib/utils')).toBe(false);
|
||||
expect(regex.test('@test/lib')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle exact subpath exports', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils': './src/utils.ts',
|
||||
'./types': './src/types.ts',
|
||||
});
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/utils', '@test/lib/types']);
|
||||
});
|
||||
|
||||
it('should handle conditional exports', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils': {
|
||||
import: './src/utils.mjs',
|
||||
require: './src/utils.cjs',
|
||||
default: './src/utils.js',
|
||||
},
|
||||
});
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/utils']);
|
||||
});
|
||||
|
||||
it('should handle conditional exports with development priority', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils': {
|
||||
development: './src/utils.ts',
|
||||
import: './src/utils.mjs',
|
||||
require: './src/utils.cjs',
|
||||
default: './src/utils.js',
|
||||
},
|
||||
});
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/utils']);
|
||||
});
|
||||
|
||||
it('should handle mixed patterns', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils': './src/utils.ts',
|
||||
'./*': './src/*.ts',
|
||||
});
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0]).toBe('@test/lib');
|
||||
expect(result[1]).toBe('@test/lib/utils');
|
||||
expect(result[2]).toBeInstanceOf(RegExp);
|
||||
|
||||
const regex = result[2] as RegExp;
|
||||
expect(regex.test('@test/lib/helpers')).toBe(true);
|
||||
expect(regex.test('@test/lib/utils')).toBe(true); // Also matches regex
|
||||
});
|
||||
|
||||
it('should escape special characters in package names', () => {
|
||||
const result = createAllowlistFromExports('@test/lib.name', {
|
||||
'./*': './src/*.ts',
|
||||
});
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[1]).toBeInstanceOf(RegExp);
|
||||
|
||||
const regex = result[1] as RegExp;
|
||||
expect(regex.test('@test/lib.name/utils')).toBe(true);
|
||||
expect(regex.test('@test/lib-name/utils')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle scoped package names with special characters', () => {
|
||||
const result = createAllowlistFromExports('@my-org/my-lib.pkg', {
|
||||
'./*': './src/*.ts',
|
||||
});
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[1]).toBeInstanceOf(RegExp);
|
||||
|
||||
const regex = result[1] as RegExp;
|
||||
expect(regex.test('@my-org/my-lib.pkg/utils')).toBe(true);
|
||||
expect(regex.test('@my-org/my-lib-pkg/utils')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle complex wildcard patterns', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils/*': './src/utils/*.ts',
|
||||
'./types/*': './src/types/*.ts',
|
||||
});
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0]).toBe('@test/lib');
|
||||
expect(result[1]).toBeInstanceOf(RegExp);
|
||||
expect(result[2]).toBeInstanceOf(RegExp);
|
||||
|
||||
const utilsRegex = result[1] as RegExp;
|
||||
const typesRegex = result[2] as RegExp;
|
||||
|
||||
expect(utilsRegex.test('@test/lib/utils/helpers')).toBe(true);
|
||||
expect(utilsRegex.test('@test/lib/types/common')).toBe(false);
|
||||
expect(typesRegex.test('@test/lib/types/common')).toBe(true);
|
||||
expect(typesRegex.test('@test/lib/utils/helpers')).toBe(false);
|
||||
});
|
||||
|
||||
it('should ignore main export (.)', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'.': './src/index.ts',
|
||||
'./utils': './src/utils.ts',
|
||||
});
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/utils']);
|
||||
});
|
||||
|
||||
it('should handle invalid conditional exports gracefully', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils': {
|
||||
import: null,
|
||||
require: undefined,
|
||||
types: './src/types.d.ts', // Should be ignored
|
||||
},
|
||||
'./valid': './src/valid.ts',
|
||||
});
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/valid']);
|
||||
});
|
||||
|
||||
it('should handle non-string export paths', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
123: './src/invalid.ts',
|
||||
'./valid': './src/valid.ts',
|
||||
} as any);
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/valid']);
|
||||
});
|
||||
});
|
||||
@ -1,7 +1,76 @@
|
||||
import { type ProjectGraph } from '@nx/devkit';
|
||||
import { type ProjectGraph, readJsonFile } from '@nx/devkit';
|
||||
import { join } from 'path';
|
||||
import { getAllTransitiveDeps } from './get-transitive-deps';
|
||||
import { isBuildableLibrary } from './is-lib-buildable';
|
||||
|
||||
function escapePackageName(packageName: string): string {
|
||||
return packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function escapeRegexAndConvertWildcard(pattern: string): string {
|
||||
return pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\*/g, '.*');
|
||||
}
|
||||
|
||||
function resolveConditionalExport(target: any): string | null {
|
||||
if (typeof target === 'string') {
|
||||
return target;
|
||||
}
|
||||
|
||||
if (typeof target === 'object' && target !== null) {
|
||||
// Priority order for conditions
|
||||
const conditions = ['development', 'import', 'require', 'default'];
|
||||
for (const condition of conditions) {
|
||||
if (target[condition] && typeof target[condition] === 'string') {
|
||||
return target[condition];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function createAllowlistFromExports(
|
||||
packageName: string,
|
||||
exports: Record<string, any> | string | undefined
|
||||
): (string | RegExp)[] {
|
||||
if (!exports) {
|
||||
return [packageName];
|
||||
}
|
||||
|
||||
const allowlist: (string | RegExp)[] = [];
|
||||
allowlist.push(packageName);
|
||||
|
||||
if (typeof exports === 'string') {
|
||||
return allowlist;
|
||||
}
|
||||
|
||||
if (typeof exports === 'object') {
|
||||
for (const [exportPath, target] of Object.entries(exports)) {
|
||||
if (typeof exportPath !== 'string') continue;
|
||||
|
||||
const resolvedTarget = resolveConditionalExport(target);
|
||||
if (!resolvedTarget) continue;
|
||||
|
||||
if (exportPath === '.') {
|
||||
continue;
|
||||
} else if (exportPath.startsWith('./')) {
|
||||
const subpath = exportPath.slice(2);
|
||||
|
||||
if (subpath.includes('*')) {
|
||||
const regexPattern = escapeRegexAndConvertWildcard(subpath);
|
||||
allowlist.push(
|
||||
new RegExp(`^${escapePackageName(packageName)}/${regexPattern}$`)
|
||||
);
|
||||
} else {
|
||||
allowlist.push(`${packageName}/${subpath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allowlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all non-buildable libraries in the project graph for a given project.
|
||||
* This function retrieves all direct and transitive dependencies of a project,
|
||||
@ -14,10 +83,10 @@ import { isBuildableLibrary } from './is-lib-buildable';
|
||||
export function getNonBuildableLibs(
|
||||
graph: ProjectGraph,
|
||||
projectName: string
|
||||
): string[] {
|
||||
): (string | RegExp)[] {
|
||||
const deps = graph?.dependencies?.[projectName] ?? [];
|
||||
|
||||
const allNonBuildable = new Set<string>();
|
||||
const allNonBuildable = new Set<string | RegExp>();
|
||||
|
||||
// First, find all direct non-buildable deps and add them App -> library
|
||||
const directNonBuildable = deps.filter((dep) => {
|
||||
@ -28,12 +97,38 @@ export function getNonBuildableLibs(
|
||||
return !isBuildableLibrary(node);
|
||||
});
|
||||
|
||||
// Add direct non-buildable dependencies
|
||||
// Add direct non-buildable dependencies with expanded export patterns
|
||||
for (const dep of directNonBuildable) {
|
||||
const packageName =
|
||||
graph.nodes?.[dep.target]?.data?.metadata?.js?.packageName;
|
||||
const node = graph.nodes?.[dep.target];
|
||||
const packageName = node?.data?.metadata?.js?.packageName;
|
||||
|
||||
if (packageName) {
|
||||
allNonBuildable.add(packageName);
|
||||
// Get exports from project metadata first (most reliable)
|
||||
const packageExports = node?.data?.metadata?.js?.packageExports;
|
||||
|
||||
if (packageExports) {
|
||||
// Use metadata exports if available
|
||||
const allowlistPatterns = createAllowlistFromExports(
|
||||
packageName,
|
||||
packageExports
|
||||
);
|
||||
allowlistPatterns.forEach((pattern) => allNonBuildable.add(pattern));
|
||||
} else {
|
||||
// Fallback: try to read package.json directly
|
||||
try {
|
||||
const projectRoot = node.data.root;
|
||||
const packageJsonPath = join(projectRoot, 'package.json');
|
||||
const packageJson = readJsonFile(packageJsonPath);
|
||||
const allowlistPatterns = createAllowlistFromExports(
|
||||
packageName,
|
||||
packageJson.exports
|
||||
);
|
||||
allowlistPatterns.forEach((pattern) => allNonBuildable.add(pattern));
|
||||
} catch (error) {
|
||||
// Final fallback: just add base package name
|
||||
allNonBuildable.add(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get all transitive non-buildable dependencies App -> library1 -> library2
|
||||
|
||||
154
packages/webpack/src/plugins/nx-webpack-plugin/lib/utils.spec.ts
Normal file
154
packages/webpack/src/plugins/nx-webpack-plugin/lib/utils.spec.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import { logger } from '@nx/devkit';
|
||||
import { createAllowlistFromExports } from './utils';
|
||||
|
||||
describe('createAllowlistFromExports', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(logger, 'warn').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should handle undefined exports', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', undefined);
|
||||
expect(result).toEqual(['@test/lib']);
|
||||
});
|
||||
|
||||
it('should handle string exports', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', './index.js');
|
||||
expect(result).toEqual(['@test/lib']);
|
||||
});
|
||||
|
||||
it('should handle wildcard exports', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./*': './src/*.ts',
|
||||
});
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]).toBe('@test/lib');
|
||||
expect(result[1]).toBeInstanceOf(RegExp);
|
||||
|
||||
const regex = result[1] as RegExp;
|
||||
expect(regex.test('@test/lib/utils')).toBe(true);
|
||||
expect(regex.test('@test/lib/nested/path')).toBe(true);
|
||||
expect(regex.test('@other/lib/utils')).toBe(false);
|
||||
expect(regex.test('@test/lib')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle exact subpath exports', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils': './src/utils.ts',
|
||||
'./types': './src/types.ts',
|
||||
});
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/utils', '@test/lib/types']);
|
||||
});
|
||||
|
||||
it('should handle conditional exports', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils': {
|
||||
import: './src/utils.mjs',
|
||||
require: './src/utils.cjs',
|
||||
default: './src/utils.js',
|
||||
},
|
||||
});
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/utils']);
|
||||
});
|
||||
|
||||
it('should handle conditional exports with development priority', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils': {
|
||||
development: './src/utils.ts',
|
||||
import: './src/utils.mjs',
|
||||
require: './src/utils.cjs',
|
||||
default: './src/utils.js',
|
||||
},
|
||||
});
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/utils']);
|
||||
});
|
||||
|
||||
it('should handle mixed patterns', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils': './src/utils.ts',
|
||||
'./*': './src/*.ts',
|
||||
});
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0]).toBe('@test/lib');
|
||||
expect(result[1]).toBe('@test/lib/utils');
|
||||
expect(result[2]).toBeInstanceOf(RegExp);
|
||||
|
||||
const regex = result[2] as RegExp;
|
||||
expect(regex.test('@test/lib/helpers')).toBe(true);
|
||||
expect(regex.test('@test/lib/utils')).toBe(true); // Also matches regex
|
||||
});
|
||||
|
||||
it('should escape special characters in package names', () => {
|
||||
const result = createAllowlistFromExports('@test/lib.name', {
|
||||
'./*': './src/*.ts',
|
||||
});
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[1]).toBeInstanceOf(RegExp);
|
||||
|
||||
const regex = result[1] as RegExp;
|
||||
expect(regex.test('@test/lib.name/utils')).toBe(true);
|
||||
expect(regex.test('@test/lib-name/utils')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle scoped package names with special characters', () => {
|
||||
const result = createAllowlistFromExports('@my-org/my-lib.pkg', {
|
||||
'./*': './src/*.ts',
|
||||
});
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[1]).toBeInstanceOf(RegExp);
|
||||
|
||||
const regex = result[1] as RegExp;
|
||||
expect(regex.test('@my-org/my-lib.pkg/utils')).toBe(true);
|
||||
expect(regex.test('@my-org/my-lib-pkg/utils')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle complex wildcard patterns', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils/*': './src/utils/*.ts',
|
||||
'./types/*': './src/types/*.ts',
|
||||
});
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0]).toBe('@test/lib');
|
||||
expect(result[1]).toBeInstanceOf(RegExp);
|
||||
expect(result[2]).toBeInstanceOf(RegExp);
|
||||
|
||||
const utilsRegex = result[1] as RegExp;
|
||||
const typesRegex = result[2] as RegExp;
|
||||
|
||||
expect(utilsRegex.test('@test/lib/utils/helpers')).toBe(true);
|
||||
expect(utilsRegex.test('@test/lib/types/common')).toBe(false);
|
||||
expect(typesRegex.test('@test/lib/types/common')).toBe(true);
|
||||
expect(typesRegex.test('@test/lib/utils/helpers')).toBe(false);
|
||||
});
|
||||
|
||||
it('should ignore main export (.)', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'.': './src/index.ts',
|
||||
'./utils': './src/utils.ts',
|
||||
});
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/utils']);
|
||||
});
|
||||
|
||||
it('should handle invalid conditional exports gracefully', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
'./utils': {
|
||||
import: null,
|
||||
require: undefined,
|
||||
types: './src/types.d.ts', // Should be ignored
|
||||
},
|
||||
'./valid': './src/valid.ts',
|
||||
});
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/valid']);
|
||||
});
|
||||
|
||||
it('should handle non-string export paths', () => {
|
||||
const result = createAllowlistFromExports('@test/lib', {
|
||||
123: './src/invalid.ts',
|
||||
'./valid': './src/valid.ts',
|
||||
} as any);
|
||||
expect(result).toEqual(['@test/lib', '@test/lib/valid']);
|
||||
});
|
||||
});
|
||||
@ -1,4 +1,74 @@
|
||||
import type { ProjectGraph, ProjectGraphProjectNode } from '@nx/devkit';
|
||||
import { readJsonFile } from '@nx/devkit';
|
||||
import { join } from 'path';
|
||||
|
||||
function escapePackageName(packageName: string): string {
|
||||
return packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function escapeRegexAndConvertWildcard(pattern: string): string {
|
||||
return pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\*/g, '.*');
|
||||
}
|
||||
|
||||
function resolveConditionalExport(target: any): string | null {
|
||||
if (typeof target === 'string') {
|
||||
return target;
|
||||
}
|
||||
|
||||
if (typeof target === 'object' && target !== null) {
|
||||
// Priority order for conditions
|
||||
const conditions = ['development', 'import', 'require', 'default'];
|
||||
for (const condition of conditions) {
|
||||
if (target[condition] && typeof target[condition] === 'string') {
|
||||
return target[condition];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function createAllowlistFromExports(
|
||||
packageName: string,
|
||||
exports: Record<string, any> | string | undefined
|
||||
): (string | RegExp)[] {
|
||||
if (!exports) {
|
||||
return [packageName];
|
||||
}
|
||||
|
||||
const allowlist: (string | RegExp)[] = [];
|
||||
allowlist.push(packageName);
|
||||
|
||||
if (typeof exports === 'string') {
|
||||
return allowlist;
|
||||
}
|
||||
|
||||
if (typeof exports === 'object') {
|
||||
for (const [exportPath, target] of Object.entries(exports)) {
|
||||
if (typeof exportPath !== 'string') continue;
|
||||
|
||||
const resolvedTarget = resolveConditionalExport(target);
|
||||
if (!resolvedTarget) continue;
|
||||
|
||||
if (exportPath === '.') {
|
||||
continue;
|
||||
} else if (exportPath.startsWith('./')) {
|
||||
const subpath = exportPath.slice(2);
|
||||
|
||||
if (subpath.includes('*')) {
|
||||
const regexPattern = escapeRegexAndConvertWildcard(subpath);
|
||||
allowlist.push(
|
||||
new RegExp(`^${escapePackageName(packageName)}/${regexPattern}$`)
|
||||
);
|
||||
} else {
|
||||
allowlist.push(`${packageName}/${subpath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allowlist;
|
||||
}
|
||||
|
||||
function isSourceFile(path: string): boolean {
|
||||
return ['.ts', '.tsx', '.mts', '.cts'].some((ext) => path.endsWith(ext));
|
||||
@ -122,10 +192,10 @@ export function getAllTransitiveDeps(
|
||||
export function getNonBuildableLibs(
|
||||
graph: ProjectGraph,
|
||||
projectName: string
|
||||
): string[] {
|
||||
): (string | RegExp)[] {
|
||||
const deps = graph?.dependencies?.[projectName] ?? [];
|
||||
|
||||
const allNonBuildable = new Set<string>();
|
||||
const allNonBuildable = new Set<string | RegExp>();
|
||||
|
||||
// First, find all direct non-buildable deps and add them App -> library
|
||||
const directNonBuildable = deps.filter((dep) => {
|
||||
@ -136,12 +206,38 @@ export function getNonBuildableLibs(
|
||||
return !isBuildableLibrary(node);
|
||||
});
|
||||
|
||||
// Add direct non-buildable dependencies
|
||||
// Add direct non-buildable dependencies with expanded export patterns
|
||||
for (const dep of directNonBuildable) {
|
||||
const packageName =
|
||||
graph.nodes?.[dep.target]?.data?.metadata?.js?.packageName;
|
||||
const node = graph.nodes?.[dep.target];
|
||||
const packageName = node?.data?.metadata?.js?.packageName;
|
||||
|
||||
if (packageName) {
|
||||
allNonBuildable.add(packageName);
|
||||
// Get exports from project metadata first (most reliable)
|
||||
const packageExports = node?.data?.metadata?.js?.packageExports;
|
||||
|
||||
if (packageExports) {
|
||||
// Use metadata exports if available
|
||||
const allowlistPatterns = createAllowlistFromExports(
|
||||
packageName,
|
||||
packageExports
|
||||
);
|
||||
allowlistPatterns.forEach((pattern) => allNonBuildable.add(pattern));
|
||||
} else {
|
||||
// Fallback: try to read package.json directly
|
||||
try {
|
||||
const projectRoot = node.data.root;
|
||||
const packageJsonPath = join(projectRoot, 'package.json');
|
||||
const packageJson = readJsonFile(packageJsonPath);
|
||||
const allowlistPatterns = createAllowlistFromExports(
|
||||
packageName,
|
||||
packageJson.exports
|
||||
);
|
||||
allowlistPatterns.forEach((pattern) => allNonBuildable.add(pattern));
|
||||
} catch (error) {
|
||||
// Final fallback: just add base package name
|
||||
allNonBuildable.add(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get all transitive non-buildable dependencies App -> library1 -> library2
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user