Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): conditionally enable deprecated L…
Browse files Browse the repository at this point in the history
…ess 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.
  • Loading branch information
clydin authored and alan-agius4 committed Oct 11, 2023
1 parent c74e618 commit c12f98f
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function createStylesheetBundleOptions(
preserveSymlinks: options.preserveSymlinks,
external: options.externalDependencies,
publicPath: options.publicPath,
conditions: ['style', 'sass'],
conditions: ['style', 'sass', 'less'],
mainFields: ['style', 'sass'],
plugins: [
pluginFactory.create(SassStylesheetLanguage),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

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

Expand All @@ -32,7 +32,13 @@ export const LessStylesheetLanguage = Object.freeze<StylesheetLanguage>({
componentFilter: /^less;/,
fileFilter: /\.less$/,
process(data, file, _, options, build) {
return compileString(data, file, options, build.resolve.bind(build));
return compileString(
data,
file,
options,
build.resolve.bind(build),
/* unsafeInlineJavaScript */ false,
);
},
});

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

Expand Down Expand Up @@ -92,6 +99,7 @@ async function compileString(
paths: options.includePaths,
plugins: [resolverPlugin],
rewriteUrls: 'all',
javascriptEnabled: unsafeInlineJavaScript,
sourceMap: options.sourcemap
? {
sourceMapFileInline: true,
Expand All @@ -107,17 +115,40 @@ async function compileString(
};
} catch (error) {
if (isLessException(error)) {
// Retry with a warning for less files requiring the deprecated inline JavaScript option
if (error.message.includes('Inline JavaScript is not enabled.')) {
const withJsResult = await compileString(
data,
filename,
options,
resolver,
/* unsafeInlineJavaScript */ true,
);
withJsResult.warnings = [
{
text: 'Deprecated inline execution of JavaScript has been enabled ("javascriptEnabled")',
location: convertExceptionLocation(error),
notes: [
{
location: null,
text: 'JavaScript found within less stylesheets may be executed at build time. [https://lesscss.org/usage/#less-options]',
},
{
location: null,
text: 'Support for "javascriptEnabled" may be removed from the Angular CLI starting with Angular v19.',
},
],
},
];

return withJsResult;
}

return {
errors: [
{
text: error.message,
location: {
file: error.filename,
line: error.line,
column: error.column,
// Middle element represents the line containing the error
lineText: error.extract && error.extract[Math.trunc(error.extract.length / 2)],
},
location: convertExceptionLocation(error),
},
],
loader: 'css',
Expand All @@ -127,3 +158,13 @@ async function compileString(
throw error;
}
}

function convertExceptionLocation(exception: LessException): Partial<Location> {
return {
file: exception.filename,
line: exception.line,
column: exception.column,
// Middle element represents the line containing the exception
lineText: exception.extract && exception.extract[Math.trunc(exception.extract.length / 2)],
};
}

0 comments on commit c12f98f

Please sign in to comment.