webpack 5.108.2 → 5.108.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Compilation.js +1 -4
- package/lib/FlagDependencyExportsPlugin.js +55 -8
- package/lib/LazyBarrel.js +30 -3
- package/lib/NormalModule.js +8 -12
- package/lib/config/defaults.js +3 -3
- package/lib/css/CssGenerator.js +33 -42
- package/lib/css/CssModulesPlugin.js +79 -53
- package/lib/css/CssParser.js +699 -449
- package/lib/css/syntax.js +1146 -360
- package/lib/dependencies/CssIcssExportDependency.js +28 -16
- package/lib/dependencies/HarmonyAcceptDependency.js +8 -16
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +23 -96
- package/lib/dependencies/HarmonyImportGuard.js +192 -20
- package/lib/dependencies/ImportParserPlugin.js +7 -9
- package/lib/html/HtmlGenerator.js +53 -76
- package/lib/html/HtmlModule.js +1 -0
- package/lib/html/HtmlModulesPlugin.js +24 -13
- package/lib/html/HtmlParser.js +240 -124
- package/lib/html/syntax.js +1362 -683
- package/lib/javascript/JavascriptParser.js +10 -0
- package/lib/url/URLParserPlugin.js +33 -7
- package/lib/util/LocConverter.js +9 -8
- package/lib/util/SourceProcessor.js +41 -28
- package/package.json +3 -3
- package/types.d.ts +79 -221
package/lib/html/HtmlParser.js
CHANGED
|
@@ -35,14 +35,13 @@ const {
|
|
|
35
35
|
webpackCommentRegExp
|
|
36
36
|
} = require("../util/magicComment");
|
|
37
37
|
const {
|
|
38
|
+
NS_HTML,
|
|
38
39
|
NS_SVG,
|
|
39
40
|
NodeType,
|
|
40
41
|
SVG_TAG_ADJUST,
|
|
41
42
|
SourceProcessor,
|
|
42
|
-
buildHtmlAst,
|
|
43
43
|
decodeHtmlEntities,
|
|
44
44
|
decodeHtmlEntitiesWithMap,
|
|
45
|
-
findAttr,
|
|
46
45
|
parseSrcset
|
|
47
46
|
} = require("./syntax");
|
|
48
47
|
|
|
@@ -113,6 +112,10 @@ const FUNC_IRI_URL_REGEXP = /url\(/i;
|
|
|
113
112
|
// formatting/text markup (`<p>hi</p>`) rewrites to itself, so skip it.
|
|
114
113
|
const SRCDOC_ASSET_REGEXP = /[=]|url\(|@import/i;
|
|
115
114
|
|
|
115
|
+
// A URL carrying its own scheme (`https:`, `data:`, …) ignores the document
|
|
116
|
+
// base per the URL spec, so `<base href>` never rewrites it.
|
|
117
|
+
const ABSOLUTE_URL_SCHEME_REGEXP = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
|
|
118
|
+
|
|
116
119
|
// CSP/fetch attributes copied verbatim onto a synthesized sibling `<link>` /
|
|
117
120
|
// `<script>` (`HtmlEntryDependency`). Fixed output order, independent of
|
|
118
121
|
// source order.
|
|
@@ -120,24 +123,28 @@ const COPYABLE_SIBLING_ATTRS = ["nonce", "crossorigin", "referrerpolicy"];
|
|
|
120
123
|
|
|
121
124
|
const CC_QUOTATION = '"'.charCodeAt(0);
|
|
122
125
|
const CC_APOSTROPHE = "'".charCodeAt(0);
|
|
126
|
+
const CC_SLASH = "/".charCodeAt(0);
|
|
123
127
|
|
|
124
128
|
/**
|
|
125
129
|
* Byte-exact source span of an attribute including the single leading
|
|
126
130
|
* whitespace (` name`, ` name=value`, ` name="value"`), mirroring the
|
|
127
131
|
* tokenizer's end-of-attribute rule (`valueEnd + 1` past the closing quote).
|
|
132
|
+
* @param {import("./syntax").HtmlPath} path walk path (used only for attribute-ref reads)
|
|
128
133
|
* @param {string} source HTML source
|
|
129
|
-
* @param {import("./syntax").
|
|
134
|
+
* @param {import("./syntax").HtmlAttributeRef} attr attribute ref
|
|
130
135
|
* @returns {string} the attribute's source slice
|
|
131
136
|
*/
|
|
132
|
-
const attrSourceSpan = (source, attr) => {
|
|
137
|
+
const attrSourceSpan = (path, source, attr) => {
|
|
138
|
+
const valueStart = path.attributeValueStart(attr);
|
|
139
|
+
const valueEnd = path.attributeValueEnd(attr);
|
|
133
140
|
const end =
|
|
134
|
-
|
|
135
|
-
? attr
|
|
136
|
-
: source.charCodeAt(
|
|
137
|
-
source.charCodeAt(
|
|
138
|
-
?
|
|
139
|
-
:
|
|
140
|
-
return source.slice(attr
|
|
141
|
+
valueStart === -1
|
|
142
|
+
? path.attributeNameEnd(attr)
|
|
143
|
+
: source.charCodeAt(valueStart - 1) === CC_QUOTATION ||
|
|
144
|
+
source.charCodeAt(valueStart - 1) === CC_APOSTROPHE
|
|
145
|
+
? valueEnd + 1
|
|
146
|
+
: valueEnd;
|
|
147
|
+
return source.slice(path.attributeNameStart(attr) - 1, end);
|
|
141
148
|
};
|
|
142
149
|
|
|
143
150
|
// eslint-disable-next-line no-control-regex
|
|
@@ -909,6 +916,23 @@ class HtmlParser extends Parser {
|
|
|
909
916
|
|
|
910
917
|
let nextEntryIndex = 0;
|
|
911
918
|
|
|
919
|
+
// `<base href>` resolves the relative URLs that follow it. A relative base
|
|
920
|
+
// (`./assets/`) rewrites them into a subdirectory — still bundled; a
|
|
921
|
+
// root-relative or absolute base (`/`, `https://cdn/`) points them outside
|
|
922
|
+
// the build, so those URLs are left untouched. Resolved in the walk below
|
|
923
|
+
// from the first `<base href>` seen (`documentBase` undefined until then).
|
|
924
|
+
/** @type {string | undefined} */
|
|
925
|
+
let documentBase;
|
|
926
|
+
/** @type {string | undefined} */
|
|
927
|
+
let baseDir;
|
|
928
|
+
let baseIsExternal = false;
|
|
929
|
+
// Prepended to the emitted URLs' auto-public-path undo path so the base
|
|
930
|
+
// doesn't misdirect them: the browser resolves rewritten (relative)
|
|
931
|
+
// output URLs against the base dir, so one `../` per base segment cancels
|
|
932
|
+
// it. Undefined for absolute publicPath (no undo path is emitted).
|
|
933
|
+
/** @type {string | undefined} */
|
|
934
|
+
let baseUrlPrefix;
|
|
935
|
+
|
|
912
936
|
/**
|
|
913
937
|
* Tracks the `webpackIgnore` value from the most recent comment that
|
|
914
938
|
* appears before the next tag. Reset whenever a tag is emitted or a
|
|
@@ -920,41 +944,39 @@ class HtmlParser extends Parser {
|
|
|
920
944
|
const magicCommentContext = this.magicCommentContext;
|
|
921
945
|
|
|
922
946
|
/**
|
|
923
|
-
* @param {import("./syntax").
|
|
947
|
+
* @param {import("./syntax").HtmlPath} path walk path (used only for attribute-ref reads)
|
|
948
|
+
* @param {import("./syntax").HtmlAttributeRef} typeAttr type attribute ref (0 = none)
|
|
924
949
|
* @param {number} nameEnd end offset of the tag name
|
|
925
950
|
* @param {string} type type of the script
|
|
926
951
|
* @param {string} input source string
|
|
927
952
|
*/
|
|
928
|
-
const reconcileScriptTypeAttr = (typeAttr, nameEnd, type, input) => {
|
|
953
|
+
const reconcileScriptTypeAttr = (path, typeAttr, nameEnd, type, input) => {
|
|
954
|
+
const valueStart =
|
|
955
|
+
typeAttr !== 0 ? path.attributeValueStart(typeAttr) : -1;
|
|
956
|
+
const valueEnd = typeAttr !== 0 ? path.attributeValueEnd(typeAttr) : -1;
|
|
929
957
|
if (outputModule && type === "script") {
|
|
930
958
|
// Chunk is an ES module; upgrade the tag.
|
|
931
|
-
if (typeAttr &&
|
|
959
|
+
if (typeAttr !== 0 && valueStart !== -1) {
|
|
932
960
|
module.addPresentationalDependency(
|
|
933
|
-
new ConstDependency("module", [
|
|
934
|
-
typeAttr.valueStart,
|
|
935
|
-
typeAttr.valueEnd
|
|
936
|
-
])
|
|
961
|
+
new ConstDependency("module", [valueStart, valueEnd])
|
|
937
962
|
);
|
|
938
963
|
} else {
|
|
939
964
|
module.addPresentationalDependency(
|
|
940
965
|
new ConstDependency(' type="module"', nameEnd)
|
|
941
966
|
);
|
|
942
967
|
}
|
|
943
|
-
} else if (!outputModule && type === "script-module" && typeAttr) {
|
|
968
|
+
} else if (!outputModule && type === "script-module" && typeAttr !== 0) {
|
|
944
969
|
// Chunk is a classic IIFE; drop `type="module"` so the
|
|
945
970
|
// browser doesn't load it under module semantics.
|
|
946
971
|
let attrEnd;
|
|
947
|
-
if (
|
|
948
|
-
attrEnd = typeAttr
|
|
949
|
-
} else if (
|
|
950
|
-
|
|
951
|
-
input[typeAttr.valueEnd] === "'"
|
|
952
|
-
) {
|
|
953
|
-
attrEnd = typeAttr.valueEnd + 1;
|
|
972
|
+
if (valueStart === -1) {
|
|
973
|
+
attrEnd = path.attributeNameEnd(typeAttr);
|
|
974
|
+
} else if (input[valueEnd] === '"' || input[valueEnd] === "'") {
|
|
975
|
+
attrEnd = valueEnd + 1;
|
|
954
976
|
} else {
|
|
955
|
-
attrEnd =
|
|
977
|
+
attrEnd = valueEnd;
|
|
956
978
|
}
|
|
957
|
-
let attrStart = typeAttr
|
|
979
|
+
let attrStart = path.attributeNameStart(typeAttr);
|
|
958
980
|
if (
|
|
959
981
|
attrStart > 0 &&
|
|
960
982
|
isASCIIWhitespace(input.charCodeAt(attrStart - 1))
|
|
@@ -986,29 +1008,102 @@ class HtmlParser extends Parser {
|
|
|
986
1008
|
dep.setLoc(s.line, s.column, e.line, e.column);
|
|
987
1009
|
};
|
|
988
1010
|
|
|
1011
|
+
/**
|
|
1012
|
+
* Classifies the first `<base href>` seen in the walk, setting
|
|
1013
|
+
* `documentBase`/`baseDir`/`baseIsExternal`/`baseUrlPrefix` for the URLs
|
|
1014
|
+
* that follow it.
|
|
1015
|
+
* @param {import("./syntax").HtmlPath} path walk path (for attribute reads)
|
|
1016
|
+
* @param {import("./syntax").HtmlAttributeRef} hrefAttr the base's href attribute
|
|
1017
|
+
*/
|
|
1018
|
+
const resolveDocumentBase = (path, hrefAttr) => {
|
|
1019
|
+
documentBase = decodeHtmlEntities(
|
|
1020
|
+
path.attributeValue(hrefAttr),
|
|
1021
|
+
true
|
|
1022
|
+
).trim();
|
|
1023
|
+
if (!documentBase) return;
|
|
1024
|
+
if (
|
|
1025
|
+
ABSOLUTE_URL_SCHEME_REGEXP.test(documentBase) ||
|
|
1026
|
+
documentBase.charCodeAt(0) === CC_SLASH
|
|
1027
|
+
) {
|
|
1028
|
+
baseIsExternal = true;
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
// Normalize the base path into its descending directory segments,
|
|
1032
|
+
// resolving `.`/`..` like the URL spec. The final segment is the
|
|
1033
|
+
// referenced file (dropped) unless the href ends with `/` or a
|
|
1034
|
+
// dot-segment; `up` counts `..`s that climb above the document dir.
|
|
1035
|
+
const parts = documentBase.split("/");
|
|
1036
|
+
/** @type {string[]} */
|
|
1037
|
+
const dirs = [];
|
|
1038
|
+
let up = 0;
|
|
1039
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1040
|
+
const part = parts[i];
|
|
1041
|
+
if (part === "" || part === ".") continue;
|
|
1042
|
+
if (part === "..") {
|
|
1043
|
+
if (dirs.length > 0) dirs.pop();
|
|
1044
|
+
else up++;
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
// A trailing plain segment (no following `/`) is the file, not a dir.
|
|
1048
|
+
if (i === parts.length - 1) continue;
|
|
1049
|
+
dirs.push(part);
|
|
1050
|
+
}
|
|
1051
|
+
baseDir = `${"../".repeat(up)}${dirs.length > 0 ? `${dirs.join("/")}/` : ""}`;
|
|
1052
|
+
// A base above the document dir can't be cancelled with `../` (the
|
|
1053
|
+
// document's own dir name is unknown) — leave the output URLs
|
|
1054
|
+
// un-prefixed; they still resolve under an absolute publicPath.
|
|
1055
|
+
if (up === 0 && dirs.length > 0) {
|
|
1056
|
+
baseUrlPrefix = "../".repeat(dirs.length);
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
// Lazy decoded-attribute map for the current element — parse-scoped
|
|
1061
|
+
// to avoid a closure and memo slot per element.
|
|
1062
|
+
/** @type {import("./syntax").HtmlPath | undefined} */
|
|
1063
|
+
let attrMapPath;
|
|
1064
|
+
let attrMapCount = 0;
|
|
1065
|
+
/** @type {Map<string, string> | undefined} */
|
|
1066
|
+
let currentAttributesMap;
|
|
1067
|
+
const getAttributesMap = () => {
|
|
1068
|
+
if (currentAttributesMap) return currentAttributesMap;
|
|
1069
|
+
currentAttributesMap = new Map();
|
|
1070
|
+
const path = /** @type {import("./syntax").HtmlPath} */ (attrMapPath);
|
|
1071
|
+
for (let i = 0; i < attrMapCount; i++) {
|
|
1072
|
+
const attr = path.attributeAt(i);
|
|
1073
|
+
// Decoded values — filters and type resolvers compare what
|
|
1074
|
+
// the browser sees (e.g. `rel="icon"` means `icon`)
|
|
1075
|
+
currentAttributesMap.set(
|
|
1076
|
+
path.attributeName(attr),
|
|
1077
|
+
decodeHtmlEntities(path.attributeValue(attr), true)
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
return currentAttributesMap;
|
|
1081
|
+
};
|
|
1082
|
+
|
|
989
1083
|
// TODO implement full HTML parser (WASM)
|
|
990
1084
|
// The walker descends into children (and `<template>` content) itself;
|
|
991
1085
|
// the Element `exit` clears a pending `webpackIgnore` once an element's
|
|
992
1086
|
// children are done (the old `walkChildren` behaviour).
|
|
993
1087
|
new SourceProcessor()
|
|
994
1088
|
.use({
|
|
995
|
-
[NodeType.Comment]: (
|
|
996
|
-
const
|
|
1089
|
+
[NodeType.Comment]: (path) => {
|
|
1090
|
+
const start = path.start();
|
|
1091
|
+
const end = path.end();
|
|
997
1092
|
// Only proper `<!-- ... -->` comments carry magic comments.
|
|
998
1093
|
if (
|
|
999
|
-
|
|
1000
|
-
source.charCodeAt(
|
|
1001
|
-
source.charCodeAt(
|
|
1002
|
-
source.charCodeAt(
|
|
1003
|
-
source.charCodeAt(
|
|
1004
|
-
source.charCodeAt(
|
|
1005
|
-
source.charCodeAt(
|
|
1006
|
-
source.charCodeAt(
|
|
1094
|
+
end - start < 7 ||
|
|
1095
|
+
source.charCodeAt(start) !== 0x3c ||
|
|
1096
|
+
source.charCodeAt(start + 1) !== 0x21 ||
|
|
1097
|
+
source.charCodeAt(start + 2) !== 0x2d ||
|
|
1098
|
+
source.charCodeAt(start + 3) !== 0x2d ||
|
|
1099
|
+
source.charCodeAt(end - 1) !== 0x3e ||
|
|
1100
|
+
source.charCodeAt(end - 2) !== 0x2d ||
|
|
1101
|
+
source.charCodeAt(end - 3) !== 0x2d
|
|
1007
1102
|
) {
|
|
1008
1103
|
pendingWebpackIgnore = undefined;
|
|
1009
1104
|
return;
|
|
1010
1105
|
}
|
|
1011
|
-
const value =
|
|
1106
|
+
const value = path.data();
|
|
1012
1107
|
if (!webpackCommentRegExp.test(value)) {
|
|
1013
1108
|
pendingWebpackIgnore = undefined;
|
|
1014
1109
|
return;
|
|
@@ -1018,8 +1113,8 @@ class HtmlParser extends Parser {
|
|
|
1018
1113
|
try {
|
|
1019
1114
|
options = parseMagicComment(value, magicCommentContext);
|
|
1020
1115
|
} catch (err) {
|
|
1021
|
-
const { line: sl, column: sc } = locConverter.get(
|
|
1022
|
-
const { line: el, column: ec } = locConverter.get(
|
|
1116
|
+
const { line: sl, column: sc } = locConverter.get(start);
|
|
1117
|
+
const { line: el, column: ec } = locConverter.get(end);
|
|
1023
1118
|
module.addWarning(
|
|
1024
1119
|
new CommentCompilationWarning(
|
|
1025
1120
|
`Compilation error while processing magic comment(-s): /*${value}*/: ${
|
|
@@ -1039,8 +1134,8 @@ class HtmlParser extends Parser {
|
|
|
1039
1134
|
return;
|
|
1040
1135
|
}
|
|
1041
1136
|
if (typeof options.webpackIgnore !== "boolean") {
|
|
1042
|
-
const { line: sl, column: sc } = locConverter.get(
|
|
1043
|
-
const { line: el, column: ec } = locConverter.get(
|
|
1137
|
+
const { line: sl, column: sc } = locConverter.get(start);
|
|
1138
|
+
const { line: el, column: ec } = locConverter.get(end);
|
|
1044
1139
|
module.addWarning(
|
|
1045
1140
|
new UnsupportedFeatureWarning(
|
|
1046
1141
|
`\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
|
|
@@ -1062,8 +1157,7 @@ class HtmlParser extends Parser {
|
|
|
1062
1157
|
// Children (and `<template>` content) are walked by the
|
|
1063
1158
|
// processor after `enter`; `exit` clears a pending
|
|
1064
1159
|
// `webpackIgnore` once they're done.
|
|
1065
|
-
enter: (
|
|
1066
|
-
const node = /** @type {import("./syntax").HtmlElement} */ (n);
|
|
1160
|
+
enter: (path) => {
|
|
1067
1161
|
const ignore = pendingWebpackIgnore === true;
|
|
1068
1162
|
pendingWebpackIgnore = undefined;
|
|
1069
1163
|
|
|
@@ -1071,24 +1165,26 @@ class HtmlParser extends Parser {
|
|
|
1071
1165
|
return;
|
|
1072
1166
|
}
|
|
1073
1167
|
|
|
1074
|
-
const elementName =
|
|
1075
|
-
const
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1168
|
+
const elementName = path.tagName();
|
|
1169
|
+
const attributeCount = path.attributeCount();
|
|
1170
|
+
const elementStart = path.start();
|
|
1171
|
+
const tagEnd = path.tagEnd();
|
|
1172
|
+
const nameEnd = path.nameEnd();
|
|
1173
|
+
|
|
1174
|
+
// The first `<base href>` freezes the base for later URLs.
|
|
1175
|
+
if (
|
|
1176
|
+
documentBase === undefined &&
|
|
1177
|
+
elementName === "base" &&
|
|
1178
|
+
path.namespace() === NS_HTML
|
|
1179
|
+
) {
|
|
1180
|
+
const hrefAttr = path.findAttribute("href");
|
|
1181
|
+
if (hrefAttr !== 0) resolveDocumentBase(path, hrefAttr);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// Rebind the parse-scoped lazy attribute map to this element.
|
|
1185
|
+
attrMapPath = path;
|
|
1186
|
+
attrMapCount = attributeCount;
|
|
1187
|
+
currentAttributesMap = undefined;
|
|
1092
1188
|
|
|
1093
1189
|
// Each matched attribute is a source; the element body (inline
|
|
1094
1190
|
// `<style>`/`<script>`) is one more — content rather than an attribute.
|
|
@@ -1097,10 +1193,10 @@ class HtmlParser extends Parser {
|
|
|
1097
1193
|
this.sourcesByTag[elementName] || this.sourcesByTag[ANY_TAG];
|
|
1098
1194
|
const bodyItem = CONTENT_SOURCES[elementName];
|
|
1099
1195
|
// Iterate the attributes, then one extra step for the element
|
|
1100
|
-
// body (`i ===
|
|
1101
|
-
// attribute steps, so
|
|
1102
|
-
for (let i = 0; i <
|
|
1103
|
-
const content = i ===
|
|
1196
|
+
// body (`i === attributeCount`); the attribute ref is only read on the
|
|
1197
|
+
// attribute steps, so it stays in bounds.
|
|
1198
|
+
for (let i = 0; i < attributeCount + 1; i++) {
|
|
1199
|
+
const content = i === attributeCount;
|
|
1104
1200
|
/** @type {SourceType} */
|
|
1105
1201
|
let type;
|
|
1106
1202
|
/** @type {string} */
|
|
@@ -1109,23 +1205,18 @@ class HtmlParser extends Parser {
|
|
|
1109
1205
|
let start;
|
|
1110
1206
|
/** @type {number} */
|
|
1111
1207
|
let end;
|
|
1112
|
-
/** @type {import("./syntax").
|
|
1113
|
-
let attr;
|
|
1208
|
+
/** @type {import("./syntax").HtmlAttributeRef} */
|
|
1209
|
+
let attr = 0;
|
|
1114
1210
|
if (content) {
|
|
1115
1211
|
if (!bodyItem) continue;
|
|
1116
1212
|
if (bodyItem.filter && !bodyItem.filter(getAttributesMap())) {
|
|
1117
1213
|
continue;
|
|
1118
1214
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
if (bodyStart === undefined) bodyStart = child.start;
|
|
1125
|
-
bodyEnd = child.end;
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
if (bodyStart === undefined) continue;
|
|
1215
|
+
// `skip.text` drops the body `Text` node; the raw content
|
|
1216
|
+
// span is [`tagEnd`, `contentEnd`] on the element.
|
|
1217
|
+
const bodyStart = tagEnd;
|
|
1218
|
+
const bodyEnd = path.contentEnd();
|
|
1219
|
+
if (bodyEnd <= bodyStart) continue;
|
|
1129
1220
|
value = source.slice(bodyStart, bodyEnd);
|
|
1130
1221
|
if (value.trim() === "") continue;
|
|
1131
1222
|
start = bodyStart;
|
|
@@ -1135,20 +1226,20 @@ class HtmlParser extends Parser {
|
|
|
1135
1226
|
? bodyItem.type(getAttributesMap(), css)
|
|
1136
1227
|
: bodyItem.type;
|
|
1137
1228
|
} else {
|
|
1138
|
-
attr =
|
|
1139
|
-
const item = sources[attr
|
|
1229
|
+
attr = path.attributeAt(i);
|
|
1230
|
+
const item = sources[path.attributeName(attr)];
|
|
1140
1231
|
if (!item) continue;
|
|
1141
1232
|
if (
|
|
1142
1233
|
item.namespace !== undefined &&
|
|
1143
|
-
|
|
1234
|
+
path.namespace() !== item.namespace
|
|
1144
1235
|
) {
|
|
1145
1236
|
continue;
|
|
1146
1237
|
}
|
|
1147
1238
|
// Adoption-agency clones carry no offsets; skip blank values.
|
|
1148
|
-
if (
|
|
1239
|
+
if (path.attributeValueStart(attr) === -1) {
|
|
1149
1240
|
continue;
|
|
1150
1241
|
}
|
|
1151
|
-
value = attr
|
|
1242
|
+
value = path.attributeValue(attr);
|
|
1152
1243
|
if (!value || !/\S/.test(value)) continue;
|
|
1153
1244
|
const filter = item.filter;
|
|
1154
1245
|
if (filter) {
|
|
@@ -1159,8 +1250,8 @@ class HtmlParser extends Parser {
|
|
|
1159
1250
|
: value;
|
|
1160
1251
|
if (!filter(getAttributesMap(), decoded)) continue;
|
|
1161
1252
|
}
|
|
1162
|
-
start = attr
|
|
1163
|
-
end = attr
|
|
1253
|
+
start = path.attributeValueStart(attr);
|
|
1254
|
+
end = path.attributeValueEnd(attr);
|
|
1164
1255
|
type =
|
|
1165
1256
|
typeof item.type === "function"
|
|
1166
1257
|
? item.type(getAttributesMap(), css)
|
|
@@ -1225,7 +1316,7 @@ class HtmlParser extends Parser {
|
|
|
1225
1316
|
const entryName = `__html_${moduleHash}_${nextEntryIndex++}`;
|
|
1226
1317
|
const dep = new HtmlInlineScriptDependency(
|
|
1227
1318
|
request,
|
|
1228
|
-
|
|
1319
|
+
nameEnd,
|
|
1229
1320
|
[start, end],
|
|
1230
1321
|
entryName,
|
|
1231
1322
|
scriptType === "script-module" ? "esm" : "commonjs"
|
|
@@ -1233,8 +1324,9 @@ class HtmlParser extends Parser {
|
|
|
1233
1324
|
setLoc(dep, start, end);
|
|
1234
1325
|
module.addPresentationalDependency(dep);
|
|
1235
1326
|
reconcileScriptTypeAttr(
|
|
1236
|
-
|
|
1237
|
-
|
|
1327
|
+
path,
|
|
1328
|
+
path.findAttribute("type"),
|
|
1329
|
+
nameEnd,
|
|
1238
1330
|
scriptType,
|
|
1239
1331
|
source
|
|
1240
1332
|
);
|
|
@@ -1258,9 +1350,6 @@ class HtmlParser extends Parser {
|
|
|
1258
1350
|
// Parse the value into one or more URLs (decoding character
|
|
1259
1351
|
// references, mapping spans back to raw offsets), then emit a plain
|
|
1260
1352
|
// asset reference or an entry chunk per URL.
|
|
1261
|
-
const a = /** @type {import("./syntax").HtmlAttribute} */ (
|
|
1262
|
-
attr
|
|
1263
|
-
);
|
|
1264
1353
|
const parse =
|
|
1265
1354
|
type === "srcset"
|
|
1266
1355
|
? parseSrcset
|
|
@@ -1287,9 +1376,9 @@ class HtmlParser extends Parser {
|
|
|
1287
1376
|
new ModuleDependencyError(
|
|
1288
1377
|
module,
|
|
1289
1378
|
new WebpackError(
|
|
1290
|
-
`Bad value for attribute "${
|
|
1291
|
-
|
|
1292
|
-
}" on element "${elementName}": ${
|
|
1379
|
+
`Bad value for attribute "${path.attributeName(
|
|
1380
|
+
attr
|
|
1381
|
+
)}" on element "${elementName}": ${
|
|
1293
1382
|
/** @type {Error} */ (err).message
|
|
1294
1383
|
}`
|
|
1295
1384
|
),
|
|
@@ -1305,6 +1394,18 @@ class HtmlParser extends Parser {
|
|
|
1305
1394
|
for (const [url, us, ue] of parsed) {
|
|
1306
1395
|
// Internal `url(#id)` / fragment-only refs aren't assets.
|
|
1307
1396
|
if (!url || url.startsWith("#")) continue;
|
|
1397
|
+
// Resolve the request against `<base href>`. Only relative
|
|
1398
|
+
// URLs are affected (scheme / `//` / `/` URLs ignore the
|
|
1399
|
+
// base); an external base drops them from the build.
|
|
1400
|
+
let request = url;
|
|
1401
|
+
if (
|
|
1402
|
+
documentBase &&
|
|
1403
|
+
url.charCodeAt(0) !== CC_SLASH &&
|
|
1404
|
+
!ABSOLUTE_URL_SCHEME_REGEXP.test(url)
|
|
1405
|
+
) {
|
|
1406
|
+
if (baseIsExternal) continue;
|
|
1407
|
+
request = baseDir + url;
|
|
1408
|
+
}
|
|
1308
1409
|
const s = start + (map ? map[us] : us);
|
|
1309
1410
|
const e = start + (map ? map[ue] : ue);
|
|
1310
1411
|
// `src`/`srcset`/`css-url`/`msapplication-task` are plain assets;
|
|
@@ -1345,11 +1446,12 @@ class HtmlParser extends Parser {
|
|
|
1345
1446
|
// CSP/fetch attributes the template copies onto siblings.
|
|
1346
1447
|
let copyableAttrsText = "";
|
|
1347
1448
|
let hasOwnCrossOrigin = false;
|
|
1348
|
-
if (
|
|
1449
|
+
if (elementStart >= 0) {
|
|
1349
1450
|
for (const copyableName of COPYABLE_SIBLING_ATTRS) {
|
|
1350
|
-
const copyableAttr =
|
|
1351
|
-
if (copyableAttr) {
|
|
1451
|
+
const copyableAttr = path.findAttribute(copyableName);
|
|
1452
|
+
if (copyableAttr !== 0) {
|
|
1352
1453
|
copyableAttrsText += attrSourceSpan(
|
|
1454
|
+
path,
|
|
1353
1455
|
source,
|
|
1354
1456
|
copyableAttr
|
|
1355
1457
|
);
|
|
@@ -1363,7 +1465,7 @@ class HtmlParser extends Parser {
|
|
|
1363
1465
|
firstScriptStart === -1 &&
|
|
1364
1466
|
(type === "script" || type === "script-module")
|
|
1365
1467
|
) {
|
|
1366
|
-
firstScriptStart =
|
|
1468
|
+
firstScriptStart = elementStart;
|
|
1367
1469
|
}
|
|
1368
1470
|
// Sibling-clone attribute edits, captured from the parsed
|
|
1369
1471
|
// attributes (offsets relative to the tag) so the template
|
|
@@ -1374,10 +1476,13 @@ class HtmlParser extends Parser {
|
|
|
1374
1476
|
let integrityRange = null;
|
|
1375
1477
|
/** @type {Range | null} */
|
|
1376
1478
|
let typeValueRange = null;
|
|
1377
|
-
if (
|
|
1378
|
-
const integrityAttr =
|
|
1379
|
-
if (
|
|
1380
|
-
|
|
1479
|
+
if (elementStart >= 0 && elementName === nativeTag) {
|
|
1480
|
+
const integrityAttr = path.findAttribute("integrity");
|
|
1481
|
+
if (
|
|
1482
|
+
integrityAttr !== 0 &&
|
|
1483
|
+
path.attributeNameStart(integrityAttr) >= 0
|
|
1484
|
+
) {
|
|
1485
|
+
let rs = path.attributeNameStart(integrityAttr);
|
|
1381
1486
|
while (
|
|
1382
1487
|
rs > 0 &&
|
|
1383
1488
|
isASCIIWhitespace(source.charCodeAt(rs - 1))
|
|
@@ -1385,40 +1490,46 @@ class HtmlParser extends Parser {
|
|
|
1385
1490
|
rs--;
|
|
1386
1491
|
}
|
|
1387
1492
|
let re;
|
|
1388
|
-
|
|
1389
|
-
|
|
1493
|
+
const ivs = path.attributeValueStart(integrityAttr);
|
|
1494
|
+
if (ivs === -1) {
|
|
1495
|
+
re = path.attributeNameEnd(integrityAttr);
|
|
1390
1496
|
} else {
|
|
1391
|
-
const q = source.charCodeAt(
|
|
1392
|
-
|
|
1393
|
-
);
|
|
1497
|
+
const q = source.charCodeAt(ivs - 1);
|
|
1498
|
+
const ive = path.attributeValueEnd(integrityAttr);
|
|
1394
1499
|
re =
|
|
1395
1500
|
q === CC_QUOTATION || q === CC_APOSTROPHE
|
|
1396
|
-
?
|
|
1397
|
-
:
|
|
1501
|
+
? ive + 1
|
|
1502
|
+
: ive;
|
|
1398
1503
|
}
|
|
1399
|
-
integrityRange = [
|
|
1504
|
+
integrityRange = [
|
|
1505
|
+
rs - elementStart,
|
|
1506
|
+
re - elementStart
|
|
1507
|
+
];
|
|
1400
1508
|
}
|
|
1401
1509
|
if (elementKind === "script-module") {
|
|
1402
|
-
const typeAttr =
|
|
1403
|
-
if (
|
|
1510
|
+
const typeAttr = path.findAttribute("type");
|
|
1511
|
+
if (
|
|
1512
|
+
typeAttr !== 0 &&
|
|
1513
|
+
path.attributeValueStart(typeAttr) >= 0
|
|
1514
|
+
) {
|
|
1404
1515
|
typeValueRange = [
|
|
1405
|
-
typeAttr
|
|
1406
|
-
typeAttr
|
|
1516
|
+
path.attributeValueStart(typeAttr) - elementStart,
|
|
1517
|
+
path.attributeValueEnd(typeAttr) - elementStart
|
|
1407
1518
|
];
|
|
1408
1519
|
}
|
|
1409
1520
|
}
|
|
1410
1521
|
}
|
|
1411
1522
|
const dep = new HtmlEntryDependency(
|
|
1412
|
-
|
|
1523
|
+
request,
|
|
1413
1524
|
[s, e],
|
|
1414
1525
|
entryName,
|
|
1415
1526
|
entryCategory,
|
|
1416
1527
|
elementKind,
|
|
1417
|
-
|
|
1418
|
-
|
|
1528
|
+
elementStart,
|
|
1529
|
+
tagEnd,
|
|
1419
1530
|
elementName === nativeTag,
|
|
1420
1531
|
copyableAttrsText,
|
|
1421
|
-
|
|
1532
|
+
nameEnd,
|
|
1422
1533
|
hasOwnCrossOrigin,
|
|
1423
1534
|
firstScriptStart,
|
|
1424
1535
|
integrityRange,
|
|
@@ -1431,8 +1542,9 @@ class HtmlParser extends Parser {
|
|
|
1431
1542
|
(type === "script" || type === "script-module")
|
|
1432
1543
|
) {
|
|
1433
1544
|
reconcileScriptTypeAttr(
|
|
1434
|
-
|
|
1435
|
-
|
|
1545
|
+
path,
|
|
1546
|
+
path.findAttribute("type"),
|
|
1547
|
+
nameEnd,
|
|
1436
1548
|
type,
|
|
1437
1549
|
source
|
|
1438
1550
|
);
|
|
@@ -1444,9 +1556,9 @@ class HtmlParser extends Parser {
|
|
|
1444
1556
|
: type === "stylesheet"
|
|
1445
1557
|
? stylesheetEntries
|
|
1446
1558
|
: modulePreloadEntries
|
|
1447
|
-
).push({ request
|
|
1559
|
+
).push({ request, entryName, type });
|
|
1448
1560
|
} else {
|
|
1449
|
-
const dep = new HtmlSourceDependency(
|
|
1561
|
+
const dep = new HtmlSourceDependency(request, [s, e]);
|
|
1450
1562
|
setLoc(dep, s, e);
|
|
1451
1563
|
module.addDependency(dep);
|
|
1452
1564
|
module.addCodeGenerationDependency(dep);
|
|
@@ -1462,10 +1574,14 @@ class HtmlParser extends Parser {
|
|
|
1462
1574
|
}
|
|
1463
1575
|
}
|
|
1464
1576
|
})
|
|
1465
|
-
|
|
1577
|
+
// Skip AST output this walk never reads: `text` (script/style bodies are
|
|
1578
|
+
// read by offset via `contentEnd`) and `doctype`; keep `comments` for
|
|
1579
|
+
// magic comments (`webpackIgnore`).
|
|
1580
|
+
.process(source, { skip: { text: true, doctype: true } });
|
|
1466
1581
|
|
|
1467
1582
|
const buildInfo = /** @type {HtmlModuleBuildInfo} */ (module.buildInfo);
|
|
1468
1583
|
buildInfo.strict = true;
|
|
1584
|
+
if (baseUrlPrefix !== undefined) buildInfo.baseUrlPrefix = baseUrlPrefix;
|
|
1469
1585
|
// Hand off the collected entries to HtmlModulesPlugin; it creates the
|
|
1470
1586
|
// real compilation entries during the finishMake hook. The `script`
|
|
1471
1587
|
// and `script-module` groups are chained via a leader-only dependOn
|