Skip to content

Commit d23a168

Browse files
alan-agius4dgp1130
authored andcommitted
feat(@angular-devkit/build-angular): validate file extensions for scripts and styles options
In some cases unexpected files may be provided which can cause to unsupported or broken behaviour. One such use-case is users can provide TypeScript files as `scripts` input, this would not be processed by the TypeScript compiler, see: #17125 and would cause the build to fail with a an unhelpful error message during optimization as the JS optimizers cannot parse TypeScript input. BREAKING CHANGE: `browser` and `karma` builders `script` and `styles` options input files extensions are now validated. Valid extensions for `scripts` are: - `.js` - `.cjs` - `.mjs` - `.jsx` - `.cjsx` - `.mjsx` Valid extensions for `styles` are: - `.css` - `.less` - `.sass` - `.scss` - `.styl`
1 parent ed13222 commit d23a168

File tree

8 files changed

+137
-95
lines changed

8 files changed

+137
-95
lines changed

goldens/public-api/angular_devkit/build_angular/src/index.md

+4-14
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ export interface BrowserBuilderOptions {
5959
preserveSymlinks?: boolean;
6060
progress?: boolean;
6161
resourcesOutputPath?: string;
62-
scripts?: ExtraEntryPoint[];
62+
scripts?: ScriptElement[];
6363
serviceWorker?: boolean;
6464
// @deprecated
6565
showCircularDependencies?: boolean;
6666
sourceMap?: SourceMapUnion;
6767
statsJson?: boolean;
6868
stylePreprocessorOptions?: StylePreprocessorOptions;
69-
styles?: ExtraEntryPoint[];
69+
styles?: StyleElement[];
7070
subresourceIntegrity?: boolean;
7171
tsConfig: string;
7272
vendorChunk?: boolean;
@@ -155,16 +155,6 @@ export type ExecutionTransformer<T> = (input: T) => T | Promise<T>;
155155
// @public (undocumented)
156156
export type ExtractI18nBuilderOptions = Schema_2 & JsonObject;
157157

158-
// @public (undocumented)
159-
export type ExtraEntryPoint = ExtraEntryPointObject | string;
160-
161-
// @public (undocumented)
162-
export interface ExtraEntryPointObject {
163-
bundleName?: string;
164-
inject?: boolean;
165-
input: string;
166-
}
167-
168158
// @public (undocumented)
169159
export interface FileReplacement {
170160
// (undocumented)
@@ -193,10 +183,10 @@ export interface KarmaBuilderOptions {
193183
preserveSymlinks?: boolean;
194184
progress?: boolean;
195185
reporters?: string[];
196-
scripts?: ExtraEntryPoint_2[];
186+
scripts?: ScriptElement_2[];
197187
sourceMap?: SourceMapUnion_2;
198188
stylePreprocessorOptions?: StylePreprocessorOptions_2;
199-
styles?: ExtraEntryPoint_2[];
189+
styles?: StyleElement_2[];
200190
tsConfig: string;
201191
watch?: boolean;
202192
webWorkerTsConfig?: string;

packages/angular_devkit/build_angular/src/builders/browser/schema.json

+58-31
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,71 @@
2929
"type": "array",
3030
"default": [],
3131
"items": {
32-
"$ref": "#/definitions/extraEntryPoint"
32+
"oneOf": [
33+
{
34+
"type": "object",
35+
"properties": {
36+
"input": {
37+
"type": "string",
38+
"description": "The file to include.",
39+
"pattern": "\\.[cm]?jsx?$"
40+
},
41+
"bundleName": {
42+
"type": "string",
43+
"pattern": "^[\\w\\-.]*$",
44+
"description": "The bundle name for this extra entry point."
45+
},
46+
"inject": {
47+
"type": "boolean",
48+
"description": "If the bundle will be referenced in the HTML file.",
49+
"default": true
50+
}
51+
},
52+
"additionalProperties": false,
53+
"required": ["input"]
54+
},
55+
{
56+
"type": "string",
57+
"description": "The file to include.",
58+
"pattern": "\\.[cm]?jsx?$"
59+
}
60+
]
3361
}
3462
},
3563
"styles": {
3664
"description": "Global styles to be included in the build.",
3765
"type": "array",
3866
"default": [],
3967
"items": {
40-
"$ref": "#/definitions/extraEntryPoint"
68+
"oneOf": [
69+
{
70+
"type": "object",
71+
"properties": {
72+
"input": {
73+
"type": "string",
74+
"description": "The file to include.",
75+
"pattern": "\\.(?:css|scss|sass|less|styl)$"
76+
},
77+
"bundleName": {
78+
"type": "string",
79+
"pattern": "^[\\w\\-.]*$",
80+
"description": "The bundle name for this extra entry point."
81+
},
82+
"inject": {
83+
"type": "boolean",
84+
"description": "If the bundle will be referenced in the HTML file.",
85+
"default": true
86+
}
87+
},
88+
"additionalProperties": false,
89+
"required": ["input"]
90+
},
91+
{
92+
"type": "string",
93+
"description": "The file to include.",
94+
"pattern": "\\.(?:css|scss|sass|less|styl)$"
95+
}
96+
]
4197
}
4298
},
4399
"inlineStyleLanguage": {
@@ -437,35 +493,6 @@
437493
}
438494
]
439495
},
440-
"extraEntryPoint": {
441-
"oneOf": [
442-
{
443-
"type": "object",
444-
"properties": {
445-
"input": {
446-
"type": "string",
447-
"description": "The file to include."
448-
},
449-
"bundleName": {
450-
"type": "string",
451-
"pattern": "^[\\w\\-.]*$",
452-
"description": "The bundle name for this extra entry point."
453-
},
454-
"inject": {
455-
"type": "boolean",
456-
"description": "If the bundle will be referenced in the HTML file.",
457-
"default": true
458-
}
459-
},
460-
"additionalProperties": false,
461-
"required": ["input"]
462-
},
463-
{
464-
"type": "string",
465-
"description": "The file to include."
466-
}
467-
]
468-
},
469496
"budget": {
470497
"type": "object",
471498
"properties": {

packages/angular_devkit/build_angular/src/builders/karma/schema.json

+58-31
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,71 @@
3333
"type": "array",
3434
"default": [],
3535
"items": {
36-
"$ref": "#/definitions/extraEntryPoint"
36+
"oneOf": [
37+
{
38+
"type": "object",
39+
"properties": {
40+
"input": {
41+
"type": "string",
42+
"description": "The file to include.",
43+
"pattern": "\\.[cm]?jsx?$"
44+
},
45+
"bundleName": {
46+
"type": "string",
47+
"pattern": "^[\\w\\-.]*$",
48+
"description": "The bundle name for this extra entry point."
49+
},
50+
"inject": {
51+
"type": "boolean",
52+
"description": "If the bundle will be referenced in the HTML file.",
53+
"default": true
54+
}
55+
},
56+
"additionalProperties": false,
57+
"required": ["input"]
58+
},
59+
{
60+
"type": "string",
61+
"description": "The file to include.",
62+
"pattern": "\\.[cm]?jsx?$"
63+
}
64+
]
3765
}
3866
},
3967
"styles": {
4068
"description": "Global styles to be included in the build.",
4169
"type": "array",
4270
"default": [],
4371
"items": {
44-
"$ref": "#/definitions/extraEntryPoint"
72+
"oneOf": [
73+
{
74+
"type": "object",
75+
"properties": {
76+
"input": {
77+
"type": "string",
78+
"description": "The file to include.",
79+
"pattern": "\\.(?:css|scss|sass|less|styl)$"
80+
},
81+
"bundleName": {
82+
"type": "string",
83+
"pattern": "^[\\w\\-.]*$",
84+
"description": "The bundle name for this extra entry point."
85+
},
86+
"inject": {
87+
"type": "boolean",
88+
"description": "If the bundle will be referenced in the HTML file.",
89+
"default": true
90+
}
91+
},
92+
"additionalProperties": false,
93+
"required": ["input"]
94+
},
95+
{
96+
"type": "string",
97+
"description": "The file to include.",
98+
"pattern": "\\.(?:css|scss|sass|less|styl)$"
99+
}
100+
]
45101
}
46102
},
47103
"inlineStyleLanguage": {
@@ -218,35 +274,6 @@
218274
"type": "string"
219275
}
220276
]
221-
},
222-
"extraEntryPoint": {
223-
"oneOf": [
224-
{
225-
"type": "object",
226-
"properties": {
227-
"input": {
228-
"type": "string",
229-
"description": "The file to include."
230-
},
231-
"bundleName": {
232-
"type": "string",
233-
"pattern": "^[\\w\\-.]*$",
234-
"description": "The bundle name for this extra entry point."
235-
},
236-
"inject": {
237-
"type": "boolean",
238-
"description": "If the bundle will be referenced in the HTML file.",
239-
"default": true
240-
}
241-
},
242-
"additionalProperties": false,
243-
"required": ["input"]
244-
},
245-
{
246-
"type": "string",
247-
"description": "The file to include."
248-
}
249-
]
250277
}
251278
}
252279
}

packages/angular_devkit/build_angular/src/index.ts

-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ export {
1313
AssetPatternClass as AssetPatternObject,
1414
Budget,
1515
CrossOrigin,
16-
ExtraEntryPoint,
17-
ExtraEntryPointClass as ExtraEntryPointObject,
1816
FileReplacement,
1917
OptimizationClass as OptimizationObject,
2018
OptimizationUnion,

packages/angular_devkit/build_angular/src/utils/build-options.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import {
1212
AssetPatternClass,
1313
Budget,
1414
CrossOrigin,
15-
ExtraEntryPoint,
1615
I18NTranslation,
1716
IndexUnion,
1817
InlineStyleLanguage,
1918
Localize,
19+
ScriptElement,
2020
SourceMapClass,
21+
StyleElement,
2122
} from '../builders/browser/schema';
2223
import { Schema as DevServerSchema } from '../builders/dev-server/schema';
2324
import { NormalizedCachedOptions } from './normalize-cache';
@@ -61,8 +62,8 @@ export interface BuildOptions {
6162
polyfills?: string;
6263
budgets: Budget[];
6364
assets: AssetPatternClass[];
64-
scripts: ExtraEntryPoint[];
65-
styles: ExtraEntryPoint[];
65+
scripts: ScriptElement[];
66+
styles: StyleElement[];
6667
stylePreprocessorOptions?: { includePaths: string[] };
6768
platform?: 'browser' | 'server';
6869
fileReplacements: NormalizedFileReplacement[];

packages/angular_devkit/build_angular/src/utils/package-chunk-sort.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,21 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import { ExtraEntryPoint } from '../builders/browser/schema';
9+
import { ScriptElement, StyleElement } from '../builders/browser/schema';
1010
import { normalizeExtraEntryPoints } from '../webpack/utils/helpers';
1111

1212
export type EntryPointsType = [name: string, isModule: boolean];
1313

1414
export function generateEntryPoints(options: {
15-
styles: ExtraEntryPoint[];
16-
scripts: ExtraEntryPoint[];
15+
styles: StyleElement[];
16+
scripts: ScriptElement[];
1717
isHMREnabled?: boolean;
1818
}): EntryPointsType[] {
1919
// Add all styles/scripts, except lazy-loaded ones.
20-
const extraEntryPoints = (extraEntryPoints: ExtraEntryPoint[], defaultBundleName: string) => {
20+
const extraEntryPoints = (
21+
extraEntryPoints: (ScriptElement | ScriptElement)[],
22+
defaultBundleName: string,
23+
) => {
2124
const entryPoints = normalizeExtraEntryPoints(extraEntryPoints, defaultBundleName)
2225
.filter((entry) => entry.inject)
2326
.map((entry) => entry.bundleName);

packages/angular_devkit/build_angular/src/webpack/configs/styles.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import * as fs from 'fs';
1010
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
1111
import * as path from 'path';
1212
import { Configuration, RuleSetUseItem } from 'webpack';
13-
import { ExtraEntryPoint } from '../../builders/browser/schema';
13+
import { StyleElement } from '../../builders/browser/schema';
1414
import { SassWorkerImplementation } from '../../sass/sass-service';
1515
import { WebpackConfigOptions } from '../../utils/build-options';
1616
import {
@@ -27,7 +27,7 @@ import {
2727
} from '../utils/helpers';
2828

2929
function resolveGlobalStyles(
30-
styleEntrypoints: ExtraEntryPoint[],
30+
styleEntrypoints: StyleElement[],
3131
root: string,
3232
preserveSymlinks: boolean,
3333
): { entryPoints: Record<string, string[]>; noInjectNames: string[]; paths: string[] } {

packages/angular_devkit/build_angular/src/webpack/utils/helpers.ts

+4-8
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@ import glob from 'glob';
1313
import * as path from 'path';
1414
import { ScriptTarget } from 'typescript';
1515
import type { Configuration, WebpackOptionsNormalized } from 'webpack';
16-
import {
17-
AssetPatternClass,
18-
ExtraEntryPoint,
19-
ExtraEntryPointClass,
20-
} from '../../builders/browser/schema';
16+
import { AssetPatternClass, ScriptElement, StyleElement } from '../../builders/browser/schema';
2117
import { WebpackConfigOptions } from '../../utils/build-options';
2218
import { VERSION } from '../../utils/package-version';
2319

@@ -49,10 +45,10 @@ export function getOutputHashFormat(option: string, length = 20): HashFormat {
4945
return hashFormats[option] || hashFormats['none'];
5046
}
5147

52-
export type NormalizedEntryPoint = Required<ExtraEntryPointClass>;
48+
export type NormalizedEntryPoint = Required<Exclude<ScriptElement | StyleElement, string>>;
5349

5450
export function normalizeExtraEntryPoints(
55-
extraEntryPoints: ExtraEntryPoint[],
51+
extraEntryPoints: (ScriptElement | StyleElement)[],
5652
defaultBundleName: string,
5753
): NormalizedEntryPoint[] {
5854
return extraEntryPoints.map((entry) => {
@@ -160,7 +156,7 @@ export function getCacheSettings(
160156

161157
export function globalScriptsByBundleName(
162158
root: string,
163-
scripts: ExtraEntryPoint[],
159+
scripts: ScriptElement[],
164160
): { bundleName: string; inject: boolean; paths: string[] }[] {
165161
return normalizeExtraEntryPoints(scripts, 'scripts').reduce(
166162
(prev: { bundleName: string; paths: string[]; inject: boolean }[], curr) => {

0 commit comments

Comments
 (0)