Skip to content

Commit f62445c

Browse files
committed
fixup! feat(@angular/build): add library builder
1 parent 7bb74af commit f62445c

24 files changed

Lines changed: 1051 additions & 2428 deletions

‎packages/angular/build/src/builders/library/builder.ts‎

Lines changed: 114 additions & 180 deletions
Large diffs are not rendered by default.

‎packages/angular/build/src/builders/library/options.ts‎

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export interface PackageJsonData {
5858
scripts?: Record<string, string>;
5959
workspaces?: unknown;
6060
dependencies?: Record<string, string>;
61+
optionalDependencies?: Record<string, string>;
6162
peerDependencies?: Record<string, string>;
6263
peerDependenciesMeta?: Record<string, { optional?: boolean }>;
6364
[key: string]: unknown;
@@ -157,7 +158,7 @@ export async function normalizeLibraryOptions(
157158
packageName,
158159
);
159160

160-
const allowedNonPeerDependencies: RegExp[] = [];
161+
const allowedNonPeerDependencies: RegExp[] = [/^tslib$/];
161162
for (const pattern of rawAllowedNonPeerDependencies) {
162163
try {
163164
allowedNonPeerDependencies.push(new RegExp(pattern));
@@ -186,10 +187,12 @@ export async function normalizeLibraryOptions(
186187
});
187188
}
188189

189-
const combinedAssets = [...defaultAssets, ...(rawAssets ?? [])];
190-
const assets = combinedAssets.length
191-
? normalizeAssetPatterns(combinedAssets, workspaceRoot, projectRoot, projectSourceRoot)
192-
: [];
190+
const assets = normalizeAssetPatterns(
191+
[...defaultAssets, ...(rawAssets ?? [])],
192+
workspaceRoot,
193+
projectRoot,
194+
projectSourceRoot,
195+
);
193196

194197
const cacheOptions = normalizeCacheOptions(projectMetadata, workspaceRoot);
195198

@@ -237,19 +240,21 @@ export async function normalizeLibraryOptions(
237240
* Normalizes a single entry point specification.
238241
*
239242
* @param key The entry point key from package.json exports (e.g. '.' or './testing').
243+
* @param posixKey Normalized POSIX key without trailing slashes.
244+
* @param isPrimary Whether this is the primary entry point.
240245
* @param targetPath The relative file path string from exports.
241246
* @param projectRoot The library project root directory.
242247
* @param packageName The root package name (e.g. `@my/lib`).
243248
* @returns The normalized entry point descriptor.
244249
*/
245250
function normalizeEntryPoint(
246251
key: string,
252+
posixKey: string,
253+
isPrimary: boolean,
247254
targetPath: string,
248255
projectRoot: string,
249256
packageName: string,
250257
): NormalizedEntryPoint {
251-
const posixKey = toPosixPath(key).replace(/\/+$/, '');
252-
const isPrimary = posixKey === '.' || posixKey === '';
253258
const name = isPrimary
254259
? '.'
255260
: posixKey[0] === '.' && posixKey[1] === '/'
@@ -264,11 +269,11 @@ function normalizeEntryPoint(
264269

265270
const subpath = isPrimary ? '.' : `./${name}`;
266271
const displayName = isPrimary ? packageName : `${packageName}/${name}`;
267-
const bundleName = getEntryPointBundleName(packageName, name, isPrimary);
272+
const bundleName = getEntryPointBundleName(packageName, name);
268273

269274
const entryFilePath = path.resolve(projectRoot, targetPath);
270275

271-
if (!/\.(?:ts|mts)$/.test(entryFilePath) || /\.d\.(?:ts|mts)$/.test(entryFilePath)) {
276+
if (!/(?<!\.d)\.(?:ts|mts)$/.test(entryFilePath)) {
272277
throw new Error(
273278
`Entry point '${key}' file path must be a TypeScript file ('.ts' or '.mts'): '${entryFilePath}'.`,
274279
);
@@ -312,6 +317,7 @@ function normalizeEntryPoints(
312317

313318
for (const [key, value] of Object.entries(exportsRecord)) {
314319
let target: string | undefined;
320+
315321
if (typeof value === 'string') {
316322
target = value;
317323
} else if (
@@ -333,26 +339,33 @@ function normalizeEntryPoints(
333339
`or a 'default' condition pointing to a TypeScript file.`,
334340
);
335341
}
342+
336343
// Non-JS/TS conditional export (e.g., sass/style-only subpath); preserve in package.json without compiling.
337344
continue;
338345
}
339346

340-
if (!isPrimary) {
341-
const isTsSource = /\.(?:ts|mts)$/.test(target) && !/\.d\.(?:ts|mts)$/.test(target);
342-
const isInvalidCodeEntry = /\.(?:d\.[cm]?ts|cts|tsx|jsx)$/.test(target);
343-
if (!isTsSource && !isInvalidCodeEntry) {
344-
// Static asset, stylesheet, or package.json export; preserve in package.json without compiling.
345-
continue;
346-
}
347+
if (!isPrimary && !/\.m?ts$/.test(target)) {
348+
// Static asset, stylesheet, or package.json export; preserve in package.json without compiling.
349+
continue;
347350
}
348351

349-
const entryPoint = normalizeEntryPoint(key, target, projectRoot, packageName);
352+
const entryPoint = normalizeEntryPoint(
353+
key,
354+
posixKey,
355+
isPrimary,
356+
target,
357+
projectRoot,
358+
packageName,
359+
);
360+
350361
if (entryPoints.has(entryPoint.name)) {
351362
throw new Error(
352363
`Duplicate entry point detected: '${key}' resolves to the same name ('${entryPoint.name}') as an existing entry point.`,
353364
);
354365
}
366+
355367
entryPoints.set(entryPoint.name, entryPoint);
368+
356369
if (entryPoint.isPrimary) {
357370
hasPrimary = true;
358371
}

‎packages/angular/build/src/builders/library/pipeline/assets.ts‎

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9+
import { statSync } from 'node:fs';
910
import path from 'node:path';
1011
import picomatch from 'picomatch';
1112
import { toPosixPath } from '../../../utils/path';
@@ -33,22 +34,44 @@ export async function collectAssetsToEmit(
3334
return [];
3435
}
3536

36-
const hasModifiedFiles = !!modifiedFiles?.size;
37+
if (modifiedFiles) {
38+
if (modifiedFiles.size === 0) {
39+
return [];
40+
}
3741

38-
if (hasModifiedFiles && !checkAssetChanges(assets, workspaceRoot, modifiedFiles)) {
39-
return [];
42+
const matchers = createAssetMatchers(assets, workspaceRoot);
43+
const filesToEmit: DiskOutputFile[] = [];
44+
45+
for (const file of modifiedFiles) {
46+
const resolvedFile = path.isAbsolute(file) ? file : path.resolve(workspaceRoot, file);
47+
const posixFile = toPosixPath(resolvedFile);
48+
49+
for (const { asset, posixInputPrefix, isMatch } of matchers) {
50+
if (!posixFile.startsWith(posixInputPrefix)) {
51+
continue;
52+
}
53+
54+
const relative = posixFile.slice(posixInputPrefix.length);
55+
if (!isMatch(relative)) {
56+
continue;
57+
}
58+
59+
if (statSync(resolvedFile, { throwIfNoEntry: false })?.isFile()) {
60+
filesToEmit.push(createDiskOutputFile(resolvedFile, path.join(asset.output, relative)));
61+
allWatchedFiles.add(posixFile);
62+
}
63+
}
64+
}
65+
66+
return filesToEmit;
4067
}
4168

4269
const resolvedAssets = await resolveAssets(assets, workspaceRoot);
4370
const filesToEmit: DiskOutputFile[] = [];
4471

4572
for (const { source, destination } of resolvedAssets) {
46-
if (hasModifiedFiles && !modifiedFiles.has(toPosixPath(source))) {
47-
continue;
48-
}
49-
5073
filesToEmit.push(createDiskOutputFile(source, destination));
51-
allWatchedFiles.add(source);
74+
allWatchedFiles.add(toPosixPath(source));
5275
}
5376

5477
return filesToEmit;
@@ -71,16 +94,7 @@ export function checkAssetChanges(
7194
return false;
7295
}
7396

74-
const matchers = assets.map((asset) => {
75-
const absInput = path.resolve(workspaceRoot, asset.input);
76-
const posixInput = toPosixPath(absInput).replace(/\/+$/, '');
77-
const isMatch = picomatch(asset.glob, {
78-
dot: true,
79-
ignore: [...DEFAULT_ASSET_IGNORE, ...(asset.ignore ?? [])],
80-
});
81-
82-
return { posixInputPrefix: `${posixInput}/`, isMatch };
83-
});
97+
const matchers = createAssetMatchers(assets, workspaceRoot);
8498

8599
for (const file of changedFiles) {
86100
const resolvedFile = path.isAbsolute(file) ? file : path.resolve(workspaceRoot, file);
@@ -98,3 +112,16 @@ export function checkAssetChanges(
98112

99113
return false;
100114
}
115+
116+
function createAssetMatchers(assets: NormalizedLibraryOptions['assets'], workspaceRoot: string) {
117+
return assets.map((asset) => {
118+
const absInput = path.resolve(workspaceRoot, asset.input);
119+
const posixInput = toPosixPath(absInput).replace(/\/+$/, '');
120+
const isMatch = picomatch(asset.glob, {
121+
dot: true,
122+
ignore: [...DEFAULT_ASSET_IGNORE, ...(asset.ignore ?? [])],
123+
});
124+
125+
return { asset, posixInputPrefix: `${posixInput}/`, isMatch };
126+
});
127+
}

0 commit comments

Comments
 (0)