183 lines
4.3 KiB
JavaScript
183 lines
4.3 KiB
JavaScript
import {
|
|
WhiteSpace,
|
|
Delim,
|
|
Ident,
|
|
Function as FunctionToken,
|
|
Url,
|
|
BadUrl,
|
|
AtKeyword,
|
|
Hash,
|
|
Percentage,
|
|
Dimension,
|
|
Number as NumberToken,
|
|
String as StringToken,
|
|
Colon,
|
|
LeftParenthesis,
|
|
RightParenthesis,
|
|
CDC
|
|
} from '../tokenizer/index.js';
|
|
|
|
const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
|
|
const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
|
|
|
|
const code = (type, value) => {
|
|
if (type === 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 = [
|
|
[Ident, Ident],
|
|
[Ident, FunctionToken],
|
|
[Ident, Url],
|
|
[Ident, BadUrl],
|
|
[Ident, '-'],
|
|
[Ident, NumberToken],
|
|
[Ident, Percentage],
|
|
[Ident, Dimension],
|
|
[Ident, CDC],
|
|
[Ident, LeftParenthesis],
|
|
|
|
[AtKeyword, Ident],
|
|
[AtKeyword, FunctionToken],
|
|
[AtKeyword, Url],
|
|
[AtKeyword, BadUrl],
|
|
[AtKeyword, '-'],
|
|
[AtKeyword, NumberToken],
|
|
[AtKeyword, Percentage],
|
|
[AtKeyword, Dimension],
|
|
[AtKeyword, CDC],
|
|
|
|
[Hash, Ident],
|
|
[Hash, FunctionToken],
|
|
[Hash, Url],
|
|
[Hash, BadUrl],
|
|
[Hash, '-'],
|
|
[Hash, NumberToken],
|
|
[Hash, Percentage],
|
|
[Hash, Dimension],
|
|
[Hash, CDC],
|
|
|
|
[Dimension, Ident],
|
|
[Dimension, FunctionToken],
|
|
[Dimension, Url],
|
|
[Dimension, BadUrl],
|
|
[Dimension, '-'],
|
|
[Dimension, NumberToken],
|
|
[Dimension, Percentage],
|
|
[Dimension, Dimension],
|
|
[Dimension, CDC],
|
|
|
|
['#', Ident],
|
|
['#', FunctionToken],
|
|
['#', Url],
|
|
['#', BadUrl],
|
|
['#', '-'],
|
|
['#', NumberToken],
|
|
['#', Percentage],
|
|
['#', Dimension],
|
|
['#', CDC], // https://github.com/w3c/csswg-drafts/pull/6874
|
|
|
|
['-', Ident],
|
|
['-', FunctionToken],
|
|
['-', Url],
|
|
['-', BadUrl],
|
|
['-', '-'],
|
|
['-', NumberToken],
|
|
['-', Percentage],
|
|
['-', Dimension],
|
|
['-', CDC], // https://github.com/w3c/csswg-drafts/pull/6874
|
|
|
|
[NumberToken, Ident],
|
|
[NumberToken, FunctionToken],
|
|
[NumberToken, Url],
|
|
[NumberToken, BadUrl],
|
|
[NumberToken, NumberToken],
|
|
[NumberToken, Percentage],
|
|
[NumberToken, Dimension],
|
|
[NumberToken, '%'],
|
|
[NumberToken, CDC], // https://github.com/w3c/csswg-drafts/pull/6874
|
|
|
|
['@', Ident],
|
|
['@', FunctionToken],
|
|
['@', Url],
|
|
['@', BadUrl],
|
|
['@', '-'],
|
|
['@', CDC], // https://github.com/w3c/csswg-drafts/pull/6874
|
|
|
|
['.', NumberToken],
|
|
['.', Percentage],
|
|
['.', Dimension],
|
|
|
|
['+', NumberToken],
|
|
['+', Percentage],
|
|
['+', Dimension],
|
|
|
|
['/', '*']
|
|
];
|
|
// validate with scripts/generate-safe
|
|
const safePairs = specPairs.concat([
|
|
[Ident, Hash],
|
|
|
|
[Dimension, Hash],
|
|
|
|
[Hash, Hash],
|
|
|
|
[AtKeyword, LeftParenthesis],
|
|
[AtKeyword, StringToken],
|
|
[AtKeyword, Colon],
|
|
|
|
[Percentage, Percentage],
|
|
[Percentage, Dimension],
|
|
[Percentage, FunctionToken],
|
|
[Percentage, '-'],
|
|
|
|
[RightParenthesis, Ident],
|
|
[RightParenthesis, FunctionToken],
|
|
[RightParenthesis, Percentage],
|
|
[RightParenthesis, Dimension],
|
|
[RightParenthesis, Hash],
|
|
[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 !== Ident &&
|
|
type !== FunctionToken &&
|
|
type !== CDC) ||
|
|
(nextCharCode === PLUSSIGN)
|
|
? isWhiteSpaceRequired.has(prevCode << 16 | nextCharCode << 8)
|
|
: isWhiteSpaceRequired.has(prevCode << 16 | nextCode);
|
|
|
|
if (emitWs) {
|
|
this.emit(' ', WhiteSpace, true);
|
|
}
|
|
|
|
return nextCode;
|
|
};
|
|
}
|
|
|
|
export const spec = createMap(specPairs);
|
|
export const safe = createMap(safePairs);
|