Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: media query range syntax & single value function support via css-tree extension #8430

Merged
merged 1 commit into from
Mar 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>