Skip to content

Commit c12f98f

Browse files
clydinalan-agius4
authored andcommitted
fix(@angular-devkit/build-angular): conditionally enable deprecated Less stylesheet JavaScript support
When a Less stylesheet is detected to require the deprecated `javascriptEnabled` Less option, the option is enabled for the stylesheet and a warning is issued to inform the user of the behavior change. The Less `javascriptEnabled` option has been deprecated and disabled by default since Less v3 (https://github.com/less/less.js/releases/tag/v3.0.0). When enabled, the `javascriptEnabled` option allows JavaScript within Less stylesheets to be executed at build time. Less option reference: https://lesscss.org/usage/#less-options This provides similar behavior to the default Webpack-based build system. However, the Webpack-based build system currently unconditionally enables the option.
1 parent c74e618 commit c12f98f

File tree

2 files changed

+51
-10
lines changed

2 files changed

+51
-10
lines changed

packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/bundle-options.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export function createStylesheetBundleOptions(
6464
preserveSymlinks: options.preserveSymlinks,
6565
external: options.externalDependencies,
6666
publicPath: options.publicPath,
67-
conditions: ['style', 'sass'],
67+
conditions: ['style', 'sass', 'less'],
6868
mainFields: ['style', 'sass'],
6969
plugins: [
7070
pluginFactory.create(SassStylesheetLanguage),

packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/less-language.ts

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import type { OnLoadResult, PluginBuild } from 'esbuild';
9+
import type { Location, OnLoadResult, PluginBuild } from 'esbuild';
1010
import { readFile } from 'node:fs/promises';
1111
import { StylesheetLanguage, StylesheetPluginOptions } from './stylesheet-plugin-factory';
1212

@@ -32,7 +32,13 @@ export const LessStylesheetLanguage = Object.freeze<StylesheetLanguage>({
3232
componentFilter: /^less;/,
3333
fileFilter: /\.less$/,
3434
process(data, file, _, options, build) {
35-
return compileString(data, file, options, build.resolve.bind(build));
35+
return compileString(
36+
data,
37+
file,
38+
options,
39+
build.resolve.bind(build),
40+
/* unsafeInlineJavaScript */ false,
41+
);
3642
},
3743
});
3844

@@ -41,6 +47,7 @@ async function compileString(
4147
filename: string,
4248
options: StylesheetPluginOptions,
4349
resolver: PluginBuild['resolve'],
50+
unsafeInlineJavaScript: boolean,
4451
): Promise<OnLoadResult> {
4552
const less = (lessPreprocessor ??= (await import('less')).default);
4653

@@ -92,6 +99,7 @@ async function compileString(
9299
paths: options.includePaths,
93100
plugins: [resolverPlugin],
94101
rewriteUrls: 'all',
102+
javascriptEnabled: unsafeInlineJavaScript,
95103
sourceMap: options.sourcemap
96104
? {
97105
sourceMapFileInline: true,
@@ -107,17 +115,40 @@ async function compileString(
107115
};
108116
} catch (error) {
109117
if (isLessException(error)) {
118+
// Retry with a warning for less files requiring the deprecated inline JavaScript option
119+
if (error.message.includes('Inline JavaScript is not enabled.')) {
120+
const withJsResult = await compileString(
121+
data,
122+
filename,
123+
options,
124+
resolver,
125+
/* unsafeInlineJavaScript */ true,
126+
);
127+
withJsResult.warnings = [
128+
{
129+
text: 'Deprecated inline execution of JavaScript has been enabled ("javascriptEnabled")',
130+
location: convertExceptionLocation(error),
131+
notes: [
132+
{
133+
location: null,
134+
text: 'JavaScript found within less stylesheets may be executed at build time. [https://lesscss.org/usage/#less-options]',
135+
},
136+
{
137+
location: null,
138+
text: 'Support for "javascriptEnabled" may be removed from the Angular CLI starting with Angular v19.',
139+
},
140+
],
141+
},
142+
];
143+
144+
return withJsResult;
145+
}
146+
110147
return {
111148
errors: [
112149
{
113150
text: error.message,
114-
location: {
115-
file: error.filename,
116-
line: error.line,
117-
column: error.column,
118-
// Middle element represents the line containing the error
119-
lineText: error.extract && error.extract[Math.trunc(error.extract.length / 2)],
120-
},
151+
location: convertExceptionLocation(error),
121152
},
122153
],
123154
loader: 'css',
@@ -127,3 +158,13 @@ async function compileString(
127158
throw error;
128159
}
129160
}
161+
162+
function convertExceptionLocation(exception: LessException): Partial<Location> {
163+
return {
164+
file: exception.filename,
165+
line: exception.line,
166+
column: exception.column,
167+
// Middle element represents the line containing the exception
168+
lineText: exception.extract && exception.extract[Math.trunc(exception.extract.length / 2)],
169+
};
170+
}

0 commit comments

Comments
 (0)