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.
Files changed (226) hide show
  1. package/README.md +5 -2
  2. package/lib/APIPlugin.js +23 -7
  3. package/lib/AutomaticPrefetchPlugin.js +1 -1
  4. package/lib/Cache.js +8 -3
  5. package/lib/CacheFacade.js +1 -1
  6. package/lib/ChunkGraph.js +20 -18
  7. package/lib/CircularModulesPlugin.js +157 -55
  8. package/lib/CleanPlugin.js +2 -2
  9. package/lib/Compilation.js +321 -303
  10. package/lib/Compiler.js +209 -46
  11. package/lib/ConcatenationScope.js +25 -35
  12. package/lib/ContextModule.js +17 -81
  13. package/lib/ContextModuleFactory.js +1 -1
  14. package/lib/CopyPlugin.js +1054 -0
  15. package/lib/DefinePlugin.js +24 -8
  16. package/lib/DotenvPlugin.js +41 -13
  17. package/lib/ExternalModule.js +41 -14
  18. package/lib/FileSystemInfo.js +17 -14
  19. package/lib/FlagDependencyExportsPlugin.js +45 -21
  20. package/lib/FlagDependencyUsagePlugin.js +8 -2
  21. package/lib/HotModuleReplacementPlugin.js +53 -7
  22. package/lib/JavascriptMetaInfoPlugin.js +5 -3
  23. package/lib/MainTemplate.js +5 -1
  24. package/lib/Module.js +16 -7
  25. package/lib/MultiCompiler.js +1 -1
  26. package/lib/MultiWatching.js +1 -1
  27. package/lib/NormalModule.js +58 -32
  28. package/lib/NormalModuleFactory.js +1 -1
  29. package/lib/RuntimeGlobals.js +21 -7
  30. package/lib/RuntimeModule.js +28 -12
  31. package/lib/RuntimePlugin.js +33 -3
  32. package/lib/RuntimeTemplate.js +794 -477
  33. package/lib/SourceMapDevToolPlugin.js +1 -1
  34. package/lib/Watching.js +9 -0
  35. package/lib/WebpackOptionsApply.js +84 -91
  36. package/lib/asset/AssetGenerator.js +25 -8
  37. package/lib/asset/AssetModule.js +3 -0
  38. package/lib/asset/WebManifestParser.js +7 -5
  39. package/lib/async-modules/AsyncModuleHelpers.js +10 -4
  40. package/lib/cache/AddBuildDependenciesPlugin.js +33 -4
  41. package/lib/cache/IdleFileCachePlugin.js +2 -2
  42. package/lib/cache/PackFileCacheStrategy.js +14 -1
  43. package/lib/config/browserslistTargetHandler.js +19 -0
  44. package/lib/config/defaults.js +110 -64
  45. package/lib/config/normalization.js +100 -20
  46. package/lib/config/target.js +10 -0
  47. package/lib/container/ContainerReferencePlugin.js +6 -0
  48. package/lib/container/RemoteRuntimeModule.js +9 -0
  49. package/lib/container/declaredRemotes.js +39 -0
  50. package/lib/css/CssGenerator.js +8 -0
  51. package/lib/css/CssInjectStyleRuntimeModule.js +9 -0
  52. package/lib/css/CssLoadingRuntimeModule.js +94 -36
  53. package/lib/css/CssModule.js +1 -1
  54. package/lib/css/CssModulesPlugin.js +7 -6
  55. package/lib/css/CssParser.js +144 -11
  56. package/lib/css/CssServerStylesRuntimeModule.js +9 -0
  57. package/lib/css/cssMinify.js +11 -3
  58. package/lib/css/data.js +2814 -678
  59. package/lib/css/syntax.js +4082 -248
  60. package/lib/debug/ProfilingPlugin.js +4 -0
  61. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -3
  62. package/lib/dependencies/AMDRuntimeModules.js +18 -0
  63. package/lib/dependencies/CommonJsExportRequireDependency.js +3 -1
  64. package/lib/dependencies/CommonJsExportsParserPlugin.js +12 -13
  65. package/lib/dependencies/CommonJsImportsParserPlugin.js +1 -0
  66. package/lib/dependencies/CommonJsPlugin.js +19 -3
  67. package/lib/dependencies/CommonJsRequireDependency.js +32 -9
  68. package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +20 -7
  69. package/lib/dependencies/CssIcssExportDependency.js +7 -1
  70. package/lib/dependencies/HarmonyCompatibilityDependency.js +58 -4
  71. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +8 -3
  72. package/lib/dependencies/HarmonyExportExpressionDependency.js +44 -29
  73. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +171 -32
  74. package/lib/dependencies/HarmonyExportInitFragment.js +99 -19
  75. package/lib/dependencies/HarmonyExportSpecifierDependency.js +18 -30
  76. package/lib/dependencies/HarmonyImportBareSideEffectDependency.js +59 -0
  77. package/lib/dependencies/HarmonyImportDependency.js +99 -41
  78. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +20 -3
  79. package/lib/dependencies/HarmonyImportSpecifierDependency.js +24 -1
  80. package/lib/dependencies/HarmonyModulesPlugin.js +20 -0
  81. package/lib/dependencies/HtmlEntryDependency.js +62 -2
  82. package/lib/dependencies/ImportContextDependency.js +43 -3
  83. package/lib/dependencies/ImportDependency.js +8 -29
  84. package/lib/dependencies/ImportMetaMainDependency.js +104 -0
  85. package/lib/dependencies/ImportMetaPlugin.js +59 -15
  86. package/lib/dependencies/ImportParserPlugin.js +24 -14
  87. package/lib/dependencies/RequireHeaderDependency.js +20 -21
  88. package/lib/dependencies/SystemRuntimeModule.js +9 -0
  89. package/lib/dependencies/WorkerDependency.js +1 -2
  90. package/lib/dependencies/WorkletDependency.js +1 -2
  91. package/lib/dependencies/importOptionsCheck.js +47 -0
  92. package/lib/dll/DelegatedModule.js +18 -1
  93. package/lib/dll/DllReferencePlugin.js +11 -8
  94. package/lib/dll/LibManifestPlugin.js +1 -1
  95. package/lib/errors/AnalyzableBailoutsWarning.js +41 -0
  96. package/lib/errors/DotenvFileError.js +29 -0
  97. package/lib/errors/DuplicateModulesWarning.js +12 -7
  98. package/lib/errors/EmptyCopyPatternWarning.js +26 -0
  99. package/lib/errors/ExternalStarReexportError.js +44 -0
  100. package/lib/errors/ImportedBindingAssignmentError.js +30 -0
  101. package/lib/errors/MissingSourceMapsWarning.js +36 -0
  102. package/lib/errors/ModuleParseError.js +2 -1
  103. package/lib/errors/UnusedAssetsWarning.js +36 -0
  104. package/lib/errors/UnusedFederationWarning.js +35 -0
  105. package/lib/errors/UnusedModulesWarning.js +38 -0
  106. package/lib/esm/ExportWebpackRequireRuntimeModule.js +9 -0
  107. package/lib/esm/ModuleChunkFormatPlugin.js +28 -3
  108. package/lib/esm/ModuleChunkLoadingPlugin.js +30 -11
  109. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +92 -60
  110. package/lib/hmr/HotModuleReplacement.runtime.js +8 -2
  111. package/lib/hmr/HotModuleReplacementRuntimeModule.js +9 -0
  112. package/lib/html/HtmlGenerator.js +11 -5
  113. package/lib/html/HtmlModulesPlugin.js +360 -84
  114. package/lib/html/HtmlParser.js +13 -6
  115. package/lib/html/builtinEmbeddedRenderer.js +107 -0
  116. package/lib/html/data.js +121 -7
  117. package/lib/html/htmlMinify.js +84 -113
  118. package/lib/html/syntax.js +1067 -569
  119. package/lib/ids/HashedModuleIdsPlugin.js +2 -1
  120. package/lib/index.js +3 -0
  121. package/lib/javascript/JavascriptModule.js +1 -0
  122. package/lib/javascript/JavascriptModulesPlugin.js +52 -22
  123. package/lib/javascript/JavascriptParser.js +1255 -728
  124. package/lib/javascript/StartupHelpers.js +27 -3
  125. package/lib/javascript/data.js +172 -0
  126. package/lib/javascript/grammar.js +5546 -0
  127. package/lib/javascript/parser.js +1494 -0
  128. package/lib/javascript/regexp.js +1762 -0
  129. package/lib/javascript/regexpData.js +51 -0
  130. package/lib/javascript/syntax.js +8696 -5225
  131. package/lib/library/ModuleLibraryPlugin.js +116 -5
  132. package/lib/node/ReadFileChunkLoadingRuntimeModule.js +22 -0
  133. package/lib/node/RequireChunkLoadingRuntimeModule.js +22 -0
  134. package/lib/optimize/ConcatenatedModule.js +133 -25
  135. package/lib/optimize/ConstExportsPlugin.js +50 -39
  136. package/lib/optimize/InlineExports.js +20 -123
  137. package/lib/optimize/MangleExportsPlugin.js +10 -0
  138. package/lib/optimize/ModuleConcatenationPlugin.js +24 -1
  139. package/lib/optimize/RealContentHashPlugin.js +68 -9
  140. package/lib/optimize/SideEffectsFlagPlugin.js +17 -9
  141. package/lib/performance/AnalyzableBailoutsPlugin.js +115 -0
  142. package/lib/performance/CacheEffectivenessPlugin.js +4 -3
  143. package/lib/performance/DuplicateModulesPlugin.js +57 -13
  144. package/lib/performance/EvalUsagePlugin.js +5 -2
  145. package/lib/performance/MissingSourceMapsPlugin.js +115 -0
  146. package/lib/performance/PureAnnotationsPlugin.js +6 -3
  147. package/lib/performance/TopLevelThisPlugin.js +5 -4
  148. package/lib/performance/UnusedAssetsPlugin.js +109 -0
  149. package/lib/performance/UnusedFederationPlugin.js +147 -0
  150. package/lib/performance/UnusedModulesPlugin.js +141 -0
  151. package/lib/performance/UnusedReexportsPlugin.js +1 -55
  152. package/lib/performance/isOnlyReexported.js +65 -0
  153. package/lib/prefetch/ChunkPrefetchFunctionRuntimeModule.js +25 -5
  154. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +2 -0
  155. package/lib/prefetch/ChunkPrefetchStartupRuntimeModule.js +9 -0
  156. package/lib/prefetch/ChunkPrefetchTriggerRuntimeModule.js +9 -0
  157. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +9 -0
  158. package/lib/prefetch/ResourceHintPlugin.js +7 -3
  159. package/lib/prefetch/ResourceHintRuntimeModule.js +9 -0
  160. package/lib/prefetch/StartupAssetHintRuntimeModule.js +9 -0
  161. package/lib/rules/RuleSetCompiler.js +211 -4
  162. package/lib/runtime/AsyncModuleRuntimeModule.js +105 -67
  163. package/lib/runtime/AutoPublicPathRuntimeModule.js +9 -0
  164. package/lib/runtime/BaseUriRuntimeModule.js +9 -0
  165. package/lib/runtime/ChunkNameRuntimeModule.js +9 -0
  166. package/lib/runtime/EnsureChunkRuntimeModule.js +35 -16
  167. package/lib/runtime/GetChunkFilenameRuntimeModule.js +9 -0
  168. package/lib/runtime/GetFullHashRuntimeModule.js +9 -0
  169. package/lib/runtime/GetMainFilenameRuntimeModule.js +9 -0
  170. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +9 -0
  171. package/lib/runtime/GlobalRuntimeModule.js +9 -0
  172. package/lib/runtime/HasOwnPropertyRuntimeModule.js +9 -0
  173. package/lib/runtime/HelperRuntimeModule.js +10 -0
  174. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +338 -217
  175. package/lib/runtime/NonceRuntimeModule.js +9 -0
  176. package/lib/runtime/OnChunksLoadedRuntimeModule.js +67 -20
  177. package/lib/runtime/PublicPathRuntimeModule.js +9 -0
  178. package/lib/runtime/RuntimeIdRuntimeModule.js +9 -0
  179. package/lib/runtime/SpecNamespaceObjectRuntimeModule.js +90 -0
  180. package/lib/runtime/StartupChunkDependenciesRuntimeModule.js +9 -0
  181. package/lib/runtime/StartupEntrypointRuntimeModule.js +9 -0
  182. package/lib/runtime/SystemContextRuntimeModule.js +9 -0
  183. package/lib/runtime/ToBinaryRuntimeModule.js +9 -0
  184. package/lib/schemes/HttpUriPlugin.js +22 -5
  185. package/lib/serialization/FileMiddleware.js +15 -2
  186. package/lib/serialization/ObjectMiddleware.js +138 -106
  187. package/lib/sharing/ConsumeSharedPlugin.js +12 -0
  188. package/lib/sharing/ConsumeSharedRuntimeModule.js +11 -0
  189. package/lib/sharing/ShareRuntimeModule.js +9 -0
  190. package/lib/sharing/declaredShared.js +47 -0
  191. package/lib/stats/DefaultStatsFactoryPlugin.js +3 -1
  192. package/lib/stats/DefaultStatsPrinterPlugin.js +3 -0
  193. package/lib/util/ArrayQueue.js +3 -3
  194. package/lib/util/SourceProcessor.js +76 -69
  195. package/lib/util/URLAbsoluteSpecifier.js +3 -1
  196. package/lib/util/async.js +292 -0
  197. package/lib/util/buildDiagnostics.js +69 -0
  198. package/lib/util/conventions.js +5 -1
  199. package/lib/util/fs.js +27 -1
  200. package/lib/util/handlerKeys.js +40 -0
  201. package/lib/util/identifier.js +20 -0
  202. package/lib/util/internalSerializables.js +6 -0
  203. package/lib/util/registerExternalSerializer.js +6 -45
  204. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +9 -0
  205. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +9 -0
  206. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +9 -0
  207. package/lib/web/JsonpChunkLoadingRuntimeModule.js +36 -0
  208. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +19 -0
  209. package/package.json +22 -16
  210. package/schemas/WebpackOptions.check.js +1 -1
  211. package/schemas/WebpackOptions.json +388 -76
  212. package/schemas/plugins/LoaderOptionsPlugin.check.js +1 -1
  213. package/schemas/plugins/ManifestPlugin.check.js +1 -1
  214. package/schemas/plugins/SourceMapDevToolPlugin.check.js +1 -1
  215. package/schemas/plugins/asset/AssetGeneratorOptions.check.js +1 -1
  216. package/schemas/plugins/asset/AssetResourceGeneratorOptions.check.js +1 -1
  217. package/schemas/plugins/container/ContainerPlugin.check.js +1 -1
  218. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  219. package/schemas/plugins/debug/ProfilingPlugin.check.js +1 -1
  220. package/schemas/plugins/dll/DllReferencePlugin.check.js +1 -1
  221. package/schemas/plugins/ids/HashedModuleIdsPlugin.check.js +1 -1
  222. package/schemas/plugins/schemes/HttpUriPlugin.check.js +1 -1
  223. package/types.d.ts +1440 -331
  224. package/lib/dependencies/ExportBindingInitFragment.js +0 -191
  225. package/lib/errors/EntrypointOverlapWarning.js +0 -45
  226. package/lib/performance/EntrypointOverlapPlugin.js +0 -122
@@ -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, an `<svg>` subtree, and the document an `<iframe srcdoc>` holds. Returning the input unchanged declines
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
- // The contents of this region are auto-generated by
157
- // `tooling/generate-html-entities.js` from `tooling/html-entities.json`.
158
- // Do not edit by hand — re-run the generator (via `yarn fix:special`) to refresh.
159
- //
160
- // WHATWG named character references. Keys are entity names WITHOUT the
161
- // leading `&` (some end with `;`, others omit it for legacy entities that
162
- // match without a closing semicolon). Values are the decoded character
163
- // strings (1–2 UTF-16 code units).
164
- // Built on a null prototype so bracket lookups (`HTML_ENTITIES[name]`)
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
- // Clone of an element's attribute run: keep name/value (and the serializer
5241
- // name flag) but drop source offsets so the consumer doesn't emit a duplicate
5242
- // dependency for the reopened element's spans. Values are materialized since
5243
- // the offsets are gone.
5244
- const cloneAttrs = (/** @type {HtmlElement} */ el) => {
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
- // Merge a repeated `<html>`/`<body>` tag's attributes into the element: only
5256
- // names not already present are added (in source order after the existing
5257
- // ones). Runs are contiguous, so any addition re-allocates the whole run; the
5258
- // old slots are orphaned (at most once per repeated tag, rare).
5259
- const mergeAttrs = (
5260
- /** @type {HtmlElement} */ el,
5261
- /** @type {AttributeRun} */ run
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
- // Every insertion mode handles a processing instruction token on the same arc
5339
- // as a comment token, so the arcs test both.
5340
- const isCommentOrProcessingInstruction = (/** @type {Token} */ t) =>
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
- // MathML/SVG specials handled via namespace checks below.
5403
-
5404
- const isSpecial = (/** @type {HtmlElement} */ el) => {
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
- // `<font color|face|size>` breaks out of foreign content (§13.2.6.5).
5414
- const hasFontBreakoutAttr = (/** @type {AttributeRun} */ run) => {
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
- // Hoisted so the many `open.some(...)` "is there an open HTML <template>?"
5594
- // checks reuse one predicate instead of allocating an arrow per call.
5595
- const isHtmlTemplateEl = (/** @type {HtmlElement} */ e) =>
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
- const mkEl = (
5655
- /** @type {string} */ tagName,
5656
- /** @type {number} */ ns,
5657
- /** @type {AttributeRun} */ attrs,
5658
- /** @type {TagPos | null | undefined} */ pos
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
- const isScopeBoundary = (/** @type {HtmlElement} */ el) => {
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
- // Attribute values are stored raw (offsets into the source), so two unresolved
5717
- // values compare by range — `_attrValueOf` would slice a string per side just
5718
- // to throw it away.
5719
- /** @type {(i: number, j: number) => boolean} */
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
- const sameAttrs = (
5740
- /** @type {HtmlElement} */ a,
5741
- /** @type {HtmlElement} */ b
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
- const startsWithWs = (/** @type {string} */ s) => {
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
- const isAllWs = (/** @type {string} */ s) => {
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
- const mathmlTextIntegrationPoint = (/** @type {HtmlElement} */ el) =>
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
- const htmlIntegrationPoint = (/** @type {HtmlElement} */ el) => {
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
- // Adjust a foreign start tag's attribute run in place (the run is consumed
5834
- // only by this tag's element): SVG camelCase names are rewritten, and
5835
- // namespaced names get the serializer-name flag (see `_attrSerializedName`).
5836
- const adjustForeignAttrs = (
5837
- /** @type {AttributeRun} */ run,
5838
- /** @type {number} */ ns
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
- // Only `definitionurl` is rewritten (in place — the run is consumed only by
5858
- // this tag's element); the camelCase name serializes as itself.
5859
- const adjustMathmlAttrs = (/** @type {AttributeRun} */ run) => {
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
- const appendTo = (
5960
- /** @type {HtmlNodeRef} */ parent,
5961
- /** @type {HtmlNodeRef} */ node
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
- const placeAt = (
5983
- /** @type {HtmlNodeRef} */ parent,
5984
- /** @type {HtmlNodeRef} */ beforeNode
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
- const insertAtPlace = (
6034
- /** @type {InsertionPlace} */ place,
6035
- /** @type {HtmlNodeRef} */ node
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
- const insertCharacters = (
6088
- /** @type {string} */ data,
6089
- /** @type {number} */ start,
6090
- /** @type {number} */ end
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
- const insertHtmlElement = (
6170
- /** @type {string} */ tagName,
6171
- /** @type {AttributeRun} */ attrs,
6172
- /** @type {TagPos | null} */ pos
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
- const insertForeignElement = (
6182
- /** @type {string} */ tagName,
6183
- /** @type {number} */ ns,
6184
- /** @type {AttributeRun} */ attrs,
6185
- /** @type {TagPos | null} */ pos
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
- const isBoundaryForKind = (
6202
- /** @type {HtmlElement} */ el,
6203
- /** @type {number} */ kind
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
- // "have an element in scope": walk the open stack from the top until the
6214
- // named HTML element is found (true) or a scope boundary is hit (false).
6215
- const hasNameInScope = (
6216
- /** @type {string} */ tagName,
6217
- /** @type {number} */ kind
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
- const inScope = (/** @type {string} */ tagName) =>
6229
- hasNameInScope(tagName, SCOPE_DEFAULT);
6230
- const inButtonScope = (/** @type {string} */ tagName) =>
6231
- hasNameInScope(tagName, SCOPE_BUTTON);
6232
- const inListItemScope = (/** @type {string} */ tagName) =>
6233
- hasNameInScope(tagName, SCOPE_LIST_ITEM);
6234
- const inScopeEl = (/** @type {HtmlElement} */ target) => {
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
- // `target` is a single tag name (the common case) or a Set of names.
6243
- const inTableScope = (/** @type {string | Set<string>} */ target) => {
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
- // `splice` allocates an array for the removed element on every call; the list
6283
- // is short, so shifting in place is both cheaper and allocation-free.
6284
- /** @type {(i: number) => void} */
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
- const pushAfe = (/** @type {HtmlElement} */ el) => {
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
- const popUntil = (/** @type {string} */ tagName) => {
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
- const popUntilOneOf = (/** @type {Set<string>} */ set) => {
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
- // Split a character token's leading whitespace; per the spec each character
6436
- // is its own token, so a mixed run can straddle a mode change. Inserts the
6437
- // leading whitespace when `insert`, returns the non-whitespace remainder
6438
- // token (or null when the token was entirely whitespace).
6439
- const leadingWs = (
6440
- /** @type {CharToken} */ t,
6441
- /** @type {boolean} */ insert
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
- const process = (/** @type {Token} */ t) => {
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
- const shouldUseForeignRules = (
6484
- /** @type {HtmlElement} */ ac,
6485
- /** @type {Token} */ t
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
- const foreignContent = (/** @type {Token} */ t) => {
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
- const adoptionAgency = (
6616
- /** @type {string} */ subject,
6617
- /** @type {TagPos | null} */ pos
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
- const placeForCommonAncestor = (/** @type {HtmlElement} */ commonAncestor) => {
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
- const detach = (/** @type {HtmlNodeRef} */ node) => {
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
- // Dispatch the current insertion mode. An integer switch (cases ordered by
6792
- // frequency) keeps each `modes.x(t)` call site monomorphic, where a keyed
6793
- // `modes[mode]` load + indirect call would defeat inlining on the per-token
6794
- // hot path.
6795
- const runMode = (/** @type {Token} */ t) => {
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
- // "any other end tag" in body: pop to the matching open element, stopping at
7141
- // the first special element; also the adoption agency's no-element fallback.
7142
- const anyOtherEndTag = (/** @type {string} */ name) => {
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
- const startTagInBody = (/** @type {StartTagToken} */ t) => {
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
- const endTagInBody = (/** @type {EndTagToken} */ t) => {
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
- // generic RCDATA/RAWTEXT: tokenizer already emits the text + end tag, so we
7577
- // just insert the element and switch to "text" mode; text mode appends chars
7578
- // and the matching end tag pops.
7579
- const genericRawtext = (/** @type {StartTagToken} */ t) => {
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
- const genericRcdata = (/** @type {StartTagToken} */ t, swallow = false) => {
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) pendAttrStart = _attrCount + 1;
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 spelled with its own name (`disabled="disabled"`) as the bare name
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. A `style=""` stays with the built-in CSS minifier, whose text this print reads back to decide how the attribute is written. Takes precedence over `renderEmbeddedSource`
8786
- * @property {EmbeddedSourceRenderer=} renderEmbeddedSource renders each nested body this document embeds — an inline `<style>`, every `style=""` (handed over as a whole stylesheet, SVG's and MathML's included), a `<script>` holding JSON or JavaScript, an `<svg>` subtree, and the document an `<iframe srcdoc>` holds (decoded, and written back escaped). Replaces the built-in CSS and JSON minifiers wherever it answers, and returning anything but text falls back to them; it is the only way inline JavaScript, SVG and a nested document are reached at all
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
- * The first two are not HTML's own: an inline `<style>` and every `style=""` run
8792
- * through the CSS minifier, so `optimization.minimize.css` rides along to reach
8793
- * it (see `lib/config/defaults.js`) and the two must not disagree about what the
8794
- * target can read. The rest is `optimization.minimize.html`.
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
- let out = `<${path.tagName()}`;
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
- out +=
8919
- attribute.value === ""
8920
- ? ` ${name}`
8921
- : ` ${name}="${attribute.value.replace(/"/g, "&quot;")}"`;
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, "&quot;")}"`;
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 `_cssEnvironment`. "none" is off; the rest name how much of the
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. It
9352
- // replaces the built-in CSS/JSON minifiers and is the only way inline
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. A body whose text this print reads back to decide
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, which must be present.
9371
- * Anything it throws is a renderer that did not answer: a document must not
9372
- * fail to serialize because something inside it did not parse.
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 = /** @type {EmbeddedSourceRenderer} */ (
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` for a body webpack has no minifier of its own for, so
9421
- * a renderer that declines leaves it exactly as written.
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(source, type, (rendered) =>
9429
- typeof rendered === "string" ? rendered : source
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
- // A run of adjacent `<style>` elements prints as one, so the elements after the
9487
- // first print nothing at all. Filled by the first one's text as it prints, which
9488
- // is before any of theirs does; null until a document actually has a run.
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 _absorbedStyles = null;
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
- * Minify a `<style>` body with webpack's own CSS minifier — the one that runs on
9637
- * CSS assets, so an inline sheet is held to the same rules. `null` when it
9638
- * declines the text, which a caller joining two sheets has to tell apart from a
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 minified text
9898
+ * @returns {string} the rendered text, the marker standing for it, or the text as written
9704
9899
  */
9705
- const _minifyInlineCss = (css) => {
9706
- const minified = _minifyStyleBody(css);
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 < (unquotable ? value.length : rawValue.length + 2)
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
- * Minify a `style` attribute's declaration list with webpack's CSS minifier, as
9762
- * the block-contents production it is — which is what gives the list the
9763
- * duplicate-drop and shorthand merges a rule's block gets.
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 minified declarations
9961
+ * @returns {string} the rendered declarations, or the value as written
9766
9962
  */
9767
9963
  const _minifyStyleAttribute = (raw) => {
9768
- // Ahead of the memo: with the switch off nothing is minified, so there is
9769
- // nothing to keep and nothing to read back.
9770
- if (!_transforms.minifyStyles) return raw;
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
- lowered !== "" &&
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 the DOM reflects the list as a `DOMTokenList`, an ordered *set*,
10032
- * so a repeat was never a second token — drop a token already in it. Order is
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} unique whether a repeated token may go (`DOM_TOKEN_LIST_ATTRIBUTES`)
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, unique, sort) => {
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 check reads the spans
10066
- // in place and only a list that does pay for the pieces. Sorting needs them
10067
- // either way.
10068
- if (!sort && !(unique && _repeatsAToken(collapsed))) return collapsed;
10069
- if (sort) return _sortedTokenList(collapsed, unique);
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, deduplicated when it is a set. They are
10101
- * ordered where they sit, so the pieces `split` would build are never built.
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
- * @param {boolean} unique whether repeats collapse
10104
- * @returns {string} the ordered list
10296
+ * @returns {string} the ordered set
10105
10297
  */
10106
- const _sortedTokenList = (collapsed, unique) => {
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
- const tokenListOn = _transforms.normalizeListAttributes
10231
- ? TOKEN_LIST_ATTRIBUTES.get(name)
10232
- : undefined;
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 its requests go out in and
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 = URL_ATTRIBUTES.get(name);
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
- return _asciiLowerCase(_asciiTrim(attribute.value));
10469
+ // The value the spec matches is the one the parser read, so a reference
10470
+ // is decoded first: `text&#47;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
- * Strip the whitespace between a JSON body's tokens. Every literal is copied
10298
- * byte for byte — re-serializing would reorder nothing but would round numbers
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 stripped body
10499
+ * @returns {string} the rendered body, the marker standing for it, or the body as written
10304
10500
  */
10305
- const _minifyInlineJson = (json) => {
10306
- if (!_transforms.minifyJson) return json;
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 `<style>`'s only child when that is one text node — the shape a merge needs,
10378
- * since `<style>` is parsed as raw text and anything else means it is empty.
10379
- * @param {HtmlElement} element the `<style>` 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 _styleTextChild = (element) => {
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
- _styleTextChild(next) !== 0 &&
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
- * The text the first `<style>` of a run prints: every sheet in it, each minified
10451
- * on its own and then joined. Marks the rest of the run absorbed, which is what
10452
- * makes their tags and text print as nothing — it runs first because a run's
10453
- * first element prints before any of the others in both print paths.
10454
- *
10455
- * Each sheet is minified before it is joined, never after: only text the
10456
- * minifier accepted is known to be terminated, and appending to a sheet that is
10457
- * not makes the next one part of the last rule of this one. A sheet after the
10458
- * first carrying `@import` (or `@charset` / `@namespace`) is not folded either —
10459
- * those only apply at the top of a sheet, and would silently stop applying.
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
- const head = _minifyStyleBody(css);
10467
- if (head === null) return css;
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 decided = { text: "" };
10475
- const body = _minifyStyleBody(
10476
- _nodeStrings[/** @type {HtmlNodeRef} */ (_styleTextChild(next))],
10477
- decided
10478
- );
10479
- if (body === null) break;
10480
- // Read the built-in's text, not the marker: whether this sheet may be
10481
- // absorbed decides what its own element prints, so it cannot wait for an
10482
- // answer.
10483
- if (_SHEET_LEADING_AT_RULE_REGEXP.test(decided.text || body)) break;
10484
- if (_absorbedStyles === null) _absorbedStyles = new Set();
10485
- _absorbedStyles.add(next);
10486
- out += body;
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
- if (quoted) {
10779
- const hasReference = rawValue.includes("&");
10780
- // A whole document, so it is offered decoded and written back escaped —
10781
- // the one attribute whose reference-carrying value is still rewritten,
10782
- // since a document is mostly references. The renderer cannot parse it
10783
- // here (the walk is mid-parse), so it looks up what a walk-only pass
10784
- // prepared, exactly as an async minifier does.
10785
- if (
10786
- html &&
10787
- (_renderEmbeddedSource !== undefined ||
10788
- (_deferEmbeddedSource !== undefined && _deferSrcdoc)) &&
10789
- attributeName === "srcdoc" &&
10790
- elementName === "iframe" &&
10791
- rawValue !== ""
10792
- ) {
10793
- const document = decodeEntities(rawValue, true);
10794
- // The whole attribute is deferred, not the document inside it: what
10795
- // escaping it needs is decided by what it holds, which is the answer.
10796
- // The same test the gate above makes: with both renderers set, a
10797
- // caller asking for `srcdoc` on the normal path gets it there.
10798
- if (_deferEmbeddedSource !== undefined && _deferSrcdoc) {
10799
- // Declining leaves the attribute exactly as it was written, down to
10800
- // the delimiter: re-escaping it into double quotes would lengthen a
10801
- // value the source spelled with single ones.
10802
- const delimiter = open[valueStart - tagStart - 1];
10803
- const asWritten = ` ${attributeName}=${delimiter}${rawValue}${delimiter}`;
10804
-
10805
- out += _deferEmbedded(document, HTML_TYPE, (rendered) =>
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
- // The synchronous renderer: `_renderEmbedded` defers where a collector
10816
- // is set, and this attribute is the one kept off that path.
10817
- const rendered = _renderEmbeddedOrNull(document, HTML_TYPE);
10818
- if (rendered !== null && rendered !== document) {
10819
- out += ` ${attributeName}="${escapeAttribute(rendered, 34, true)}"`;
10820
- unquotedTail = false;
10821
- consumedEnd = valueEnd;
10822
- continue;
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
- (rawValue === "" ||
11236
+ (value === "" ||
10855
11237
  (html &&
10856
11238
  _transforms.collapseBooleanAttributes &&
10857
- rawValue.length === attributeName.length &&
10858
- _isBooleanAttribute(elementName, attributeName) &&
10859
- _asciiLowerCase(rawValue) === attributeName))
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(attributeName, minified, rawValue, spellable);
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 — `&amp;&amp;`
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
- // The list is deferred, not the shape around it: whether this value
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
- ((_absorbedStyles !== null && _absorbedStyles.has(path.node)) ||
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
- (_absorbedStyles !== null && _absorbedStyles.has(node))
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
- if (REWRITABLE_ATTRIBUTES.has(a.name) || a.name === "srcdoc") {
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
- ((_absorbedStyles !== null && _absorbedStyles.has(path.node)) ||
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 _absorbedStyles !== null && _absorbedStyles.has(parent)
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
- // No built-in: webpack ships no JS minifier, so an inline script is
11918
- // touched only by a caller's renderer.
11919
- if (
11920
- (_renderEmbeddedSource !== undefined ||
11921
- _deferEmbeddedSource !== undefined) &&
11922
- JAVASCRIPT_SCRIPT_TYPES.has(type) &&
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
- raw.charCodeAt(raw.length - 1) !== 60 &&
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
- _absorbedStyles = null;
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
- _absorbedStyles = null;
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` deliberately defers it the same way, and most documents
13139
- // never carry a URL-bearing SVG presentation attribute.
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;