Skip to content

Commit ab72056

Browse files
committed
fixup! fix(@angular/build): conditionally declare tslib in built library package
1 parent 227a0d9 commit ab72056

6 files changed

Lines changed: 54 additions & 48 deletions

File tree

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

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import type { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
1010
import type { logging } from '@angular-devkit/core';
11-
import fs from 'node:fs/promises';
1211
import {
1312
resetSassWorkerPoolCaches,
1413
shutdownSassWorkerPool,
@@ -27,8 +26,9 @@ import type { BuildWatcher } from '../../utils/watcher';
2726
import { normalizeLibraryOptions } from './options';
2827
import { updateWatchedEntryPoints } from './pipeline/entry-points';
2928
import type { createComponentStylesheetBundlerForLibrary } from './pipeline/stylesheet-bundler';
29+
import { loadPackageJson } from './pipeline/utils';
3030
import type { Schema as LibraryBuilderOptions } from './schema';
31-
import type { NormalizedLibraryOptions, PackageJsonData, SingleBuildState } from './types';
31+
import type { NormalizedLibraryOptions, SingleBuildState } from './types';
3232

3333
/**
3434
* Executes the library builder to compile, bundle, and package an Angular library into the Angular Package Format (APF).
@@ -330,15 +330,6 @@ async function* runWatchLoop(
330330
}
331331
}
332332

333-
/**
334-
* Loads and parses a JSON file from disk.
335-
*/
336-
async function loadPackageJson(packageJsonPath: string): Promise<PackageJsonData> {
337-
const content = await fs.readFile(packageJsonPath, 'utf-8');
338-
339-
return JSON.parse(content) as PackageJsonData;
340-
}
341-
342333
/**
343334
* Logs the build completion time and status.
344335
*/

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

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

99
import type { BuilderContext } from '@angular-devkit/architect';
10-
import fs from 'node:fs/promises';
1110
import path from 'node:path';
1211
import type { StylesheetPluginsass } from '../../tools/esbuild/stylesheets/stylesheet-plugin-factory';
1312
import { normalizeAssetPatterns } from '../../utils';
@@ -22,6 +21,7 @@ import {
2221
} from '../../utils/postcss-configuration';
2322
import { getProjectRootPaths } from '../../utils/project-metadata';
2423
import { normalizeEntryPoints } from './pipeline/entry-points';
24+
import { loadPackageJson } from './pipeline/utils';
2525
import type { Schema as LibraryBuilderOptions } from './schema';
2626
import type { NormalizedLibraryOptions, PackageJsonData } from './types';
2727

@@ -69,8 +69,7 @@ export async function normalizeLibraryOptions(
6969

7070
let packageJson: PackageJsonData;
7171
try {
72-
const packageJsonContent = await fs.readFile(packageJsonPath, 'utf8');
73-
packageJson = JSON.parse(packageJsonContent) as PackageJsonData;
72+
packageJson = await loadPackageJson(packageJsonPath);
7473
} catch (error) {
7574
assertIsError(error);
7675
throw new Error(`Failed to read 'package.json' at '${packageJsonPath}': ${error.message}`, {

‎packages/angular/build/src/builders/library/pipeline/build-action.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ export async function buildAction(actionContext: BuildActionContext): Promise<vo
175175
}
176176

177177
if (shouldGenerateManifests || buildState.hasEmittedTslib !== hasTslibImport) {
178-
filesToEmit.push(...generatePackageManifests(options, isWatchMode, hasTslibImport));
178+
filesToEmit.push(...(await generatePackageManifests(options, isWatchMode, hasTslibImport)));
179179
}
180180

181181
filesToEmit.push(

‎packages/angular/build/src/builders/library/pipeline/package-manifests.ts‎

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
type MemoryOutputFile,
1515
TYPES_OUTPUT_DIR,
1616
createMemoryOutputFile,
17+
loadPackageJson,
1718
} from './utils';
1819

1920
/**
@@ -24,11 +25,11 @@ import {
2425
* @param hasTslibImport Whether any output chunk imports 'tslib'.
2526
* @returns An array of memory output files containing generated package manifests and .npmignore.
2627
*/
27-
export function generatePackageManifests(
28+
export async function generatePackageManifests(
2829
options: NormalizedLibraryOptions,
2930
isWatchMode: boolean,
3031
hasTslibImport = false,
31-
): MemoryOutputFile[] {
32+
): Promise<MemoryOutputFile[]> {
3233
const { packageJson: rawPackageJson, keepLifecycleScripts, compilationMode } = options;
3334

3435
const {
@@ -77,7 +78,7 @@ export function generatePackageManifests(
7778
};
7879

7980
if (hasTslibImport && !rawPackageJson.dependencies?.['tslib']) {
80-
const tslibVersion = getAngularTslibRange(options.workspaceRoot);
81+
const tslibVersion = await getAngularTslibRange(options.workspaceRoot);
8182
if (tslibVersion) {
8283
distPackageJson.dependencies = {
8384
...rawPackageJson.dependencies,
@@ -190,25 +191,25 @@ let cachedTslibRange: string | undefined;
190191
* @param workspaceRoot path to the workspace root
191192
* @returns tslib range
192193
*/
193-
function getAngularTslibRange(workspaceRoot: string): string {
194+
async function getAngularTslibRange(workspaceRoot: string): Promise<string> {
194195
if (cachedTslibRange) {
195196
return cachedTslibRange;
196197
}
197198

198-
const workspaceRequire = createRequire(path.join(workspaceRoot, '/'));
199+
const workspaceRequire = createRequire(path.join(workspaceRoot, 'package.json'));
199200

200201
// Try resolving via @angular/compiler dependencies
201202
try {
202-
const angularCompilerPkg = workspaceRequire('@angular/compiler/package.json') as {
203-
dependencies?: Record<string, string>;
204-
};
203+
const angularCompilerPkg = await loadPackageJson(
204+
workspaceRequire.resolve('@angular/compiler/package.json'),
205+
);
205206
cachedTslibRange = angularCompilerPkg.dependencies?.['tslib'];
206207
} catch {}
207208

208209
// Fallback: Try resolving tslib directly
209210
if (!cachedTslibRange) {
210211
try {
211-
const tslibPkg = workspaceRequire('tslib/package.json') as { version?: string };
212+
const tslibPkg = await loadPackageJson(workspaceRequire.resolve('tslib/package.json'));
212213
if (tslibPkg.version) {
213214
cachedTslibRange = `^${tslibPkg.version}`;
214215
}

‎packages/angular/build/src/builders/library/pipeline/package-manifests_spec.ts‎

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ describe('generatePackageManifests', () => {
9393
};
9494
}
9595

96-
it('should generate a valid APF package.json for an unscoped package', () => {
96+
it('should generate a valid APF package.json for an unscoped package', async () => {
9797
const options = createOptions({
9898
packageJson: {
9999
name: 'my-lib',
@@ -108,7 +108,7 @@ describe('generatePackageManifests', () => {
108108
},
109109
});
110110

111-
const files = generatePackageManifests(options, false);
111+
const files = await generatePackageManifests(options, false);
112112
const result = getRootPackageJson(files);
113113

114114
expect(result).toEqual({
@@ -131,15 +131,15 @@ describe('generatePackageManifests', () => {
131131
});
132132
});
133133

134-
it('should sanitize scoped package names in fesm and types paths', () => {
134+
it('should sanitize scoped package names in fesm and types paths', async () => {
135135
const options = createOptions({
136136
packageJson: {
137137
name: '@my-scope/my-lib',
138138
version: '2.1.0',
139139
},
140140
});
141141

142-
const files = generatePackageManifests(options, false);
142+
const files = await generatePackageManifests(options, false);
143143
const result = getRootPackageJson(files);
144144

145145
expect(result).toEqual(
@@ -158,7 +158,7 @@ describe('generatePackageManifests', () => {
158158
);
159159
});
160160

161-
it('should retain scripts when keepLifecycleScripts is true', () => {
161+
it('should retain scripts when keepLifecycleScripts is true', async () => {
162162
const options = createOptions({
163163
keepLifecycleScripts: true,
164164
packageJson: {
@@ -170,12 +170,12 @@ describe('generatePackageManifests', () => {
170170
},
171171
});
172172

173-
const files = generatePackageManifests(options, false);
173+
const files = await generatePackageManifests(options, false);
174174
const result = getRootPackageJson(files);
175175
expect(result.scripts).toEqual({ postinstall: 'echo done' });
176176
});
177177

178-
it('should configure secondary entry points and create secondary manifests', () => {
178+
it('should configure secondary entry points and create secondary manifests', async () => {
179179
const options = createOptions(
180180
{
181181
packageJson: {
@@ -186,7 +186,7 @@ describe('generatePackageManifests', () => {
186186
true,
187187
);
188188

189-
const files = generatePackageManifests(options, false);
189+
const files = await generatePackageManifests(options, false);
190190
const result = getRootPackageJson(files);
191191

192192
expect(result.exports).toEqual(
@@ -210,20 +210,20 @@ describe('generatePackageManifests', () => {
210210
expect(npmignoreFile?.contents).toContain('/testing/package.json');
211211
});
212212

213-
it('should inject watch version when isWatchMode is true', () => {
213+
it('should inject watch version when isWatchMode is true', async () => {
214214
const options = createOptions({
215215
packageJson: {
216216
name: 'my-lib',
217217
version: '1.0.0',
218218
},
219219
});
220220

221-
const files = generatePackageManifests(options, true);
221+
const files = await generatePackageManifests(options, true);
222222
const result = getRootPackageJson(files);
223223
expect(result.version).toMatch(/^0\.0\.0-watch\+\d+$/);
224224
});
225225

226-
it('should throw an error if primary entry point is missing', () => {
226+
it('should throw an error if primary entry point is missing', async () => {
227227
const options = createOptions({
228228
packageJson: {
229229
name: 'my-lib',
@@ -232,12 +232,12 @@ describe('generatePackageManifests', () => {
232232
entryPoints: new Map(),
233233
});
234234

235-
expect(() => generatePackageManifests(options, false)).toThrowError(
235+
await expectAsync(generatePackageManifests(options, false)).toBeRejectedWithError(
236236
/Primary entry point '\.' was not found in entryPoints\./,
237237
);
238238
});
239239

240-
it('should inject prepublishOnly guard script when compilationMode is full', () => {
240+
it('should inject prepublishOnly guard script when compilationMode is full', async () => {
241241
const options = createOptions({
242242
compilationMode: 'full',
243243
packageJson: {
@@ -246,14 +246,14 @@ describe('generatePackageManifests', () => {
246246
},
247247
});
248248

249-
const files = generatePackageManifests(options, false);
249+
const files = await generatePackageManifests(options, false);
250250
const result = getRootPackageJson(files);
251251
expect(result.scripts?.['prepublishOnly']).toContain(
252252
'Trying to publish a package that has been compiled in full compilation mode',
253253
);
254254
});
255255

256-
it('should preserve custom user exports in package.json and merge subpath conditions', () => {
256+
it('should preserve custom user exports in package.json and merge subpath conditions', async () => {
257257
const options = createOptions({
258258
packageJson: {
259259
name: 'my-lib',
@@ -268,7 +268,7 @@ describe('generatePackageManifests', () => {
268268
},
269269
});
270270

271-
const files = generatePackageManifests(options, false);
271+
const files = await generatePackageManifests(options, false);
272272
const result = getRootPackageJson(files);
273273

274274
expect(result.exports).toEqual({
@@ -283,8 +283,8 @@ describe('generatePackageManifests', () => {
283283
});
284284
});
285285

286-
it('should default sideEffects to false if not specified, and preserve when set', () => {
287-
const files1 = generatePackageManifests(
286+
it('should default sideEffects to false if not specified, and preserve when set', async () => {
287+
const files1 = await generatePackageManifests(
288288
createOptions({
289289
packageJson: {
290290
name: 'my-lib',
@@ -295,7 +295,7 @@ describe('generatePackageManifests', () => {
295295
);
296296
expect(getRootPackageJson(files1).sideEffects).toBeFalse();
297297

298-
const files2 = generatePackageManifests(
298+
const files2 = await generatePackageManifests(
299299
createOptions({
300300
packageJson: {
301301
name: 'my-lib',
@@ -308,7 +308,7 @@ describe('generatePackageManifests', () => {
308308
expect(getRootPackageJson(files2).sideEffects).toEqual(['*.css']);
309309
});
310310

311-
it('should conditionally inject tslib into dependencies only when hasTslibImport is true', () => {
311+
it('should conditionally inject tslib into dependencies only when hasTslibImport is true', async () => {
312312
const options = createOptions({
313313
workspaceRoot: process.cwd(),
314314
packageJson: {
@@ -317,17 +317,17 @@ describe('generatePackageManifests', () => {
317317
},
318318
});
319319

320-
const filesWithoutTslib = generatePackageManifests(options, false, false);
320+
const filesWithoutTslib = await generatePackageManifests(options, false, false);
321321
expect(getRootPackageJson(filesWithoutTslib).dependencies).toBeUndefined();
322322

323-
const filesWithTslib = generatePackageManifests(options, false, true);
323+
const filesWithTslib = await generatePackageManifests(options, false, true);
324324
expect(getRootPackageJson(filesWithTslib).dependencies).toEqual({
325325
tslib: jasmine.any(String),
326326
});
327327
});
328328

329-
it('should preserve existing user tslib declaration when hasTslibImport is true', () => {
330-
const filesInDeps = generatePackageManifests(
329+
it('should preserve existing user tslib declaration when hasTslibImport is true', async () => {
330+
const filesInDeps = await generatePackageManifests(
331331
createOptions({
332332
workspaceRoot: process.cwd(),
333333
packageJson: {

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

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

9+
import fs from 'node:fs/promises';
10+
import type { PackageJsonData } from '../types';
11+
912
const IS_DTS_FILE_REGEXP = /\.d\.[cm]?ts$/i;
1013
const IS_DTS_MAP_FILE_REGEXP = /\.d\.[cm]?ts\.map$/i;
1114

@@ -105,3 +108,15 @@ export function isDeclarationFile(path: string): boolean {
105108
export function isDeclarationSourceMapFile(path: string): boolean {
106109
return IS_DTS_MAP_FILE_REGEXP.test(path);
107110
}
111+
112+
/**
113+
* Loads and parses a package.json file from disk.
114+
*
115+
* @param packageJsonPath The path to the package.json file.
116+
* @returns The parsed package.json data.
117+
*/
118+
export async function loadPackageJson(packageJsonPath: string): Promise<PackageJsonData> {
119+
const content = await fs.readFile(packageJsonPath, 'utf-8');
120+
121+
return JSON.parse(content) as PackageJsonData;
122+
}

0 commit comments

Comments
 (0)