Skip to content

Commit cc0b0ea

Browse files
committed
fix(@schematics/angular): migrate secondary entry point style options in use-library-builder
migration read only `lib.entryFile` from secondary `ng-package.json` and then deleted it, so its `styleIncludePaths` and `sass` options were lost and next build failed with `Can't find stylesheet to import` library builder has single global `stylePreprocessorOptions`, so secondary optoins are now merged into it next to primary ones
1 parent 8aa16b6 commit cc0b0ea

2 files changed

Lines changed: 74 additions & 10 deletions

File tree

‎packages/schematics/angular/migrations/use-library-builder/migration.ts‎

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ interface SecondaryEntryPoint {
6060
subpath: string;
6161
entryPoint: string;
6262
configFile: string;
63+
config: NgPackageConfig | undefined;
6364
}
6465

6566
/**
@@ -176,6 +177,7 @@ function findSecondaryEntryPoints(
176177
subpath,
177178
entryPoint: join(currentPath, entryFile),
178179
configFile: ngPackageJsonPath,
180+
config,
179181
});
180182
} else if (
181183
current.subfiles.some((f) => f === 'package.json') &&
@@ -189,6 +191,7 @@ function findSecondaryEntryPoints(
189191
subpath,
190192
entryPoint: join(currentPath, entryFile),
191193
configFile: packageJsonPath,
194+
config,
192195
});
193196
}
194197
}
@@ -217,13 +220,15 @@ function findSecondaryEntryPoints(
217220
* @param projectDir The directory containing the primary `ng-packagr` configuration file.
218221
* @param buildTarget The library's `build` target definition in `angular.json`.
219222
* @param ngPackageConfig The parsed `NgPackageConfig`, if available.
223+
* @param secondaryEntryPoints The discovered secondary entry points of the library.
220224
* @param tree The virtual file system tree.
221225
*/
222226
function migrateTargetOptions(
223227
projectRoot: string,
224228
projectDir: string,
225229
buildTarget: TargetDefinition,
226230
ngPackageConfig: NgPackageConfig | undefined,
231+
secondaryEntryPoints: SecondaryEntryPoint[],
227232
tree: Tree,
228233
): void {
229234
buildTarget.builder = Builders.BuildLibrary;
@@ -281,27 +286,43 @@ function migrateTargetOptions(
281286
buildTarget.options['assets'] = [...existingAssets, ...migratedAssets];
282287
}
283288

284-
if (ngPackageConfig?.lib?.styleIncludePaths?.length || ngPackageConfig?.lib?.sass) {
289+
// The library builder applies style options to all entry points, so the options of
290+
// secondary entry points are merged with the primary ones.
291+
const styleConfigs = [
292+
{ dir: projectDir, lib: ngPackageConfig?.lib },
293+
...secondaryEntryPoints.map(({ configFile, config }) => ({
294+
dir: dirname(configFile),
295+
lib: config?.lib,
296+
})),
297+
];
298+
const migratedIncludePaths = styleConfigs.flatMap(({ dir, lib }) =>
299+
(lib?.styleIncludePaths ?? []).map((p) => join(dir, p)),
300+
);
301+
const migratedSassOptions = styleConfigs.flatMap(({ lib }) => (lib?.sass ? [lib.sass] : []));
302+
303+
if (migratedIncludePaths.length || migratedSassOptions.length) {
285304
const stylePreprocessorOptions = buildTarget.options['stylePreprocessorOptions'];
286305
const existingPreprocessorOptions: JsonObject =
287306
stylePreprocessorOptions && isJsonObject(stylePreprocessorOptions)
288307
? { ...stylePreprocessorOptions }
289308
: {};
290309

291-
if (ngPackageConfig.lib.styleIncludePaths?.length) {
310+
if (migratedIncludePaths.length) {
292311
const existingIncludePaths = Array.isArray(existingPreprocessorOptions['includePaths'])
293312
? (existingPreprocessorOptions['includePaths'] as string[])
294313
: [];
295-
const migratedIncludePaths = ngPackageConfig.lib.styleIncludePaths.map((p) =>
296-
join(projectDir, p),
297-
);
298314
existingPreprocessorOptions['includePaths'] = [
299-
...existingIncludePaths,
300-
...migratedIncludePaths,
315+
...new Set([...existingIncludePaths, ...migratedIncludePaths]),
301316
];
302317
}
303-
if (ngPackageConfig.lib.sass) {
304-
existingPreprocessorOptions['sass'] = ngPackageConfig.lib.sass;
318+
if (migratedSassOptions.length) {
319+
const sass: Record<string, string[]> = {};
320+
for (const options of migratedSassOptions) {
321+
for (const [key, value] of Object.entries(options)) {
322+
sass[key] = [...new Set([...(sass[key] ?? []), ...(value ?? [])])];
323+
}
324+
}
325+
existingPreprocessorOptions['sass'] = sass;
305326
}
306327

307328
buildTarget.options['stylePreprocessorOptions'] = existingPreprocessorOptions;
@@ -432,7 +453,14 @@ function updateLibraryTarget(
432453
entryPointExports[exportKey] = toRelativeExportPath(secondary.entryPoint);
433454
}
434455

435-
migrateTargetOptions(normalizedProjectRoot, projectDir, buildTarget, ngPackageConfig, tree);
456+
migrateTargetOptions(
457+
normalizedProjectRoot,
458+
projectDir,
459+
buildTarget,
460+
ngPackageConfig,
461+
secondaryEntryPoints,
462+
tree,
463+
);
436464

437465
const packageJsonPath = join(normalizedProjectRoot, 'package.json');
438466

‎packages/schematics/angular/migrations/use-library-builder/migration_spec.ts‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,42 @@ describe('Migration to use the library builder', () => {
399399
});
400400
});
401401

402+
it('should migrate styleIncludePaths and sass options from secondary entry points', async () => {
403+
tree.overwrite(
404+
'/projects/my-lib/ng-package.json',
405+
JSON.stringify({
406+
lib: {
407+
entryFile: 'src/public-api.ts',
408+
styleIncludePaths: ['styles'],
409+
sass: { silenceDeprecations: ['import'] },
410+
},
411+
}),
412+
);
413+
tree.create(
414+
'/projects/my-lib/testing/ng-package.json',
415+
JSON.stringify({
416+
lib: {
417+
entryFile: 'src/public-api.ts',
418+
styleIncludePaths: ['../styles', 'styles'],
419+
sass: { silenceDeprecations: ['import', 'global-builtin'] },
420+
},
421+
}),
422+
);
423+
424+
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
425+
const {
426+
projects: { 'my-lib': lib },
427+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
428+
} = newTree.readJson('/angular.json') as any;
429+
430+
expect(lib.architect['build'].options.stylePreprocessorOptions).toEqual({
431+
includePaths: ['projects/my-lib/styles', 'projects/my-lib/testing/styles'],
432+
sass: {
433+
silenceDeprecations: ['import', 'global-builtin'],
434+
},
435+
});
436+
});
437+
402438
it('should throw an error when entry file is not configured and no default candidate exists', async () => {
403439
tree.overwrite('/projects/my-lib/ng-package.json', JSON.stringify({}, undefined, 2));
404440

0 commit comments

Comments
 (0)