Skip to content

Commit 5c2f19f

Browse files
committed
feat(@angular/build): add library builder
Add a new native `@angular/build:library` builder providing a modern, high-performance compilation and packaging pipeline.
1 parent dcfa27a commit 5c2f19f

54 files changed

Lines changed: 5993 additions & 129 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

modules/testing/builder/src/builder-harness.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export class BuilderHarness<T> {
111111
}
112112
}
113113

114-
private resolvePath(path: string): string {
114+
resolvePath(path: string): string {
115115
return join(getSystemPath(this.host.root()), path);
116116
}
117117

packages/angular/build/BUILD.bazel

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ ts_json_schema(
2424
src = "src/builders/extract-i18n/schema.json",
2525
)
2626

27+
ts_json_schema(
28+
name = "library_schema",
29+
src = "src/builders/library/schema.json",
30+
)
31+
2732
ts_json_schema(
2833
name = "ng_karma_schema",
2934
src = "src/builders/karma/schema.json",
@@ -72,6 +77,7 @@ ts_project(
7277
"//packages/angular/build:src/builders/dev-server/schema.ts",
7378
"//packages/angular/build:src/builders/extract-i18n/schema.ts",
7479
"//packages/angular/build:src/builders/karma/schema.ts",
80+
"//packages/angular/build:src/builders/library/schema.ts",
7581
"//packages/angular/build:src/builders/ng-packagr/schema.ts",
7682
"//packages/angular/build:src/builders/unit-test/schema.ts",
7783
],
@@ -104,6 +110,7 @@ ts_project(
104110
":node_modules/piscina",
105111
":node_modules/postcss",
106112
":node_modules/rolldown",
113+
":node_modules/rolldown-plugin-dts",
107114
":node_modules/rollup",
108115
":node_modules/sass",
109116
":node_modules/sass-embedded",
@@ -286,6 +293,32 @@ ts_project(
286293
],
287294
)
288295

296+
ts_project(
297+
name = "library_integration_test_lib",
298+
testonly = True,
299+
srcs = glob(include = ["src/builders/library/tests/**/*.ts"]),
300+
deps = [
301+
":build",
302+
"//packages/angular/build/private",
303+
"//modules/testing/builder",
304+
":node_modules/@angular-devkit/architect",
305+
":node_modules/@angular-devkit/core",
306+
"//:node_modules/@types/node",
307+
308+
# Base dependencies for the library in hello-world-lib.
309+
"//:node_modules/@angular/common",
310+
"//:node_modules/@angular/compiler",
311+
"//:node_modules/@angular/compiler-cli",
312+
"//:node_modules/@angular/core",
313+
"//:node_modules/@angular/platform-browser",
314+
"//:node_modules/@angular/router",
315+
":node_modules/rxjs",
316+
"//:node_modules/tslib",
317+
"//:node_modules/typescript",
318+
"//:node_modules/zone.js",
319+
],
320+
)
321+
289322
jasmine_test(
290323
name = "application_integration_tests",
291324
size = "medium",
@@ -327,6 +360,13 @@ jasmine_test(
327360
shard_count = 5,
328361
)
329362

363+
jasmine_test(
364+
name = "library_integration_tests",
365+
size = "medium",
366+
data = [":library_integration_test_lib"],
367+
shard_count = 4,
368+
)
369+
330370
genrule(
331371
name = "license",
332372
srcs = ["//:LICENSE"],

packages/angular/build/builders.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
"schema": "./src/builders/karma/schema.json",
2121
"description": "Run Karma unit tests."
2222
},
23+
"library": {
24+
"implementation": "./src/builders/library/index",
25+
"schema": "./src/builders/library/schema.json",
26+
"description": "Build an Angular library package conforming to the Angular Package Format (APF)."
27+
},
2328
"ng-packagr": {
2429
"implementation": "./src/builders/ng-packagr/index",
2530
"schema": "./src/builders/ng-packagr/schema.json",

packages/angular/build/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"picomatch": "4.0.7",
3939
"piscina": "5.3.2",
4040
"rolldown": "1.2.8",
41+
"rolldown-plugin-dts": "0.28.5",
4142
"sass": "1.104.1",
4243
"sass-embedded": "1.104.1",
4344
"semver": "7.8.5",

packages/angular/build/src/builders/application/build-action.ts

Lines changed: 10 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
*/
88

99
import { BuilderContext } from '@angular-devkit/architect';
10-
import { existsSync } from 'node:fs';
11-
import path from 'node:path';
1210
import {
1311
BuildOutputAsset,
1412
ExecutionResult,
@@ -21,10 +19,8 @@ import {
2119
} from '../../tools/esbuild/stylesheets/sass-language';
2220
import { logMessages, withNoProgress, withSpinner } from '../../tools/esbuild/utils';
2321
import { ChangedFiles } from '../../tools/esbuild/watcher';
24-
import { shouldWatchRoot } from '../../utils/environment-options';
2522
import { initializeHash } from '../../utils/hash';
2623
import { NormalizedCachedOptions } from '../../utils/normalize-cache';
27-
import { toPosixPath } from '../../utils/path';
2824
import { NormalizedApplicationBuildOptions, NormalizedOutputOptions } from './options';
2925
import {
3026
ComponentUpdateResult,
@@ -35,20 +31,6 @@ import {
3531
ResultMessage,
3632
} from './results';
3733

38-
// Watch workspace for package manager changes
39-
const packageWatchFiles = [
40-
// manifest can affect module resolution
41-
'package.json',
42-
// npm lock file
43-
'package-lock.json',
44-
// pnpm lock file
45-
'pnpm-lock.yaml',
46-
// yarn lock file including Yarn PnP manifest files (https://yarnpkg.com/advanced/pnp-spec/)
47-
'yarn.lock',
48-
'.pnp.cjs',
49-
'.pnp.data.json',
50-
];
51-
5234
// eslint-disable-next-line max-lines-per-function
5335
export async function* runEsBuildBuildAction(
5436
action: (rebuildState?: RebuildState) => Promise<ExecutionResult>,
@@ -115,55 +97,18 @@ export async function* runEsBuildBuildAction(
11597
logger.info('Watch mode enabled. Watching for file changes...');
11698
}
11799

118-
const normalizedOutputBase = toPosixPath(outputOptions.base);
119-
const normalizedCacheBase = toPosixPath(cacheOptions.basePath);
120-
const ignored: string[] = [
121-
// Ignore the output and cache paths to avoid infinite rebuild cycles
122-
normalizedOutputBase,
123-
`${normalizedOutputBase}/**`,
124-
normalizedCacheBase,
125-
`${normalizedCacheBase}/**`,
126-
`${toPosixPath(workspaceRoot)}/**/.*/**`,
127-
];
128-
129-
if (cacheOptions.localBasePath && cacheOptions.localBasePath !== cacheOptions.basePath) {
130-
const normalizedLocalCacheBase = toPosixPath(cacheOptions.localBasePath);
131-
ignored.push(normalizedLocalCacheBase, `${normalizedLocalCacheBase}/**`);
132-
}
133-
134100
// Setup a watcher
135-
const { createWatcher } = await import('../../tools/esbuild/watcher');
136-
watcher = await createWatcher({
137-
polling: typeof poll === 'number',
138-
interval: poll,
139-
followSymlinks: preserveSymlinks,
140-
ignored,
141-
cwd: workspaceRoot,
101+
const { setupWatcher } = await import('../../tools/esbuild/watcher');
102+
watcher = await setupWatcher({
103+
workspaceRoot,
104+
projectRoot,
105+
outputPath: outputOptions.base,
106+
cacheOptions,
107+
poll,
108+
preserveSymlinks,
109+
signal: options.signal,
110+
watchFiles: result.watchFiles,
142111
});
143-
144-
// Setup abort support
145-
options.signal?.addEventListener('abort', () => void watcher?.close());
146-
147-
// Watch the entire project root if 'NG_BUILD_WATCH_ROOT' environment variable is set
148-
if (shouldWatchRoot) {
149-
if (!preserveSymlinks) {
150-
// Ignore all node modules directories to avoid excessive file watchers.
151-
// Package changes are handled below by watching manifest and lock files.
152-
// NOTE: this is not enable when preserveSymlinks is true as this would break `npm link` usages.
153-
ignored.push('**/node_modules/**');
154-
155-
watcher.add(
156-
packageWatchFiles
157-
.map((file) => path.join(workspaceRoot, file))
158-
.filter((file) => existsSync(file)),
159-
);
160-
}
161-
162-
watcher.add(projectRoot);
163-
}
164-
165-
// Watch locations provided by the initial build result
166-
watcher.add(result.watchFiles);
167112
}
168113

169114
// Output the first build results after setting up the watcher to ensure that any code executed

packages/angular/build/src/builders/application/options.ts

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import type { BuilderContext } from '@angular-devkit/architect';
1010
import type { Plugin } from 'esbuild';
1111
import { access, constants, readFile } from 'node:fs/promises';
12-
import { createRequire } from 'node:module';
1312
import path from 'node:path';
1413
import { normalizeAssetPatterns, normalizeOptimization, normalizeSourceMaps } from '../../utils';
1514
import { supportColor } from '../../utils/color';
@@ -19,9 +18,8 @@ import { IndexHtmlTransform } from '../../utils/index-file/index-html-generator'
1918
import { normalizeCacheOptions } from '../../utils/normalize-cache';
2019
import { canonicalizePath } from '../../utils/path';
2120
import {
22-
SearchDirectory,
23-
findTailwindConfiguration,
2421
generateSearchDirectories,
22+
getTailwindConfig,
2523
loadPostcssConfiguration,
2624
} from '../../utils/postcss-configuration';
2725
import { getProjectRootPaths, normalizeDirectoryPath } from '../../utils/project-metadata';
@@ -280,7 +278,7 @@ export async function normalizeOptions(
280278
// Skip tailwind configuration if postcss is customized
281279
const tailwindConfiguration = postcssConfiguration
282280
? undefined
283-
: await getTailwindConfig(searchDirectories, workspaceRoot, context);
281+
: await getTailwindConfig(searchDirectories, workspaceRoot, context.logger);
284282

285283
let serverEntryPoint: string | undefined;
286284
if (typeof options.server === 'string') {
@@ -538,36 +536,6 @@ export async function normalizeOptions(
538536
};
539537
}
540538

541-
async function getTailwindConfig(
542-
searchDirectories: SearchDirectory[],
543-
workspaceRoot: string,
544-
context: BuilderContext,
545-
): Promise<{ file: string; package: string } | undefined> {
546-
const tailwindConfigurationPath = findTailwindConfiguration(searchDirectories);
547-
548-
if (!tailwindConfigurationPath) {
549-
return undefined;
550-
}
551-
552-
// Create a node resolver from the configuration file
553-
const resolver = createRequire(tailwindConfigurationPath);
554-
try {
555-
return {
556-
file: tailwindConfigurationPath,
557-
package: resolver.resolve('tailwindcss'),
558-
};
559-
} catch {
560-
const relativeTailwindConfigPath = path.relative(workspaceRoot, tailwindConfigurationPath);
561-
context.logger.warn(
562-
`Tailwind CSS configuration file found (${relativeTailwindConfigPath})` +
563-
` but the 'tailwindcss' package is not installed.` +
564-
` To enable Tailwind CSS, please install the 'tailwindcss' package.`,
565-
);
566-
}
567-
568-
return undefined;
569-
}
570-
571539
/**
572540
* Normalize entry point options. To maintain compatibility with the legacy browser builder, we need a single `browser`
573541
* option which defines a single entry point. However, we also want to support multiple entry points as an internal option.

0 commit comments

Comments
 (0)