@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.
- package/README.md +10 -4
- package/dist/charts/node.d.ts +11 -1
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +44 -14
- package/dist/charts/node.js.map +1 -1
- package/dist/charts/render.d.ts +12 -3
- package/dist/charts/render.d.ts.map +1 -1
- package/dist/charts/render.js +94 -25
- package/dist/charts/render.js.map +1 -1
- package/dist/code/index.d.ts.map +1 -1
- package/dist/code/index.js +27 -24
- package/dist/code/index.js.map +1 -1
- package/dist/code-editor/completion.d.ts.map +1 -1
- package/dist/code-editor/completion.js +3 -2
- package/dist/code-editor/completion.js.map +1 -1
- package/dist/code-editor/index.d.ts.map +1 -1
- package/dist/code-editor/index.js +19 -0
- package/dist/code-editor/index.js.map +1 -1
- package/dist/code-editor/node.d.ts +177 -7
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +1329 -164
- package/dist/code-editor/node.js.map +1 -1
- package/dist/code-language/index.d.ts +1 -1
- package/dist/code-language/index.d.ts.map +1 -1
- package/dist/code-language/index.js +1 -1
- package/dist/code-language/index.js.map +1 -1
- package/dist/code-language/languages/glsl.d.ts.map +1 -1
- package/dist/code-language/languages/glsl.js +10 -2
- package/dist/code-language/languages/glsl.js.map +1 -1
- package/dist/code-language/languages/javascript.d.ts.map +1 -1
- package/dist/code-language/languages/javascript.js +18 -3
- package/dist/code-language/languages/javascript.js.map +1 -1
- package/dist/code-language/languages/json.d.ts.map +1 -1
- package/dist/code-language/languages/json.js +10 -2
- package/dist/code-language/languages/json.js.map +1 -1
- package/dist/code-language/languages/shell.d.ts.map +1 -1
- package/dist/code-language/languages/shell.js +10 -2
- package/dist/code-language/languages/shell.js.map +1 -1
- package/dist/code-language/languages/sql.d.ts.map +1 -1
- package/dist/code-language/languages/sql.js +20 -2
- package/dist/code-language/languages/sql.js.map +1 -1
- package/dist/code-language/runs.d.ts +31 -0
- package/dist/code-language/runs.d.ts.map +1 -1
- package/dist/code-language/runs.js +105 -0
- package/dist/code-language/runs.js.map +1 -1
- package/dist/code-language/stream.d.ts +22 -0
- package/dist/code-language/stream.d.ts.map +1 -1
- package/dist/code-language/stream.js +259 -9
- package/dist/code-language/stream.js.map +1 -1
- package/dist/code-language/types.d.ts +5 -4
- package/dist/code-language/types.d.ts.map +1 -1
- package/dist/codeblock/index.d.ts +26 -2
- package/dist/codeblock/index.d.ts.map +1 -1
- package/dist/codeblock/index.js +57 -4
- package/dist/codeblock/index.js.map +1 -1
- package/dist/flow/draw.d.ts +50 -0
- package/dist/flow/draw.d.ts.map +1 -1
- package/dist/flow/draw.js +132 -33
- package/dist/flow/draw.js.map +1 -1
- package/dist/flow/gl/color.d.ts +13 -0
- package/dist/flow/gl/color.d.ts.map +1 -0
- package/dist/flow/gl/color.js +82 -0
- package/dist/flow/gl/color.js.map +1 -0
- package/dist/flow/gl/index.d.ts +60 -0
- package/dist/flow/gl/index.d.ts.map +1 -0
- package/dist/flow/gl/index.js +283 -0
- package/dist/flow/gl/index.js.map +1 -0
- package/dist/flow/gl/pack.d.ts +133 -0
- package/dist/flow/gl/pack.d.ts.map +1 -0
- package/dist/flow/gl/pack.js +408 -0
- package/dist/flow/gl/pack.js.map +1 -0
- package/dist/flow/gl/renderer.d.ts +95 -0
- package/dist/flow/gl/renderer.d.ts.map +1 -0
- package/dist/flow/gl/renderer.js +459 -0
- package/dist/flow/gl/renderer.js.map +1 -0
- package/dist/flow/gl/shaders.d.ts +79 -0
- package/dist/flow/gl/shaders.d.ts.map +1 -0
- package/dist/flow/gl/shaders.js +346 -0
- package/dist/flow/gl/shaders.js.map +1 -0
- package/dist/flow/gl/text.d.ts +318 -0
- package/dist/flow/gl/text.d.ts.map +1 -0
- package/dist/flow/gl/text.js +1103 -0
- package/dist/flow/gl/text.js.map +1 -0
- package/dist/flow/index.d.ts +1 -1
- package/dist/flow/index.d.ts.map +1 -1
- package/dist/flow/index.js +619 -64
- package/dist/flow/index.js.map +1 -1
- package/dist/flow/node.d.ts +621 -72
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +2253 -1018
- package/dist/flow/node.js.map +1 -1
- package/dist/flow/paint.d.ts +35 -0
- package/dist/flow/paint.d.ts.map +1 -0
- package/dist/flow/paint.js +393 -0
- package/dist/flow/paint.js.map +1 -0
- package/dist/flow/paths.d.ts +14 -0
- package/dist/flow/paths.d.ts.map +1 -1
- package/dist/flow/paths.js +45 -19
- package/dist/flow/paths.js.map +1 -1
- package/dist/flow/scene.d.ts +584 -0
- package/dist/flow/scene.d.ts.map +1 -0
- package/dist/flow/scene.js +1453 -0
- package/dist/flow/scene.js.map +1 -0
- package/dist/flow/types.d.ts +111 -1
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/html/controls.d.ts +28 -0
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +52 -2
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/calc.d.ts +14 -0
- package/dist/html/css/calc.d.ts.map +1 -0
- package/dist/html/css/calc.js +344 -0
- package/dist/html/css/calc.js.map +1 -0
- package/dist/html/css/cascade.d.ts +262 -6
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +1373 -67
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +23 -0
- package/dist/html/css/color.d.ts.map +1 -0
- package/dist/html/css/color.js +740 -0
- package/dist/html/css/color.js.map +1 -0
- package/dist/html/css/content.d.ts +92 -0
- package/dist/html/css/content.d.ts.map +1 -0
- package/dist/html/css/content.js +388 -0
- package/dist/html/css/content.js.map +1 -0
- package/dist/html/css/counter-styles.d.ts +76 -0
- package/dist/html/css/counter-styles.d.ts.map +1 -0
- package/dist/html/css/counter-styles.js +893 -0
- package/dist/html/css/counter-styles.js.map +1 -0
- package/dist/html/css/decode.d.ts +12 -0
- package/dist/html/css/decode.d.ts.map +1 -0
- package/dist/html/css/decode.js +116 -0
- package/dist/html/css/decode.js.map +1 -0
- package/dist/html/css/parse.d.ts +110 -10
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +1473 -180
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +501 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +3729 -206
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +65 -16
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +37 -3
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +122 -48
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/css/vars.d.ts +52 -0
- package/dist/html/css/vars.d.ts.map +1 -0
- package/dist/html/css/vars.js +358 -0
- package/dist/html/css/vars.js.map +1 -0
- package/dist/html/dom.d.ts +30 -4
- package/dist/html/dom.d.ts.map +1 -1
- package/dist/html/dom.js +278 -17
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +106 -0
- package/dist/html/fonts.d.ts.map +1 -0
- package/dist/html/fonts.js +565 -0
- package/dist/html/fonts.js.map +1 -0
- package/dist/html/gif.d.ts +15 -0
- package/dist/html/gif.d.ts.map +1 -0
- package/dist/html/gif.js +229 -0
- package/dist/html/gif.js.map +1 -0
- package/dist/html/index.d.ts +50 -11
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +115 -9
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts +226 -3
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +3077 -219
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +367 -17
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +1742 -182
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +58 -0
- package/dist/html/layout/cache.d.ts.map +1 -0
- package/dist/html/layout/cache.js +224 -0
- package/dist/html/layout/cache.js.map +1 -0
- package/dist/html/layout/collapse.d.ts +45 -0
- package/dist/html/layout/collapse.d.ts.map +1 -0
- package/dist/html/layout/collapse.js +246 -0
- package/dist/html/layout/collapse.js.map +1 -0
- package/dist/html/layout/css-grid.d.ts +7 -0
- package/dist/html/layout/css-grid.d.ts.map +1 -0
- package/dist/html/layout/css-grid.js +900 -0
- package/dist/html/layout/css-grid.js.map +1 -0
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +1025 -89
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/floats.d.ts +45 -6
- package/dist/html/layout/floats.d.ts.map +1 -1
- package/dist/html/layout/floats.js +113 -14
- package/dist/html/layout/floats.js.map +1 -1
- package/dist/html/layout/grid-lines.d.ts +39 -0
- package/dist/html/layout/grid-lines.d.ts.map +1 -0
- package/dist/html/layout/grid-lines.js +119 -0
- package/dist/html/layout/grid-lines.js.map +1 -0
- package/dist/html/layout/grid.d.ts +53 -0
- package/dist/html/layout/grid.d.ts.map +1 -0
- package/dist/html/layout/grid.js +153 -0
- package/dist/html/layout/grid.js.map +1 -0
- package/dist/html/layout/inline.d.ts +154 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +3598 -175
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/shaping.d.ts +6 -0
- package/dist/html/layout/shaping.d.ts.map +1 -0
- package/dist/html/layout/shaping.js +100 -0
- package/dist/html/layout/shaping.js.map +1 -0
- package/dist/html/layout/table.d.ts +3 -0
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +681 -175
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/layout/tracks.d.ts +44 -0
- package/dist/html/layout/tracks.d.ts.map +1 -0
- package/dist/html/layout/tracks.js +349 -0
- package/dist/html/layout/tracks.js.map +1 -0
- package/dist/html/node.d.ts +169 -4
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +1429 -137
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +92 -3
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +3635 -107
- package/dist/html/paint.js.map +1 -1
- package/dist/html/resources.d.ts +60 -14
- package/dist/html/resources.d.ts.map +1 -1
- package/dist/html/resources.js +122 -36
- package/dist/html/resources.js.map +1 -1
- package/dist/html/surfaces.d.ts +23 -0
- package/dist/html/surfaces.d.ts.map +1 -0
- package/dist/html/surfaces.js +68 -0
- package/dist/html/surfaces.js.map +1 -0
- package/dist/html/svg.d.ts +80 -0
- package/dist/html/svg.d.ts.map +1 -0
- package/dist/html/svg.js +442 -0
- package/dist/html/svg.js.map +1 -0
- package/dist/html/url.d.ts +23 -0
- package/dist/html/url.d.ts.map +1 -0
- package/dist/html/url.js +80 -0
- package/dist/html/url.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/codelines.d.ts +42 -0
- package/dist/internal/codelines.d.ts.map +1 -0
- package/dist/internal/codelines.js +170 -0
- package/dist/internal/codelines.js.map +1 -0
- package/dist/internal/collate.d.ts +3 -0
- package/dist/internal/collate.d.ts.map +1 -0
- package/dist/internal/collate.js +11 -0
- package/dist/internal/collate.js.map +1 -0
- package/dist/internal/markdown/ast.d.ts +11 -0
- package/dist/internal/markdown/ast.d.ts.map +1 -1
- package/dist/internal/markdown/parse.d.ts.map +1 -1
- package/dist/internal/markdown/parse.js +109 -8
- package/dist/internal/markdown/parse.js.map +1 -1
- package/dist/internal/prewarm.d.ts +18 -0
- package/dist/internal/prewarm.d.ts.map +1 -0
- package/dist/internal/prewarm.js +42 -0
- package/dist/internal/prewarm.js.map +1 -0
- package/dist/internal/sdf.d.ts +20 -0
- package/dist/internal/sdf.d.ts.map +1 -0
- package/dist/internal/sdf.js +216 -0
- package/dist/internal/sdf.js.map +1 -0
- package/dist/internal/splice.d.ts +12 -0
- package/dist/internal/splice.d.ts.map +1 -0
- package/dist/internal/splice.js +23 -0
- package/dist/internal/splice.js.map +1 -0
- package/dist/internal/window.d.ts +12 -0
- package/dist/internal/window.d.ts.map +1 -1
- package/dist/internal/window.js +23 -6
- package/dist/internal/window.js.map +1 -1
- package/dist/maps/anchors.d.ts +6 -1
- package/dist/maps/anchors.d.ts.map +1 -1
- package/dist/maps/anchors.js +260 -1
- package/dist/maps/anchors.js.map +1 -1
- package/dist/maps/controller.d.ts +57 -0
- package/dist/maps/controller.d.ts.map +1 -1
- package/dist/maps/controller.js +97 -18
- package/dist/maps/controller.js.map +1 -1
- package/dist/maps/gl/buckets.d.ts +5 -1
- package/dist/maps/gl/buckets.d.ts.map +1 -1
- package/dist/maps/gl/buckets.js +5 -2
- package/dist/maps/gl/buckets.js.map +1 -1
- package/dist/maps/gl/build-worker.d.ts +2 -0
- package/dist/maps/gl/build-worker.d.ts.map +1 -1
- package/dist/maps/gl/build-worker.js +1 -1
- package/dist/maps/gl/build-worker.js.map +1 -1
- package/dist/maps/gl/cover.d.ts +23 -4
- package/dist/maps/gl/cover.d.ts.map +1 -1
- package/dist/maps/gl/cover.js +61 -48
- package/dist/maps/gl/cover.js.map +1 -1
- package/dist/maps/gl/placement.d.ts +21 -8
- package/dist/maps/gl/placement.d.ts.map +1 -1
- package/dist/maps/gl/placement.js +133 -58
- package/dist/maps/gl/placement.js.map +1 -1
- package/dist/maps/gl/renderer.d.ts +36 -4
- package/dist/maps/gl/renderer.d.ts.map +1 -1
- package/dist/maps/gl/renderer.js +81 -24
- package/dist/maps/gl/renderer.js.map +1 -1
- package/dist/maps/gl/shaders.d.ts +18 -17
- package/dist/maps/gl/shaders.d.ts.map +1 -1
- package/dist/maps/gl/shaders.js +34 -32
- package/dist/maps/gl/shaders.js.map +1 -1
- package/dist/maps/gl/store.d.ts +10 -0
- package/dist/maps/gl/store.d.ts.map +1 -1
- package/dist/maps/gl/store.js +47 -4
- package/dist/maps/gl/store.js.map +1 -1
- package/dist/maps/gl/text.d.ts +153 -24
- package/dist/maps/gl/text.d.ts.map +1 -1
- package/dist/maps/gl/text.js +379 -63
- package/dist/maps/gl/text.js.map +1 -1
- package/dist/maps/gl/view.d.ts +293 -2
- package/dist/maps/gl/view.d.ts.map +1 -1
- package/dist/maps/gl/view.js +317 -82
- package/dist/maps/gl/view.js.map +1 -1
- package/dist/maps/icons.d.ts +32 -0
- package/dist/maps/icons.d.ts.map +1 -0
- package/dist/maps/icons.js +151 -0
- package/dist/maps/icons.js.map +1 -0
- package/dist/maps/index.d.ts +2 -0
- package/dist/maps/index.d.ts.map +1 -1
- package/dist/maps/index.js +2 -0
- package/dist/maps/index.js.map +1 -1
- package/dist/maps/labels.d.ts +56 -4
- package/dist/maps/labels.d.ts.map +1 -1
- package/dist/maps/labels.js +213 -46
- package/dist/maps/labels.js.map +1 -1
- package/dist/maps/node.d.ts +17 -0
- package/dist/maps/node.d.ts.map +1 -1
- package/dist/maps/node.js +64 -30
- package/dist/maps/node.js.map +1 -1
- package/dist/maps/proj.d.ts +48 -0
- package/dist/maps/proj.d.ts.map +1 -1
- package/dist/maps/proj.js +73 -0
- package/dist/maps/proj.js.map +1 -1
- package/dist/maps/sources.d.ts +5 -3
- package/dist/maps/sources.d.ts.map +1 -1
- package/dist/maps/sources.js +5 -3
- package/dist/maps/sources.js.map +1 -1
- package/dist/maps/style.d.ts +35 -0
- package/dist/maps/style.d.ts.map +1 -1
- package/dist/maps/style.js.map +1 -1
- package/dist/maps/styles.d.ts +12 -0
- package/dist/maps/styles.d.ts.map +1 -1
- package/dist/maps/styles.js +120 -6
- package/dist/maps/styles.js.map +1 -1
- package/dist/maps/tiles.d.ts +31 -11
- package/dist/maps/tiles.d.ts.map +1 -1
- package/dist/maps/tiles.js +61 -27
- package/dist/maps/tiles.js.map +1 -1
- package/dist/maps/types.d.ts +28 -4
- package/dist/maps/types.d.ts.map +1 -1
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +209 -68
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/spans.d.ts.map +1 -1
- package/dist/markdown/spans.js +6 -0
- package/dist/markdown/spans.js.map +1 -1
- package/dist/qml/containers.d.ts.map +1 -1
- package/dist/qml/containers.js +6 -1
- package/dist/qml/containers.js.map +1 -1
- package/dist/qml/interaction.d.ts.map +1 -1
- package/dist/qml/interaction.js +38 -5
- package/dist/qml/interaction.js.map +1 -1
- package/dist/qml/layouts.d.ts.map +1 -1
- package/dist/qml/layouts.js +21 -6
- package/dist/qml/layouts.js.map +1 -1
- package/dist/qml/qtquick.d.ts.map +1 -1
- package/dist/qml/qtquick.js +9 -2
- package/dist/qml/qtquick.js.map +1 -1
- package/dist/qml/react.d.ts.map +1 -1
- package/dist/qml/react.js +9 -7
- package/dist/qml/react.js.map +1 -1
- package/dist/reorder/clock.d.ts +7 -0
- package/dist/reorder/clock.d.ts.map +1 -0
- package/dist/reorder/clock.js +26 -0
- package/dist/reorder/clock.js.map +1 -0
- package/dist/reorder/index.d.ts +25 -3
- package/dist/reorder/index.d.ts.map +1 -1
- package/dist/reorder/index.js +64 -19
- package/dist/reorder/index.js.map +1 -1
- package/dist/rich-text-editor/index.d.ts +0 -11
- package/dist/rich-text-editor/index.d.ts.map +1 -1
- package/dist/rich-text-editor/index.js +38 -9
- package/dist/rich-text-editor/index.js.map +1 -1
- package/dist/rich-text-editor/keymap.d.ts.map +1 -1
- package/dist/rich-text-editor/keymap.js +2 -1
- package/dist/rich-text-editor/keymap.js.map +1 -1
- package/dist/rich-text-editor/keys.d.ts +17 -6
- package/dist/rich-text-editor/keys.d.ts.map +1 -1
- package/dist/rich-text-editor/keys.js +135 -26
- package/dist/rich-text-editor/keys.js.map +1 -1
- package/dist/rich-text-editor/markdown.d.ts.map +1 -1
- package/dist/rich-text-editor/markdown.js +30 -1
- package/dist/rich-text-editor/markdown.js.map +1 -1
- package/dist/rich-text-editor/marks.d.ts +9 -0
- package/dist/rich-text-editor/marks.d.ts.map +1 -0
- package/dist/rich-text-editor/marks.js +83 -0
- package/dist/rich-text-editor/marks.js.map +1 -0
- package/dist/rich-text-editor/render.d.ts.map +1 -1
- package/dist/rich-text-editor/render.js +28 -10
- package/dist/rich-text-editor/render.js.map +1 -1
- package/dist/rich-text-editor/replace.d.ts +13 -0
- package/dist/rich-text-editor/replace.d.ts.map +1 -0
- package/dist/rich-text-editor/replace.js +64 -0
- package/dist/rich-text-editor/replace.js.map +1 -0
- package/dist/rich-text-editor/toolbar.d.ts.map +1 -1
- package/dist/rich-text-editor/toolbar.js +1 -1
- package/dist/rich-text-editor/toolbar.js.map +1 -1
- package/dist/rich-text-editor/virtual.d.ts.map +1 -1
- package/dist/rich-text-editor/virtual.js +17 -0
- package/dist/rich-text-editor/virtual.js.map +1 -1
- package/dist/richtext/node.d.ts +46 -2
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +187 -4
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +13 -8
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +18 -8
- package/dist/richtext/runs.js.map +1 -1
- package/dist/table/index.d.ts.map +1 -1
- package/dist/table/index.js +23 -0
- package/dist/table/index.js.map +1 -1
- package/dist/table/rows.d.ts +3 -2
- package/dist/table/rows.d.ts.map +1 -1
- package/dist/table/rows.js +20 -6
- package/dist/table/rows.js.map +1 -1
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +4 -0
- package/dist/terminal/index.js.map +1 -1
- package/dist/terminal/vt/pty.d.ts +13 -2
- package/dist/terminal/vt/pty.d.ts.map +1 -1
- package/dist/terminal/vt/pty.js +18 -3
- package/dist/terminal/vt/pty.js.map +1 -1
- package/dist/terminal-output/index.d.ts.map +1 -1
- package/dist/terminal-output/index.js +103 -23
- package/dist/terminal-output/index.js.map +1 -1
- package/dist/terminal-output/runs.d.ts +4 -1
- package/dist/terminal-output/runs.d.ts.map +1 -1
- package/dist/terminal-output/runs.js +5 -1
- package/dist/terminal-output/runs.js.map +1 -1
- package/dist/three/canvas.d.ts.map +1 -1
- package/dist/three/canvas.js +85 -7
- package/dist/three/canvas.js.map +1 -1
- package/dist/three/events.d.ts +22 -4
- package/dist/three/events.d.ts.map +1 -1
- package/dist/three/events.js +34 -4
- package/dist/three/events.js.map +1 -1
- package/dist/tree/index.d.ts.map +1 -1
- package/dist/tree/index.js +17 -0
- package/dist/tree/index.js.map +1 -1
- package/package.json +42 -33
- package/src/charts/node.ts +62 -26
- package/src/charts/render.ts +120 -30
- package/src/code/index.ts +36 -32
- package/src/code-editor/completion.ts +3 -2
- package/src/code-editor/index.ts +19 -0
- package/src/code-editor/node.ts +1574 -184
- package/src/code-language/index.ts +1 -1
- package/src/code-language/languages/glsl.ts +12 -2
- package/src/code-language/languages/javascript.ts +21 -4
- package/src/code-language/languages/json.ts +12 -2
- package/src/code-language/languages/shell.ts +12 -2
- package/src/code-language/languages/sql.ts +22 -2
- package/src/code-language/runs.ts +111 -1
- package/src/code-language/stream.ts +257 -17
- package/src/code-language/types.ts +5 -4
- package/src/codeblock/index.ts +68 -4
- package/src/flow/draw.ts +167 -41
- package/src/flow/gl/color.ts +85 -0
- package/src/flow/gl/index.ts +342 -0
- package/src/flow/gl/pack.ts +585 -0
- package/src/flow/gl/renderer.ts +679 -0
- package/src/flow/gl/shaders.ts +371 -0
- package/src/flow/gl/text.ts +1295 -0
- package/src/flow/index.ts +794 -67
- package/src/flow/node.ts +2496 -1256
- package/src/flow/paint.ts +511 -0
- package/src/flow/paths.ts +80 -15
- package/src/flow/scene.ts +2199 -0
- package/src/flow/types.ts +113 -1
- package/src/html/controls.ts +73 -2
- package/src/html/css/calc.ts +385 -0
- package/src/html/css/cascade.ts +1481 -62
- package/src/html/css/color.ts +755 -0
- package/src/html/css/content.ts +417 -0
- package/src/html/css/counter-styles.ts +973 -0
- package/src/html/css/decode.ts +130 -0
- package/src/html/css/parse.ts +1514 -160
- package/src/html/css/style.ts +4131 -223
- package/src/html/css/ua.ts +66 -17
- package/src/html/css/values.ts +150 -50
- package/src/html/css/vars.ts +348 -0
- package/src/html/dom.ts +284 -17
- package/src/html/fonts.ts +641 -0
- package/src/html/gif.ts +233 -0
- package/src/html/index.ts +180 -20
- package/src/html/layout/block.ts +3560 -208
- package/src/html/layout/boxes.ts +2141 -190
- package/src/html/layout/cache.ts +289 -0
- package/src/html/layout/collapse.ts +317 -0
- package/src/html/layout/css-grid.ts +1063 -0
- package/src/html/layout/flex.ts +1115 -106
- package/src/html/layout/floats.ts +128 -14
- package/src/html/layout/grid-lines.ts +141 -0
- package/src/html/layout/grid.ts +181 -0
- package/src/html/layout/inline.ts +4187 -192
- package/src/html/layout/shaping.ts +140 -0
- package/src/html/layout/table.ts +696 -186
- package/src/html/layout/tracks.ts +437 -0
- package/src/html/node.ts +1585 -156
- package/src/html/paint.ts +4582 -267
- package/src/html/resources.ts +161 -50
- package/src/html/surfaces.ts +81 -0
- package/src/html/svg.ts +504 -0
- package/src/html/url.ts +86 -0
- package/src/index.ts +2 -0
- package/src/internal/codelines.ts +200 -0
- package/src/internal/collate.ts +12 -0
- package/src/internal/markdown/ast.ts +11 -0
- package/src/internal/markdown/parse.ts +154 -11
- package/src/internal/prewarm.ts +45 -0
- package/src/internal/sdf.ts +227 -0
- package/src/internal/splice.ts +25 -0
- package/src/internal/window.ts +36 -5
- package/src/maps/anchors.ts +307 -1
- package/src/maps/controller.ts +116 -31
- package/src/maps/gl/buckets.ts +5 -1
- package/src/maps/gl/build-worker.ts +13 -2
- package/src/maps/gl/cover.ts +79 -44
- package/src/maps/gl/placement.ts +221 -69
- package/src/maps/gl/renderer.ts +104 -20
- package/src/maps/gl/shaders.ts +35 -32
- package/src/maps/gl/store.ts +66 -17
- package/src/maps/gl/text.ts +444 -71
- package/src/maps/gl/view.ts +361 -96
- package/src/maps/icons.ts +183 -0
- package/src/maps/index.ts +4 -0
- package/src/maps/labels.ts +269 -49
- package/src/maps/node.ts +82 -49
- package/src/maps/proj.ts +87 -0
- package/src/maps/sources.ts +5 -3
- package/src/maps/style.ts +32 -0
- package/src/maps/styles.ts +215 -4
- package/src/maps/tiles.ts +64 -31
- package/src/maps/types.ts +28 -4
- package/src/markdown/index.ts +293 -92
- package/src/markdown/spans.ts +5 -0
- package/src/qml/containers.tsx +6 -1
- package/src/qml/interaction.tsx +42 -5
- package/src/qml/layouts.tsx +21 -6
- package/src/qml/qtquick.tsx +9 -2
- package/src/qml/react.tsx +7 -5
- package/src/reorder/clock.ts +27 -0
- package/src/reorder/index.ts +100 -27
- package/src/rich-text-editor/index.ts +37 -12
- package/src/rich-text-editor/keymap.ts +1 -1
- package/src/rich-text-editor/keys.ts +135 -26
- package/src/rich-text-editor/markdown.ts +36 -1
- package/src/rich-text-editor/marks.ts +101 -0
- package/src/rich-text-editor/render.ts +49 -12
- package/src/rich-text-editor/replace.ts +85 -0
- package/src/rich-text-editor/toolbar.ts +1 -1
- package/src/rich-text-editor/virtual.ts +25 -0
- package/src/richtext/node.ts +235 -14
- package/src/richtext/runs.ts +23 -12
- package/src/table/index.ts +29 -0
- package/src/table/rows.ts +18 -8
- package/src/terminal/index.ts +4 -0
- package/src/terminal/vt/pty.ts +16 -2
- package/src/terminal-output/index.ts +129 -31
- package/src/terminal-output/runs.ts +16 -2
- package/src/three/canvas.ts +102 -8
- package/src/three/events.ts +49 -9
- package/src/tree/index.ts +19 -0
package/dist/html/css/parse.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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 +
|
|
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
|
-
// @
|
|
64
|
-
//
|
|
168
|
+
// @keyframes, @page: nothing to do, and the block was already
|
|
169
|
+
// consumed.
|
|
65
170
|
continue;
|
|
66
171
|
}
|
|
67
|
-
|
|
68
|
-
|
|
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,
|
|
71
|
-
const block = readBlock(source,
|
|
178
|
+
const prelude = source.slice(i, blockAt).trim();
|
|
179
|
+
const block = readBlock(source, blockAt);
|
|
72
180
|
i = block.end;
|
|
73
|
-
|
|
181
|
+
const selectors = rawSelectors(prelude);
|
|
182
|
+
if (!selectors)
|
|
74
183
|
continue;
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
105
|
-
if (
|
|
106
|
-
|
|
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
|
-
|
|
119
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
if (
|
|
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 =
|
|
818
|
+
i = identEnd(selector, i + 1);
|
|
184
819
|
}
|
|
185
820
|
else if (c === '.') {
|
|
186
821
|
classes += 1;
|
|
187
|
-
i =
|
|
822
|
+
i = identEnd(selector, i + 1);
|
|
188
823
|
}
|
|
189
824
|
else if (c === '[') {
|
|
190
825
|
classes += 1;
|
|
191
|
-
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 =
|
|
831
|
+
i = identEnd(selector, i + 2);
|
|
197
832
|
}
|
|
198
833
|
else {
|
|
199
|
-
|
|
200
|
-
|
|
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 =
|
|
844
|
+
i = componentEnd(selector, i);
|
|
203
845
|
}
|
|
204
846
|
}
|
|
205
|
-
else if (
|
|
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 =
|
|
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
|
-
|
|
242
|
-
|
|
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
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
/**
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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 (
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
296
|
-
return -1; // a stray declaration
|
|
1173
|
+
i = opens(code) ? componentEnd(text, i) : i + 1;
|
|
297
1174
|
}
|
|
298
|
-
return
|
|
1175
|
+
return text.length;
|
|
299
1176
|
}
|
|
300
|
-
/**
|
|
301
|
-
function
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
for (let
|
|
305
|
-
|
|
306
|
-
if (
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
|
1347
|
+
return { declarations, nested };
|
|
332
1348
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
-
|
|
1396
|
+
out += c;
|
|
349
1397
|
}
|
|
350
|
-
if (c === '"' || c === "'")
|
|
1398
|
+
else if (c === '"' || c === "'") {
|
|
351
1399
|
quote = c;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
else
|
|
355
|
-
|
|
356
|
-
else if (depth === 0 && (c === ';' || c === '{'))
|
|
357
|
-
break;
|
|
1400
|
+
out += c;
|
|
1401
|
+
}
|
|
1402
|
+
else
|
|
1403
|
+
out += c === '&' ? by : c;
|
|
358
1404
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|