feat(css-parser, formatter): handle ratio inside media queries
ADD
internal/ast/css/core/CSSRatio.ts
+import {CSSNumber, NodeBaseWithComments} from "@internal/ast";
+import {createBuilder} from "../../utils";
+
+export interface CSSRatio extends NodeBaseWithComments {
+ readonly type: "CSSRatio";
+ readonly numerator: CSSNumber;
+ readonly denominator: CSSNumber;
+}
+
+export const cssRatio = createBuilder<CSSRatio>(
+ "CSSRatio",
+ {
+ bindingKeys: {},
+ visitorKeys: {
+ numerator: true,
+ denominator: true,
+ },
+ },
+);

MODIFY
internal/ast/css/media/CSSMediaFeatureValue.ts
internal/ast/css/media/CSSMediaFeatureValue.ts
@@ -2,13 +2,14 @@ import {
CSSDimension,
CSSIdentifier,
CSSNumber,
+ CSSRatio,
NodeBaseWithComments,
} from "@internal/ast";
import {createBuilder} from "../../utils";
export interface CSSMediaFeatureValue extends NodeBaseWithComments {
readonly type: "CSSMediaFeatureValue";
- readonly value: CSSNumber | CSSDimension | CSSIdentifier;
+ readonly value: CSSNumber | CSSDimension | CSSIdentifier | CSSRatio;
}
export const cssMediaFeatureValue = createBuilder<CSSMediaFeatureValue>(

MODIFY
internal/ast/index.ts
internal/ast/index.ts
@@ -12,7 +12,7 @@ export * from "./unions";
export {bindingKeys, nodeNames, visitorKeys} from "./utils";
export type AnyNodes = AnyNode | (AnyNode[]);
-/* GENERATED:START(hash:47f3274dc1afbb79b39d6d2c678490d88fa9c2a5,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/ast` to update. */
+/* GENERATED:START(hash:18258adf54b8055f532ed86bf9a65a6224b77b27,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/ast` to update. */
export * from "./common/comments/CommentBlock";
export * from "./common/comments/CommentLine";
export * from "./css/page/CSSAtPage";
@@ -68,6 +68,7 @@ export * from "./css/core/CSSPercentage";
export * from "./css/selectors/CSSPseudoClassSelector";
export * from "./css/selectors/CSSPseudoElementSelector";
export * from "./css/page/CSSPseudoPage";
+export * from "./css/core/CSSRatio";
export * from "./css/core/CSSRaw";
export * from "./css/core/CSSRoot";
export * from "./css/core/CSSRule";
@@ -373,6 +374,7 @@ export type AnyNode =
| n.CSSPseudoClassSelector
| n.CSSPseudoElementSelector
| n.CSSPseudoPage
+ | n.CSSRatio
| n.CSSRaw
| n.CSSRoot
| n.CSSRule

MODIFY
internal/css-parser/parser/media/feature.ts
internal/css-parser/parser/media/feature.ts
@@ -8,6 +8,7 @@ import {
CSSMediaFeaturePlain,
CSSMediaFeatureValue,
CSSNumber,
+ CSSRatio,
} from "@internal/ast";
import {matchToken, nextToken, readToken} from "@internal/css-parser/tokenizer";
import {descriptions} from "@internal/diagnostics";
@@ -55,7 +56,7 @@ export function parseMediaFeatureValue(
}
const token = parser.getToken();
const start = parser.getPosition();
- let value: CSSDimension | CSSIdentifier | CSSNumber | undefined = undefined;
+ let value: CSSDimension | CSSIdentifier | CSSNumber | CSSRatio | undefined = undefined;
if (token.type === "Ident") {
nextToken(parser);
@@ -77,8 +78,7 @@ export function parseMediaFeatureValue(
},
);
} else if (token.type === "Number") {
- nextToken(parser);
- value = parser.finishNode(
+ const firstNumber = parser.finishNode(
start,
{
type: "CSSNumber",
@@ -86,6 +86,50 @@ export function parseMediaFeatureValue(
value: token.value,
},
);
+ nextToken(parser);
+
+ while (matchToken(parser, "Whitespace")) {
+ readToken(parser, "Whitespace");
+ }
+
+ if (
+ matchToken(parser, "Delim") &&
+ (parser.getToken() as Tokens["Delim"]).value === "/"
+ ) {
+ nextToken(parser);
+ while (matchToken(parser, "Whitespace")) {
+ readToken(parser, "Whitespace");
+ }
+ const maybeNumberToken = parser.getToken();
+ if (maybeNumberToken.type === "Number") {
+ const secondStart = parser.getPosition();
+ nextToken(parser);
+ value = parser.finishNode(
+ start,
+ {
+ type: "CSSRatio",
+ numerator: firstNumber,
+ denominator: parser.finishNode(
+ secondStart,
+ {
+ type: "CSSNumber",
+ raw: maybeNumberToken.raw,
+ value: maybeNumberToken.value,
+ },
+ ),
+ },
+ );
+ } else {
+ nextToken(parser);
+ parser.unexpectedDiagnostic({
+ description: descriptions.CSS_PARSER.MEDIA_QUERY_FEATURE_INCORRENT_RATIO,
+ token: maybeNumberToken,
+ });
+ return undefined;
+ }
+ } else {
+ value = firstNumber;
+ }
} else {
parser.unexpectedDiagnostic({
description: descriptions.CSS_PARSER.MEDIA_QUERY_FEATURE_UNEXPECTED_VALUE,

MODIFY
internal/css-parser/parser/media/range.ts
internal/css-parser/parser/media/range.ts
@@ -123,7 +123,7 @@ export function parseMediaFeatureRange(
}
parser.unexpectedDiagnostic({
- description: descriptions.CSS_PARSER.MALFORMED_MEDIA_QUERY,
+ description: descriptions.CSS_PARSER.MEDIA_QUERY_MALFORMED_RANGE,
start,
end: parser.getPosition(),
});

ADD
internal/css-parser/test-fixtures/invalid/media/ratio/input.css
+@media (aspect-ratio: 12/) {}

ADD
internal/css-parser/test-fixtures/invalid/media/ratio/input.test.md
+# `index.test.ts`
+
+**DO NOT MODIFY**. This file has been autogenerated. Run `rome test internal/css-parser/index.test.ts --update-snapshots` to update.
+
+## `invalid > media > ratio`
+
+```javascript
+CSSRoot {
+ body: [
+ CSSAtRule {
+ name: "media"
+ prelude: []
+ block: CSSMediaQueryList {
+ prelude: []
+ block: CSSBlock {
+ value: []
+ startingTokenValue: "{"
+ loc: SourceLocation invalid/media/ratio/input.css 1:27-1:29
+ }
+ loc: SourceLocation invalid/media/ratio/input.css 1:6-1:29
+ }
+ loc: SourceLocation invalid/media/ratio/input.css 1:0-1:29
+ }
+ ]
+ comments: []
+ corrupt: false
+ diagnostics: [
+ {
+ origins: [{entity: "ParserCore<css>"}]
+ description: {
+ advice: []
+ category: ["parse"]
+ categoryValue: "css"
+ message: RAW_MARKUP {value: "The ratio value is not syntactically correct; you always have to provide a denominator after the slash."}
+ }
+ location: {
+ language: "css"
+ path: RelativePath<invalid/media/ratio/input.css>
+ end: Position 1:26
+ start: Position 1:25
+ }
+ }
+ ]
+ path: RelativePath<invalid/media/ratio/input.css>
+ loc: SourceLocation invalid/media/ratio/input.css 1:0-1:29
+}
+```

ADD
internal/css-parser/test-fixtures/media/ratio/input.css
+@media (aspect-ratio: 12/9) {}

ADD
internal/css-parser/test-fixtures/media/ratio/input.test.md
+# `index.test.ts`
+
+**DO NOT MODIFY**. This file has been autogenerated. Run `rome test internal/css-parser/index.test.ts --update-snapshots` to update.
+
+## `media > ratio`
+
+```javascript
+CSSRoot {
+ body: [
+ CSSAtRule {
+ name: "media"
+ prelude: []
+ block: CSSMediaQueryList {
+ prelude: [
+ CSSMediaQuery {
+ value: CSSMediaCondition {
+ value: [
+ CSSMediaInParens {
+ value: CSSMediaFeature {
+ value: CSSMediaFeaturePlain {
+ name: CSSMediaFeatureName {
+ value: "aspect-ratio"
+ loc: SourceLocation media/ratio/input.css 1:8-1:20
+ }
+ value: CSSMediaFeatureValue {
+ value: CSSRatio {
+ denominator: CSSNumber {
+ value: 9
+ raw: "9"
+ loc: SourceLocation media/ratio/input.css 1:25-1:26
+ }
+ numerator: CSSNumber {
+ value: 12
+ raw: "12"
+ loc: SourceLocation media/ratio/input.css 1:22-1:22
+ }
+ loc: SourceLocation media/ratio/input.css 1:22-1:26
+ }
+ loc: SourceLocation media/ratio/input.css 1:22-1:26
+ }
+ loc: SourceLocation media/ratio/input.css 1:8-1:26
+ }
+ loc: SourceLocation media/ratio/input.css 1:8-1:26
+ }
+ loc: SourceLocation media/ratio/input.css 1:7-1:27
+ }
+ ]
+ loc: SourceLocation media/ratio/input.css 1:7-1:28
+ }
+ loc: SourceLocation media/ratio/input.css 1:6-1:28
+ }
+ ]
+ block: CSSBlock {
+ value: []
+ startingTokenValue: "{"
+ loc: SourceLocation media/ratio/input.css 1:28-1:30
+ }
+ loc: SourceLocation media/ratio/input.css 1:6-1:30
+ }
+ loc: SourceLocation media/ratio/input.css 1:0-1:30
+ }
+ ]
+ comments: []
+ corrupt: false
+ diagnostics: []
+ path: RelativePath<media/ratio/input.css>
+ loc: SourceLocation media/ratio/input.css 1:0-1:30
+}
+```

MODIFY
internal/diagnostics/descriptions/parsers/cssParser.ts
internal/diagnostics/descriptions/parsers/cssParser.ts
@@ -138,9 +138,9 @@ export const cssParser = createDiagnosticsCategory({
MEDIA_QUERY_DEPRECATED_MEDIA_TYPE: (wrongValue: string) => ({
message: markup`The media type <emphasis>${wrongValue}</emphasis> is deprecated.`,
}),
- MEDIA_QUERY_UNKNOWN_MEDIA_FEATURES: {},
- MEDIA_QUERY_UNKNOWN_MEDIA_FEATURE_VALUE: {},
- MALFORMED_MEDIA_QUERY: {},
+ MEDIA_QUERY_MALFORMED_RANGE: {
+ message: markup`The range value provided to the media query is not correct`,
+ },
MEDIA_QUERY_EXPECTED_PARENTHESIS: {
message: markup`A left parenthesis is expected in this position.`,
},
@@ -163,6 +163,10 @@ export const cssParser = createDiagnosticsCategory({
message: markup`The media feature is not grammatically correct`,
},
+ MEDIA_QUERY_FEATURE_INCORRENT_RATIO: {
+ message: markup`The ratio value is not syntactically correct; you always have to provide a denominator after the slash.`,
+ },
+
AT_SUPPORTS_MALFORMED: {
message: markup`The rule @supports is not syntactically correct`,
advice: [

ADD
internal/formatter/builders/css/core/CSSRatio.ts
+import {CSSRatio} from "@internal/ast";
+import {Builder, Token, concat} from "@internal/formatter";
+
+export default function CSSRatio(builder: Builder, node: CSSRatio): Token {
+ return concat([
+ builder.tokenize(node.numerator, node),
+ "/",
+ builder.tokenize(node.denominator, node),
+ ]);
+}

MODIFY
internal/formatter/builders/index.ts
internal/formatter/builders/index.ts
@@ -14,7 +14,7 @@ const builders: ExtendedMap<string, BuilderMethod<any>> = new ExtendedMap(
);
export default builders;
-/* GENERATED:START(hash:6daa229af34e4d524f9dff506187807e2f62444a,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/ast` to update. */
+/* GENERATED:START(hash:327dbbcbe40d0b51a3a35cd9b34bae51635b2a5a,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/ast` to update. */
import CommentBlock from "./common/comments/CommentBlock";
builders.set("CommentBlock", CommentBlock);
import CommentLine from "./common/comments/CommentLine";
@@ -125,6 +125,8 @@ import CSSPseudoElementSelector from "./css/selectors/CSSPseudoElementSelector";
builders.set("CSSPseudoElementSelector", CSSPseudoElementSelector);
import CSSPseudoPage from "./css/page/CSSPseudoPage";
builders.set("CSSPseudoPage", CSSPseudoPage);
+import CSSRatio from "./css/core/CSSRatio";
+builders.set("CSSRatio", CSSRatio);
import CSSRaw from "./css/core/CSSRaw";
builders.set("CSSRaw", CSSRaw);
import CSSRoot from "./css/core/CSSRoot";

ADD
internal/formatter/test-fixtures/css/media/ratio/input.css
+@media (aspect-ratio: 12
+/ 9) {}

ADD
internal/formatter/test-fixtures/css/media/ratio/input.test.md
+# `index.test.ts`
+
+**DO NOT MODIFY**. This file has been autogenerated. Run `rome test internal/formatter/index.test.ts --update-snapshots` to update.
+
+## `css > media > ratio`
+
+### `Diagnostics`
+
+```
+
+```
+
+### `Input`
+
+```css
+@media (aspect-ratio: 12
+/ 9) {}
+
+```
+
+### `Output`
+
+```css
+@media (aspect-ratio: 12/9) {
+}
+
+
+```

feat(css-parser, formatter): handle ratio inside media queries (#1494)