webpack 5.110.2 → 5.111.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 +5 -2
- package/lib/APIPlugin.js +23 -7
- package/lib/AutomaticPrefetchPlugin.js +1 -1
- package/lib/Cache.js +8 -3
- package/lib/CacheFacade.js +1 -1
- package/lib/ChunkGraph.js +20 -18
- package/lib/CircularModulesPlugin.js +157 -55
- package/lib/CleanPlugin.js +2 -2
- package/lib/Compilation.js +321 -303
- package/lib/Compiler.js +209 -46
- package/lib/ConcatenationScope.js +25 -35
- package/lib/ContextModule.js +17 -81
- package/lib/ContextModuleFactory.js +1 -1
- package/lib/CopyPlugin.js +1054 -0
- package/lib/DefinePlugin.js +24 -8
- package/lib/DotenvPlugin.js +41 -13
- package/lib/ExternalModule.js +41 -14
- package/lib/FileSystemInfo.js +17 -14
- package/lib/FlagDependencyExportsPlugin.js +45 -21
- package/lib/FlagDependencyUsagePlugin.js +8 -2
- package/lib/HotModuleReplacementPlugin.js +53 -7
- package/lib/JavascriptMetaInfoPlugin.js +5 -3
- package/lib/MainTemplate.js +5 -1
- package/lib/Module.js +16 -7
- package/lib/MultiCompiler.js +1 -1
- package/lib/MultiWatching.js +1 -1
- package/lib/NormalModule.js +58 -32
- package/lib/NormalModuleFactory.js +1 -1
- package/lib/RuntimeGlobals.js +21 -7
- package/lib/RuntimeModule.js +28 -12
- package/lib/RuntimePlugin.js +33 -3
- package/lib/RuntimeTemplate.js +794 -477
- package/lib/SourceMapDevToolPlugin.js +1 -1
- package/lib/Watching.js +9 -0
- package/lib/WebpackOptionsApply.js +84 -91
- package/lib/asset/AssetGenerator.js +25 -8
- package/lib/asset/AssetModule.js +3 -0
- package/lib/asset/WebManifestParser.js +7 -5
- package/lib/async-modules/AsyncModuleHelpers.js +10 -4
- package/lib/cache/AddBuildDependenciesPlugin.js +33 -4
- package/lib/cache/IdleFileCachePlugin.js +2 -2
- package/lib/cache/PackFileCacheStrategy.js +14 -1
- package/lib/config/browserslistTargetHandler.js +19 -0
- package/lib/config/defaults.js +110 -64
- package/lib/config/normalization.js +100 -20
- package/lib/config/target.js +10 -0
- package/lib/container/ContainerReferencePlugin.js +6 -0
- package/lib/container/RemoteRuntimeModule.js +9 -0
- package/lib/container/declaredRemotes.js +39 -0
- package/lib/css/CssGenerator.js +8 -0
- package/lib/css/CssInjectStyleRuntimeModule.js +9 -0
- package/lib/css/CssLoadingRuntimeModule.js +94 -36
- package/lib/css/CssModule.js +1 -1
- package/lib/css/CssModulesPlugin.js +7 -6
- package/lib/css/CssParser.js +144 -11
- package/lib/css/CssServerStylesRuntimeModule.js +9 -0
- package/lib/css/cssMinify.js +11 -3
- package/lib/css/data.js +2814 -678
- package/lib/css/syntax.js +4082 -248
- package/lib/debug/ProfilingPlugin.js +4 -0
- package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -3
- package/lib/dependencies/AMDRuntimeModules.js +18 -0
- package/lib/dependencies/CommonJsExportRequireDependency.js +3 -1
- package/lib/dependencies/CommonJsExportsParserPlugin.js +12 -13
- package/lib/dependencies/CommonJsImportsParserPlugin.js +1 -0
- package/lib/dependencies/CommonJsPlugin.js +19 -3
- package/lib/dependencies/CommonJsRequireDependency.js +32 -9
- package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +20 -7
- package/lib/dependencies/CssIcssExportDependency.js +7 -1
- package/lib/dependencies/HarmonyCompatibilityDependency.js +58 -4
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +8 -3
- package/lib/dependencies/HarmonyExportExpressionDependency.js +44 -29
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +171 -32
- package/lib/dependencies/HarmonyExportInitFragment.js +99 -19
- package/lib/dependencies/HarmonyExportSpecifierDependency.js +18 -30
- package/lib/dependencies/HarmonyImportBareSideEffectDependency.js +59 -0
- package/lib/dependencies/HarmonyImportDependency.js +99 -41
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +20 -3
- package/lib/dependencies/HarmonyImportSpecifierDependency.js +24 -1
- package/lib/dependencies/HarmonyModulesPlugin.js +20 -0
- package/lib/dependencies/HtmlEntryDependency.js +62 -2
- package/lib/dependencies/ImportContextDependency.js +43 -3
- package/lib/dependencies/ImportDependency.js +8 -29
- package/lib/dependencies/ImportMetaMainDependency.js +104 -0
- package/lib/dependencies/ImportMetaPlugin.js +59 -15
- package/lib/dependencies/ImportParserPlugin.js +24 -14
- package/lib/dependencies/RequireHeaderDependency.js +20 -21
- package/lib/dependencies/SystemRuntimeModule.js +9 -0
- package/lib/dependencies/WorkerDependency.js +1 -2
- package/lib/dependencies/WorkletDependency.js +1 -2
- package/lib/dependencies/importOptionsCheck.js +47 -0
- package/lib/dll/DelegatedModule.js +18 -1
- package/lib/dll/DllReferencePlugin.js +11 -8
- package/lib/dll/LibManifestPlugin.js +1 -1
- package/lib/errors/AnalyzableBailoutsWarning.js +41 -0
- package/lib/errors/DotenvFileError.js +29 -0
- package/lib/errors/DuplicateModulesWarning.js +12 -7
- package/lib/errors/EmptyCopyPatternWarning.js +26 -0
- package/lib/errors/ExternalStarReexportError.js +44 -0
- package/lib/errors/ImportedBindingAssignmentError.js +30 -0
- package/lib/errors/MissingSourceMapsWarning.js +36 -0
- package/lib/errors/ModuleParseError.js +2 -1
- package/lib/errors/UnusedAssetsWarning.js +36 -0
- package/lib/errors/UnusedFederationWarning.js +35 -0
- package/lib/errors/UnusedModulesWarning.js +38 -0
- package/lib/esm/ExportWebpackRequireRuntimeModule.js +9 -0
- package/lib/esm/ModuleChunkFormatPlugin.js +28 -3
- package/lib/esm/ModuleChunkLoadingPlugin.js +30 -11
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +92 -60
- package/lib/hmr/HotModuleReplacement.runtime.js +8 -2
- package/lib/hmr/HotModuleReplacementRuntimeModule.js +9 -0
- package/lib/html/HtmlGenerator.js +11 -5
- package/lib/html/HtmlModulesPlugin.js +360 -84
- package/lib/html/HtmlParser.js +13 -6
- package/lib/html/builtinEmbeddedRenderer.js +107 -0
- package/lib/html/data.js +121 -7
- package/lib/html/htmlMinify.js +84 -113
- package/lib/html/syntax.js +1067 -569
- package/lib/ids/HashedModuleIdsPlugin.js +2 -1
- package/lib/index.js +3 -0
- package/lib/javascript/JavascriptModule.js +1 -0
- package/lib/javascript/JavascriptModulesPlugin.js +52 -22
- package/lib/javascript/JavascriptParser.js +1255 -728
- package/lib/javascript/StartupHelpers.js +27 -3
- package/lib/javascript/data.js +172 -0
- package/lib/javascript/grammar.js +5546 -0
- package/lib/javascript/parser.js +1494 -0
- package/lib/javascript/regexp.js +1762 -0
- package/lib/javascript/regexpData.js +51 -0
- package/lib/javascript/syntax.js +8696 -5225
- package/lib/library/ModuleLibraryPlugin.js +116 -5
- package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -0
- package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -0
- package/lib/optimize/ConcatenatedModule.js +133 -25
- package/lib/optimize/ConstExportsPlugin.js +50 -39
- package/lib/optimize/InlineExports.js +20 -123
- package/lib/optimize/MangleExportsPlugin.js +10 -0
- package/lib/optimize/ModuleConcatenationPlugin.js +24 -1
- package/lib/optimize/RealContentHashPlugin.js +68 -9
- package/lib/optimize/SideEffectsFlagPlugin.js +17 -9
- package/lib/performance/AnalyzableBailoutsPlugin.js +115 -0
- package/lib/performance/CacheEffectivenessPlugin.js +4 -3
- package/lib/performance/DuplicateModulesPlugin.js +57 -13
- package/lib/performance/EvalUsagePlugin.js +5 -2
- package/lib/performance/MissingSourceMapsPlugin.js +115 -0
- package/lib/performance/PureAnnotationsPlugin.js +6 -3
- package/lib/performance/TopLevelThisPlugin.js +5 -4
- package/lib/performance/UnusedAssetsPlugin.js +109 -0
- package/lib/performance/UnusedFederationPlugin.js +147 -0
- package/lib/performance/UnusedModulesPlugin.js +141 -0
- package/lib/performance/UnusedReexportsPlugin.js +1 -55
- package/lib/performance/isOnlyReexported.js +65 -0
- package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +25 -5
- package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +2 -0
- package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +9 -0
- package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +9 -0
- package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +9 -0
- package/lib/prefetch/ResourceHintPlugin.js +7 -3
- package/lib/prefetch/ResourceHintRuntimeModule.js +9 -0
- package/lib/prefetch/StartupAssetHintRuntimeModule.js +9 -0
- package/lib/rules/RuleSetCompiler.js +211 -4
- package/lib/runtime/AsyncModuleRuntimeModule.js +105 -67
- package/lib/runtime/AutoPublicPathRuntimeModule.js +9 -0
- package/lib/runtime/BaseUriRuntimeModule.js +9 -0
- package/lib/runtime/ChunkNameRuntimeModule.js +9 -0
- package/lib/runtime/EnsureChunkRuntimeModule.js +35 -16
- package/lib/runtime/GetChunkFilenameRuntimeModule.js +9 -0
- package/lib/runtime/GetFullHashRuntimeModule.js +9 -0
- package/lib/runtime/GetMainFilenameRuntimeModule.js +9 -0
- package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +9 -0
- package/lib/runtime/GlobalRuntimeModule.js +9 -0
- package/lib/runtime/HasOwnPropertyRuntimeModule.js +9 -0
- package/lib/runtime/HelperRuntimeModule.js +10 -0
- package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +338 -217
- package/lib/runtime/NonceRuntimeModule.js +9 -0
- package/lib/runtime/OnChunksLoadedRuntimeModule.js +67 -20
- package/lib/runtime/PublicPathRuntimeModule.js +9 -0
- package/lib/runtime/RuntimeIdRuntimeModule.js +9 -0
- package/lib/runtime/SpecNamespaceObjectRuntimeModule.js +90 -0
- package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +9 -0
- package/lib/runtime/StartupEntrypointRuntimeModule.js +9 -0
- package/lib/runtime/SystemContextRuntimeModule.js +9 -0
- package/lib/runtime/ToBinaryRuntimeModule.js +9 -0
- package/lib/schemes/HttpUriPlugin.js +22 -5
- package/lib/serialization/FileMiddleware.js +15 -2
- package/lib/serialization/ObjectMiddleware.js +138 -106
- package/lib/sharing/ConsumeSharedPlugin.js +12 -0
- package/lib/sharing/ConsumeSharedRuntimeModule.js +11 -0
- package/lib/sharing/ShareRuntimeModule.js +9 -0
- package/lib/sharing/declaredShared.js +47 -0
- package/lib/stats/DefaultStatsFactoryPlugin.js +3 -1
- package/lib/stats/DefaultStatsPrinterPlugin.js +3 -0
- package/lib/util/ArrayQueue.js +3 -3
- package/lib/util/SourceProcessor.js +76 -69
- package/lib/util/URLAbsoluteSpecifier.js +3 -1
- package/lib/util/async.js +292 -0
- package/lib/util/buildDiagnostics.js +69 -0
- package/lib/util/conventions.js +5 -1
- package/lib/util/fs.js +27 -1
- package/lib/util/handlerKeys.js +40 -0
- package/lib/util/identifier.js +20 -0
- package/lib/util/internalSerializables.js +6 -0
- package/lib/util/registerExternalSerializer.js +6 -45
- package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +9 -0
- package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +9 -0
- package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +9 -0
- package/lib/web/JsonpChunkLoadingRuntimeModule.js +36 -0
- package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +19 -0
- package/package.json +22 -16
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +388 -76
- package/schemas/plugins/LoaderOptionsPlugin.check.js +1 -1
- package/schemas/plugins/ManifestPlugin.check.js +1 -1
- package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
- package/schemas/plugins/asset/AssetGeneratorOptions.check.js +1 -1
- package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +1 -1
- package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
- package/schemas/plugins/debug/ProfilingPlugin.check.js +1 -1
- package/schemas/plugins/dll/DllReferencePlugin.check.js +1 -1
- package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +1 -1
- package/schemas/plugins/schemes/HttpUriPlugin.check.js +1 -1
- package/types.d.ts +1440 -331
- package/lib/dependencies/ExportBindingInitFragment.js +0 -191
- package/lib/errors/EntrypointOverlapWarning.js +0 -45
- package/lib/performance/EntrypointOverlapPlugin.js +0 -122
package/lib/html/syntax.js
CHANGED
|
@@ -14,13 +14,14 @@ const GenericSourceProcessor = require("../util/SourceProcessor");
|
|
|
14
14
|
|
|
15
15
|
const { deferredWrite, declineDeferredWrites } = GenericSourceProcessor;
|
|
16
16
|
|
|
17
|
-
/** @import { CssEnvironment, CssProcessOptions, CssTransformOptions } from "../css/syntax" */
|
|
18
17
|
/** @import { OutputHtmlOptions } from "../../declarations/WebpackOptions" */
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
20
|
* Renders one nested body this document embeds — an inline `<style>`, every
|
|
22
21
|
* `style=""` (handed over as a whole stylesheet), a `<script>` holding JSON or
|
|
23
|
-
* JavaScript,
|
|
22
|
+
* JavaScript, every event handler attribute (`as` says it is a function body
|
|
23
|
+
* rather than a script), an `<svg>` subtree, and the document an
|
|
24
|
+
* `<iframe srcdoc>` holds. Returning the input unchanged declines
|
|
24
25
|
* it. Synchronous: it is called from inside the print walk, so a caller with an
|
|
25
26
|
* async minifier collects the bodies in a walk-only pass first and looks the
|
|
26
27
|
* results up here.
|
|
@@ -47,6 +48,13 @@ const SVG_TYPE = "svg";
|
|
|
47
48
|
// it is offered as CSS — with `as` saying which production it is, the same
|
|
48
49
|
// vocabulary `module.parser.css.as` uses.
|
|
49
50
|
const BLOCK_CONTENTS = "block-contents";
|
|
51
|
+
// An `onclick=""` is that shape one language over: JavaScript, but a function
|
|
52
|
+
// body — the production the `return` cancelling an event is written in.
|
|
53
|
+
const EVENT_HANDLER = "event-handler";
|
|
54
|
+
// JavaScript's two top level productions. Which one a body is decides whether
|
|
55
|
+
// `import` parses, whether it is strict, and whether `-->` opens a comment.
|
|
56
|
+
const MODULE_SCRIPT = "module";
|
|
57
|
+
const CLASSIC_SCRIPT = "script";
|
|
50
58
|
|
|
51
59
|
const {
|
|
52
60
|
askEmbeddedRenderer,
|
|
@@ -65,6 +73,10 @@ const EMBEDDED_LANGUAGES = [
|
|
|
65
73
|
HTML_TYPE
|
|
66
74
|
];
|
|
67
75
|
|
|
76
|
+
const {
|
|
77
|
+
builtinEmbeddedRenderer,
|
|
78
|
+
stripJsonWhitespace
|
|
79
|
+
} = require("./builtinEmbeddedRenderer");
|
|
68
80
|
const {
|
|
69
81
|
ADDRESS_DIV_P,
|
|
70
82
|
APPLET_MARQUEE_OBJECT,
|
|
@@ -87,6 +99,7 @@ const {
|
|
|
87
99
|
EMPTY_REMOVABLE_ATTRIBUTES,
|
|
88
100
|
ENUMERATED_ATTRIBUTE_NAMES,
|
|
89
101
|
ENUMERATED_KEYWORDS,
|
|
102
|
+
EVENT_HANDLER_ATTRIBUTES,
|
|
90
103
|
FONT_BREAKOUT_ATTRS,
|
|
91
104
|
FOREIGN_ATTR_NS,
|
|
92
105
|
FOREIGN_BREAKOUT,
|
|
@@ -149,22 +162,20 @@ const {
|
|
|
149
162
|
VOID,
|
|
150
163
|
VOID_FORMATTING
|
|
151
164
|
} = require("./data");
|
|
165
|
+
const htmlMinify = require("./htmlMinify");
|
|
152
166
|
|
|
153
167
|
// cspell:ignore apos notpre noncharacter noncharacters DFFF FFFE CCLS ALNUM
|
|
154
168
|
|
|
155
169
|
// #region html entities
|
|
156
|
-
//
|
|
157
|
-
// `tooling/
|
|
158
|
-
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
// can't be poisoned by inherited `Object.prototype` keys like `toString`,
|
|
166
|
-
// `constructor`, or `__proto__` — without this, `&toString;` would falsely
|
|
167
|
-
// look like a matched named character reference.
|
|
170
|
+
// Auto-generated by `tooling/generate-html-data.js` from
|
|
171
|
+
// `tooling/html-entities.json` — run `yarn fix:special` to refresh.
|
|
172
|
+
|
|
173
|
+
// WHATWG named character references, keyed without the leading `&`: the keys
|
|
174
|
+
// that do not end in `;` are the legacy names matching unterminated.
|
|
175
|
+
|
|
176
|
+
// Null-prototype, so `&toString;` cannot reach an inherited `Object.prototype`
|
|
177
|
+
// key and read back as a matched reference.
|
|
178
|
+
|
|
168
179
|
// prettier-ignore
|
|
169
180
|
// cspell:disable-next-line
|
|
170
181
|
const HTML_ENTITIES = /** @type {Readonly<Record<string, string>>} */ (Object.freeze(Object.assign(Object.create(null), {"AElig":"Æ","AElig;":"Æ","AMP":"&","AMP;":"&","Aacute":"Á","Aacute;":"Á","Abreve;":"Ă","Acirc":"Â","Acirc;":"Â","Acy;":"А","Afr;":"𝔄","Agrave":"À","Agrave;":"À","Alpha;":"Α","Amacr;":"Ā","And;":"⩓","Aogon;":"Ą","Aopf;":"𝔸","ApplyFunction;":"","Aring":"Å","Aring;":"Å","Ascr;":"𝒜","Assign;":"≔","Atilde":"Ã","Atilde;":"Ã","Auml":"Ä","Auml;":"Ä","Backslash;":"∖","Barv;":"⫧","Barwed;":"⌆","Bcy;":"Б","Because;":"∵","Bernoullis;":"ℬ","Beta;":"Β","Bfr;":"𝔅","Bopf;":"𝔹","Breve;":"˘","Bscr;":"ℬ","Bumpeq;":"≎","CHcy;":"Ч","COPY":"©","COPY;":"©","Cacute;":"Ć","Cap;":"⋒","CapitalDifferentialD;":"ⅅ","Cayleys;":"ℭ","Ccaron;":"Č","Ccedil":"Ç","Ccedil;":"Ç","Ccirc;":"Ĉ","Cconint;":"∰","Cdot;":"Ċ","Cedilla;":"¸","CenterDot;":"·","Cfr;":"ℭ","Chi;":"Χ","CircleDot;":"⊙","CircleMinus;":"⊖","CirclePlus;":"⊕","CircleTimes;":"⊗","ClockwiseContourIntegral;":"∲","CloseCurlyDoubleQuote;":"”","CloseCurlyQuote;":"’","Colon;":"∷","Colone;":"⩴","Congruent;":"≡","Conint;":"∯","ContourIntegral;":"∮","Copf;":"ℂ","Coproduct;":"∐","CounterClockwiseContourIntegral;":"∳","Cross;":"⨯","Cscr;":"𝒞","Cup;":"⋓","CupCap;":"≍","DD;":"ⅅ","DDotrahd;":"⤑","DJcy;":"Ђ","DScy;":"Ѕ","DZcy;":"Џ","Dagger;":"‡","Darr;":"↡","Dashv;":"⫤","Dcaron;":"Ď","Dcy;":"Д","Del;":"∇","Delta;":"Δ","Dfr;":"𝔇","DiacriticalAcute;":"´","DiacriticalDot;":"˙","DiacriticalDoubleAcute;":"˝","DiacriticalGrave;":"`","DiacriticalTilde;":"˜","Diamond;":"⋄","DifferentialD;":"ⅆ","Dopf;":"𝔻","Dot;":"¨","DotDot;":"⃜","DotEqual;":"≐","DoubleContourIntegral;":"∯","DoubleDot;":"¨","DoubleDownArrow;":"⇓","DoubleLeftArrow;":"⇐","DoubleLeftRightArrow;":"⇔","DoubleLeftTee;":"⫤","DoubleLongLeftArrow;":"⟸","DoubleLongLeftRightArrow;":"⟺","DoubleLongRightArrow;":"⟹","DoubleRightArrow;":"⇒","DoubleRightTee;":"⊨","DoubleUpArrow;":"⇑","DoubleUpDownArrow;":"⇕","DoubleVerticalBar;":"∥","DownArrow;":"↓","DownArrowBar;":"⤓","DownArrowUpArrow;":"⇵","DownBreve;":"̑","DownLeftRightVector;":"⥐","DownLeftTeeVector;":"⥞","DownLeftVector;":"↽","DownLeftVectorBar;":"⥖","DownRightTeeVector;":"⥟","DownRightVector;":"⇁","DownRightVectorBar;":"⥗","DownTee;":"⊤","DownTeeArrow;":"↧","Downarrow;":"⇓","Dscr;":"𝒟","Dstrok;":"Đ","ENG;":"Ŋ","ETH":"Ð","ETH;":"Ð","Eacute":"É","Eacute;":"É","Ecaron;":"Ě","Ecirc":"Ê","Ecirc;":"Ê","Ecy;":"Э","Edot;":"Ė","Efr;":"𝔈","Egrave":"È","Egrave;":"È","Element;":"∈","Emacr;":"Ē","EmptySmallSquare;":"◻","EmptyVerySmallSquare;":"▫","Eogon;":"Ę","Eopf;":"𝔼","Epsilon;":"Ε","Equal;":"⩵","EqualTilde;":"≂","Equilibrium;":"⇌","Escr;":"ℰ","Esim;":"⩳","Eta;":"Η","Euml":"Ë","Euml;":"Ë","Exists;":"∃","ExponentialE;":"ⅇ","Fcy;":"Ф","Ffr;":"𝔉","FilledSmallSquare;":"◼","FilledVerySmallSquare;":"▪","Fopf;":"𝔽","ForAll;":"∀","Fouriertrf;":"ℱ","Fscr;":"ℱ","GJcy;":"Ѓ","GT":">","GT;":">","Gamma;":"Γ","Gammad;":"Ϝ","Gbreve;":"Ğ","Gcedil;":"Ģ","Gcirc;":"Ĝ","Gcy;":"Г","Gdot;":"Ġ","Gfr;":"𝔊","Gg;":"⋙","Gopf;":"𝔾","GreaterEqual;":"≥","GreaterEqualLess;":"⋛","GreaterFullEqual;":"≧","GreaterGreater;":"⪢","GreaterLess;":"≷","GreaterSlantEqual;":"⩾","GreaterTilde;":"≳","Gscr;":"𝒢","Gt;":"≫","HARDcy;":"Ъ","Hacek;":"ˇ","Hat;":"^","Hcirc;":"Ĥ","Hfr;":"ℌ","HilbertSpace;":"ℋ","Hopf;":"ℍ","HorizontalLine;":"─","Hscr;":"ℋ","Hstrok;":"Ħ","HumpDownHump;":"≎","HumpEqual;":"≏","IEcy;":"Е","IJlig;":"IJ","IOcy;":"Ё","Iacute":"Í","Iacute;":"Í","Icirc":"Î","Icirc;":"Î","Icy;":"И","Idot;":"İ","Ifr;":"ℑ","Igrave":"Ì","Igrave;":"Ì","Im;":"ℑ","Imacr;":"Ī","ImaginaryI;":"ⅈ","Implies;":"⇒","Int;":"∬","Integral;":"∫","Intersection;":"⋂","InvisibleComma;":"","InvisibleTimes;":"","Iogon;":"Į","Iopf;":"𝕀","Iota;":"Ι","Iscr;":"ℐ","Itilde;":"Ĩ","Iukcy;":"І","Iuml":"Ï","Iuml;":"Ï","Jcirc;":"Ĵ","Jcy;":"Й","Jfr;":"𝔍","Jopf;":"𝕁","Jscr;":"𝒥","Jsercy;":"Ј","Jukcy;":"Є","KHcy;":"Х","KJcy;":"Ќ","Kappa;":"Κ","Kcedil;":"Ķ","Kcy;":"К","Kfr;":"𝔎","Kopf;":"𝕂","Kscr;":"𝒦","LJcy;":"Љ","LT":"<","LT;":"<","Lacute;":"Ĺ","Lambda;":"Λ","Lang;":"⟪","Laplacetrf;":"ℒ","Larr;":"↞","Lcaron;":"Ľ","Lcedil;":"Ļ","Lcy;":"Л","LeftAngleBracket;":"⟨","LeftArrow;":"←","LeftArrowBar;":"⇤","LeftArrowRightArrow;":"⇆","LeftCeiling;":"⌈","LeftDoubleBracket;":"⟦","LeftDownTeeVector;":"⥡","LeftDownVector;":"⇃","LeftDownVectorBar;":"⥙","LeftFloor;":"⌊","LeftRightArrow;":"↔","LeftRightVector;":"⥎","LeftTee;":"⊣","LeftTeeArrow;":"↤","LeftTeeVector;":"⥚","LeftTriangle;":"⊲","LeftTriangleBar;":"⧏","LeftTriangleEqual;":"⊴","LeftUpDownVector;":"⥑","LeftUpTeeVector;":"⥠","LeftUpVector;":"↿","LeftUpVectorBar;":"⥘","LeftVector;":"↼","LeftVectorBar;":"⥒","Leftarrow;":"⇐","Leftrightarrow;":"⇔","LessEqualGreater;":"⋚","LessFullEqual;":"≦","LessGreater;":"≶","LessLess;":"⪡","LessSlantEqual;":"⩽","LessTilde;":"≲","Lfr;":"𝔏","Ll;":"⋘","Lleftarrow;":"⇚","Lmidot;":"Ŀ","LongLeftArrow;":"⟵","LongLeftRightArrow;":"⟷","LongRightArrow;":"⟶","Longleftarrow;":"⟸","Longleftrightarrow;":"⟺","Longrightarrow;":"⟹","Lopf;":"𝕃","LowerLeftArrow;":"↙","LowerRightArrow;":"↘","Lscr;":"ℒ","Lsh;":"↰","Lstrok;":"Ł","Lt;":"≪","Map;":"⤅","Mcy;":"М","MediumSpace;":" ","Mellintrf;":"ℳ","Mfr;":"𝔐","MinusPlus;":"∓","Mopf;":"𝕄","Mscr;":"ℳ","Mu;":"Μ","NJcy;":"Њ","Nacute;":"Ń","Ncaron;":"Ň","Ncedil;":"Ņ","Ncy;":"Н","NegativeMediumSpace;":"","NegativeThickSpace;":"","NegativeThinSpace;":"","NegativeVeryThinSpace;":"","NestedGreaterGreater;":"≫","NestedLessLess;":"≪","NewLine;":"\n","Nfr;":"𝔑","NoBreak;":"","NonBreakingSpace;":" ","Nopf;":"ℕ","Not;":"⫬","NotCongruent;":"≢","NotCupCap;":"≭","NotDoubleVerticalBar;":"∦","NotElement;":"∉","NotEqual;":"≠","NotEqualTilde;":"≂̸","NotExists;":"∄","NotGreater;":"≯","NotGreaterEqual;":"≱","NotGreaterFullEqual;":"≧̸","NotGreaterGreater;":"≫̸","NotGreaterLess;":"≹","NotGreaterSlantEqual;":"⩾̸","NotGreaterTilde;":"≵","NotHumpDownHump;":"≎̸","NotHumpEqual;":"≏̸","NotLeftTriangle;":"⋪","NotLeftTriangleBar;":"⧏̸","NotLeftTriangleEqual;":"⋬","NotLess;":"≮","NotLessEqual;":"≰","NotLessGreater;":"≸","NotLessLess;":"≪̸","NotLessSlantEqual;":"⩽̸","NotLessTilde;":"≴","NotNestedGreaterGreater;":"⪢̸","NotNestedLessLess;":"⪡̸","NotPrecedes;":"⊀","NotPrecedesEqual;":"⪯̸","NotPrecedesSlantEqual;":"⋠","NotReverseElement;":"∌","NotRightTriangle;":"⋫","NotRightTriangleBar;":"⧐̸","NotRightTriangleEqual;":"⋭","NotSquareSubset;":"⊏̸","NotSquareSubsetEqual;":"⋢","NotSquareSuperset;":"⊐̸","NotSquareSupersetEqual;":"⋣","NotSubset;":"⊂⃒","NotSubsetEqual;":"⊈","NotSucceeds;":"⊁","NotSucceedsEqual;":"⪰̸","NotSucceedsSlantEqual;":"⋡","NotSucceedsTilde;":"≿̸","NotSuperset;":"⊃⃒","NotSupersetEqual;":"⊉","NotTilde;":"≁","NotTildeEqual;":"≄","NotTildeFullEqual;":"≇","NotTildeTilde;":"≉","NotVerticalBar;":"∤","Nscr;":"𝒩","Ntilde":"Ñ","Ntilde;":"Ñ","Nu;":"Ν","OElig;":"Œ","Oacute":"Ó","Oacute;":"Ó","Ocirc":"Ô","Ocirc;":"Ô","Ocy;":"О","Odblac;":"Ő","Ofr;":"𝔒","Ograve":"Ò","Ograve;":"Ò","Omacr;":"Ō","Omega;":"Ω","Omicron;":"Ο","Oopf;":"𝕆","OpenCurlyDoubleQuote;":"“","OpenCurlyQuote;":"‘","Or;":"⩔","Oscr;":"𝒪","Oslash":"Ø","Oslash;":"Ø","Otilde":"Õ","Otilde;":"Õ","Otimes;":"⨷","Ouml":"Ö","Ouml;":"Ö","OverBar;":"‾","OverBrace;":"⏞","OverBracket;":"⎴","OverParenthesis;":"⏜","PartialD;":"∂","Pcy;":"П","Pfr;":"𝔓","Phi;":"Φ","Pi;":"Π","PlusMinus;":"±","Poincareplane;":"ℌ","Popf;":"ℙ","Pr;":"⪻","Precedes;":"≺","PrecedesEqual;":"⪯","PrecedesSlantEqual;":"≼","PrecedesTilde;":"≾","Prime;":"″","Product;":"∏","Proportion;":"∷","Proportional;":"∝","Pscr;":"𝒫","Psi;":"Ψ","QUOT":"\"","QUOT;":"\"","Qfr;":"𝔔","Qopf;":"ℚ","Qscr;":"𝒬","RBarr;":"⤐","REG":"®","REG;":"®","Racute;":"Ŕ","Rang;":"⟫","Rarr;":"↠","Rarrtl;":"⤖","Rcaron;":"Ř","Rcedil;":"Ŗ","Rcy;":"Р","Re;":"ℜ","ReverseElement;":"∋","ReverseEquilibrium;":"⇋","ReverseUpEquilibrium;":"⥯","Rfr;":"ℜ","Rho;":"Ρ","RightAngleBracket;":"⟩","RightArrow;":"→","RightArrowBar;":"⇥","RightArrowLeftArrow;":"⇄","RightCeiling;":"⌉","RightDoubleBracket;":"⟧","RightDownTeeVector;":"⥝","RightDownVector;":"⇂","RightDownVectorBar;":"⥕","RightFloor;":"⌋","RightTee;":"⊢","RightTeeArrow;":"↦","RightTeeVector;":"⥛","RightTriangle;":"⊳","RightTriangleBar;":"⧐","RightTriangleEqual;":"⊵","RightUpDownVector;":"⥏","RightUpTeeVector;":"⥜","RightUpVector;":"↾","RightUpVectorBar;":"⥔","RightVector;":"⇀","RightVectorBar;":"⥓","Rightarrow;":"⇒","Ropf;":"ℝ","RoundImplies;":"⥰","Rrightarrow;":"⇛","Rscr;":"ℛ","Rsh;":"↱","RuleDelayed;":"⧴","SHCHcy;":"Щ","SHcy;":"Ш","SOFTcy;":"Ь","Sacute;":"Ś","Sc;":"⪼","Scaron;":"Š","Scedil;":"Ş","Scirc;":"Ŝ","Scy;":"С","Sfr;":"𝔖","ShortDownArrow;":"↓","ShortLeftArrow;":"←","ShortRightArrow;":"→","ShortUpArrow;":"↑","Sigma;":"Σ","SmallCircle;":"∘","Sopf;":"𝕊","Sqrt;":"√","Square;":"□","SquareIntersection;":"⊓","SquareSubset;":"⊏","SquareSubsetEqual;":"⊑","SquareSuperset;":"⊐","SquareSupersetEqual;":"⊒","SquareUnion;":"⊔","Sscr;":"𝒮","Star;":"⋆","Sub;":"⋐","Subset;":"⋐","SubsetEqual;":"⊆","Succeeds;":"≻","SucceedsEqual;":"⪰","SucceedsSlantEqual;":"≽","SucceedsTilde;":"≿","SuchThat;":"∋","Sum;":"∑","Sup;":"⋑","Superset;":"⊃","SupersetEqual;":"⊇","Supset;":"⋑","THORN":"Þ","THORN;":"Þ","TRADE;":"™","TSHcy;":"Ћ","TScy;":"Ц","Tab;":"\t","Tau;":"Τ","Tcaron;":"Ť","Tcedil;":"Ţ","Tcy;":"Т","Tfr;":"𝔗","Therefore;":"∴","Theta;":"Θ","ThickSpace;":" ","ThinSpace;":" ","Tilde;":"∼","TildeEqual;":"≃","TildeFullEqual;":"≅","TildeTilde;":"≈","Topf;":"𝕋","TripleDot;":"⃛","Tscr;":"𝒯","Tstrok;":"Ŧ","Uacute":"Ú","Uacute;":"Ú","Uarr;":"↟","Uarrocir;":"⥉","Ubrcy;":"Ў","Ubreve;":"Ŭ","Ucirc":"Û","Ucirc;":"Û","Ucy;":"У","Udblac;":"Ű","Ufr;":"𝔘","Ugrave":"Ù","Ugrave;":"Ù","Umacr;":"Ū","UnderBar;":"_","UnderBrace;":"⏟","UnderBracket;":"⎵","UnderParenthesis;":"⏝","Union;":"⋃","UnionPlus;":"⊎","Uogon;":"Ų","Uopf;":"𝕌","UpArrow;":"↑","UpArrowBar;":"⤒","UpArrowDownArrow;":"⇅","UpDownArrow;":"↕","UpEquilibrium;":"⥮","UpTee;":"⊥","UpTeeArrow;":"↥","Uparrow;":"⇑","Updownarrow;":"⇕","UpperLeftArrow;":"↖","UpperRightArrow;":"↗","Upsi;":"ϒ","Upsilon;":"Υ","Uring;":"Ů","Uscr;":"𝒰","Utilde;":"Ũ","Uuml":"Ü","Uuml;":"Ü","VDash;":"⊫","Vbar;":"⫫","Vcy;":"В","Vdash;":"⊩","Vdashl;":"⫦","Vee;":"⋁","Verbar;":"‖","Vert;":"‖","VerticalBar;":"∣","VerticalLine;":"|","VerticalSeparator;":"❘","VerticalTilde;":"≀","VeryThinSpace;":" ","Vfr;":"𝔙","Vopf;":"𝕍","Vscr;":"𝒱","Vvdash;":"⊪","Wcirc;":"Ŵ","Wedge;":"⋀","Wfr;":"𝔚","Wopf;":"𝕎","Wscr;":"𝒲","Xfr;":"𝔛","Xi;":"Ξ","Xopf;":"𝕏","Xscr;":"𝒳","YAcy;":"Я","YIcy;":"Ї","YUcy;":"Ю","Yacute":"Ý","Yacute;":"Ý","Ycirc;":"Ŷ","Ycy;":"Ы","Yfr;":"𝔜","Yopf;":"𝕐","Yscr;":"𝒴","Yuml;":"Ÿ","ZHcy;":"Ж","Zacute;":"Ź","Zcaron;":"Ž","Zcy;":"З","Zdot;":"Ż","ZeroWidthSpace;":"","Zeta;":"Ζ","Zfr;":"ℨ","Zopf;":"ℤ","Zscr;":"𝒵","aacute":"á","aacute;":"á","abreve;":"ă","ac;":"∾","acE;":"∾̳","acd;":"∿","acirc":"â","acirc;":"â","acute":"´","acute;":"´","acy;":"а","aelig":"æ","aelig;":"æ","af;":"","afr;":"𝔞","agrave":"à","agrave;":"à","alefsym;":"ℵ","aleph;":"ℵ","alpha;":"α","amacr;":"ā","amalg;":"⨿","amp":"&","amp;":"&","and;":"∧","andand;":"⩕","andd;":"⩜","andslope;":"⩘","andv;":"⩚","ang;":"∠","ange;":"⦤","angle;":"∠","angmsd;":"∡","angmsdaa;":"⦨","angmsdab;":"⦩","angmsdac;":"⦪","angmsdad;":"⦫","angmsdae;":"⦬","angmsdaf;":"⦭","angmsdag;":"⦮","angmsdah;":"⦯","angrt;":"∟","angrtvb;":"⊾","angrtvbd;":"⦝","angsph;":"∢","angst;":"Å","angzarr;":"⍼","aogon;":"ą","aopf;":"𝕒","ap;":"≈","apE;":"⩰","apacir;":"⩯","ape;":"≊","apid;":"≋","apos;":"'","approx;":"≈","approxeq;":"≊","aring":"å","aring;":"å","ascr;":"𝒶","ast;":"*","asymp;":"≈","asympeq;":"≍","atilde":"ã","atilde;":"ã","auml":"ä","auml;":"ä","awconint;":"∳","awint;":"⨑","bNot;":"⫭","backcong;":"≌","backepsilon;":"϶","backprime;":"‵","backsim;":"∽","backsimeq;":"⋍","barvee;":"⊽","barwed;":"⌅","barwedge;":"⌅","bbrk;":"⎵","bbrktbrk;":"⎶","bcong;":"≌","bcy;":"б","bdquo;":"„","becaus;":"∵","because;":"∵","bemptyv;":"⦰","bepsi;":"϶","bernou;":"ℬ","beta;":"β","beth;":"ℶ","between;":"≬","bfr;":"𝔟","bigcap;":"⋂","bigcirc;":"◯","bigcup;":"⋃","bigodot;":"⨀","bigoplus;":"⨁","bigotimes;":"⨂","bigsqcup;":"⨆","bigstar;":"★","bigtriangledown;":"▽","bigtriangleup;":"△","biguplus;":"⨄","bigvee;":"⋁","bigwedge;":"⋀","bkarow;":"⤍","blacklozenge;":"⧫","blacksquare;":"▪","blacktriangle;":"▴","blacktriangledown;":"▾","blacktriangleleft;":"◂","blacktriangleright;":"▸","blank;":"␣","blk12;":"▒","blk14;":"░","blk34;":"▓","block;":"█","bne;":"=⃥","bnequiv;":"≡⃥","bnot;":"⌐","bopf;":"𝕓","bot;":"⊥","bottom;":"⊥","bowtie;":"⋈","boxDL;":"╗","boxDR;":"╔","boxDl;":"╖","boxDr;":"╓","boxH;":"═","boxHD;":"╦","boxHU;":"╩","boxHd;":"╤","boxHu;":"╧","boxUL;":"╝","boxUR;":"╚","boxUl;":"╜","boxUr;":"╙","boxV;":"║","boxVH;":"╬","boxVL;":"╣","boxVR;":"╠","boxVh;":"╫","boxVl;":"╢","boxVr;":"╟","boxbox;":"⧉","boxdL;":"╕","boxdR;":"╒","boxdl;":"┐","boxdr;":"┌","boxh;":"─","boxhD;":"╥","boxhU;":"╨","boxhd;":"┬","boxhu;":"┴","boxminus;":"⊟","boxplus;":"⊞","boxtimes;":"⊠","boxuL;":"╛","boxuR;":"╘","boxul;":"┘","boxur;":"└","boxv;":"│","boxvH;":"╪","boxvL;":"╡","boxvR;":"╞","boxvh;":"┼","boxvl;":"┤","boxvr;":"├","bprime;":"‵","breve;":"˘","brvbar":"¦","brvbar;":"¦","bscr;":"𝒷","bsemi;":"⁏","bsim;":"∽","bsime;":"⋍","bsol;":"\\","bsolb;":"⧅","bsolhsub;":"⟈","bull;":"•","bullet;":"•","bump;":"≎","bumpE;":"⪮","bumpe;":"≏","bumpeq;":"≏","cacute;":"ć","cap;":"∩","capand;":"⩄","capbrcup;":"⩉","capcap;":"⩋","capcup;":"⩇","capdot;":"⩀","caps;":"∩︀","caret;":"⁁","caron;":"ˇ","ccaps;":"⩍","ccaron;":"č","ccedil":"ç","ccedil;":"ç","ccirc;":"ĉ","ccups;":"⩌","ccupssm;":"⩐","cdot;":"ċ","cedil":"¸","cedil;":"¸","cemptyv;":"⦲","cent":"¢","cent;":"¢","centerdot;":"·","cfr;":"𝔠","chcy;":"ч","check;":"✓","checkmark;":"✓","chi;":"χ","cir;":"○","cirE;":"⧃","circ;":"ˆ","circeq;":"≗","circlearrowleft;":"↺","circlearrowright;":"↻","circledR;":"®","circledS;":"Ⓢ","circledast;":"⊛","circledcirc;":"⊚","circleddash;":"⊝","cire;":"≗","cirfnint;":"⨐","cirmid;":"⫯","cirscir;":"⧂","clubs;":"♣","clubsuit;":"♣","colon;":":","colone;":"≔","coloneq;":"≔","comma;":",","commat;":"@","comp;":"∁","compfn;":"∘","complement;":"∁","complexes;":"ℂ","cong;":"≅","congdot;":"⩭","conint;":"∮","copf;":"𝕔","coprod;":"∐","copy":"©","copy;":"©","copysr;":"℗","crarr;":"↵","cross;":"✗","cscr;":"𝒸","csub;":"⫏","csube;":"⫑","csup;":"⫐","csupe;":"⫒","ctdot;":"⋯","cudarrl;":"⤸","cudarrr;":"⤵","cuepr;":"⋞","cuesc;":"⋟","cularr;":"↶","cularrp;":"⤽","cup;":"∪","cupbrcap;":"⩈","cupcap;":"⩆","cupcup;":"⩊","cupdot;":"⊍","cupor;":"⩅","cups;":"∪︀","curarr;":"↷","curarrm;":"⤼","curlyeqprec;":"⋞","curlyeqsucc;":"⋟","curlyvee;":"⋎","curlywedge;":"⋏","curren":"¤","curren;":"¤","curvearrowleft;":"↶","curvearrowright;":"↷","cuvee;":"⋎","cuwed;":"⋏","cwconint;":"∲","cwint;":"∱","cylcty;":"⌭","dArr;":"⇓","dHar;":"⥥","dagger;":"†","daleth;":"ℸ","darr;":"↓","dash;":"‐","dashv;":"⊣","dbkarow;":"⤏","dblac;":"˝","dcaron;":"ď","dcy;":"д","dd;":"ⅆ","ddagger;":"‡","ddarr;":"⇊","ddotseq;":"⩷","deg":"°","deg;":"°","delta;":"δ","demptyv;":"⦱","dfisht;":"⥿","dfr;":"𝔡","dharl;":"⇃","dharr;":"⇂","diam;":"⋄","diamond;":"⋄","diamondsuit;":"♦","diams;":"♦","die;":"¨","digamma;":"ϝ","disin;":"⋲","div;":"÷","divide":"÷","divide;":"÷","divideontimes;":"⋇","divonx;":"⋇","djcy;":"ђ","dlcorn;":"⌞","dlcrop;":"⌍","dollar;":"$","dopf;":"𝕕","dot;":"˙","doteq;":"≐","doteqdot;":"≑","dotminus;":"∸","dotplus;":"∔","dotsquare;":"⊡","doublebarwedge;":"⌆","downarrow;":"↓","downdownarrows;":"⇊","downharpoonleft;":"⇃","downharpoonright;":"⇂","drbkarow;":"⤐","drcorn;":"⌟","drcrop;":"⌌","dscr;":"𝒹","dscy;":"ѕ","dsol;":"⧶","dstrok;":"đ","dtdot;":"⋱","dtri;":"▿","dtrif;":"▾","duarr;":"⇵","duhar;":"⥯","dwangle;":"⦦","dzcy;":"џ","dzigrarr;":"⟿","eDDot;":"⩷","eDot;":"≑","eacute":"é","eacute;":"é","easter;":"⩮","ecaron;":"ě","ecir;":"≖","ecirc":"ê","ecirc;":"ê","ecolon;":"≕","ecy;":"э","edot;":"ė","ee;":"ⅇ","efDot;":"≒","efr;":"𝔢","eg;":"⪚","egrave":"è","egrave;":"è","egs;":"⪖","egsdot;":"⪘","el;":"⪙","elinters;":"⏧","ell;":"ℓ","els;":"⪕","elsdot;":"⪗","emacr;":"ē","empty;":"∅","emptyset;":"∅","emptyv;":"∅","emsp13;":" ","emsp14;":" ","emsp;":" ","eng;":"ŋ","ensp;":" ","eogon;":"ę","eopf;":"𝕖","epar;":"⋕","eparsl;":"⧣","eplus;":"⩱","epsi;":"ε","epsilon;":"ε","epsiv;":"ϵ","eqcirc;":"≖","eqcolon;":"≕","eqsim;":"≂","eqslantgtr;":"⪖","eqslantless;":"⪕","equals;":"=","equest;":"≟","equiv;":"≡","equivDD;":"⩸","eqvparsl;":"⧥","erDot;":"≓","erarr;":"⥱","escr;":"ℯ","esdot;":"≐","esim;":"≂","eta;":"η","eth":"ð","eth;":"ð","euml":"ë","euml;":"ë","euro;":"€","excl;":"!","exist;":"∃","expectation;":"ℰ","exponentiale;":"ⅇ","fallingdotseq;":"≒","fcy;":"ф","female;":"♀","ffilig;":"ffi","fflig;":"ff","ffllig;":"ffl","ffr;":"𝔣","filig;":"fi","fjlig;":"fj","flat;":"♭","fllig;":"fl","fltns;":"▱","fnof;":"ƒ","fopf;":"𝕗","forall;":"∀","fork;":"⋔","forkv;":"⫙","fpartint;":"⨍","frac12":"½","frac12;":"½","frac13;":"⅓","frac14":"¼","frac14;":"¼","frac15;":"⅕","frac16;":"⅙","frac18;":"⅛","frac23;":"⅔","frac25;":"⅖","frac34":"¾","frac34;":"¾","frac35;":"⅗","frac38;":"⅜","frac45;":"⅘","frac56;":"⅚","frac58;":"⅝","frac78;":"⅞","frasl;":"⁄","frown;":"⌢","fscr;":"𝒻","gE;":"≧","gEl;":"⪌","gacute;":"ǵ","gamma;":"γ","gammad;":"ϝ","gap;":"⪆","gbreve;":"ğ","gcirc;":"ĝ","gcy;":"г","gdot;":"ġ","ge;":"≥","gel;":"⋛","geq;":"≥","geqq;":"≧","geqslant;":"⩾","ges;":"⩾","gescc;":"⪩","gesdot;":"⪀","gesdoto;":"⪂","gesdotol;":"⪄","gesl;":"⋛︀","gesles;":"⪔","gfr;":"𝔤","gg;":"≫","ggg;":"⋙","gimel;":"ℷ","gjcy;":"ѓ","gl;":"≷","glE;":"⪒","gla;":"⪥","glj;":"⪤","gnE;":"≩","gnap;":"⪊","gnapprox;":"⪊","gne;":"⪈","gneq;":"⪈","gneqq;":"≩","gnsim;":"⋧","gopf;":"𝕘","grave;":"`","gscr;":"ℊ","gsim;":"≳","gsime;":"⪎","gsiml;":"⪐","gt":">","gt;":">","gtcc;":"⪧","gtcir;":"⩺","gtdot;":"⋗","gtlPar;":"⦕","gtquest;":"⩼","gtrapprox;":"⪆","gtrarr;":"⥸","gtrdot;":"⋗","gtreqless;":"⋛","gtreqqless;":"⪌","gtrless;":"≷","gtrsim;":"≳","gvertneqq;":"≩︀","gvnE;":"≩︀","hArr;":"⇔","hairsp;":" ","half;":"½","hamilt;":"ℋ","hardcy;":"ъ","harr;":"↔","harrcir;":"⥈","harrw;":"↭","hbar;":"ℏ","hcirc;":"ĥ","hearts;":"♥","heartsuit;":"♥","hellip;":"…","hercon;":"⊹","hfr;":"𝔥","hksearow;":"⤥","hkswarow;":"⤦","hoarr;":"⇿","homtht;":"∻","hookleftarrow;":"↩","hookrightarrow;":"↪","hopf;":"𝕙","horbar;":"―","hscr;":"𝒽","hslash;":"ℏ","hstrok;":"ħ","hybull;":"⁃","hyphen;":"‐","iacute":"í","iacute;":"í","ic;":"","icirc":"î","icirc;":"î","icy;":"и","iecy;":"е","iexcl":"¡","iexcl;":"¡","iff;":"⇔","ifr;":"𝔦","igrave":"ì","igrave;":"ì","ii;":"ⅈ","iiiint;":"⨌","iiint;":"∭","iinfin;":"⧜","iiota;":"℩","ijlig;":"ij","imacr;":"ī","image;":"ℑ","imagline;":"ℐ","imagpart;":"ℑ","imath;":"ı","imof;":"⊷","imped;":"Ƶ","in;":"∈","incare;":"℅","infin;":"∞","infintie;":"⧝","inodot;":"ı","int;":"∫","intcal;":"⊺","integers;":"ℤ","intercal;":"⊺","intlarhk;":"⨗","intprod;":"⨼","iocy;":"ё","iogon;":"į","iopf;":"𝕚","iota;":"ι","iprod;":"⨼","iquest":"¿","iquest;":"¿","iscr;":"𝒾","isin;":"∈","isinE;":"⋹","isindot;":"⋵","isins;":"⋴","isinsv;":"⋳","isinv;":"∈","it;":"","itilde;":"ĩ","iukcy;":"і","iuml":"ï","iuml;":"ï","jcirc;":"ĵ","jcy;":"й","jfr;":"𝔧","jmath;":"ȷ","jopf;":"𝕛","jscr;":"𝒿","jsercy;":"ј","jukcy;":"є","kappa;":"κ","kappav;":"ϰ","kcedil;":"ķ","kcy;":"к","kfr;":"𝔨","kgreen;":"ĸ","khcy;":"х","kjcy;":"ќ","kopf;":"𝕜","kscr;":"𝓀","lAarr;":"⇚","lArr;":"⇐","lAtail;":"⤛","lBarr;":"⤎","lE;":"≦","lEg;":"⪋","lHar;":"⥢","lacute;":"ĺ","laemptyv;":"⦴","lagran;":"ℒ","lambda;":"λ","lang;":"⟨","langd;":"⦑","langle;":"⟨","lap;":"⪅","laquo":"«","laquo;":"«","larr;":"←","larrb;":"⇤","larrbfs;":"⤟","larrfs;":"⤝","larrhk;":"↩","larrlp;":"↫","larrpl;":"⤹","larrsim;":"⥳","larrtl;":"↢","lat;":"⪫","latail;":"⤙","late;":"⪭","lates;":"⪭︀","lbarr;":"⤌","lbbrk;":"❲","lbrace;":"{","lbrack;":"[","lbrke;":"⦋","lbrksld;":"⦏","lbrkslu;":"⦍","lcaron;":"ľ","lcedil;":"ļ","lceil;":"⌈","lcub;":"{","lcy;":"л","ldca;":"⤶","ldquo;":"“","ldquor;":"„","ldrdhar;":"⥧","ldrushar;":"⥋","ldsh;":"↲","le;":"≤","leftarrow;":"←","leftarrowtail;":"↢","leftharpoondown;":"↽","leftharpoonup;":"↼","leftleftarrows;":"⇇","leftrightarrow;":"↔","leftrightarrows;":"⇆","leftrightharpoons;":"⇋","leftrightsquigarrow;":"↭","leftthreetimes;":"⋋","leg;":"⋚","leq;":"≤","leqq;":"≦","leqslant;":"⩽","les;":"⩽","lescc;":"⪨","lesdot;":"⩿","lesdoto;":"⪁","lesdotor;":"⪃","lesg;":"⋚︀","lesges;":"⪓","lessapprox;":"⪅","lessdot;":"⋖","lesseqgtr;":"⋚","lesseqqgtr;":"⪋","lessgtr;":"≶","lesssim;":"≲","lfisht;":"⥼","lfloor;":"⌊","lfr;":"𝔩","lg;":"≶","lgE;":"⪑","lhard;":"↽","lharu;":"↼","lharul;":"⥪","lhblk;":"▄","ljcy;":"љ","ll;":"≪","llarr;":"⇇","llcorner;":"⌞","llhard;":"⥫","lltri;":"◺","lmidot;":"ŀ","lmoust;":"⎰","lmoustache;":"⎰","lnE;":"≨","lnap;":"⪉","lnapprox;":"⪉","lne;":"⪇","lneq;":"⪇","lneqq;":"≨","lnsim;":"⋦","loang;":"⟬","loarr;":"⇽","lobrk;":"⟦","longleftarrow;":"⟵","longleftrightarrow;":"⟷","longmapsto;":"⟼","longrightarrow;":"⟶","looparrowleft;":"↫","looparrowright;":"↬","lopar;":"⦅","lopf;":"𝕝","loplus;":"⨭","lotimes;":"⨴","lowast;":"∗","lowbar;":"_","loz;":"◊","lozenge;":"◊","lozf;":"⧫","lpar;":"(","lparlt;":"⦓","lrarr;":"⇆","lrcorner;":"⌟","lrhar;":"⇋","lrhard;":"⥭","lrm;":"","lrtri;":"⊿","lsaquo;":"‹","lscr;":"𝓁","lsh;":"↰","lsim;":"≲","lsime;":"⪍","lsimg;":"⪏","lsqb;":"[","lsquo;":"‘","lsquor;":"‚","lstrok;":"ł","lt":"<","lt;":"<","ltcc;":"⪦","ltcir;":"⩹","ltdot;":"⋖","lthree;":"⋋","ltimes;":"⋉","ltlarr;":"⥶","ltquest;":"⩻","ltrPar;":"⦖","ltri;":"◃","ltrie;":"⊴","ltrif;":"◂","lurdshar;":"⥊","luruhar;":"⥦","lvertneqq;":"≨︀","lvnE;":"≨︀","mDDot;":"∺","macr":"¯","macr;":"¯","male;":"♂","malt;":"✠","maltese;":"✠","map;":"↦","mapsto;":"↦","mapstodown;":"↧","mapstoleft;":"↤","mapstoup;":"↥","marker;":"▮","mcomma;":"⨩","mcy;":"м","mdash;":"—","measuredangle;":"∡","mfr;":"𝔪","mho;":"℧","micro":"µ","micro;":"µ","mid;":"∣","midast;":"*","midcir;":"⫰","middot":"·","middot;":"·","minus;":"−","minusb;":"⊟","minusd;":"∸","minusdu;":"⨪","mlcp;":"⫛","mldr;":"…","mnplus;":"∓","models;":"⊧","mopf;":"𝕞","mp;":"∓","mscr;":"𝓂","mstpos;":"∾","mu;":"μ","multimap;":"⊸","mumap;":"⊸","nGg;":"⋙̸","nGt;":"≫⃒","nGtv;":"≫̸","nLeftarrow;":"⇍","nLeftrightarrow;":"⇎","nLl;":"⋘̸","nLt;":"≪⃒","nLtv;":"≪̸","nRightarrow;":"⇏","nVDash;":"⊯","nVdash;":"⊮","nabla;":"∇","nacute;":"ń","nang;":"∠⃒","nap;":"≉","napE;":"⩰̸","napid;":"≋̸","napos;":"ʼn","napprox;":"≉","natur;":"♮","natural;":"♮","naturals;":"ℕ","nbsp":" ","nbsp;":" ","nbump;":"≎̸","nbumpe;":"≏̸","ncap;":"⩃","ncaron;":"ň","ncedil;":"ņ","ncong;":"≇","ncongdot;":"⩭̸","ncup;":"⩂","ncy;":"н","ndash;":"–","ne;":"≠","neArr;":"⇗","nearhk;":"⤤","nearr;":"↗","nearrow;":"↗","nedot;":"≐̸","nequiv;":"≢","nesear;":"⤨","nesim;":"≂̸","nexist;":"∄","nexists;":"∄","nfr;":"𝔫","ngE;":"≧̸","nge;":"≱","ngeq;":"≱","ngeqq;":"≧̸","ngeqslant;":"⩾̸","nges;":"⩾̸","ngsim;":"≵","ngt;":"≯","ngtr;":"≯","nhArr;":"⇎","nharr;":"↮","nhpar;":"⫲","ni;":"∋","nis;":"⋼","nisd;":"⋺","niv;":"∋","njcy;":"њ","nlArr;":"⇍","nlE;":"≦̸","nlarr;":"↚","nldr;":"‥","nle;":"≰","nleftarrow;":"↚","nleftrightarrow;":"↮","nleq;":"≰","nleqq;":"≦̸","nleqslant;":"⩽̸","nles;":"⩽̸","nless;":"≮","nlsim;":"≴","nlt;":"≮","nltri;":"⋪","nltrie;":"⋬","nmid;":"∤","nopf;":"𝕟","not":"¬","not;":"¬","notin;":"∉","notinE;":"⋹̸","notindot;":"⋵̸","notinva;":"∉","notinvb;":"⋷","notinvc;":"⋶","notni;":"∌","notniva;":"∌","notnivb;":"⋾","notnivc;":"⋽","npar;":"∦","nparallel;":"∦","nparsl;":"⫽⃥","npart;":"∂̸","npolint;":"⨔","npr;":"⊀","nprcue;":"⋠","npre;":"⪯̸","nprec;":"⊀","npreceq;":"⪯̸","nrArr;":"⇏","nrarr;":"↛","nrarrc;":"⤳̸","nrarrw;":"↝̸","nrightarrow;":"↛","nrtri;":"⋫","nrtrie;":"⋭","nsc;":"⊁","nsccue;":"⋡","nsce;":"⪰̸","nscr;":"𝓃","nshortmid;":"∤","nshortparallel;":"∦","nsim;":"≁","nsime;":"≄","nsimeq;":"≄","nsmid;":"∤","nspar;":"∦","nsqsube;":"⋢","nsqsupe;":"⋣","nsub;":"⊄","nsubE;":"⫅̸","nsube;":"⊈","nsubset;":"⊂⃒","nsubseteq;":"⊈","nsubseteqq;":"⫅̸","nsucc;":"⊁","nsucceq;":"⪰̸","nsup;":"⊅","nsupE;":"⫆̸","nsupe;":"⊉","nsupset;":"⊃⃒","nsupseteq;":"⊉","nsupseteqq;":"⫆̸","ntgl;":"≹","ntilde":"ñ","ntilde;":"ñ","ntlg;":"≸","ntriangleleft;":"⋪","ntrianglelefteq;":"⋬","ntriangleright;":"⋫","ntrianglerighteq;":"⋭","nu;":"ν","num;":"#","numero;":"№","numsp;":" ","nvDash;":"⊭","nvHarr;":"⤄","nvap;":"≍⃒","nvdash;":"⊬","nvge;":"≥⃒","nvgt;":">⃒","nvinfin;":"⧞","nvlArr;":"⤂","nvle;":"≤⃒","nvlt;":"<⃒","nvltrie;":"⊴⃒","nvrArr;":"⤃","nvrtrie;":"⊵⃒","nvsim;":"∼⃒","nwArr;":"⇖","nwarhk;":"⤣","nwarr;":"↖","nwarrow;":"↖","nwnear;":"⤧","oS;":"Ⓢ","oacute":"ó","oacute;":"ó","oast;":"⊛","ocir;":"⊚","ocirc":"ô","ocirc;":"ô","ocy;":"о","odash;":"⊝","odblac;":"ő","odiv;":"⨸","odot;":"⊙","odsold;":"⦼","oelig;":"œ","ofcir;":"⦿","ofr;":"𝔬","ogon;":"˛","ograve":"ò","ograve;":"ò","ogt;":"⧁","ohbar;":"⦵","ohm;":"Ω","oint;":"∮","olarr;":"↺","olcir;":"⦾","olcross;":"⦻","oline;":"‾","olt;":"⧀","omacr;":"ō","omega;":"ω","omicron;":"ο","omid;":"⦶","ominus;":"⊖","oopf;":"𝕠","opar;":"⦷","operp;":"⦹","oplus;":"⊕","or;":"∨","orarr;":"↻","ord;":"⩝","order;":"ℴ","orderof;":"ℴ","ordf":"ª","ordf;":"ª","ordm":"º","ordm;":"º","origof;":"⊶","oror;":"⩖","orslope;":"⩗","orv;":"⩛","oscr;":"ℴ","oslash":"ø","oslash;":"ø","osol;":"⊘","otilde":"õ","otilde;":"õ","otimes;":"⊗","otimesas;":"⨶","ouml":"ö","ouml;":"ö","ovbar;":"⌽","par;":"∥","para":"¶","para;":"¶","parallel;":"∥","parsim;":"⫳","parsl;":"⫽","part;":"∂","pcy;":"п","percnt;":"%","period;":".","permil;":"‰","perp;":"⊥","pertenk;":"‱","pfr;":"𝔭","phi;":"φ","phiv;":"ϕ","phmmat;":"ℳ","phone;":"☎","pi;":"π","pitchfork;":"⋔","piv;":"ϖ","planck;":"ℏ","planckh;":"ℎ","plankv;":"ℏ","plus;":"+","plusacir;":"⨣","plusb;":"⊞","pluscir;":"⨢","plusdo;":"∔","plusdu;":"⨥","pluse;":"⩲","plusmn":"±","plusmn;":"±","plussim;":"⨦","plustwo;":"⨧","pm;":"±","pointint;":"⨕","popf;":"𝕡","pound":"£","pound;":"£","pr;":"≺","prE;":"⪳","prap;":"⪷","prcue;":"≼","pre;":"⪯","prec;":"≺","precapprox;":"⪷","preccurlyeq;":"≼","preceq;":"⪯","precnapprox;":"⪹","precneqq;":"⪵","precnsim;":"⋨","precsim;":"≾","prime;":"′","primes;":"ℙ","prnE;":"⪵","prnap;":"⪹","prnsim;":"⋨","prod;":"∏","profalar;":"⌮","profline;":"⌒","profsurf;":"⌓","prop;":"∝","propto;":"∝","prsim;":"≾","prurel;":"⊰","pscr;":"𝓅","psi;":"ψ","puncsp;":" ","qfr;":"𝔮","qint;":"⨌","qopf;":"𝕢","qprime;":"⁗","qscr;":"𝓆","quaternions;":"ℍ","quatint;":"⨖","quest;":"?","questeq;":"≟","quot":"\"","quot;":"\"","rAarr;":"⇛","rArr;":"⇒","rAtail;":"⤜","rBarr;":"⤏","rHar;":"⥤","race;":"∽̱","racute;":"ŕ","radic;":"√","raemptyv;":"⦳","rang;":"⟩","rangd;":"⦒","range;":"⦥","rangle;":"⟩","raquo":"»","raquo;":"»","rarr;":"→","rarrap;":"⥵","rarrb;":"⇥","rarrbfs;":"⤠","rarrc;":"⤳","rarrfs;":"⤞","rarrhk;":"↪","rarrlp;":"↬","rarrpl;":"⥅","rarrsim;":"⥴","rarrtl;":"↣","rarrw;":"↝","ratail;":"⤚","ratio;":"∶","rationals;":"ℚ","rbarr;":"⤍","rbbrk;":"❳","rbrace;":"}","rbrack;":"]","rbrke;":"⦌","rbrksld;":"⦎","rbrkslu;":"⦐","rcaron;":"ř","rcedil;":"ŗ","rceil;":"⌉","rcub;":"}","rcy;":"р","rdca;":"⤷","rdldhar;":"⥩","rdquo;":"”","rdquor;":"”","rdsh;":"↳","real;":"ℜ","realine;":"ℛ","realpart;":"ℜ","reals;":"ℝ","rect;":"▭","reg":"®","reg;":"®","rfisht;":"⥽","rfloor;":"⌋","rfr;":"𝔯","rhard;":"⇁","rharu;":"⇀","rharul;":"⥬","rho;":"ρ","rhov;":"ϱ","rightarrow;":"→","rightarrowtail;":"↣","rightharpoondown;":"⇁","rightharpoonup;":"⇀","rightleftarrows;":"⇄","rightleftharpoons;":"⇌","rightrightarrows;":"⇉","rightsquigarrow;":"↝","rightthreetimes;":"⋌","ring;":"˚","risingdotseq;":"≓","rlarr;":"⇄","rlhar;":"⇌","rlm;":"","rmoust;":"⎱","rmoustache;":"⎱","rnmid;":"⫮","roang;":"⟭","roarr;":"⇾","robrk;":"⟧","ropar;":"⦆","ropf;":"𝕣","roplus;":"⨮","rotimes;":"⨵","rpar;":")","rpargt;":"⦔","rppolint;":"⨒","rrarr;":"⇉","rsaquo;":"›","rscr;":"𝓇","rsh;":"↱","rsqb;":"]","rsquo;":"’","rsquor;":"’","rthree;":"⋌","rtimes;":"⋊","rtri;":"▹","rtrie;":"⊵","rtrif;":"▸","rtriltri;":"⧎","ruluhar;":"⥨","rx;":"℞","sacute;":"ś","sbquo;":"‚","sc;":"≻","scE;":"⪴","scap;":"⪸","scaron;":"š","sccue;":"≽","sce;":"⪰","scedil;":"ş","scirc;":"ŝ","scnE;":"⪶","scnap;":"⪺","scnsim;":"⋩","scpolint;":"⨓","scsim;":"≿","scy;":"с","sdot;":"⋅","sdotb;":"⊡","sdote;":"⩦","seArr;":"⇘","searhk;":"⤥","searr;":"↘","searrow;":"↘","sect":"§","sect;":"§","semi;":";","seswar;":"⤩","setminus;":"∖","setmn;":"∖","sext;":"✶","sfr;":"𝔰","sfrown;":"⌢","sharp;":"♯","shchcy;":"щ","shcy;":"ш","shortmid;":"∣","shortparallel;":"∥","shy":"","shy;":"","sigma;":"σ","sigmaf;":"ς","sigmav;":"ς","sim;":"∼","simdot;":"⩪","sime;":"≃","simeq;":"≃","simg;":"⪞","simgE;":"⪠","siml;":"⪝","simlE;":"⪟","simne;":"≆","simplus;":"⨤","simrarr;":"⥲","slarr;":"←","smallsetminus;":"∖","smashp;":"⨳","smeparsl;":"⧤","smid;":"∣","smile;":"⌣","smt;":"⪪","smte;":"⪬","smtes;":"⪬︀","softcy;":"ь","sol;":"/","solb;":"⧄","solbar;":"⌿","sopf;":"𝕤","spades;":"♠","spadesuit;":"♠","spar;":"∥","sqcap;":"⊓","sqcaps;":"⊓︀","sqcup;":"⊔","sqcups;":"⊔︀","sqsub;":"⊏","sqsube;":"⊑","sqsubset;":"⊏","sqsubseteq;":"⊑","sqsup;":"⊐","sqsupe;":"⊒","sqsupset;":"⊐","sqsupseteq;":"⊒","squ;":"□","square;":"□","squarf;":"▪","squf;":"▪","srarr;":"→","sscr;":"𝓈","ssetmn;":"∖","ssmile;":"⌣","sstarf;":"⋆","star;":"☆","starf;":"★","straightepsilon;":"ϵ","straightphi;":"ϕ","strns;":"¯","sub;":"⊂","subE;":"⫅","subdot;":"⪽","sube;":"⊆","subedot;":"⫃","submult;":"⫁","subnE;":"⫋","subne;":"⊊","subplus;":"⪿","subrarr;":"⥹","subset;":"⊂","subseteq;":"⊆","subseteqq;":"⫅","subsetneq;":"⊊","subsetneqq;":"⫋","subsim;":"⫇","subsub;":"⫕","subsup;":"⫓","succ;":"≻","succapprox;":"⪸","succcurlyeq;":"≽","succeq;":"⪰","succnapprox;":"⪺","succneqq;":"⪶","succnsim;":"⋩","succsim;":"≿","sum;":"∑","sung;":"♪","sup1":"¹","sup1;":"¹","sup2":"²","sup2;":"²","sup3":"³","sup3;":"³","sup;":"⊃","supE;":"⫆","supdot;":"⪾","supdsub;":"⫘","supe;":"⊇","supedot;":"⫄","suphsol;":"⟉","suphsub;":"⫗","suplarr;":"⥻","supmult;":"⫂","supnE;":"⫌","supne;":"⊋","supplus;":"⫀","supset;":"⊃","supseteq;":"⊇","supseteqq;":"⫆","supsetneq;":"⊋","supsetneqq;":"⫌","supsim;":"⫈","supsub;":"⫔","supsup;":"⫖","swArr;":"⇙","swarhk;":"⤦","swarr;":"↙","swarrow;":"↙","swnwar;":"⤪","szlig":"ß","szlig;":"ß","target;":"⌖","tau;":"τ","tbrk;":"⎴","tcaron;":"ť","tcedil;":"ţ","tcy;":"т","tdot;":"⃛","telrec;":"⌕","tfr;":"𝔱","there4;":"∴","therefore;":"∴","theta;":"θ","thetasym;":"ϑ","thetav;":"ϑ","thickapprox;":"≈","thicksim;":"∼","thinsp;":" ","thkap;":"≈","thksim;":"∼","thorn":"þ","thorn;":"þ","tilde;":"˜","times":"×","times;":"×","timesb;":"⊠","timesbar;":"⨱","timesd;":"⨰","tint;":"∭","toea;":"⤨","top;":"⊤","topbot;":"⌶","topcir;":"⫱","topf;":"𝕥","topfork;":"⫚","tosa;":"⤩","tprime;":"‴","trade;":"™","triangle;":"▵","triangledown;":"▿","triangleleft;":"◃","trianglelefteq;":"⊴","triangleq;":"≜","triangleright;":"▹","trianglerighteq;":"⊵","tridot;":"◬","trie;":"≜","triminus;":"⨺","triplus;":"⨹","trisb;":"⧍","tritime;":"⨻","trpezium;":"⏢","tscr;":"𝓉","tscy;":"ц","tshcy;":"ћ","tstrok;":"ŧ","twixt;":"≬","twoheadleftarrow;":"↞","twoheadrightarrow;":"↠","uArr;":"⇑","uHar;":"⥣","uacute":"ú","uacute;":"ú","uarr;":"↑","ubrcy;":"ў","ubreve;":"ŭ","ucirc":"û","ucirc;":"û","ucy;":"у","udarr;":"⇅","udblac;":"ű","udhar;":"⥮","ufisht;":"⥾","ufr;":"𝔲","ugrave":"ù","ugrave;":"ù","uharl;":"↿","uharr;":"↾","uhblk;":"▀","ulcorn;":"⌜","ulcorner;":"⌜","ulcrop;":"⌏","ultri;":"◸","umacr;":"ū","uml":"¨","uml;":"¨","uogon;":"ų","uopf;":"𝕦","uparrow;":"↑","updownarrow;":"↕","upharpoonleft;":"↿","upharpoonright;":"↾","uplus;":"⊎","upsi;":"υ","upsih;":"ϒ","upsilon;":"υ","upuparrows;":"⇈","urcorn;":"⌝","urcorner;":"⌝","urcrop;":"⌎","uring;":"ů","urtri;":"◹","uscr;":"𝓊","utdot;":"⋰","utilde;":"ũ","utri;":"▵","utrif;":"▴","uuarr;":"⇈","uuml":"ü","uuml;":"ü","uwangle;":"⦧","vArr;":"⇕","vBar;":"⫨","vBarv;":"⫩","vDash;":"⊨","vangrt;":"⦜","varepsilon;":"ϵ","varkappa;":"ϰ","varnothing;":"∅","varphi;":"ϕ","varpi;":"ϖ","varpropto;":"∝","varr;":"↕","varrho;":"ϱ","varsigma;":"ς","varsubsetneq;":"⊊︀","varsubsetneqq;":"⫋︀","varsupsetneq;":"⊋︀","varsupsetneqq;":"⫌︀","vartheta;":"ϑ","vartriangleleft;":"⊲","vartriangleright;":"⊳","vcy;":"в","vdash;":"⊢","vee;":"∨","veebar;":"⊻","veeeq;":"≚","vellip;":"⋮","verbar;":"|","vert;":"|","vfr;":"𝔳","vltri;":"⊲","vnsub;":"⊂⃒","vnsup;":"⊃⃒","vopf;":"𝕧","vprop;":"∝","vrtri;":"⊳","vscr;":"𝓋","vsubnE;":"⫋︀","vsubne;":"⊊︀","vsupnE;":"⫌︀","vsupne;":"⊋︀","vzigzag;":"⦚","wcirc;":"ŵ","wedbar;":"⩟","wedge;":"∧","wedgeq;":"≙","weierp;":"℘","wfr;":"𝔴","wopf;":"𝕨","wp;":"℘","wr;":"≀","wreath;":"≀","wscr;":"𝓌","xcap;":"⋂","xcirc;":"◯","xcup;":"⋃","xdtri;":"▽","xfr;":"𝔵","xhArr;":"⟺","xharr;":"⟷","xi;":"ξ","xlArr;":"⟸","xlarr;":"⟵","xmap;":"⟼","xnis;":"⋻","xodot;":"⨀","xopf;":"𝕩","xoplus;":"⨁","xotime;":"⨂","xrArr;":"⟹","xrarr;":"⟶","xscr;":"𝓍","xsqcup;":"⨆","xuplus;":"⨄","xutri;":"△","xvee;":"⋁","xwedge;":"⋀","yacute":"ý","yacute;":"ý","yacy;":"я","ycirc;":"ŷ","ycy;":"ы","yen":"¥","yen;":"¥","yfr;":"𝔶","yicy;":"ї","yopf;":"𝕪","yscr;":"𝓎","yucy;":"ю","yuml":"ÿ","yuml;":"ÿ","zacute;":"ź","zcaron;":"ž","zcy;":"з","zdot;":"ż","zeetrf;":"ℨ","zeta;":"ζ","zfr;":"𝔷","zhcy;":"ж","zigrarr;":"⇝","zopf;":"𝕫","zscr;":"𝓏","zwj;":"","zwnj;":""})));
|
|
@@ -289,6 +300,12 @@ const CC_GREATER_THAN = 0x3e;
|
|
|
289
300
|
const CC_QUESTION_MARK = 0x3f;
|
|
290
301
|
const CC_LEFT_SQUARE_BRACKET = 0x5b;
|
|
291
302
|
const CC_RIGHT_SQUARE_BRACKET = 0x5d;
|
|
303
|
+
const CC_LEFT_PARENTHESIS = 0x28;
|
|
304
|
+
const CC_RIGHT_PARENTHESIS = 0x29;
|
|
305
|
+
const CC_ASTERISK = 0x2a;
|
|
306
|
+
const CC_REVERSE_SOLIDUS = 0x5c;
|
|
307
|
+
const CC_LEFT_CURLY_BRACKET = 0x7b;
|
|
308
|
+
const CC_RIGHT_CURLY_BRACKET = 0x7d;
|
|
292
309
|
const CC_LOW_LINE = 0x5f;
|
|
293
310
|
const CC_GRAVE_ACCENT = 0x60;
|
|
294
311
|
const CC_NO_BREAK_SPACE = 0xa0;
|
|
@@ -4912,6 +4929,10 @@ let _attrCount = 0;
|
|
|
4912
4929
|
// (§13.2.6.4.4, §13.2.6.4.7). The merged names are not in the element's source
|
|
4913
4930
|
// open tag, so the printer rebuilds that tag instead of echoing it.
|
|
4914
4931
|
const _mergedAttrNodes = new Set();
|
|
4932
|
+
// Attribute runs whose start tag dropped a duplicate name and spells one no
|
|
4933
|
+
// HTML vocabulary does: the printer hands those back whole (see `_isWordSpan`).
|
|
4934
|
+
/** @type {Set<number>} */
|
|
4935
|
+
const _foreignSyntaxAttrRuns = new Set();
|
|
4915
4936
|
// A processing instruction's target; its data rides in `_nodeStrings` like a
|
|
4916
4937
|
// comment's. A map, since a second string column per node would cost more.
|
|
4917
4938
|
/** @type {Map<HtmlNodeRef, string>} */
|
|
@@ -4983,6 +5004,32 @@ const _allocAttr = (name, value, nameStart, nameEnd, valueStart, valueEnd) => {
|
|
|
4983
5004
|
return i;
|
|
4984
5005
|
};
|
|
4985
5006
|
|
|
5007
|
+
/**
|
|
5008
|
+
* Whether a span reads as HTML vocabulary rather than as another language's
|
|
5009
|
+
* text: an attribute name or list token starts with an ASCII letter, `_` or
|
|
5010
|
+
* `-`, and no delimiter a template engine wraps a statement in appears in it.
|
|
5011
|
+
* @param {string} text the string holding the span
|
|
5012
|
+
* @param {number} start first index
|
|
5013
|
+
* @param {number} end index after the last
|
|
5014
|
+
* @returns {boolean} true when the span is a word
|
|
5015
|
+
*/
|
|
5016
|
+
const _isWordSpan = (text, start, end) => {
|
|
5017
|
+
const first = text.charCodeAt(start);
|
|
5018
|
+
if (
|
|
5019
|
+
!(first >= 0x61 && first <= 0x7a) &&
|
|
5020
|
+
!(first >= 0x41 && first <= 0x5a) &&
|
|
5021
|
+
first !== 0x5f &&
|
|
5022
|
+
first !== 0x2d
|
|
5023
|
+
) {
|
|
5024
|
+
return false;
|
|
5025
|
+
}
|
|
5026
|
+
for (let i = start + 1; i < end; i++) {
|
|
5027
|
+
const c = text.charCodeAt(i);
|
|
5028
|
+
if (c === 0x7b || c === 0x7d || c === 0x3c || c === 0x3e) return false;
|
|
5029
|
+
}
|
|
5030
|
+
return true;
|
|
5031
|
+
};
|
|
5032
|
+
|
|
4986
5033
|
/** @type {(i: number) => string} */
|
|
4987
5034
|
const _attrValueOf = (i) => {
|
|
4988
5035
|
const v = _attrValues[i];
|
|
@@ -5106,6 +5153,7 @@ const _resetAstColumns = () => {
|
|
|
5106
5153
|
_nodeHighWater = 0;
|
|
5107
5154
|
_attrCount = 0;
|
|
5108
5155
|
_mergedAttrNodes.clear();
|
|
5156
|
+
_foreignSyntaxAttrRuns.clear();
|
|
5109
5157
|
_piTargets.clear();
|
|
5110
5158
|
_doctypePublicId = null;
|
|
5111
5159
|
_doctypeSystemId = null;
|
|
@@ -5150,6 +5198,7 @@ const _releaseAstColumns = () => {
|
|
|
5150
5198
|
_nodeHighWater = 0;
|
|
5151
5199
|
_attrCount = 0;
|
|
5152
5200
|
_mergedAttrNodes.clear();
|
|
5201
|
+
_foreignSyntaxAttrRuns.clear();
|
|
5153
5202
|
_piTargets.clear();
|
|
5154
5203
|
_htmlSource = "";
|
|
5155
5204
|
_doctypePublicId = null;
|
|
@@ -5237,11 +5286,13 @@ const _makeProcessingInstructionNode = (target, data, start, end) => {
|
|
|
5237
5286
|
// Marker entry in the active-formatting-elements list (never a valid ref).
|
|
5238
5287
|
const AFE_MARKER = -1;
|
|
5239
5288
|
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5289
|
+
/**
|
|
5290
|
+
* Clones an element's attribute run, dropping source offsets so the consumer
|
|
5291
|
+
* emits no duplicate dependency for the reopened element's spans.
|
|
5292
|
+
* @param {HtmlElement} el element whose attributes are cloned
|
|
5293
|
+
* @returns {AttributeRun} the cloned run
|
|
5294
|
+
*/
|
|
5295
|
+
const cloneAttrs = (el) => {
|
|
5245
5296
|
const start = _nodeAttrStarts[el];
|
|
5246
5297
|
const count = _nodeAttrCounts[el];
|
|
5247
5298
|
const newStart = _attrCount + 1;
|
|
@@ -5252,14 +5303,15 @@ const cloneAttrs = (/** @type {HtmlElement} */ el) => {
|
|
|
5252
5303
|
return { start: newStart, count };
|
|
5253
5304
|
};
|
|
5254
5305
|
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5306
|
+
/**
|
|
5307
|
+
* Merges a repeated `<html>`/`<body>` tag's attributes into the element, adding
|
|
5308
|
+
* only names not already present. Runs are contiguous, so any addition
|
|
5309
|
+
* re-allocates the whole run and orphans the old slots.
|
|
5310
|
+
* @param {HtmlElement} el element receiving the attributes
|
|
5311
|
+
* @param {AttributeRun} run attributes of the repeated tag
|
|
5312
|
+
* @returns {void}
|
|
5313
|
+
*/
|
|
5314
|
+
const mergeAttrs = (el, run) => {
|
|
5263
5315
|
const start = _nodeAttrStarts[el];
|
|
5264
5316
|
const count = _nodeAttrCounts[el];
|
|
5265
5317
|
let extra = 0;
|
|
@@ -5335,9 +5387,13 @@ const TOKEN_PROCESSING_INSTRUCTION = 7;
|
|
|
5335
5387
|
/** @typedef {{ type: typeof TOKEN_EOF }} EofToken */
|
|
5336
5388
|
/** @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
5389
|
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5390
|
+
/**
|
|
5391
|
+
* Every insertion mode handles a processing instruction token on the same arc
|
|
5392
|
+
* as a comment token, so the arcs test both.
|
|
5393
|
+
* @param {Token} t current token
|
|
5394
|
+
* @returns {t is CommentToken | ProcessingInstructionToken} true for a comment or processing instruction token
|
|
5395
|
+
*/
|
|
5396
|
+
const isCommentOrProcessingInstruction = (t) =>
|
|
5341
5397
|
t.type === TOKEN_COMMENT || t.type === TOKEN_PROCESSING_INSTRUCTION;
|
|
5342
5398
|
|
|
5343
5399
|
/**
|
|
@@ -5399,9 +5455,12 @@ const TEMPLATE_START_TAG_MODES = new Map(
|
|
|
5399
5455
|
})
|
|
5400
5456
|
);
|
|
5401
5457
|
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5458
|
+
/**
|
|
5459
|
+
* MathML/SVG specials are handled via namespace checks rather than one table.
|
|
5460
|
+
* @param {HtmlElement} el element to test
|
|
5461
|
+
* @returns {boolean} true when the element is "special" (§13.2.4.2)
|
|
5462
|
+
*/
|
|
5463
|
+
const isSpecial = (el) => {
|
|
5405
5464
|
const ns = _namespaceOf(el);
|
|
5406
5465
|
const tag = _tagNameOf(el);
|
|
5407
5466
|
if (ns === NS_HTML) return SPECIAL.has(tag);
|
|
@@ -5410,8 +5469,12 @@ const isSpecial = (/** @type {HtmlElement} */ el) => {
|
|
|
5410
5469
|
return false;
|
|
5411
5470
|
};
|
|
5412
5471
|
|
|
5413
|
-
|
|
5414
|
-
|
|
5472
|
+
/**
|
|
5473
|
+
* `<font color|face|size>` breaks out of foreign content (§13.2.6.5).
|
|
5474
|
+
* @param {AttributeRun} run the tag's attributes
|
|
5475
|
+
* @returns {boolean} true when one of the breakout attributes is present
|
|
5476
|
+
*/
|
|
5477
|
+
const hasFontBreakoutAttr = (run) => {
|
|
5415
5478
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
5416
5479
|
if (FONT_BREAKOUT_ATTRS.has(_attrNames[i])) return true;
|
|
5417
5480
|
}
|
|
@@ -5590,9 +5653,13 @@ const ATTR_NAME_INTERN = buildNameInternTable(
|
|
|
5590
5653
|
)
|
|
5591
5654
|
);
|
|
5592
5655
|
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5656
|
+
/**
|
|
5657
|
+
* Hoisted so the many `open.some(...)` "is there an open HTML `<template>`?"
|
|
5658
|
+
* checks reuse one predicate instead of allocating an arrow per call.
|
|
5659
|
+
* @param {HtmlElement} e element to test
|
|
5660
|
+
* @returns {boolean} true for an HTML `<template>` element
|
|
5661
|
+
*/
|
|
5662
|
+
const isHtmlTemplateEl = (e) =>
|
|
5596
5663
|
_tagNameOf(e) === "template" && _namespaceOf(e) === NS_HTML;
|
|
5597
5664
|
|
|
5598
5665
|
/**
|
|
@@ -5651,12 +5718,15 @@ const isQuirky = (name, pub, sys) => {
|
|
|
5651
5718
|
return false;
|
|
5652
5719
|
};
|
|
5653
5720
|
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5721
|
+
/**
|
|
5722
|
+
* Allocates an element node.
|
|
5723
|
+
* @param {string} tagName tag name
|
|
5724
|
+
* @param {number} ns namespace flag (`NS_HTML`, `NS_MATHML`, `NS_SVG`)
|
|
5725
|
+
* @param {AttributeRun} attrs the element's attributes
|
|
5726
|
+
* @param {TagPos | null | undefined} pos source position, absent for synthesized elements
|
|
5727
|
+
* @returns {HtmlElement} the new element
|
|
5728
|
+
*/
|
|
5729
|
+
const mkEl = (tagName, ns, attrs, pos) => {
|
|
5660
5730
|
// Inlined element allocation: every column is written exactly once (the
|
|
5661
5731
|
// generic _allocNode would zero five columns mkEl immediately overwrites).
|
|
5662
5732
|
const el = ++_nodeCount;
|
|
@@ -5702,7 +5772,12 @@ const effParent = (parent) => {
|
|
|
5702
5772
|
return tc !== 0 ? tc : parent;
|
|
5703
5773
|
};
|
|
5704
5774
|
|
|
5705
|
-
|
|
5775
|
+
/**
|
|
5776
|
+
* Elements that stop a "have an element in scope" walk (§13.2.4.2).
|
|
5777
|
+
* @param {HtmlElement} el element to test
|
|
5778
|
+
* @returns {boolean} true when the element bounds a scope
|
|
5779
|
+
*/
|
|
5780
|
+
const isScopeBoundary = (el) => {
|
|
5706
5781
|
if (_namespaceOf(el) === NS_HTML) return HTML_SCOPE.has(_tagNameOf(el));
|
|
5707
5782
|
if (_namespaceOf(el) === NS_MATHML) {
|
|
5708
5783
|
return MATHML_SPECIAL.has(_tagNameOf(el));
|
|
@@ -5713,10 +5788,13 @@ const isScopeBoundary = (/** @type {HtmlElement} */ el) => {
|
|
|
5713
5788
|
return false;
|
|
5714
5789
|
};
|
|
5715
5790
|
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5791
|
+
/**
|
|
5792
|
+
* Compares two attribute values, by source range while both are unresolved —
|
|
5793
|
+
* `_attrValueOf` would slice a string per side just to throw it away.
|
|
5794
|
+
* @param {number} i first attribute id
|
|
5795
|
+
* @param {number} j second attribute id
|
|
5796
|
+
* @returns {boolean} true when both values are equal
|
|
5797
|
+
*/
|
|
5720
5798
|
const _attrValueEquals = (i, j) => {
|
|
5721
5799
|
if (_attrValues[i] === null && _attrValues[j] === null) {
|
|
5722
5800
|
const start = _attrValueStarts[i];
|
|
@@ -5736,10 +5814,14 @@ const _attrValueEquals = (i, j) => {
|
|
|
5736
5814
|
return _attrValueOf(i) === _attrValueOf(j);
|
|
5737
5815
|
};
|
|
5738
5816
|
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5817
|
+
/**
|
|
5818
|
+
* Compares two elements' attribute runs, as the active-formatting-elements
|
|
5819
|
+
* "Noah's Ark" clause does.
|
|
5820
|
+
* @param {HtmlElement} a first element
|
|
5821
|
+
* @param {HtmlElement} b second element
|
|
5822
|
+
* @returns {boolean} true when both carry the same attributes
|
|
5823
|
+
*/
|
|
5824
|
+
const sameAttrs = (a, b) => {
|
|
5743
5825
|
const aStart = _nodeAttrStarts[a];
|
|
5744
5826
|
const aCount = _nodeAttrCounts[a];
|
|
5745
5827
|
const bStart = _nodeAttrStarts[b];
|
|
@@ -5754,12 +5836,20 @@ const sameAttrs = (
|
|
|
5754
5836
|
return true;
|
|
5755
5837
|
};
|
|
5756
5838
|
|
|
5757
|
-
|
|
5839
|
+
/**
|
|
5840
|
+
* @param {string} s text to test
|
|
5841
|
+
* @returns {boolean} true when the text starts with HTML whitespace
|
|
5842
|
+
*/
|
|
5843
|
+
const startsWithWs = (s) => {
|
|
5758
5844
|
const c = s.charCodeAt(0);
|
|
5759
5845
|
return c === 0x09 || c === 0x0a || c === 0x0c || c === 0x0d || c === 0x20;
|
|
5760
5846
|
};
|
|
5761
5847
|
|
|
5762
|
-
|
|
5848
|
+
/**
|
|
5849
|
+
* @param {string} s text to test
|
|
5850
|
+
* @returns {boolean} true when the text is HTML whitespace only
|
|
5851
|
+
*/
|
|
5852
|
+
const isAllWs = (s) => {
|
|
5763
5853
|
for (let i = 0; i < s.length; i++) {
|
|
5764
5854
|
const c = s.charCodeAt(i);
|
|
5765
5855
|
// HTML whitespace: tab / LF / FF / CR / space. A charCodeAt loop avoids
|
|
@@ -5807,10 +5897,18 @@ const collapseWhitespaceRuns = (s) => {
|
|
|
5807
5897
|
return last === 0 ? s : out + s.slice(last);
|
|
5808
5898
|
};
|
|
5809
5899
|
|
|
5810
|
-
|
|
5900
|
+
/**
|
|
5901
|
+
* @param {HtmlElement} el element to test
|
|
5902
|
+
* @returns {boolean} true for a MathML text integration point (§13.2.6.5)
|
|
5903
|
+
*/
|
|
5904
|
+
const mathmlTextIntegrationPoint = (el) =>
|
|
5811
5905
|
_namespaceOf(el) === NS_MATHML && MATHML_TEXT_INTEGRATION.has(_tagNameOf(el));
|
|
5812
5906
|
|
|
5813
|
-
|
|
5907
|
+
/**
|
|
5908
|
+
* @param {HtmlElement} el element to test
|
|
5909
|
+
* @returns {boolean} true for an HTML integration point (§13.2.6.5)
|
|
5910
|
+
*/
|
|
5911
|
+
const htmlIntegrationPoint = (el) => {
|
|
5814
5912
|
if (_namespaceOf(el) === NS_MATHML && _tagNameOf(el) === "annotation-xml") {
|
|
5815
5913
|
const enc = _findAttr(_nodeAttrStarts[el], _nodeAttrCounts[el], "encoding");
|
|
5816
5914
|
if (enc !== 0) {
|
|
@@ -5830,13 +5928,14 @@ const htmlIntegrationPoint = (/** @type {HtmlElement} */ el) => {
|
|
|
5830
5928
|
return false;
|
|
5831
5929
|
};
|
|
5832
5930
|
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5931
|
+
/**
|
|
5932
|
+
* Rewrites SVG camelCase names and flags namespaced ones for the serializer.
|
|
5933
|
+
* In place: the run is consumed only by this tag's element.
|
|
5934
|
+
* @param {AttributeRun} run the tag's attributes
|
|
5935
|
+
* @param {number} ns namespace flag of the tag
|
|
5936
|
+
* @returns {AttributeRun} the same run
|
|
5937
|
+
*/
|
|
5938
|
+
const adjustForeignAttrs = (run, ns) => {
|
|
5840
5939
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
5841
5940
|
const name = _attrNames[i];
|
|
5842
5941
|
if (
|
|
@@ -5854,9 +5953,13 @@ const adjustForeignAttrs = (
|
|
|
5854
5953
|
return run;
|
|
5855
5954
|
};
|
|
5856
5955
|
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5956
|
+
/**
|
|
5957
|
+
* Only `definitionurl` is rewritten (in place — the run is consumed only by
|
|
5958
|
+
* this tag's element); the camelCase name serializes as itself.
|
|
5959
|
+
* @param {AttributeRun} run the tag's attributes
|
|
5960
|
+
* @returns {AttributeRun} the same run
|
|
5961
|
+
*/
|
|
5962
|
+
const adjustMathmlAttrs = (run) => {
|
|
5860
5963
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
5861
5964
|
if (_attrNames[i] === "definitionurl") _attrNames[i] = "definitionURL";
|
|
5862
5965
|
}
|
|
@@ -5891,6 +5994,9 @@ let head = 0;
|
|
|
5891
5994
|
let form = 0;
|
|
5892
5995
|
let framesetOk = true;
|
|
5893
5996
|
let pendAttrStart = 0;
|
|
5997
|
+
// Whether §13.2.5.33 dropped a repeated name from the tag being read; a tag
|
|
5998
|
+
// that dropped none needs no word test, keeping it off the per-attribute path.
|
|
5999
|
+
let pendAttrDropped = false;
|
|
5894
6000
|
let fosterParenting = false;
|
|
5895
6001
|
/** @type {number[]} */
|
|
5896
6002
|
const templateModes = [];
|
|
@@ -5956,10 +6062,13 @@ const adjustedCurrent = () => {
|
|
|
5956
6062
|
return cur();
|
|
5957
6063
|
};
|
|
5958
6064
|
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
6065
|
+
/**
|
|
6066
|
+
* Appends a node, merging it into the previous sibling when both are text.
|
|
6067
|
+
* @param {HtmlNodeRef} parent container
|
|
6068
|
+
* @param {HtmlNodeRef} node node to append
|
|
6069
|
+
* @returns {void}
|
|
6070
|
+
*/
|
|
6071
|
+
const appendTo = (parent, node) => {
|
|
5963
6072
|
const p = effParent(parent);
|
|
5964
6073
|
const last = _nodeLastChildren[p];
|
|
5965
6074
|
if (
|
|
@@ -5979,10 +6088,12 @@ const appendTo = (
|
|
|
5979
6088
|
// allocation per inserted node.
|
|
5980
6089
|
/** @type {InsertionPlace} */
|
|
5981
6090
|
const sharedPlace = { parent: doc, beforeNode: 0 };
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
6091
|
+
/**
|
|
6092
|
+
* @param {HtmlNodeRef} parent container
|
|
6093
|
+
* @param {HtmlNodeRef} beforeNode node to insert before (`0` = append)
|
|
6094
|
+
* @returns {InsertionPlace} the shared insertion place
|
|
6095
|
+
*/
|
|
6096
|
+
const placeAt = (parent, beforeNode) => {
|
|
5986
6097
|
sharedPlace.parent = parent;
|
|
5987
6098
|
sharedPlace.beforeNode = beforeNode;
|
|
5988
6099
|
return sharedPlace;
|
|
@@ -6030,10 +6141,13 @@ const appropriatePlace = () => {
|
|
|
6030
6141
|
return placeAt(target, 0);
|
|
6031
6142
|
};
|
|
6032
6143
|
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6144
|
+
/**
|
|
6145
|
+
* Inserts a node at an insertion place, appending when `beforeNode` is `0`.
|
|
6146
|
+
* @param {InsertionPlace} place where the node goes
|
|
6147
|
+
* @param {HtmlNodeRef} node node to insert
|
|
6148
|
+
* @returns {void}
|
|
6149
|
+
*/
|
|
6150
|
+
const insertAtPlace = (place, node) => {
|
|
6037
6151
|
const before = place.beforeNode;
|
|
6038
6152
|
if (before !== 0) {
|
|
6039
6153
|
const p = effParent(place.parent);
|
|
@@ -6084,11 +6198,14 @@ const _recordFostered = (table, node) => {
|
|
|
6084
6198
|
_fosteredLog.push(table, node);
|
|
6085
6199
|
};
|
|
6086
6200
|
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6201
|
+
/**
|
|
6202
|
+
* Inserts a character run at the appropriate place (§13.2.6.1).
|
|
6203
|
+
* @param {string} data decoded text
|
|
6204
|
+
* @param {number} start source offset of the run
|
|
6205
|
+
* @param {number} end source offset past the run
|
|
6206
|
+
* @returns {void}
|
|
6207
|
+
*/
|
|
6208
|
+
const insertCharacters = (data, start, end) => {
|
|
6092
6209
|
const place = appropriatePlace();
|
|
6093
6210
|
if (_nodeTypes[place.parent] === NodeType.Document) return; // never insert text into document
|
|
6094
6211
|
// `skip.text`: drop every `Text` node — construction already used the
|
|
@@ -6149,11 +6266,12 @@ const insertCharacters = (
|
|
|
6149
6266
|
};
|
|
6150
6267
|
|
|
6151
6268
|
/**
|
|
6269
|
+
* Every insertion mode inserts a processing instruction where it inserts a
|
|
6270
|
+
* comment (§13.2.6.4). `skip.comments` drops comments alone.
|
|
6152
6271
|
* @param {CommentToken | ProcessingInstructionToken} t comment or processing instruction token
|
|
6153
6272
|
* @param {InsertionPlace=} place explicit insertion place
|
|
6273
|
+
* @returns {void}
|
|
6154
6274
|
*/
|
|
6155
|
-
// Every insertion mode inserts a processing instruction where it inserts a
|
|
6156
|
-
// comment (§13.2.6.4). `skip.comments` drops comments alone.
|
|
6157
6275
|
const insertCommentOrProcessingInstruction = (t, place) => {
|
|
6158
6276
|
const isPi = t.type === TOKEN_PROCESSING_INSTRUCTION;
|
|
6159
6277
|
if (skipComments && !isPi) return;
|
|
@@ -6166,11 +6284,14 @@ const insertCommentOrProcessingInstruction = (t, place) => {
|
|
|
6166
6284
|
);
|
|
6167
6285
|
};
|
|
6168
6286
|
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6287
|
+
/**
|
|
6288
|
+
* Inserts an HTML element and pushes it onto the open element stack.
|
|
6289
|
+
* @param {string} tagName tag name
|
|
6290
|
+
* @param {AttributeRun} attrs the element's attributes
|
|
6291
|
+
* @param {TagPos | null} pos source position, `null` for a synthesized element
|
|
6292
|
+
* @returns {HtmlElement} the inserted element
|
|
6293
|
+
*/
|
|
6294
|
+
const insertHtmlElement = (tagName, attrs, pos) => {
|
|
6174
6295
|
const el = mkEl(tagName, NS_HTML, attrs, pos);
|
|
6175
6296
|
const place = appropriatePlace();
|
|
6176
6297
|
insertAtPlace(place, el);
|
|
@@ -6178,12 +6299,15 @@ const insertHtmlElement = (
|
|
|
6178
6299
|
return el;
|
|
6179
6300
|
};
|
|
6180
6301
|
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6302
|
+
/**
|
|
6303
|
+
* Inserts a MathML/SVG element and pushes it onto the open element stack.
|
|
6304
|
+
* @param {string} tagName tag name
|
|
6305
|
+
* @param {number} ns namespace flag (`NS_MATHML`, `NS_SVG`)
|
|
6306
|
+
* @param {AttributeRun} attrs the element's attributes
|
|
6307
|
+
* @param {TagPos | null} pos source position, `null` for a synthesized element
|
|
6308
|
+
* @returns {HtmlElement} the inserted element
|
|
6309
|
+
*/
|
|
6310
|
+
const insertForeignElement = (tagName, ns, attrs, pos) => {
|
|
6187
6311
|
const el = mkEl(tagName, ns, attrs, pos);
|
|
6188
6312
|
const place = appropriatePlace();
|
|
6189
6313
|
insertAtPlace(place, el);
|
|
@@ -6198,10 +6322,12 @@ const insertForeignElement = (
|
|
|
6198
6322
|
const SCOPE_DEFAULT = 0;
|
|
6199
6323
|
const SCOPE_BUTTON = 1;
|
|
6200
6324
|
const SCOPE_LIST_ITEM = 2;
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6325
|
+
/**
|
|
6326
|
+
* @param {HtmlElement} el element to test
|
|
6327
|
+
* @param {number} kind scope kind (`SCOPE_DEFAULT`, `SCOPE_BUTTON`, `SCOPE_LIST_ITEM`)
|
|
6328
|
+
* @returns {boolean} true when the element bounds that scope
|
|
6329
|
+
*/
|
|
6330
|
+
const isBoundaryForKind = (el, kind) => {
|
|
6205
6331
|
if (isScopeBoundary(el)) return true;
|
|
6206
6332
|
if (_namespaceOf(el) !== NS_HTML) return false;
|
|
6207
6333
|
if (kind === SCOPE_BUTTON) return _tagNameOf(el) === "button";
|
|
@@ -6210,12 +6336,14 @@ const isBoundaryForKind = (
|
|
|
6210
6336
|
}
|
|
6211
6337
|
return false;
|
|
6212
6338
|
};
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6339
|
+
/**
|
|
6340
|
+
* "have an element in scope": walks the open stack from the top until the
|
|
6341
|
+
* named HTML element is found (true) or a scope boundary is hit (false).
|
|
6342
|
+
* @param {string} tagName tag name to look for
|
|
6343
|
+
* @param {number} kind scope kind (`SCOPE_DEFAULT`, `SCOPE_BUTTON`, `SCOPE_LIST_ITEM`)
|
|
6344
|
+
* @returns {boolean} true when the element is in that scope
|
|
6345
|
+
*/
|
|
6346
|
+
const hasNameInScope = (tagName, kind) => {
|
|
6219
6347
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
6220
6348
|
const el = open[i];
|
|
6221
6349
|
if (_namespaceOf(el) === NS_HTML && _tagNameOf(el) === tagName) {
|
|
@@ -6225,13 +6353,26 @@ const hasNameInScope = (
|
|
|
6225
6353
|
}
|
|
6226
6354
|
return false;
|
|
6227
6355
|
};
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
const
|
|
6233
|
-
|
|
6234
|
-
|
|
6356
|
+
/**
|
|
6357
|
+
* @param {string} tagName tag name to look for
|
|
6358
|
+
* @returns {boolean} true when the element is in scope
|
|
6359
|
+
*/
|
|
6360
|
+
const inScope = (tagName) => hasNameInScope(tagName, SCOPE_DEFAULT);
|
|
6361
|
+
/**
|
|
6362
|
+
* @param {string} tagName tag name to look for
|
|
6363
|
+
* @returns {boolean} true when the element is in button scope
|
|
6364
|
+
*/
|
|
6365
|
+
const inButtonScope = (tagName) => hasNameInScope(tagName, SCOPE_BUTTON);
|
|
6366
|
+
/**
|
|
6367
|
+
* @param {string} tagName tag name to look for
|
|
6368
|
+
* @returns {boolean} true when the element is in list item scope
|
|
6369
|
+
*/
|
|
6370
|
+
const inListItemScope = (tagName) => hasNameInScope(tagName, SCOPE_LIST_ITEM);
|
|
6371
|
+
/**
|
|
6372
|
+
* @param {HtmlElement} target element to look for
|
|
6373
|
+
* @returns {boolean} true when that element itself is in scope
|
|
6374
|
+
*/
|
|
6375
|
+
const inScopeEl = (target) => {
|
|
6235
6376
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
6236
6377
|
const el = open[i];
|
|
6237
6378
|
if (el === target) return true;
|
|
@@ -6239,8 +6380,11 @@ const inScopeEl = (/** @type {HtmlElement} */ target) => {
|
|
|
6239
6380
|
}
|
|
6240
6381
|
return false;
|
|
6241
6382
|
};
|
|
6242
|
-
|
|
6243
|
-
|
|
6383
|
+
/**
|
|
6384
|
+
* @param {string | Set<string>} target a single tag name (the common case) or a set of names
|
|
6385
|
+
* @returns {boolean} true when one of them is in table scope
|
|
6386
|
+
*/
|
|
6387
|
+
const inTableScope = (target) => {
|
|
6244
6388
|
const set = typeof target === "string" ? null : target;
|
|
6245
6389
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
6246
6390
|
const el = open[i];
|
|
@@ -6279,15 +6423,25 @@ const generateImpliedEndTagsThorough = () => {
|
|
|
6279
6423
|
};
|
|
6280
6424
|
|
|
6281
6425
|
// ---- active formatting elements ----
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6426
|
+
|
|
6427
|
+
/**
|
|
6428
|
+
* `splice` allocates an array for the removed element on every call; the list
|
|
6429
|
+
* is short, so shifting in place is both cheaper and allocation-free.
|
|
6430
|
+
* @param {number} i position in `afe` to remove
|
|
6431
|
+
* @returns {void}
|
|
6432
|
+
*/
|
|
6285
6433
|
const afeRemoveAt = (i) => {
|
|
6286
6434
|
const last = afe.length - 1;
|
|
6287
6435
|
for (let k = i; k < last; k++) afe[k] = afe[k + 1];
|
|
6288
6436
|
afe.pop();
|
|
6289
6437
|
};
|
|
6290
|
-
|
|
6438
|
+
/**
|
|
6439
|
+
* Pushes a formatting element, applying the "Noah's Ark" clause: a third
|
|
6440
|
+
* equal entry since the last marker drops the earliest of them.
|
|
6441
|
+
* @param {HtmlElement} el formatting element to push
|
|
6442
|
+
* @returns {void}
|
|
6443
|
+
*/
|
|
6444
|
+
const pushAfe = (el) => {
|
|
6291
6445
|
let count = 0;
|
|
6292
6446
|
for (let i = afe.length - 1; i >= 0; i--) {
|
|
6293
6447
|
const e = afe[i];
|
|
@@ -6349,7 +6503,12 @@ const closePElement = () => {
|
|
|
6349
6503
|
}
|
|
6350
6504
|
};
|
|
6351
6505
|
|
|
6352
|
-
|
|
6506
|
+
/**
|
|
6507
|
+
* Pops the open element stack through the named HTML element.
|
|
6508
|
+
* @param {string} tagName tag name to pop through
|
|
6509
|
+
* @returns {void}
|
|
6510
|
+
*/
|
|
6511
|
+
const popUntil = (tagName) => {
|
|
6353
6512
|
while (open.length) {
|
|
6354
6513
|
const el = /** @type {HtmlElement} */ (popOpen());
|
|
6355
6514
|
_nodeEnds[el] = tokenEnd;
|
|
@@ -6358,7 +6517,12 @@ const popUntil = (/** @type {string} */ tagName) => {
|
|
|
6358
6517
|
}
|
|
6359
6518
|
}
|
|
6360
6519
|
};
|
|
6361
|
-
|
|
6520
|
+
/**
|
|
6521
|
+
* Pops the open element stack through the first of the named HTML elements.
|
|
6522
|
+
* @param {Set<string>} set tag names to pop through
|
|
6523
|
+
* @returns {void}
|
|
6524
|
+
*/
|
|
6525
|
+
const popUntilOneOf = (set) => {
|
|
6362
6526
|
while (open.length) {
|
|
6363
6527
|
const el = /** @type {HtmlElement} */ (popOpen());
|
|
6364
6528
|
_nodeEnds[el] = tokenEnd;
|
|
@@ -6432,14 +6596,14 @@ const resetInsertionMode = () => {
|
|
|
6432
6596
|
};
|
|
6433
6597
|
|
|
6434
6598
|
// ---------- token processing ----------
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
) => {
|
|
6599
|
+
/**
|
|
6600
|
+
* Splits a character token's leading whitespace; per the spec each character
|
|
6601
|
+
* is its own token, so a mixed run can straddle a mode change.
|
|
6602
|
+
* @param {CharToken} t character token
|
|
6603
|
+
* @param {boolean} insert whether to insert the leading whitespace
|
|
6604
|
+
* @returns {CharToken | null} the non-whitespace remainder, `null` when the token was all whitespace
|
|
6605
|
+
*/
|
|
6606
|
+
const leadingWs = (t, insert) => {
|
|
6443
6607
|
const m = /^[\t\n\f\r ]+/.exec(t.data);
|
|
6444
6608
|
const ws = m ? m[0] : "";
|
|
6445
6609
|
// `t.data` is decoded, the offsets are raw, so counting one in the other
|
|
@@ -6458,7 +6622,12 @@ const leadingWs = (
|
|
|
6458
6622
|
};
|
|
6459
6623
|
};
|
|
6460
6624
|
|
|
6461
|
-
|
|
6625
|
+
/**
|
|
6626
|
+
* Runs one token through tree construction (§13.2.6).
|
|
6627
|
+
* @param {Token} t token to process
|
|
6628
|
+
* @returns {void}
|
|
6629
|
+
*/
|
|
6630
|
+
const process = (t) => {
|
|
6462
6631
|
// Track the current token's end so explicit closes can set element `.end`.
|
|
6463
6632
|
// Dispatch on `type` instead of the `in` operator, which goes megamorphic
|
|
6464
6633
|
// across the token union and shows up on the per-token hot path.
|
|
@@ -6480,10 +6649,12 @@ const process = (/** @type {Token} */ t) => {
|
|
|
6480
6649
|
runMode(t);
|
|
6481
6650
|
};
|
|
6482
6651
|
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6652
|
+
/**
|
|
6653
|
+
* @param {HtmlElement} ac adjusted current node
|
|
6654
|
+
* @param {Token} t token to process
|
|
6655
|
+
* @returns {boolean} true when the token follows the foreign content rules
|
|
6656
|
+
*/
|
|
6657
|
+
const shouldUseForeignRules = (ac, t) => {
|
|
6487
6658
|
if (_namespaceOf(ac) === NS_HTML) return false;
|
|
6488
6659
|
if (t.type === TOKEN_START_TAG) {
|
|
6489
6660
|
if (
|
|
@@ -6513,7 +6684,12 @@ const shouldUseForeignRules = (
|
|
|
6513
6684
|
return false;
|
|
6514
6685
|
};
|
|
6515
6686
|
|
|
6516
|
-
|
|
6687
|
+
/**
|
|
6688
|
+
* "in foreign content" rules (§13.2.6.5).
|
|
6689
|
+
* @param {Token} t token to process
|
|
6690
|
+
* @returns {void}
|
|
6691
|
+
*/
|
|
6692
|
+
const foreignContent = (t) => {
|
|
6517
6693
|
if (t.type === TOKEN_CHAR) {
|
|
6518
6694
|
const data = inputHasNul ? t.data.replace(/\0/g, "�") : t.data;
|
|
6519
6695
|
insertCharacters(data, t.start, t.end);
|
|
@@ -6612,10 +6788,13 @@ const foreignContent = (/** @type {Token} */ t) => {
|
|
|
6612
6788
|
};
|
|
6613
6789
|
|
|
6614
6790
|
// ---------- adoption agency algorithm ----------
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6791
|
+
/**
|
|
6792
|
+
* Adoption agency algorithm (§13.2.6.4.7).
|
|
6793
|
+
* @param {string} subject tag name of the end tag being handled
|
|
6794
|
+
* @param {TagPos | null} pos source position of that end tag
|
|
6795
|
+
* @returns {boolean} false when the caller must fall back to "any other end tag"
|
|
6796
|
+
*/
|
|
6797
|
+
const adoptionAgency = (subject, pos) => {
|
|
6619
6798
|
// step 1
|
|
6620
6799
|
const c = cur();
|
|
6621
6800
|
if (
|
|
@@ -6752,7 +6931,11 @@ const adoptionAgency = (
|
|
|
6752
6931
|
return true;
|
|
6753
6932
|
};
|
|
6754
6933
|
|
|
6755
|
-
|
|
6934
|
+
/**
|
|
6935
|
+
* @param {HtmlElement} commonAncestor common ancestor node of the adoption agency
|
|
6936
|
+
* @returns {InsertionPlace} where the moved node goes
|
|
6937
|
+
*/
|
|
6938
|
+
const placeForCommonAncestor = (commonAncestor) => {
|
|
6756
6939
|
if (
|
|
6757
6940
|
TABLE_CONTEXT.has(_tagNameOf(commonAncestor)) &&
|
|
6758
6941
|
_namespaceOf(commonAncestor) === NS_HTML
|
|
@@ -6766,7 +6949,12 @@ const placeForCommonAncestor = (/** @type {HtmlElement} */ commonAncestor) => {
|
|
|
6766
6949
|
return { parent: commonAncestor, beforeNode: 0 };
|
|
6767
6950
|
};
|
|
6768
6951
|
|
|
6769
|
-
|
|
6952
|
+
/**
|
|
6953
|
+
* Unlinks a node from its parent so it can be inserted again.
|
|
6954
|
+
* @param {HtmlNodeRef} node node to detach
|
|
6955
|
+
* @returns {void}
|
|
6956
|
+
*/
|
|
6957
|
+
const detach = (node) => {
|
|
6770
6958
|
const p = _nodeParents[node];
|
|
6771
6959
|
if (p === 0) return;
|
|
6772
6960
|
let prev = 0;
|
|
@@ -6788,11 +6976,13 @@ const detach = (/** @type {HtmlNodeRef} */ node) => {
|
|
|
6788
6976
|
/** @type {Record<string, (t: Token) => void>} */
|
|
6789
6977
|
const modes = {};
|
|
6790
6978
|
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6979
|
+
/**
|
|
6980
|
+
* Dispatches the current insertion mode. An integer switch keeps each
|
|
6981
|
+
* `modes.x(t)` call site monomorphic, where `modes[mode]` would not.
|
|
6982
|
+
* @param {Token} t token to process
|
|
6983
|
+
* @returns {void}
|
|
6984
|
+
*/
|
|
6985
|
+
const runMode = (t) => {
|
|
6796
6986
|
switch (mode) {
|
|
6797
6987
|
case MODE_IN_BODY:
|
|
6798
6988
|
return modes.inBody(t);
|
|
@@ -7137,9 +7327,13 @@ const closeIfPInButtonScope = () => {
|
|
|
7137
7327
|
if (inButtonScope("p")) closePElement();
|
|
7138
7328
|
};
|
|
7139
7329
|
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7330
|
+
/**
|
|
7331
|
+
* "any other end tag" in body: pops to the matching open element, stopping at
|
|
7332
|
+
* the first special element; also the adoption agency's no-element fallback.
|
|
7333
|
+
* @param {string} name tag name of the end tag
|
|
7334
|
+
* @returns {void}
|
|
7335
|
+
*/
|
|
7336
|
+
const anyOtherEndTag = (name) => {
|
|
7143
7337
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
7144
7338
|
const node = open[i];
|
|
7145
7339
|
if (_namespaceOf(node) === NS_HTML && _tagNameOf(node) === name) {
|
|
@@ -7154,7 +7348,12 @@ const anyOtherEndTag = (/** @type {string} */ name) => {
|
|
|
7154
7348
|
}
|
|
7155
7349
|
};
|
|
7156
7350
|
|
|
7157
|
-
|
|
7351
|
+
/**
|
|
7352
|
+
* "in body" start tag rules (§13.2.6.4.7).
|
|
7353
|
+
* @param {StartTagToken} t start tag token
|
|
7354
|
+
* @returns {void}
|
|
7355
|
+
*/
|
|
7356
|
+
const startTagInBody = (t) => {
|
|
7158
7357
|
const name = t.name;
|
|
7159
7358
|
if (name === "html") {
|
|
7160
7359
|
if (open.some(isHtmlTemplateEl)) {
|
|
@@ -7236,6 +7435,9 @@ const startTagInBody = (/** @type {StartTagToken} */ t) => {
|
|
|
7236
7435
|
isSpecial(node) &&
|
|
7237
7436
|
!(_namespaceOf(node) === NS_HTML && ADDRESS_DIV_P.has(_tagNameOf(node)))
|
|
7238
7437
|
) {
|
|
7438
|
+
// A list-item-scope boundary (`ul`, `ol`, a table cell) keeps every item
|
|
7439
|
+
// below it out of reach of any `</li>`, whatever the print writes or drops.
|
|
7440
|
+
if (isBoundaryForKind(node, SCOPE_LIST_ITEM)) break;
|
|
7239
7441
|
// Inlined rather than a call: this runs for every `<li>` in a list.
|
|
7240
7442
|
for (let j = i - 1; j >= 0 && !_printFromSource; j--) {
|
|
7241
7443
|
const below = open[j];
|
|
@@ -7479,7 +7681,12 @@ const startTagInBody = (/** @type {StartTagToken} */ t) => {
|
|
|
7479
7681
|
insertHtmlElement(name, t.attrs, t.pos);
|
|
7480
7682
|
};
|
|
7481
7683
|
|
|
7482
|
-
|
|
7684
|
+
/**
|
|
7685
|
+
* "in body" end tag rules (§13.2.6.4.7).
|
|
7686
|
+
* @param {EndTagToken} t end tag token
|
|
7687
|
+
* @returns {void}
|
|
7688
|
+
*/
|
|
7689
|
+
const endTagInBody = (t) => {
|
|
7483
7690
|
const name = t.name;
|
|
7484
7691
|
if (name === "template") return modes.inHead(t);
|
|
7485
7692
|
if (name === "select") {
|
|
@@ -7573,15 +7780,24 @@ const endTagInBody = (/** @type {EndTagToken} */ t) => {
|
|
|
7573
7780
|
anyOtherEndTag(name);
|
|
7574
7781
|
};
|
|
7575
7782
|
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7783
|
+
/**
|
|
7784
|
+
* Generic RAWTEXT: inserts the element and switches to "text" mode, which
|
|
7785
|
+
* appends the characters the tokenizer already emits and pops on the end tag.
|
|
7786
|
+
* @param {StartTagToken} t start tag token
|
|
7787
|
+
* @returns {void}
|
|
7788
|
+
*/
|
|
7789
|
+
const genericRawtext = (t) => {
|
|
7580
7790
|
insertHtmlElement(t.name, t.attrs, t.pos);
|
|
7581
7791
|
originalMode = mode;
|
|
7582
7792
|
mode = MODE_TEXT;
|
|
7583
7793
|
};
|
|
7584
|
-
|
|
7794
|
+
/**
|
|
7795
|
+
* Generic RCDATA, as `genericRawtext` for the elements whose text is decoded.
|
|
7796
|
+
* @param {StartTagToken} t start tag token
|
|
7797
|
+
* @param {boolean=} swallow whether a newline right after the tag is dropped
|
|
7798
|
+
* @returns {void}
|
|
7799
|
+
*/
|
|
7800
|
+
const genericRcdata = (t, swallow = false) => {
|
|
7585
7801
|
insertHtmlElement(t.name, t.attrs, t.pos);
|
|
7586
7802
|
if (swallow) t.swallowNewline = true;
|
|
7587
7803
|
originalMode = mode;
|
|
@@ -8355,7 +8571,10 @@ const handleAttributeToken = (
|
|
|
8355
8571
|
quoteType
|
|
8356
8572
|
) => {
|
|
8357
8573
|
const name = internLowerName(ATTR_NAME_INTERN, input, nameStart, nameEnd);
|
|
8358
|
-
if (pendAttrStart === 0)
|
|
8574
|
+
if (pendAttrStart === 0) {
|
|
8575
|
+
pendAttrStart = _attrCount + 1;
|
|
8576
|
+
pendAttrDropped = false;
|
|
8577
|
+
}
|
|
8359
8578
|
// Drop duplicate attribute names (per spec). Plain loop avoids a
|
|
8360
8579
|
// per-attribute closure allocation on this hot path.
|
|
8361
8580
|
let dup = false;
|
|
@@ -8365,6 +8584,7 @@ const handleAttributeToken = (
|
|
|
8365
8584
|
break;
|
|
8366
8585
|
}
|
|
8367
8586
|
}
|
|
8587
|
+
if (dup) pendAttrDropped = true;
|
|
8368
8588
|
if (!dup) {
|
|
8369
8589
|
// The raw (undecoded) value is read from the source by offset on
|
|
8370
8590
|
// demand: consumers re-resolve requests from it and the offsets must
|
|
@@ -8399,6 +8619,7 @@ const handleStartTagToken = (
|
|
|
8399
8619
|
if (eofInTag) {
|
|
8400
8620
|
// Any attribute slots already allocated for it are orphaned.
|
|
8401
8621
|
pendAttrStart = 0;
|
|
8622
|
+
pendAttrDropped = false;
|
|
8402
8623
|
return end;
|
|
8403
8624
|
}
|
|
8404
8625
|
const name = internLowerName(TAG_NAME_INTERN, input, nameStart, nameEnd);
|
|
@@ -8409,7 +8630,18 @@ const handleStartTagToken = (
|
|
|
8409
8630
|
// `t.attrs` runs synchronously within this dispatch.
|
|
8410
8631
|
tok.attrs.start = pendAttrStart;
|
|
8411
8632
|
tok.attrs.count = pendAttrStart === 0 ? 0 : _attrCount + 1 - pendAttrStart;
|
|
8633
|
+
// Only a name the run kept is tested: a dropped one repeats one of them.
|
|
8634
|
+
if (pendAttrDropped) {
|
|
8635
|
+
for (let i = pendAttrStart; i <= _attrCount; i++) {
|
|
8636
|
+
const attributeName = _attrNames[i];
|
|
8637
|
+
if (!_isWordSpan(attributeName, 0, attributeName.length)) {
|
|
8638
|
+
_foreignSyntaxAttrRuns.add(pendAttrStart);
|
|
8639
|
+
break;
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
}
|
|
8412
8643
|
pendAttrStart = 0;
|
|
8644
|
+
pendAttrDropped = false;
|
|
8413
8645
|
tok.selfClosing = selfClosing;
|
|
8414
8646
|
tok.swallowNewline = false;
|
|
8415
8647
|
tok.pos.start = start;
|
|
@@ -8439,6 +8671,7 @@ const handleEndTagToken = (input, start, end, nameStart, nameEnd) => {
|
|
|
8439
8671
|
}
|
|
8440
8672
|
// End tags drop any parsed attributes (the slots are orphaned).
|
|
8441
8673
|
pendAttrStart = 0;
|
|
8674
|
+
pendAttrDropped = false;
|
|
8442
8675
|
tok.type = TOKEN_END_TAG;
|
|
8443
8676
|
tok.name = name;
|
|
8444
8677
|
tok.pos.start = start;
|
|
@@ -8524,6 +8757,7 @@ const parseHtml = (input, pos = 0, options = {}) => {
|
|
|
8524
8757
|
form = 0;
|
|
8525
8758
|
framesetOk = true;
|
|
8526
8759
|
pendAttrStart = 0;
|
|
8760
|
+
pendAttrDropped = false;
|
|
8527
8761
|
fosterParenting = false;
|
|
8528
8762
|
_fosteredLog = null;
|
|
8529
8763
|
_printFromSource = false;
|
|
@@ -8752,14 +8986,13 @@ const mirrorSelectedContent = (node, select) => {
|
|
|
8752
8986
|
* What the minifying printer may rewrite. Every entry is on unless it is
|
|
8753
8987
|
* `false`, so a document one transform breaks can still be minified by the rest.
|
|
8754
8988
|
* @typedef {object} HtmlTransformOptions
|
|
8755
|
-
* @property {boolean=} collapseBooleanAttributes write a boolean attribute
|
|
8989
|
+
* @property {(boolean | "all")=} collapseBooleanAttributes write a boolean attribute as the bare name its presence already means; `true` only the spellings the spec canonicalizes, `"all"` any value
|
|
8756
8990
|
* @property {(boolean | "all" | "some" | string | RegExp | ((comment: string) => boolean))=} comments which comments survive: `"some"` (the default) the ones that carry something, `true` / `"all"` every one, `false` none, or the ones a pattern matches / a predicate accepts, over the comment's own text. A comment a parser or a server reads is kept whatever this says
|
|
8757
|
-
* @property {boolean=} minifyJson strip the whitespace between a JSON `<script>`'s tokens
|
|
8758
|
-
* @property {boolean=} minifyStyles run the CSS minifier over an inline `<style>` and every `style=""`
|
|
8759
8991
|
* @property {boolean=} normalizeAttributeQuotes drop or re-pick an attribute value's quotes
|
|
8760
8992
|
* @property {boolean=} normalizeEnumeratedAttributes fold an enumerated value to the keyword it names
|
|
8761
8993
|
* @property {boolean=} normalizeListAttributes normalize a space-, comma- or descriptor-separated list value (`class`, `rel`, `srcset`, `sizes`, the viewport `content`)
|
|
8762
8994
|
* @property {boolean=} normalizeNumericAttributes write an integer attribute the one way its rules read it
|
|
8995
|
+
* @property {boolean=} normalizeUrlAttributes drop the whitespace around a URL value (`href`, `src`, `action`, …), which resolving the URL skips over
|
|
8763
8996
|
* @property {boolean=} removeOptionalTags leave out an optional tag other than the `<html>` / `<head>` / `<body>` shell, which is `removeImpliedTags`
|
|
8764
8997
|
*/
|
|
8765
8998
|
|
|
@@ -8768,31 +9001,27 @@ const mirrorSelectedContent = (node, select) => {
|
|
|
8768
9001
|
* @property {string=} fragmentContext context element tag name for fragment parsing (see `parseHtml`); the HTML analog of the CSS parser's `as` parse-mode option
|
|
8769
9002
|
* @property {HtmlAstSkip=} skip node kinds to omit from the AST for speed/memory (see `HtmlAstSkip`)
|
|
8770
9003
|
* @property {boolean=} minimize print the safely-minified serialization (nodes rebuilt from source, inert comments dropped, opening-tag whitespace collapsed) as `process` walks, and return it (default false = walk only, return `""`)
|
|
8771
|
-
* @property {CssEnvironment=} environment CSS's, not HTML's: handed to the CSS minifier that runs over an inline `<style>` and every `style=""`, so the inline copy of a declaration agrees with the `.css` asset
|
|
8772
|
-
* @property {boolean=} convertLengthUnits CSS's too, handed over with `environment` (see `HtmlPrintOptions`)
|
|
8773
|
-
* @property {boolean=} rewriteCustomProperties CSS's too, handed over with `environment` (see `HtmlPrintOptions`)
|
|
8774
|
-
* @property {CssTransformOptions=} cssTransforms CSS's per-transform switches too, handed over with `environment` (see `HtmlPrintOptions`)
|
|
8775
9004
|
* @property {HtmlTransformOptions=} transforms which of the meaning-preserving rewrites the minifying print makes; each is on unless it is `false`
|
|
8776
9005
|
* @property {(boolean | "conservative" | "smart" | "all")=} collapseWhitespace collapse each run of whitespace in text to a single space, except where an ancestor renders it verbatim; `"smart"` also drops what sits against a block edge and `"all"` drops every edge (default false)
|
|
8777
9006
|
* @property {boolean=} removeEmptyAttributes drop an attribute whose empty value leaves it in the state its absence gives (default false)
|
|
8778
9007
|
* @property {boolean=} removeEmptyElements drop an element with no children and no attributes, unless its bare form is meaningful (default false)
|
|
8779
9008
|
* @property {boolean=} mergeStyles print a run of adjacent `<style>` elements as one sheet, which removes elements (default false)
|
|
9009
|
+
* @property {boolean=} mergeScripts print a run of adjacent bare `<script>` elements whose bodies this print writes itself as one, which removes elements, hoists a later body's declarations into the ones before it, and lets a failing body end the whole run (default false)
|
|
8780
9010
|
* @property {boolean=} sortAttributes print an element's attributes commonest name first, ties by name, which nothing in HTML reads (default false)
|
|
8781
9011
|
* @property {boolean=} sortTokenLists print every token list the DOM reads as a set (`class`, `rel`, `part`, …) in token order (default false)
|
|
8782
9012
|
* @property {(boolean | "smart" | "all")=} removeRedundantAttributes drop an attribute whose value is the element's own default; `true` is `"smart"`, and `"all"` also drops the spec defaults a selector can match (default false)
|
|
8783
9013
|
* @property {(boolean | "smart" | "all")=} removeImpliedTags how much of the `<html>` / `<head>` / `<body>` shell §13.1.2.4 lets the parser imply may be left out: `"smart"` leaves out only the `<html>` start tag, `true` (or `"all"`) all six, `false` none (default `"smart"`)
|
|
8784
9014
|
* @property {boolean=} deferSrcdoc whether an `<iframe srcdoc>` is among what `deferEmbeddedSource` collects (default true); false for a caller that minifies them itself, which keeps the attribute on the normal path and its shorter delimiter
|
|
8785
|
-
* @property {DeferredEmbeddedSource[]=} deferEmbeddedSource collects what `renderEmbeddedSource` would be offered instead of offering it, for a caller whose renderer is asynchronous: the print leaves a marker for each and `finish` puts the answers in their place, so one parse serves both.
|
|
8786
|
-
* @property {EmbeddedSourceRenderer=} renderEmbeddedSource renders each nested body this document embeds — an inline `<style>`, every `style=""` (
|
|
9015
|
+
* @property {DeferredEmbeddedSource[]=} deferEmbeddedSource collects what `renderEmbeddedSource` would be offered instead of offering it, for a caller whose renderer is asynchronous: the print leaves a marker for each and `finish` puts the answers in their place, so one parse serves both. Takes precedence over `renderEmbeddedSource`
|
|
9016
|
+
* @property {EmbeddedSourceRenderer=} renderEmbeddedSource renders each nested body this document embeds — an inline `<style>`, every `style=""` (as the block's contents it is, SVG's and MathML's included), a `<script>` holding JSON or JavaScript (with `as` naming which production of it the body is — `"module"` for a `<script type=module>`, `"script"` for a classic one), every event handler attribute, with `as: "event-handler"` saying it is a function body rather than a script — the production a `return` at its top level is in, which a renderer whose engine takes only whole scripts wraps before it reads — an `<svg>` subtree, and the document an `<iframe srcdoc>` holds (decoded, and written back escaped). The only way any of them is minified: webpack minifies nothing here itself, and `builtinEmbeddedRenderer` is its own CSS and JSON minifiers for a caller to pass; returning anything but text leaves a body as written
|
|
8787
9017
|
*/
|
|
8788
9018
|
|
|
8789
9019
|
/**
|
|
8790
|
-
* What the HTML printer may be told, on top of the `mode` every language has
|
|
8791
|
-
*
|
|
8792
|
-
*
|
|
8793
|
-
*
|
|
8794
|
-
*
|
|
8795
|
-
* @typedef {Pick<CssProcessOptions, "environment" | "convertLengthUnits" | "rewriteCustomProperties"> & { cssTransforms?: CssTransformOptions, transforms?: HtmlTransformOptions, collapseWhitespace?: boolean | "conservative" | "smart" | "all", mergeStyles?: boolean, removeEmptyAttributes?: boolean, removeEmptyElements?: boolean, removeRedundantAttributes?: boolean | "smart" | "all", sortAttributes?: boolean, sortTokenLists?: boolean, removeImpliedTags?: boolean | "smart" | "all", renderEmbeddedSource?: EmbeddedSourceRenderer, deferEmbeddedSource?: DeferredEmbeddedSource[], deferSrcdoc?: boolean }} HtmlPrintOptions
|
|
9020
|
+
* What the HTML printer may be told, on top of the `mode` every language has:
|
|
9021
|
+
* `optimization.minimize.html`, and the renderer every nested body goes through.
|
|
9022
|
+
* `optimization.minimize.css` reaches inline CSS through `builtinEmbeddedRenderer`,
|
|
9023
|
+
* which `htmlMinify` passes (see `lib/config/defaults.js`).
|
|
9024
|
+
* @typedef {{ transforms?: HtmlTransformOptions, collapseWhitespace?: boolean | "conservative" | "smart" | "all", mergeStyles?: boolean, mergeScripts?: boolean, removeEmptyAttributes?: boolean, removeEmptyElements?: boolean, removeRedundantAttributes?: boolean | "smart" | "all", sortAttributes?: boolean, sortTokenLists?: boolean, removeImpliedTags?: boolean | "smart" | "all", renderEmbeddedSource?: EmbeddedSourceRenderer, deferEmbeddedSource?: DeferredEmbeddedSource[], deferSrcdoc?: boolean }} HtmlPrintOptions
|
|
8796
9025
|
*/
|
|
8797
9026
|
|
|
8798
9027
|
/** @typedef {import("../util/SourceProcessor").PrintContext<HtmlPath, HtmlNodeRef, HtmlPrintOptions>} PrintContext */
|
|
@@ -8815,6 +9044,18 @@ const _hasOpenReference = (s) => {
|
|
|
8815
9044
|
return true;
|
|
8816
9045
|
};
|
|
8817
9046
|
|
|
9047
|
+
/**
|
|
9048
|
+
* Whether the tag printed after this text would fuse with its tail: a `<` runs
|
|
9049
|
+
* into the next tag's name, and a `</` meets that tag's `<` as a bogus comment,
|
|
9050
|
+
* which consumes to the next `>` and takes the text with it.
|
|
9051
|
+
* @param {string} s source text
|
|
9052
|
+
* @returns {boolean} true when the text cannot be emitted as written
|
|
9053
|
+
*/
|
|
9054
|
+
const _tailFusesWithNextTag = (s) => {
|
|
9055
|
+
const tail = s.charCodeAt(s.length - 1);
|
|
9056
|
+
return tail === 60 || (tail === 47 && s.charCodeAt(s.length - 2) === 60);
|
|
9057
|
+
};
|
|
9058
|
+
|
|
8818
9059
|
/**
|
|
8819
9060
|
* Whether the `&` at `i` would be consumed as a character reference when the
|
|
8820
9061
|
* text is read back (§13.2.5.72): only before `#` or a name the table holds.
|
|
@@ -8909,16 +9150,36 @@ const _escapeTextContent = (s, minify) => {
|
|
|
8909
9150
|
* @returns {string} serialized opening tag
|
|
8910
9151
|
*/
|
|
8911
9152
|
const _synthesizeOpenTag = (path) => {
|
|
8912
|
-
|
|
9153
|
+
const tagName = path.tagName();
|
|
9154
|
+
let out = `<${tagName}`;
|
|
9155
|
+
// Looked up once for the tag, as the attribute loop does for a written one.
|
|
9156
|
+
const enumeratedOn =
|
|
9157
|
+
_minifying && path.namespace(path.node) === NS_HTML
|
|
9158
|
+
? ENUMERATED_KEYWORDS[tagName]
|
|
9159
|
+
: undefined;
|
|
8913
9160
|
for (const attribute of path.attributes()) {
|
|
8914
9161
|
const name =
|
|
8915
9162
|
attribute.serializedName !== undefined
|
|
8916
9163
|
? attribute.serializedName
|
|
8917
9164
|
: attribute.name;
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
9165
|
+
if (attribute.value === "") {
|
|
9166
|
+
out += ` ${name}`;
|
|
9167
|
+
continue;
|
|
9168
|
+
}
|
|
9169
|
+
// Nothing was written here for this tag, so there is no source spelling to
|
|
9170
|
+
// beat: the shortest one wins outright.
|
|
9171
|
+
if (!_minifying || !_transforms.normalizeAttributeQuotes) {
|
|
9172
|
+
out += ` ${name}="${attribute.value.replace(/"/g, """)}"`;
|
|
9173
|
+
continue;
|
|
9174
|
+
}
|
|
9175
|
+
const value = _rewriteAttributeValue(
|
|
9176
|
+
tagName,
|
|
9177
|
+
name,
|
|
9178
|
+
attribute.value,
|
|
9179
|
+
false,
|
|
9180
|
+
enumeratedOn
|
|
9181
|
+
);
|
|
9182
|
+
out += ` ${name}=${_minimalAttributeValue(decodeEntities(value, true))}`;
|
|
8922
9183
|
}
|
|
8923
9184
|
return `${out}>`;
|
|
8924
9185
|
};
|
|
@@ -9223,36 +9484,17 @@ const _canOmitColgroupStart = (path, node) => {
|
|
|
9223
9484
|
return !_canOmitEndTag(path, "colgroup", previous);
|
|
9224
9485
|
};
|
|
9225
9486
|
|
|
9226
|
-
// The CSS abilities of the target, forwarded into the nested CSS minifier so an
|
|
9227
|
-
// inline `<style>` or `style=""` is gated exactly like a `.css` asset. Set per
|
|
9228
|
-
// print, since the printer is the only place the options are in scope.
|
|
9229
|
-
/** @type {CssEnvironment | undefined} */
|
|
9230
|
-
let _cssEnvironment;
|
|
9231
|
-
|
|
9232
|
-
// Whether the nested CSS minifier may rewrite a length's unit — forwarded like
|
|
9233
|
-
// `_cssEnvironment` so inline CSS agrees with the `.css` assets.
|
|
9234
|
-
let _cssConvertLengthUnits = false;
|
|
9235
|
-
|
|
9236
|
-
// And whether it may shorten a custom property's value, forwarded the same way.
|
|
9237
|
-
let _cssRewriteCustomProperties = false;
|
|
9238
|
-
|
|
9239
|
-
// And which of its rewrites it makes at all, forwarded the same way — inline CSS
|
|
9240
|
-
// is minified by the same switches a `.css` asset is.
|
|
9241
|
-
/** @type {CssTransformOptions | undefined} */
|
|
9242
|
-
let _cssTransforms;
|
|
9243
|
-
|
|
9244
9487
|
// Every rewrite on, which is what a print with no `transforms` option makes and
|
|
9245
9488
|
// what the walk-only passes hold. Frozen, since it is shared rather than copied.
|
|
9246
9489
|
/** @type {Required<HtmlTransformOptions>} */
|
|
9247
9490
|
const _ALL_TRANSFORMS = Object.freeze({
|
|
9248
9491
|
collapseBooleanAttributes: true,
|
|
9249
9492
|
comments: "some",
|
|
9250
|
-
minifyJson: true,
|
|
9251
|
-
minifyStyles: true,
|
|
9252
9493
|
normalizeAttributeQuotes: true,
|
|
9253
9494
|
normalizeEnumeratedAttributes: true,
|
|
9254
9495
|
normalizeListAttributes: true,
|
|
9255
9496
|
normalizeNumericAttributes: true,
|
|
9497
|
+
normalizeUrlAttributes: true,
|
|
9256
9498
|
removeOptionalTags: true
|
|
9257
9499
|
});
|
|
9258
9500
|
|
|
@@ -9333,13 +9575,8 @@ const pickTransforms = (options) => {
|
|
|
9333
9575
|
return out;
|
|
9334
9576
|
};
|
|
9335
9577
|
|
|
9336
|
-
// Minified `style` attributes by raw text — a pure function of the value and
|
|
9337
|
-
// the options above, which a document repeats far more often than it adds one.
|
|
9338
|
-
/** @type {Map<string, string>} */
|
|
9339
|
-
const _styleAttributeCache = new Map();
|
|
9340
|
-
|
|
9341
9578
|
// How far text whitespace may be collapsed (see `HtmlProcessOptions`) — set per
|
|
9342
|
-
// print, like `
|
|
9579
|
+
// print, like `_minifying`. "none" is off; the rest name how much of the
|
|
9343
9580
|
// whitespace against a boundary no line box reaches goes with it.
|
|
9344
9581
|
/** @type {"none" | "conservative" | "smart" | "all"} */
|
|
9345
9582
|
let _collapseWhitespace = "none";
|
|
@@ -9348,38 +9585,53 @@ let _collapseWhitespace = "none";
|
|
|
9348
9585
|
// have to predict what it will drop.
|
|
9349
9586
|
let _minifying = false;
|
|
9350
9587
|
|
|
9351
|
-
// The caller's renderer for source nested in this document, set per print
|
|
9352
|
-
//
|
|
9353
|
-
// JavaScript and SVG are reached — webpack ships no minifier for either.
|
|
9588
|
+
// The caller's renderer for source nested in this document, set per print: the
|
|
9589
|
+
// only way any of it is minified, `builtinEmbeddedRenderer` being webpack's own.
|
|
9354
9590
|
/** @type {EmbeddedSourceRenderer | undefined} */
|
|
9355
9591
|
let _renderEmbeddedSource;
|
|
9356
9592
|
|
|
9357
9593
|
// Where an asynchronous caller's embedded sources are recorded instead of
|
|
9358
|
-
// rendered, set per print
|
|
9359
|
-
// what to emit is still minified by the built-in for that decision — only the
|
|
9360
|
-
// text that reaches the output is deferred.
|
|
9594
|
+
// rendered, set per print; what the print decides from a body reads its source.
|
|
9361
9595
|
/** @type {DeferredEmbeddedSource[] | undefined} */
|
|
9362
9596
|
let _deferEmbeddedSource;
|
|
9363
9597
|
|
|
9364
9598
|
// Whether an `<iframe srcdoc>` is among what is deferred. A caller minifying
|
|
9365
9599
|
// them itself keeps the attribute on the normal path, which re-quotes it to
|
|
9366
9600
|
// whichever delimiter its content makes shorter.
|
|
9601
|
+
/**
|
|
9602
|
+
* One attribute, written with whichever delimiter its value needs fewer escapes
|
|
9603
|
+
* inside — a minified document is mostly `"`, so one holding a `"` is shorter
|
|
9604
|
+
* quoted with `'`. Where neither costs anything the source's own is kept.
|
|
9605
|
+
* @param {string} name the attribute name
|
|
9606
|
+
* @param {string} value the value to write
|
|
9607
|
+
* @param {string} source the delimiter the source spelled it with, if any
|
|
9608
|
+
* @returns {string} the attribute, delimiter and all
|
|
9609
|
+
*/
|
|
9610
|
+
const _writeQuotedAttribute = (name, value, source) => {
|
|
9611
|
+
const double = value.includes('"');
|
|
9612
|
+
const single = value.includes("'");
|
|
9613
|
+
const quote =
|
|
9614
|
+
double !== single ? (double ? 39 : 34) : source === "'" ? 39 : 34;
|
|
9615
|
+
const delimiter = String.fromCharCode(quote);
|
|
9616
|
+
|
|
9617
|
+
return ` ${name}=${delimiter}${escapeAttribute(value, quote, true)}${delimiter}`;
|
|
9618
|
+
};
|
|
9619
|
+
|
|
9367
9620
|
let _deferSrcdoc = true;
|
|
9368
9621
|
|
|
9369
9622
|
/**
|
|
9370
|
-
* Hand one nested body to the caller's renderer
|
|
9371
|
-
*
|
|
9372
|
-
*
|
|
9623
|
+
* Hand one nested body to the caller's renderer. Anything it throws is a
|
|
9624
|
+
* renderer that did not answer: a document must not fail to serialize because
|
|
9625
|
+
* something inside it did not parse.
|
|
9373
9626
|
* @param {string} source the nested body
|
|
9374
9627
|
* @param {string} type its language, e.g. `"css"` / `"javascript"` / `"svg"`
|
|
9375
9628
|
* @param {string=} as which production of that language it is, for one with more than one
|
|
9376
|
-
* @returns {string | null} the rendered body, or null when it declined
|
|
9629
|
+
* @returns {string | null} the rendered body, or null when there is no renderer or it declined
|
|
9377
9630
|
*/
|
|
9378
9631
|
const _renderEmbeddedOrNull = (source, type, as) => {
|
|
9632
|
+
if (_renderEmbeddedSource === undefined) return null;
|
|
9379
9633
|
try {
|
|
9380
|
-
const rendered =
|
|
9381
|
-
_renderEmbeddedSource
|
|
9382
|
-
)(source, {
|
|
9634
|
+
const rendered = _renderEmbeddedSource(source, {
|
|
9383
9635
|
type,
|
|
9384
9636
|
hostType: HTML_TYPE,
|
|
9385
9637
|
...(as === undefined ? undefined : { as })
|
|
@@ -9417,19 +9669,23 @@ const _deferEmbedded = (source, type, build, as) => {
|
|
|
9417
9669
|
};
|
|
9418
9670
|
|
|
9419
9671
|
/**
|
|
9420
|
-
* `_renderEmbeddedOrNull`
|
|
9421
|
-
*
|
|
9672
|
+
* `_renderEmbeddedOrNull` with the body left exactly as written where the
|
|
9673
|
+
* renderer declines it, deferred to the caller's where one collects.
|
|
9422
9674
|
* @param {string} source the nested body
|
|
9423
9675
|
* @param {string} type its language, e.g. `"css"` / `"javascript"` / `"svg"`
|
|
9676
|
+
* @param {string=} as which production of that language it is, for one with more than one
|
|
9424
9677
|
* @returns {string} the rendered body, or the original
|
|
9425
9678
|
*/
|
|
9426
|
-
const _renderEmbedded = (source, type) => {
|
|
9679
|
+
const _renderEmbedded = (source, type, as) => {
|
|
9427
9680
|
if (_deferEmbeddedSource !== undefined) {
|
|
9428
|
-
return _deferEmbedded(
|
|
9429
|
-
|
|
9681
|
+
return _deferEmbedded(
|
|
9682
|
+
source,
|
|
9683
|
+
type,
|
|
9684
|
+
(rendered) => (typeof rendered === "string" ? rendered : source),
|
|
9685
|
+
as
|
|
9430
9686
|
);
|
|
9431
9687
|
}
|
|
9432
|
-
const rendered = _renderEmbeddedOrNull(source, type);
|
|
9688
|
+
const rendered = _renderEmbeddedOrNull(source, type, as);
|
|
9433
9689
|
return rendered === null ? source : rendered;
|
|
9434
9690
|
};
|
|
9435
9691
|
|
|
@@ -9483,11 +9739,13 @@ let _sortTokenLists = false;
|
|
|
9483
9739
|
// `document.styleSheets` and a `:nth-child` selector read.
|
|
9484
9740
|
let _mergeStyles = false;
|
|
9485
9741
|
|
|
9486
|
-
//
|
|
9487
|
-
|
|
9488
|
-
|
|
9742
|
+
// Print a run of adjacent bare `<script>` elements as one.
|
|
9743
|
+
let _mergeScripts = false;
|
|
9744
|
+
|
|
9745
|
+
// A run of adjacent `<style>` or `<script>` prints as one, so the elements
|
|
9746
|
+
// after the first print nothing; null until a document has a run.
|
|
9489
9747
|
/** @type {Set<HtmlElement> | null} */
|
|
9490
|
-
let
|
|
9748
|
+
let _absorbed = null;
|
|
9491
9749
|
|
|
9492
9750
|
// Foster parenting puts a node before the table it was written inside, so tree
|
|
9493
9751
|
// order and source order disagree there. Recorded where it happens.
|
|
@@ -9633,79 +9891,14 @@ const _canOmitStartTag = (path, name, node, open) => {
|
|
|
9633
9891
|
};
|
|
9634
9892
|
|
|
9635
9893
|
/**
|
|
9636
|
-
*
|
|
9637
|
-
*
|
|
9638
|
-
*
|
|
9639
|
-
* sheet that was already minimal: text the minifier cannot parse may be
|
|
9640
|
-
* unterminated, and then it swallows whatever is concatenated after it.
|
|
9641
|
-
* @param {string} css the element's text
|
|
9642
|
-
* @param {{ text: string }=} decided filled with the text this print may read back, which is the built-in's rather than the marker when the caller's renderer is deferred
|
|
9643
|
-
* @returns {string | null} the minified text, or null
|
|
9644
|
-
*/
|
|
9645
|
-
const _minifyStyleBody = (css, decided) => {
|
|
9646
|
-
if (!_transforms.minifyStyles) return null;
|
|
9647
|
-
if (css.trim() === "") return css;
|
|
9648
|
-
try {
|
|
9649
|
-
if (_deferEmbeddedSource !== undefined) {
|
|
9650
|
-
// The built-in still runs: what this print does next is decided by
|
|
9651
|
-
// reading the minified text back (whether a run merges), and a marker
|
|
9652
|
-
// cannot be read. Only the text that reaches the output is the caller's,
|
|
9653
|
-
// and `decided` carries the readable text to whoever needs it.
|
|
9654
|
-
const builtin = _builtinStyleBody(css);
|
|
9655
|
-
if (builtin === null) return null;
|
|
9656
|
-
if (decided !== undefined) decided.text = builtin;
|
|
9657
|
-
return _deferEmbedded(css, CSS_TYPE, (rendered) =>
|
|
9658
|
-
typeof rendered === "string" ? rendered : builtin
|
|
9659
|
-
);
|
|
9660
|
-
}
|
|
9661
|
-
if (_renderEmbeddedSource !== undefined) {
|
|
9662
|
-
// Declining falls back to the built-in, so a renderer that covers some
|
|
9663
|
-
// languages does not turn the ones it does not cover off.
|
|
9664
|
-
const rendered = _renderEmbeddedOrNull(css, CSS_TYPE);
|
|
9665
|
-
if (rendered !== null) return rendered;
|
|
9666
|
-
}
|
|
9667
|
-
return _builtinStyleBody(css);
|
|
9668
|
-
} catch (_err) {
|
|
9669
|
-
return null;
|
|
9670
|
-
}
|
|
9671
|
-
};
|
|
9672
|
-
|
|
9673
|
-
/**
|
|
9674
|
-
* The CSS minify webpack ships, for a body no caller's renderer answered for.
|
|
9675
|
-
* @param {string} css the element's text
|
|
9676
|
-
* @param {("stylesheet" | "block-contents")=} as which production it is — a `style=""` holds a block's contents, an element a stylesheet
|
|
9677
|
-
* @returns {string | null} the minified text, or null
|
|
9678
|
-
*/
|
|
9679
|
-
const _builtinStyleBody = (css, as) => {
|
|
9680
|
-
try {
|
|
9681
|
-
const cssSyntax = _cssSyntax || (_cssSyntax = require("../css/syntax"));
|
|
9682
|
-
const options = {
|
|
9683
|
-
environment: _cssEnvironment,
|
|
9684
|
-
convertLengthUnits: _cssConvertLengthUnits,
|
|
9685
|
-
rewriteCustomProperties: _cssRewriteCustomProperties,
|
|
9686
|
-
transforms: _cssTransforms
|
|
9687
|
-
};
|
|
9688
|
-
|
|
9689
|
-
return new cssSyntax.SourceProcessor().process(css, {
|
|
9690
|
-
mode: "minify",
|
|
9691
|
-
...options,
|
|
9692
|
-
as
|
|
9693
|
-
}).code;
|
|
9694
|
-
} catch (_err) {
|
|
9695
|
-
return null;
|
|
9696
|
-
}
|
|
9697
|
-
};
|
|
9698
|
-
|
|
9699
|
-
/**
|
|
9700
|
-
* `_minifyStyleBody`, with text it rejects handed back untouched rather than
|
|
9701
|
-
* failing the build.
|
|
9894
|
+
* Offer a `<style>` body to the renderer, blank text kept as written. Webpack
|
|
9895
|
+
* minifies no CSS here itself: `builtinEmbeddedRenderer` is what a caller
|
|
9896
|
+
* passes for that.
|
|
9702
9897
|
* @param {string} css the element's text
|
|
9703
|
-
* @returns {string} the
|
|
9898
|
+
* @returns {string} the rendered text, the marker standing for it, or the text as written
|
|
9704
9899
|
*/
|
|
9705
|
-
const
|
|
9706
|
-
|
|
9707
|
-
return minified === null ? css : minified;
|
|
9708
|
-
};
|
|
9900
|
+
const _minifyStyleBody = (css) =>
|
|
9901
|
+
css.trim() === "" ? css : _renderEmbedded(css, CSS_TYPE);
|
|
9709
9902
|
|
|
9710
9903
|
// The throwaway rule a `style` attribute's declaration list is minified inside.
|
|
9711
9904
|
// The selector is only ever written and read back here, so it is the shortest
|
|
@@ -9720,9 +9913,10 @@ const _minifyInlineCss = (css) => {
|
|
|
9720
9913
|
* @param {string} value its rewritten value
|
|
9721
9914
|
* @param {string} rawValue its value as the source spelled it
|
|
9722
9915
|
* @param {string | null} decoded the value decoded, for one carrying character references, or null
|
|
9916
|
+
* @param {boolean} quoted whether the source spelled the value with a delimiter, which is two characters this has to beat
|
|
9723
9917
|
* @returns {{ text: string, unquotedTail: boolean } | null} what to emit, or null to leave the source alone
|
|
9724
9918
|
*/
|
|
9725
|
-
const _writeAttribute = (attributeName, value, rawValue, decoded) => {
|
|
9919
|
+
const _writeAttribute = (attributeName, value, rawValue, decoded, quoted) => {
|
|
9726
9920
|
const minimal =
|
|
9727
9921
|
_transforms.normalizeAttributeQuotes && decoded !== null
|
|
9728
9922
|
? _minimalAttributeValue(decoded)
|
|
@@ -9732,7 +9926,8 @@ const _writeAttribute = (attributeName, value, rawValue, decoded) => {
|
|
|
9732
9926
|
|
|
9733
9927
|
if (
|
|
9734
9928
|
minimal !== null &&
|
|
9735
|
-
minimal.length <
|
|
9929
|
+
minimal.length <
|
|
9930
|
+
(unquotable ? value.length : rawValue.length + (quoted ? 2 : 0))
|
|
9736
9931
|
) {
|
|
9737
9932
|
const opened = minimal.charCodeAt(0);
|
|
9738
9933
|
|
|
@@ -9758,54 +9953,17 @@ const _writeAttribute = (attributeName, value, rawValue, decoded) => {
|
|
|
9758
9953
|
};
|
|
9759
9954
|
|
|
9760
9955
|
/**
|
|
9761
|
-
*
|
|
9762
|
-
*
|
|
9763
|
-
*
|
|
9956
|
+
* Offer a `style` attribute's declaration list to the renderer as the
|
|
9957
|
+
* block-contents production it is, which gives it the merges a rule's block
|
|
9958
|
+
* gets. The deferring path offers it at the attribute instead, where the shape
|
|
9959
|
+
* an answer has to land in is known.
|
|
9764
9960
|
* @param {string} raw raw (undecoded) attribute value
|
|
9765
|
-
* @returns {string} the
|
|
9961
|
+
* @returns {string} the rendered declarations, or the value as written
|
|
9766
9962
|
*/
|
|
9767
9963
|
const _minifyStyleAttribute = (raw) => {
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
// A caller's renderer is handed every `style=""` the document holds, so it is
|
|
9772
|
-
// not a function of the value alone and nothing about it may be reused.
|
|
9773
|
-
if (_renderEmbeddedSource !== undefined) {
|
|
9774
|
-
return _minifyStyleAttributeUncached(raw);
|
|
9775
|
-
}
|
|
9776
|
-
const memoized = _styleAttributeCache.get(raw);
|
|
9777
|
-
if (memoized !== undefined) return memoized;
|
|
9778
|
-
const minified = _minifyStyleAttributeUncached(raw);
|
|
9779
|
-
_styleAttributeCache.set(raw, minified);
|
|
9780
|
-
return minified;
|
|
9781
|
-
};
|
|
9782
|
-
|
|
9783
|
-
/**
|
|
9784
|
-
* `_minifyStyleAttribute` without the memo — every path here runs the CSS
|
|
9785
|
-
* minifier, and every return is safe to keep, none of them reading state the
|
|
9786
|
-
* print goes on to change.
|
|
9787
|
-
* @param {string} raw raw (undecoded) attribute value
|
|
9788
|
-
* @returns {string} the minified declarations
|
|
9789
|
-
*/
|
|
9790
|
-
const _minifyStyleAttributeUncached = (raw) => {
|
|
9791
|
-
if (raw.trim() === "") return raw;
|
|
9792
|
-
|
|
9793
|
-
// Offered as the CSS it is, with `as` saying which production — the wrapper a
|
|
9794
|
-
// declaration list needs for the transforms a block's contents get is the CSS
|
|
9795
|
-
// layer's own business. The deferring path offers it at the attribute
|
|
9796
|
-
// instead, where the shape an answer has to land in is known.
|
|
9797
|
-
if (
|
|
9798
|
-
_renderEmbeddedSource !== undefined &&
|
|
9799
|
-
_deferEmbeddedSource === undefined
|
|
9800
|
-
) {
|
|
9801
|
-
const rendered = _renderEmbeddedOrNull(raw, CSS_TYPE, BLOCK_CONTENTS);
|
|
9802
|
-
|
|
9803
|
-
if (rendered !== null) return rendered;
|
|
9804
|
-
}
|
|
9805
|
-
|
|
9806
|
-
const out = _builtinStyleBody(raw, BLOCK_CONTENTS);
|
|
9807
|
-
|
|
9808
|
-
return out === null ? raw : out;
|
|
9964
|
+
if (_deferEmbeddedSource !== undefined || raw.trim() === "") return raw;
|
|
9965
|
+
const rendered = _renderEmbeddedOrNull(raw, CSS_TYPE, BLOCK_CONTENTS);
|
|
9966
|
+
return rendered === null ? raw : rendered;
|
|
9809
9967
|
};
|
|
9810
9968
|
|
|
9811
9969
|
/**
|
|
@@ -9888,10 +10046,11 @@ const _isRedundantAttribute = (markers, defaults, tagName, name, value) => {
|
|
|
9888
10046
|
// `<script type>`: only a spelling that names JavaScript, which is what the
|
|
9889
10047
|
// element already runs. `module` and the empty spelling say something.
|
|
9890
10048
|
if (tagName === "script" && name === "type") {
|
|
10049
|
+
// Read the way `_scriptType` reads it, or the two disagree about which
|
|
10050
|
+
// `<script>` this is and the body's handler and this one part ways.
|
|
10051
|
+
const type = _asciiLowerCase(_asciiTrim(decodeEntities(value, true)));
|
|
9891
10052
|
return (
|
|
9892
|
-
|
|
9893
|
-
lowered !== "module" &&
|
|
9894
|
-
JAVASCRIPT_SCRIPT_TYPES.has(lowered)
|
|
10053
|
+
type !== "" && type !== "module" && JAVASCRIPT_SCRIPT_TYPES.has(type)
|
|
9895
10054
|
);
|
|
9896
10055
|
}
|
|
9897
10056
|
if (markers !== undefined && markers[name] === lowered) return true;
|
|
@@ -9993,6 +10152,20 @@ const _isBooleanAttribute = (tagName, name) => {
|
|
|
9993
10152
|
return elements === null || elements.has(tagName);
|
|
9994
10153
|
};
|
|
9995
10154
|
|
|
10155
|
+
/**
|
|
10156
|
+
* Whether an attribute is an event handler on this element, so its value is
|
|
10157
|
+
* JavaScript rather than text. `null` in the table means the attribute is a
|
|
10158
|
+
* handler on every element.
|
|
10159
|
+
* @param {string} tagName lowercased element name
|
|
10160
|
+
* @param {string} name lowercased attribute name
|
|
10161
|
+
* @returns {boolean} true when the value is a handler body
|
|
10162
|
+
*/
|
|
10163
|
+
const _isEventHandlerAttribute = (tagName, name) => {
|
|
10164
|
+
const elements = EVENT_HANDLER_ATTRIBUTES.get(name);
|
|
10165
|
+
if (elements === undefined) return false;
|
|
10166
|
+
return elements === null || elements.has(tagName);
|
|
10167
|
+
};
|
|
10168
|
+
|
|
9996
10169
|
/**
|
|
9997
10170
|
* Whether a space-separated list names the same token twice, read span by span
|
|
9998
10171
|
* so nothing is allocated. The lists are short — `class` is a handful of tokens
|
|
@@ -10026,17 +10199,33 @@ const _repeatsAToken = (list) => {
|
|
|
10026
10199
|
return false;
|
|
10027
10200
|
};
|
|
10028
10201
|
|
|
10202
|
+
/**
|
|
10203
|
+
* Whether every token of a collapsed list is a word, which is what makes the
|
|
10204
|
+
* list the set the DOM reflects rather than another language's text.
|
|
10205
|
+
* @param {string} list a collapsed, trimmed token list
|
|
10206
|
+
* @returns {boolean} true when no token holds a foreign delimiter
|
|
10207
|
+
*/
|
|
10208
|
+
const _tokensAreWords = (list) => {
|
|
10209
|
+
const length = list.length;
|
|
10210
|
+
for (let start = 0; start < length;) {
|
|
10211
|
+
let end = list.indexOf(" ", start);
|
|
10212
|
+
if (end === -1) end = length;
|
|
10213
|
+
if (!_isWordSpan(list, start, end)) return false;
|
|
10214
|
+
start = end + 1;
|
|
10215
|
+
}
|
|
10216
|
+
return true;
|
|
10217
|
+
};
|
|
10218
|
+
|
|
10029
10219
|
/**
|
|
10030
10220
|
* Collapse an ASCII-whitespace-separated token list (`class`) to single spaces,
|
|
10031
|
-
* and — where
|
|
10032
|
-
*
|
|
10033
|
-
* kept either way: the first spelling of each token stays put.
|
|
10221
|
+
* and — where every token is a word, so the list is the ordered *set* the DOM
|
|
10222
|
+
* reflects — drop a token already in it. Order is kept unless `sort` asks.
|
|
10034
10223
|
* @param {string} raw raw (undecoded) value
|
|
10035
|
-
* @param {boolean}
|
|
10224
|
+
* @param {boolean} isSet whether the DOM reflects the list as a `DOMTokenList`
|
|
10036
10225
|
* @param {boolean} sort whether to print the tokens in order (`sortTokenLists`)
|
|
10037
10226
|
* @returns {string} the collapsed list
|
|
10038
10227
|
*/
|
|
10039
|
-
const _normalizeTokenList = (raw,
|
|
10228
|
+
const _normalizeTokenList = (raw, isSet, sort) => {
|
|
10040
10229
|
// One walk answers "already collapsed?" — the common already-minified list
|
|
10041
10230
|
// rewrites nothing, so it must not pay the trim + regex replace either.
|
|
10042
10231
|
let dirty = false;
|
|
@@ -10061,12 +10250,16 @@ const _normalizeTokenList = (raw, unique, sort) => {
|
|
|
10061
10250
|
if (!dirty && previousSpace && raw.length !== 0) dirty = true;
|
|
10062
10251
|
const collapsed = dirty ? _asciiTrim(raw).replace(/[\t\n\f\r ]+/g, " ") : raw;
|
|
10063
10252
|
if (dirty ? !collapsed.includes(" ") : !hasSpace) return collapsed;
|
|
10253
|
+
if (!isSet) return collapsed;
|
|
10064
10254
|
// Cutting the list into tokens is the allocation this runs often enough to
|
|
10065
|
-
// care about, and almost no list repeats one — so the
|
|
10066
|
-
// in place and only a list that
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10255
|
+
// care about, and almost no list repeats one — so the checks read the spans
|
|
10256
|
+
// in place and only a list that folds pays for the pieces.
|
|
10257
|
+
if (sort) {
|
|
10258
|
+
return _tokensAreWords(collapsed) ? _sortedTokenList(collapsed) : collapsed;
|
|
10259
|
+
}
|
|
10260
|
+
if (!_repeatsAToken(collapsed) || !_tokensAreWords(collapsed)) {
|
|
10261
|
+
return collapsed;
|
|
10262
|
+
}
|
|
10070
10263
|
return [...new Set(collapsed.split(" "))].join(" ");
|
|
10071
10264
|
};
|
|
10072
10265
|
|
|
@@ -10097,13 +10290,12 @@ const _compareTokens = (list, aStart, aEnd, bStart, bEnd) => {
|
|
|
10097
10290
|
};
|
|
10098
10291
|
|
|
10099
10292
|
/**
|
|
10100
|
-
* `collapsed`'s tokens in sorted order,
|
|
10101
|
-
*
|
|
10293
|
+
* `collapsed`'s tokens in sorted order, with a repeat dropped. They are ordered
|
|
10294
|
+
* where they sit, so the pieces `split` would build are never built.
|
|
10102
10295
|
* @param {string} collapsed a single-space-separated list
|
|
10103
|
-
* @
|
|
10104
|
-
* @returns {string} the ordered list
|
|
10296
|
+
* @returns {string} the ordered set
|
|
10105
10297
|
*/
|
|
10106
|
-
const _sortedTokenList = (collapsed
|
|
10298
|
+
const _sortedTokenList = (collapsed) => {
|
|
10107
10299
|
const starts = _tokenSpanStarts;
|
|
10108
10300
|
const ends = _tokenSpanEnds;
|
|
10109
10301
|
let count = 0;
|
|
@@ -10135,7 +10327,6 @@ const _sortedTokenList = (collapsed, unique) => {
|
|
|
10135
10327
|
let out = "";
|
|
10136
10328
|
for (let i = 0; i < count; i++) {
|
|
10137
10329
|
if (
|
|
10138
|
-
unique &&
|
|
10139
10330
|
i !== 0 &&
|
|
10140
10331
|
_compareTokens(
|
|
10141
10332
|
collapsed,
|
|
@@ -10227,16 +10418,20 @@ const _rewriteAttributeValue = (element, name, raw, viewport, enumeratedOn) => {
|
|
|
10227
10418
|
return _transforms.normalizeListAttributes ? _normalizeSrcset(raw) : raw;
|
|
10228
10419
|
}
|
|
10229
10420
|
if (name === "style") return _minifyStyleAttribute(raw);
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
|
|
10421
|
+
// A list `sortTokenLists` reorders is read whatever `normalizeListAttributes`
|
|
10422
|
+
// says: another order rewrites the separators between the tokens anyway.
|
|
10423
|
+
const tokenListOn =
|
|
10424
|
+
_transforms.normalizeListAttributes ||
|
|
10425
|
+
(_sortTokenLists && DOM_TOKEN_LIST_ATTRIBUTES.has(name))
|
|
10426
|
+
? TOKEN_LIST_ATTRIBUTES.get(name)
|
|
10427
|
+
: undefined;
|
|
10233
10428
|
if (
|
|
10234
10429
|
tokenListOn !== undefined &&
|
|
10235
10430
|
(tokenListOn === null || tokenListOn.has(element))
|
|
10236
10431
|
) {
|
|
10237
10432
|
// Only the lists the DOM reflects as a `DOMTokenList` are sets, so only
|
|
10238
|
-
// they may be reordered — `ping` is the order
|
|
10239
|
-
// `accesskey` the order its keys are tried.
|
|
10433
|
+
// they may be reordered or have a repeat folded away — `ping` is the order
|
|
10434
|
+
// its requests go out in and `accesskey` the order its keys are tried.
|
|
10240
10435
|
const isSet = DOM_TOKEN_LIST_ATTRIBUTES.has(name);
|
|
10241
10436
|
return _normalizeTokenList(raw, isSet, _sortTokenLists && isSet);
|
|
10242
10437
|
}
|
|
@@ -10244,7 +10439,9 @@ const _rewriteAttributeValue = (element, name, raw, viewport, enumeratedOn) => {
|
|
|
10244
10439
|
if (viewport && name === "content") return _normalizeViewport(raw);
|
|
10245
10440
|
if (COMMA_LIST_ATTRIBUTES.has(name)) return _normalizeCommaList(raw);
|
|
10246
10441
|
}
|
|
10247
|
-
const urlOn =
|
|
10442
|
+
const urlOn = _transforms.normalizeUrlAttributes
|
|
10443
|
+
? URL_ATTRIBUTES.get(name)
|
|
10444
|
+
: undefined;
|
|
10248
10445
|
if (urlOn !== undefined && (urlOn === null || urlOn.has(element))) {
|
|
10249
10446
|
return _asciiTrim(raw);
|
|
10250
10447
|
}
|
|
@@ -10269,7 +10466,9 @@ const _rewriteAttributeValue = (element, name, raw, viewport, enumeratedOn) => {
|
|
|
10269
10466
|
const _scriptType = (path, element) => {
|
|
10270
10467
|
for (const attribute of path.attributes(element)) {
|
|
10271
10468
|
if (attribute.name === "type") {
|
|
10272
|
-
|
|
10469
|
+
// The value the spec matches is the one the parser read, so a reference
|
|
10470
|
+
// is decoded first: `text/javascript` runs, it is not a data block.
|
|
10471
|
+
return _asciiLowerCase(_asciiTrim(decodeEntities(attribute.value, true)));
|
|
10273
10472
|
}
|
|
10274
10473
|
}
|
|
10275
10474
|
return "";
|
|
@@ -10294,64 +10493,13 @@ const _JSON_SUBTYPE_REGEXP =
|
|
|
10294
10493
|
/^[!#$%&'*+.^_`|~\w-]+\/[!#$%&'*+.^_`|~\w-]*\+json$/;
|
|
10295
10494
|
|
|
10296
10495
|
/**
|
|
10297
|
-
*
|
|
10298
|
-
*
|
|
10299
|
-
* through a double, drop a duplicate key and rewrite escapes, none of which is
|
|
10300
|
-
* this transform's to do. Escapes are also the security case: unescaping a
|
|
10301
|
-
* `<` lets a string close the `<script>` early.
|
|
10496
|
+
* Offer a JSON `<script>` body to the renderer, blank text kept as written;
|
|
10497
|
+
* `stripJsonWhitespace` is what webpack's own answers with.
|
|
10302
10498
|
* @param {string} json a `<script>` body
|
|
10303
|
-
* @returns {string} the
|
|
10499
|
+
* @returns {string} the rendered body, the marker standing for it, or the body as written
|
|
10304
10500
|
*/
|
|
10305
|
-
const _minifyInlineJson = (json) =>
|
|
10306
|
-
|
|
10307
|
-
if (json.trim() === "") return json;
|
|
10308
|
-
if (_deferEmbeddedSource !== undefined) {
|
|
10309
|
-
return _deferEmbedded(json, JSON_TYPE, (rendered) =>
|
|
10310
|
-
typeof rendered === "string" ? rendered : _builtinInlineJson(json)
|
|
10311
|
-
);
|
|
10312
|
-
}
|
|
10313
|
-
if (_renderEmbeddedSource !== undefined) {
|
|
10314
|
-
const rendered = _renderEmbeddedOrNull(json, JSON_TYPE);
|
|
10315
|
-
if (rendered !== null) return rendered;
|
|
10316
|
-
}
|
|
10317
|
-
return _builtinInlineJson(json);
|
|
10318
|
-
};
|
|
10319
|
-
|
|
10320
|
-
/**
|
|
10321
|
-
* The JSON strip webpack ships, for a body no caller's renderer answered for.
|
|
10322
|
-
* @param {string} json a `<script>` body
|
|
10323
|
-
* @returns {string} the stripped body
|
|
10324
|
-
*/
|
|
10325
|
-
const _builtinInlineJson = (json) => {
|
|
10326
|
-
try {
|
|
10327
|
-
JSON.parse(json);
|
|
10328
|
-
} catch (_err) {
|
|
10329
|
-
// Not JSON after all (a template, a placeholder) — not ours to touch.
|
|
10330
|
-
return json;
|
|
10331
|
-
}
|
|
10332
|
-
let out = "";
|
|
10333
|
-
let inString = false;
|
|
10334
|
-
let escaped = false;
|
|
10335
|
-
for (let i = 0; i < json.length; i++) {
|
|
10336
|
-
const c = json.charCodeAt(i);
|
|
10337
|
-
if (inString) {
|
|
10338
|
-
out += json[i];
|
|
10339
|
-
if (escaped) escaped = false;
|
|
10340
|
-
else if (c === 0x5c) escaped = true;
|
|
10341
|
-
else if (c === 0x22) inString = false;
|
|
10342
|
-
continue;
|
|
10343
|
-
}
|
|
10344
|
-
if (c === 0x22) {
|
|
10345
|
-
inString = true;
|
|
10346
|
-
out += json[i];
|
|
10347
|
-
continue;
|
|
10348
|
-
}
|
|
10349
|
-
// JSON whitespace (RFC 8259): tab, LF, CR, space.
|
|
10350
|
-
if (c === 0x09 || c === 0x0a || c === 0x0d || c === 0x20) continue;
|
|
10351
|
-
out += json[i];
|
|
10352
|
-
}
|
|
10353
|
-
return out;
|
|
10354
|
-
};
|
|
10501
|
+
const _minifyInlineJson = (json) =>
|
|
10502
|
+
json.trim() === "" ? json : _renderEmbedded(json, JSON_TYPE);
|
|
10355
10503
|
|
|
10356
10504
|
/**
|
|
10357
10505
|
* Whether a `<style>` element's body is CSS: the attribute is optional, and the
|
|
@@ -10374,12 +10522,12 @@ const _isCssStyleElement = (path, element) => {
|
|
|
10374
10522
|
const _SHEET_LEADING_AT_RULE_REGEXP = /@(?:charset|import|namespace)\b/i;
|
|
10375
10523
|
|
|
10376
10524
|
/**
|
|
10377
|
-
* A
|
|
10378
|
-
*
|
|
10379
|
-
* @param {HtmlElement} element
|
|
10525
|
+
* A literal-text element's only child when that is one text node — the shape a
|
|
10526
|
+
* merge needs, since anything else means the element is empty.
|
|
10527
|
+
* @param {HtmlElement} element a `<style>` or `<script>`
|
|
10380
10528
|
* @returns {HtmlNodeRef} the text node, or 0
|
|
10381
10529
|
*/
|
|
10382
|
-
const
|
|
10530
|
+
const _onlyTextChild = (element) => {
|
|
10383
10531
|
const first = _nodeFirstChildren[element];
|
|
10384
10532
|
return first !== 0 &&
|
|
10385
10533
|
_nodeTypes[first] === NodeType.Text &&
|
|
@@ -10437,7 +10585,7 @@ const _mergeableStyleAfter = (path, element) => {
|
|
|
10437
10585
|
return type === NodeType.Element &&
|
|
10438
10586
|
path.namespace(next) === NS_HTML &&
|
|
10439
10587
|
path.tagName(next) === "style" &&
|
|
10440
|
-
|
|
10588
|
+
_onlyTextChild(next) !== 0 &&
|
|
10441
10589
|
_isCssStyleElement(path, next) &&
|
|
10442
10590
|
_sameAttributes(path, element, next)
|
|
10443
10591
|
? next
|
|
@@ -10447,43 +10595,265 @@ const _mergeableStyleAfter = (path, element) => {
|
|
|
10447
10595
|
};
|
|
10448
10596
|
|
|
10449
10597
|
/**
|
|
10450
|
-
*
|
|
10451
|
-
*
|
|
10452
|
-
*
|
|
10453
|
-
*
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
|
|
10457
|
-
|
|
10458
|
-
|
|
10459
|
-
|
|
10598
|
+
* Whether a sheet ends outside every string, comment and bracket, so a sheet
|
|
10599
|
+
* appended to it starts rules of its own rather than finishing this one's last.
|
|
10600
|
+
* @param {string} css the sheet's text
|
|
10601
|
+
* @returns {boolean} true when another sheet may follow it
|
|
10602
|
+
*/
|
|
10603
|
+
const _sheetEndsCleanly = (css) => {
|
|
10604
|
+
let depth = 0;
|
|
10605
|
+
for (let i = 0; i < css.length; i++) {
|
|
10606
|
+
const c = css.charCodeAt(i);
|
|
10607
|
+
if (c === CC_REVERSE_SOLIDUS) {
|
|
10608
|
+
i++;
|
|
10609
|
+
} else if (c === CC_SOLIDUS && css.charCodeAt(i + 1) === CC_ASTERISK) {
|
|
10610
|
+
const close = css.indexOf("*/", i + 2);
|
|
10611
|
+
if (close === -1) return false;
|
|
10612
|
+
i = close + 1;
|
|
10613
|
+
} else if (c === CC_QUOTATION_MARK || c === CC_APOSTROPHE) {
|
|
10614
|
+
// A string ends at its quote, or at a newline as a bad string does.
|
|
10615
|
+
for (i++; i < css.length; i++) {
|
|
10616
|
+
const d = css.charCodeAt(i);
|
|
10617
|
+
if (d === CC_REVERSE_SOLIDUS) i++;
|
|
10618
|
+
else if (d === c || d === CC_LF) break;
|
|
10619
|
+
}
|
|
10620
|
+
if (i >= css.length) return false;
|
|
10621
|
+
} else if (
|
|
10622
|
+
c === CC_LEFT_CURLY_BRACKET ||
|
|
10623
|
+
c === CC_LEFT_PARENTHESIS ||
|
|
10624
|
+
c === CC_LEFT_SQUARE_BRACKET
|
|
10625
|
+
) {
|
|
10626
|
+
depth++;
|
|
10627
|
+
} else if (
|
|
10628
|
+
depth !== 0 &&
|
|
10629
|
+
(c === CC_RIGHT_CURLY_BRACKET ||
|
|
10630
|
+
c === CC_RIGHT_PARENTHESIS ||
|
|
10631
|
+
c === CC_RIGHT_SQUARE_BRACKET)
|
|
10632
|
+
) {
|
|
10633
|
+
depth--;
|
|
10634
|
+
}
|
|
10635
|
+
}
|
|
10636
|
+
return depth === 0;
|
|
10637
|
+
};
|
|
10638
|
+
|
|
10639
|
+
/**
|
|
10640
|
+
* The text the first `<style>` of a run prints: every sheet in it, each offered
|
|
10641
|
+
* on its own and then joined, the rest of the run marked absorbed so their tags
|
|
10642
|
+
* and text print as nothing. Whether a sheet joins is read off its source, since
|
|
10643
|
+
* an answer may be a marker: one not ending cleanly would swallow the next, and
|
|
10644
|
+
* one after the first carrying `@import` (or `@charset` / `@namespace`) would
|
|
10645
|
+
* silently stop applying those.
|
|
10460
10646
|
* @param {HtmlPath} path the accessor
|
|
10461
10647
|
* @param {HtmlElement} element the run's first `<style>`
|
|
10462
10648
|
* @param {string} css its own text
|
|
10463
10649
|
* @returns {string} the run's text
|
|
10464
10650
|
*/
|
|
10465
10651
|
const _mergedStyleRun = (path, element, css) => {
|
|
10466
|
-
|
|
10467
|
-
if (
|
|
10468
|
-
let out = head;
|
|
10652
|
+
let out = _minifyStyleBody(css);
|
|
10653
|
+
if (!_sheetEndsCleanly(css)) return out;
|
|
10469
10654
|
for (
|
|
10470
10655
|
let next = _mergeableStyleAfter(path, element);
|
|
10471
10656
|
next !== 0;
|
|
10472
10657
|
next = _mergeableStyleAfter(path, next)
|
|
10473
10658
|
) {
|
|
10474
|
-
const
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
if (
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10659
|
+
const text =
|
|
10660
|
+
_nodeStrings[/** @type {HtmlNodeRef} */ (_onlyTextChild(next))];
|
|
10661
|
+
if (!_sheetEndsCleanly(text) || _SHEET_LEADING_AT_RULE_REGEXP.test(text)) {
|
|
10662
|
+
break;
|
|
10663
|
+
}
|
|
10664
|
+
if (_absorbed === null) _absorbed = new Set();
|
|
10665
|
+
_absorbed.add(next);
|
|
10666
|
+
out += _minifyStyleBody(text);
|
|
10667
|
+
}
|
|
10668
|
+
return out;
|
|
10669
|
+
};
|
|
10670
|
+
|
|
10671
|
+
/**
|
|
10672
|
+
* Whether a script body ends where another can be appended after it. A body
|
|
10673
|
+
* still inside a string, template or block comment would swallow the next one;
|
|
10674
|
+
* a trailing line comment is fine because the join writes a newline first. The
|
|
10675
|
+
* body is read rather than parsed, so a regex literal can be taken for a string
|
|
10676
|
+
* or a comment — the answer holds for a body that parses, and one that does not
|
|
10677
|
+
* takes its whole run with it however this reads.
|
|
10678
|
+
* @param {string} js the script's text
|
|
10679
|
+
* @returns {boolean} true when nothing is left open
|
|
10680
|
+
*/
|
|
10681
|
+
const _scriptEndsClean = (js) => {
|
|
10682
|
+
let state = 0; // 0 none, 1 line comment, 2 block comment, 3 ' 4 " 5 `
|
|
10683
|
+
// Read by code unit: a body is scanned whole, and `js[i]` would allocate a
|
|
10684
|
+
// one-character string at every position of it.
|
|
10685
|
+
for (let i = 0; i < js.length; i++) {
|
|
10686
|
+
const c = js.charCodeAt(i);
|
|
10687
|
+
if (state === 0) {
|
|
10688
|
+
if (c === 47 /* / */) {
|
|
10689
|
+
const next = js.charCodeAt(i + 1);
|
|
10690
|
+
if (next === 47) {
|
|
10691
|
+
state = 1;
|
|
10692
|
+
i++;
|
|
10693
|
+
} else if (next === 42 /* * */) {
|
|
10694
|
+
state = 2;
|
|
10695
|
+
i++;
|
|
10696
|
+
}
|
|
10697
|
+
} else if (c === 39 /* ' */) {
|
|
10698
|
+
state = 3;
|
|
10699
|
+
} else if (c === 34 /* " */) {
|
|
10700
|
+
state = 4;
|
|
10701
|
+
} else if (c === 96 /* ` */) {
|
|
10702
|
+
state = 5;
|
|
10703
|
+
}
|
|
10704
|
+
} else if (state === 1) {
|
|
10705
|
+
// Every line terminator ends a line comment, not just the two HTML
|
|
10706
|
+
// normalizes text to — a `"` past one really does open a string.
|
|
10707
|
+
if (c === 10 || c === 13 || c === 0x2028 || c === 0x2029) state = 0;
|
|
10708
|
+
} else if (state === 2) {
|
|
10709
|
+
if (c === 42 && js.charCodeAt(i + 1) === 47) {
|
|
10710
|
+
state = 0;
|
|
10711
|
+
i++;
|
|
10712
|
+
}
|
|
10713
|
+
} else if (c === 92 /* \\ */) {
|
|
10714
|
+
i++;
|
|
10715
|
+
} else if (
|
|
10716
|
+
(state === 3 && c === 39) ||
|
|
10717
|
+
(state === 4 && c === 34) ||
|
|
10718
|
+
(state === 5 && c === 96)
|
|
10719
|
+
) {
|
|
10720
|
+
state = 0;
|
|
10721
|
+
}
|
|
10722
|
+
}
|
|
10723
|
+
return state === 0 || state === 1;
|
|
10724
|
+
};
|
|
10725
|
+
|
|
10726
|
+
// ECMAScript's own whitespace and line terminators, which are wider than the
|
|
10727
|
+
// HTML ones: a BOM, a form feed and U+2028 all sit before a directive.
|
|
10728
|
+
const JS_WHITESPACE_REGEXP = /\s/;
|
|
10729
|
+
const JS_LINE_TERMINATOR_REGEXP = /[\n\r\u2028\u2029]/;
|
|
10730
|
+
const BOM_REGEXP = /^\uFEFF/;
|
|
10731
|
+
|
|
10732
|
+
/**
|
|
10733
|
+
* Whether a script body opens with what only a script's own start carries: a
|
|
10734
|
+
* hashbang, or a directive prologue — the leading string `"use strict"` is one.
|
|
10735
|
+
* Appended after another body neither means that any more.
|
|
10736
|
+
* @param {string} js the script's text
|
|
10737
|
+
* @returns {boolean} true when the body must start a script of its own
|
|
10738
|
+
*/
|
|
10739
|
+
const _opensAScript = (js) => {
|
|
10740
|
+
// A hashbang is only one at the very start, a BOM aside.
|
|
10741
|
+
if (js.replace(BOM_REGEXP, "").startsWith("#!")) return true;
|
|
10742
|
+
let i = 0;
|
|
10743
|
+
while (i < js.length) {
|
|
10744
|
+
const c = js[i];
|
|
10745
|
+
if (JS_WHITESPACE_REGEXP.test(c)) {
|
|
10746
|
+
i++;
|
|
10747
|
+
} else if (c === "/" && js[i + 1] === "/") {
|
|
10748
|
+
i += 2;
|
|
10749
|
+
while (i < js.length && !JS_LINE_TERMINATOR_REGEXP.test(js[i])) i++;
|
|
10750
|
+
} else if (c === "/" && js[i + 1] === "*") {
|
|
10751
|
+
const end = js.indexOf("*/", i + 2);
|
|
10752
|
+
if (end === -1) return true;
|
|
10753
|
+
i = end + 2;
|
|
10754
|
+
} else {
|
|
10755
|
+
break;
|
|
10756
|
+
}
|
|
10757
|
+
}
|
|
10758
|
+
return js[i] === '"' || js[i] === "'";
|
|
10759
|
+
};
|
|
10760
|
+
|
|
10761
|
+
/**
|
|
10762
|
+
* Whether a body opens with `-->` still on its first line. That is a single
|
|
10763
|
+
* line comment only where nothing but whitespace and same-line comments comes
|
|
10764
|
+
* before it on the line, which an appended body no longer begins.
|
|
10765
|
+
* @param {string} js the script's text
|
|
10766
|
+
* @returns {boolean} true when appending the body would change what `-->` means
|
|
10767
|
+
*/
|
|
10768
|
+
const _opensWithHtmlCloseComment = (js) => {
|
|
10769
|
+
let i = 0;
|
|
10770
|
+
while (i < js.length) {
|
|
10771
|
+
const c = js[i];
|
|
10772
|
+
if (JS_LINE_TERMINATOR_REGEXP.test(c)) return false;
|
|
10773
|
+
if (JS_WHITESPACE_REGEXP.test(c)) {
|
|
10774
|
+
i++;
|
|
10775
|
+
} else if (c === "/" && js[i + 1] === "*") {
|
|
10776
|
+
const end = js.indexOf("*/", i + 2);
|
|
10777
|
+
// One spanning a line terminator ends the line it opened, so what
|
|
10778
|
+
// follows starts a line of its own.
|
|
10779
|
+
if (end === -1 || JS_LINE_TERMINATOR_REGEXP.test(js.slice(i + 2, end))) {
|
|
10780
|
+
return false;
|
|
10781
|
+
}
|
|
10782
|
+
i = end + 2;
|
|
10783
|
+
} else {
|
|
10784
|
+
return js.startsWith("-->", i);
|
|
10785
|
+
}
|
|
10786
|
+
}
|
|
10787
|
+
return false;
|
|
10788
|
+
};
|
|
10789
|
+
|
|
10790
|
+
/**
|
|
10791
|
+
* The `<script>` that may be folded into `element`: the next sibling, when only
|
|
10792
|
+
* whitespace lies between them and neither carries a single attribute. Bare is
|
|
10793
|
+
* the whole test — a `src`, `type`, `nonce`, `async` or `id` all say the two
|
|
10794
|
+
* elements are not interchangeable with one.
|
|
10795
|
+
* @param {HtmlPath} path the accessor
|
|
10796
|
+
* @param {HtmlElement} element a `<script>` element
|
|
10797
|
+
* @returns {HtmlElement} the next `<script>` of the run, or 0
|
|
10798
|
+
*/
|
|
10799
|
+
const _mergeableScriptAfter = (path, element) => {
|
|
10800
|
+
if (path.attributeCount(element) !== 0) return 0;
|
|
10801
|
+
for (
|
|
10802
|
+
let next = _nodeNextSiblings[element];
|
|
10803
|
+
next !== 0;
|
|
10804
|
+
next = _nodeNextSiblings[next]
|
|
10805
|
+
) {
|
|
10806
|
+
const type = _nodeTypes[next];
|
|
10807
|
+
if (type === NodeType.Text) {
|
|
10808
|
+
if (!isAllWs(_nodeStrings[next])) return 0;
|
|
10809
|
+
continue;
|
|
10810
|
+
}
|
|
10811
|
+
return type === NodeType.Element &&
|
|
10812
|
+
path.namespace(next) === NS_HTML &&
|
|
10813
|
+
path.tagName(next) === "script" &&
|
|
10814
|
+
path.attributeCount(next) === 0 &&
|
|
10815
|
+
_onlyTextChild(next) !== 0
|
|
10816
|
+
? next
|
|
10817
|
+
: 0;
|
|
10818
|
+
}
|
|
10819
|
+
return 0;
|
|
10820
|
+
};
|
|
10821
|
+
|
|
10822
|
+
/**
|
|
10823
|
+
* The text the first `<script>` of a run prints: every body in it, joined by a
|
|
10824
|
+
* newline and a `;`. The newline ends a trailing line comment and the `;` keeps
|
|
10825
|
+
* the next body from continuing the last statement — `var a = 1` followed by
|
|
10826
|
+
* `(f)()` is one call without it.
|
|
10827
|
+
* @param {HtmlPath} path the accessor
|
|
10828
|
+
* @param {HtmlElement} element the run's first `<script>`
|
|
10829
|
+
* @param {string} js its own text
|
|
10830
|
+
* @returns {string} the run's text
|
|
10831
|
+
*/
|
|
10832
|
+
const _mergedScriptRun = (path, element, js) => {
|
|
10833
|
+
// A deferred body is a stand-in the caller swaps out after the print, so the
|
|
10834
|
+
// text read here is neither the code that runs nor what a guard can judge.
|
|
10835
|
+
if (_deferEmbeddedSource !== undefined) return js;
|
|
10836
|
+
// Asked before the body is scanned: a lone `<script>` is the common case and
|
|
10837
|
+
// scanning it whole to then find nothing to fold into is the whole cost.
|
|
10838
|
+
const first = _mergeableScriptAfter(path, element);
|
|
10839
|
+
if (first === 0) return js;
|
|
10840
|
+
// A prologue reaches whatever is appended after it, so a strict first body
|
|
10841
|
+
// would put the rest of the run in strict mode too.
|
|
10842
|
+
if (!_scriptEndsClean(js) || _opensAScript(js)) return js;
|
|
10843
|
+
let out = js;
|
|
10844
|
+
for (let next = first; next !== 0; next = _mergeableScriptAfter(path, next)) {
|
|
10845
|
+
const body =
|
|
10846
|
+
_nodeStrings[/** @type {HtmlNodeRef} */ (_onlyTextChild(next))];
|
|
10847
|
+
if (
|
|
10848
|
+
!_scriptEndsClean(body) ||
|
|
10849
|
+
_opensAScript(body) ||
|
|
10850
|
+
_opensWithHtmlCloseComment(body)
|
|
10851
|
+
) {
|
|
10852
|
+
break;
|
|
10853
|
+
}
|
|
10854
|
+
if (_absorbed === null) _absorbed = new Set();
|
|
10855
|
+
_absorbed.add(next);
|
|
10856
|
+
out += `\n;${body}`;
|
|
10487
10857
|
}
|
|
10488
10858
|
return out;
|
|
10489
10859
|
};
|
|
@@ -10687,6 +11057,15 @@ const _minifyOpenTag = (path, open) => {
|
|
|
10687
11057
|
// content keeps its source bytes: SVG admits element names no adjustment table
|
|
10688
11058
|
// would case-correct on the way back in.
|
|
10689
11059
|
const html = path.namespace() === NS_HTML;
|
|
11060
|
+
// Rebuilding a tag another language wrote would print the spec's duplicate
|
|
11061
|
+
// drop into the output, which a re-parse of the source makes again anyway.
|
|
11062
|
+
if (
|
|
11063
|
+
count !== 0 &&
|
|
11064
|
+
_foreignSyntaxAttrRuns.size !== 0 &&
|
|
11065
|
+
_foreignSyntaxAttrRuns.has(path.attributeAt(0))
|
|
11066
|
+
) {
|
|
11067
|
+
return open;
|
|
11068
|
+
}
|
|
10690
11069
|
const nameEnd = path.nameEnd() - tagStart;
|
|
10691
11070
|
// A bare `<name>` — half of all tags — is what the rest would rebuild byte
|
|
10692
11071
|
// for byte; decided before the name is sliced, so it allocates nothing.
|
|
@@ -10709,6 +11088,9 @@ const _minifyOpenTag = (path, open) => {
|
|
|
10709
11088
|
const isViewportMeta =
|
|
10710
11089
|
html && path.tagName() === "meta" && _isViewportMeta(path);
|
|
10711
11090
|
let unquotedTail = false;
|
|
11091
|
+
// Only a foreign element's surviving `/>` reads it, so only there is a
|
|
11092
|
+
// deferred answer held to the shape the built-in was written in.
|
|
11093
|
+
const tailIsRead = !html && open.charCodeAt(open.length - 2) === 47;
|
|
10712
11094
|
let consumedEnd = path.nameEnd();
|
|
10713
11095
|
// HTML only, which is also what keeps `consumedEnd` meaningful: it is read
|
|
10714
11096
|
// once more below, and only for a foreign element's `/>`.
|
|
@@ -10775,53 +11157,53 @@ const _minifyOpenTag = (path, open) => {
|
|
|
10775
11157
|
consumedEnd = valueEnd;
|
|
10776
11158
|
continue;
|
|
10777
11159
|
}
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
10781
|
-
|
|
10782
|
-
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
|
|
10786
|
-
|
|
10787
|
-
|
|
10788
|
-
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
|
|
10792
|
-
|
|
10793
|
-
|
|
10794
|
-
|
|
10795
|
-
|
|
10796
|
-
|
|
10797
|
-
|
|
10798
|
-
|
|
10799
|
-
|
|
10800
|
-
|
|
10801
|
-
|
|
10802
|
-
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
typeof rendered !== "string" || rendered === document
|
|
10807
|
-
? asWritten
|
|
10808
|
-
: ` ${attributeName}="${escapeAttribute(rendered, 34, true)}"`
|
|
10809
|
-
);
|
|
10810
|
-
unquotedTail = false;
|
|
10811
|
-
consumedEnd = valueEnd;
|
|
10812
|
-
continue;
|
|
10813
|
-
}
|
|
11160
|
+
// A whole document, so it is offered decoded and written back escaped —
|
|
11161
|
+
// the one attribute whose reference-carrying value is still rewritten.
|
|
11162
|
+
if (
|
|
11163
|
+
html &&
|
|
11164
|
+
(_renderEmbeddedSource !== undefined ||
|
|
11165
|
+
(_deferEmbeddedSource !== undefined && _deferSrcdoc)) &&
|
|
11166
|
+
attributeName === "srcdoc" &&
|
|
11167
|
+
elementName === "iframe" &&
|
|
11168
|
+
rawValue !== ""
|
|
11169
|
+
) {
|
|
11170
|
+
const document = decodeEntities(rawValue, true);
|
|
11171
|
+
// The whole attribute is deferred, not the document inside it: what
|
|
11172
|
+
// escaping it needs is decided by what it holds, which is the answer.
|
|
11173
|
+
if (_deferEmbeddedSource !== undefined && _deferSrcdoc) {
|
|
11174
|
+
// Declining leaves the attribute as written, delimiter and all: a bare
|
|
11175
|
+
// value has none, and quoting one would only lengthen it.
|
|
11176
|
+
const delimiter = quoted ? open[valueStart - tagStart - 1] : "";
|
|
11177
|
+
const asWritten = ` ${attributeName}=${delimiter}${rawValue}${delimiter}`;
|
|
11178
|
+
|
|
11179
|
+
out += _deferEmbedded(document, HTML_TYPE, (rendered) =>
|
|
11180
|
+
typeof rendered !== "string" || rendered === document
|
|
11181
|
+
? asWritten
|
|
11182
|
+
: _writeQuotedAttribute(attributeName, rendered, delimiter)
|
|
11183
|
+
);
|
|
11184
|
+
unquotedTail = false;
|
|
11185
|
+
consumedEnd = valueEnd;
|
|
11186
|
+
continue;
|
|
11187
|
+
}
|
|
10814
11188
|
|
|
10815
|
-
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
11189
|
+
// The synchronous renderer: `_renderEmbedded` defers where a collector
|
|
11190
|
+
// is set, and this attribute is the one kept off that path.
|
|
11191
|
+
const rendered = _renderEmbeddedOrNull(document, HTML_TYPE);
|
|
11192
|
+
if (rendered !== null && rendered !== document) {
|
|
11193
|
+
out += _writeQuotedAttribute(
|
|
11194
|
+
attributeName,
|
|
11195
|
+
rendered,
|
|
11196
|
+
quoted ? open[valueStart - tagStart - 1] : ""
|
|
11197
|
+
);
|
|
11198
|
+
unquotedTail = false;
|
|
11199
|
+
consumedEnd = valueEnd;
|
|
11200
|
+
continue;
|
|
10824
11201
|
}
|
|
11202
|
+
}
|
|
11203
|
+
// Where nothing below can change the value: foreign content rewrites only
|
|
11204
|
+
// `style`, and a bare value is already the shortest spelling of itself.
|
|
11205
|
+
if (quoted || html || attributeName === "style" || rawValue.includes("&")) {
|
|
11206
|
+
const hasReference = rawValue.includes("&");
|
|
10825
11207
|
// A character reference would decode to something these grammars read
|
|
10826
11208
|
// differently, so only a reference-free value is rewritten.
|
|
10827
11209
|
let value = rawValue;
|
|
@@ -10851,12 +11233,13 @@ const _minifyOpenTag = (path, open) => {
|
|
|
10851
11233
|
// the switch that names the rest of them.
|
|
10852
11234
|
if (
|
|
10853
11235
|
_transforms.normalizeAttributeQuotes &&
|
|
10854
|
-
(
|
|
11236
|
+
(value === "" ||
|
|
10855
11237
|
(html &&
|
|
10856
11238
|
_transforms.collapseBooleanAttributes &&
|
|
10857
|
-
|
|
10858
|
-
|
|
10859
|
-
|
|
11239
|
+
(_transforms.collapseBooleanAttributes === "all" ||
|
|
11240
|
+
(rawValue.length === attributeName.length &&
|
|
11241
|
+
_asciiLowerCase(rawValue) === attributeName)) &&
|
|
11242
|
+
_isBooleanAttribute(elementName, attributeName)))
|
|
10860
11243
|
) {
|
|
10861
11244
|
out += ` ${attributeName}`;
|
|
10862
11245
|
unquotedTail = false;
|
|
@@ -10885,38 +11268,136 @@ const _minifyOpenTag = (path, open) => {
|
|
|
10885
11268
|
*/
|
|
10886
11269
|
const writeStyle = (minified) =>
|
|
10887
11270
|
declarations !== null
|
|
10888
|
-
? _writeAttribute(attributeName, rawValue, rawValue, minified)
|
|
10889
|
-
: _writeAttribute(
|
|
11271
|
+
? _writeAttribute(attributeName, rawValue, rawValue, minified, quoted)
|
|
11272
|
+
: _writeAttribute(
|
|
11273
|
+
attributeName,
|
|
11274
|
+
minified,
|
|
11275
|
+
rawValue,
|
|
11276
|
+
spellable,
|
|
11277
|
+
quoted
|
|
11278
|
+
);
|
|
10890
11279
|
const written = isStyle
|
|
10891
11280
|
? writeStyle(declarations !== null ? declarations : value)
|
|
10892
|
-
: _writeAttribute(attributeName, value, rawValue, spellable);
|
|
11281
|
+
: _writeAttribute(attributeName, value, rawValue, spellable, quoted);
|
|
11282
|
+
// The JavaScript an event handler holds, offered decoded — `&&`
|
|
11283
|
+
// is an operator to JavaScript rather than an entity. No built-in.
|
|
11284
|
+
const handler =
|
|
11285
|
+
!isStyle &&
|
|
11286
|
+
(_renderEmbeddedSource !== undefined ||
|
|
11287
|
+
_deferEmbeddedSource !== undefined) &&
|
|
11288
|
+
_isEventHandlerAttribute(elementName, attributeName)
|
|
11289
|
+
? hasReference
|
|
11290
|
+
? spellable
|
|
11291
|
+
: rawValue
|
|
11292
|
+
: null;
|
|
11293
|
+
|
|
11294
|
+
if (handler !== null && _asciiTrim(handler) !== "") {
|
|
11295
|
+
const delimiter = quoted ? open[valueStart - tagStart - 1] : "";
|
|
11296
|
+
// A foreign `/>` reads back whether this value ends unquoted, which a
|
|
11297
|
+
// deferred answer is not there to decide: the source's quoting stands.
|
|
11298
|
+
const holdQuoted = tailIsRead && _deferEmbeddedSource !== undefined;
|
|
11299
|
+
// Declining leaves the attribute as an untapped run writes it, which is
|
|
11300
|
+
// the source spelling wherever nothing shorter was found.
|
|
11301
|
+
const asWritten =
|
|
11302
|
+
written === null || holdQuoted
|
|
11303
|
+
? ` ${attributeName}=${delimiter}${rawValue}${delimiter}`
|
|
11304
|
+
: written.text;
|
|
11305
|
+
const declinedTail =
|
|
11306
|
+
!holdQuoted && written !== null && written.unquotedTail;
|
|
11307
|
+
/**
|
|
11308
|
+
* The answer is only written where it spells shorter than the source
|
|
11309
|
+
* does: a minifier ends a statement it was handed without one, so a
|
|
11310
|
+
* handler as short as `onclick="x"` comes back a byte longer.
|
|
11311
|
+
* @param {string | null | undefined} rendered what the renderer answered
|
|
11312
|
+
* @returns {{ text: string, unquotedTail: boolean } | null} what to emit, or null where nothing it answered is written
|
|
11313
|
+
*/
|
|
11314
|
+
const writeHandler = (rendered) => {
|
|
11315
|
+
if (typeof rendered !== "string") return null;
|
|
11316
|
+
|
|
11317
|
+
// The answer is source text, so it is escaped for the delimiter it
|
|
11318
|
+
// lands in — an `&` in it opens a reference where it is not.
|
|
11319
|
+
const answered = holdQuoted
|
|
11320
|
+
? {
|
|
11321
|
+
text: _writeQuotedAttribute(attributeName, rendered, delimiter),
|
|
11322
|
+
unquotedTail: false
|
|
11323
|
+
}
|
|
11324
|
+
: _writeAttribute(
|
|
11325
|
+
attributeName,
|
|
11326
|
+
escapeAttribute(rendered, CC_QUOTATION_MARK, true),
|
|
11327
|
+
rawValue,
|
|
11328
|
+
rendered,
|
|
11329
|
+
quoted
|
|
11330
|
+
);
|
|
11331
|
+
|
|
11332
|
+
return answered !== null && answered.text.length < asWritten.length
|
|
11333
|
+
? answered
|
|
11334
|
+
: null;
|
|
11335
|
+
};
|
|
11336
|
+
|
|
11337
|
+
if (_deferEmbeddedSource !== undefined) {
|
|
11338
|
+
out += _deferEmbedded(
|
|
11339
|
+
handler,
|
|
11340
|
+
JAVASCRIPT_TYPE,
|
|
11341
|
+
(rendered) => {
|
|
11342
|
+
const answered = writeHandler(rendered);
|
|
11343
|
+
|
|
11344
|
+
return answered === null ? asWritten : answered.text;
|
|
11345
|
+
},
|
|
11346
|
+
EVENT_HANDLER
|
|
11347
|
+
);
|
|
11348
|
+
unquotedTail = declinedTail;
|
|
11349
|
+
} else {
|
|
11350
|
+
const answered = writeHandler(
|
|
11351
|
+
_renderEmbeddedOrNull(handler, JAVASCRIPT_TYPE, EVENT_HANDLER)
|
|
11352
|
+
);
|
|
11353
|
+
|
|
11354
|
+
out += answered === null ? asWritten : answered.text;
|
|
11355
|
+
unquotedTail =
|
|
11356
|
+
answered === null ? declinedTail : answered.unquotedTail;
|
|
11357
|
+
}
|
|
11358
|
+
consumedEnd = valueEnd;
|
|
11359
|
+
continue;
|
|
11360
|
+
}
|
|
10893
11361
|
|
|
11362
|
+
// Offered whatever the built-in made of it: a list already at its
|
|
11363
|
+
// shortest is one `_writeAttribute` declines, not one to ask nobody about.
|
|
11364
|
+
if (isStyle && _deferEmbeddedSource !== undefined) {
|
|
11365
|
+
// Declining leaves the attribute as written, delimiter and all — an
|
|
11366
|
+
// answer nothing beats is the renderer keeping what it was given.
|
|
11367
|
+
const delimiter = quoted ? open[valueStart - tagStart - 1] : "";
|
|
11368
|
+
const asWritten = ` ${attributeName}=${delimiter}${rawValue}${delimiter}`;
|
|
11369
|
+
// What stands until an answer arrives, and the tag ending it commits
|
|
11370
|
+
// to: the built-in's text where it beat the source, else the source.
|
|
11371
|
+
const standing = written !== null ? written.text : asWritten;
|
|
11372
|
+
const standingTail = written !== null && written.unquotedTail;
|
|
11373
|
+
|
|
11374
|
+
// The list is deferred, not the shape around it: the tag ending is
|
|
11375
|
+
// read back before an answer arrives, so one changing it is declined.
|
|
11376
|
+
out += _deferEmbedded(
|
|
11377
|
+
// Decoded when the source spelled it with references, since that
|
|
11378
|
+
// is the list itself rather than one attribute's spelling.
|
|
11379
|
+
spellable !== null ? spellable : rawValue,
|
|
11380
|
+
CSS_TYPE,
|
|
11381
|
+
(rendered) => {
|
|
11382
|
+
if (typeof rendered !== "string") return standing;
|
|
11383
|
+
|
|
11384
|
+
const answered = writeStyle(rendered);
|
|
11385
|
+
// Declining ends the value quoted: this is the branch the
|
|
11386
|
+
// source spelled with a delimiter.
|
|
11387
|
+
const tail = answered !== null && answered.unquotedTail;
|
|
11388
|
+
|
|
11389
|
+
if (tailIsRead && tail !== standingTail) return standing;
|
|
11390
|
+
|
|
11391
|
+
return answered !== null ? answered.text : asWritten;
|
|
11392
|
+
},
|
|
11393
|
+
BLOCK_CONTENTS
|
|
11394
|
+
);
|
|
11395
|
+
unquotedTail = standingTail;
|
|
11396
|
+
consumedEnd = valueEnd;
|
|
11397
|
+
continue;
|
|
11398
|
+
}
|
|
10894
11399
|
if (written !== null) {
|
|
10895
|
-
|
|
10896
|
-
// leaves the tag ending unquoted is read back when the tag closes and
|
|
10897
|
-
// cannot wait for an answer, so one that would land in the other shape
|
|
10898
|
-
// is declined and the built-in's text stands.
|
|
10899
|
-
out =
|
|
10900
|
-
isStyle && _deferEmbeddedSource !== undefined
|
|
10901
|
-
? out +
|
|
10902
|
-
_deferEmbedded(
|
|
10903
|
-
// Decoded when the source spelled it with references, since
|
|
10904
|
-
// that is the list itself rather than one attribute's spelling.
|
|
10905
|
-
spellable !== null ? spellable : rawValue,
|
|
10906
|
-
CSS_TYPE,
|
|
10907
|
-
(rendered) => {
|
|
10908
|
-
if (typeof rendered !== "string") return written.text;
|
|
10909
|
-
|
|
10910
|
-
const answered = writeStyle(rendered);
|
|
10911
|
-
|
|
10912
|
-
return answered !== null &&
|
|
10913
|
-
answered.unquotedTail === written.unquotedTail
|
|
10914
|
-
? answered.text
|
|
10915
|
-
: written.text;
|
|
10916
|
-
},
|
|
10917
|
-
BLOCK_CONTENTS
|
|
10918
|
-
)
|
|
10919
|
-
: out + written.text;
|
|
11400
|
+
out += written.text;
|
|
10920
11401
|
unquotedTail = written.unquotedTail;
|
|
10921
11402
|
consumedEnd = valueEnd;
|
|
10922
11403
|
continue;
|
|
@@ -11099,7 +11580,7 @@ const _streamTags = (path, minify) => {
|
|
|
11099
11580
|
// carrying nothing at all.
|
|
11100
11581
|
if (
|
|
11101
11582
|
minify &&
|
|
11102
|
-
((
|
|
11583
|
+
((_absorbed !== null && _absorbed.has(path.node)) ||
|
|
11103
11584
|
_isRemovableEmptyElement(path, path.node))
|
|
11104
11585
|
) {
|
|
11105
11586
|
_streamOpen = "";
|
|
@@ -11565,7 +12046,7 @@ const _digest = (root) => {
|
|
|
11565
12046
|
// Same as a comment: an element the print drops joins what surrounds it.
|
|
11566
12047
|
if (
|
|
11567
12048
|
_isRemovableEmptyElement(A, node) ||
|
|
11568
|
-
(
|
|
12049
|
+
(_absorbed !== null && _absorbed.has(node))
|
|
11569
12050
|
) {
|
|
11570
12051
|
continue;
|
|
11571
12052
|
}
|
|
@@ -11600,7 +12081,13 @@ const _digest = (root) => {
|
|
|
11600
12081
|
continue;
|
|
11601
12082
|
}
|
|
11602
12083
|
const name = a.serializedName || a.name;
|
|
11603
|
-
|
|
12084
|
+
// A handler joins the two the printer rewrites wholesale: a renderer's
|
|
12085
|
+
// answer is other text for the same behavior, so comparing it says nothing.
|
|
12086
|
+
if (
|
|
12087
|
+
REWRITABLE_ATTRIBUTES.has(a.name) ||
|
|
12088
|
+
a.name === "srcdoc" ||
|
|
12089
|
+
_isEventHandlerAttribute(elementName, a.name)
|
|
12090
|
+
) {
|
|
11604
12091
|
attributes.push(`${name}=${_DIGEST_OPAQUE}`);
|
|
11605
12092
|
continue;
|
|
11606
12093
|
}
|
|
@@ -11784,7 +12271,7 @@ const printer = (path, writer) => {
|
|
|
11784
12271
|
// carrying nothing at all.
|
|
11785
12272
|
if (
|
|
11786
12273
|
minify &&
|
|
11787
|
-
((
|
|
12274
|
+
((_absorbed !== null && _absorbed.has(path.node)) ||
|
|
11788
12275
|
_isRemovableEmptyElement(path, path.node))
|
|
11789
12276
|
) {
|
|
11790
12277
|
return "";
|
|
@@ -11891,9 +12378,6 @@ const printer = (path, writer) => {
|
|
|
11891
12378
|
// Literal-text elements (`script` / `style` / …) keep their body raw;
|
|
11892
12379
|
// every other text node is escaped (the WHATWG text split).
|
|
11893
12380
|
const parent = path.parentOf();
|
|
11894
|
-
// TODO minify an inline `<script>` too: terser reaches webpack only
|
|
11895
|
-
// through `minimizer-webpack-plugin` and its API is async, so it needs
|
|
11896
|
-
// a JS-minify hook on the options rather than a call from here.
|
|
11897
12381
|
if (parent !== 0 && LITERAL_TEXT_PARENTS.has(path.tagName(parent))) {
|
|
11898
12382
|
if (!minify) return data;
|
|
11899
12383
|
const literalName = path.tagName(parent);
|
|
@@ -11905,7 +12389,7 @@ const printer = (path, writer) => {
|
|
|
11905
12389
|
}
|
|
11906
12390
|
// The run's first sheet prints the whole run; the rest printed
|
|
11907
12391
|
// into it here, before their own text or tags are reached.
|
|
11908
|
-
return
|
|
12392
|
+
return _absorbed !== null && _absorbed.has(parent)
|
|
11909
12393
|
? ""
|
|
11910
12394
|
: _mergedStyleRun(path, parent, data);
|
|
11911
12395
|
}
|
|
@@ -11914,16 +12398,23 @@ const printer = (path, writer) => {
|
|
|
11914
12398
|
if (JSON_SCRIPT_TYPES.has(type) || _JSON_SUBTYPE_REGEXP.test(type)) {
|
|
11915
12399
|
return _minifyInlineJson(data);
|
|
11916
12400
|
}
|
|
11917
|
-
//
|
|
11918
|
-
//
|
|
11919
|
-
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
data.trim() !== ""
|
|
11924
|
-
) {
|
|
11925
|
-
return _renderEmbedded(data, JAVASCRIPT_TYPE);
|
|
12401
|
+
// A bare `<script>` is the classic one a run may fold into, and
|
|
12402
|
+
// the run's first prints all of it — the rest printed into it here.
|
|
12403
|
+
let body = data;
|
|
12404
|
+
if (_mergeScripts && path.attributeCount(parent) === 0) {
|
|
12405
|
+
if (_absorbed !== null && _absorbed.has(parent)) return "";
|
|
12406
|
+
body = _mergedScriptRun(path, parent, data);
|
|
11926
12407
|
}
|
|
12408
|
+
// Webpack ships no JS minifier, so an inline script is touched only
|
|
12409
|
+
// by a caller's renderer; the built-in declines it.
|
|
12410
|
+
if (JAVASCRIPT_SCRIPT_TYPES.has(type) && body.trim() !== "") {
|
|
12411
|
+
return _renderEmbedded(
|
|
12412
|
+
body,
|
|
12413
|
+
JAVASCRIPT_TYPE,
|
|
12414
|
+
type === MODULE_SCRIPT ? MODULE_SCRIPT : CLASSIC_SCRIPT
|
|
12415
|
+
);
|
|
12416
|
+
}
|
|
12417
|
+
return body;
|
|
11927
12418
|
}
|
|
11928
12419
|
return data;
|
|
11929
12420
|
}
|
|
@@ -11989,7 +12480,7 @@ const printer = (path, writer) => {
|
|
|
11989
12480
|
// without changing length — so it is excluded by name.
|
|
11990
12481
|
if (
|
|
11991
12482
|
raw.length === data.length &&
|
|
11992
|
-
|
|
12483
|
+
!_tailFusesWithNextTag(raw) &&
|
|
11993
12484
|
!(inputHasCr && raw.includes("\r")) &&
|
|
11994
12485
|
!_hasOpenReference(raw)
|
|
11995
12486
|
) {
|
|
@@ -12569,16 +13060,6 @@ const grammar = (input, visitors, writer, options) => {
|
|
|
12569
13060
|
writer !== undefined && writer.options.removeEmptyAttributes === true;
|
|
12570
13061
|
_removeEmptyElements =
|
|
12571
13062
|
writer !== undefined && writer.options.removeEmptyElements === true;
|
|
12572
|
-
// With the rest rather than in `printer`: they are the print's, not the
|
|
12573
|
-
// node's, and that runs once per node.
|
|
12574
|
-
_cssEnvironment =
|
|
12575
|
-
writer === undefined ? undefined : writer.options.environment;
|
|
12576
|
-
_cssConvertLengthUnits =
|
|
12577
|
-
writer !== undefined && writer.options.convertLengthUnits === true;
|
|
12578
|
-
_cssRewriteCustomProperties =
|
|
12579
|
-
writer !== undefined && writer.options.rewriteCustomProperties === true;
|
|
12580
|
-
_cssTransforms =
|
|
12581
|
-
writer === undefined ? undefined : writer.options.cssTransforms;
|
|
12582
13063
|
_transforms = _transformsFrom(
|
|
12583
13064
|
writer === undefined ? undefined : writer.options.transforms
|
|
12584
13065
|
);
|
|
@@ -12605,9 +13086,10 @@ const grammar = (input, visitors, writer, options) => {
|
|
|
12605
13086
|
_sortTokenLists =
|
|
12606
13087
|
writer !== undefined && writer.options.sortTokenLists === true;
|
|
12607
13088
|
_mergeStyles = writer !== undefined && writer.options.mergeStyles === true;
|
|
13089
|
+
_mergeScripts = writer !== undefined && writer.options.mergeScripts === true;
|
|
12608
13090
|
// Node ids are per-parse, so a set kept from the last one would name this
|
|
12609
13091
|
// one's nodes. Dropped rather than cleared: most documents never fill it.
|
|
12610
|
-
|
|
13092
|
+
_absorbed = null;
|
|
12611
13093
|
// Counted from the parse this call is about to run, so it cannot be kept.
|
|
12612
13094
|
_attributeFrequencies = null;
|
|
12613
13095
|
_attributeRanks = null;
|
|
@@ -12651,7 +13133,7 @@ const grammar = (input, visitors, writer, options) => {
|
|
|
12651
13133
|
: declineDeferredWrites(writer.result(), _deferEmbeddedSource);
|
|
12652
13134
|
// Both are keyed by node id, which the next parse hands out afresh.
|
|
12653
13135
|
_emptyElements.clear();
|
|
12654
|
-
|
|
13136
|
+
_absorbed = null;
|
|
12655
13137
|
if (_digest(parseHtml(printed, 0, options)) !== digest) {
|
|
12656
13138
|
writer.replaceAll(input);
|
|
12657
13139
|
}
|
|
@@ -12669,8 +13151,6 @@ const grammar = (input, visitors, writer, options) => {
|
|
|
12669
13151
|
// Keyed by this parse's interned names, so it must not outlive it.
|
|
12670
13152
|
_attributeFrequencies = null;
|
|
12671
13153
|
_attributeRanks = null;
|
|
12672
|
-
// Keyed by this print's options, and holding slices of its input.
|
|
12673
|
-
_styleAttributeCache.clear();
|
|
12674
13154
|
_emptyElements.clear();
|
|
12675
13155
|
_streamEligible = false;
|
|
12676
13156
|
_streaming = false;
|
|
@@ -13135,8 +13615,8 @@ const parseMsapplicationTask = (input) => {
|
|
|
13135
13615
|
};
|
|
13136
13616
|
|
|
13137
13617
|
// CSS syntax is loaded on first `parseCssUrls` call, not at module load —
|
|
13138
|
-
// `HtmlGenerator`
|
|
13139
|
-
//
|
|
13618
|
+
// `HtmlGenerator` defers it the same way, and most documents carry no
|
|
13619
|
+
// URL-bearing SVG presentation attribute.
|
|
13140
13620
|
/** @type {typeof import("../css/syntax") | undefined} */
|
|
13141
13621
|
let _cssSyntax;
|
|
13142
13622
|
|
|
@@ -13561,7 +14041,22 @@ const A = {
|
|
|
13561
14041
|
};
|
|
13562
14042
|
|
|
13563
14043
|
module.exports.A = A;
|
|
14044
|
+
// The `as` a `style=""` is offered under: a block's contents rather than a
|
|
14045
|
+
// whole stylesheet, which is what gives it a rule block's merges.
|
|
14046
|
+
module.exports.BLOCK_CONTENTS = BLOCK_CONTENTS;
|
|
14047
|
+
// The `as` a classic `<script>` is offered under — the production every
|
|
14048
|
+
// JavaScript engine reads by default.
|
|
14049
|
+
module.exports.CLASSIC_SCRIPT = CLASSIC_SCRIPT;
|
|
13564
14050
|
module.exports.EMBEDDED_LANGUAGES = EMBEDDED_LANGUAGES;
|
|
14051
|
+
// The `as` an event handler attribute is offered under, read by a renderer
|
|
14052
|
+
// whose engine takes only whole scripts.
|
|
14053
|
+
module.exports.EVENT_HANDLER = EVENT_HANDLER;
|
|
14054
|
+
// Neither a JSON `<script>` body nor an `<svg>` subtree is a module source
|
|
14055
|
+
// type, so both name themselves here.
|
|
14056
|
+
module.exports.JSON_TYPE = JSON_TYPE;
|
|
14057
|
+
// The `as` a `<script type=module>` is offered under: the goal symbol decides
|
|
14058
|
+
// what parses, so a renderer cannot read it off the body.
|
|
14059
|
+
module.exports.MODULE_SCRIPT = MODULE_SCRIPT;
|
|
13565
14060
|
module.exports.NS_HTML = NS_HTML;
|
|
13566
14061
|
module.exports.NS_MATHML = NS_MATHML;
|
|
13567
14062
|
module.exports.NS_SVG = NS_SVG;
|
|
@@ -13576,11 +14071,13 @@ module.exports.SourceProcessor = SourceProcessor;
|
|
|
13576
14071
|
module.exports.askEmbeddedRenderer = askEmbeddedRenderer;
|
|
13577
14072
|
module.exports.baseTag = baseTag;
|
|
13578
14073
|
module.exports.buildHeadTags = buildHeadTags;
|
|
14074
|
+
module.exports.builtinEmbeddedRenderer = builtinEmbeddedRenderer;
|
|
13579
14075
|
module.exports.collectEmbeddedDiagnostics = collectEmbeddedDiagnostics;
|
|
13580
14076
|
module.exports.decodeEntities = decodeEntities;
|
|
13581
14077
|
module.exports.embeddedText = embeddedText;
|
|
13582
14078
|
module.exports.escapeAttribute = escapeAttribute;
|
|
13583
14079
|
module.exports.escapeText = escapeText;
|
|
14080
|
+
module.exports.htmlMinify = htmlMinify;
|
|
13584
14081
|
module.exports.isAsciiWhitespace = isSpace;
|
|
13585
14082
|
module.exports.metaTag = metaTag;
|
|
13586
14083
|
// WHATWG "ASCII whitespace" (tab / LF / FF / CR / space) — the tokenizer's
|
|
@@ -13592,4 +14089,5 @@ module.exports.parseSrc = parseSrc;
|
|
|
13592
14089
|
module.exports.parseSrcset = parseSrcset;
|
|
13593
14090
|
module.exports.pickTransforms = pickTransforms;
|
|
13594
14091
|
module.exports.printer = printer;
|
|
14092
|
+
module.exports.stripJsonWhitespace = stripJsonWhitespace;
|
|
13595
14093
|
module.exports.tokenize = tokenize;
|