diff --git a/src/compiler/parse/read/css-tree-cq/css_tree_parse.ts b/src/compiler/parse/read/css-tree-cq/css_tree_parse.ts index d56e6d72eddb..bd519530c211 100644 --- a/src/compiler/parse/read/css-tree-cq/css_tree_parse.ts +++ b/src/compiler/parse/read/css-tree-cq/css_tree_parse.ts @@ -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`. @@ -30,14 +25,7 @@ const cqSyntax = fork({ } } }, - node: { // extend node types - Comparison, - ContainerFeature, - ContainerFeatureRange, - ContainerFeatureStyle, - ContainerQuery, - QueryCSSFunction - } + node }); export const parse = cqSyntax.parse; diff --git a/src/compiler/parse/read/css-tree-cq/node/container_query.ts b/src/compiler/parse/read/css-tree-cq/node/container_query.ts index c88b91f12ddc..2a5369fedafb 100644 --- a/src/compiler/parse/read/css-tree-cq/node/container_query.ts +++ b/src/compiler/parse/read/css-tree-cq/node/container_query.ts @@ -1,17 +1,14 @@ // @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'; @@ -19,48 +16,13 @@ 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' / - // ' '. 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(); @@ -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: diff --git a/src/compiler/parse/read/css-tree-cq/node/index.ts b/src/compiler/parse/read/css-tree-cq/node/index.ts new file mode 100644 index 000000000000..cab88925f969 --- /dev/null +++ b/src/compiler/parse/read/css-tree-cq/node/index.ts @@ -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'; diff --git a/src/compiler/parse/read/css-tree-cq/node/lookahead_is_range.ts b/src/compiler/parse/read/css-tree-cq/node/lookahead_is_range.ts new file mode 100644 index 000000000000..d78196cb4b50 --- /dev/null +++ b/src/compiler/parse/read/css-tree-cq/node/lookahead_is_range.ts @@ -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' / + // ' '. 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; +} diff --git a/src/compiler/parse/read/css-tree-cq/node/media_query.ts b/src/compiler/parse/read/css-tree-cq/node/media_query.ts new file mode 100644 index 000000000000..11235e29d917 --- /dev/null +++ b/src/compiler/parse/read/css-tree-cq/node/media_query.ts @@ -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); +} diff --git a/src/compiler/parse/read/css-tree-cq/node/container_feature.ts b/src/compiler/parse/read/css-tree-cq/node/query_feature.ts similarity index 95% rename from src/compiler/parse/read/css-tree-cq/node/container_feature.ts rename to src/compiler/parse/read/css-tree-cq/node/query_feature.ts index d84d5f5afd01..e019f050bb68 100644 --- a/src/compiler/parse/read/css-tree-cq/node/container_feature.ts +++ b/src/compiler/parse/read/css-tree-cq/node/query_feature.ts @@ -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] @@ -62,7 +62,7 @@ export function parse() { this.eat(RightParenthesis); return { - type: 'ContainerFeature', + type: 'QueryFeature', loc: this.getLocation(start, this.tokenStart), name, value diff --git a/src/compiler/parse/read/css-tree-cq/node/container_feature_range.ts b/src/compiler/parse/read/css-tree-cq/node/query_feature_range.ts similarity index 90% rename from src/compiler/parse/read/css-tree-cq/node/container_feature_range.ts rename to src/compiler/parse/read/css-tree-cq/node/query_feature_range.ts index 747fbe8da0a0..ee8f14abeadd 100644 --- a/src/compiler/parse/read/css-tree-cq/node/container_feature_range.ts +++ b/src/compiler/parse/read/css-tree-cq/node/query_feature_range.ts @@ -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] @@ -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; @@ -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 }; } diff --git a/test/css/samples/media-query/expected.css b/test/css/samples/media-query/expected.css index 8ea74bf3b1a4..ca5de85cfc45 100644 --- a/test/css/samples/media-query/expected.css +++ b/test/css/samples/media-query/expected.css @@ -1 +1 @@ -@media(min-width: 400px){.large-screen.svelte-xyz{display:block}} \ No newline at end of file +@media(min-width: 400px){.large-screen.svelte-xyz{display:block}}@media(min-width: calc(400px + 1px)){.large-screen.svelte-xyz{display:block}}@media(width >= 600px){.large-screen.svelte-xyz{display:block}}@media(400px <= width <= 1000px){.large-screen.svelte-xyz{display:block}}@media(width < clamp(200px, 40%, 400px)){.large-screen.svelte-xyz{display:block}}@media(calc(400px + 1px) <= width <= calc(1000px + 1px)){.large-screen.svelte-xyz{display:block}} \ No newline at end of file diff --git a/test/css/samples/media-query/input.svelte b/test/css/samples/media-query/input.svelte index d5465ab4f9b7..d634d7a61d0c 100644 --- a/test/css/samples/media-query/input.svelte +++ b/test/css/samples/media-query/input.svelte @@ -6,4 +6,34 @@ display: block; } } - \ No newline at end of file + + @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; + } + } +