'use strict'; const types = require('../tokenizer/types.cjs'); const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+) const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-) const code = (type, value) => { if (type === types.Delim) { type = value; } if (typeof type === 'string') { const charCode = type.charCodeAt(0); return charCode > 0x7F ? 0x8000 : charCode << 8; } return type; }; // https://www.w3.org/TR/css-syntax-3/#serialization // The only requirement for serialization is that it must "round-trip" with parsing, // that is, parsing the stylesheet must produce the same data structures as parsing, // serializing, and parsing again, except for consecutive s, // which may be collapsed into a single token. const specPairs = [ [types.Ident, types.Ident], [types.Ident, types.Function], [types.Ident, types.Url], [types.Ident, types.BadUrl], [types.Ident, '-'], [types.Ident, types.Number], [types.Ident, types.Percentage], [types.Ident, types.Dimension], [types.Ident, types.CDC], [types.Ident, types.LeftParenthesis], [types.AtKeyword, types.Ident], [types.AtKeyword, types.Function], [types.AtKeyword, types.Url], [types.AtKeyword, types.BadUrl], [types.AtKeyword, '-'], [types.AtKeyword, types.Number], [types.AtKeyword, types.Percentage], [types.AtKeyword, types.Dimension], [types.AtKeyword, types.CDC], [types.Hash, types.Ident], [types.Hash, types.Function], [types.Hash, types.Url], [types.Hash, types.BadUrl], [types.Hash, '-'], [types.Hash, types.Number], [types.Hash, types.Percentage], [types.Hash, types.Dimension], [types.Hash, types.CDC], [types.Dimension, types.Ident], [types.Dimension, types.Function], [types.Dimension, types.Url], [types.Dimension, types.BadUrl], [types.Dimension, '-'], [types.Dimension, types.Number], [types.Dimension, types.Percentage], [types.Dimension, types.Dimension], [types.Dimension, types.CDC], ['#', types.Ident], ['#', types.Function], ['#', types.Url], ['#', types.BadUrl], ['#', '-'], ['#', types.Number], ['#', types.Percentage], ['#', types.Dimension], ['#', types.CDC], // https://github.com/w3c/csswg-drafts/pull/6874 ['-', types.Ident], ['-', types.Function], ['-', types.Url], ['-', types.BadUrl], ['-', '-'], ['-', types.Number], ['-', types.Percentage], ['-', types.Dimension], ['-', types.CDC], // https://github.com/w3c/csswg-drafts/pull/6874 [types.Number, types.Ident], [types.Number, types.Function], [types.Number, types.Url], [types.Number, types.BadUrl], [types.Number, types.Number], [types.Number, types.Percentage], [types.Number, types.Dimension], [types.Number, '%'], [types.Number, types.CDC], // https://github.com/w3c/csswg-drafts/pull/6874 ['@', types.Ident], ['@', types.Function], ['@', types.Url], ['@', types.BadUrl], ['@', '-'], ['@', types.CDC], // https://github.com/w3c/csswg-drafts/pull/6874 ['.', types.Number], ['.', types.Percentage], ['.', types.Dimension], ['+', types.Number], ['+', types.Percentage], ['+', types.Dimension], ['/', '*'] ]; // validate with scripts/generate-safe const safePairs = specPairs.concat([ [types.Ident, types.Hash], [types.Dimension, types.Hash], [types.Hash, types.Hash], [types.AtKeyword, types.LeftParenthesis], [types.AtKeyword, types.String], [types.AtKeyword, types.Colon], [types.Percentage, types.Percentage], [types.Percentage, types.Dimension], [types.Percentage, types.Function], [types.Percentage, '-'], [types.RightParenthesis, types.Ident], [types.RightParenthesis, types.Function], [types.RightParenthesis, types.Percentage], [types.RightParenthesis, types.Dimension], [types.RightParenthesis, types.Hash], [types.RightParenthesis, '-'] ]); function createMap(pairs) { const isWhiteSpaceRequired = new Set( pairs.map(([prev, next]) => (code(prev) << 16 | code(next))) ); return function(prevCode, type, value) { const nextCode = code(type, value); const nextCharCode = value.charCodeAt(0); const emitWs = (nextCharCode === HYPHENMINUS && type !== types.Ident && type !== types.Function && type !== types.CDC) || (nextCharCode === PLUSSIGN) ? isWhiteSpaceRequired.has(prevCode << 16 | nextCharCode << 8) : isWhiteSpaceRequired.has(prevCode << 16 | nextCode); if (emitWs) { this.emit(' ', types.WhiteSpace, true); } return nextCode; }; } const spec = createMap(specPairs); const safe = createMap(safePairs); exports.safe = safe; exports.spec = spec;