Skip to content

Commit

Permalink
Closes sveltejs#5876. Adds single value function support to media que…
Browse files Browse the repository at this point in the history
…ries and media query range syntax / MQ level 4 support. (sveltejs#8430)
  • Loading branch information
typhonrt committed Mar 30, 2023
1 parent cfe26d8 commit d42ca04
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 65 deletions.
16 changes: 2 additions & 14 deletions src/compiler/parse/read/css-tree-cq/css_tree_parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@
// `css-tree` Node module directly. This allows the production build of Svelte to work correctly.
import { fork } from '../../../../../node_modules/css-tree/dist/csstree.esm.js';

import * as Comparison from './node/comparison';
import * as ContainerFeature from './node/container_feature';
import * as ContainerFeatureRange from './node/container_feature_range';
import * as ContainerFeatureStyle from './node/container_feature_style';
import * as ContainerQuery from './node/container_query';
import * as QueryCSSFunction from './node/query_css_function';
import * as node from './node';

/**
* Extends `css-tree` for container query support by forking and adding new nodes and at-rule support for `@container`.
Expand All @@ -30,14 +25,7 @@ const cqSyntax = fork({
}
}
},
node: { // extend node types
Comparison,
ContainerFeature,
ContainerFeatureRange,
ContainerFeatureStyle,
ContainerQuery,
QueryCSSFunction
}
node
});

export const parse = cqSyntax.parse;
50 changes: 6 additions & 44 deletions src/compiler/parse/read/css-tree-cq/node/container_query.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,28 @@
// @ts-nocheck
import {
EOF,
WhiteSpace,
Comment,
Delim,
Function,
Ident,
LeftParenthesis,
RightParenthesis,
LeftCurlyBracket,
Colon
LeftParenthesis
} from 'css-tree/tokenizer';

import { lookahead_is_range } from './lookahead_is_range';

const CONTAINER_QUERY_KEYWORDS = new Set(['none', 'and', 'not', 'or']);

export const name = 'ContainerQuery';
export const structure = {
name: 'Identifier',
children: [[
'Identifier',
'ContainerFeature',
'ContainerFeatureRange',
'QueryFeature',
'QueryFeatureRange',
'ContainerFeatureStyle',
'WhiteSpace'
]]
};

/**
* Looks ahead to determine if query feature is a range query. This involves locating at least one delimiter and no
* colon tokens.
*
* @returns {boolean} Is potential range query.
*/
function lookahead_is_range() {
let type;
let offset = 0;

let count = 0;
let delim_found = false;
let no_colon = true;

// A range query has maximum 5 tokens when formatted as 'mf-range' /
// '<mf-value> <mf-lt> <mf-name> <mf-lt> <mf-value>'. So only look ahead maximum of 6 non-whitespace tokens.
do {
type = this.lookupNonWSType(offset++);
if (type !== WhiteSpace) {
count++;
}
if (type === Delim) {
delim_found = true;
}
if (type === Colon) {
no_colon = false;
}
if (type === LeftCurlyBracket || type === RightParenthesis) {
break;
}
} while (type !== EOF && count <= 6);

return delim_found && no_colon;
}

export function parse() {
const start = this.tokenStart;
const children = this.createList();
Expand Down Expand Up @@ -98,7 +60,7 @@ export function parse() {

case LeftParenthesis:
// Lookahead to determine if range feature.
child = lookahead_is_range.call(this) ? this.ContainerFeatureRange() : this.ContainerFeature();
child = lookahead_is_range.call(this) ? this.QueryFeatureRange() : this.QueryFeature();
break;

default:
Expand Down
7 changes: 7 additions & 0 deletions src/compiler/parse/read/css-tree-cq/node/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export * as Comparison from './comparison';
export * as ContainerFeatureStyle from './container_feature_style';
export * as ContainerQuery from './container_query';
export * as MediaQuery from './media_query';
export * as QueryFeature from './query_feature';
export * as QueryFeatureRange from './query_feature_range';
export * as QueryCSSFunction from './query_css_function';
44 changes: 44 additions & 0 deletions src/compiler/parse/read/css-tree-cq/node/lookahead_is_range.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// @ts-nocheck
import {
EOF,
WhiteSpace,
Delim,
RightParenthesis,
LeftCurlyBracket,
Colon
} from 'css-tree/tokenizer';

/**
* Looks ahead to determine if query feature is a range query. This involves locating at least one delimiter and no
* colon tokens.
*
* @returns {boolean} Is potential range query.
*/
export function lookahead_is_range() {
let type;
let offset = 0;

let count = 0;
let delim_found = false;
let no_colon = true;

// A range query has maximum 5 tokens when formatted as 'mf-range' /
// '<mf-value> <mf-lt> <mf-name> <mf-lt> <mf-value>'. So only look ahead maximum of 6 non-whitespace tokens.
do {
type = this.lookupNonWSType(offset++);
if (type !== WhiteSpace) {
count++;
}
if (type === Delim) {
delim_found = true;
}
if (type === Colon) {
no_colon = false;
}
if (type === LeftCurlyBracket || type === RightParenthesis) {
break;
}
} while (type !== EOF && count <= 6);

return delim_found && no_colon;
}
64 changes: 64 additions & 0 deletions src/compiler/parse/read/css-tree-cq/node/media_query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// @ts-nocheck
import {
WhiteSpace,
Comment,
Ident,
LeftParenthesis
} from 'css-tree/tokenizer';

import { lookahead_is_range } from './lookahead_is_range';

export const name = 'MediaQuery';
export const structure = {
children: [[
'Identifier',
'QueryFeature',
'QueryFeatureRange',
'WhiteSpace'
]]
};

export function parse() {
const children = this.createList();
let child = null;

this.skipSC();

scan:
while (!this.eof) {
switch (this.tokenType) {
case Comment:
case WhiteSpace:
this.next();
continue;

case Ident:
child = this.Identifier();
break;

case LeftParenthesis:
// Lookahead to determine if range feature.
child = lookahead_is_range.call(this) ? this.QueryFeatureRange() : this.QueryFeature();
break;

default:
break scan;
}

children.push(child);
}

if (child === null) {
this.error('Identifier or parenthesis is expected');
}

return {
type: 'MediaQuery',
loc: this.getLocationFromList(children),
children
};
}

export function generate(node) {
this.children(node);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
Delim
} from 'css-tree/tokenizer';

export const name = 'ContainerFeature';
export const name = 'QueryFeature';
export const structure = {
name: String,
value: ['Identifier', 'Number', 'Dimension', 'QueryCSSFunction', 'Ratio', null]
Expand Down Expand Up @@ -62,7 +62,7 @@ export function parse() {
this.eat(RightParenthesis);

return {
type: 'ContainerFeature',
type: 'QueryFeature',
loc: this.getLocation(start, this.tokenStart),
name,
value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
WhiteSpace
} from 'css-tree/tokenizer';

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

export function parse() {
const start = this.tokenStart;
const children = this.createList();
let child = null;

Expand Down Expand Up @@ -75,8 +76,8 @@ export function parse() {
this.eat(RightParenthesis);

return {
type: 'ContainerFeatureRange',
loc: this.getLocationFromList(children),
type: 'QueryFeatureRange',
loc: this.getLocation(start, this.tokenStart),
children
};
}
Expand Down
2 changes: 1 addition & 1 deletion test/css/samples/media-query/expected.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 31 additions & 1 deletion test/css/samples/media-query/input.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,34 @@
display: block;
}
}
</style>
@media (min-width: calc(400px + 1px)) {
.large-screen {
display: block;
}
}
@media (width >= 600px) {
.large-screen {
display: block;
}
}
@media (400px <= width <= 1000px) {
.large-screen {
display: block;
}
}
@media (width < clamp(200px, 40%, 400px)) {
.large-screen {
display: block;
}
}
@media (calc(400px + 1px) <= width <= calc(1000px + 1px)) {
.large-screen {
display: block;
}
}
</style>

0 comments on commit d42ca04

Please sign in to comment.