From 4fcb0a82b5fa8a092d8c374cdea448edd80270d4 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Tue, 11 Oct 2022 14:15:08 +0000 Subject: [PATCH] fix(@angular-devkit/build-angular): correctly resolve Sass partial files in node packages Prior to this change non relative partial files were not resolved properly. Example we did not try to resolve `@material/button/button` as `@material/button/_button` which caused the compilation to fail. --- .../src/webpack/configs/styles.ts | 26 +++++++++++++--- .../build/styles/scss-partial-resolution.ts | 31 +++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 tests/legacy-cli/e2e/tests/build/styles/scss-partial-resolution.ts diff --git a/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts b/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts index 2a9385641126..5c2d1fbb7f16 100644 --- a/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts +++ b/packages/angular_devkit/build_angular/src/webpack/configs/styles.ts @@ -423,12 +423,30 @@ function getSassResolutionImporter( }); return { - findFileUrl: (url, { fromImport }): Promise => { + findFileUrl: async (url, { fromImport }): Promise => { + if (url.charAt(0) === '.') { + // Let Sass handle relative imports. + return null; + } + + let file: string | undefined; const resolve = fromImport ? resolveImport : resolveModule; - return resolve(root, url) - .then((file) => pathToFileURL(file)) - .catch(() => null); + try { + file = await resolve(root, url); + } catch { + // Try to resolve a partial file + // @use '@material/button/button' as mdc-button; + // `@material/button/button` -> `@material/button/_button` + const lastSlashIndex = url.lastIndexOf('/'); + const underscoreIndex = lastSlashIndex + 1; + if (underscoreIndex > 0 && url.charAt(underscoreIndex) !== '_') { + const partialFileUrl = `${url.slice(0, underscoreIndex)}_${url.slice(underscoreIndex)}`; + file = await resolve(root, partialFileUrl).catch(() => undefined); + } + } + + return file ? pathToFileURL(file) : null; }, }; } diff --git a/tests/legacy-cli/e2e/tests/build/styles/scss-partial-resolution.ts b/tests/legacy-cli/e2e/tests/build/styles/scss-partial-resolution.ts new file mode 100644 index 000000000000..f3863ad72adb --- /dev/null +++ b/tests/legacy-cli/e2e/tests/build/styles/scss-partial-resolution.ts @@ -0,0 +1,31 @@ +import { installPackage } from '../../../utils/packages'; +import { writeMultipleFiles, deleteFile, replaceInFile } from '../../../utils/fs'; +import { ng } from '../../../utils/process'; +import { updateJsonFile } from '../../../utils/project'; + +export default async function () { + // Supports resolving node_modules with are pointing to partial files partial files. + // @material/button/button below points to @material/button/_button.scss + // https://unpkg.com/browse/@material/button@14.0.0/_button.scss + + await installPackage('@material/button@14.0.0'); + + await writeMultipleFiles({ + 'src/styles.scss': ` + @use '@material/button/button' as mat; + `, + 'src/app/app.component.scss': ` + @use '@material/button/button' as mat; + `, + }); + + await updateJsonFile('angular.json', (workspaceJson) => { + const appArchitect = workspaceJson.projects['test-project'].architect; + appArchitect.build.options.styles = ['src/styles.scss']; + }); + + await deleteFile('src/app/app.component.css'); + await replaceInFile('src/app/app.component.ts', './app.component.css', './app.component.scss'); + + await ng('build', '--configuration=development'); +}