From 3dcc4d96cddbee7da914e01017218e485fb8b4e5 Mon Sep 17 00:00:00 2001 From: Jan Nicklas Date: Fri, 31 May 2024 14:43:36 +0200 Subject: [PATCH] add support for @starting-style --- src/parse/index.ts | 27 ++++++++++++++++++++++++ src/stringify/compiler.ts | 23 ++++++++++++++++++++ src/type.ts | 9 +++++++- test/cases/starting-style/ast.json | 1 + test/cases/starting-style/compressed.css | 1 + test/cases/starting-style/input.css | 21 ++++++++++++++++++ test/cases/starting-style/output.css | 21 ++++++++++++++++++ 7 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test/cases/starting-style/ast.json create mode 100644 test/cases/starting-style/compressed.css create mode 100644 test/cases/starting-style/input.css create mode 100644 test/cases/starting-style/output.css diff --git a/src/parse/index.ts b/src/parse/index.ts index a4ff2645..1c7af682 100644 --- a/src/parse/index.ts +++ b/src/parse/index.ts @@ -19,6 +19,7 @@ import { CssNamespaceAST, CssPageAST, CssRuleAST, + CssStartingStyleAST, CssStylesheetAST, CssSupportsAST, CssTypes, @@ -680,6 +681,31 @@ export const parse = ( }); } + /** + * Parse starting style. + */ + function atstartingstyle(): CssStartingStyleAST | void { + const pos = position(); + const m = match(/^@starting-style\s*/); + if (!m) { + return; + } + + if (!open()) { + return error("@starting-style missing '{'"); + } + const style = comments().concat(rules()); + + if (!close()) { + return error("@starting-style missing '}'"); + } + + return pos({ + type: CssTypes.startingStyle, + rules: style, + }); + } + /** * Parse import */ @@ -742,6 +768,7 @@ export const parse = ( athost() || atfontface() || atcontainer() || + atstartingstyle() || atlayer() ); } diff --git a/src/stringify/compiler.ts b/src/stringify/compiler.ts index 221b1b57..9eef0eeb 100644 --- a/src/stringify/compiler.ts +++ b/src/stringify/compiler.ts @@ -17,6 +17,7 @@ import { CssNamespaceAST, CssPageAST, CssRuleAST, + CssStartingStyleAST, CssStylesheetAST, CssSupportsAST, CssTypes, @@ -92,6 +93,8 @@ class Compiler { return this.namespace(node); case CssTypes.page: return this.page(node); + case CssTypes.startingStyle: + return this.startingStyle(node); case CssTypes.supports: return this.supports(node); } @@ -242,6 +245,26 @@ class Compiler { return this.emit('@namespace ' + node.namespace + ';', node.position); } + /** + * Visit container node. + */ + startingStyle(node: CssStartingStyleAST) { + if (this.compress) { + return ( + this.emit('@starting-style', node.position) + + this.emit('{') + + this.mapVisit(node.rules) + + this.emit('}') + ); + } + return ( + this.emit(this.indent() + '@starting-style', node.position) + + this.emit(' {\n' + this.indent(1)) + + this.mapVisit(node.rules, '\n\n') + + this.emit('\n' + this.indent(-1) + this.indent() + '}') + ); + } + /** * Visit supports node. */ diff --git a/src/type.ts b/src/type.ts index de8cbdcd..06b89a17 100644 --- a/src/type.ts +++ b/src/type.ts @@ -19,6 +19,7 @@ export enum CssTypes { media = 'media', namespace = 'namespace', page = 'page', + startingStyle = 'starting-style', supports = 'supports', } @@ -125,6 +126,11 @@ export type CssSupportsAST = CssCommonPositionAST & { rules: Array; }; +export type CssStartingStyleAST = CssCommonPositionAST & { + type: CssTypes.startingStyle; + rules: Array; +}; + export type CssAtRuleAST = | CssRuleAST | CssCommentAST @@ -140,7 +146,8 @@ export type CssAtRuleAST = | CssMediaAST | CssNamespaceAST | CssPageAST - | CssSupportsAST; + | CssSupportsAST + | CssStartingStyleAST; export type CssAllNodesAST = | CssAtRuleAST diff --git a/test/cases/starting-style/ast.json b/test/cases/starting-style/ast.json new file mode 100644 index 00000000..a8d1931b --- /dev/null +++ b/test/cases/starting-style/ast.json @@ -0,0 +1 @@ +{"type":"stylesheet","stylesheet":{"source":"input.css","rules":[{"type":"starting-style","rules":[{"type":"rule","selectors":["h2"],"declarations":[{"type":"declaration","property":"font-size","value":"1.5em","position":{"start":{"line":3,"column":5},"end":{"line":3,"column":21},"source":"input.css"}}],"position":{"start":{"line":2,"column":3},"end":{"line":4,"column":4},"source":"input.css"}}],"position":{"start":{"line":1,"column":1},"end":{"line":5,"column":2},"source":"input.css"}},{"type":"media","media":"screen","rules":[{"type":"starting-style","rules":[{"type":"rule","selectors":["h2"],"declarations":[{"type":"declaration","property":"font-size","value":"1.5em","position":{"start":{"line":10,"column":7},"end":{"line":10,"column":23},"source":"input.css"}}],"position":{"start":{"line":9,"column":5},"end":{"line":11,"column":6},"source":"input.css"}}],"position":{"start":{"line":8,"column":3},"end":{"line":12,"column":4},"source":"input.css"}}],"position":{"start":{"line":7,"column":1},"end":{"line":13,"column":2},"source":"input.css"}},{"type":"starting-style","rules":[{"type":"media","media":"screen","rules":[{"type":"rule","selectors":["h2"],"declarations":[{"type":"declaration","property":"font-size","value":"1.5em","position":{"start":{"line":18,"column":7},"end":{"line":18,"column":23},"source":"input.css"}}],"position":{"start":{"line":17,"column":5},"end":{"line":19,"column":6},"source":"input.css"}}],"position":{"start":{"line":16,"column":3},"end":{"line":20,"column":4},"source":"input.css"}}],"position":{"start":{"line":15,"column":1},"end":{"line":21,"column":2},"source":"input.css"}}],"parsingErrors":[]}} \ No newline at end of file diff --git a/test/cases/starting-style/compressed.css b/test/cases/starting-style/compressed.css new file mode 100644 index 00000000..6eedbe3e --- /dev/null +++ b/test/cases/starting-style/compressed.css @@ -0,0 +1 @@ +@starting-style{h2{font-size:1.5em;}}@media screen{@starting-style{h2{font-size:1.5em;}}}@starting-style{@media screen{h2{font-size:1.5em;}}} \ No newline at end of file diff --git a/test/cases/starting-style/input.css b/test/cases/starting-style/input.css new file mode 100644 index 00000000..494d9ee7 --- /dev/null +++ b/test/cases/starting-style/input.css @@ -0,0 +1,21 @@ +@starting-style { + h2 { + font-size: 1.5em; + } +} + +@media screen { + @starting-style { + h2 { + font-size: 1.5em; + } + } +} + +@starting-style { + @media screen { + h2 { + font-size: 1.5em; + } + } +} \ No newline at end of file diff --git a/test/cases/starting-style/output.css b/test/cases/starting-style/output.css new file mode 100644 index 00000000..494d9ee7 --- /dev/null +++ b/test/cases/starting-style/output.css @@ -0,0 +1,21 @@ +@starting-style { + h2 { + font-size: 1.5em; + } +} + +@media screen { + @starting-style { + h2 { + font-size: 1.5em; + } + } +} + +@starting-style { + @media screen { + h2 { + font-size: 1.5em; + } + } +} \ No newline at end of file