Skip to content

Commit a0804eb

Browse files
committed
refactor(@angular-devkit/build-angular): use parse5 version 6 to augment index
1 parent 378faa7 commit a0804eb

File tree

5 files changed

+63
-104
lines changed

5 files changed

+63
-104
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,9 @@
177177
"open": "7.0.3",
178178
"ora": "^4.0.2",
179179
"pacote": "11.1.4",
180-
"parse5": "4.0.0",
180+
"parse5": "6.0.1",
181181
"parse5-html-rewriting-stream": "6.0.1",
182+
"parse5-htmlparser2-tree-adapter": "6.0.1",
182183
"pidtree": "^0.5.0",
183184
"pidusage": "^2.0.17",
184185
"pnp-webpack-plugin": "1.6.4",

packages/angular_devkit/build_angular/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ ts_library(
147147
"@npm//minimatch",
148148
"@npm//open",
149149
"@npm//parse5",
150+
"@npm//parse5-htmlparser2-tree-adapter",
150151
"@npm//pnp-webpack-plugin",
151152
"@npm//postcss",
152153
"@npm//postcss-import",

packages/angular_devkit/build_angular/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@
4040
"mini-css-extract-plugin": "0.9.0",
4141
"minimatch": "3.0.4",
4242
"open": "7.1.0",
43-
"parse5": "4.0.0",
43+
"parse5": "6.0.1",
44+
"parse5-htmlparser2-tree-adapter": "6.0.1",
4445
"pnp-webpack-plugin": "1.6.4",
4546
"postcss": "7.0.32",
4647
"postcss-import": "12.0.1",

packages/angular_devkit/build_angular/src/angular-cli-files/utilities/index-file/augment-index-html.ts

Lines changed: 46 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { createHash } from 'crypto';
1010
import { RawSource, ReplaceSource } from 'webpack-sources';
1111

1212
const parse5 = require('parse5');
13+
const treeAdapter = require('parse5-htmlparser2-tree-adapter');
1314

1415
export type LoadOutputFileFunctionType = (file: string) => Promise<string>;
1516

@@ -90,53 +91,27 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise
9091
}
9192

9293
// Find the head and body elements
93-
const treeAdapter = parse5.treeAdapters.default;
94-
const document = parse5.parse(params.inputContent, { treeAdapter, locationInfo: true });
95-
let headElement;
96-
let bodyElement;
97-
let htmlElement;
98-
for (const docChild of document.childNodes) {
99-
if (docChild.tagName === 'html') {
100-
htmlElement = docChild;
101-
for (const htmlChild of docChild.childNodes) {
102-
if (htmlChild.tagName === 'head') {
103-
headElement = htmlChild;
104-
} else if (htmlChild.tagName === 'body') {
105-
bodyElement = htmlChild;
106-
}
107-
}
108-
}
109-
}
94+
const document = parse5.parse(params.inputContent, {
95+
treeAdapter,
96+
sourceCodeLocationInfo: true,
97+
});
98+
99+
// tslint:disable: no-any
100+
const htmlElement = document.children.find((c: any) => c.name === 'html');
101+
const headElement = htmlElement.children.find((c: any) => c.name === 'head');
102+
const bodyElement = htmlElement.children.find((c: any) => c.name === 'body');
103+
// tslint:enable: no-any
110104

111105
if (!headElement || !bodyElement) {
112106
throw new Error('Missing head and/or body elements');
113107
}
114108

115-
// Determine script insertion point
116-
let scriptInsertionPoint;
117-
if (bodyElement.__location && bodyElement.__location.endTag) {
118-
scriptInsertionPoint = bodyElement.__location.endTag.startOffset;
119-
} else {
120-
// Less accurate fallback
121-
// parse5 4.x does not provide locations if malformed html is present
122-
scriptInsertionPoint = params.inputContent.indexOf('</body>');
123-
}
124-
125-
let styleInsertionPoint;
126-
if (headElement.__location && headElement.__location.endTag) {
127-
styleInsertionPoint = headElement.__location.endTag.startOffset;
128-
} else {
129-
// Less accurate fallback
130-
// parse5 4.x does not provide locations if malformed html is present
131-
styleInsertionPoint = params.inputContent.indexOf('</head>');
132-
}
133-
134109
// Inject into the html
135110
const indexSource = new ReplaceSource(new RawSource(params.inputContent), params.input);
136111

137-
let scriptElements = '';
112+
const scriptsElements = treeAdapter.createDocumentFragment();
138113
for (const script of scripts) {
139-
const attrs: { name: string; value: string | null }[] = [
114+
const attrs: { name: string; value: string }[] = [
140115
{ name: 'src', value: (params.deployUrl || '') + script },
141116
];
142117

@@ -156,69 +131,55 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise
156131

157132
if (isNoModuleType && !isModuleType) {
158133
attrs.push(
159-
{ name: 'nomodule', value: null },
160-
{ name: 'defer', value: null },
134+
{ name: 'nomodule', value: '' },
135+
{ name: 'defer', value: '' },
161136
);
162137
} else if (isModuleType && !isNoModuleType) {
163138
attrs.push({ name: 'type', value: 'module' });
164139
} else {
165-
attrs.push({ name: 'defer', value: null });
140+
attrs.push({ name: 'defer', value: '' });
166141
}
167142
} else {
168-
attrs.push({ name: 'defer', value: null });
143+
attrs.push({ name: 'defer', value: '' });
169144
}
170145

171146
if (params.sri) {
172147
const content = await loadOutputFile(script);
173-
attrs.push(..._generateSriAttributes(content));
148+
attrs.push(_generateSriAttributes(content));
174149
}
175150

176-
const attributes = attrs
177-
.map(attr => (attr.value === null ? attr.name : `${attr.name}="${attr.value}"`))
178-
.join(' ');
179-
scriptElements += `<script ${attributes}></script>`;
151+
const baseElement = treeAdapter.createElement('script', undefined, attrs);
152+
treeAdapter.setTemplateContent(scriptsElements, baseElement);
180153
}
181154

182-
indexSource.insert(scriptInsertionPoint, scriptElements);
155+
indexSource.insert(
156+
// parse5 does not provide locations if malformed html is present
157+
bodyElement.sourceCodeLocation?.endTag?.startOffset || params.inputContent.indexOf('</body>'),
158+
parse5.serialize(scriptsElements, { treeAdapter }).replace(/\=""/g, ''),
159+
);
183160

184161
// Adjust base href if specified
185162
if (typeof params.baseHref == 'string') {
186-
let baseElement;
187-
for (const headChild of headElement.childNodes) {
188-
if (headChild.tagName === 'base') {
189-
baseElement = headChild;
190-
}
191-
}
192-
163+
// tslint:disable-next-line: no-any
164+
let baseElement = headElement.children.find((t: any) => t.name === 'base');
193165
const baseFragment = treeAdapter.createDocumentFragment();
194166

195167
if (!baseElement) {
196168
baseElement = treeAdapter.createElement('base', undefined, [
197169
{ name: 'href', value: params.baseHref },
198170
]);
199171

200-
treeAdapter.appendChild(baseFragment, baseElement);
172+
treeAdapter.setTemplateContent(baseFragment, baseElement);
201173
indexSource.insert(
202-
headElement.__location.startTag.endOffset,
174+
headElement.sourceCodeLocation.startTag.endOffset,
203175
parse5.serialize(baseFragment, { treeAdapter }),
204176
);
205177
} else {
206-
let hrefAttribute;
207-
for (const attribute of baseElement.attrs) {
208-
if (attribute.name === 'href') {
209-
hrefAttribute = attribute;
210-
}
211-
}
212-
if (hrefAttribute) {
213-
hrefAttribute.value = params.baseHref;
214-
} else {
215-
baseElement.attrs.push({ name: 'href', value: params.baseHref });
216-
}
217-
218-
treeAdapter.appendChild(baseFragment, baseElement);
178+
baseElement.attribs['href'] = params.baseHref;
179+
treeAdapter.setTemplateContent(baseFragment, baseElement);
219180
indexSource.replace(
220-
baseElement.__location.startOffset,
221-
baseElement.__location.endOffset,
181+
baseElement.sourceCodeLocation.startOffset,
182+
baseElement.sourceCodeLocation.endOffset,
222183
parse5.serialize(baseFragment, { treeAdapter }),
223184
);
224185
}
@@ -237,38 +198,31 @@ export async function augmentIndexHtml(params: AugmentIndexHtmlOptions): Promise
237198

238199
if (params.sri) {
239200
const content = await loadOutputFile(stylesheet);
240-
attrs.push(..._generateSriAttributes(content));
201+
attrs.push(_generateSriAttributes(content));
241202
}
242203

243204
const element = treeAdapter.createElement('link', undefined, attrs);
244-
treeAdapter.appendChild(styleElements, element);
205+
treeAdapter.setTemplateContent(styleElements, element);
245206
}
246207

247-
indexSource.insert(styleInsertionPoint, parse5.serialize(styleElements, { treeAdapter }));
208+
indexSource.insert(
209+
// parse5 does not provide locations if malformed html is present
210+
headElement.sourceCodeLocation?.endTag?.startOffset || params.inputContent.indexOf('</head>'),
211+
parse5.serialize(styleElements, { treeAdapter }),
212+
);
248213

249214
// Adjust document locale if specified
250215
if (typeof params.lang == 'string') {
251-
252216
const htmlFragment = treeAdapter.createDocumentFragment();
217+
htmlElement.attribs['lang'] = params.lang;
253218

254-
let langAttribute;
255-
for (const attribute of htmlElement.attrs) {
256-
if (attribute.name === 'lang') {
257-
langAttribute = attribute;
258-
}
259-
}
260-
if (langAttribute) {
261-
langAttribute.value = params.lang;
262-
} else {
263-
htmlElement.attrs.push({ name: 'lang', value: params.lang });
264-
}
265219
// we want only openning tag
266-
htmlElement.childNodes = [];
220+
htmlElement.children = [];
267221

268-
treeAdapter.appendChild(htmlFragment, htmlElement);
222+
treeAdapter.setTemplateContent(htmlFragment, htmlElement);
269223
indexSource.replace(
270-
htmlElement.__location.startTag.startOffset,
271-
htmlElement.__location.startTag.endOffset - 1,
224+
htmlElement.sourceCodeLocation.startTag.startOffset,
225+
htmlElement.sourceCodeLocation.startTag.endOffset - 1,
272226
parse5.serialize(htmlFragment, { treeAdapter }).replace('</html>', ''),
273227
);
274228
}
@@ -282,5 +236,5 @@ function _generateSriAttributes(content: string) {
282236
.update(content, 'utf8')
283237
.digest('base64');
284238

285-
return [{ name: 'integrity', value: `${algo}-${hash}` }];
239+
return { name: 'integrity', value: `${algo}-${hash}` };
286240
}

yarn.lock

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9479,33 +9479,35 @@ [email protected]:
94799479
parse5 "^6.0.1"
94809480
parse5-sax-parser "^6.0.1"
94819481

9482+
9483+
version "6.0.1"
9484+
resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
9485+
integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
9486+
dependencies:
9487+
parse5 "^6.0.1"
9488+
94829489
parse5-sax-parser@^6.0.1:
94839490
version "6.0.1"
94849491
resolved "https://registry.yarnpkg.com/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz#98b4d366b5b266a7cd90b4b58906667af882daba"
94859492
integrity sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==
94869493
dependencies:
94879494
parse5 "^6.0.1"
94889495

9489-
9490-
version "4.0.0"
9491-
resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
9492-
integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==
9493-
94949496
94959497
version "5.1.0"
94969498
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2"
94979499
integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==
94989500

9501+
[email protected], parse5@^6.0.1:
9502+
version "6.0.1"
9503+
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
9504+
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
9505+
94999506
parse5@^5.0.0:
95009507
version "5.1.1"
95019508
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
95029509
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
95039510

9504-
parse5@^6.0.1:
9505-
version "6.0.1"
9506-
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
9507-
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
9508-
95099511
95109512
version "0.0.5"
95119513
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"

0 commit comments

Comments
 (0)