Skip to content

Commit 19f5cc7

Browse files
committedNov 21, 2022
fix(@angular-devkit/build-angular): improve package deep import Sass index resolution in esbuild plugin
When resolving Sass imports in the experimental esbuild-based browser application builder's Sass plugin, previously imported modules are used as the base for resolution attempts to workaround the lack of the importer file provided by Sass. When attempting to resolve a deep import into a package (including the potential Sass index files), these previously imported modules also need to be checked. This is particularly relevant when using the Yarn PnP or pnpm package managers which enforce strict dependency resolution. Fixes #24271 (cherry picked from commit 67752a4)
1 parent 52f63b1 commit 19f5cc7

File tree

1 file changed

+44
-38
lines changed
  • packages/angular_devkit/build_angular/src/builders/browser-esbuild

1 file changed

+44
-38
lines changed
 

‎packages/angular_devkit/build_angular/src/builders/browser-esbuild/sass-plugin.ts

+44-38
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,30 @@ export function createSassPlugin(options: { sourcemap: boolean; loadPaths?: stri
3131
return {
3232
name: 'angular-sass',
3333
setup(build: PluginBuild): void {
34+
const resolveUrl = async (url: string, previousResolvedModules?: Set<string>) => {
35+
let result = await build.resolve(url, {
36+
kind: 'import-rule',
37+
// This should ideally be the directory of the importer file from Sass
38+
// but that is not currently available from the Sass importer API.
39+
resolveDir: build.initialOptions.absWorkingDir,
40+
});
41+
42+
// Workaround to support Yarn PnP without access to the importer file from Sass
43+
if (!result.path && previousResolvedModules?.size) {
44+
for (const previous of previousResolvedModules) {
45+
result = await build.resolve(url, {
46+
kind: 'import-rule',
47+
resolveDir: previous,
48+
});
49+
if (result.path) {
50+
break;
51+
}
52+
}
53+
}
54+
55+
return result;
56+
};
57+
3458
build.onLoad({ filter: /\.s[ac]ss$/ }, async (args) => {
3559
// Lazily load Sass when a Sass file is found
3660
sassWorkerPool ??= new SassWorkerImplementation(true);
@@ -51,48 +75,30 @@ export function createSassPlugin(options: { sourcemap: boolean; loadPaths?: stri
5175
url,
5276
{ previousResolvedModules }: FileImporterWithRequestContextOptions,
5377
): Promise<URL | null> => {
54-
let result = await build.resolve(url, {
55-
kind: 'import-rule',
56-
// This should ideally be the directory of the importer file from Sass
57-
// but that is not currently available from the Sass importer API.
58-
resolveDir: build.initialOptions.absWorkingDir,
59-
});
60-
61-
// Workaround to support Yarn PnP without access to the importer file from Sass
62-
if (!result.path && previousResolvedModules?.size) {
63-
for (const previous of previousResolvedModules) {
64-
result = await build.resolve(url, {
65-
kind: 'import-rule',
66-
resolveDir: previous,
67-
});
68-
}
69-
}
78+
const result = await resolveUrl(url, previousResolvedModules);
7079

7180
// Check for package deep imports
7281
if (!result.path) {
7382
const parts = url.split('/');
74-
const hasScope = parts.length > 2 && parts[0].startsWith('@');
75-
if (hasScope || parts.length > 1) {
76-
const [nameOrScope, nameOrFirstPath, ...pathPart] = parts;
77-
const packageName = hasScope
78-
? `${nameOrScope}/${nameOrFirstPath}`
79-
: nameOrScope;
80-
const packageResult = await build.resolve(packageName + '/package.json', {
81-
kind: 'import-rule',
82-
// This should ideally be the directory of the importer file from Sass
83-
// but that is not currently available from the Sass importer API.
84-
resolveDir: build.initialOptions.absWorkingDir,
85-
});
86-
87-
if (packageResult.path) {
88-
return pathToFileURL(
89-
join(
90-
dirname(packageResult.path),
91-
!hasScope ? nameOrFirstPath : '',
92-
...pathPart,
93-
),
94-
);
95-
}
83+
const hasScope = parts.length >= 2 && parts[0].startsWith('@');
84+
const [nameOrScope, nameOrFirstPath, ...pathPart] = parts;
85+
const packageName = hasScope
86+
? `${nameOrScope}/${nameOrFirstPath}`
87+
: nameOrScope;
88+
89+
const packageResult = await resolveUrl(
90+
packageName + '/package.json',
91+
previousResolvedModules,
92+
);
93+
94+
if (packageResult.path) {
95+
return pathToFileURL(
96+
join(
97+
dirname(packageResult.path),
98+
!hasScope ? nameOrFirstPath : '',
99+
...pathPart,
100+
),
101+
);
96102
}
97103
}
98104

0 commit comments

Comments
 (0)