fix(angular): restore esm2022 bundle and drop fesm2022 in ng-packagr-lite executor (#29615)
## Current Behavior In Nx 20.2.0, the `ng-packagr-lite` executor stopped producing the ESM2022 outputs and started producing the FESM2022 outputs. This was due to the Angular Package Format (APF) dropping the ESM2022 outputs, which was reflected in the upstream `ng-packagr` implementation. Due to this change, the libraries' build time and memory increased compared to the previous versions. ## Expected Behavior The `ng-packagr-lite` executor should only produce ESM2022 outputs and avoid running an extra step to bundle the outputs to produce the FESM2022. Given the `ng-packagr-lite` executor is not meant to produce publishable artifacts, its output doesn't need to strictly comply with the APF and can focus more on build performance. ## Related Issue(s) Fixes #29519
This commit is contained in:
parent
0ae8665a88
commit
f24a869b67
@ -174,10 +174,13 @@ describe('Tailwind support', () => {
|
||||
|
||||
const assertLibComponentStyles = (
|
||||
lib: string,
|
||||
libSpacing: (typeof spacing)['root']
|
||||
libSpacing: (typeof spacing)['root'],
|
||||
isPublishable: boolean = true
|
||||
) => {
|
||||
const builtComponentContent = readFile(
|
||||
`dist/${lib}/fesm2022/${project}-${lib}.mjs`
|
||||
isPublishable
|
||||
? `dist/${lib}/fesm2022/${project}-${lib}.mjs`
|
||||
: `dist/${lib}/esm2022/lib/foo.component.mjs`
|
||||
);
|
||||
let expectedStylesRegex = new RegExp(
|
||||
`styles: \\[\\"\\.custom\\-btn(\\[_ngcontent\\-%COMP%\\])?{margin:${libSpacing.md};padding:${libSpacing.sm}}(\\\\n)?\\"\\]`
|
||||
@ -203,7 +206,8 @@ describe('Tailwind support', () => {
|
||||
|
||||
assertLibComponentStyles(
|
||||
buildLibWithTailwind.name,
|
||||
spacing.projectVariant1
|
||||
spacing.projectVariant1,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
@ -223,7 +227,11 @@ describe('Tailwind support', () => {
|
||||
|
||||
runCLI(`build ${buildLibSetupTailwind}`);
|
||||
|
||||
assertLibComponentStyles(buildLibSetupTailwind, spacing.projectVariant2);
|
||||
assertLibComponentStyles(
|
||||
buildLibSetupTailwind,
|
||||
spacing.projectVariant2,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('should correctly build a buildable library with a tailwind.config.js file in the project root or workspace root', () => {
|
||||
@ -241,7 +249,8 @@ describe('Tailwind support', () => {
|
||||
|
||||
assertLibComponentStyles(
|
||||
buildLibNoProjectConfig,
|
||||
spacing.projectVariant3
|
||||
spacing.projectVariant3,
|
||||
false
|
||||
);
|
||||
|
||||
// remove tailwind.config.js file from the project root to test the one in the workspace root
|
||||
@ -249,7 +258,7 @@ describe('Tailwind support', () => {
|
||||
|
||||
runCLI(`build ${buildLibNoProjectConfig}`);
|
||||
|
||||
assertLibComponentStyles(buildLibNoProjectConfig, spacing.root);
|
||||
assertLibComponentStyles(buildLibNoProjectConfig, spacing.root, false);
|
||||
});
|
||||
|
||||
it('should generate a publishable library with tailwind and build correctly', () => {
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
import {
|
||||
type DestinationFiles,
|
||||
NgEntryPoint as NgEntryPointBase,
|
||||
} from 'ng-packagr/lib/ng-package/entry-point/entry-point';
|
||||
import { dirname } from 'node:path';
|
||||
|
||||
export class NgEntryPoint extends NgEntryPointBase {
|
||||
/**
|
||||
* Point the FESM2022 files to the ESM2022 files.
|
||||
*/
|
||||
public override get destinationFiles(): DestinationFiles {
|
||||
const result = super.destinationFiles;
|
||||
result.fesm2022 = result.esm2022;
|
||||
result.fesm2022Dir = dirname(result.esm2022);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Adapted from the original ng-packagr.
|
||||
*
|
||||
* Changes made:
|
||||
* - Removed bundling altogether.
|
||||
* - Write the ESM2022 outputs to the file system.
|
||||
* - Fake the FESM2022 outputs pointing them to the ESM2022 outputs.
|
||||
*/
|
||||
|
||||
import { BuildGraph } from 'ng-packagr/lib/graph/build-graph';
|
||||
import { transformFromPromise } from 'ng-packagr/lib/graph/transform';
|
||||
import type { NgEntryPoint as NgEntryPointBase } from 'ng-packagr/lib/ng-package/entry-point/entry-point';
|
||||
import { isEntryPoint, isPackage } from 'ng-packagr/lib/ng-package/nodes';
|
||||
import type { NgPackagrOptions } from 'ng-packagr/lib/ng-package/options.di';
|
||||
import { NgPackage } from 'ng-packagr/lib/ng-package/package';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { dirname } from 'node:path';
|
||||
import { NgEntryPoint } from './entry-point';
|
||||
|
||||
export const writeBundlesTransform = (_options: NgPackagrOptions) =>
|
||||
transformFromPromise(async (graph) => {
|
||||
const updatedGraph = new BuildGraph();
|
||||
|
||||
for (const entry of graph.entries()) {
|
||||
if (isEntryPoint(entry)) {
|
||||
const entryPoint = toCustomNgEntryPoint(entry.data.entryPoint);
|
||||
entry.data.entryPoint = entryPoint;
|
||||
entry.data.destinationFiles = entryPoint.destinationFiles;
|
||||
|
||||
for (const [path, outputCache] of entry.cache.outputCache.entries()) {
|
||||
// write the outputs to the file system
|
||||
await mkdir(dirname(path), { recursive: true });
|
||||
await writeFile(path, outputCache.content);
|
||||
}
|
||||
} else if (isPackage(entry)) {
|
||||
entry.data = new NgPackage(
|
||||
entry.data.src,
|
||||
toCustomNgEntryPoint(entry.data.primary),
|
||||
entry.data.secondaries.map((secondary) =>
|
||||
toCustomNgEntryPoint(secondary)
|
||||
)
|
||||
);
|
||||
}
|
||||
updatedGraph.put(entry);
|
||||
}
|
||||
|
||||
return updatedGraph;
|
||||
});
|
||||
|
||||
function toCustomNgEntryPoint(entryPoint: NgEntryPointBase): NgEntryPoint {
|
||||
return new NgEntryPoint(
|
||||
entryPoint.packageJson,
|
||||
entryPoint.ngPackageJson,
|
||||
entryPoint.basePath,
|
||||
// @ts-expect-error this is a TS private property, but it can be accessed at runtime
|
||||
entryPoint.secondaryData
|
||||
);
|
||||
}
|
||||
@ -1,35 +1,15 @@
|
||||
import { NgPackagr, ngPackagr } from 'ng-packagr';
|
||||
import { getInstalledAngularVersionInfo } from '../../utilities/angular-version-utils';
|
||||
|
||||
export async function getNgPackagrInstance(): Promise<NgPackagr> {
|
||||
const { major: angularMajorVersion } = getInstalledAngularVersionInfo();
|
||||
if (angularMajorVersion >= 19) {
|
||||
const { STYLESHEET_PROCESSOR } = await import(
|
||||
'../../utilities/ng-packagr/stylesheet-processor.di.js'
|
||||
);
|
||||
|
||||
const packagr = ngPackagr();
|
||||
packagr.withProviders([STYLESHEET_PROCESSOR]);
|
||||
|
||||
return packagr;
|
||||
}
|
||||
|
||||
const { WRITE_BUNDLES_TRANSFORM } = await import(
|
||||
'./pre-v19/ng-package/entry-point/write-bundles.di.js'
|
||||
);
|
||||
const { WRITE_PACKAGE_TRANSFORM } = await import(
|
||||
'./pre-v19/ng-package/entry-point/write-package.di.js'
|
||||
'./ng-package/entry-point/write-bundles.di.js'
|
||||
);
|
||||
const { STYLESHEET_PROCESSOR } = await import(
|
||||
'../../utilities/ng-packagr/stylesheet-processor.di.js'
|
||||
);
|
||||
|
||||
const packagr = ngPackagr();
|
||||
packagr.withProviders([
|
||||
WRITE_BUNDLES_TRANSFORM,
|
||||
WRITE_PACKAGE_TRANSFORM,
|
||||
STYLESHEET_PROCESSOR,
|
||||
]);
|
||||
packagr.withProviders([WRITE_BUNDLES_TRANSFORM, STYLESHEET_PROCESSOR]);
|
||||
|
||||
return packagr;
|
||||
}
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Adapted from the original ng-packagr.
|
||||
*
|
||||
* Changes made:
|
||||
* - Removed bundling altogether.
|
||||
*/
|
||||
|
||||
import { transformFromPromise } from 'ng-packagr/lib/graph/transform';
|
||||
import { NgPackagrOptions } from 'ng-packagr/lib/ng-package/options.di';
|
||||
|
||||
export const writeBundlesTransform = (_options: NgPackagrOptions) =>
|
||||
transformFromPromise(async (graph) => graph);
|
||||
@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Adapted from the original ng-packagr source.
|
||||
*
|
||||
* Changes made:
|
||||
* - Provide our own writePackageTransform function.
|
||||
*/
|
||||
|
||||
import {
|
||||
provideTransform,
|
||||
TransformProvider,
|
||||
} from 'ng-packagr/lib/graph/transform.di';
|
||||
import { WRITE_PACKAGE_TRANSFORM_TOKEN } from 'ng-packagr/lib/ng-package/entry-point/write-package.di';
|
||||
import { OPTIONS_TOKEN } from 'ng-packagr/lib/ng-package/options.di';
|
||||
import { nxWritePackageTransform } from './write-package.transform';
|
||||
|
||||
export const WRITE_PACKAGE_TRANSFORM: TransformProvider = provideTransform({
|
||||
provide: WRITE_PACKAGE_TRANSFORM_TOKEN,
|
||||
useFactory: nxWritePackageTransform,
|
||||
deps: [OPTIONS_TOKEN],
|
||||
});
|
||||
@ -1,424 +0,0 @@
|
||||
/**
|
||||
* Adapted from the original ng-packagr.
|
||||
*
|
||||
* Changes made:
|
||||
* - Change the package.json metadata to only use the ESM2022 output.
|
||||
*/
|
||||
|
||||
import { logger } from '@nx/devkit';
|
||||
import { BuildGraph } from 'ng-packagr/lib/graph/build-graph';
|
||||
import { Node } from 'ng-packagr/lib/graph/node';
|
||||
import { transformFromPromise } from 'ng-packagr/lib/graph/transform';
|
||||
import { NgEntryPoint } from 'ng-packagr/lib/ng-package/entry-point/entry-point';
|
||||
import {
|
||||
EntryPointNode,
|
||||
fileUrl,
|
||||
isEntryPointInProgress,
|
||||
isEntryPoint,
|
||||
isPackage,
|
||||
PackageNode,
|
||||
} from 'ng-packagr/lib/ng-package/nodes';
|
||||
import { NgPackagrOptions } from 'ng-packagr/lib/ng-package/options.di';
|
||||
import { NgPackage } from 'ng-packagr/lib/ng-package/package';
|
||||
import {
|
||||
copyFile,
|
||||
exists,
|
||||
readFile,
|
||||
rmdir,
|
||||
stat,
|
||||
writeFile,
|
||||
} from 'ng-packagr/lib/utils/fs';
|
||||
import { globFiles } from 'ng-packagr/lib/utils/glob';
|
||||
import { ensureUnixPath } from 'ng-packagr/lib/utils/path';
|
||||
import { AssetPattern } from 'ng-packagr/ng-package.schema';
|
||||
import * as path from 'path';
|
||||
|
||||
export const nxWritePackageTransform = (options: NgPackagrOptions) =>
|
||||
transformFromPromise(async (graph) => {
|
||||
const entryPoint: EntryPointNode = graph.find(isEntryPointInProgress());
|
||||
const ngEntryPoint: NgEntryPoint = entryPoint.data.entryPoint;
|
||||
const ngPackageNode: PackageNode = graph.find(isPackage);
|
||||
const ngPackage = ngPackageNode.data;
|
||||
const { destinationFiles } = entryPoint.data;
|
||||
|
||||
if (!ngEntryPoint.isSecondaryEntryPoint) {
|
||||
logger.log('Copying assets');
|
||||
|
||||
try {
|
||||
await copyAssets(graph, entryPoint, ngPackageNode);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. WRITE PACKAGE.JSON
|
||||
const relativeUnixFromDestPath = (filePath: string) =>
|
||||
ensureUnixPath(path.relative(ngEntryPoint.destinationPath, filePath));
|
||||
|
||||
if (!ngEntryPoint.isSecondaryEntryPoint) {
|
||||
try {
|
||||
logger.info('Writing package manifest');
|
||||
if (!options.watch) {
|
||||
const primary = ngPackageNode.data.primary;
|
||||
await writeFile(
|
||||
path.join(primary.destinationPath, '.npmignore'),
|
||||
`# Nested package.json's are only needed for development.\n**/package.json`
|
||||
);
|
||||
}
|
||||
|
||||
await writePackageJson(
|
||||
ngEntryPoint,
|
||||
ngPackage,
|
||||
{
|
||||
module: relativeUnixFromDestPath(destinationFiles.esm2022),
|
||||
typings: relativeUnixFromDestPath(destinationFiles.declarations),
|
||||
exports: generatePackageExports(ngEntryPoint, graph),
|
||||
// webpack v4+ specific flag to enable advanced optimizations and code splitting
|
||||
sideEffects: ngEntryPoint.packageJson.sideEffects ?? false,
|
||||
},
|
||||
!!options.watch
|
||||
);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
} else if (ngEntryPoint.isSecondaryEntryPoint) {
|
||||
if (options.watch) {
|
||||
// Update the watch version of the primary entry point `package.json` file.
|
||||
// this is needed because of Webpack's 5 `cachemanagedpaths`
|
||||
// https://github.com/ng-packagr/ng-packagr/issues/2069
|
||||
const primary = ngPackageNode.data.primary;
|
||||
const packageJsonPath = path.join(
|
||||
primary.destinationPath,
|
||||
'package.json'
|
||||
);
|
||||
|
||||
if (await exists(packageJsonPath)) {
|
||||
const packageJson = JSON.parse(
|
||||
await readFile(packageJsonPath, { encoding: 'utf8' })
|
||||
);
|
||||
packageJson.version = generateWatchVersion();
|
||||
await writeFile(
|
||||
path.join(primary.destinationPath, 'package.json'),
|
||||
JSON.stringify(packageJson, undefined, 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Write a package.json in each secondary entry-point
|
||||
// This is need for esbuild to secondary entry-points in dist correctly.
|
||||
await writeFile(
|
||||
path.join(ngEntryPoint.destinationPath, 'package.json'),
|
||||
JSON.stringify(
|
||||
{ module: relativeUnixFromDestPath(destinationFiles.esm2022) },
|
||||
undefined,
|
||||
2
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(`Built ${ngEntryPoint.moduleId}`);
|
||||
|
||||
return graph;
|
||||
});
|
||||
|
||||
type AssetEntry = Exclude<AssetPattern, string>;
|
||||
|
||||
async function copyAssets(
|
||||
graph: BuildGraph,
|
||||
entryPointNode: EntryPointNode,
|
||||
ngPackageNode: PackageNode
|
||||
): Promise<void> {
|
||||
const ngPackage = ngPackageNode.data;
|
||||
|
||||
const globsForceIgnored: string[] = [
|
||||
'.gitkeep',
|
||||
'**/.DS_Store',
|
||||
'**/Thumbs.db',
|
||||
`${ngPackage.dest}/**`,
|
||||
];
|
||||
|
||||
const assets: AssetEntry[] = [];
|
||||
|
||||
for (const assetPath of ngPackage.assets) {
|
||||
let asset: AssetEntry;
|
||||
if (typeof assetPath === 'object') {
|
||||
asset = { ...assetPath };
|
||||
} else {
|
||||
const [isDir, isFile] = await stat(path.join(ngPackage.src, assetPath))
|
||||
.then((stats) => [stats.isDirectory(), stats.isFile()])
|
||||
.catch(() => [false, false]);
|
||||
if (isDir) {
|
||||
asset = { glob: '**/*', input: assetPath, output: assetPath };
|
||||
} else if (isFile) {
|
||||
// filenames are their own glob
|
||||
asset = {
|
||||
glob: path.basename(assetPath),
|
||||
input: path.dirname(assetPath),
|
||||
output: path.dirname(assetPath),
|
||||
};
|
||||
} else {
|
||||
asset = { glob: assetPath, input: '/', output: '/' };
|
||||
}
|
||||
}
|
||||
|
||||
asset.input = path.join(ngPackage.src, asset.input);
|
||||
asset.output = path.join(ngPackage.dest, asset.output);
|
||||
|
||||
const isAncestorPath = (target: string, datum: string) =>
|
||||
path.relative(datum, target).startsWith('..');
|
||||
if (isAncestorPath(asset.input, ngPackage.src)) {
|
||||
throw new Error(
|
||||
'Cannot read assets from a location outside of the project root.'
|
||||
);
|
||||
}
|
||||
if (isAncestorPath(asset.output, ngPackage.dest)) {
|
||||
throw new Error(
|
||||
'Cannot write assets to a location outside of the output path.'
|
||||
);
|
||||
}
|
||||
|
||||
assets.push(asset);
|
||||
}
|
||||
|
||||
for (const asset of assets) {
|
||||
const filePaths = await globFiles(asset.glob, {
|
||||
cwd: asset.input,
|
||||
ignore: [...(asset.ignore ?? []), ...globsForceIgnored],
|
||||
dot: true,
|
||||
onlyFiles: true,
|
||||
followSymbolicLinks: asset.followSymlinks,
|
||||
});
|
||||
for (const filePath of filePaths) {
|
||||
const fileSrcFullPath = path.join(asset.input, filePath);
|
||||
const fileDestFullPath = path.join(asset.output, filePath);
|
||||
const nodeUri = fileUrl(ensureUnixPath(fileSrcFullPath));
|
||||
let node = graph.get(nodeUri);
|
||||
if (!node) {
|
||||
node = new Node(nodeUri);
|
||||
graph.put(node);
|
||||
}
|
||||
entryPointNode.dependsOn(node);
|
||||
await copyFile(fileSrcFullPath, fileDestFullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and writes a `package.json` file of the entry point used by the `node_module`
|
||||
* resolution strategies.
|
||||
*
|
||||
* #### Example
|
||||
*
|
||||
* A consumer of the entry point depends on it by `import {..} from '@my/module/id';`.
|
||||
* The module id `@my/module/id` will be resolved to the `package.json` file that is written by
|
||||
* this build step.
|
||||
* The properties `main`, `module`, `typings` (and so on) in the `package.json` point to the
|
||||
* flattened JavaScript bundles, type definitions, (...).
|
||||
*
|
||||
* @param entryPoint An entry point of an Angular package / library
|
||||
* @param additionalProperties Additional properties, e.g. binary artefacts (bundle files), to merge into `package.json`
|
||||
*/
|
||||
async function writePackageJson(
|
||||
entryPoint: NgEntryPoint,
|
||||
pkg: NgPackage,
|
||||
additionalProperties: {
|
||||
[key: string]: string | boolean | string[] | ConditionalExport;
|
||||
},
|
||||
isWatchMode: boolean
|
||||
): Promise<void> {
|
||||
// set additional properties
|
||||
const packageJson = { ...entryPoint.packageJson, ...additionalProperties };
|
||||
|
||||
// read tslib version from `@angular/compiler` so that our tslib
|
||||
// version at least matches that of angular if we use require('tslib').version
|
||||
// it will get what installed and not the minimum version nor if it is a `~` or `^`
|
||||
// this is only required for primary
|
||||
if (isWatchMode) {
|
||||
// Needed because of Webpack's 5 `cachemanagedpaths`
|
||||
// https://github.com/angular/angular-cli/issues/20962
|
||||
packageJson.version = generateWatchVersion();
|
||||
}
|
||||
|
||||
if (
|
||||
!packageJson.peerDependencies?.tslib &&
|
||||
!packageJson.dependencies?.tslib
|
||||
) {
|
||||
const {
|
||||
peerDependencies: angularPeerDependencies = {},
|
||||
dependencies: angularDependencies = {},
|
||||
} = require('@angular/compiler/package.json');
|
||||
const tsLibVersion =
|
||||
angularPeerDependencies.tslib || angularDependencies.tslib;
|
||||
|
||||
if (tsLibVersion) {
|
||||
packageJson.dependencies = {
|
||||
...packageJson.dependencies,
|
||||
tslib: tsLibVersion,
|
||||
};
|
||||
}
|
||||
} else if (packageJson.peerDependencies?.tslib) {
|
||||
logger.warn(
|
||||
`'tslib' is no longer recommended to be used as a 'peerDependencies'. Moving it to 'dependencies'.`
|
||||
);
|
||||
packageJson.dependencies = {
|
||||
...(packageJson.dependencies || {}),
|
||||
tslib: packageJson.peerDependencies.tslib,
|
||||
};
|
||||
|
||||
delete packageJson.peerDependencies.tslib;
|
||||
}
|
||||
|
||||
// Verify non-peerDependencies as they can easily lead to duplicate installs or version conflicts
|
||||
// in the node_modules folder of an application
|
||||
const allowedList = pkg.allowedNonPeerDependencies.map(
|
||||
(value) => new RegExp(value)
|
||||
);
|
||||
try {
|
||||
checkNonPeerDependencies(packageJson, 'dependencies', allowedList);
|
||||
} catch (e) {
|
||||
await rmdir(entryPoint.destinationPath, { recursive: true });
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Removes scripts from package.json after build
|
||||
if (packageJson.scripts) {
|
||||
if (pkg.keepLifecycleScripts !== true) {
|
||||
logger.info(
|
||||
`Removing scripts section in package.json as it's considered a potential security vulnerability.`
|
||||
);
|
||||
delete packageJson.scripts;
|
||||
} else {
|
||||
logger.warn(
|
||||
`You enabled keepLifecycleScripts explicitly. The scripts section in package.json will be published to npm.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// keep the dist package.json clean
|
||||
// this will not throw if ngPackage field does not exist
|
||||
delete packageJson.ngPackage;
|
||||
|
||||
const packageJsonPropertiesToDelete = [
|
||||
'stylelint',
|
||||
'prettier',
|
||||
'browserslist',
|
||||
'devDependencies',
|
||||
'jest',
|
||||
'workspaces',
|
||||
'husky',
|
||||
];
|
||||
|
||||
for (const prop of packageJsonPropertiesToDelete) {
|
||||
if (prop in packageJson) {
|
||||
delete packageJson[prop];
|
||||
logger.info(`Removing ${prop} section in package.json.`);
|
||||
}
|
||||
}
|
||||
|
||||
packageJson.name = entryPoint.moduleId;
|
||||
await writeFile(
|
||||
path.join(entryPoint.destinationPath, 'package.json'),
|
||||
JSON.stringify(packageJson, undefined, 2)
|
||||
);
|
||||
}
|
||||
|
||||
function checkNonPeerDependencies(
|
||||
packageJson: Record<string, unknown>,
|
||||
property: string,
|
||||
allowed: RegExp[]
|
||||
) {
|
||||
if (!packageJson[property]) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const dep of Object.keys(packageJson[property])) {
|
||||
if (!allowed.some((regex) => regex.test(dep))) {
|
||||
logger.warn(
|
||||
`Distributing npm packages with '${property}' is not recommended. Please consider adding ${dep} to 'peerDependencies' or remove it from '${property}'.`
|
||||
);
|
||||
throw new Error(
|
||||
`Dependency ${dep} must be explicitly allowed using the "allowedNonPeerDependencies" option.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type PackageExports = Record<string, ConditionalExport>;
|
||||
|
||||
/**
|
||||
* Type describing the conditional exports descriptor for an entry-point.
|
||||
* https://nodejs.org/api/packages.html#packages_conditional_exports
|
||||
*/
|
||||
type ConditionalExport = {
|
||||
types?: string;
|
||||
esm2022?: string;
|
||||
esm?: string;
|
||||
default?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates the `package.json` package exports following APF v13.
|
||||
* This is supposed to match with: https://github.com/angular/angular/blob/e0667efa6eada64d1fb8b143840689090fc82e52/packages/bazel/src/ng_package/packager.ts#L415.
|
||||
*/
|
||||
function generatePackageExports(
|
||||
{ destinationPath, packageJson }: NgEntryPoint,
|
||||
graph: BuildGraph
|
||||
): PackageExports {
|
||||
const exports: PackageExports = packageJson.exports
|
||||
? JSON.parse(JSON.stringify(packageJson.exports))
|
||||
: {};
|
||||
|
||||
const insertMappingOrError = (
|
||||
subpath: string,
|
||||
mapping: ConditionalExport
|
||||
) => {
|
||||
exports[subpath] ??= {};
|
||||
const subpathExport = exports[subpath];
|
||||
|
||||
// Go through all conditions that should be inserted. If the condition is already
|
||||
// manually set of the subpath export, we throw an error. In general, we allow for
|
||||
// additional conditions to be set. These will always precede the generated ones.
|
||||
for (const conditionName of Object.keys(mapping)) {
|
||||
if (subpathExport[conditionName] !== undefined) {
|
||||
logger.warn(
|
||||
`Found a conflicting export condition for "${subpath}". The "${conditionName}" ` +
|
||||
`condition would be overridden by ng-packagr. Please unset it.`
|
||||
);
|
||||
}
|
||||
|
||||
// **Note**: The order of the conditions is preserved even though we are setting
|
||||
// the conditions once at a time (the latest assignment will be at the end).
|
||||
subpathExport[conditionName] = mapping[conditionName];
|
||||
}
|
||||
};
|
||||
|
||||
const relativeUnixFromDestPath = (filePath: string) =>
|
||||
'./' + ensureUnixPath(path.relative(destinationPath, filePath));
|
||||
|
||||
insertMappingOrError('./package.json', { default: './package.json' });
|
||||
|
||||
const entryPoints = graph.filter(isEntryPoint);
|
||||
for (const entryPoint of entryPoints) {
|
||||
const { destinationFiles, isSecondaryEntryPoint } =
|
||||
entryPoint.data.entryPoint;
|
||||
const subpath = isSecondaryEntryPoint
|
||||
? `./${destinationFiles.directory}`
|
||||
: '.';
|
||||
|
||||
insertMappingOrError(subpath, {
|
||||
types: relativeUnixFromDestPath(destinationFiles.declarations),
|
||||
esm2022: relativeUnixFromDestPath(destinationFiles.esm2022),
|
||||
esm: relativeUnixFromDestPath(destinationFiles.esm2022),
|
||||
default: relativeUnixFromDestPath(destinationFiles.esm2022),
|
||||
});
|
||||
}
|
||||
|
||||
return exports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new version for the package `package.json` when runing in watch mode.
|
||||
*/
|
||||
function generateWatchVersion() {
|
||||
return `0.0.0-watch+${Date.now()}`;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user