Skip to content

Commit dcf1635

Browse files
authored
perf: improve parsing speed
1 parent 3ac78f4 commit dcf1635

1 file changed

Lines changed: 84 additions & 39 deletions

File tree

src/utils.js

Lines changed: 84 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -353,23 +353,27 @@ export function parseSrc(input) {
353353
throw new Error("Must be non-empty");
354354
}
355355

356-
let startOffset = 0;
357-
let value = input;
356+
let start = 0;
357+
for (; start < input.length && isASCIIWhitespace(input[start]); start++);
358358

359-
while (isASCIIWhitespace(value.substring(0, 1))) {
360-
startOffset += 1;
361-
value = value.substring(1, value.length);
359+
if (start === input.length) {
360+
throw new Error("Must be non-empty");
362361
}
363362

364-
while (isASCIIWhitespace(value.substring(value.length - 1, value.length))) {
365-
value = value.substring(0, value.length - 1);
366-
}
363+
let end = input.length - 1;
364+
for (; end > -1 && isASCIIWhitespace(input[end]); end--);
365+
end += 1;
367366

368-
if (!value) {
369-
throw new Error("Must be non-empty");
367+
let value = input;
368+
if (start !== 0 || end !== value.length) {
369+
value = value.substring(start, end);
370+
371+
if (!value) {
372+
throw new Error("Must be non-empty");
373+
}
370374
}
371375

372-
return { value, startOffset };
376+
return { value, startOffset: start };
373377
}
374378

375379
const WINDOWS_ABS_PATH_REGEXP = /^[a-zA-Z]:[\\/]|^\\\\/;
@@ -572,7 +576,7 @@ function linkHrefFilter(tag, attribute, attributes) {
572576

573577
rel = rel.toLowerCase();
574578

575-
const usedRels = rel.split(" ").filter((value) => value);
579+
const usedRels = rel.split(" ").filter(Boolean);
576580
const allowedRels = [
577581
"stylesheet",
578582
"icon",
@@ -585,7 +589,7 @@ function linkHrefFilter(tag, attribute, attributes) {
585589
"preload",
586590
];
587591

588-
return allowedRels.filter((value) => usedRels.includes(value)).length > 0;
592+
return allowedRels.some((value) => usedRels.includes(value));
589593
}
590594

591595
const META = new Map([
@@ -695,7 +699,16 @@ export function srcType(options) {
695699
);
696700
}
697701

698-
source = c0ControlCodesExclude(source);
702+
try {
703+
source = c0ControlCodesExclude(source);
704+
} catch (error) {
705+
throw new HtmlSourceError(
706+
`Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`,
707+
options.attributeStartOffset,
708+
options.attributeEndOffset,
709+
options.html
710+
);
711+
}
699712

700713
if (!isUrlRequestable(source.value)) {
701714
return [];
@@ -726,7 +739,16 @@ export function srcsetType(options) {
726739
sourceSet.forEach((sourceItem) => {
727740
let { source } = sourceItem;
728741

729-
source = c0ControlCodesExclude(source);
742+
try {
743+
source = c0ControlCodesExclude(source);
744+
} catch (error) {
745+
throw new HtmlSourceError(
746+
`Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`,
747+
options.attributeStartOffset,
748+
options.attributeEndOffset,
749+
options.html
750+
);
751+
}
730752

731753
if (!isUrlRequestable(source.value)) {
732754
return false;
@@ -778,7 +800,16 @@ function metaContentType(options) {
778800
);
779801
}
780802

781-
source = c0ControlCodesExclude(source);
803+
try {
804+
source = c0ControlCodesExclude(source);
805+
} catch (error) {
806+
throw new HtmlSourceError(
807+
`Bad value for attribute "icon-uri" on element "${options.tag}": ${error.message}`,
808+
options.attributeStartOffset,
809+
options.attributeEndOffset,
810+
options.html
811+
);
812+
}
782813

783814
({ value } = source);
784815
startOffset += source.startOffset;
@@ -805,7 +836,7 @@ function metaContentType(options) {
805836
// let source;
806837
//
807838
// try {
808-
// source = parseSrc(options.value);
839+
// source = trimASCIIWhitespace(options.value);
809840
// } catch (error) {
810841
// throw new HtmlSourceError(
811842
// `Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`,
@@ -815,7 +846,16 @@ function metaContentType(options) {
815846
// );
816847
// }
817848
//
818-
// source = c0ControlCodesExclude(source);
849+
// try {
850+
// source = c0ControlCodesExclude(source);
851+
// } catch (error) {
852+
// throw new HtmlSourceError(
853+
// `Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`,
854+
// options.attributeStartOffset,
855+
// options.attributeEndOffset,
856+
// options.html
857+
// );
858+
// }
819859
//
820860
// if (!isUrlRequestable(source.value)) {
821861
// return [];
@@ -1062,17 +1102,13 @@ function normalizeSourcesList(sources) {
10621102
for (const source of sources) {
10631103
if (source === "...") {
10641104
for (const [tag, attributes] of defaultSources.entries()) {
1065-
let newAttributes;
1066-
10671105
const existingAttributes = result.get(tag);
10681106

10691107
if (existingAttributes) {
1070-
newAttributes = new Map([...existingAttributes, ...attributes]);
1108+
attributes.forEach(([k, v]) => existingAttributes.set(k, v));
10711109
} else {
1072-
newAttributes = new Map(attributes);
1110+
result.set(tag, new Map(attributes));
10731111
}
1074-
1075-
result.set(tag, newAttributes);
10761112
}
10771113

10781114
// eslint-disable-next-line no-continue
@@ -1084,10 +1120,6 @@ function normalizeSourcesList(sources) {
10841120
tag = tag.toLowerCase();
10851121
attribute = attribute.toLowerCase();
10861122

1087-
if (!result.has(tag)) {
1088-
result.set(tag, new Map());
1089-
}
1090-
10911123
let typeFn;
10921124

10931125
// eslint-disable-next-line default-case
@@ -1100,7 +1132,14 @@ function normalizeSourcesList(sources) {
11001132
break;
11011133
}
11021134

1103-
result.get(tag).set(attribute, {
1135+
let attrMap = result.get(tag);
1136+
1137+
if (!attrMap) {
1138+
attrMap = new Map();
1139+
result.set(tag, attrMap);
1140+
}
1141+
1142+
attrMap.set(attribute, {
11041143
type: typeFn,
11051144
filter: source.filter,
11061145
});
@@ -1265,26 +1304,32 @@ function isASCIIC0group(character) {
12651304
}
12661305

12671306
export function c0ControlCodesExclude(source) {
1268-
let { value, startOffset } = source;
1307+
let { value } = source;
12691308

12701309
if (!value) {
12711310
throw new Error("Must be non-empty");
12721311
}
12731312

1274-
while (isASCIIC0group(value.substring(0, 1))) {
1275-
startOffset += 1;
1276-
value = value.substring(1, value.length);
1277-
}
1313+
let start = 0;
1314+
for (; start < value.length && isASCIIC0group(value[start]); start++);
12781315

1279-
while (isASCIIC0group(value.substring(value.length - 1, value.length))) {
1280-
value = value.substring(0, value.length - 1);
1316+
if (start === value.length) {
1317+
throw new Error("Must be non-empty");
12811318
}
12821319

1283-
if (!value) {
1284-
throw new Error("Must be non-empty");
1320+
let end = value.length - 1;
1321+
for (; end > -1 && isASCIIC0group(value[end]); end--);
1322+
end += 1;
1323+
1324+
if (start !== 0 || end !== value.length) {
1325+
value = value.substring(start, end);
1326+
1327+
if (!value) {
1328+
throw new Error("Must be non-empty");
1329+
}
12851330
}
12861331

1287-
return { value, startOffset };
1332+
return { value, startOffset: source.startOffset + start };
12881333
}
12891334

12901335
export function traverse(root, callback) {

0 commit comments

Comments
 (0)