Skip to content

Commit ce46eca

Browse files
clydinangular-robot[bot]
authored andcommitted
feat(@angular-devkit/build-angular): support module resolution with less stylesheets in esbuild builder
When using the esbuild-based browser application builder with Less stylesheets, import rules will now attempt to perform node package resolution if the import cannot be found as a relative path. Built-in Less resolution is performed first to both avoid unnecessary node module resolution overhead when not needed and also ensure Less relative import semantics continue to be supported.
1 parent c2d2da4 commit ce46eca

File tree

1 file changed

+48
-2
lines changed
  • packages/angular_devkit/build_angular/src/builders/browser-esbuild

1 file changed

+48
-2
lines changed

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

+48-2
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ export function createLessPlugin(options: LessPluginOptions): Plugin {
4747

4848
const [, , filePath] = args.path.split(';', 3);
4949

50-
return compileString(data, filePath, options);
50+
return compileString(data, filePath, options, build.resolve.bind(build));
5151
});
5252

5353
// Add a load callback to support files from disk
5454
build.onLoad({ filter: /\.less$/ }, async (args) => {
5555
const data = await readFile(args.path, 'utf-8');
5656

57-
return compileString(data, args.path, options);
57+
return compileString(data, args.path, options, build.resolve.bind(build));
5858
});
5959
},
6060
};
@@ -64,13 +64,59 @@ async function compileString(
6464
data: string,
6565
filename: string,
6666
options: LessPluginOptions,
67+
resolver: PluginBuild['resolve'],
6768
): Promise<OnLoadResult> {
6869
const less = (lessPreprocessor ??= (await import('less')).default);
6970

71+
const resolverPlugin: Less.Plugin = {
72+
install({ FileManager }, pluginManager): void {
73+
const resolverFileManager = new (class extends FileManager {
74+
override supportsSync(): boolean {
75+
return false;
76+
}
77+
78+
override supports(): boolean {
79+
return true;
80+
}
81+
82+
override async loadFile(
83+
filename: string,
84+
currentDirectory: string,
85+
options: Less.LoadFileOptions,
86+
environment: Less.Environment,
87+
): Promise<Less.FileLoadResult> {
88+
// Attempt direct loading as a relative path to avoid resolution overhead
89+
const directResult = this.loadFileSync(filename, currentDirectory, options, environment);
90+
if ('contents' in directResult) {
91+
return directResult;
92+
}
93+
94+
// Attempt a full resolution if not found
95+
const fullResult = await resolver(filename, {
96+
kind: 'import-rule',
97+
resolveDir: currentDirectory,
98+
});
99+
if (fullResult.path) {
100+
return {
101+
filename: fullResult.path,
102+
contents: await readFile(fullResult.path, 'utf-8'),
103+
};
104+
}
105+
106+
// Otherwise error by throwing the failing direct result
107+
throw directResult.error;
108+
}
109+
})();
110+
111+
pluginManager.addFileManager(resolverFileManager);
112+
},
113+
};
114+
70115
try {
71116
const result = await less.render(data, {
72117
filename,
73118
paths: options.includePaths,
119+
plugins: [resolverPlugin],
74120
rewriteUrls: 'all',
75121
sourceMap: options.sourcemap
76122
? {

0 commit comments

Comments
 (0)