@depup/webpack 5.110.1-depup.0 → 5.110.3-depup.0
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/README.md +4 -4
- package/changes.json +3 -3
- package/lib/ChunkGraph.js +20 -18
- package/lib/Compilation.js +1 -1
- package/lib/ConcatenationScope.js +25 -35
- package/lib/DotenvPlugin.js +41 -13
- package/lib/ExportsInfo.js +10 -0
- package/lib/FlagDependencyUsagePlugin.js +8 -2
- package/lib/NormalModule.js +13 -20
- package/lib/RuntimePlugin.js +3 -0
- package/lib/RuntimeTemplate.js +437 -432
- package/lib/async-modules/AsyncModuleHelpers.js +10 -4
- package/lib/config/defaults.js +26 -16
- package/lib/config/normalization.js +18 -8
- package/lib/css/CssLoadingRuntimeModule.js +32 -13
- package/lib/css/CssModulesPlugin.js +9 -17
- package/lib/css/data.js +13 -3
- package/lib/css/syntax.js +78 -10
- package/lib/dependencies/CommonJsRequireDependency.js +18 -4
- package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +20 -7
- package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +10 -4
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +2 -0
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +143 -17
- package/lib/dependencies/HarmonyImportDependency.js +94 -41
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +93 -14
- package/lib/dependencies/HarmonyImportSideEffectDependency.js +114 -0
- package/lib/dependencies/HtmlEntryDependency.js +12 -2
- package/lib/dependencies/ImportContextDependency.js +43 -3
- package/lib/dependencies/ImportDependency.js +8 -29
- package/lib/dependencies/ImportParserPlugin.js +24 -14
- package/lib/dependencies/RequireHeaderDependency.js +20 -21
- package/lib/dependencies/WorkerAndWorkletPlugin.js +1 -0
- package/lib/dependencies/WorkerDependency.js +6 -12
- package/lib/dependencies/WorkletDependency.js +7 -11
- package/lib/dependencies/importOptionsCheck.js +47 -0
- package/lib/errors/DotenvFileError.js +29 -0
- package/lib/errors/ImportedBindingAssignmentError.js +30 -0
- package/lib/esm/ModuleChunkLoadingPlugin.js +6 -9
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +7 -1
- package/lib/html/HtmlModulesPlugin.js +84 -38
- package/lib/html/syntax.js +417 -169
- package/lib/javascript/JavascriptModulesPlugin.js +5 -10
- package/lib/loaders/LoaderRunner.js +33 -5
- package/lib/node/ReadFileCompileAsyncWasmPlugin.js +8 -15
- package/lib/optimize/ConcatenatedModule.js +46 -7
- package/lib/optimize/ConstExportsPlugin.js +5 -0
- package/lib/optimize/InlineExports.js +121 -0
- package/lib/optimize/RealContentHashPlugin.js +111 -35
- package/lib/prefetch/ResourceHintPlugin.js +12 -13
- package/lib/runtime/AsyncModuleRuntimeModule.js +5 -1
- package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +11 -1
- package/lib/schemes/HttpUriPlugin.js +22 -5
- package/lib/serialization/FileMiddleware.js +15 -2
- package/lib/typescript/TypeScriptPlugin.js +8 -10
- package/lib/util/ArrayQueue.js +3 -3
- package/lib/util/SourceProcessor.js +24 -8
- package/lib/util/URLAbsoluteSpecifier.js +3 -1
- package/lib/util/conventions.js +5 -1
- package/lib/util/createMappings.js +25 -3
- package/lib/util/internalSerializables.js +2 -0
- package/lib/util/removeBOM.js +82 -1
- package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +5 -3
- package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +5 -3
- package/lib/wasm-async/UniversalCompileAsyncWasmPlugin.js +7 -12
- package/lib/web/FetchCompileAsyncWasmPlugin.js +10 -6
- package/lib/web/FetchCompileWasmPlugin.js +4 -2
- package/package.json +7 -7
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +9 -10
- package/types.d.ts +95 -27
package/lib/html/syntax.js
CHANGED
|
@@ -12,7 +12,7 @@ const {
|
|
|
12
12
|
} = require("../ModuleSourceTypeConstants");
|
|
13
13
|
const GenericSourceProcessor = require("../util/SourceProcessor");
|
|
14
14
|
|
|
15
|
-
const { deferredWrite } = GenericSourceProcessor;
|
|
15
|
+
const { deferredWrite, declineDeferredWrites } = GenericSourceProcessor;
|
|
16
16
|
|
|
17
17
|
/** @import { CssEnvironment, CssProcessOptions, CssTransformOptions } from "../css/syntax" */
|
|
18
18
|
/** @import { OutputHtmlOptions } from "../../declarations/WebpackOptions" */
|
|
@@ -5237,11 +5237,13 @@ const _makeProcessingInstructionNode = (target, data, start, end) => {
|
|
|
5237
5237
|
// Marker entry in the active-formatting-elements list (never a valid ref).
|
|
5238
5238
|
const AFE_MARKER = -1;
|
|
5239
5239
|
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5240
|
+
/**
|
|
5241
|
+
* Clones an element's attribute run, dropping source offsets so the consumer
|
|
5242
|
+
* emits no duplicate dependency for the reopened element's spans.
|
|
5243
|
+
* @param {HtmlElement} el element whose attributes are cloned
|
|
5244
|
+
* @returns {AttributeRun} the cloned run
|
|
5245
|
+
*/
|
|
5246
|
+
const cloneAttrs = (el) => {
|
|
5245
5247
|
const start = _nodeAttrStarts[el];
|
|
5246
5248
|
const count = _nodeAttrCounts[el];
|
|
5247
5249
|
const newStart = _attrCount + 1;
|
|
@@ -5252,14 +5254,15 @@ const cloneAttrs = (/** @type {HtmlElement} */ el) => {
|
|
|
5252
5254
|
return { start: newStart, count };
|
|
5253
5255
|
};
|
|
5254
5256
|
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5257
|
+
/**
|
|
5258
|
+
* Merges a repeated `<html>`/`<body>` tag's attributes into the element, adding
|
|
5259
|
+
* only names not already present. Runs are contiguous, so any addition
|
|
5260
|
+
* re-allocates the whole run and orphans the old slots.
|
|
5261
|
+
* @param {HtmlElement} el element receiving the attributes
|
|
5262
|
+
* @param {AttributeRun} run attributes of the repeated tag
|
|
5263
|
+
* @returns {void}
|
|
5264
|
+
*/
|
|
5265
|
+
const mergeAttrs = (el, run) => {
|
|
5263
5266
|
const start = _nodeAttrStarts[el];
|
|
5264
5267
|
const count = _nodeAttrCounts[el];
|
|
5265
5268
|
let extra = 0;
|
|
@@ -5335,9 +5338,13 @@ const TOKEN_PROCESSING_INSTRUCTION = 7;
|
|
|
5335
5338
|
/** @typedef {{ type: typeof TOKEN_EOF }} EofToken */
|
|
5336
5339
|
/** @typedef {{ type: typeof TOKEN_PROCESSING_INSTRUCTION, name: string, data: string, start: number, end: number }} ProcessingInstructionToken the target rides in `name`, as the doctype's does */
|
|
5337
5340
|
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
+
/**
|
|
5342
|
+
* Every insertion mode handles a processing instruction token on the same arc
|
|
5343
|
+
* as a comment token, so the arcs test both.
|
|
5344
|
+
* @param {Token} t current token
|
|
5345
|
+
* @returns {t is CommentToken | ProcessingInstructionToken} true for a comment or processing instruction token
|
|
5346
|
+
*/
|
|
5347
|
+
const isCommentOrProcessingInstruction = (t) =>
|
|
5341
5348
|
t.type === TOKEN_COMMENT || t.type === TOKEN_PROCESSING_INSTRUCTION;
|
|
5342
5349
|
|
|
5343
5350
|
/**
|
|
@@ -5399,9 +5406,12 @@ const TEMPLATE_START_TAG_MODES = new Map(
|
|
|
5399
5406
|
})
|
|
5400
5407
|
);
|
|
5401
5408
|
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5409
|
+
/**
|
|
5410
|
+
* MathML/SVG specials are handled via namespace checks rather than one table.
|
|
5411
|
+
* @param {HtmlElement} el element to test
|
|
5412
|
+
* @returns {boolean} true when the element is "special" (§13.2.4.2)
|
|
5413
|
+
*/
|
|
5414
|
+
const isSpecial = (el) => {
|
|
5405
5415
|
const ns = _namespaceOf(el);
|
|
5406
5416
|
const tag = _tagNameOf(el);
|
|
5407
5417
|
if (ns === NS_HTML) return SPECIAL.has(tag);
|
|
@@ -5410,8 +5420,12 @@ const isSpecial = (/** @type {HtmlElement} */ el) => {
|
|
|
5410
5420
|
return false;
|
|
5411
5421
|
};
|
|
5412
5422
|
|
|
5413
|
-
|
|
5414
|
-
|
|
5423
|
+
/**
|
|
5424
|
+
* `<font color|face|size>` breaks out of foreign content (§13.2.6.5).
|
|
5425
|
+
* @param {AttributeRun} run the tag's attributes
|
|
5426
|
+
* @returns {boolean} true when one of the breakout attributes is present
|
|
5427
|
+
*/
|
|
5428
|
+
const hasFontBreakoutAttr = (run) => {
|
|
5415
5429
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
5416
5430
|
if (FONT_BREAKOUT_ATTRS.has(_attrNames[i])) return true;
|
|
5417
5431
|
}
|
|
@@ -5590,9 +5604,13 @@ const ATTR_NAME_INTERN = buildNameInternTable(
|
|
|
5590
5604
|
)
|
|
5591
5605
|
);
|
|
5592
5606
|
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5607
|
+
/**
|
|
5608
|
+
* Hoisted so the many `open.some(...)` "is there an open HTML `<template>`?"
|
|
5609
|
+
* checks reuse one predicate instead of allocating an arrow per call.
|
|
5610
|
+
* @param {HtmlElement} e element to test
|
|
5611
|
+
* @returns {boolean} true for an HTML `<template>` element
|
|
5612
|
+
*/
|
|
5613
|
+
const isHtmlTemplateEl = (e) =>
|
|
5596
5614
|
_tagNameOf(e) === "template" && _namespaceOf(e) === NS_HTML;
|
|
5597
5615
|
|
|
5598
5616
|
/**
|
|
@@ -5651,12 +5669,15 @@ const isQuirky = (name, pub, sys) => {
|
|
|
5651
5669
|
return false;
|
|
5652
5670
|
};
|
|
5653
5671
|
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5672
|
+
/**
|
|
5673
|
+
* Allocates an element node.
|
|
5674
|
+
* @param {string} tagName tag name
|
|
5675
|
+
* @param {number} ns namespace flag (`NS_HTML`, `NS_MATHML`, `NS_SVG`)
|
|
5676
|
+
* @param {AttributeRun} attrs the element's attributes
|
|
5677
|
+
* @param {TagPos | null | undefined} pos source position, absent for synthesized elements
|
|
5678
|
+
* @returns {HtmlElement} the new element
|
|
5679
|
+
*/
|
|
5680
|
+
const mkEl = (tagName, ns, attrs, pos) => {
|
|
5660
5681
|
// Inlined element allocation: every column is written exactly once (the
|
|
5661
5682
|
// generic _allocNode would zero five columns mkEl immediately overwrites).
|
|
5662
5683
|
const el = ++_nodeCount;
|
|
@@ -5702,7 +5723,12 @@ const effParent = (parent) => {
|
|
|
5702
5723
|
return tc !== 0 ? tc : parent;
|
|
5703
5724
|
};
|
|
5704
5725
|
|
|
5705
|
-
|
|
5726
|
+
/**
|
|
5727
|
+
* Elements that stop a "have an element in scope" walk (§13.2.4.2).
|
|
5728
|
+
* @param {HtmlElement} el element to test
|
|
5729
|
+
* @returns {boolean} true when the element bounds a scope
|
|
5730
|
+
*/
|
|
5731
|
+
const isScopeBoundary = (el) => {
|
|
5706
5732
|
if (_namespaceOf(el) === NS_HTML) return HTML_SCOPE.has(_tagNameOf(el));
|
|
5707
5733
|
if (_namespaceOf(el) === NS_MATHML) {
|
|
5708
5734
|
return MATHML_SPECIAL.has(_tagNameOf(el));
|
|
@@ -5713,10 +5739,13 @@ const isScopeBoundary = (/** @type {HtmlElement} */ el) => {
|
|
|
5713
5739
|
return false;
|
|
5714
5740
|
};
|
|
5715
5741
|
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5742
|
+
/**
|
|
5743
|
+
* Compares two attribute values, by source range while both are unresolved —
|
|
5744
|
+
* `_attrValueOf` would slice a string per side just to throw it away.
|
|
5745
|
+
* @param {number} i first attribute id
|
|
5746
|
+
* @param {number} j second attribute id
|
|
5747
|
+
* @returns {boolean} true when both values are equal
|
|
5748
|
+
*/
|
|
5720
5749
|
const _attrValueEquals = (i, j) => {
|
|
5721
5750
|
if (_attrValues[i] === null && _attrValues[j] === null) {
|
|
5722
5751
|
const start = _attrValueStarts[i];
|
|
@@ -5736,10 +5765,14 @@ const _attrValueEquals = (i, j) => {
|
|
|
5736
5765
|
return _attrValueOf(i) === _attrValueOf(j);
|
|
5737
5766
|
};
|
|
5738
5767
|
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5768
|
+
/**
|
|
5769
|
+
* Compares two elements' attribute runs, as the active-formatting-elements
|
|
5770
|
+
* "Noah's Ark" clause does.
|
|
5771
|
+
* @param {HtmlElement} a first element
|
|
5772
|
+
* @param {HtmlElement} b second element
|
|
5773
|
+
* @returns {boolean} true when both carry the same attributes
|
|
5774
|
+
*/
|
|
5775
|
+
const sameAttrs = (a, b) => {
|
|
5743
5776
|
const aStart = _nodeAttrStarts[a];
|
|
5744
5777
|
const aCount = _nodeAttrCounts[a];
|
|
5745
5778
|
const bStart = _nodeAttrStarts[b];
|
|
@@ -5754,12 +5787,20 @@ const sameAttrs = (
|
|
|
5754
5787
|
return true;
|
|
5755
5788
|
};
|
|
5756
5789
|
|
|
5757
|
-
|
|
5790
|
+
/**
|
|
5791
|
+
* @param {string} s text to test
|
|
5792
|
+
* @returns {boolean} true when the text starts with HTML whitespace
|
|
5793
|
+
*/
|
|
5794
|
+
const startsWithWs = (s) => {
|
|
5758
5795
|
const c = s.charCodeAt(0);
|
|
5759
5796
|
return c === 0x09 || c === 0x0a || c === 0x0c || c === 0x0d || c === 0x20;
|
|
5760
5797
|
};
|
|
5761
5798
|
|
|
5762
|
-
|
|
5799
|
+
/**
|
|
5800
|
+
* @param {string} s text to test
|
|
5801
|
+
* @returns {boolean} true when the text is HTML whitespace only
|
|
5802
|
+
*/
|
|
5803
|
+
const isAllWs = (s) => {
|
|
5763
5804
|
for (let i = 0; i < s.length; i++) {
|
|
5764
5805
|
const c = s.charCodeAt(i);
|
|
5765
5806
|
// HTML whitespace: tab / LF / FF / CR / space. A charCodeAt loop avoids
|
|
@@ -5807,10 +5848,18 @@ const collapseWhitespaceRuns = (s) => {
|
|
|
5807
5848
|
return last === 0 ? s : out + s.slice(last);
|
|
5808
5849
|
};
|
|
5809
5850
|
|
|
5810
|
-
|
|
5851
|
+
/**
|
|
5852
|
+
* @param {HtmlElement} el element to test
|
|
5853
|
+
* @returns {boolean} true for a MathML text integration point (§13.2.6.5)
|
|
5854
|
+
*/
|
|
5855
|
+
const mathmlTextIntegrationPoint = (el) =>
|
|
5811
5856
|
_namespaceOf(el) === NS_MATHML && MATHML_TEXT_INTEGRATION.has(_tagNameOf(el));
|
|
5812
5857
|
|
|
5813
|
-
|
|
5858
|
+
/**
|
|
5859
|
+
* @param {HtmlElement} el element to test
|
|
5860
|
+
* @returns {boolean} true for an HTML integration point (§13.2.6.5)
|
|
5861
|
+
*/
|
|
5862
|
+
const htmlIntegrationPoint = (el) => {
|
|
5814
5863
|
if (_namespaceOf(el) === NS_MATHML && _tagNameOf(el) === "annotation-xml") {
|
|
5815
5864
|
const enc = _findAttr(_nodeAttrStarts[el], _nodeAttrCounts[el], "encoding");
|
|
5816
5865
|
if (enc !== 0) {
|
|
@@ -5830,13 +5879,14 @@ const htmlIntegrationPoint = (/** @type {HtmlElement} */ el) => {
|
|
|
5830
5879
|
return false;
|
|
5831
5880
|
};
|
|
5832
5881
|
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5882
|
+
/**
|
|
5883
|
+
* Rewrites SVG camelCase names and flags namespaced ones for the serializer.
|
|
5884
|
+
* In place: the run is consumed only by this tag's element.
|
|
5885
|
+
* @param {AttributeRun} run the tag's attributes
|
|
5886
|
+
* @param {number} ns namespace flag of the tag
|
|
5887
|
+
* @returns {AttributeRun} the same run
|
|
5888
|
+
*/
|
|
5889
|
+
const adjustForeignAttrs = (run, ns) => {
|
|
5840
5890
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
5841
5891
|
const name = _attrNames[i];
|
|
5842
5892
|
if (
|
|
@@ -5854,9 +5904,13 @@ const adjustForeignAttrs = (
|
|
|
5854
5904
|
return run;
|
|
5855
5905
|
};
|
|
5856
5906
|
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5907
|
+
/**
|
|
5908
|
+
* Only `definitionurl` is rewritten (in place — the run is consumed only by
|
|
5909
|
+
* this tag's element); the camelCase name serializes as itself.
|
|
5910
|
+
* @param {AttributeRun} run the tag's attributes
|
|
5911
|
+
* @returns {AttributeRun} the same run
|
|
5912
|
+
*/
|
|
5913
|
+
const adjustMathmlAttrs = (run) => {
|
|
5860
5914
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
5861
5915
|
if (_attrNames[i] === "definitionurl") _attrNames[i] = "definitionURL";
|
|
5862
5916
|
}
|
|
@@ -5956,10 +6010,13 @@ const adjustedCurrent = () => {
|
|
|
5956
6010
|
return cur();
|
|
5957
6011
|
};
|
|
5958
6012
|
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
6013
|
+
/**
|
|
6014
|
+
* Appends a node, merging it into the previous sibling when both are text.
|
|
6015
|
+
* @param {HtmlNodeRef} parent container
|
|
6016
|
+
* @param {HtmlNodeRef} node node to append
|
|
6017
|
+
* @returns {void}
|
|
6018
|
+
*/
|
|
6019
|
+
const appendTo = (parent, node) => {
|
|
5963
6020
|
const p = effParent(parent);
|
|
5964
6021
|
const last = _nodeLastChildren[p];
|
|
5965
6022
|
if (
|
|
@@ -5979,10 +6036,12 @@ const appendTo = (
|
|
|
5979
6036
|
// allocation per inserted node.
|
|
5980
6037
|
/** @type {InsertionPlace} */
|
|
5981
6038
|
const sharedPlace = { parent: doc, beforeNode: 0 };
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
6039
|
+
/**
|
|
6040
|
+
* @param {HtmlNodeRef} parent container
|
|
6041
|
+
* @param {HtmlNodeRef} beforeNode node to insert before (`0` = append)
|
|
6042
|
+
* @returns {InsertionPlace} the shared insertion place
|
|
6043
|
+
*/
|
|
6044
|
+
const placeAt = (parent, beforeNode) => {
|
|
5986
6045
|
sharedPlace.parent = parent;
|
|
5987
6046
|
sharedPlace.beforeNode = beforeNode;
|
|
5988
6047
|
return sharedPlace;
|
|
@@ -6030,10 +6089,13 @@ const appropriatePlace = () => {
|
|
|
6030
6089
|
return placeAt(target, 0);
|
|
6031
6090
|
};
|
|
6032
6091
|
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6092
|
+
/**
|
|
6093
|
+
* Inserts a node at an insertion place, appending when `beforeNode` is `0`.
|
|
6094
|
+
* @param {InsertionPlace} place where the node goes
|
|
6095
|
+
* @param {HtmlNodeRef} node node to insert
|
|
6096
|
+
* @returns {void}
|
|
6097
|
+
*/
|
|
6098
|
+
const insertAtPlace = (place, node) => {
|
|
6037
6099
|
const before = place.beforeNode;
|
|
6038
6100
|
if (before !== 0) {
|
|
6039
6101
|
const p = effParent(place.parent);
|
|
@@ -6084,11 +6146,14 @@ const _recordFostered = (table, node) => {
|
|
|
6084
6146
|
_fosteredLog.push(table, node);
|
|
6085
6147
|
};
|
|
6086
6148
|
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6149
|
+
/**
|
|
6150
|
+
* Inserts a character run at the appropriate place (§13.2.6.1).
|
|
6151
|
+
* @param {string} data decoded text
|
|
6152
|
+
* @param {number} start source offset of the run
|
|
6153
|
+
* @param {number} end source offset past the run
|
|
6154
|
+
* @returns {void}
|
|
6155
|
+
*/
|
|
6156
|
+
const insertCharacters = (data, start, end) => {
|
|
6092
6157
|
const place = appropriatePlace();
|
|
6093
6158
|
if (_nodeTypes[place.parent] === NodeType.Document) return; // never insert text into document
|
|
6094
6159
|
// `skip.text`: drop every `Text` node — construction already used the
|
|
@@ -6149,11 +6214,12 @@ const insertCharacters = (
|
|
|
6149
6214
|
};
|
|
6150
6215
|
|
|
6151
6216
|
/**
|
|
6217
|
+
* Every insertion mode inserts a processing instruction where it inserts a
|
|
6218
|
+
* comment (§13.2.6.4). `skip.comments` drops comments alone.
|
|
6152
6219
|
* @param {CommentToken | ProcessingInstructionToken} t comment or processing instruction token
|
|
6153
6220
|
* @param {InsertionPlace=} place explicit insertion place
|
|
6221
|
+
* @returns {void}
|
|
6154
6222
|
*/
|
|
6155
|
-
// Every insertion mode inserts a processing instruction where it inserts a
|
|
6156
|
-
// comment (§13.2.6.4). `skip.comments` drops comments alone.
|
|
6157
6223
|
const insertCommentOrProcessingInstruction = (t, place) => {
|
|
6158
6224
|
const isPi = t.type === TOKEN_PROCESSING_INSTRUCTION;
|
|
6159
6225
|
if (skipComments && !isPi) return;
|
|
@@ -6166,11 +6232,14 @@ const insertCommentOrProcessingInstruction = (t, place) => {
|
|
|
6166
6232
|
);
|
|
6167
6233
|
};
|
|
6168
6234
|
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6235
|
+
/**
|
|
6236
|
+
* Inserts an HTML element and pushes it onto the open element stack.
|
|
6237
|
+
* @param {string} tagName tag name
|
|
6238
|
+
* @param {AttributeRun} attrs the element's attributes
|
|
6239
|
+
* @param {TagPos | null} pos source position, `null` for a synthesized element
|
|
6240
|
+
* @returns {HtmlElement} the inserted element
|
|
6241
|
+
*/
|
|
6242
|
+
const insertHtmlElement = (tagName, attrs, pos) => {
|
|
6174
6243
|
const el = mkEl(tagName, NS_HTML, attrs, pos);
|
|
6175
6244
|
const place = appropriatePlace();
|
|
6176
6245
|
insertAtPlace(place, el);
|
|
@@ -6178,12 +6247,15 @@ const insertHtmlElement = (
|
|
|
6178
6247
|
return el;
|
|
6179
6248
|
};
|
|
6180
6249
|
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6250
|
+
/**
|
|
6251
|
+
* Inserts a MathML/SVG element and pushes it onto the open element stack.
|
|
6252
|
+
* @param {string} tagName tag name
|
|
6253
|
+
* @param {number} ns namespace flag (`NS_MATHML`, `NS_SVG`)
|
|
6254
|
+
* @param {AttributeRun} attrs the element's attributes
|
|
6255
|
+
* @param {TagPos | null} pos source position, `null` for a synthesized element
|
|
6256
|
+
* @returns {HtmlElement} the inserted element
|
|
6257
|
+
*/
|
|
6258
|
+
const insertForeignElement = (tagName, ns, attrs, pos) => {
|
|
6187
6259
|
const el = mkEl(tagName, ns, attrs, pos);
|
|
6188
6260
|
const place = appropriatePlace();
|
|
6189
6261
|
insertAtPlace(place, el);
|
|
@@ -6198,10 +6270,12 @@ const insertForeignElement = (
|
|
|
6198
6270
|
const SCOPE_DEFAULT = 0;
|
|
6199
6271
|
const SCOPE_BUTTON = 1;
|
|
6200
6272
|
const SCOPE_LIST_ITEM = 2;
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6273
|
+
/**
|
|
6274
|
+
* @param {HtmlElement} el element to test
|
|
6275
|
+
* @param {number} kind scope kind (`SCOPE_DEFAULT`, `SCOPE_BUTTON`, `SCOPE_LIST_ITEM`)
|
|
6276
|
+
* @returns {boolean} true when the element bounds that scope
|
|
6277
|
+
*/
|
|
6278
|
+
const isBoundaryForKind = (el, kind) => {
|
|
6205
6279
|
if (isScopeBoundary(el)) return true;
|
|
6206
6280
|
if (_namespaceOf(el) !== NS_HTML) return false;
|
|
6207
6281
|
if (kind === SCOPE_BUTTON) return _tagNameOf(el) === "button";
|
|
@@ -6210,12 +6284,14 @@ const isBoundaryForKind = (
|
|
|
6210
6284
|
}
|
|
6211
6285
|
return false;
|
|
6212
6286
|
};
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6287
|
+
/**
|
|
6288
|
+
* "have an element in scope": walks the open stack from the top until the
|
|
6289
|
+
* named HTML element is found (true) or a scope boundary is hit (false).
|
|
6290
|
+
* @param {string} tagName tag name to look for
|
|
6291
|
+
* @param {number} kind scope kind (`SCOPE_DEFAULT`, `SCOPE_BUTTON`, `SCOPE_LIST_ITEM`)
|
|
6292
|
+
* @returns {boolean} true when the element is in that scope
|
|
6293
|
+
*/
|
|
6294
|
+
const hasNameInScope = (tagName, kind) => {
|
|
6219
6295
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
6220
6296
|
const el = open[i];
|
|
6221
6297
|
if (_namespaceOf(el) === NS_HTML && _tagNameOf(el) === tagName) {
|
|
@@ -6225,13 +6301,26 @@ const hasNameInScope = (
|
|
|
6225
6301
|
}
|
|
6226
6302
|
return false;
|
|
6227
6303
|
};
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
const
|
|
6233
|
-
|
|
6234
|
-
|
|
6304
|
+
/**
|
|
6305
|
+
* @param {string} tagName tag name to look for
|
|
6306
|
+
* @returns {boolean} true when the element is in scope
|
|
6307
|
+
*/
|
|
6308
|
+
const inScope = (tagName) => hasNameInScope(tagName, SCOPE_DEFAULT);
|
|
6309
|
+
/**
|
|
6310
|
+
* @param {string} tagName tag name to look for
|
|
6311
|
+
* @returns {boolean} true when the element is in button scope
|
|
6312
|
+
*/
|
|
6313
|
+
const inButtonScope = (tagName) => hasNameInScope(tagName, SCOPE_BUTTON);
|
|
6314
|
+
/**
|
|
6315
|
+
* @param {string} tagName tag name to look for
|
|
6316
|
+
* @returns {boolean} true when the element is in list item scope
|
|
6317
|
+
*/
|
|
6318
|
+
const inListItemScope = (tagName) => hasNameInScope(tagName, SCOPE_LIST_ITEM);
|
|
6319
|
+
/**
|
|
6320
|
+
* @param {HtmlElement} target element to look for
|
|
6321
|
+
* @returns {boolean} true when that element itself is in scope
|
|
6322
|
+
*/
|
|
6323
|
+
const inScopeEl = (target) => {
|
|
6235
6324
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
6236
6325
|
const el = open[i];
|
|
6237
6326
|
if (el === target) return true;
|
|
@@ -6239,8 +6328,11 @@ const inScopeEl = (/** @type {HtmlElement} */ target) => {
|
|
|
6239
6328
|
}
|
|
6240
6329
|
return false;
|
|
6241
6330
|
};
|
|
6242
|
-
|
|
6243
|
-
|
|
6331
|
+
/**
|
|
6332
|
+
* @param {string | Set<string>} target a single tag name (the common case) or a set of names
|
|
6333
|
+
* @returns {boolean} true when one of them is in table scope
|
|
6334
|
+
*/
|
|
6335
|
+
const inTableScope = (target) => {
|
|
6244
6336
|
const set = typeof target === "string" ? null : target;
|
|
6245
6337
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
6246
6338
|
const el = open[i];
|
|
@@ -6279,15 +6371,25 @@ const generateImpliedEndTagsThorough = () => {
|
|
|
6279
6371
|
};
|
|
6280
6372
|
|
|
6281
6373
|
// ---- active formatting elements ----
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6374
|
+
|
|
6375
|
+
/**
|
|
6376
|
+
* `splice` allocates an array for the removed element on every call; the list
|
|
6377
|
+
* is short, so shifting in place is both cheaper and allocation-free.
|
|
6378
|
+
* @param {number} i position in `afe` to remove
|
|
6379
|
+
* @returns {void}
|
|
6380
|
+
*/
|
|
6285
6381
|
const afeRemoveAt = (i) => {
|
|
6286
6382
|
const last = afe.length - 1;
|
|
6287
6383
|
for (let k = i; k < last; k++) afe[k] = afe[k + 1];
|
|
6288
6384
|
afe.pop();
|
|
6289
6385
|
};
|
|
6290
|
-
|
|
6386
|
+
/**
|
|
6387
|
+
* Pushes a formatting element, applying the "Noah's Ark" clause: a third
|
|
6388
|
+
* equal entry since the last marker drops the earliest of them.
|
|
6389
|
+
* @param {HtmlElement} el formatting element to push
|
|
6390
|
+
* @returns {void}
|
|
6391
|
+
*/
|
|
6392
|
+
const pushAfe = (el) => {
|
|
6291
6393
|
let count = 0;
|
|
6292
6394
|
for (let i = afe.length - 1; i >= 0; i--) {
|
|
6293
6395
|
const e = afe[i];
|
|
@@ -6349,7 +6451,12 @@ const closePElement = () => {
|
|
|
6349
6451
|
}
|
|
6350
6452
|
};
|
|
6351
6453
|
|
|
6352
|
-
|
|
6454
|
+
/**
|
|
6455
|
+
* Pops the open element stack through the named HTML element.
|
|
6456
|
+
* @param {string} tagName tag name to pop through
|
|
6457
|
+
* @returns {void}
|
|
6458
|
+
*/
|
|
6459
|
+
const popUntil = (tagName) => {
|
|
6353
6460
|
while (open.length) {
|
|
6354
6461
|
const el = /** @type {HtmlElement} */ (popOpen());
|
|
6355
6462
|
_nodeEnds[el] = tokenEnd;
|
|
@@ -6358,7 +6465,12 @@ const popUntil = (/** @type {string} */ tagName) => {
|
|
|
6358
6465
|
}
|
|
6359
6466
|
}
|
|
6360
6467
|
};
|
|
6361
|
-
|
|
6468
|
+
/**
|
|
6469
|
+
* Pops the open element stack through the first of the named HTML elements.
|
|
6470
|
+
* @param {Set<string>} set tag names to pop through
|
|
6471
|
+
* @returns {void}
|
|
6472
|
+
*/
|
|
6473
|
+
const popUntilOneOf = (set) => {
|
|
6362
6474
|
while (open.length) {
|
|
6363
6475
|
const el = /** @type {HtmlElement} */ (popOpen());
|
|
6364
6476
|
_nodeEnds[el] = tokenEnd;
|
|
@@ -6432,14 +6544,14 @@ const resetInsertionMode = () => {
|
|
|
6432
6544
|
};
|
|
6433
6545
|
|
|
6434
6546
|
// ---------- token processing ----------
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
) => {
|
|
6547
|
+
/**
|
|
6548
|
+
* Splits a character token's leading whitespace; per the spec each character
|
|
6549
|
+
* is its own token, so a mixed run can straddle a mode change.
|
|
6550
|
+
* @param {CharToken} t character token
|
|
6551
|
+
* @param {boolean} insert whether to insert the leading whitespace
|
|
6552
|
+
* @returns {CharToken | null} the non-whitespace remainder, `null` when the token was all whitespace
|
|
6553
|
+
*/
|
|
6554
|
+
const leadingWs = (t, insert) => {
|
|
6443
6555
|
const m = /^[\t\n\f\r ]+/.exec(t.data);
|
|
6444
6556
|
const ws = m ? m[0] : "";
|
|
6445
6557
|
// `t.data` is decoded, the offsets are raw, so counting one in the other
|
|
@@ -6458,7 +6570,12 @@ const leadingWs = (
|
|
|
6458
6570
|
};
|
|
6459
6571
|
};
|
|
6460
6572
|
|
|
6461
|
-
|
|
6573
|
+
/**
|
|
6574
|
+
* Runs one token through tree construction (§13.2.6).
|
|
6575
|
+
* @param {Token} t token to process
|
|
6576
|
+
* @returns {void}
|
|
6577
|
+
*/
|
|
6578
|
+
const process = (t) => {
|
|
6462
6579
|
// Track the current token's end so explicit closes can set element `.end`.
|
|
6463
6580
|
// Dispatch on `type` instead of the `in` operator, which goes megamorphic
|
|
6464
6581
|
// across the token union and shows up on the per-token hot path.
|
|
@@ -6480,10 +6597,12 @@ const process = (/** @type {Token} */ t) => {
|
|
|
6480
6597
|
runMode(t);
|
|
6481
6598
|
};
|
|
6482
6599
|
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6600
|
+
/**
|
|
6601
|
+
* @param {HtmlElement} ac adjusted current node
|
|
6602
|
+
* @param {Token} t token to process
|
|
6603
|
+
* @returns {boolean} true when the token follows the foreign content rules
|
|
6604
|
+
*/
|
|
6605
|
+
const shouldUseForeignRules = (ac, t) => {
|
|
6487
6606
|
if (_namespaceOf(ac) === NS_HTML) return false;
|
|
6488
6607
|
if (t.type === TOKEN_START_TAG) {
|
|
6489
6608
|
if (
|
|
@@ -6513,7 +6632,12 @@ const shouldUseForeignRules = (
|
|
|
6513
6632
|
return false;
|
|
6514
6633
|
};
|
|
6515
6634
|
|
|
6516
|
-
|
|
6635
|
+
/**
|
|
6636
|
+
* "in foreign content" rules (§13.2.6.5).
|
|
6637
|
+
* @param {Token} t token to process
|
|
6638
|
+
* @returns {void}
|
|
6639
|
+
*/
|
|
6640
|
+
const foreignContent = (t) => {
|
|
6517
6641
|
if (t.type === TOKEN_CHAR) {
|
|
6518
6642
|
const data = inputHasNul ? t.data.replace(/\0/g, "�") : t.data;
|
|
6519
6643
|
insertCharacters(data, t.start, t.end);
|
|
@@ -6612,10 +6736,13 @@ const foreignContent = (/** @type {Token} */ t) => {
|
|
|
6612
6736
|
};
|
|
6613
6737
|
|
|
6614
6738
|
// ---------- adoption agency algorithm ----------
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6739
|
+
/**
|
|
6740
|
+
* Adoption agency algorithm (§13.2.6.4.7).
|
|
6741
|
+
* @param {string} subject tag name of the end tag being handled
|
|
6742
|
+
* @param {TagPos | null} pos source position of that end tag
|
|
6743
|
+
* @returns {boolean} false when the caller must fall back to "any other end tag"
|
|
6744
|
+
*/
|
|
6745
|
+
const adoptionAgency = (subject, pos) => {
|
|
6619
6746
|
// step 1
|
|
6620
6747
|
const c = cur();
|
|
6621
6748
|
if (
|
|
@@ -6752,7 +6879,11 @@ const adoptionAgency = (
|
|
|
6752
6879
|
return true;
|
|
6753
6880
|
};
|
|
6754
6881
|
|
|
6755
|
-
|
|
6882
|
+
/**
|
|
6883
|
+
* @param {HtmlElement} commonAncestor common ancestor node of the adoption agency
|
|
6884
|
+
* @returns {InsertionPlace} where the moved node goes
|
|
6885
|
+
*/
|
|
6886
|
+
const placeForCommonAncestor = (commonAncestor) => {
|
|
6756
6887
|
if (
|
|
6757
6888
|
TABLE_CONTEXT.has(_tagNameOf(commonAncestor)) &&
|
|
6758
6889
|
_namespaceOf(commonAncestor) === NS_HTML
|
|
@@ -6766,7 +6897,12 @@ const placeForCommonAncestor = (/** @type {HtmlElement} */ commonAncestor) => {
|
|
|
6766
6897
|
return { parent: commonAncestor, beforeNode: 0 };
|
|
6767
6898
|
};
|
|
6768
6899
|
|
|
6769
|
-
|
|
6900
|
+
/**
|
|
6901
|
+
* Unlinks a node from its parent so it can be inserted again.
|
|
6902
|
+
* @param {HtmlNodeRef} node node to detach
|
|
6903
|
+
* @returns {void}
|
|
6904
|
+
*/
|
|
6905
|
+
const detach = (node) => {
|
|
6770
6906
|
const p = _nodeParents[node];
|
|
6771
6907
|
if (p === 0) return;
|
|
6772
6908
|
let prev = 0;
|
|
@@ -6788,11 +6924,13 @@ const detach = (/** @type {HtmlNodeRef} */ node) => {
|
|
|
6788
6924
|
/** @type {Record<string, (t: Token) => void>} */
|
|
6789
6925
|
const modes = {};
|
|
6790
6926
|
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6927
|
+
/**
|
|
6928
|
+
* Dispatches the current insertion mode. An integer switch keeps each
|
|
6929
|
+
* `modes.x(t)` call site monomorphic, where `modes[mode]` would not.
|
|
6930
|
+
* @param {Token} t token to process
|
|
6931
|
+
* @returns {void}
|
|
6932
|
+
*/
|
|
6933
|
+
const runMode = (t) => {
|
|
6796
6934
|
switch (mode) {
|
|
6797
6935
|
case MODE_IN_BODY:
|
|
6798
6936
|
return modes.inBody(t);
|
|
@@ -7137,9 +7275,13 @@ const closeIfPInButtonScope = () => {
|
|
|
7137
7275
|
if (inButtonScope("p")) closePElement();
|
|
7138
7276
|
};
|
|
7139
7277
|
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7278
|
+
/**
|
|
7279
|
+
* "any other end tag" in body: pops to the matching open element, stopping at
|
|
7280
|
+
* the first special element; also the adoption agency's no-element fallback.
|
|
7281
|
+
* @param {string} name tag name of the end tag
|
|
7282
|
+
* @returns {void}
|
|
7283
|
+
*/
|
|
7284
|
+
const anyOtherEndTag = (name) => {
|
|
7143
7285
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
7144
7286
|
const node = open[i];
|
|
7145
7287
|
if (_namespaceOf(node) === NS_HTML && _tagNameOf(node) === name) {
|
|
@@ -7154,7 +7296,12 @@ const anyOtherEndTag = (/** @type {string} */ name) => {
|
|
|
7154
7296
|
}
|
|
7155
7297
|
};
|
|
7156
7298
|
|
|
7157
|
-
|
|
7299
|
+
/**
|
|
7300
|
+
* "in body" start tag rules (§13.2.6.4.7).
|
|
7301
|
+
* @param {StartTagToken} t start tag token
|
|
7302
|
+
* @returns {void}
|
|
7303
|
+
*/
|
|
7304
|
+
const startTagInBody = (t) => {
|
|
7158
7305
|
const name = t.name;
|
|
7159
7306
|
if (name === "html") {
|
|
7160
7307
|
if (open.some(isHtmlTemplateEl)) {
|
|
@@ -7479,7 +7626,12 @@ const startTagInBody = (/** @type {StartTagToken} */ t) => {
|
|
|
7479
7626
|
insertHtmlElement(name, t.attrs, t.pos);
|
|
7480
7627
|
};
|
|
7481
7628
|
|
|
7482
|
-
|
|
7629
|
+
/**
|
|
7630
|
+
* "in body" end tag rules (§13.2.6.4.7).
|
|
7631
|
+
* @param {EndTagToken} t end tag token
|
|
7632
|
+
* @returns {void}
|
|
7633
|
+
*/
|
|
7634
|
+
const endTagInBody = (t) => {
|
|
7483
7635
|
const name = t.name;
|
|
7484
7636
|
if (name === "template") return modes.inHead(t);
|
|
7485
7637
|
if (name === "select") {
|
|
@@ -7573,15 +7725,24 @@ const endTagInBody = (/** @type {EndTagToken} */ t) => {
|
|
|
7573
7725
|
anyOtherEndTag(name);
|
|
7574
7726
|
};
|
|
7575
7727
|
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7728
|
+
/**
|
|
7729
|
+
* Generic RAWTEXT: inserts the element and switches to "text" mode, which
|
|
7730
|
+
* appends the characters the tokenizer already emits and pops on the end tag.
|
|
7731
|
+
* @param {StartTagToken} t start tag token
|
|
7732
|
+
* @returns {void}
|
|
7733
|
+
*/
|
|
7734
|
+
const genericRawtext = (t) => {
|
|
7580
7735
|
insertHtmlElement(t.name, t.attrs, t.pos);
|
|
7581
7736
|
originalMode = mode;
|
|
7582
7737
|
mode = MODE_TEXT;
|
|
7583
7738
|
};
|
|
7584
|
-
|
|
7739
|
+
/**
|
|
7740
|
+
* Generic RCDATA, as `genericRawtext` for the elements whose text is decoded.
|
|
7741
|
+
* @param {StartTagToken} t start tag token
|
|
7742
|
+
* @param {boolean=} swallow whether a newline right after the tag is dropped
|
|
7743
|
+
* @returns {void}
|
|
7744
|
+
*/
|
|
7745
|
+
const genericRcdata = (t, swallow = false) => {
|
|
7585
7746
|
insertHtmlElement(t.name, t.attrs, t.pos);
|
|
7586
7747
|
if (swallow) t.swallowNewline = true;
|
|
7587
7748
|
originalMode = mode;
|
|
@@ -11508,11 +11669,19 @@ const _settleFosteredRuns = (path) => {
|
|
|
11508
11669
|
_fosterHolders = holders;
|
|
11509
11670
|
};
|
|
11510
11671
|
|
|
11672
|
+
// Stands in for what the print chooses the bytes of: a raw-text body it
|
|
11673
|
+
// rewrites whole, or an attribute value it respells.
|
|
11674
|
+
const _DIGEST_OPAQUE = "\u0003";
|
|
11675
|
+
|
|
11511
11676
|
/**
|
|
11512
11677
|
* The tree as a string, for the one check the printer cannot make from the tree
|
|
11513
|
-
* alone: whether what it printed parses back to what it printed from.
|
|
11514
|
-
*
|
|
11515
|
-
*
|
|
11678
|
+
* alone: whether what it printed parses back to what it printed from. Read in
|
|
11679
|
+
* the form the print is licensed to leave — comments gone, a run of text
|
|
11680
|
+
* collapsed and joined, the elements and attributes the options drop dropped,
|
|
11681
|
+
* every value the printer respells standing in for itself — so an equal digest
|
|
11682
|
+
* says the print moved nothing, not that it changed nothing. What a repair does
|
|
11683
|
+
* move (a node under another parent, an attribute a bad quote swallowed)
|
|
11684
|
+
* reshapes the tree, and that is what parts the two.
|
|
11516
11685
|
* @param {HtmlNodeRef} root the node whose children are read
|
|
11517
11686
|
* @returns {string} a rendering equal for equal trees
|
|
11518
11687
|
*/
|
|
@@ -11525,6 +11694,15 @@ const _digest = (root) => {
|
|
|
11525
11694
|
const stack = [];
|
|
11526
11695
|
const first = A.firstChild(root);
|
|
11527
11696
|
if (first !== 0) stack.push(first, 0);
|
|
11697
|
+
// A run of text siblings reads as one string: dropping the comment or the
|
|
11698
|
+
// empty element between two of them is what joins them in the output.
|
|
11699
|
+
let pendingText = "";
|
|
11700
|
+
let pendingDepth = 0;
|
|
11701
|
+
const flushText = () => {
|
|
11702
|
+
const text = _asciiTrim(pendingText);
|
|
11703
|
+
pendingText = "";
|
|
11704
|
+
if (text !== "") out.push(`${pendingDepth}\u0001t\u0001${text}`);
|
|
11705
|
+
};
|
|
11528
11706
|
while (stack.length !== 0) {
|
|
11529
11707
|
const depth = /** @type {number} */ (stack.pop());
|
|
11530
11708
|
const node = /** @type {HtmlNodeRef} */ (stack.pop());
|
|
@@ -11532,27 +11710,87 @@ const _digest = (root) => {
|
|
|
11532
11710
|
const next = A.nextSibling(node);
|
|
11533
11711
|
if (next !== 0) stack.push(next, depth);
|
|
11534
11712
|
const type = A.type(node);
|
|
11535
|
-
|
|
11536
|
-
|
|
11713
|
+
// Not flushed: a comment minifying drops leaves the text around it one run.
|
|
11714
|
+
if (type === NodeType.Comment) continue;
|
|
11715
|
+
if (type === NodeType.Text) {
|
|
11716
|
+
if (depth !== pendingDepth) flushText();
|
|
11717
|
+
pendingDepth = depth;
|
|
11718
|
+
const parent = A.parentOf(node);
|
|
11719
|
+
pendingText +=
|
|
11720
|
+
parent !== 0 && LITERAL_TEXT_PARENTS.has(A.tagName(parent))
|
|
11721
|
+
? _DIGEST_OPAQUE
|
|
11722
|
+
: collapseWhitespaceRuns(A.data(node));
|
|
11537
11723
|
continue;
|
|
11538
11724
|
}
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
);
|
|
11725
|
+
if (type === NodeType.Element) {
|
|
11726
|
+
// Same as a comment: an element the print drops joins what surrounds it.
|
|
11727
|
+
if (
|
|
11728
|
+
_isRemovableEmptyElement(A, node) ||
|
|
11729
|
+
(_absorbedStyles !== null && _absorbedStyles.has(node))
|
|
11730
|
+
) {
|
|
11731
|
+
continue;
|
|
11732
|
+
}
|
|
11733
|
+
flushText();
|
|
11734
|
+
const elementName = A.tagName(node);
|
|
11735
|
+
const html = A.namespace(node) === NS_HTML;
|
|
11736
|
+
out.push(`${depth}\u0001e\u0001${A.namespace(node)}\u0001${elementName}`);
|
|
11737
|
+
// Per element, as in `_minifyOpenTag`: every attribute of one tag gets the
|
|
11738
|
+
// same answer out of these tables.
|
|
11739
|
+
const markers =
|
|
11740
|
+
html && _removeRedundantAttributes !== "none"
|
|
11741
|
+
? REDUNDANT_TYPE_ATTRIBUTES[elementName]
|
|
11742
|
+
: undefined;
|
|
11743
|
+
const defaults =
|
|
11744
|
+
html && _removeRedundantAttributes === "all"
|
|
11745
|
+
? REDUNDANT_DEFAULT_ATTRIBUTES[elementName]
|
|
11746
|
+
: undefined;
|
|
11747
|
+
const attributes = [];
|
|
11748
|
+
for (const a of A.attributes(node)) {
|
|
11749
|
+
if (
|
|
11750
|
+
html &&
|
|
11751
|
+
(((markers !== undefined || defaults !== undefined) &&
|
|
11752
|
+
_isRedundantAttribute(
|
|
11753
|
+
markers,
|
|
11754
|
+
defaults,
|
|
11755
|
+
elementName,
|
|
11756
|
+
a.name,
|
|
11757
|
+
a.value
|
|
11758
|
+
)) ||
|
|
11759
|
+
_isRemovableEmptyAttribute(elementName, a.name, a.value))
|
|
11760
|
+
) {
|
|
11761
|
+
continue;
|
|
11762
|
+
}
|
|
11763
|
+
const name = a.serializedName || a.name;
|
|
11764
|
+
if (REWRITABLE_ATTRIBUTES.has(a.name) || a.name === "srcdoc") {
|
|
11765
|
+
attributes.push(`${name}=${_DIGEST_OPAQUE}`);
|
|
11766
|
+
continue;
|
|
11767
|
+
}
|
|
11768
|
+
// The value as the DOM holds it — the same value can be written with a
|
|
11769
|
+
// different quote and a different escape and still be the same
|
|
11770
|
+
// attribute — with a boolean spelled by its own name as the bare
|
|
11771
|
+
// presence the printer writes it back as.
|
|
11772
|
+
attributes.push(
|
|
11773
|
+
`${name}=${
|
|
11774
|
+
html &&
|
|
11775
|
+
a.value.length === a.name.length &&
|
|
11776
|
+
_isBooleanAttribute(elementName, a.name) &&
|
|
11777
|
+
_asciiLowerCase(a.value) === a.name
|
|
11778
|
+
? ""
|
|
11779
|
+
: decodeEntities(a.value, true)
|
|
11780
|
+
}`
|
|
11781
|
+
);
|
|
11782
|
+
}
|
|
11783
|
+
attributes.sort();
|
|
11784
|
+
out.push(attributes.join("\u0002"));
|
|
11785
|
+
const content = A.templateContent(node);
|
|
11786
|
+
const inner = content !== 0 ? A.firstChild(content) : A.firstChild(node);
|
|
11787
|
+
if (inner !== 0) stack.push(inner, depth + 1);
|
|
11788
|
+
continue;
|
|
11549
11789
|
}
|
|
11550
|
-
|
|
11551
|
-
out.push(
|
|
11552
|
-
const content = A.templateContent(node);
|
|
11553
|
-
const inner = content !== 0 ? A.firstChild(content) : A.firstChild(node);
|
|
11554
|
-
if (inner !== 0) stack.push(inner, depth + 1);
|
|
11790
|
+
flushText();
|
|
11791
|
+
out.push(`${depth}\u0001${type}\u0001${A.data(node)}`);
|
|
11555
11792
|
}
|
|
11793
|
+
flushText();
|
|
11556
11794
|
return out.join("\n");
|
|
11557
11795
|
};
|
|
11558
11796
|
|
|
@@ -12549,12 +12787,11 @@ const grammar = (input, visitors, writer, options) => {
|
|
|
12549
12787
|
_printDepth = 0;
|
|
12550
12788
|
try {
|
|
12551
12789
|
const root = parseHtml(input, 0, options);
|
|
12552
|
-
let digest = "";
|
|
12553
12790
|
if (writer !== undefined) {
|
|
12554
12791
|
if (_fosteredLog !== null) _settleFosteredRuns(A);
|
|
12555
12792
|
// A shape no arrangement of tags reaches, or foster parenting — whose repair
|
|
12556
12793
|
// is checked, not trusted. No real document reaches either.
|
|
12557
|
-
|
|
12794
|
+
const verify = _printFromSource || _fosteredLog !== null;
|
|
12558
12795
|
_walkRun(_nodeParents[root], root, _nodeNextSiblings[root]);
|
|
12559
12796
|
// A single root: take its text as the output (the printer read every node's
|
|
12560
12797
|
// source into the store during the walk, so this needs no source). A root
|
|
@@ -12562,9 +12799,20 @@ const grammar = (input, visitors, writer, options) => {
|
|
|
12562
12799
|
if (_printOpen[0] === 1) writer.dropStore();
|
|
12563
12800
|
else writer.take(root);
|
|
12564
12801
|
// Print, then read the output back: the source always parses to its own
|
|
12565
|
-
// tree, so it stands in when the printed form does not.
|
|
12566
|
-
|
|
12567
|
-
|
|
12802
|
+
// tree, so it stands in when the printed form does not. Digested after the
|
|
12803
|
+
// walk, which is what settles the elements the print left out.
|
|
12804
|
+
if (verify) {
|
|
12805
|
+
const digest = _digest(root);
|
|
12806
|
+
// A deferred body prints as a marker, which reads back as text where
|
|
12807
|
+
// its node stood — put it back, or the whole document is handed back.
|
|
12808
|
+
const printed =
|
|
12809
|
+
_deferEmbeddedSource === undefined ||
|
|
12810
|
+
_deferEmbeddedSource.length === 0
|
|
12811
|
+
? writer.result()
|
|
12812
|
+
: declineDeferredWrites(writer.result(), _deferEmbeddedSource);
|
|
12813
|
+
// Both are keyed by node id, which the next parse hands out afresh.
|
|
12814
|
+
_emptyElements.clear();
|
|
12815
|
+
_absorbedStyles = null;
|
|
12568
12816
|
if (_digest(parseHtml(printed, 0, options)) !== digest) {
|
|
12569
12817
|
writer.replaceAll(input);
|
|
12570
12818
|
}
|