// @ts-ignore // import parse from 'css-tree/parser'; // When css-tree supports container queries uncomment. import { parse } from './css-tree-cq/css_tree_parse.js'; // Use extended css-tree for container query support. import { walk } from 'estree-walker'; import parser_errors from '../errors.js'; const regex_closing_style_tag = /<\/style\s*>/; const regex_starts_with_closing_style_tag = /^<\/style\s*>/; /** * * @param {import('../index.js').Parser} parser * @param {number} start * @param {import('estree').Node[]} attributes * @returns {import('../../interfaces.js').Style} */ export default function read_style(parser, start, attributes) { const content_start = parser.index; const styles = parser.read_until(regex_closing_style_tag, parser_errors.unclosed_style); if (parser.index >= parser.template.length) { parser.error(parser_errors.unclosed_style); } const content_end = parser.index; // discard styles when css is disabled if (parser.css_mode === 'none') { parser.read(regex_starts_with_closing_style_tag); return null; } let ast; try { ast = parse(styles, { positions: true, offset: content_start, onParseError(error) { throw error; } }); } catch (err) { if (err.name === 'SyntaxError') { parser.error(parser_errors.css_syntax_error(err.message), err.offset); } else { throw err; } } ast = JSON.parse(JSON.stringify(ast)); // tidy up AST walk(ast, { /** @param {any} node */ enter: (node) => { // `any` because this isn't an ESTree node // replace `ref:a` nodes if (node.type === 'Selector') { for (let i = 0; i < node.children.length; i += 1) { const a = node.children[i]; const b = node.children[i + 1]; if (is_ref_selector(a, b)) { parser.error(parser_errors.invalid_ref_selector, a.loc.start.offset); } } } if ( node.type === 'Declaration' && node.value.type === 'Value' && node.value.children.length === 0 ) { parser.error(parser_errors.invalid_declaration, node.start); } if (node.type === 'PseudoClassSelector' && node.name === 'global' && node.children === null) { parser.error(parser_errors.empty_global_selector, node.loc.start.offset); } if (node.loc) { node.start = node.loc.start.offset; node.end = node.loc.end.offset; delete node.loc; } } }); parser.read(regex_starts_with_closing_style_tag); const end = parser.index; return { type: 'Style', start, end, attributes, children: ast.children, content: { start: content_start, end: content_end, styles } }; } /** * @param {any} a * @param {any} b */ function is_ref_selector(a, b) { // TODO add CSS node types if (!b) return false; return a.type === 'TypeSelector' && a.name === 'ref' && b.type === 'PseudoClassSelector'; }