@react-x11/components 0.9.0 → 0.11.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 (579) hide show
  1. package/README.md +10 -4
  2. package/dist/charts/node.d.ts +11 -1
  3. package/dist/charts/node.d.ts.map +1 -1
  4. package/dist/charts/node.js +44 -14
  5. package/dist/charts/node.js.map +1 -1
  6. package/dist/charts/render.d.ts +12 -3
  7. package/dist/charts/render.d.ts.map +1 -1
  8. package/dist/charts/render.js +94 -25
  9. package/dist/charts/render.js.map +1 -1
  10. package/dist/code/index.d.ts.map +1 -1
  11. package/dist/code/index.js +27 -24
  12. package/dist/code/index.js.map +1 -1
  13. package/dist/code-editor/completion.d.ts.map +1 -1
  14. package/dist/code-editor/completion.js +3 -2
  15. package/dist/code-editor/completion.js.map +1 -1
  16. package/dist/code-editor/index.d.ts.map +1 -1
  17. package/dist/code-editor/index.js +19 -0
  18. package/dist/code-editor/index.js.map +1 -1
  19. package/dist/code-editor/node.d.ts +177 -7
  20. package/dist/code-editor/node.d.ts.map +1 -1
  21. package/dist/code-editor/node.js +1329 -164
  22. package/dist/code-editor/node.js.map +1 -1
  23. package/dist/code-language/index.d.ts +1 -1
  24. package/dist/code-language/index.d.ts.map +1 -1
  25. package/dist/code-language/index.js +1 -1
  26. package/dist/code-language/index.js.map +1 -1
  27. package/dist/code-language/languages/glsl.d.ts.map +1 -1
  28. package/dist/code-language/languages/glsl.js +10 -2
  29. package/dist/code-language/languages/glsl.js.map +1 -1
  30. package/dist/code-language/languages/javascript.d.ts.map +1 -1
  31. package/dist/code-language/languages/javascript.js +18 -3
  32. package/dist/code-language/languages/javascript.js.map +1 -1
  33. package/dist/code-language/languages/json.d.ts.map +1 -1
  34. package/dist/code-language/languages/json.js +10 -2
  35. package/dist/code-language/languages/json.js.map +1 -1
  36. package/dist/code-language/languages/shell.d.ts.map +1 -1
  37. package/dist/code-language/languages/shell.js +10 -2
  38. package/dist/code-language/languages/shell.js.map +1 -1
  39. package/dist/code-language/languages/sql.d.ts.map +1 -1
  40. package/dist/code-language/languages/sql.js +20 -2
  41. package/dist/code-language/languages/sql.js.map +1 -1
  42. package/dist/code-language/runs.d.ts +31 -0
  43. package/dist/code-language/runs.d.ts.map +1 -1
  44. package/dist/code-language/runs.js +105 -0
  45. package/dist/code-language/runs.js.map +1 -1
  46. package/dist/code-language/stream.d.ts +22 -0
  47. package/dist/code-language/stream.d.ts.map +1 -1
  48. package/dist/code-language/stream.js +259 -9
  49. package/dist/code-language/stream.js.map +1 -1
  50. package/dist/code-language/types.d.ts +5 -4
  51. package/dist/code-language/types.d.ts.map +1 -1
  52. package/dist/codeblock/index.d.ts +26 -2
  53. package/dist/codeblock/index.d.ts.map +1 -1
  54. package/dist/codeblock/index.js +57 -4
  55. package/dist/codeblock/index.js.map +1 -1
  56. package/dist/flow/draw.d.ts +50 -0
  57. package/dist/flow/draw.d.ts.map +1 -1
  58. package/dist/flow/draw.js +132 -33
  59. package/dist/flow/draw.js.map +1 -1
  60. package/dist/flow/gl/color.d.ts +13 -0
  61. package/dist/flow/gl/color.d.ts.map +1 -0
  62. package/dist/flow/gl/color.js +82 -0
  63. package/dist/flow/gl/color.js.map +1 -0
  64. package/dist/flow/gl/index.d.ts +60 -0
  65. package/dist/flow/gl/index.d.ts.map +1 -0
  66. package/dist/flow/gl/index.js +283 -0
  67. package/dist/flow/gl/index.js.map +1 -0
  68. package/dist/flow/gl/pack.d.ts +133 -0
  69. package/dist/flow/gl/pack.d.ts.map +1 -0
  70. package/dist/flow/gl/pack.js +408 -0
  71. package/dist/flow/gl/pack.js.map +1 -0
  72. package/dist/flow/gl/renderer.d.ts +95 -0
  73. package/dist/flow/gl/renderer.d.ts.map +1 -0
  74. package/dist/flow/gl/renderer.js +459 -0
  75. package/dist/flow/gl/renderer.js.map +1 -0
  76. package/dist/flow/gl/shaders.d.ts +79 -0
  77. package/dist/flow/gl/shaders.d.ts.map +1 -0
  78. package/dist/flow/gl/shaders.js +346 -0
  79. package/dist/flow/gl/shaders.js.map +1 -0
  80. package/dist/flow/gl/text.d.ts +318 -0
  81. package/dist/flow/gl/text.d.ts.map +1 -0
  82. package/dist/flow/gl/text.js +1103 -0
  83. package/dist/flow/gl/text.js.map +1 -0
  84. package/dist/flow/index.d.ts +1 -1
  85. package/dist/flow/index.d.ts.map +1 -1
  86. package/dist/flow/index.js +619 -64
  87. package/dist/flow/index.js.map +1 -1
  88. package/dist/flow/node.d.ts +621 -72
  89. package/dist/flow/node.d.ts.map +1 -1
  90. package/dist/flow/node.js +2253 -1018
  91. package/dist/flow/node.js.map +1 -1
  92. package/dist/flow/paint.d.ts +35 -0
  93. package/dist/flow/paint.d.ts.map +1 -0
  94. package/dist/flow/paint.js +393 -0
  95. package/dist/flow/paint.js.map +1 -0
  96. package/dist/flow/paths.d.ts +14 -0
  97. package/dist/flow/paths.d.ts.map +1 -1
  98. package/dist/flow/paths.js +45 -19
  99. package/dist/flow/paths.js.map +1 -1
  100. package/dist/flow/scene.d.ts +584 -0
  101. package/dist/flow/scene.d.ts.map +1 -0
  102. package/dist/flow/scene.js +1453 -0
  103. package/dist/flow/scene.js.map +1 -0
  104. package/dist/flow/types.d.ts +111 -1
  105. package/dist/flow/types.d.ts.map +1 -1
  106. package/dist/html/controls.d.ts +28 -0
  107. package/dist/html/controls.d.ts.map +1 -1
  108. package/dist/html/controls.js +52 -2
  109. package/dist/html/controls.js.map +1 -1
  110. package/dist/html/css/calc.d.ts +14 -0
  111. package/dist/html/css/calc.d.ts.map +1 -0
  112. package/dist/html/css/calc.js +344 -0
  113. package/dist/html/css/calc.js.map +1 -0
  114. package/dist/html/css/cascade.d.ts +262 -6
  115. package/dist/html/css/cascade.d.ts.map +1 -1
  116. package/dist/html/css/cascade.js +1373 -67
  117. package/dist/html/css/cascade.js.map +1 -1
  118. package/dist/html/css/color.d.ts +23 -0
  119. package/dist/html/css/color.d.ts.map +1 -0
  120. package/dist/html/css/color.js +740 -0
  121. package/dist/html/css/color.js.map +1 -0
  122. package/dist/html/css/content.d.ts +92 -0
  123. package/dist/html/css/content.d.ts.map +1 -0
  124. package/dist/html/css/content.js +388 -0
  125. package/dist/html/css/content.js.map +1 -0
  126. package/dist/html/css/counter-styles.d.ts +76 -0
  127. package/dist/html/css/counter-styles.d.ts.map +1 -0
  128. package/dist/html/css/counter-styles.js +893 -0
  129. package/dist/html/css/counter-styles.js.map +1 -0
  130. package/dist/html/css/decode.d.ts +12 -0
  131. package/dist/html/css/decode.d.ts.map +1 -0
  132. package/dist/html/css/decode.js +116 -0
  133. package/dist/html/css/decode.js.map +1 -0
  134. package/dist/html/css/parse.d.ts +110 -10
  135. package/dist/html/css/parse.d.ts.map +1 -1
  136. package/dist/html/css/parse.js +1473 -180
  137. package/dist/html/css/parse.js.map +1 -1
  138. package/dist/html/css/style.d.ts +501 -15
  139. package/dist/html/css/style.d.ts.map +1 -1
  140. package/dist/html/css/style.js +3729 -206
  141. package/dist/html/css/style.js.map +1 -1
  142. package/dist/html/css/ua.d.ts.map +1 -1
  143. package/dist/html/css/ua.js +65 -16
  144. package/dist/html/css/ua.js.map +1 -1
  145. package/dist/html/css/values.d.ts +37 -3
  146. package/dist/html/css/values.d.ts.map +1 -1
  147. package/dist/html/css/values.js +122 -48
  148. package/dist/html/css/values.js.map +1 -1
  149. package/dist/html/css/vars.d.ts +52 -0
  150. package/dist/html/css/vars.d.ts.map +1 -0
  151. package/dist/html/css/vars.js +358 -0
  152. package/dist/html/css/vars.js.map +1 -0
  153. package/dist/html/dom.d.ts +30 -4
  154. package/dist/html/dom.d.ts.map +1 -1
  155. package/dist/html/dom.js +278 -17
  156. package/dist/html/dom.js.map +1 -1
  157. package/dist/html/fonts.d.ts +106 -0
  158. package/dist/html/fonts.d.ts.map +1 -0
  159. package/dist/html/fonts.js +565 -0
  160. package/dist/html/fonts.js.map +1 -0
  161. package/dist/html/gif.d.ts +15 -0
  162. package/dist/html/gif.d.ts.map +1 -0
  163. package/dist/html/gif.js +229 -0
  164. package/dist/html/gif.js.map +1 -0
  165. package/dist/html/index.d.ts +50 -11
  166. package/dist/html/index.d.ts.map +1 -1
  167. package/dist/html/index.js +115 -9
  168. package/dist/html/index.js.map +1 -1
  169. package/dist/html/layout/block.d.ts +226 -3
  170. package/dist/html/layout/block.d.ts.map +1 -1
  171. package/dist/html/layout/block.js +3077 -219
  172. package/dist/html/layout/block.js.map +1 -1
  173. package/dist/html/layout/boxes.d.ts +367 -17
  174. package/dist/html/layout/boxes.d.ts.map +1 -1
  175. package/dist/html/layout/boxes.js +1742 -182
  176. package/dist/html/layout/boxes.js.map +1 -1
  177. package/dist/html/layout/cache.d.ts +58 -0
  178. package/dist/html/layout/cache.d.ts.map +1 -0
  179. package/dist/html/layout/cache.js +224 -0
  180. package/dist/html/layout/cache.js.map +1 -0
  181. package/dist/html/layout/collapse.d.ts +45 -0
  182. package/dist/html/layout/collapse.d.ts.map +1 -0
  183. package/dist/html/layout/collapse.js +246 -0
  184. package/dist/html/layout/collapse.js.map +1 -0
  185. package/dist/html/layout/css-grid.d.ts +7 -0
  186. package/dist/html/layout/css-grid.d.ts.map +1 -0
  187. package/dist/html/layout/css-grid.js +900 -0
  188. package/dist/html/layout/css-grid.js.map +1 -0
  189. package/dist/html/layout/flex.d.ts.map +1 -1
  190. package/dist/html/layout/flex.js +1025 -89
  191. package/dist/html/layout/flex.js.map +1 -1
  192. package/dist/html/layout/floats.d.ts +45 -6
  193. package/dist/html/layout/floats.d.ts.map +1 -1
  194. package/dist/html/layout/floats.js +113 -14
  195. package/dist/html/layout/floats.js.map +1 -1
  196. package/dist/html/layout/grid-lines.d.ts +39 -0
  197. package/dist/html/layout/grid-lines.d.ts.map +1 -0
  198. package/dist/html/layout/grid-lines.js +119 -0
  199. package/dist/html/layout/grid-lines.js.map +1 -0
  200. package/dist/html/layout/grid.d.ts +53 -0
  201. package/dist/html/layout/grid.d.ts.map +1 -0
  202. package/dist/html/layout/grid.js +153 -0
  203. package/dist/html/layout/grid.js.map +1 -0
  204. package/dist/html/layout/inline.d.ts +154 -1
  205. package/dist/html/layout/inline.d.ts.map +1 -1
  206. package/dist/html/layout/inline.js +3598 -175
  207. package/dist/html/layout/inline.js.map +1 -1
  208. package/dist/html/layout/shaping.d.ts +6 -0
  209. package/dist/html/layout/shaping.d.ts.map +1 -0
  210. package/dist/html/layout/shaping.js +100 -0
  211. package/dist/html/layout/shaping.js.map +1 -0
  212. package/dist/html/layout/table.d.ts +3 -0
  213. package/dist/html/layout/table.d.ts.map +1 -1
  214. package/dist/html/layout/table.js +681 -175
  215. package/dist/html/layout/table.js.map +1 -1
  216. package/dist/html/layout/tracks.d.ts +44 -0
  217. package/dist/html/layout/tracks.d.ts.map +1 -0
  218. package/dist/html/layout/tracks.js +349 -0
  219. package/dist/html/layout/tracks.js.map +1 -0
  220. package/dist/html/node.d.ts +169 -4
  221. package/dist/html/node.d.ts.map +1 -1
  222. package/dist/html/node.js +1429 -137
  223. package/dist/html/node.js.map +1 -1
  224. package/dist/html/paint.d.ts +92 -3
  225. package/dist/html/paint.d.ts.map +1 -1
  226. package/dist/html/paint.js +3635 -107
  227. package/dist/html/paint.js.map +1 -1
  228. package/dist/html/resources.d.ts +60 -14
  229. package/dist/html/resources.d.ts.map +1 -1
  230. package/dist/html/resources.js +122 -36
  231. package/dist/html/resources.js.map +1 -1
  232. package/dist/html/surfaces.d.ts +23 -0
  233. package/dist/html/surfaces.d.ts.map +1 -0
  234. package/dist/html/surfaces.js +68 -0
  235. package/dist/html/surfaces.js.map +1 -0
  236. package/dist/html/svg.d.ts +80 -0
  237. package/dist/html/svg.d.ts.map +1 -0
  238. package/dist/html/svg.js +442 -0
  239. package/dist/html/svg.js.map +1 -0
  240. package/dist/html/url.d.ts +23 -0
  241. package/dist/html/url.d.ts.map +1 -0
  242. package/dist/html/url.js +80 -0
  243. package/dist/html/url.js.map +1 -0
  244. package/dist/index.d.ts +1 -1
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js.map +1 -1
  247. package/dist/internal/codelines.d.ts +42 -0
  248. package/dist/internal/codelines.d.ts.map +1 -0
  249. package/dist/internal/codelines.js +170 -0
  250. package/dist/internal/codelines.js.map +1 -0
  251. package/dist/internal/collate.d.ts +3 -0
  252. package/dist/internal/collate.d.ts.map +1 -0
  253. package/dist/internal/collate.js +11 -0
  254. package/dist/internal/collate.js.map +1 -0
  255. package/dist/internal/markdown/ast.d.ts +11 -0
  256. package/dist/internal/markdown/ast.d.ts.map +1 -1
  257. package/dist/internal/markdown/parse.d.ts.map +1 -1
  258. package/dist/internal/markdown/parse.js +109 -8
  259. package/dist/internal/markdown/parse.js.map +1 -1
  260. package/dist/internal/prewarm.d.ts +18 -0
  261. package/dist/internal/prewarm.d.ts.map +1 -0
  262. package/dist/internal/prewarm.js +42 -0
  263. package/dist/internal/prewarm.js.map +1 -0
  264. package/dist/internal/sdf.d.ts +20 -0
  265. package/dist/internal/sdf.d.ts.map +1 -0
  266. package/dist/internal/sdf.js +216 -0
  267. package/dist/internal/sdf.js.map +1 -0
  268. package/dist/internal/splice.d.ts +12 -0
  269. package/dist/internal/splice.d.ts.map +1 -0
  270. package/dist/internal/splice.js +23 -0
  271. package/dist/internal/splice.js.map +1 -0
  272. package/dist/internal/window.d.ts +12 -0
  273. package/dist/internal/window.d.ts.map +1 -1
  274. package/dist/internal/window.js +23 -6
  275. package/dist/internal/window.js.map +1 -1
  276. package/dist/maps/anchors.d.ts +6 -1
  277. package/dist/maps/anchors.d.ts.map +1 -1
  278. package/dist/maps/anchors.js +260 -1
  279. package/dist/maps/anchors.js.map +1 -1
  280. package/dist/maps/controller.d.ts +57 -0
  281. package/dist/maps/controller.d.ts.map +1 -1
  282. package/dist/maps/controller.js +97 -18
  283. package/dist/maps/controller.js.map +1 -1
  284. package/dist/maps/gl/buckets.d.ts +5 -1
  285. package/dist/maps/gl/buckets.d.ts.map +1 -1
  286. package/dist/maps/gl/buckets.js +5 -2
  287. package/dist/maps/gl/buckets.js.map +1 -1
  288. package/dist/maps/gl/build-worker.d.ts +2 -0
  289. package/dist/maps/gl/build-worker.d.ts.map +1 -1
  290. package/dist/maps/gl/build-worker.js +1 -1
  291. package/dist/maps/gl/build-worker.js.map +1 -1
  292. package/dist/maps/gl/cover.d.ts +23 -4
  293. package/dist/maps/gl/cover.d.ts.map +1 -1
  294. package/dist/maps/gl/cover.js +61 -48
  295. package/dist/maps/gl/cover.js.map +1 -1
  296. package/dist/maps/gl/placement.d.ts +21 -8
  297. package/dist/maps/gl/placement.d.ts.map +1 -1
  298. package/dist/maps/gl/placement.js +133 -58
  299. package/dist/maps/gl/placement.js.map +1 -1
  300. package/dist/maps/gl/renderer.d.ts +36 -4
  301. package/dist/maps/gl/renderer.d.ts.map +1 -1
  302. package/dist/maps/gl/renderer.js +81 -24
  303. package/dist/maps/gl/renderer.js.map +1 -1
  304. package/dist/maps/gl/shaders.d.ts +18 -17
  305. package/dist/maps/gl/shaders.d.ts.map +1 -1
  306. package/dist/maps/gl/shaders.js +34 -32
  307. package/dist/maps/gl/shaders.js.map +1 -1
  308. package/dist/maps/gl/store.d.ts +10 -0
  309. package/dist/maps/gl/store.d.ts.map +1 -1
  310. package/dist/maps/gl/store.js +47 -4
  311. package/dist/maps/gl/store.js.map +1 -1
  312. package/dist/maps/gl/text.d.ts +153 -24
  313. package/dist/maps/gl/text.d.ts.map +1 -1
  314. package/dist/maps/gl/text.js +379 -63
  315. package/dist/maps/gl/text.js.map +1 -1
  316. package/dist/maps/gl/view.d.ts +293 -2
  317. package/dist/maps/gl/view.d.ts.map +1 -1
  318. package/dist/maps/gl/view.js +317 -82
  319. package/dist/maps/gl/view.js.map +1 -1
  320. package/dist/maps/icons.d.ts +32 -0
  321. package/dist/maps/icons.d.ts.map +1 -0
  322. package/dist/maps/icons.js +151 -0
  323. package/dist/maps/icons.js.map +1 -0
  324. package/dist/maps/index.d.ts +2 -0
  325. package/dist/maps/index.d.ts.map +1 -1
  326. package/dist/maps/index.js +2 -0
  327. package/dist/maps/index.js.map +1 -1
  328. package/dist/maps/labels.d.ts +56 -4
  329. package/dist/maps/labels.d.ts.map +1 -1
  330. package/dist/maps/labels.js +213 -46
  331. package/dist/maps/labels.js.map +1 -1
  332. package/dist/maps/node.d.ts +17 -0
  333. package/dist/maps/node.d.ts.map +1 -1
  334. package/dist/maps/node.js +64 -30
  335. package/dist/maps/node.js.map +1 -1
  336. package/dist/maps/proj.d.ts +48 -0
  337. package/dist/maps/proj.d.ts.map +1 -1
  338. package/dist/maps/proj.js +73 -0
  339. package/dist/maps/proj.js.map +1 -1
  340. package/dist/maps/sources.d.ts +5 -3
  341. package/dist/maps/sources.d.ts.map +1 -1
  342. package/dist/maps/sources.js +5 -3
  343. package/dist/maps/sources.js.map +1 -1
  344. package/dist/maps/style.d.ts +35 -0
  345. package/dist/maps/style.d.ts.map +1 -1
  346. package/dist/maps/style.js.map +1 -1
  347. package/dist/maps/styles.d.ts +12 -0
  348. package/dist/maps/styles.d.ts.map +1 -1
  349. package/dist/maps/styles.js +120 -6
  350. package/dist/maps/styles.js.map +1 -1
  351. package/dist/maps/tiles.d.ts +31 -11
  352. package/dist/maps/tiles.d.ts.map +1 -1
  353. package/dist/maps/tiles.js +61 -27
  354. package/dist/maps/tiles.js.map +1 -1
  355. package/dist/maps/types.d.ts +28 -4
  356. package/dist/maps/types.d.ts.map +1 -1
  357. package/dist/markdown/index.d.ts.map +1 -1
  358. package/dist/markdown/index.js +209 -68
  359. package/dist/markdown/index.js.map +1 -1
  360. package/dist/markdown/spans.d.ts.map +1 -1
  361. package/dist/markdown/spans.js +6 -0
  362. package/dist/markdown/spans.js.map +1 -1
  363. package/dist/qml/containers.d.ts.map +1 -1
  364. package/dist/qml/containers.js +6 -1
  365. package/dist/qml/containers.js.map +1 -1
  366. package/dist/qml/interaction.d.ts.map +1 -1
  367. package/dist/qml/interaction.js +38 -5
  368. package/dist/qml/interaction.js.map +1 -1
  369. package/dist/qml/layouts.d.ts.map +1 -1
  370. package/dist/qml/layouts.js +21 -6
  371. package/dist/qml/layouts.js.map +1 -1
  372. package/dist/qml/qtquick.d.ts.map +1 -1
  373. package/dist/qml/qtquick.js +9 -2
  374. package/dist/qml/qtquick.js.map +1 -1
  375. package/dist/qml/react.d.ts.map +1 -1
  376. package/dist/qml/react.js +9 -7
  377. package/dist/qml/react.js.map +1 -1
  378. package/dist/reorder/clock.d.ts +7 -0
  379. package/dist/reorder/clock.d.ts.map +1 -0
  380. package/dist/reorder/clock.js +26 -0
  381. package/dist/reorder/clock.js.map +1 -0
  382. package/dist/reorder/index.d.ts +25 -3
  383. package/dist/reorder/index.d.ts.map +1 -1
  384. package/dist/reorder/index.js +64 -19
  385. package/dist/reorder/index.js.map +1 -1
  386. package/dist/rich-text-editor/index.d.ts +0 -11
  387. package/dist/rich-text-editor/index.d.ts.map +1 -1
  388. package/dist/rich-text-editor/index.js +38 -9
  389. package/dist/rich-text-editor/index.js.map +1 -1
  390. package/dist/rich-text-editor/keymap.d.ts.map +1 -1
  391. package/dist/rich-text-editor/keymap.js +2 -1
  392. package/dist/rich-text-editor/keymap.js.map +1 -1
  393. package/dist/rich-text-editor/keys.d.ts +17 -6
  394. package/dist/rich-text-editor/keys.d.ts.map +1 -1
  395. package/dist/rich-text-editor/keys.js +135 -26
  396. package/dist/rich-text-editor/keys.js.map +1 -1
  397. package/dist/rich-text-editor/markdown.d.ts.map +1 -1
  398. package/dist/rich-text-editor/markdown.js +30 -1
  399. package/dist/rich-text-editor/markdown.js.map +1 -1
  400. package/dist/rich-text-editor/marks.d.ts +9 -0
  401. package/dist/rich-text-editor/marks.d.ts.map +1 -0
  402. package/dist/rich-text-editor/marks.js +83 -0
  403. package/dist/rich-text-editor/marks.js.map +1 -0
  404. package/dist/rich-text-editor/render.d.ts.map +1 -1
  405. package/dist/rich-text-editor/render.js +28 -10
  406. package/dist/rich-text-editor/render.js.map +1 -1
  407. package/dist/rich-text-editor/replace.d.ts +13 -0
  408. package/dist/rich-text-editor/replace.d.ts.map +1 -0
  409. package/dist/rich-text-editor/replace.js +64 -0
  410. package/dist/rich-text-editor/replace.js.map +1 -0
  411. package/dist/rich-text-editor/toolbar.d.ts.map +1 -1
  412. package/dist/rich-text-editor/toolbar.js +1 -1
  413. package/dist/rich-text-editor/toolbar.js.map +1 -1
  414. package/dist/rich-text-editor/virtual.d.ts.map +1 -1
  415. package/dist/rich-text-editor/virtual.js +17 -0
  416. package/dist/rich-text-editor/virtual.js.map +1 -1
  417. package/dist/richtext/node.d.ts +46 -2
  418. package/dist/richtext/node.d.ts.map +1 -1
  419. package/dist/richtext/node.js +187 -4
  420. package/dist/richtext/node.js.map +1 -1
  421. package/dist/richtext/runs.d.ts +13 -8
  422. package/dist/richtext/runs.d.ts.map +1 -1
  423. package/dist/richtext/runs.js +18 -8
  424. package/dist/richtext/runs.js.map +1 -1
  425. package/dist/table/index.d.ts.map +1 -1
  426. package/dist/table/index.js +23 -0
  427. package/dist/table/index.js.map +1 -1
  428. package/dist/table/rows.d.ts +3 -2
  429. package/dist/table/rows.d.ts.map +1 -1
  430. package/dist/table/rows.js +20 -6
  431. package/dist/table/rows.js.map +1 -1
  432. package/dist/terminal/index.d.ts.map +1 -1
  433. package/dist/terminal/index.js +4 -0
  434. package/dist/terminal/index.js.map +1 -1
  435. package/dist/terminal/vt/pty.d.ts +13 -2
  436. package/dist/terminal/vt/pty.d.ts.map +1 -1
  437. package/dist/terminal/vt/pty.js +18 -3
  438. package/dist/terminal/vt/pty.js.map +1 -1
  439. package/dist/terminal-output/index.d.ts.map +1 -1
  440. package/dist/terminal-output/index.js +103 -23
  441. package/dist/terminal-output/index.js.map +1 -1
  442. package/dist/terminal-output/runs.d.ts +4 -1
  443. package/dist/terminal-output/runs.d.ts.map +1 -1
  444. package/dist/terminal-output/runs.js +5 -1
  445. package/dist/terminal-output/runs.js.map +1 -1
  446. package/dist/three/canvas.d.ts.map +1 -1
  447. package/dist/three/canvas.js +85 -7
  448. package/dist/three/canvas.js.map +1 -1
  449. package/dist/three/events.d.ts +22 -4
  450. package/dist/three/events.d.ts.map +1 -1
  451. package/dist/three/events.js +34 -4
  452. package/dist/three/events.js.map +1 -1
  453. package/dist/tree/index.d.ts.map +1 -1
  454. package/dist/tree/index.js +17 -0
  455. package/dist/tree/index.js.map +1 -1
  456. package/package.json +42 -33
  457. package/src/charts/node.ts +62 -26
  458. package/src/charts/render.ts +120 -30
  459. package/src/code/index.ts +36 -32
  460. package/src/code-editor/completion.ts +3 -2
  461. package/src/code-editor/index.ts +19 -0
  462. package/src/code-editor/node.ts +1574 -184
  463. package/src/code-language/index.ts +1 -1
  464. package/src/code-language/languages/glsl.ts +12 -2
  465. package/src/code-language/languages/javascript.ts +21 -4
  466. package/src/code-language/languages/json.ts +12 -2
  467. package/src/code-language/languages/shell.ts +12 -2
  468. package/src/code-language/languages/sql.ts +22 -2
  469. package/src/code-language/runs.ts +111 -1
  470. package/src/code-language/stream.ts +257 -17
  471. package/src/code-language/types.ts +5 -4
  472. package/src/codeblock/index.ts +68 -4
  473. package/src/flow/draw.ts +167 -41
  474. package/src/flow/gl/color.ts +85 -0
  475. package/src/flow/gl/index.ts +342 -0
  476. package/src/flow/gl/pack.ts +585 -0
  477. package/src/flow/gl/renderer.ts +679 -0
  478. package/src/flow/gl/shaders.ts +371 -0
  479. package/src/flow/gl/text.ts +1295 -0
  480. package/src/flow/index.ts +794 -67
  481. package/src/flow/node.ts +2496 -1256
  482. package/src/flow/paint.ts +511 -0
  483. package/src/flow/paths.ts +80 -15
  484. package/src/flow/scene.ts +2199 -0
  485. package/src/flow/types.ts +113 -1
  486. package/src/html/controls.ts +73 -2
  487. package/src/html/css/calc.ts +385 -0
  488. package/src/html/css/cascade.ts +1481 -62
  489. package/src/html/css/color.ts +755 -0
  490. package/src/html/css/content.ts +417 -0
  491. package/src/html/css/counter-styles.ts +973 -0
  492. package/src/html/css/decode.ts +130 -0
  493. package/src/html/css/parse.ts +1514 -160
  494. package/src/html/css/style.ts +4131 -223
  495. package/src/html/css/ua.ts +66 -17
  496. package/src/html/css/values.ts +150 -50
  497. package/src/html/css/vars.ts +348 -0
  498. package/src/html/dom.ts +284 -17
  499. package/src/html/fonts.ts +641 -0
  500. package/src/html/gif.ts +233 -0
  501. package/src/html/index.ts +180 -20
  502. package/src/html/layout/block.ts +3560 -208
  503. package/src/html/layout/boxes.ts +2141 -190
  504. package/src/html/layout/cache.ts +289 -0
  505. package/src/html/layout/collapse.ts +317 -0
  506. package/src/html/layout/css-grid.ts +1063 -0
  507. package/src/html/layout/flex.ts +1115 -106
  508. package/src/html/layout/floats.ts +128 -14
  509. package/src/html/layout/grid-lines.ts +141 -0
  510. package/src/html/layout/grid.ts +181 -0
  511. package/src/html/layout/inline.ts +4187 -192
  512. package/src/html/layout/shaping.ts +140 -0
  513. package/src/html/layout/table.ts +696 -186
  514. package/src/html/layout/tracks.ts +437 -0
  515. package/src/html/node.ts +1585 -156
  516. package/src/html/paint.ts +4582 -267
  517. package/src/html/resources.ts +161 -50
  518. package/src/html/surfaces.ts +81 -0
  519. package/src/html/svg.ts +504 -0
  520. package/src/html/url.ts +86 -0
  521. package/src/index.ts +2 -0
  522. package/src/internal/codelines.ts +200 -0
  523. package/src/internal/collate.ts +12 -0
  524. package/src/internal/markdown/ast.ts +11 -0
  525. package/src/internal/markdown/parse.ts +154 -11
  526. package/src/internal/prewarm.ts +45 -0
  527. package/src/internal/sdf.ts +227 -0
  528. package/src/internal/splice.ts +25 -0
  529. package/src/internal/window.ts +36 -5
  530. package/src/maps/anchors.ts +307 -1
  531. package/src/maps/controller.ts +116 -31
  532. package/src/maps/gl/buckets.ts +5 -1
  533. package/src/maps/gl/build-worker.ts +13 -2
  534. package/src/maps/gl/cover.ts +79 -44
  535. package/src/maps/gl/placement.ts +221 -69
  536. package/src/maps/gl/renderer.ts +104 -20
  537. package/src/maps/gl/shaders.ts +35 -32
  538. package/src/maps/gl/store.ts +66 -17
  539. package/src/maps/gl/text.ts +444 -71
  540. package/src/maps/gl/view.ts +361 -96
  541. package/src/maps/icons.ts +183 -0
  542. package/src/maps/index.ts +4 -0
  543. package/src/maps/labels.ts +269 -49
  544. package/src/maps/node.ts +82 -49
  545. package/src/maps/proj.ts +87 -0
  546. package/src/maps/sources.ts +5 -3
  547. package/src/maps/style.ts +32 -0
  548. package/src/maps/styles.ts +215 -4
  549. package/src/maps/tiles.ts +64 -31
  550. package/src/maps/types.ts +28 -4
  551. package/src/markdown/index.ts +293 -92
  552. package/src/markdown/spans.ts +5 -0
  553. package/src/qml/containers.tsx +6 -1
  554. package/src/qml/interaction.tsx +42 -5
  555. package/src/qml/layouts.tsx +21 -6
  556. package/src/qml/qtquick.tsx +9 -2
  557. package/src/qml/react.tsx +7 -5
  558. package/src/reorder/clock.ts +27 -0
  559. package/src/reorder/index.ts +100 -27
  560. package/src/rich-text-editor/index.ts +37 -12
  561. package/src/rich-text-editor/keymap.ts +1 -1
  562. package/src/rich-text-editor/keys.ts +135 -26
  563. package/src/rich-text-editor/markdown.ts +36 -1
  564. package/src/rich-text-editor/marks.ts +101 -0
  565. package/src/rich-text-editor/render.ts +49 -12
  566. package/src/rich-text-editor/replace.ts +85 -0
  567. package/src/rich-text-editor/toolbar.ts +1 -1
  568. package/src/rich-text-editor/virtual.ts +25 -0
  569. package/src/richtext/node.ts +235 -14
  570. package/src/richtext/runs.ts +23 -12
  571. package/src/table/index.ts +29 -0
  572. package/src/table/rows.ts +18 -8
  573. package/src/terminal/index.ts +4 -0
  574. package/src/terminal/vt/pty.ts +16 -2
  575. package/src/terminal-output/index.ts +129 -31
  576. package/src/terminal-output/runs.ts +16 -2
  577. package/src/three/canvas.ts +102 -8
  578. package/src/three/events.ts +49 -9
  579. package/src/tree/index.ts +19 -0
@@ -14,153 +14,785 @@
14
14
  // Selector *matching* is still `css-select`'s (see cascade.ts). Parsing a
15
15
  // stylesheet is easy; matching `li:nth-child(2n+1) > a[href^="/"]` correctly
16
16
  // and quickly is not, and that is the part worth importing.
17
+ import { resolveUrl } from '../url.js';
17
18
  import { parseLength } from './values.js';
19
+ import { hasVar, unbalanced, validVars } from './vars.js';
18
20
  const IMPORTANT_RE = /!\s*important\s*$/i;
21
+ /** A layer's name: identifiers joined by dots. */
22
+ const LAYER_NAME = /^-?[_a-zA-Z\u00a0-\uffff][\w\u00a0-\uffff-]*(?:\.-?[_a-zA-Z\u00a0-\uffff][\w\u00a0-\uffff-]*)*$/;
23
+ /** The at-rules that are rules with a block — which an `@import` after them
24
+ * is too late for. */
25
+ const BLOCK_AT_RULES = new Set([
26
+ 'media',
27
+ 'supports',
28
+ 'font-face',
29
+ 'page',
30
+ 'keyframes',
31
+ '-webkit-keyframes',
32
+ 'counter-style',
33
+ 'layer',
34
+ 'container',
35
+ 'property',
36
+ 'font-feature-values',
37
+ ]);
19
38
  /**
20
39
  * Parse a stylesheet. `order` continues across sheets, so the caller passes
21
40
  * a running counter and the cascade's document-order tie-break holds across
22
41
  * the whole document rather than restarting per `<style>`.
42
+ *
43
+ * The structure is CSS Syntax's: an at-rule is an at-keyword up to its `;`
44
+ * or its block, a style rule is everything up to its block, and what lies
45
+ * between is read a component value at a time — a string, an escape, a
46
+ * bracketed block with its match — so a brace or a semicolon in a string, in
47
+ * an escape or inside brackets never ends anything. A rule whose selector is
48
+ * not one is dropped whole, and so is a group with any selector in it that
49
+ * is not one (CSS 2.1 4.1.7).
23
50
  */
24
- export function parseStylesheet(text, startOrder = 0) {
25
- const sheet = { rules: [], imports: [], breakpoints: [] };
51
+ export function parseStylesheet(text, startOrder = 0,
52
+ /** The document's layers, by their full names, ranked in the order the
53
+ * document first names them: one map across all of its sheets, since a
54
+ * layer one sheet names first is the same layer in the next. */
55
+ layers = new Map(),
56
+ /** The URL the sheet's own relative URLs resolve against — its own, or
57
+ * the document's for a `<style>` — or null to leave them as written.
58
+ * Every `url()`, `@import` and `@font-face` source comes out absolute. */
59
+ base = null) {
60
+ text = withoutComments(text);
61
+ const sheet = {
62
+ rules: [],
63
+ imports: [],
64
+ breakpoints: [],
65
+ fontFaces: [],
66
+ };
26
67
  let order = startOrder;
27
68
  const breakpoints = new Set();
28
- const walk = (source, media) => {
69
+ // `@import` counts only ahead of every other rule, `@charset` aside
70
+ let importsAllowed = true;
71
+ /** A layer's rank, named for the first time if it has not been. */
72
+ const rankOf = (name) => {
73
+ let rank = layers.get(name);
74
+ if (rank === undefined) {
75
+ rank = layers.size;
76
+ layers.set(name, rank);
77
+ }
78
+ return rank;
79
+ };
80
+ /** The path of ranks down to a layer, `a.b` inside `outer` being three
81
+ * levels, and the layer's full name. */
82
+ const enter = (name, layer, path) => {
83
+ const ranks = layer ? [...layer] : [];
84
+ let full = path;
85
+ for (const part of name.split('.')) {
86
+ full = full ? `${full}.${part}` : part;
87
+ ranks.push(rankOf(full));
88
+ }
89
+ return [ranks, full];
90
+ };
91
+ let anonymous = 0;
92
+ const walk = (source, media, layer = null, path = '') => {
29
93
  let i = 0;
30
94
  const n = source.length;
31
95
  while (i < n) {
32
96
  i = skipTrivia(source, i);
33
97
  if (i >= n)
34
98
  break;
35
- if (source[i] === '@') {
99
+ if (source[i] === '@' && startsIdent(source, i + 1)) {
36
100
  const at = readAtRule(source, i);
37
101
  i = at.end;
38
102
  const name = at.name.toLowerCase();
103
+ if (name === 'charset')
104
+ continue;
39
105
  if (name === 'import') {
40
- const url = importUrl(at.prelude);
41
- if (url)
42
- sheet.imports.push(url);
106
+ if (importsAllowed && media === null) {
107
+ const url = importUrl(at.prelude);
108
+ if (url && importApplies(at.prelude)) {
109
+ sheet.imports.push(resolveUrl(url, base));
110
+ }
111
+ }
112
+ continue;
113
+ }
114
+ // Only a rule that is one closes the imports: `@media;`, `@page;`
115
+ // and an at-rule nobody knows are dropped as though never written.
116
+ if (at.block !== null && BLOCK_AT_RULES.has(name)) {
117
+ importsAllowed = false;
43
118
  }
44
- else if (name === 'media' && at.block !== null) {
119
+ if (name === 'media' && at.block !== null) {
45
120
  const conditions = parseMediaQuery(at.prelude);
46
121
  for (const c of conditions) {
47
122
  if (c.min !== undefined)
48
123
  breakpoints.add(c.min);
49
124
  if (c.max !== undefined)
50
- breakpoints.add(c.max + 1);
125
+ breakpoints.add(c.max + MAX_EDGE);
51
126
  }
52
127
  // A nested `@media` intersects with the one above it; pushing a
53
128
  // level rather than merging keeps "all of these blocks hold" exact
54
129
  // when two of them overlap.
55
- walk(at.block, media ? [...media, conditions] : [conditions]);
130
+ walk(at.block, media ? [...media, conditions] : [conditions], layer, path);
56
131
  }
57
132
  else if (name === 'supports' && at.block !== null) {
58
133
  // Everything in a `@supports` block is markup this renderer either
59
134
  // understands or ignores per-declaration, so entering it is closer
60
135
  // to right than skipping it.
61
- walk(at.block, media);
136
+ walk(at.block, media, layer, path);
137
+ }
138
+ else if (name === 'layer') {
139
+ // `@layer a, b;` names layers, and so fixes their order, and
140
+ // `@layer a { … }` puts rules in one; one with no name is a layer
141
+ // of its own. Tailwind 4 writes all of its CSS in four of them.
142
+ const names = at.prelude
143
+ .split(',')
144
+ .map((part) => part.trim())
145
+ .filter(Boolean);
146
+ if (!names.every((part) => LAYER_NAME.test(part)))
147
+ continue;
148
+ if (at.block === null) {
149
+ for (const part of names)
150
+ enter(part, layer, path);
151
+ }
152
+ else if (names.length <= 1) {
153
+ const [ranks, full] = enter(names[0] ?? `\u0000${anonymous++}`, layer, path);
154
+ walk(at.block, media, ranks, full);
155
+ }
156
+ }
157
+ else if (name === 'font-face' && at.block !== null) {
158
+ const face = parseFontFace(at.block, base, media);
159
+ if (face)
160
+ sheet.fontFaces.push(face);
161
+ }
162
+ else if (name === 'counter-style' && at.block !== null) {
163
+ (sheet.counterStyles ??= []).push({
164
+ prelude: at.prelude,
165
+ declarations: parseDeclarations(at.block),
166
+ });
62
167
  }
63
- // @font-face, @keyframes, @page, @charset: nothing to do, and the
64
- // block was already consumed.
168
+ // @keyframes, @page: nothing to do, and the block was already
169
+ // consumed.
65
170
  continue;
66
171
  }
67
- const braceAt = findBrace(source, i);
68
- if (braceAt < 0)
172
+ // A style rule runs to its block, whatever comes first: a stray `;`
173
+ // or an `@` that names nothing is part of its selector, which then is
174
+ // not one, and the rule goes with it.
175
+ const blockAt = scanTo(source, i, '{');
176
+ if (blockAt >= n)
69
177
  break;
70
- const prelude = source.slice(i, braceAt).trim();
71
- const block = readBlock(source, braceAt);
178
+ const prelude = source.slice(i, blockAt).trim();
179
+ const block = readBlock(source, blockAt);
72
180
  i = block.end;
73
- if (!prelude)
181
+ const selectors = rawSelectors(prelude);
182
+ if (!selectors)
74
183
  continue;
75
- const declarations = parseDeclarations(block.body);
76
- if (!declarations.length)
184
+ importsAllowed = false;
185
+ styleRule(selectors, block.body, media, layer, path, 0);
186
+ }
187
+ };
188
+ /**
189
+ * A style rule, and the rules nested in it (CSS Nesting 1): a rule inside
190
+ * a rule's block is relative to it, `&` standing for it and a selector
191
+ * without one a descendant of it, and an `@media`, `@supports` or
192
+ * `@layer` inside one holds declarations for the same selectors, under
193
+ * its condition. Tailwind 4 writes its variants so — `md:flex` is
194
+ * `.md\:flex { @media (width >= 48rem) { display: flex } }`, and
195
+ * `hover:` is `&:hover` — and they were dropped with the rule. Each
196
+ * nested rule comes after its parent's declarations in the cascade's
197
+ * order, as it comes after them in the sheet.
198
+ */
199
+ const styleRule = (selectors, body, media, layer, path, depth) => {
200
+ // the plain case, a block with nothing nested in it
201
+ if (!body.includes('{')) {
202
+ emit(selectors, parseDeclarations(body), media, layer);
203
+ return;
204
+ }
205
+ const { declarations, nested } = splitNested(body);
206
+ emit(selectors, parseDeclarations(declarations), media, layer);
207
+ if (depth >= MAX_NESTING)
208
+ return;
209
+ for (const item of nested) {
210
+ if (item.kind === 'rule') {
211
+ const inner = nestedSelectors(item.prelude, selectors);
212
+ if (inner)
213
+ styleRule(inner, item.body, media, layer, path, depth + 1);
77
214
  continue;
78
- for (const selector of splitSelectors(prelude)) {
79
- if (!selector)
80
- continue;
81
- sheet.rules.push({
82
- selector,
83
- specificity: specificityOf(selector),
84
- order: order++,
85
- declarations,
86
- media,
87
- });
88
215
  }
216
+ const name = item.name.toLowerCase();
217
+ if (item.block === null)
218
+ continue;
219
+ if (name === 'media') {
220
+ const conditions = parseMediaQuery(item.prelude);
221
+ for (const c of conditions) {
222
+ if (c.min !== undefined)
223
+ breakpoints.add(c.min);
224
+ if (c.max !== undefined)
225
+ breakpoints.add(c.max + MAX_EDGE);
226
+ }
227
+ styleRule(selectors, item.block, media ? [...media, conditions] : [conditions], layer, path, depth + 1);
228
+ }
229
+ else if (name === 'supports') {
230
+ styleRule(selectors, item.block, media, layer, path, depth + 1);
231
+ }
232
+ else if (name === 'layer' && LAYER_NAME.test(item.prelude.trim())) {
233
+ const [ranks, full] = enter(item.prelude.trim(), layer, path);
234
+ styleRule(selectors, item.block, media, ranks, full, depth + 1);
235
+ }
236
+ }
237
+ };
238
+ const emit = (selectors, declarations, media, layer) => {
239
+ if (!declarations.length)
240
+ return;
241
+ if (base !== null) {
242
+ for (const d of declarations)
243
+ d.value = absoluteUrls(d.value, base);
244
+ }
245
+ for (const raw of selectors) {
246
+ const selector = raw.includes('\\') ? forMatcher(raw) : raw;
247
+ sheet.rules.push({
248
+ selector,
249
+ specificity: specificityOf(selector),
250
+ order: order++,
251
+ declarations,
252
+ media,
253
+ layer,
254
+ });
89
255
  }
90
256
  };
91
257
  walk(text, null);
92
258
  sheet.breakpoints = [...breakpoints].sort((a, b) => a - b);
93
259
  return sheet;
94
260
  }
261
+ /**
262
+ * A style sheet with its comments taken out, strings left alone. CSS drops a
263
+ * comment wherever it stands (CSS 2.1 4.1.9) — between rules, which the
264
+ * scanner already skipped, but also inside a selector: `div /* note *\/ {`
265
+ * kept the note in the selector, the matcher refused it, and the rule was
266
+ * dropped whole. In a selector a comment is replaced by nothing, as the
267
+ * tokenizer does: `.a/**\/.b` is one compound selector. In a declaration
268
+ * it is replaced by a space, since there it still ends the token before it:
269
+ * `1/**\/0px` is two values and no length, and `-/**\/10px` is no
270
+ * negative one. `decls` says the text starts inside a declaration block,
271
+ * which a `style` attribute does.
272
+ */
273
+ function withoutComments(text, decls = false) {
274
+ if (!text.includes('/*'))
275
+ return text;
276
+ let out = '';
277
+ let from = 0;
278
+ let quote = '';
279
+ // for each open block, whether it holds declarations rather than rules
280
+ const blocks = [];
281
+ let prelude = 0;
282
+ // whether the rule being read is an at-rule, once its first token says
283
+ let atRule = null;
284
+ for (let i = 0; i < text.length; i += 1) {
285
+ const c = text[i];
286
+ if (quote) {
287
+ if (c === '\\')
288
+ i = escapeEnd(text, i) - 1;
289
+ else if (c === quote || c === '\n')
290
+ quote = '';
291
+ continue;
292
+ }
293
+ if (atRule === null && !isSpace(c) && !(c === '/' && text[i + 1] === '*')) {
294
+ atRule = c === '@';
295
+ }
296
+ if (c === '\\') {
297
+ // an escape: `\/*` is a slash and an asterisk, not a comment
298
+ i += 1;
299
+ }
300
+ else if (c === '"' || c === "'") {
301
+ quote = c;
302
+ }
303
+ else if ((c === 'u' || c === 'U') &&
304
+ text[i + 3] === '(' &&
305
+ text.slice(i, i + 3).toLowerCase() === 'url' &&
306
+ !isNameChar(text[i - 1])) {
307
+ // an unquoted url is one token, `/*` and all: `url(a/*b)` is no
308
+ // comment, and taken for one it ran on through the rest of the sheet
309
+ i = urlEnd(text, i + 3) - 1;
310
+ }
311
+ else if (c === '{') {
312
+ const inDecls = blocks.length ? blocks[blocks.length - 1] : decls;
313
+ blocks.push(inDecls || !opensRuleList(text, prelude, i));
314
+ prelude = i + 1;
315
+ atRule = null;
316
+ }
317
+ else if (c === '}') {
318
+ blocks.pop();
319
+ prelude = i + 1;
320
+ atRule = null;
321
+ }
322
+ else if (c === ';') {
323
+ prelude = i + 1;
324
+ atRule = null;
325
+ }
326
+ else if (c === '/' && text[i + 1] === '*') {
327
+ const close = text.indexOf('*/', i + 2);
328
+ const inDecls = blocks.length ? blocks[blocks.length - 1] : decls;
329
+ // `@media/**\/all` is `@media all`
330
+ out += text.slice(from, i) + (inDecls || atRule ? ' ' : '');
331
+ i = close < 0 ? text.length : close + 1;
332
+ from = i + 1;
333
+ }
334
+ }
335
+ return out + text.slice(from);
336
+ }
337
+ /** Whether the block a `{` at `end` opens holds rules rather than
338
+ * declarations: a conditional or grouping at-rule's does. */
339
+ function opensRuleList(text, from, end) {
340
+ let j = from;
341
+ while (j < end && (isSpace(text[j]) || text[j] === '/')) {
342
+ if (text[j] === '/' && text[j + 1] === '*') {
343
+ const close = text.indexOf('*/', j + 2);
344
+ j = close < 0 ? end : close + 2;
345
+ }
346
+ else if (text[j] === '/') {
347
+ break;
348
+ }
349
+ else {
350
+ j += 1;
351
+ }
352
+ }
353
+ return text[j] === '@' && RULE_LIST_AT.test(text.slice(j + 1, end));
354
+ }
355
+ const RULE_LIST_AT = /^(?:media|supports|document|-moz-document|layer|container|scope|starting-style)(?![\w-])/i;
95
356
  /**
96
357
  * Parse a declaration block — also the parser for a `style=""` attribute,
97
- * which is the same grammar with no braces around it.
358
+ * which is the same grammar with no braces around it. CSS Syntax's list of
359
+ * declarations: a name, a colon, and everything up to the next `;` that no
360
+ * string or bracket holds; an at-rule in the list is read to its `;` or its
361
+ * block and dropped; anything else is dropped to the next `;`. A value with
362
+ * a `!` left in it once `!important` is taken off is not a value.
98
363
  */
99
364
  export function parseDeclarations(text) {
365
+ // a `style` attribute comes here without the sheet's pass over comments
366
+ text = withoutComments(text, true);
100
367
  const out = [];
101
368
  let i = 0;
102
369
  const n = text.length;
103
370
  while (i < n) {
104
- i = skipTrivia(text, i);
105
- if (i >= n)
106
- break;
107
- // A nested block inside a declaration list is something this does not
108
- // understand (a nested rule, an `@media` inside a rule); skip it whole
109
- // rather than reading its declarations as if they were the parent's.
110
- const colon = findChar(text, i, ':', ';{');
111
- if (colon < 0) {
112
- const semi = findChar(text, i, ';', '');
113
- if (semi < 0)
114
- break;
115
- i = semi + 1;
371
+ const c = text[i];
372
+ if (isSpace(c) || c === ';') {
373
+ i += 1;
116
374
  continue;
117
375
  }
118
- const prop = text.slice(i, colon).trim().toLowerCase();
119
- const valueEnd = findChar(text, colon + 1, ';', '');
120
- const end = valueEnd < 0 ? n : valueEnd;
121
- let value = text.slice(colon + 1, end).trim();
122
- i = end + 1;
123
- if (!prop || !value)
376
+ if (c === '@' && startsIdent(text, i + 1)) {
377
+ i = readAtRule(text, i).end;
124
378
  continue;
125
- const important = IMPORTANT_RE.test(value);
126
- if (important)
127
- value = value
128
- .replace(IMPORTANT_RE, '')
129
- .replace(/\s*!\s*$/, '')
130
- .trim();
131
- if (!value)
132
- continue;
133
- out.push({ prop, value, important });
379
+ }
380
+ const end = scanTo(text, i, ';');
381
+ if (startsIdent(text, i)) {
382
+ const name = readIdent(text, i);
383
+ let colon = name.end;
384
+ while (colon < end && isSpace(text[colon]))
385
+ colon += 1;
386
+ if (text[colon] === ':') {
387
+ const raw = text.slice(colon + 1, end);
388
+ let value = raw.trim();
389
+ const important = IMPORTANT_RE.test(value);
390
+ if (important)
391
+ value = value.replace(IMPORTANT_RE, '').trim();
392
+ const custom = name.value.startsWith('--');
393
+ // `--` alone is no custom property's name, and a bracket closed
394
+ // that nothing opened makes a value none
395
+ if ((value || custom) &&
396
+ !hasBang(value) &&
397
+ name.value !== '--' &&
398
+ !(custom && unbalanced(value))) {
399
+ const declaration = {
400
+ // a custom property's name is case-sensitive
401
+ prop: custom ? name.value : name.value.toLowerCase(),
402
+ value: unescapeValue(value),
403
+ important,
404
+ };
405
+ if (custom)
406
+ declaration.custom = true;
407
+ if (hasVar(value)) {
408
+ // a `var()` that is none is a declaration that is none — read
409
+ // untrimmed, since a newline in a string the end left open
410
+ // makes a bad string, where the end alone closes it
411
+ if (!validVars(raw)) {
412
+ i = end + 1;
413
+ continue;
414
+ }
415
+ declaration.vars = true;
416
+ }
417
+ out.push(declaration);
418
+ }
419
+ }
420
+ }
421
+ i = end + 1;
134
422
  }
135
423
  return out;
136
424
  }
137
425
  /** Split `a, b > c, d` on top-level commas. */
138
426
  export function splitSelectors(prelude) {
139
427
  const out = [];
140
- let depth = 0;
141
- let quote = '';
142
428
  let start = 0;
143
- for (let i = 0; i < prelude.length; i += 1) {
144
- const c = prelude[i];
145
- if (quote) {
146
- if (c === quote && prelude[i - 1] !== '\\')
147
- quote = '';
148
- continue;
149
- }
150
- if (c === '"' || c === "'")
151
- quote = c;
152
- else if (c === '(' || c === '[')
153
- depth += 1;
154
- else if (c === ')' || c === ']')
155
- depth = Math.max(0, depth - 1);
156
- else if (c === ',' && depth === 0) {
429
+ let i = 0;
430
+ while (i < prelude.length) {
431
+ if (prelude[i] === ',') {
157
432
  out.push(prelude.slice(start, i).trim());
158
433
  start = i + 1;
434
+ i += 1;
435
+ }
436
+ else {
437
+ i = opens(prelude.charCodeAt(i)) ? componentEnd(prelude, i) : i + 1;
159
438
  }
160
439
  }
161
440
  out.push(prelude.slice(start).trim());
162
441
  return out.filter(Boolean);
163
442
  }
443
+ /**
444
+ * A rule's selectors, or null where any of them is not a selector — an
445
+ * empty one in the list, an identifier that starts with a digit, a
446
+ * combinator with nothing after it — and the rule is dropped whole.
447
+ */
448
+ export function selectorList(prelude) {
449
+ const raw = rawSelectors(prelude);
450
+ return raw && raw.map((s) => (s.includes('\\') ? forMatcher(s) : s));
451
+ }
452
+ /** A rule's selectors as written, or null where any of them is not one:
453
+ * what `selectorList` hands the matcher before its escapes are read, and
454
+ * what a nested rule's `&` stands for. */
455
+ function rawSelectors(prelude) {
456
+ if (!prelude)
457
+ return null;
458
+ const out = [];
459
+ let start = 0;
460
+ let i = 0;
461
+ const push = (end) => {
462
+ const selector = prelude.slice(start, end).trim();
463
+ if (!selector || !isSelector(selector))
464
+ return false;
465
+ out.push(selector);
466
+ return true;
467
+ };
468
+ while (i < prelude.length) {
469
+ if (prelude[i] === ',') {
470
+ if (!push(i))
471
+ return null;
472
+ start = i + 1;
473
+ i += 1;
474
+ }
475
+ else {
476
+ i = opens(prelude.charCodeAt(i)) ? componentEnd(prelude, i) : i + 1;
477
+ }
478
+ }
479
+ return push(prelude.length) ? out : null;
480
+ }
481
+ /**
482
+ * A selector's escapes as css-select reads them right. Its hex escapes go
483
+ * in lower case: css-what takes the space that ends `\\6C ` for a
484
+ * descendant combinator, where after `\\6c ` it does not. And in a string
485
+ * an escaped newline goes, since it only continues the line (CSS 2.1
486
+ * 4.3.7): `[title="a\\` and a newline and `b"]` is `[title="ab"]`.
487
+ */
488
+ function forMatcher(selector) {
489
+ let quote = '';
490
+ let out = '';
491
+ for (let i = 0; i < selector.length; i += 1) {
492
+ const c = selector[i];
493
+ if (c === '\\') {
494
+ const end = escapeEnd(selector, i);
495
+ const escape = selector.slice(i, end);
496
+ if (/^\\[0-9a-f]/i.test(escape)) {
497
+ // the hex digits, and the one space that may end them
498
+ out += escape.toLowerCase().replace(/\s+$/, ' ');
499
+ }
500
+ else if (!(quote && /^\\[\n\r\f]/.test(escape))) {
501
+ out += escape;
502
+ }
503
+ i = end - 1;
504
+ continue;
505
+ }
506
+ if (quote) {
507
+ if (c === quote)
508
+ quote = '';
509
+ }
510
+ else if (c === '"' || c === "'") {
511
+ quote = c;
512
+ }
513
+ out += c;
514
+ }
515
+ return out;
516
+ }
517
+ /**
518
+ * Whether a selector parses as one: compounds of type, universal, class,
519
+ * id, attribute and pseudo selectors, each name a CSS identifier, joined by
520
+ * combinators. What a selector *means* — which pseudo-classes exist — is
521
+ * the matcher's to decide; this only refuses what no grammar would read.
522
+ */
523
+ function isSelector(s) {
524
+ const n = s.length;
525
+ let i = 0;
526
+ let expectCompound = true;
527
+ // a pseudo-element ends its selector (CSS 2.1 5.10): `p:first-line p`
528
+ // is none, and neither is `p:first-line[id]` — but for the user action
529
+ // pseudo-classes Selectors 4 lets follow one, as in `a::before:hover`
530
+ let ended = false;
531
+ while (i < n) {
532
+ const c = s[i];
533
+ if (isSpace(c)) {
534
+ i += 1;
535
+ continue;
536
+ }
537
+ if (ended)
538
+ return false;
539
+ if (c === '>' || c === '+' || c === '~') {
540
+ if (expectCompound)
541
+ return false;
542
+ expectCompound = true;
543
+ i += 1;
544
+ continue;
545
+ }
546
+ // one compound: simple selectors with nothing between them
547
+ const from = i;
548
+ while (i < n && !isSpace(s[i]) && !'>+~'.includes(s[i])) {
549
+ const d = s[i];
550
+ if (ended &&
551
+ !(d === ':' &&
552
+ s[i + 1] !== ':' &&
553
+ USER_ACTIONS.test(readIdent(s, i + 1).value))) {
554
+ return false;
555
+ }
556
+ if (d === '*' || d === '|') {
557
+ i += 1;
558
+ }
559
+ else if (d === '.' || d === '#') {
560
+ if (!startsIdent(s, i + 1))
561
+ return false;
562
+ i = identEnd(s, i + 1);
563
+ }
564
+ else if (d === '[') {
565
+ let j = i + 1;
566
+ while (j < n && isSpace(s[j]))
567
+ j += 1;
568
+ if (s[j] === '*' || s[j] === '|')
569
+ j += 1;
570
+ if (s[j] === '|')
571
+ j += 1;
572
+ if (!startsIdent(s, j))
573
+ return false;
574
+ const end = componentEnd(s, i);
575
+ if (s[end - 1] !== ']')
576
+ return false;
577
+ // an operator needs a value after it: `[title~=]` is no selector,
578
+ // and takes its group with it (CSS 2.1 4.1.7)
579
+ let k = identEnd(s, j);
580
+ if (s[k] === '|' && s[k + 1] !== '=' && startsIdent(s, k + 1)) {
581
+ k = identEnd(s, k + 1);
582
+ }
583
+ while (k < end - 1 && isSpace(s[k]))
584
+ k += 1;
585
+ if (k < end - 1) {
586
+ if (s[k] === '=')
587
+ k += 1;
588
+ else if ('~|^$*'.includes(s[k]) && s[k + 1] === '=')
589
+ k += 2;
590
+ else
591
+ return false;
592
+ while (k < end - 1 && isSpace(s[k]))
593
+ k += 1;
594
+ if (k >= end - 1)
595
+ return false;
596
+ }
597
+ i = end;
598
+ }
599
+ else if (d === ':') {
600
+ const element = s[i + 1] === ':';
601
+ const at = element ? i + 2 : i + 1;
602
+ if (!startsIdent(s, at))
603
+ return false;
604
+ const name = readIdent(s, at);
605
+ // ASCII's case only (CSS 2.1 4.1.3): Unicode's makes `:lin\u212A`,
606
+ // with a Kelvin sign, `:link`
607
+ const lower = asciiLower(name.value);
608
+ if (!knownPseudo(lower, element))
609
+ return false;
610
+ if (element || LEGACY_PSEUDO_ELEMENTS.test(lower))
611
+ ended = true;
612
+ i = name.end;
613
+ if (s[i] === '(') {
614
+ const end = componentEnd(s, i);
615
+ if (s[end - 1] !== ')')
616
+ return false;
617
+ if (!element && !argumentFits(lower, s.slice(i + 1, end - 1))) {
618
+ return false;
619
+ }
620
+ i = end;
621
+ }
622
+ else if (!element && NEEDS_ARGUMENT.has(lower))
623
+ return false;
624
+ }
625
+ else if (startsIdent(s, i) && i === from) {
626
+ i = identEnd(s, i);
627
+ }
628
+ else {
629
+ return false;
630
+ }
631
+ }
632
+ expectCompound = false;
633
+ }
634
+ return !expectCompound;
635
+ }
636
+ /**
637
+ * Whether a pseudo-class takes the argument it is given: one where it
638
+ * takes one — `:lang()` names no language, and makes its group invalid as
639
+ * an unknown name does — and none where it takes none. `:is()` and
640
+ * `:where()` forgive an empty list; a vendor's are its own affair.
641
+ */
642
+ function argumentFits(name, argument) {
643
+ if (name.startsWith('-'))
644
+ return true;
645
+ if (NEEDS_ARGUMENT.has(name)) {
646
+ return name === 'is' || name === 'where' || /\S/.test(argument);
647
+ }
648
+ return MAY_TAKE_ARGUMENT.has(name);
649
+ }
650
+ /** A name with its ASCII letters in lower case, and no other letter's. */
651
+ export function asciiLower(name) {
652
+ return name.replace(/[A-Z]+/g, (upper) => upper.toLowerCase());
653
+ }
654
+ /** The pseudo-classes that are functions, and are nothing without it. */
655
+ const NEEDS_ARGUMENT = new Set([
656
+ 'contains',
657
+ 'dir',
658
+ 'has',
659
+ 'host-context',
660
+ 'icontains',
661
+ 'is',
662
+ 'lang',
663
+ 'matches',
664
+ 'not',
665
+ 'nth-child',
666
+ 'nth-col',
667
+ 'nth-last-child',
668
+ 'nth-last-col',
669
+ 'nth-last-of-type',
670
+ 'nth-of-type',
671
+ 'state',
672
+ 'where',
673
+ ]);
674
+ /** The ones that are a pseudo-class with an argument or without. */
675
+ const MAY_TAKE_ARGUMENT = new Set(['current', 'future', 'host', 'past']);
676
+ /**
677
+ * Whether a pseudo-class or pseudo-element is one: an unknown one makes its
678
+ * selector invalid, and with it the group (CSS 2.1 4.1.7) — which is also
679
+ * what a browser does with another engine's `:-moz-…`. A vendor prefix is
680
+ * let through: which of them a browser knows is its own affair.
681
+ */
682
+ function knownPseudo(name, element) {
683
+ if (name.startsWith('-'))
684
+ return true;
685
+ if (element)
686
+ return PSEUDO_ELEMENTS.has(name);
687
+ return PSEUDO_CLASSES.has(name) || LEGACY_PSEUDO_ELEMENTS.test(name);
688
+ }
689
+ const PSEUDO_ELEMENTS = new Set([
690
+ 'before',
691
+ 'after',
692
+ 'first-line',
693
+ 'first-letter',
694
+ 'selection',
695
+ 'placeholder',
696
+ 'marker',
697
+ 'backdrop',
698
+ 'file-selector-button',
699
+ 'cue',
700
+ 'part',
701
+ 'slotted',
702
+ 'spelling-error',
703
+ 'grammar-error',
704
+ 'target-text',
705
+ 'highlight',
706
+ 'details-content',
707
+ ]);
708
+ const PSEUDO_CLASSES = new Set([
709
+ // css-select's own, which the matcher answers and the docs promise
710
+ 'button',
711
+ 'checkbox',
712
+ 'file',
713
+ 'header',
714
+ 'icontains',
715
+ 'image',
716
+ 'input',
717
+ 'parent',
718
+ 'password',
719
+ 'radio',
720
+ 'reset',
721
+ 'selected',
722
+ 'submit',
723
+ 'text',
724
+ // CSS's
725
+ 'active',
726
+ 'any-link',
727
+ 'autofill',
728
+ 'blank',
729
+ 'checked',
730
+ 'closed',
731
+ 'contains',
732
+ 'current',
733
+ 'default',
734
+ 'defined',
735
+ 'dir',
736
+ 'disabled',
737
+ 'empty',
738
+ 'enabled',
739
+ 'first',
740
+ 'first-child',
741
+ 'first-of-type',
742
+ 'focus',
743
+ 'focus-visible',
744
+ 'focus-within',
745
+ 'fullscreen',
746
+ 'future',
747
+ 'has',
748
+ 'host',
749
+ 'host-context',
750
+ 'hover',
751
+ 'in-range',
752
+ 'indeterminate',
753
+ 'invalid',
754
+ 'is',
755
+ 'lang',
756
+ 'last-child',
757
+ 'last-of-type',
758
+ 'left',
759
+ 'link',
760
+ 'local-link',
761
+ 'matches',
762
+ 'modal',
763
+ 'not',
764
+ 'nth-child',
765
+ 'nth-col',
766
+ 'nth-last-child',
767
+ 'nth-last-col',
768
+ 'nth-last-of-type',
769
+ 'nth-of-type',
770
+ 'only-child',
771
+ 'only-of-type',
772
+ 'open',
773
+ 'optional',
774
+ 'out-of-range',
775
+ 'past',
776
+ 'paused',
777
+ 'picture-in-picture',
778
+ 'placeholder-shown',
779
+ 'playing',
780
+ 'popover-open',
781
+ 'read-only',
782
+ 'read-write',
783
+ 'required',
784
+ 'right',
785
+ 'root',
786
+ 'scope',
787
+ 'state',
788
+ 'target',
789
+ 'target-within',
790
+ 'user-invalid',
791
+ 'user-valid',
792
+ 'valid',
793
+ 'visited',
794
+ 'where',
795
+ ]);
164
796
  /**
165
797
  * Specificity, packed. Counted from the selector text rather than from a
166
798
  * parse: `#a` is an id, `.a`/`[a]`/`:a` is a class, a bare name is a type,
@@ -170,6 +802,9 @@ export function splitSelectors(prelude) {
170
802
  * specificity is their argument's. The approximation costs an author who
171
803
  * writes `:is(#id)` and expects it to beat a class; nothing else.
172
804
  */
805
+ const LEGACY_PSEUDO_ELEMENTS = /^(?:before|after|first-line|first-letter)$/i;
806
+ /** The pseudo-classes that may follow a pseudo-element. */
807
+ const USER_ACTIONS = /^(?:hover|active|focus|focus-visible|focus-within)$/i;
173
808
  export function specificityOf(selector) {
174
809
  let ids = 0;
175
810
  let classes = 0;
@@ -180,31 +815,42 @@ export function specificityOf(selector) {
180
815
  const c = selector[i];
181
816
  if (c === '#') {
182
817
  ids += 1;
183
- i = skipIdent(selector, i + 1);
818
+ i = identEnd(selector, i + 1);
184
819
  }
185
820
  else if (c === '.') {
186
821
  classes += 1;
187
- i = skipIdent(selector, i + 1);
822
+ i = identEnd(selector, i + 1);
188
823
  }
189
824
  else if (c === '[') {
190
825
  classes += 1;
191
- i = skipBalanced(selector, i, '[', ']');
826
+ i = componentEnd(selector, i);
192
827
  }
193
828
  else if (c === ':') {
194
829
  if (selector[i + 1] === ':') {
195
830
  types += 1;
196
- i = skipIdent(selector, i + 2);
831
+ i = identEnd(selector, i + 2);
197
832
  }
198
833
  else {
199
- classes += 1;
200
- i = skipIdent(selector, i + 1);
834
+ const name = readIdent(selector, i + 1);
835
+ // CSS 2 spelled the four pseudo-elements it had with one colon
836
+ if (LEGACY_PSEUDO_ELEMENTS.test(name.value)) {
837
+ types += 1;
838
+ }
839
+ else {
840
+ classes += 1;
841
+ }
842
+ i = name.end;
201
843
  if (selector[i] === '(')
202
- i = skipBalanced(selector, i, '(', ')');
844
+ i = componentEnd(selector, i);
203
845
  }
204
846
  }
205
- else if (isIdentStart(c)) {
847
+ else if (c === '*' || c === '|') {
848
+ // the universal selector, and a namespace's bar, count nothing
849
+ i += 1;
850
+ }
851
+ else if (startsIdent(selector, i)) {
206
852
  types += 1;
207
- i = skipIdent(selector, i);
853
+ i = identEnd(selector, i);
208
854
  }
209
855
  else {
210
856
  i += 1;
@@ -238,135 +884,765 @@ function skipTrivia(text, from) {
238
884
  function isSpace(c) {
239
885
  return c === ' ' || c === '\t' || c === '\n' || c === '\r' || c === '\f';
240
886
  }
241
- function isIdentStart(c) {
242
- return /[a-zA-Z_\-*|\\]/.test(c);
887
+ /** A character an identifier may start with, before escapes: a letter, an
888
+ * underscore, anything past ASCII. */
889
+ function isNameStart(c) {
890
+ if (c === undefined)
891
+ return false;
892
+ const code = c.charCodeAt(0);
893
+ return ((code >= 0x61 && code <= 0x7a) ||
894
+ (code >= 0x41 && code <= 0x5a) ||
895
+ code === 0x5f ||
896
+ code >= 0x80);
243
897
  }
244
- function skipIdent(text, from) {
245
- let i = from;
246
- while (i < text.length && /[a-zA-Z0-9_\-\\]/.test(text[i]))
247
- i += 1;
898
+ function isNameChar(c) {
899
+ if (c === undefined)
900
+ return false;
901
+ const code = c.charCodeAt(0);
902
+ return isNameStart(c) || (code >= 0x30 && code <= 0x39) || code === 0x2d;
903
+ }
904
+ /** Whether a backslash at `at` escapes what follows — not a newline, and
905
+ * not the end of the text. */
906
+ function isEscape(text, at) {
907
+ return text[at] === '\\' && at + 1 < text.length && text[at + 1] !== '\n';
908
+ }
909
+ /**
910
+ * Whether an identifier starts at `at` (CSS Syntax 4.3.10): a name-start
911
+ * character or an escape, after at most one hyphen — or two hyphens. Every
912
+ * character this accepts, `readIdent` steps over: a scan that finds an
913
+ * identifier and then reads none of it never moves again, which is how a
914
+ * `*` selector once hung the application.
915
+ */
916
+ export function startsIdent(text, at) {
917
+ const c = text[at];
918
+ if (c === '-') {
919
+ const d = text[at + 1];
920
+ return isNameStart(d) || d === '-' || isEscape(text, at + 1);
921
+ }
922
+ return isNameStart(c) || isEscape(text, at);
923
+ }
924
+ /** Where an identifier from `at` ends, its escapes stepped over. */
925
+ function identEnd(text, at) {
926
+ let i = at;
927
+ while (i < text.length) {
928
+ const code = text.charCodeAt(i);
929
+ if ((code >= 0x61 && code <= 0x7a) ||
930
+ (code >= 0x41 && code <= 0x5a) ||
931
+ (code >= 0x30 && code <= 0x39) ||
932
+ code === 0x2d ||
933
+ code === 0x5f ||
934
+ code >= 0x80) {
935
+ i += 1;
936
+ }
937
+ else if (code === 0x5c && isEscape(text, i)) {
938
+ i = readEscape(text, i).end;
939
+ }
940
+ else {
941
+ break;
942
+ }
943
+ }
248
944
  return i;
249
945
  }
250
- function skipBalanced(text, from, open, close) {
251
- let depth = 0;
252
- let quote = '';
253
- for (let i = from; i < text.length; i += 1) {
254
- const c = text[i];
255
- if (quote) {
256
- if (c === quote && text[i - 1] !== '\\')
257
- quote = '';
258
- continue;
946
+ /** An identifier from `at`, its escapes resolved, and where it ends. */
947
+ export function readIdent(text, at) {
948
+ const end = identEnd(text, at);
949
+ const raw = text.slice(at, end);
950
+ if (!raw.includes('\\'))
951
+ return { value: raw, end };
952
+ let value = '';
953
+ let i = at;
954
+ while (i < end) {
955
+ if (text[i] === '\\') {
956
+ const escape = readEscape(text, i);
957
+ value += escape.char;
958
+ i = escape.end;
259
959
  }
260
- if (c === '"' || c === "'")
261
- quote = c;
262
- else if (c === open)
263
- depth += 1;
264
- else if (c === close) {
265
- depth -= 1;
266
- if (depth === 0)
267
- return i + 1;
960
+ else {
961
+ value += text[i];
962
+ i += 1;
268
963
  }
269
964
  }
965
+ return { value, end };
966
+ }
967
+ /** The character an escape at `at` stands for, and where it ends: up to six
968
+ * hex digits and one white space after them, or the next character. */
969
+ function readEscape(text, at) {
970
+ let i = at + 1;
971
+ let hex = '';
972
+ while (hex.length < 6 && i < text.length && /[0-9a-fA-F]/.test(text[i])) {
973
+ hex += text[i];
974
+ i += 1;
975
+ }
976
+ if (!hex)
977
+ return { char: text[i] ?? '�', end: i + 1 };
978
+ if (text[i] === '\r' && text[i + 1] === '\n')
979
+ i += 2;
980
+ else if (isSpace(text[i] ?? ''))
981
+ i += 1;
982
+ const code = parseInt(hex, 16);
983
+ const char = code === 0 || code > 0x10ffff || (code >= 0xd800 && code <= 0xdfff)
984
+ ? '�'
985
+ : String.fromCodePoint(code);
986
+ return { char, end: i };
987
+ }
988
+ /**
989
+ * Where the component value starting at `at` ends (CSS Syntax 5.4.7): a
990
+ * string to its closing quote (or the line that breaks it), an escape, a
991
+ * `url(…)` to its parenthesis, a bracketed block to its match — whatever
992
+ * closing brackets of another kind it holds — and otherwise one character.
993
+ * Every scan below goes through this, so a brace in a string, a bracket
994
+ * escaped with a backslash and a semicolon in `url(a;b)` are what they are
995
+ * everywhere at once.
996
+ */
997
+ function componentEnd(text, at) {
998
+ const c = text[at];
999
+ if (c === '"' || c === "'")
1000
+ return stringEnd(text, at);
1001
+ if (c === '\\')
1002
+ return isEscape(text, at) ? readEscape(text, at).end : at + 1;
1003
+ if (c === '(' || c === '[' || c === '{')
1004
+ return blockEnd(text, at);
1005
+ if ((c === 'u' || c === 'U') &&
1006
+ text[at + 3] === '(' &&
1007
+ text.slice(at, at + 3).toLowerCase() === 'url' &&
1008
+ !isNameChar(text[at - 1])) {
1009
+ return urlEnd(text, at + 3);
1010
+ }
1011
+ return at + 1;
1012
+ }
1013
+ function stringEnd(text, at) {
1014
+ const quote = text[at];
1015
+ let i = at + 1;
1016
+ while (i < text.length) {
1017
+ const c = text[i];
1018
+ if (c === quote)
1019
+ return i + 1;
1020
+ // a newline ends a string that has not ended: it is a bad string, and
1021
+ // the newline is not in it
1022
+ if (c === '\n' || c === '\r' || c === '\f')
1023
+ return i;
1024
+ i = c === '\\' ? escapeEnd(text, i) : i + 1;
1025
+ }
270
1026
  return text.length;
271
1027
  }
272
- /** The next `{` that is not inside a string, comment or bracket. */
273
- function findBrace(text, from) {
274
- let quote = '';
275
- let bracket = 0;
276
- for (let i = from; i < text.length; i += 1) {
1028
+ /** Where a backslash at `at` stops meaning something inside a string: a
1029
+ * newline after it continues the line, and an escape — six hex digits and
1030
+ * the white space after them included — is read whole. */
1031
+ /** Where an escape at `at` ends: past its hex digits and the one space
1032
+ * that may close them, or past the character it escapes. */
1033
+ export function escapeEnd(text, at) {
1034
+ if (text[at + 1] === '\r' && text[at + 2] === '\n')
1035
+ return at + 3;
1036
+ if (isEscape(text, at))
1037
+ return readEscape(text, at).end;
1038
+ return Math.min(text.length, at + 2);
1039
+ }
1040
+ function blockEnd(text, at) {
1041
+ const open = text.charCodeAt(at);
1042
+ const close = open === 0x28 ? 0x29 : open === 0x5b ? 0x5d : 0x7d;
1043
+ let i = at + 1;
1044
+ while (i < text.length) {
1045
+ const code = text.charCodeAt(i);
1046
+ if (code === close)
1047
+ return i + 1;
1048
+ // a closing bracket of another kind inside is only a token
1049
+ i = opens(code) ? componentEnd(text, i) : i + 1;
1050
+ }
1051
+ return text.length;
1052
+ }
1053
+ /** Whether a character starts a component value longer than itself: a
1054
+ * quote, an escape, an opening bracket, or the `u` of a `url(`. Every other
1055
+ * character is its own, which is what keeps a scan a loop over codes. */
1056
+ function opens(code) {
1057
+ return (code === 0x22 ||
1058
+ code === 0x27 ||
1059
+ code === 0x5c ||
1060
+ code === 0x28 ||
1061
+ code === 0x5b ||
1062
+ code === 0x7b ||
1063
+ code === 0x75 ||
1064
+ code === 0x55);
1065
+ }
1066
+ /** `url(` at the parenthesis: a quoted argument is a function like any
1067
+ * other; an unquoted one runs to its `)` with its escapes. */
1068
+ /** Where a `url(` whose parenthesis is at `paren` ends: past its `)`. */
1069
+ export function urlEnd(text, paren) {
1070
+ let i = paren + 1;
1071
+ while (i < text.length && isSpace(text[i]))
1072
+ i += 1;
1073
+ if (text[i] === '"' || text[i] === "'")
1074
+ return blockEnd(text, paren);
1075
+ while (i < text.length) {
277
1076
  const c = text[i];
278
- if (quote) {
279
- if (c === quote && text[i - 1] !== '\\')
280
- quote = '';
281
- continue;
1077
+ if (c === ')')
1078
+ return i + 1;
1079
+ i += c === '\\' ? 2 : 1;
1080
+ }
1081
+ return text.length;
1082
+ }
1083
+ /**
1084
+ * A `url(…)` as CSS Syntax 3 reads it (4.3.6): quoted, a string, which the
1085
+ * end of the sheet may close as it closes the parenthesis; or unquoted, to
1086
+ * its `)`, with white space only around it and its escapes resolved. `null`
1087
+ * for `none`, or a url of nothing. `undefined` for a bad url — a quote, a
1088
+ * parenthesis or white space inside an unquoted one, or anything after a
1089
+ * quoted one's string — which makes the declaration it is in invalid.
1090
+ */
1091
+ export function parseUrl(value) {
1092
+ const v = value.trim();
1093
+ if (!v || v.toLowerCase() === 'none')
1094
+ return null;
1095
+ if (!/^url\(/i.test(v))
1096
+ return unquote(v) || null;
1097
+ let i = 4;
1098
+ while (i < v.length && isSpace(v[i]))
1099
+ i += 1;
1100
+ let out = '';
1101
+ const quote = v[i];
1102
+ if (quote === '"' || quote === "'") {
1103
+ i += 1;
1104
+ while (i < v.length && v[i] !== quote) {
1105
+ if (v[i] === '\\') {
1106
+ if (v[i + 1] === '\n') {
1107
+ i += 2;
1108
+ continue;
1109
+ }
1110
+ const escape = readEscape(v, i);
1111
+ out += escape.char;
1112
+ i = escape.end;
1113
+ }
1114
+ else {
1115
+ out += v[i];
1116
+ i += 1;
1117
+ }
282
1118
  }
283
- if (c === '"' || c === "'")
284
- quote = c;
285
- else if (c === '/' && text[i + 1] === '*') {
286
- const close = text.indexOf('*/', i + 2);
287
- i = close < 0 ? text.length : close + 1;
1119
+ i += 1;
1120
+ while (i < v.length && isSpace(v[i]))
1121
+ i += 1;
1122
+ if (i < v.length && v[i] !== ')')
1123
+ return undefined;
1124
+ return rest(v, i) ? undefined : out || null;
1125
+ }
1126
+ while (i < v.length && v[i] !== ')') {
1127
+ const c = v[i];
1128
+ if (isSpace(c)) {
1129
+ while (i < v.length && isSpace(v[i]))
1130
+ i += 1;
1131
+ if (i < v.length && v[i] !== ')')
1132
+ return undefined;
1133
+ break;
288
1134
  }
289
- else if (c === '(' || c === '[')
290
- bracket += 1;
291
- else if (c === ')' || c === ']')
292
- bracket = Math.max(0, bracket - 1);
293
- else if (c === '{' && bracket === 0)
1135
+ if (c === '"' || c === "'" || c === '(')
1136
+ return undefined;
1137
+ if (c === '\\') {
1138
+ if (!isEscape(v, i))
1139
+ return undefined;
1140
+ const escape = readEscape(v, i);
1141
+ out += escape.char;
1142
+ i = escape.end;
1143
+ continue;
1144
+ }
1145
+ out += c;
1146
+ i += 1;
1147
+ }
1148
+ return rest(v, i) ? undefined : out || null;
1149
+ }
1150
+ /** Whether anything follows a url's `)` at `at`: `url(a) repeat` is no
1151
+ * image, and the declaration it is in is invalid. */
1152
+ function rest(v, at) {
1153
+ return at + 1 < v.length && v.slice(at + 1).trim() !== '';
1154
+ }
1155
+ function unquote(value) {
1156
+ const v = value.trim();
1157
+ if (v.length >= 2 &&
1158
+ (v[0] === '"' || v[0] === "'") &&
1159
+ v[v.length - 1] === v[0]) {
1160
+ return v.slice(1, -1);
1161
+ }
1162
+ return v;
1163
+ }
1164
+ /** The first `target` from `at` that no string, escape or bracket holds, or
1165
+ * the length of the text. */
1166
+ function scanTo(text, at, target) {
1167
+ const want = target.charCodeAt(0);
1168
+ let i = at;
1169
+ while (i < text.length) {
1170
+ const code = text.charCodeAt(i);
1171
+ if (code === want)
294
1172
  return i;
295
- else if (c === ';' && bracket === 0)
296
- return -1; // a stray declaration
1173
+ i = opens(code) ? componentEnd(text, i) : i + 1;
297
1174
  }
298
- return -1;
1175
+ return text.length;
299
1176
  }
300
- /** Find `target` at depth zero, stopping at any of `stopAt`. */
301
- function findChar(text, from, target, stopAt) {
302
- let quote = '';
303
- let depth = 0;
304
- for (let i = from; i < text.length; i += 1) {
305
- const c = text[i];
306
- if (quote) {
307
- if (c === quote && text[i - 1] !== '\\')
308
- quote = '';
1177
+ /** Whether a `!` stands in a value outside its strings and brackets. */
1178
+ function hasBang(value) {
1179
+ if (!value.includes('!'))
1180
+ return false;
1181
+ for (let at = scanTo(value, 0, '!'); at < value.length;) {
1182
+ // `<!--` is a token of its own, which a custom property may hold
1183
+ if (value[at - 1] !== '<' || !value.startsWith('--', at + 1))
1184
+ return true;
1185
+ at = scanTo(value, at + 1, '!');
1186
+ }
1187
+ return false;
1188
+ }
1189
+ /**
1190
+ * A value with its escapes resolved outside its strings and `url()`s:
1191
+ * `\67reen` is `green`. A string keeps its escapes for whoever reads its
1192
+ * text, `content` among them.
1193
+ */
1194
+ function unescapeValue(value) {
1195
+ if (!value.includes('\\'))
1196
+ return value;
1197
+ let out = '';
1198
+ let i = 0;
1199
+ while (i < value.length) {
1200
+ if (isEscape(value, i)) {
1201
+ // Resolved where it stands for a name character; any other it is part
1202
+ // of an identifier that a value parser should see as one, not as the
1203
+ // tab or the semicolon it names — `red \9` is not `red`.
1204
+ const escape = readEscape(value, i);
1205
+ out += isNameChar(escape.char) ? escape.char : value.slice(i, escape.end);
1206
+ i = escape.end;
309
1207
  continue;
310
1208
  }
311
- if (c === '"' || c === "'")
312
- quote = c;
313
- else if (c === '/' && text[i + 1] === '*') {
314
- const close = text.indexOf('*/', i + 2);
315
- i = close < 0 ? text.length : close + 1;
1209
+ const end = componentEnd(value, i);
1210
+ out += value.slice(i, end);
1211
+ i = end;
1212
+ }
1213
+ return out;
1214
+ }
1215
+ /**
1216
+ * A `{…}` block: its body, and where the text goes on after it. The end of
1217
+ * a style sheet closes a block still open (CSS 2.1 4.2), so a block the end
1218
+ * cut off is everything after its brace. Cutting its last character as if
1219
+ * it were the `}` made `color: blue` `color: blu` — and left an unclosed
1220
+ * `rgb(` to reach paint whole or not, by where the sheet's whitespace fell.
1221
+ */
1222
+ function readBlock(text, braceAt) {
1223
+ const end = blockEnd(text, braceAt);
1224
+ const closed = text[end - 1] === '}' && end - 1 > braceAt;
1225
+ return { body: text.slice(braceAt + 1, closed ? end - 1 : end), end };
1226
+ }
1227
+ /** An at-rule: its name, and everything up to its `;` or its block. */
1228
+ function readAtRule(text, at) {
1229
+ const name = readIdent(text, at + 1);
1230
+ let i = name.end;
1231
+ while (i < text.length && text[i] !== ';' && text[i] !== '{') {
1232
+ i = opens(text.charCodeAt(i)) ? componentEnd(text, i) : i + 1;
1233
+ }
1234
+ const prelude = text.slice(name.end, i).trim();
1235
+ if (text[i] === '{') {
1236
+ const block = readBlock(text, i);
1237
+ return { name: name.value, prelude, block: block.body, end: block.end };
1238
+ }
1239
+ return {
1240
+ name: name.value,
1241
+ prelude,
1242
+ block: null,
1243
+ end: Math.min(text.length, i + 1),
1244
+ };
1245
+ }
1246
+ /** Where a width past a maximum starts, for the widths a restyle is due
1247
+ * at: a sixty-fourth of a pixel past it, where the widths a display scale
1248
+ * divides a device width into (`640.5` at 2x) fall on the right side. */
1249
+ const MAX_EDGE = 1 / 64;
1250
+ /**
1251
+ * A width range in Media Queries 4's syntax — `(width >= 48rem)`, `(60rem >
1252
+ * width)`, `(40rem <= width < 60rem)` — as the widths it holds between, or
1253
+ * null for a term that is not one. A strict bound is a sixty-fourth of a
1254
+ * pixel inside the value, where a viewport's width never lands.
1255
+ */
1256
+ function widthRange(term) {
1257
+ const inner = /^\(\s*(.*?)\s*\)$/.exec(term)?.[1];
1258
+ if (!inner || !/[<>=]/.test(inner))
1259
+ return null;
1260
+ const parts = inner.split(/\s*(<=|>=|<|>|=)\s*/);
1261
+ const isWidth = (part) => part.toLowerCase() === 'width';
1262
+ const out = {};
1263
+ // `width OP value`, with the operator read from the width's side
1264
+ const bound = (op, value) => {
1265
+ const len = parseLength(value, ZERO_UNITS);
1266
+ if (typeof len !== 'number')
1267
+ return false;
1268
+ const edge = 1 / 64;
1269
+ if (op === '>=' || op === '=')
1270
+ out.min = Math.max(out.min ?? 0, len);
1271
+ if (op === '>')
1272
+ out.min = Math.max(out.min ?? 0, len + edge);
1273
+ if (op === '<=' || op === '=')
1274
+ out.max = Math.min(out.max ?? Infinity, len);
1275
+ if (op === '<')
1276
+ out.max = Math.min(out.max ?? Infinity, len - edge);
1277
+ return true;
1278
+ };
1279
+ const flip = {
1280
+ '<': '>',
1281
+ '<=': '>=',
1282
+ '>': '<',
1283
+ '>=': '<=',
1284
+ '=': '=',
1285
+ };
1286
+ if (parts.length === 3 && isWidth(parts[0])) {
1287
+ return bound(parts[1], parts[2]) ? out : null;
1288
+ }
1289
+ if (parts.length === 3 && isWidth(parts[2])) {
1290
+ return bound(flip[parts[1]], parts[0]) ? out : null;
1291
+ }
1292
+ if (parts.length === 5 && isWidth(parts[2])) {
1293
+ return bound(flip[parts[1]], parts[0]) && bound(parts[3], parts[4])
1294
+ ? out
1295
+ : null;
1296
+ }
1297
+ return null;
1298
+ }
1299
+ /** How deep rules may nest before the ones further in are dropped: far
1300
+ * past anything written by hand or by a framework, and short of what a
1301
+ * generated sheet could make of the stack. */
1302
+ const MAX_NESTING = 16;
1303
+ /**
1304
+ * A style rule's block, parted into its declarations and the rules nested
1305
+ * in it. A declaration runs to its `;` and a nested rule to its block,
1306
+ * whichever comes first — `color: red;` and `a:hover { … }` — and a custom
1307
+ * property is a declaration whatever its value holds.
1308
+ */
1309
+ function splitNested(body) {
1310
+ let declarations = '';
1311
+ const nested = [];
1312
+ let i = 0;
1313
+ while (i < body.length) {
1314
+ i = skipTrivia(body, i);
1315
+ if (i >= body.length)
1316
+ break;
1317
+ if (body[i] === ';') {
1318
+ i += 1;
1319
+ continue;
316
1320
  }
317
- else if (c === '(' || c === '[' || c === '{')
318
- depth += 1;
319
- else if (c === ')' || c === ']' || c === '}') {
320
- if (depth === 0 && stopAt.includes(c))
321
- return -1;
322
- depth -= 1;
1321
+ if (body[i] === '@' && startsIdent(body, i + 1)) {
1322
+ const at = readAtRule(body, i);
1323
+ nested.push({
1324
+ kind: 'at',
1325
+ name: at.name,
1326
+ prelude: at.prelude,
1327
+ block: at.block,
1328
+ });
1329
+ i = at.end;
1330
+ continue;
323
1331
  }
324
- else if (depth === 0) {
325
- if (c === target)
326
- return i;
327
- if (stopAt.includes(c))
328
- return -1;
1332
+ const semi = scanTo(body, i, ';');
1333
+ const brace = scanTo(body, i, '{');
1334
+ if (brace < semi && !CUSTOM_DECLARATION.test(body.slice(i, brace))) {
1335
+ const block = readBlock(body, brace);
1336
+ nested.push({
1337
+ kind: 'rule',
1338
+ prelude: body.slice(i, brace).trim(),
1339
+ body: block.body,
1340
+ });
1341
+ i = block.end;
1342
+ continue;
329
1343
  }
1344
+ declarations += `${body.slice(i, semi)};`;
1345
+ i = semi + 1;
330
1346
  }
331
- return -1;
1347
+ return { declarations, nested };
332
1348
  }
333
- function readBlock(text, braceAt) {
334
- const end = skipBalanced(text, braceAt, '{', '}');
335
- return { body: text.slice(braceAt + 1, Math.max(braceAt + 1, end - 1)), end };
1349
+ const CUSTOM_DECLARATION = /^\s*--[\w-]*\s*:/;
1350
+ /**
1351
+ * A nested rule's selectors against its parent's, as written (CSS Nesting
1352
+ * 1, 3): a selector that starts with `&` is the parent's with the rest
1353
+ * after it, `&:hover` the parent hovered; any other `&` is `:is()` of the
1354
+ * parent; one with none is relative to the parent, a descendant unless it
1355
+ * starts with a combinator. Null where one of them comes to no selector.
1356
+ */
1357
+ function nestedSelectors(prelude, parents) {
1358
+ const out = [];
1359
+ for (const part of splitTopLevel(prelude, ',')) {
1360
+ const written = part.trim();
1361
+ if (!written)
1362
+ return null;
1363
+ const own = hasNesting(written) ? written : `& ${written}`;
1364
+ for (const parent of parents) {
1365
+ const resolved = own[0] === '&' &&
1366
+ !hasNesting(own.slice(1)) &&
1367
+ !/^[\w-]/.test(own.slice(1))
1368
+ ? parent + own.slice(1)
1369
+ : replaceNesting(own, `:is(${parent})`);
1370
+ if (!isSelector(resolved))
1371
+ return null;
1372
+ out.push(resolved);
1373
+ }
1374
+ }
1375
+ return out;
336
1376
  }
337
- function readAtRule(text, at) {
338
- const nameEnd = skipIdent(text, at + 1);
339
- const name = text.slice(at + 1, nameEnd);
340
- let i = nameEnd;
1377
+ /** Whether a selector has a `&` outside its strings and escapes. */
1378
+ function hasNesting(selector) {
1379
+ return replaceNesting(selector, '') !== selector;
1380
+ }
1381
+ /** A selector with every `&` outside its strings and escapes replaced. */
1382
+ function replaceNesting(selector, by) {
1383
+ if (!selector.includes('&'))
1384
+ return selector;
1385
+ let out = '';
341
1386
  let quote = '';
342
- let depth = 0;
343
- for (; i < text.length; i += 1) {
344
- const c = text[i];
345
- if (quote) {
346
- if (c === quote && text[i - 1] !== '\\')
1387
+ for (let i = 0; i < selector.length; i += 1) {
1388
+ const c = selector[i];
1389
+ if (c === '\\') {
1390
+ out += selector.slice(i, i + 2);
1391
+ i += 1;
1392
+ }
1393
+ else if (quote) {
1394
+ if (c === quote)
347
1395
  quote = '';
348
- continue;
1396
+ out += c;
349
1397
  }
350
- if (c === '"' || c === "'")
1398
+ else if (c === '"' || c === "'") {
351
1399
  quote = c;
352
- else if (c === '(' || c === '[')
353
- depth += 1;
354
- else if (c === ')' || c === ']')
355
- depth = Math.max(0, depth - 1);
356
- else if (depth === 0 && (c === ';' || c === '{'))
357
- break;
1400
+ out += c;
1401
+ }
1402
+ else
1403
+ out += c === '&' ? by : c;
358
1404
  }
359
- const prelude = text.slice(nameEnd, i).trim();
360
- if (text[i] === '{') {
361
- const block = readBlock(text, i);
362
- return { name, prelude, block: block.body, end: block.end };
1405
+ return out;
1406
+ }
1407
+ /** A list's items at the top level: no string, escape or bracket parts
1408
+ * them. */
1409
+ function splitTopLevel(text, at) {
1410
+ const out = [];
1411
+ let start = 0;
1412
+ let i = 0;
1413
+ while (i < text.length) {
1414
+ if (text[i] === at) {
1415
+ out.push(text.slice(start, i));
1416
+ start = i + 1;
1417
+ i += 1;
1418
+ }
1419
+ else {
1420
+ i = opens(text.charCodeAt(i)) ? componentEnd(text, i) : i + 1;
1421
+ }
363
1422
  }
364
- return { name, prelude, block: null, end: Math.min(text.length, i + 1) };
1423
+ out.push(text.slice(start));
1424
+ return out;
365
1425
  }
366
1426
  function importUrl(prelude) {
367
1427
  const m = /^\s*(?:url\(\s*)?["']?([^"')\s]+)["']?\s*\)?/.exec(prelude);
368
1428
  return m ? m[1] : null;
369
1429
  }
1430
+ /** Whether an `@import`'s media list, the rest of its prelude after the
1431
+ * URL, lets it apply here: a list that only names other media, `print` or
1432
+ * `braille`, leaves the sheet out (CSS 2.1 6.3). A list that depends on the
1433
+ * width is taken to apply; an import is fetched once, not per width. */
1434
+ function importApplies(prelude) {
1435
+ const m = /^\s*(?:url\(\s*(?:"[^"]*"|'[^']*'|[^)]*)\s*\)|"[^"]*"|'[^']*')(.*)$/s.exec(prelude);
1436
+ const list = m?.[1].trim();
1437
+ if (!list)
1438
+ return true;
1439
+ return parseMediaQuery(list).some((c) => c.staticPass !== false);
1440
+ }
1441
+ /**
1442
+ * A declaration value with every `url()` in it made absolute against `base`
1443
+ * — inside a function too, `image-set()` or a `var()` fallback, and never
1444
+ * inside a string. `url(#id)`, a reference into the document itself, is left
1445
+ * as it is, as are a `data:` URL, absolute already, and a url that is none.
1446
+ */
1447
+ export function absoluteUrls(value, base) {
1448
+ if (!/url\(/i.test(value))
1449
+ return value;
1450
+ let out = '';
1451
+ let from = 0;
1452
+ let i = 0;
1453
+ while (i < value.length) {
1454
+ const c = value[i];
1455
+ if (c === '"' || c === "'") {
1456
+ i = stringEnd(value, i);
1457
+ continue;
1458
+ }
1459
+ if (c === '\\') {
1460
+ i = escapeEnd(value, i);
1461
+ continue;
1462
+ }
1463
+ if ((c === 'u' || c === 'U') &&
1464
+ value[i + 3] === '(' &&
1465
+ value.slice(i, i + 3).toLowerCase() === 'url' &&
1466
+ !isNameChar(value[i - 1])) {
1467
+ const end = urlEnd(value, i + 3);
1468
+ const url = parseUrl(value.slice(i, end));
1469
+ if (typeof url === 'string' && url[0] !== '#' && !/^data:/i.test(url)) {
1470
+ const resolved = resolveUrl(url, base);
1471
+ if (resolved !== url) {
1472
+ out += value.slice(from, i) + cssUrl(resolved);
1473
+ from = end;
1474
+ }
1475
+ }
1476
+ i = end;
1477
+ continue;
1478
+ }
1479
+ i += 1;
1480
+ }
1481
+ return from === 0 ? value : out + value.slice(from);
1482
+ }
1483
+ /** A URL as a `url()` that reads back as it: quoted, with the characters a
1484
+ * string cannot hold as they are escaped. */
1485
+ function cssUrl(url) {
1486
+ const escaped = url.replace(/[\\"\n]/g, (c) => c === '\n' ? '\\a ' : `\\${c}`);
1487
+ return `url("${escaped}")`;
1488
+ }
1489
+ /** The generic families: a `@font-face` may not take one's name, and one
1490
+ * that did would take over every element that asks for it. */
1491
+ const GENERIC_FAMILIES = new Set([
1492
+ 'serif',
1493
+ 'sans-serif',
1494
+ 'monospace',
1495
+ 'cursive',
1496
+ 'fantasy',
1497
+ 'system-ui',
1498
+ 'ui-serif',
1499
+ 'ui-sans-serif',
1500
+ 'ui-monospace',
1501
+ 'ui-rounded',
1502
+ 'math',
1503
+ 'emoji',
1504
+ 'fangsong',
1505
+ '-apple-system',
1506
+ 'blinkmacsystemfont',
1507
+ 'inherit',
1508
+ 'initial',
1509
+ 'unset',
1510
+ 'revert',
1511
+ 'default',
1512
+ ]);
1513
+ /**
1514
+ * A `@font-face` block, or null when it names no family, or no source this
1515
+ * can ask for: `local()` names a face the system has, which this does not
1516
+ * look up, and a family that is only `local()`s is left to the system as it
1517
+ * would be anyway.
1518
+ */
1519
+ function parseFontFace(block, base, media) {
1520
+ let family = null;
1521
+ let src = null;
1522
+ let weight = [400, 400];
1523
+ let style = 'normal';
1524
+ let unicodeRange = null;
1525
+ // a descriptor that is not one is dropped, as a declaration is, and the
1526
+ // one before it stands
1527
+ for (const d of parseDeclarations(block)) {
1528
+ if (d.prop === 'font-family') {
1529
+ family = faceFamily(d.value) ?? family;
1530
+ }
1531
+ else if (d.prop === 'src') {
1532
+ src = d.value;
1533
+ }
1534
+ else if (d.prop === 'font-weight') {
1535
+ weight = faceWeight(d.value) ?? weight;
1536
+ }
1537
+ else if (d.prop === 'font-style') {
1538
+ const v = d.value.trim().toLowerCase();
1539
+ if (v === 'normal')
1540
+ style = 'normal';
1541
+ else if (v === 'italic' || /^oblique\b/.test(v))
1542
+ style = 'italic';
1543
+ }
1544
+ else if (d.prop === 'unicode-range') {
1545
+ unicodeRange = unicodeRanges(d.value) ?? unicodeRange;
1546
+ }
1547
+ }
1548
+ if (!family || src === null)
1549
+ return null;
1550
+ const sources = [];
1551
+ for (const part of splitTopLevel(src, ',')) {
1552
+ const item = part.trim();
1553
+ if (!/^url\(/i.test(item))
1554
+ continue;
1555
+ const end = urlEnd(item, 3);
1556
+ const url = parseUrl(item.slice(0, end));
1557
+ if (typeof url !== 'string')
1558
+ continue;
1559
+ // `format("woff2")`, the older `format(woff2)`, or a list of them, of
1560
+ // which the first says enough
1561
+ const hint = /format\(\s*(?:"([^"]*)"|'([^']*)'|([\w-]+))/i.exec(item.slice(end));
1562
+ const format = hint ? (hint[1] ?? hint[2] ?? hint[3]).toLowerCase() : null;
1563
+ sources.push({ url: resolveUrl(url, base), format });
1564
+ }
1565
+ if (!sources.length)
1566
+ return null;
1567
+ return { family, sources, weight, style, unicodeRange, media };
1568
+ }
1569
+ /** A `@font-face`'s one family name: a string, or identifiers, which one
1570
+ * space joins. Not a list, and not a generic family's name. */
1571
+ function faceFamily(value) {
1572
+ const v = value.trim();
1573
+ let name;
1574
+ if (v[0] === '"' || v[0] === "'") {
1575
+ if (stringEnd(v, 0) !== v.length)
1576
+ return null;
1577
+ name = unquote(v).trim();
1578
+ }
1579
+ else {
1580
+ const words = v.split(/\s+/);
1581
+ for (const word of words) {
1582
+ if (!startsIdent(word, 0) || readIdent(word, 0).end !== word.length) {
1583
+ return null;
1584
+ }
1585
+ }
1586
+ name = words.join(' ');
1587
+ }
1588
+ if (!name || name.includes(','))
1589
+ return null;
1590
+ return GENERIC_FAMILIES.has(name.toLowerCase()) ? null : name;
1591
+ }
1592
+ /** A `@font-face`'s `font-weight`: one weight, or the range a variable face
1593
+ * covers. `auto` — the file's own range — is taken to be every weight. */
1594
+ function faceWeight(value) {
1595
+ const parts = value.trim().toLowerCase().split(/\s+/);
1596
+ const one = (p) => {
1597
+ if (p === 'normal')
1598
+ return 400;
1599
+ if (p === 'bold')
1600
+ return 700;
1601
+ if (!/^\d+(?:\.\d+)?$/.test(p))
1602
+ return null;
1603
+ const n = Number(p);
1604
+ return n >= 1 && n <= 1000 ? n : null;
1605
+ };
1606
+ if (parts.length === 1) {
1607
+ if (parts[0] === 'auto')
1608
+ return [1, 1000];
1609
+ const w = one(parts[0]);
1610
+ return w === null ? null : [w, w];
1611
+ }
1612
+ if (parts.length !== 2)
1613
+ return null;
1614
+ const a = one(parts[0]);
1615
+ const b = one(parts[1]);
1616
+ if (a === null || b === null)
1617
+ return null;
1618
+ return a <= b ? [a, b] : [b, a];
1619
+ }
1620
+ /** `unicode-range`: `U+26`, `U+0-7F`, `U+4??` — inclusive ranges, or null
1621
+ * when any part is not one, and the descriptor is dropped. */
1622
+ function unicodeRanges(value) {
1623
+ const out = [];
1624
+ for (const part of value.split(',')) {
1625
+ const m = /^\s*u\+([0-9a-f?]{1,6})(?:-([0-9a-f]{1,6}))?\s*$/i.exec(part);
1626
+ if (!m)
1627
+ return null;
1628
+ let lo;
1629
+ let hi;
1630
+ if (m[1].includes('?')) {
1631
+ if (m[2] || !/^[0-9a-f]*\?+$/i.test(m[1]))
1632
+ return null;
1633
+ lo = parseInt(m[1].replace(/\?/g, '0'), 16);
1634
+ hi = parseInt(m[1].replace(/\?/g, 'f'), 16);
1635
+ }
1636
+ else {
1637
+ lo = parseInt(m[1], 16);
1638
+ hi = m[2] ? parseInt(m[2], 16) : lo;
1639
+ }
1640
+ if (hi < lo || lo > 0x10ffff)
1641
+ return null;
1642
+ out.push([lo, Math.min(hi, 0x10ffff)]);
1643
+ }
1644
+ return out.length ? out : null;
1645
+ }
370
1646
  /**
371
1647
  * A `@media` prelude, reduced to the width and colour-scheme tests this can
372
1648
  * honour. Each comma group is one condition and they are OR-ed; within a
@@ -387,6 +1663,19 @@ export function parseMediaQuery(prelude) {
387
1663
  .replace(/^only\s+/i, '');
388
1664
  if (!term)
389
1665
  continue;
1666
+ // Media Queries 4's ranges, which Tailwind 4 writes its breakpoints
1667
+ // in: `(width >= 48rem)`, `(40rem <= width < 60rem)`
1668
+ const range = widthRange(term);
1669
+ if (range) {
1670
+ if (range.min !== undefined) {
1671
+ condition.min = Math.max(condition.min ?? 0, range.min);
1672
+ }
1673
+ if (range.max !== undefined) {
1674
+ condition.max = Math.min(condition.max ?? Infinity, range.max);
1675
+ }
1676
+ sawWidth = true;
1677
+ continue;
1678
+ }
390
1679
  const feature = /^\(\s*([a-z-]+)\s*:\s*([^)]+?)\s*\)$/i.exec(term);
391
1680
  if (feature) {
392
1681
  const key = feature[1].toLowerCase();
@@ -429,7 +1718,11 @@ export function parseMediaQuery(prelude) {
429
1718
  const type = term.toLowerCase();
430
1719
  if (type === 'screen' || type === 'all')
431
1720
  continue;
432
- if (type === 'print' || type === 'speech')
1721
+ // any other medium is not this one: `print`, `speech`, and the ones
1722
+ // CSS 2.1 named that Media Queries retired, `braille`, `embossed`,
1723
+ // `handheld`, `projection`, `tty`, `tv`. A term that is no name is
1724
+ // left as it was.
1725
+ if (/^[a-z-]+$/.test(type))
433
1726
  pass = false;
434
1727
  }
435
1728
  if (negated) {