From d85cbf60ecf710de3b8fe7f01f45fd3a234d4fbd Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Fri, 4 Aug 2023 15:38:35 -0400 Subject: [PATCH] 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. --- .../esbuild/stylesheets/bundle-options.ts | 2 +- .../esbuild/stylesheets/less-language.ts | 59 ++++++++++++++++--- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/bundle-options.ts b/packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/bundle-options.ts index cca5270ea56e..7f6c1e5841bd 100644 --- a/packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/bundle-options.ts +++ b/packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/bundle-options.ts @@ -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), diff --git a/packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/less-language.ts b/packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/less-language.ts index ce5d104d5491..4d86a6424e6d 100644 --- a/packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/less-language.ts +++ b/packages/angular_devkit/build_angular/src/tools/esbuild/stylesheets/less-language.ts @@ -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'; @@ -32,7 +32,13 @@ export const LessStylesheetLanguage = Object.freeze({ 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, + ); }, }); @@ -41,6 +47,7 @@ async function compileString( filename: string, options: StylesheetPluginOptions, resolver: PluginBuild['resolve'], + unsafeInlineJavaScript: boolean, ): Promise { const less = (lessPreprocessor ??= (await import('less')).default); @@ -92,6 +99,7 @@ async function compileString( paths: options.includePaths, plugins: [resolverPlugin], rewriteUrls: 'all', + javascriptEnabled: unsafeInlineJavaScript, sourceMap: options.sourcemap ? { sourceMapFileInline: true, @@ -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', @@ -127,3 +158,13 @@ async function compileString( throw error; } } + +function convertExceptionLocation(exception: LessException): Partial { + 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)], + }; +}