Skip to content

Commit db2d1eb

Browse files
committed
feat(@angular/build): Support Sass package importers
Enhanced Sass integration by adding support for package importers. See: https://sass-lang.com/blog/announcing-pkg-importers/ Closes: #29854
1 parent 318c164 commit db2d1eb

File tree

3 files changed

+44
-2
lines changed

3 files changed

+44
-2
lines changed

packages/angular/build/src/tools/esbuild/stylesheets/sass-language.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99
import type { OnLoadResult, PartialMessage, PartialNote, ResolveResult } from 'esbuild';
1010
import { dirname, join } from 'node:path';
1111
import { fileURLToPath, pathToFileURL } from 'node:url';
12-
import type { CanonicalizeContext, CompileResult, Exception, Syntax } from 'sass';
12+
import {
13+
type CanonicalizeContext,
14+
type CompileResult,
15+
type Exception,
16+
NodePackageImporter,
17+
type Syntax,
18+
} from 'sass';
1319
import type { SassWorkerImplementation } from '../../sass/sass-service';
1420
import { MemoryCache } from '../cache';
1521
import { StylesheetLanguage, StylesheetPluginOptions } from './stylesheet-plugin-factory';
@@ -56,7 +62,7 @@ export const SassStylesheetLanguage = Object.freeze<StylesheetLanguage>({
5662
});
5763

5864
function parsePackageName(url: string): { packageName: string; readonly pathSegments: string[] } {
59-
const parts = url.split('/');
65+
const parts = (url.startsWith('pkg:') ? url.slice(4) : url).split('/');
6066
const hasScope = parts.length >= 2 && parts[0].startsWith('@');
6167
const [nameOrScope, nameOrFirstPath, ...pathPart] = parts;
6268
const packageName = hasScope ? `${nameOrScope}/${nameOrFirstPath}` : nameOrScope;
@@ -110,6 +116,7 @@ async function compileString(
110116
futureDeprecations,
111117
quietDeps: true,
112118
importers: [
119+
new NodePackageImporter(),
113120
{
114121
findFileUrl: (url, options) =>
115122
resolutionCache.getOrCreate(url, async () => {

tests/legacy-cli/e2e.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ WEBPACK_IGNORE_TESTS = [
4646
"tests/i18n/ivy-localize-app-shell.js",
4747
"tests/i18n/ivy-localize-app-shell-service-worker.js",
4848
"tests/commands/serve/ssr-http-requests-assets.js",
49+
"tests/legacy-cli/e2e/tests/build/styles/sass-pkg-importer.js",
4950
"tests/build/prerender/http-requests-assets.js",
5051
"tests/build/prerender/error-with-sourcemaps.js",
5152
"tests/build/server-rendering/server-routes-*",
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import assert from 'node:assert';
2+
import { writeFile } from '../../../utils/fs';
3+
import { getActivePackageManager, uninstallPackage } from '../../../utils/packages';
4+
import { ng } from '../../../utils/process';
5+
import { isPrereleaseCli, updateJsonFile } from '../../../utils/project';
6+
import { appendFile } from 'node:fs/promises';
7+
import { getGlobalVariable } from '../../../utils/env';
8+
9+
export default async function () {
10+
assert(
11+
getGlobalVariable('argv')['esbuild'],
12+
'This test should not be called in the Webpack suite.',
13+
);
14+
15+
// forcibly remove in case another test doesn't clean itself up
16+
await uninstallPackage('@angular/material');
17+
18+
const isPrerelease = await isPrereleaseCli();
19+
const tag = isPrerelease ? '@next' : '';
20+
if (getActivePackageManager() === 'npm') {
21+
await appendFile('.npmrc', '\nlegacy-peer-deps=true');
22+
}
23+
24+
await ng('add', `@angular/material${tag}`, '--skip-confirmation');
25+
await Promise.all([
26+
updateJsonFile('angular.json', (workspaceJson) => {
27+
const appArchitect = workspaceJson.projects['test-project'].architect;
28+
appArchitect.build.options.styles = ['src/styles.scss'];
29+
}),
30+
writeFile('src/styles.scss', `@use 'pkg:@angular/material' as mat;`),
31+
]);
32+
33+
await ng('build');
34+
}

0 commit comments

Comments
 (0)