Skip to content

Commit d42ca04

Browse files
authored
Closes #5876. Adds single value function support to media queries and media query range syntax / MQ level 4 support. (#8430)
1 parent cfe26d8 commit d42ca04

File tree

9 files changed

+161
-65
lines changed

9 files changed

+161
-65
lines changed

src/compiler/parse/read/css-tree-cq/css_tree_parse.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,7 @@
33
// `css-tree` Node module directly. This allows the production build of Svelte to work correctly.
44
import { fork } from '../../../../../node_modules/css-tree/dist/csstree.esm.js';
55

6-
import * as Comparison from './node/comparison';
7-
import * as ContainerFeature from './node/container_feature';
8-
import * as ContainerFeatureRange from './node/container_feature_range';
9-
import * as ContainerFeatureStyle from './node/container_feature_style';
10-
import * as ContainerQuery from './node/container_query';
11-
import * as QueryCSSFunction from './node/query_css_function';
6+
import * as node from './node';
127

138
/**
149
* Extends `css-tree` for container query support by forking and adding new nodes and at-rule support for `@container`.
@@ -30,14 +25,7 @@ const cqSyntax = fork({
3025
}
3126
}
3227
},
33-
node: { // extend node types
34-
Comparison,
35-
ContainerFeature,
36-
ContainerFeatureRange,
37-
ContainerFeatureStyle,
38-
ContainerQuery,
39-
QueryCSSFunction
40-
}
28+
node
4129
});
4230

4331
export const parse = cqSyntax.parse;

src/compiler/parse/read/css-tree-cq/node/container_query.ts

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,28 @@
11
// @ts-nocheck
22
import {
3-
EOF,
43
WhiteSpace,
54
Comment,
6-
Delim,
75
Function,
86
Ident,
9-
LeftParenthesis,
10-
RightParenthesis,
11-
LeftCurlyBracket,
12-
Colon
7+
LeftParenthesis
138
} from 'css-tree/tokenizer';
149

10+
import { lookahead_is_range } from './lookahead_is_range';
11+
1512
const CONTAINER_QUERY_KEYWORDS = new Set(['none', 'and', 'not', 'or']);
1613

1714
export const name = 'ContainerQuery';
1815
export const structure = {
1916
name: 'Identifier',
2017
children: [[
2118
'Identifier',
22-
'ContainerFeature',
23-
'ContainerFeatureRange',
19+
'QueryFeature',
20+
'QueryFeatureRange',
2421
'ContainerFeatureStyle',
2522
'WhiteSpace'
2623
]]
2724
};
2825

29-
/**
30-
* Looks ahead to determine if query feature is a range query. This involves locating at least one delimiter and no
31-
* colon tokens.
32-
*
33-
* @returns {boolean} Is potential range query.
34-
*/
35-
function lookahead_is_range() {
36-
let type;
37-
let offset = 0;
38-
39-
let count = 0;
40-
let delim_found = false;
41-
let no_colon = true;
42-
43-
// A range query has maximum 5 tokens when formatted as 'mf-range' /
44-
// '<mf-value> <mf-lt> <mf-name> <mf-lt> <mf-value>'. So only look ahead maximum of 6 non-whitespace tokens.
45-
do {
46-
type = this.lookupNonWSType(offset++);
47-
if (type !== WhiteSpace) {
48-
count++;
49-
}
50-
if (type === Delim) {
51-
delim_found = true;
52-
}
53-
if (type === Colon) {
54-
no_colon = false;
55-
}
56-
if (type === LeftCurlyBracket || type === RightParenthesis) {
57-
break;
58-
}
59-
} while (type !== EOF && count <= 6);
60-
61-
return delim_found && no_colon;
62-
}
63-
6426
export function parse() {
6527
const start = this.tokenStart;
6628
const children = this.createList();
@@ -98,7 +60,7 @@ export function parse() {
9860

9961
case LeftParenthesis:
10062
// Lookahead to determine if range feature.
101-
child = lookahead_is_range.call(this) ? this.ContainerFeatureRange() : this.ContainerFeature();
63+
child = lookahead_is_range.call(this) ? this.QueryFeatureRange() : this.QueryFeature();
10264
break;
10365

10466
default:
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export * as Comparison from './comparison';
2+
export * as ContainerFeatureStyle from './container_feature_style';
3+
export * as ContainerQuery from './container_query';
4+
export * as MediaQuery from './media_query';
5+
export * as QueryFeature from './query_feature';
6+
export * as QueryFeatureRange from './query_feature_range';
7+
export * as QueryCSSFunction from './query_css_function';
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// @ts-nocheck
2+
import {
3+
EOF,
4+
WhiteSpace,
5+
Delim,
6+
RightParenthesis,
7+
LeftCurlyBracket,
8+
Colon
9+
} from 'css-tree/tokenizer';
10+
11+
/**
12+
* Looks ahead to determine if query feature is a range query. This involves locating at least one delimiter and no
13+
* colon tokens.
14+
*
15+
* @returns {boolean} Is potential range query.
16+
*/
17+
export function lookahead_is_range() {
18+
let type;
19+
let offset = 0;
20+
21+
let count = 0;
22+
let delim_found = false;
23+
let no_colon = true;
24+
25+
// A range query has maximum 5 tokens when formatted as 'mf-range' /
26+
// '<mf-value> <mf-lt> <mf-name> <mf-lt> <mf-value>'. So only look ahead maximum of 6 non-whitespace tokens.
27+
do {
28+
type = this.lookupNonWSType(offset++);
29+
if (type !== WhiteSpace) {
30+
count++;
31+
}
32+
if (type === Delim) {
33+
delim_found = true;
34+
}
35+
if (type === Colon) {
36+
no_colon = false;
37+
}
38+
if (type === LeftCurlyBracket || type === RightParenthesis) {
39+
break;
40+
}
41+
} while (type !== EOF && count <= 6);
42+
43+
return delim_found && no_colon;
44+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// @ts-nocheck
2+
import {
3+
WhiteSpace,
4+
Comment,
5+
Ident,
6+
LeftParenthesis
7+
} from 'css-tree/tokenizer';
8+
9+
import { lookahead_is_range } from './lookahead_is_range';
10+
11+
export const name = 'MediaQuery';
12+
export const structure = {
13+
children: [[
14+
'Identifier',
15+
'QueryFeature',
16+
'QueryFeatureRange',
17+
'WhiteSpace'
18+
]]
19+
};
20+
21+
export function parse() {
22+
const children = this.createList();
23+
let child = null;
24+
25+
this.skipSC();
26+
27+
scan:
28+
while (!this.eof) {
29+
switch (this.tokenType) {
30+
case Comment:
31+
case WhiteSpace:
32+
this.next();
33+
continue;
34+
35+
case Ident:
36+
child = this.Identifier();
37+
break;
38+
39+
case LeftParenthesis:
40+
// Lookahead to determine if range feature.
41+
child = lookahead_is_range.call(this) ? this.QueryFeatureRange() : this.QueryFeature();
42+
break;
43+
44+
default:
45+
break scan;
46+
}
47+
48+
children.push(child);
49+
}
50+
51+
if (child === null) {
52+
this.error('Identifier or parenthesis is expected');
53+
}
54+
55+
return {
56+
type: 'MediaQuery',
57+
loc: this.getLocationFromList(children),
58+
children
59+
};
60+
}
61+
62+
export function generate(node) {
63+
this.children(node);
64+
}

src/compiler/parse/read/css-tree-cq/node/container_feature.ts renamed to src/compiler/parse/read/css-tree-cq/node/query_feature.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
Delim
1111
} from 'css-tree/tokenizer';
1212

13-
export const name = 'ContainerFeature';
13+
export const name = 'QueryFeature';
1414
export const structure = {
1515
name: String,
1616
value: ['Identifier', 'Number', 'Dimension', 'QueryCSSFunction', 'Ratio', null]
@@ -62,7 +62,7 @@ export function parse() {
6262
this.eat(RightParenthesis);
6363

6464
return {
65-
type: 'ContainerFeature',
65+
type: 'QueryFeature',
6666
loc: this.getLocation(start, this.tokenStart),
6767
name,
6868
value

src/compiler/parse/read/css-tree-cq/node/container_feature_range.ts renamed to src/compiler/parse/read/css-tree-cq/node/query_feature_range.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
WhiteSpace
1111
} from 'css-tree/tokenizer';
1212

13-
export const name = 'ContainerFeatureRange';
13+
export const name = 'QueryFeatureRange';
1414
export const structure = {
1515
name: String,
1616
value: ['Identifier', 'Number', 'Comparison', 'Dimension', 'QueryCSSFunction', 'Ratio', null]
@@ -30,6 +30,7 @@ function lookup_non_WS_type_and_value(offset, type, referenceStr) {
3030
}
3131

3232
export function parse() {
33+
const start = this.tokenStart;
3334
const children = this.createList();
3435
let child = null;
3536

@@ -75,8 +76,8 @@ export function parse() {
7576
this.eat(RightParenthesis);
7677

7778
return {
78-
type: 'ContainerFeatureRange',
79-
loc: this.getLocationFromList(children),
79+
type: 'QueryFeatureRange',
80+
loc: this.getLocation(start, this.tokenStart),
8081
children
8182
};
8283
}

test/css/samples/media-query/expected.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/css/samples/media-query/input.svelte

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,34 @@
66
display: block;
77
}
88
}
9-
</style>
9+
10+
@media (min-width: calc(400px + 1px)) {
11+
.large-screen {
12+
display: block;
13+
}
14+
}
15+
16+
@media (width >= 600px) {
17+
.large-screen {
18+
display: block;
19+
}
20+
}
21+
22+
@media (400px <= width <= 1000px) {
23+
.large-screen {
24+
display: block;
25+
}
26+
}
27+
28+
@media (width < clamp(200px, 40%, 400px)) {
29+
.large-screen {
30+
display: block;
31+
}
32+
}
33+
34+
@media (calc(400px + 1px) <= width <= calc(1000px + 1px)) {
35+
.large-screen {
36+
display: block;
37+
}
38+
}
39+
</style>

0 commit comments

Comments
 (0)