parserlib: cascade layers
+ simplify Parser.ACTIONS
This commit is contained in:
parent
9b0db09b6c
commit
421526d60b
|
@ -1243,6 +1243,7 @@ self.parserlib = (() => {
|
||||||
FONT_FACE_SYM: {text: '@font-face'},
|
FONT_FACE_SYM: {text: '@font-face'},
|
||||||
IMPORT_SYM: {text: '@import'},
|
IMPORT_SYM: {text: '@import'},
|
||||||
KEYFRAMES_SYM: {text: ['@keyframes', '@-webkit-keyframes', '@-moz-keyframes', '@-o-keyframes']},
|
KEYFRAMES_SYM: {text: ['@keyframes', '@-webkit-keyframes', '@-moz-keyframes', '@-o-keyframes']},
|
||||||
|
LAYER_SYM: {text: '@layer'},
|
||||||
MEDIA_SYM: {text: '@media'},
|
MEDIA_SYM: {text: '@media'},
|
||||||
NAMESPACE_SYM: {text: '@namespace'},
|
NAMESPACE_SYM: {text: '@namespace'},
|
||||||
PAGE_SYM: {text: '@page'},
|
PAGE_SYM: {text: '@page'},
|
||||||
|
@ -3442,6 +3443,8 @@ self.parserlib = (() => {
|
||||||
//#endregion
|
//#endregion
|
||||||
//#region Parser
|
//#region Parser
|
||||||
|
|
||||||
|
const ParserRoute = {};
|
||||||
|
|
||||||
class Parser extends EventTarget {
|
class Parser extends EventTarget {
|
||||||
/**
|
/**
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
|
@ -3477,15 +3480,53 @@ self.parserlib = (() => {
|
||||||
super.fire(event);
|
super.fire(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @layer <layer-name>#;
|
||||||
|
* @layer <layer-name>? { <stylesheet> };
|
||||||
|
*/
|
||||||
|
_layer(start) {
|
||||||
|
const stream = this._tokenStream;
|
||||||
|
const ids = [];
|
||||||
|
let t, val;
|
||||||
|
do {
|
||||||
|
this._ws();
|
||||||
|
if ((t = stream.get(true)).type === Tokens.IDENT) {
|
||||||
|
ids.push(t.value);
|
||||||
|
this._ws();
|
||||||
|
t = stream.get(true);
|
||||||
|
}
|
||||||
|
if ((val = t.value) === '{') {
|
||||||
|
if (ids[1]) this.fire({type: 'error', message: '@layer block cannot have multiple ids'}, start);
|
||||||
|
this._layerBlock(start, true, ids[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while (val === ',');
|
||||||
|
if (val !== ';') stream.mustMatch(Tokens.SEMICOLON);
|
||||||
|
this.fire({type: 'layer', ids}, start);
|
||||||
|
this._ws();
|
||||||
|
}
|
||||||
|
|
||||||
|
_layerBlock(start, inBlock, id) {
|
||||||
|
if (!inBlock) {
|
||||||
|
this._ws();
|
||||||
|
id = this._tokenStream.match(Tokens.IDENT);
|
||||||
|
this._tokenStream.mustMatch(Tokens.LBRACE);
|
||||||
|
}
|
||||||
|
this.fire({type: 'startlayer', id: id || null}, start);
|
||||||
|
this._rulesetBlock(start);
|
||||||
|
this.fire('endlayer');
|
||||||
|
this._ws();
|
||||||
|
}
|
||||||
|
|
||||||
_stylesheet() {
|
_stylesheet() {
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
this.fire('startstylesheet');
|
this.fire('startstylesheet');
|
||||||
this._sheetGlobals();
|
this._sheetGlobals();
|
||||||
const {topDocOnly} = this.options;
|
const {topDocOnly} = this.options;
|
||||||
const allowedActions = topDocOnly ? Parser.ACTIONS.topDoc : Parser.ACTIONS.stylesheet;
|
const allowedActions = topDocOnly ? ParserRoute.topDoc : ParserRoute.stylesheet;
|
||||||
for (let tt, token; (tt = (token = stream.get(true)).type); this._skipCruft()) {
|
for (let tt, token; (tt = (token = stream.get(true)).type); this._skipCruft()) {
|
||||||
try {
|
try {
|
||||||
const action = allowedActions.get(tt);
|
const action = allowedActions[tt];
|
||||||
if (action) {
|
if (action) {
|
||||||
action.call(this, token);
|
action.call(this, token);
|
||||||
continue;
|
continue;
|
||||||
|
@ -3520,6 +3561,7 @@ self.parserlib = (() => {
|
||||||
this._skipCruft();
|
this._skipCruft();
|
||||||
for (const [type, fn, max = Infinity] of [
|
for (const [type, fn, max = Infinity] of [
|
||||||
[Tokens.CHARSET_SYM, this._charset, 1],
|
[Tokens.CHARSET_SYM, this._charset, 1],
|
||||||
|
[Tokens.LAYER_SYM, this._layer],
|
||||||
[Tokens.IMPORT_SYM, this._import],
|
[Tokens.IMPORT_SYM, this._import],
|
||||||
[Tokens.NAMESPACE_SYM, this._namespace],
|
[Tokens.NAMESPACE_SYM, this._namespace],
|
||||||
]) {
|
]) {
|
||||||
|
@ -3538,13 +3580,28 @@ self.parserlib = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
_import(start) {
|
_import(start) {
|
||||||
|
let t, layer;
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
const token = stream.mustMatch(TT.stringUri);
|
t = stream.mustMatch(TT.stringUri);
|
||||||
const uri = token.uri || token.value.replace(/^["']|["']$/g, '');
|
const uri = t.uri || t.value.replace(/^["']|["']$/g, '');
|
||||||
this._ws();
|
this._ws();
|
||||||
|
t = stream.get(true);
|
||||||
|
if (/^layer(\()?$/i.test(t.value)) {
|
||||||
|
layer = RegExp.$1 ? stream.mustMatch(Tokens.IDENT) : '';
|
||||||
|
if (layer) stream.mustMatch(Tokens.RPAREN);
|
||||||
|
this._ws();
|
||||||
|
t = stream.get(true);
|
||||||
|
}
|
||||||
|
if (lowerCmp('supports(', t.value)) {
|
||||||
|
this._ws();
|
||||||
|
if (!this._declaration()) this._supportsCondition();
|
||||||
|
stream.mustMatch(Tokens.RPAREN);
|
||||||
|
} else {
|
||||||
|
stream.unget();
|
||||||
|
}
|
||||||
const media = this._mediaQueryList();
|
const media = this._mediaQueryList();
|
||||||
stream.mustMatch(Tokens.SEMICOLON);
|
stream.mustMatch(Tokens.SEMICOLON);
|
||||||
this.fire({type: 'import', media, uri}, start);
|
this.fire({type: 'import', layer, media, uri}, start);
|
||||||
this._ws();
|
this._ws();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3566,16 +3623,7 @@ self.parserlib = (() => {
|
||||||
this._supportsCondition();
|
this._supportsCondition();
|
||||||
stream.mustMatch(Tokens.LBRACE);
|
stream.mustMatch(Tokens.LBRACE);
|
||||||
this.fire('startsupports', start);
|
this.fire('startsupports', start);
|
||||||
this._ws();
|
this._rulesetBlock(start);
|
||||||
for (;; stream.skipComment()) {
|
|
||||||
const action = Parser.ACTIONS.supports.get(stream.peek());
|
|
||||||
if (action) {
|
|
||||||
action.call(this, stream.get(true));
|
|
||||||
} else if (!this._ruleset()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stream.mustMatch(Tokens.RBRACE);
|
|
||||||
this.fire('endsupports');
|
this.fire('endsupports');
|
||||||
this._ws();
|
this._ws();
|
||||||
}
|
}
|
||||||
|
@ -3642,11 +3690,7 @@ self.parserlib = (() => {
|
||||||
type: 'startmedia',
|
type: 'startmedia',
|
||||||
media: mediaList,
|
media: mediaList,
|
||||||
}, start);
|
}, start);
|
||||||
this._ws();
|
this._rulesetBlock(start);
|
||||||
let action;
|
|
||||||
do action = Parser.ACTIONS.media.get(stream.peek());
|
|
||||||
while (action ? action.call(this, stream.get(true)) || true : this._ruleset());
|
|
||||||
stream.mustMatch(Tokens.RBRACE);
|
|
||||||
this.fire({
|
this.fire({
|
||||||
type: 'endmedia',
|
type: 'endmedia',
|
||||||
media: mediaList,
|
media: mediaList,
|
||||||
|
@ -3788,16 +3832,13 @@ self.parserlib = (() => {
|
||||||
this.fire({type: 'startdocument', functions, prefix}, start);
|
this.fire({type: 'startdocument', functions, prefix}, start);
|
||||||
if (this.options.topDocOnly) {
|
if (this.options.topDocOnly) {
|
||||||
stream.readDeclValue({stopOn: '}'});
|
stream.readDeclValue({stopOn: '}'});
|
||||||
|
stream.mustMatch(Tokens.RBRACE);
|
||||||
} else {
|
} else {
|
||||||
/* We allow @import and such inside document sections because the final generated CSS for
|
/* We allow @import and such inside document sections because the final generated CSS for
|
||||||
* a given page may be valid e.g. if this section is the first one that matched the URL */
|
* a given page may be valid e.g. if this section is the first one that matched the URL */
|
||||||
this._sheetGlobals();
|
this._sheetGlobals();
|
||||||
this._ws();
|
this._rulesetBlock(start);
|
||||||
let action;
|
|
||||||
do action = Parser.ACTIONS.document.get(stream.peek());
|
|
||||||
while (action ? action.call(this, stream.get(true)) || true : this._ruleset());
|
|
||||||
}
|
}
|
||||||
stream.mustMatch(Tokens.RBRACE);
|
|
||||||
this.fire({type: 'enddocument', functions, prefix});
|
this.fire({type: 'enddocument', functions, prefix});
|
||||||
this._ws();
|
this._ws();
|
||||||
}
|
}
|
||||||
|
@ -3879,6 +3920,20 @@ self.parserlib = (() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {parserlib.Token} start */
|
||||||
|
_rulesetBlock(start) {
|
||||||
|
const stream = this._tokenStream;
|
||||||
|
const map = ParserRoute[start.type];
|
||||||
|
this._ws();
|
||||||
|
while (true) {
|
||||||
|
const fn = map[stream.LT(1).type];
|
||||||
|
if (fn) fn.call(this, stream.get(true));
|
||||||
|
else if (!this._ruleset()) break;
|
||||||
|
stream.skipComment();
|
||||||
|
}
|
||||||
|
stream.mustMatch(Tokens.RBRACE);
|
||||||
|
}
|
||||||
|
|
||||||
_selectorsGroup() {
|
_selectorsGroup() {
|
||||||
const stream = this._tokenStream;
|
const stream = this._tokenStream;
|
||||||
const selectors = [];
|
const selectors = [];
|
||||||
|
@ -3949,7 +4004,7 @@ self.parserlib = (() => {
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
const token = stream.get(true);
|
const token = stream.get(true);
|
||||||
const action = Parser.ACTIONS.simpleSelectorSequence.get(token.type);
|
const action = ParserRoute.simpleSelectorSequence[token.type];
|
||||||
const component = action ? action.call(this, token) : (stream.unget(), 0);
|
const component = action ? action.call(this, token) : (stream.unget(), 0);
|
||||||
if (!component) break;
|
if (!component) break;
|
||||||
modifiers.push(component);
|
modifiers.push(component);
|
||||||
|
@ -4543,77 +4598,38 @@ self.parserlib = (() => {
|
||||||
Object.assign(Parser.prototype, TYPES);
|
Object.assign(Parser.prototype, TYPES);
|
||||||
Parser.prototype._readWhitespace = Parser.prototype._ws;
|
Parser.prototype._readWhitespace = Parser.prototype._ws;
|
||||||
|
|
||||||
const symDocument = [Tokens.DOCUMENT_SYM, Parser.prototype._document];
|
ParserRoute[Tokens.DOCUMENT_SYM] =
|
||||||
const symDocMisplaced = [Tokens.DOCUMENT_SYM, Parser.prototype._documentMisplaced];
|
ParserRoute[Tokens.LAYER_SYM] =
|
||||||
const symFontFace = [Tokens.FONT_FACE_SYM, Parser.prototype._fontFace];
|
ParserRoute[Tokens.MEDIA_SYM] =
|
||||||
const symKeyframes = [Tokens.KEYFRAMES_SYM, Parser.prototype._keyframes];
|
ParserRoute[Tokens.SUPPORTS_SYM] = {
|
||||||
const symMedia = [Tokens.MEDIA_SYM, Parser.prototype._media];
|
[Tokens.DOCUMENT_SYM]: Parser.prototype._documentMisplaced,
|
||||||
const symPage = [Tokens.PAGE_SYM, Parser.prototype._page];
|
[Tokens.FONT_FACE_SYM]: Parser.prototype._fontFace,
|
||||||
const symSupports = [Tokens.SUPPORTS_SYM, Parser.prototype._supports];
|
[Tokens.KEYFRAMES_SYM]: Parser.prototype._keyframes,
|
||||||
const symUnknown = [Tokens.UNKNOWN_SYM, Parser.prototype._unknownSym];
|
[Tokens.LAYER_SYM]: Parser.prototype._layerBlock,
|
||||||
const symViewport = [Tokens.VIEWPORT_SYM, Parser.prototype._viewport];
|
[Tokens.MEDIA_SYM]: Parser.prototype._media,
|
||||||
|
[Tokens.PAGE_SYM]: Parser.prototype._page,
|
||||||
Parser.ACTIONS = {
|
[Tokens.SUPPORTS_SYM]: Parser.prototype._supports,
|
||||||
|
[Tokens.UNKNOWN_SYM]: Parser.prototype._unknownSym,
|
||||||
stylesheet: new Map([
|
[Tokens.VIEWPORT_SYM]: Parser.prototype._viewport,
|
||||||
symMedia,
|
};
|
||||||
symDocument,
|
ParserRoute.stylesheet = Object.assign({}, ParserRoute[Tokens.DOCUMENT_SYM], {
|
||||||
symSupports,
|
[Tokens.DOCUMENT_SYM]: Parser.prototype._document,
|
||||||
symPage,
|
[Tokens.S]: Parser.prototype._ws,
|
||||||
symFontFace,
|
});
|
||||||
symKeyframes,
|
ParserRoute.topDoc = {
|
||||||
symViewport,
|
[Tokens.DOCUMENT_SYM]: Parser.prototype._document,
|
||||||
symUnknown,
|
[Tokens.UNKNOWN_SYM]: Parser.prototype._unknownSym,
|
||||||
[Tokens.S, Parser.prototype._ws],
|
[Tokens.S]: Parser.prototype._ws,
|
||||||
]),
|
};
|
||||||
|
ParserRoute.simpleSelectorSequence = {
|
||||||
topDoc: new Map([
|
[Tokens.HASH]: Parser.prototype._hash,
|
||||||
symDocument,
|
[Tokens.DOT]: Parser.prototype._class,
|
||||||
symUnknown,
|
[Tokens.LBRACKET]: Parser.prototype._attrib,
|
||||||
[Tokens.S, Parser.prototype._ws],
|
[Tokens.COLON]: Parser.prototype._pseudo,
|
||||||
]),
|
[Tokens.IS]: Parser.prototype._is,
|
||||||
|
[Tokens.ANY]: Parser.prototype._is,
|
||||||
document: new Map([
|
[Tokens.WHERE]: Parser.prototype._is,
|
||||||
symMedia,
|
[Tokens.NOT]: Parser.prototype._negation,
|
||||||
symDocMisplaced,
|
|
||||||
symSupports,
|
|
||||||
symPage,
|
|
||||||
symFontFace,
|
|
||||||
symViewport,
|
|
||||||
symKeyframes,
|
|
||||||
symUnknown,
|
|
||||||
]),
|
|
||||||
|
|
||||||
supports: new Map([
|
|
||||||
symKeyframes,
|
|
||||||
symMedia,
|
|
||||||
symSupports,
|
|
||||||
symDocMisplaced,
|
|
||||||
symViewport,
|
|
||||||
symUnknown,
|
|
||||||
]),
|
|
||||||
|
|
||||||
media: new Map([
|
|
||||||
symKeyframes,
|
|
||||||
symMedia,
|
|
||||||
symDocMisplaced,
|
|
||||||
symSupports,
|
|
||||||
symPage,
|
|
||||||
symFontFace,
|
|
||||||
symViewport,
|
|
||||||
symUnknown,
|
|
||||||
]),
|
|
||||||
|
|
||||||
simpleSelectorSequence: new Map([
|
|
||||||
[Tokens.HASH, Parser.prototype._hash],
|
|
||||||
[Tokens.DOT, Parser.prototype._class],
|
|
||||||
[Tokens.LBRACKET, Parser.prototype._attrib],
|
|
||||||
[Tokens.COLON, Parser.prototype._pseudo],
|
|
||||||
[Tokens.IS, Parser.prototype._is],
|
|
||||||
[Tokens.ANY, Parser.prototype._is],
|
|
||||||
[Tokens.WHERE, Parser.prototype._is],
|
|
||||||
[Tokens.NOT, Parser.prototype._negation],
|
|
||||||
]),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
Loading…
Reference in New Issue
Block a user