diff --git a/packages/schematics/angular/migrations/use-library-builder/migration.ts b/packages/schematics/angular/migrations/use-library-builder/migration.ts index 285cdf9f2332..4cbd8634f248 100644 --- a/packages/schematics/angular/migrations/use-library-builder/migration.ts +++ b/packages/schematics/angular/migrations/use-library-builder/migration.ts @@ -60,6 +60,7 @@ interface SecondaryEntryPoint { subpath: string; entryPoint: string; configFile: string; + config: NgPackageConfig | undefined; } /** @@ -176,6 +177,7 @@ function findSecondaryEntryPoints( subpath, entryPoint: join(currentPath, entryFile), configFile: ngPackageJsonPath, + config, }); } else if ( current.subfiles.some((f) => f === 'package.json') && @@ -189,6 +191,7 @@ function findSecondaryEntryPoints( subpath, entryPoint: join(currentPath, entryFile), configFile: packageJsonPath, + config, }); } } @@ -296,12 +299,19 @@ function migrateTargetOptions( join(projectDir, p), ); existingPreprocessorOptions['includePaths'] = [ - ...existingIncludePaths, - ...migratedIncludePaths, + ...new Set([...existingIncludePaths, ...migratedIncludePaths]), ]; } if (ngPackageConfig.lib.sass) { - existingPreprocessorOptions['sass'] = ngPackageConfig.lib.sass; + const existingSass = existingPreprocessorOptions['sass']; + const sass: JsonObject = isJsonObject(existingSass) ? { ...existingSass } : {}; + for (const [key, value] of Object.entries(ngPackageConfig.lib.sass)) { + if (Array.isArray(value)) { + const existing = Array.isArray(sass[key]) ? (sass[key] as string[]) : []; + sass[key] = [...new Set([...existing, ...value])]; + } + } + existingPreprocessorOptions['sass'] = sass; } buildTarget.options['stylePreprocessorOptions'] = existingPreprocessorOptions; @@ -400,7 +410,7 @@ function updateLibraryTarget( return false; } - const ngPackageConfig = primaryProjectFile + let ngPackageConfig = primaryProjectFile ? parseNgPackageFile(tree, primaryProjectFile) : undefined; @@ -430,6 +440,31 @@ function updateLibraryTarget( ? secondary.subpath : `./${secondary.subpath}`; entryPointExports[exportKey] = toRelativeExportPath(secondary.entryPoint); + + if (secondary.config?.lib) { + ngPackageConfig ??= {}; + ngPackageConfig.lib ??= {}; + + if (secondary.config.lib.styleIncludePaths?.length) { + const secondaryDir = dirname(secondary.configFile); + ngPackageConfig.lib.styleIncludePaths ??= []; + for (const includePath of secondary.config.lib.styleIncludePaths) { + ngPackageConfig.lib.styleIncludePaths.push( + relative(projectDir, join(secondaryDir, includePath)), + ); + } + } + + if (secondary.config.lib.sass) { + const sass: Record = (ngPackageConfig.lib.sass ??= {}); + for (const [key, value] of Object.entries(secondary.config.lib.sass)) { + if (Array.isArray(value)) { + const existing = Array.isArray(sass[key]) ? (sass[key] as string[]) : []; + sass[key] = [...new Set([...existing, ...value])]; + } + } + } + } } migrateTargetOptions(normalizedProjectRoot, projectDir, buildTarget, ngPackageConfig, tree); diff --git a/packages/schematics/angular/migrations/use-library-builder/migration_spec.ts b/packages/schematics/angular/migrations/use-library-builder/migration_spec.ts index d54d5f62f61a..98fbe65fd5f6 100644 --- a/packages/schematics/angular/migrations/use-library-builder/migration_spec.ts +++ b/packages/schematics/angular/migrations/use-library-builder/migration_spec.ts @@ -399,6 +399,74 @@ describe('Migration to use the library builder', () => { }); }); + it('should merge existing stylePreprocessorOptions.sass when migrating sass options', async () => { + const angularJson = JSON.parse(tree.readContent('/angular.json')); + angularJson.projects['my-lib'].architect.build.options.stylePreprocessorOptions = { + sass: { silenceDeprecations: ['mixed-decls'] }, + }; + tree.overwrite('/angular.json', JSON.stringify(angularJson, undefined, 2)); + + tree.overwrite( + '/projects/my-lib/ng-package.json', + JSON.stringify( + { + lib: { + entryFile: 'src/public-api.ts', + sass: { silenceDeprecations: ['import'] }, + }, + }, + undefined, + 2, + ), + ); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { 'my-lib': lib }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } = newTree.readJson('/angular.json') as any; + + expect(lib.architect['build'].options.stylePreprocessorOptions).toEqual({ + sass: { silenceDeprecations: ['mixed-decls', 'import'] }, + }); + }); + + it('should migrate styleIncludePaths and sass options from secondary entry points', async () => { + tree.overwrite( + '/projects/my-lib/ng-package.json', + JSON.stringify({ + lib: { + entryFile: 'src/public-api.ts', + styleIncludePaths: ['styles'], + sass: { silenceDeprecations: ['import'] }, + }, + }), + ); + tree.create( + '/projects/my-lib/testing/ng-package.json', + JSON.stringify({ + lib: { + entryFile: 'src/public-api.ts', + styleIncludePaths: ['../styles', 'styles'], + sass: { silenceDeprecations: ['import', 'global-builtin'] }, + }, + }), + ); + + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { + projects: { 'my-lib': lib }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } = newTree.readJson('/angular.json') as any; + + expect(lib.architect['build'].options.stylePreprocessorOptions).toEqual({ + includePaths: ['projects/my-lib/styles', 'projects/my-lib/testing/styles'], + sass: { + silenceDeprecations: ['import', 'global-builtin'], + }, + }); + }); + it('should throw an error when entry file is not configured and no default candidate exists', async () => { tree.overwrite('/projects/my-lib/ng-package.json', JSON.stringify({}, undefined, 2));