171 lines
4.8 KiB
JavaScript
171 lines
4.8 KiB
JavaScript
|
'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 <whitespace-token>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;
|