remove unused parsel
This commit is contained in:
parent
d2839332dc
commit
b366f3af8f
3 changed files with 0 additions and 439 deletions
|
|
@ -1,13 +1,11 @@
|
||||||
import { parse, walk, generate } from 'css-tree';
|
import { parse, walk, generate } from 'css-tree';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import parsel from './parsel.js';
|
|
||||||
|
|
||||||
class CSS extends EventEmitter {
|
class CSS extends EventEmitter {
|
||||||
constructor(ctx) {
|
constructor(ctx) {
|
||||||
super();
|
super();
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.meta = ctx.meta;
|
this.meta = ctx.meta;
|
||||||
this.parsel = parsel;
|
|
||||||
this.parse = parse;
|
this.parse = parse;
|
||||||
this.walk = walk;
|
this.walk = walk;
|
||||||
this.generate = generate;
|
this.generate = generate;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ import {
|
||||||
wrapEval,
|
wrapEval,
|
||||||
} from './rewrite.script.js';
|
} from './rewrite.script.js';
|
||||||
import { openDB } from 'idb';
|
import { openDB } from 'idb';
|
||||||
import parsel from './parsel.js';
|
|
||||||
import BareClient from '@tomphttp/bare-client';
|
import BareClient from '@tomphttp/bare-client';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
|
|
||||||
|
|
@ -64,7 +63,6 @@ class Ultraviolet {
|
||||||
this.html = new HTML(this);
|
this.html = new HTML(this);
|
||||||
this.css = new CSS(this);
|
this.css = new CSS(this);
|
||||||
this.js = new JS(this);
|
this.js = new JS(this);
|
||||||
this.parsel = parsel;
|
|
||||||
this.openDB = this.constructor.openDB;
|
this.openDB = this.constructor.openDB;
|
||||||
this.master = '__uv';
|
this.master = '__uv';
|
||||||
this.dataPrefix = '__uv$';
|
this.dataPrefix = '__uv$';
|
||||||
|
|
|
||||||
|
|
@ -1,435 +0,0 @@
|
||||||
export default (function (exports) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const TOKENS = {
|
|
||||||
attribute:
|
|
||||||
/\[\s*(?:(?<namespace>\*|[-\w]*)\|)?(?<name>[-\w\u{0080}-\u{FFFF}]+)\s*(?:(?<operator>\W?=)\s*(?<value>.+?)\s*(?<caseSensitive>[iIsS])?\s*)?\]/gu,
|
|
||||||
id: /#(?<name>(?:[-\w\u{0080}-\u{FFFF}]|\\.)+)/gu,
|
|
||||||
class: /\.(?<name>(?:[-\w\u{0080}-\u{FFFF}]|\\.)+)/gu,
|
|
||||||
comma: /\s*,\s*/g, // must be before combinator
|
|
||||||
combinator: /\s*[\s>+~]\s*/g, // this must be after attribute
|
|
||||||
'pseudo-element':
|
|
||||||
/::(?<name>[-\w\u{0080}-\u{FFFF}]+)(?:\((?<argument>¶+)\))?/gu, // this must be before pseudo-class
|
|
||||||
'pseudo-class':
|
|
||||||
/:(?<name>[-\w\u{0080}-\u{FFFF}]+)(?:\((?<argument>¶+)\))?/gu,
|
|
||||||
type: /(?:(?<namespace>\*|[-\w]*)\|)?(?<name>[-\w\u{0080}-\u{FFFF}]+)|\*/gu, // this must be last
|
|
||||||
};
|
|
||||||
|
|
||||||
const TOKENS_WITH_PARENS = new Set(['pseudo-class', 'pseudo-element']);
|
|
||||||
const TOKENS_WITH_STRINGS = new Set([...TOKENS_WITH_PARENS, 'attribute']);
|
|
||||||
const TRIM_TOKENS = new Set(['combinator', 'comma']);
|
|
||||||
const RECURSIVE_PSEUDO_CLASSES = new Set([
|
|
||||||
'not',
|
|
||||||
'is',
|
|
||||||
'where',
|
|
||||||
'has',
|
|
||||||
'matches',
|
|
||||||
'-moz-any',
|
|
||||||
'-webkit-any',
|
|
||||||
'nth-child',
|
|
||||||
'nth-last-child',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const RECURSIVE_PSEUDO_CLASSES_ARGS = {
|
|
||||||
'nth-child': /(?<index>[\dn+-]+)\s+of\s+(?<subtree>.+)/,
|
|
||||||
};
|
|
||||||
|
|
||||||
RECURSIVE_PSEUDO_CLASSES['nth-last-child'] =
|
|
||||||
RECURSIVE_PSEUDO_CLASSES_ARGS['nth-child'];
|
|
||||||
|
|
||||||
const TOKENS_FOR_RESTORE = Object.assign({}, TOKENS);
|
|
||||||
TOKENS_FOR_RESTORE['pseudo-element'] = RegExp(
|
|
||||||
TOKENS['pseudo-element'].source.replace(
|
|
||||||
'(?<argument>¶+)',
|
|
||||||
'(?<argument>.+?)'
|
|
||||||
),
|
|
||||||
'gu'
|
|
||||||
);
|
|
||||||
TOKENS_FOR_RESTORE['pseudo-class'] = RegExp(
|
|
||||||
TOKENS['pseudo-class'].source.replace(
|
|
||||||
'(?<argument>¶+)',
|
|
||||||
'(?<argument>.+)'
|
|
||||||
),
|
|
||||||
'gu'
|
|
||||||
);
|
|
||||||
|
|
||||||
function gobbleParens(text, i) {
|
|
||||||
let str = '',
|
|
||||||
stack = [];
|
|
||||||
|
|
||||||
for (; i < text.length; i++) {
|
|
||||||
let char = text[i];
|
|
||||||
|
|
||||||
if (char === '(') {
|
|
||||||
stack.push(char);
|
|
||||||
} else if (char === ')') {
|
|
||||||
if (stack.length > 0) {
|
|
||||||
stack.pop();
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
'Closing paren without opening paren at ' + i
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
str += char;
|
|
||||||
|
|
||||||
if (stack.length === 0) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Opening paren without closing paren');
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenizeBy(text, grammar) {
|
|
||||||
if (!text) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
var strarr = [text];
|
|
||||||
|
|
||||||
for (var token in grammar) {
|
|
||||||
let pattern = grammar[token];
|
|
||||||
|
|
||||||
for (var i = 0; i < strarr.length; i++) {
|
|
||||||
// Don’t cache length as it changes during the loop
|
|
||||||
var str = strarr[i];
|
|
||||||
|
|
||||||
if (typeof str === 'string') {
|
|
||||||
pattern.lastIndex = 0;
|
|
||||||
|
|
||||||
var match = pattern.exec(str);
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
let from = match.index - 1;
|
|
||||||
let args = [];
|
|
||||||
let content = match[0];
|
|
||||||
|
|
||||||
let before = str.slice(0, from + 1);
|
|
||||||
if (before) {
|
|
||||||
args.push(before);
|
|
||||||
}
|
|
||||||
|
|
||||||
args.push({
|
|
||||||
type: token,
|
|
||||||
content,
|
|
||||||
...match.groups,
|
|
||||||
});
|
|
||||||
|
|
||||||
let after = str.slice(from + content.length + 1);
|
|
||||||
if (after) {
|
|
||||||
args.push(after);
|
|
||||||
}
|
|
||||||
|
|
||||||
strarr.splice(i, 1, ...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let offset = 0;
|
|
||||||
for (let i = 0; i < strarr.length; i++) {
|
|
||||||
let token = strarr[i];
|
|
||||||
let length = token.length || token.content.length;
|
|
||||||
|
|
||||||
if (typeof token === 'object') {
|
|
||||||
token.pos = [offset, offset + length];
|
|
||||||
|
|
||||||
if (TRIM_TOKENS.has(token.type)) {
|
|
||||||
token.content = token.content.trim() || ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return strarr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenize(selector) {
|
|
||||||
if (!selector) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
selector = selector.trim(); // prevent leading/trailing whitespace be interpreted as combinators
|
|
||||||
|
|
||||||
// Replace strings with whitespace strings (to preserve offsets)
|
|
||||||
let strings = [];
|
|
||||||
// FIXME Does not account for escaped backslashes before a quote
|
|
||||||
selector = selector.replace(
|
|
||||||
/(['"])(\\\1|.)+?\1/g,
|
|
||||||
(str, quote, content, start) => {
|
|
||||||
strings.push({ str, start });
|
|
||||||
return quote + '§'.repeat(content.length) + quote;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Now that strings are out of the way, extract parens and replace them with parens with whitespace (to preserve offsets)
|
|
||||||
let parens = [],
|
|
||||||
offset = 0,
|
|
||||||
start;
|
|
||||||
while ((start = selector.indexOf('(', offset)) > -1) {
|
|
||||||
let str = gobbleParens(selector, start);
|
|
||||||
parens.push({ str, start });
|
|
||||||
selector =
|
|
||||||
selector.substring(0, start) +
|
|
||||||
'(' +
|
|
||||||
'¶'.repeat(str.length - 2) +
|
|
||||||
')' +
|
|
||||||
selector.substring(start + str.length);
|
|
||||||
offset = start + str.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we have no nested structures and we can parse with regexes
|
|
||||||
let tokens = tokenizeBy(selector, TOKENS);
|
|
||||||
|
|
||||||
// Now restore parens and strings in reverse order
|
|
||||||
function restoreNested(strings, regex, types) {
|
|
||||||
for (let str of strings) {
|
|
||||||
for (let token of tokens) {
|
|
||||||
if (
|
|
||||||
types.has(token.type) &&
|
|
||||||
token.pos[0] < str.start &&
|
|
||||||
str.start < token.pos[1]
|
|
||||||
) {
|
|
||||||
let content = token.content;
|
|
||||||
token.content = token.content.replace(regex, str.str);
|
|
||||||
|
|
||||||
if (token.content !== content) {
|
|
||||||
// actually changed?
|
|
||||||
// Re-evaluate groups
|
|
||||||
TOKENS_FOR_RESTORE[token.type].lastIndex = 0;
|
|
||||||
let match = TOKENS_FOR_RESTORE[token.type].exec(
|
|
||||||
token.content
|
|
||||||
);
|
|
||||||
let groups = match.groups;
|
|
||||||
Object.assign(token, groups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreNested(parens, /\(¶+\)/, TOKENS_WITH_PARENS);
|
|
||||||
restoreNested(strings, /(['"])§+?\1/, TOKENS_WITH_STRINGS);
|
|
||||||
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert a flat list of tokens into a tree of complex & compound selectors
|
|
||||||
function nestTokens(tokens, { list = true } = {}) {
|
|
||||||
if (list && tokens.find((t) => t.type === 'comma')) {
|
|
||||||
let selectors = [],
|
|
||||||
temp = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
|
||||||
if (tokens[i].type === 'comma') {
|
|
||||||
if (temp.length === 0) {
|
|
||||||
throw new Error('Incorrect comma at ' + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
selectors.push(nestTokens(temp, { list: false }));
|
|
||||||
temp.length = 0;
|
|
||||||
} else {
|
|
||||||
temp.push(tokens[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (temp.length === 0) {
|
|
||||||
throw new Error('Trailing comma');
|
|
||||||
} else {
|
|
||||||
selectors.push(nestTokens(temp, { list: false }));
|
|
||||||
}
|
|
||||||
|
|
||||||
return { type: 'list', list: selectors };
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
||||||
let token = tokens[i];
|
|
||||||
|
|
||||||
if (token.type === 'combinator') {
|
|
||||||
let left = tokens.slice(0, i);
|
|
||||||
let right = tokens.slice(i + 1);
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'complex',
|
|
||||||
combinator: token.content,
|
|
||||||
left: nestTokens(left),
|
|
||||||
right: nestTokens(right),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tokens.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're here, there are no combinators, so it's just a list
|
|
||||||
return tokens.length === 1
|
|
||||||
? tokens[0]
|
|
||||||
: {
|
|
||||||
type: 'compound',
|
|
||||||
list: [...tokens], // clone to avoid pointers messing up the AST
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Traverse an AST (or part thereof), in depth-first order
|
|
||||||
function walk(node, callback, o, parent) {
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.type === 'complex') {
|
|
||||||
walk(node.left, callback, o, node);
|
|
||||||
walk(node.right, callback, o, node);
|
|
||||||
} else if (node.type === 'compound') {
|
|
||||||
for (let n of node.list) {
|
|
||||||
walk(n, callback, o, node);
|
|
||||||
}
|
|
||||||
} else if (node.subtree && o && o.subtree) {
|
|
||||||
walk(node.subtree, callback, o, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(node, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a CSS selector
|
|
||||||
* @param selector {String} The selector to parse
|
|
||||||
* @param options.recursive {Boolean} Whether to parse the arguments of pseudo-classes like :is(), :has() etc. Defaults to true.
|
|
||||||
* @param options.list {Boolean} Whether this can be a selector list (A, B, C etc). Defaults to true.
|
|
||||||
*/
|
|
||||||
function parse(selector, { recursive = true, list = true } = {}) {
|
|
||||||
let tokens = tokenize(selector);
|
|
||||||
|
|
||||||
if (!tokens) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ast = nestTokens(tokens, { list });
|
|
||||||
|
|
||||||
if (recursive) {
|
|
||||||
walk(ast, (node) => {
|
|
||||||
if (node.type === 'pseudo-class' && node.argument) {
|
|
||||||
if (RECURSIVE_PSEUDO_CLASSES.has(node.name)) {
|
|
||||||
let argument = node.argument;
|
|
||||||
const childArg =
|
|
||||||
RECURSIVE_PSEUDO_CLASSES_ARGS[node.name];
|
|
||||||
if (childArg) {
|
|
||||||
const match = childArg.exec(argument);
|
|
||||||
if (!match) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(node, match.groups);
|
|
||||||
argument = match.groups.subtree;
|
|
||||||
}
|
|
||||||
if (argument) {
|
|
||||||
node.subtree = parse(argument, {
|
|
||||||
recursive: true,
|
|
||||||
list: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return ast;
|
|
||||||
}
|
|
||||||
|
|
||||||
function specificityToNumber(specificity, base) {
|
|
||||||
base = base || Math.max(...specificity) + 1;
|
|
||||||
|
|
||||||
return (
|
|
||||||
specificity[0] * base ** 2 + specificity[1] * base + specificity[2]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function maxIndexOf(arr) {
|
|
||||||
let max = arr[0],
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < arr.length; i++) {
|
|
||||||
if (arr[i] > max) {
|
|
||||||
ret = i;
|
|
||||||
max = arr[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr.length === 0 ? -1 : ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate specificity of a selector.
|
|
||||||
* If the selector is a list, the max specificity is returned.
|
|
||||||
*/
|
|
||||||
function specificity(selector) {
|
|
||||||
let ast =
|
|
||||||
typeof selector === 'object'
|
|
||||||
? selector
|
|
||||||
: parse(selector, { recursive: true });
|
|
||||||
|
|
||||||
if (!ast) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ast.type === 'list') {
|
|
||||||
// Return max specificity
|
|
||||||
let base = 10;
|
|
||||||
let specificities = ast.list.map((s) => {
|
|
||||||
let sp = specificity(s);
|
|
||||||
base = Math.max(base, ...sp);
|
|
||||||
return sp;
|
|
||||||
});
|
|
||||||
let numbers = specificities.map((s) =>
|
|
||||||
specificityToNumber(s, base)
|
|
||||||
);
|
|
||||||
let i = maxIndexOf(numbers);
|
|
||||||
return specificities[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret = [0, 0, 0];
|
|
||||||
|
|
||||||
walk(ast, (node) => {
|
|
||||||
if (node.type === 'id') {
|
|
||||||
ret[0]++;
|
|
||||||
} else if (node.type === 'class' || node.type === 'attribute') {
|
|
||||||
ret[1]++;
|
|
||||||
} else if (
|
|
||||||
(node.type === 'type' && node.content !== '*') ||
|
|
||||||
node.type === 'pseudo-element'
|
|
||||||
) {
|
|
||||||
ret[2]++;
|
|
||||||
} else if (node.type === 'pseudo-class' && node.name !== 'where') {
|
|
||||||
if (RECURSIVE_PSEUDO_CLASSES.has(node.name) && node.subtree) {
|
|
||||||
// Max of argument list
|
|
||||||
let sub = specificity(node.subtree);
|
|
||||||
sub.forEach((s, i) => (ret[i] += s));
|
|
||||||
} else {
|
|
||||||
ret[1]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.RECURSIVE_PSEUDO_CLASSES = RECURSIVE_PSEUDO_CLASSES;
|
|
||||||
exports.RECURSIVE_PSEUDO_CLASSES_ARGS = RECURSIVE_PSEUDO_CLASSES_ARGS;
|
|
||||||
exports.TOKENS = TOKENS;
|
|
||||||
exports.TRIM_TOKENS = TRIM_TOKENS;
|
|
||||||
exports.gobbleParens = gobbleParens;
|
|
||||||
exports.nestTokens = nestTokens;
|
|
||||||
exports.parse = parse;
|
|
||||||
exports.specificity = specificity;
|
|
||||||
exports.specificityToNumber = specificityToNumber;
|
|
||||||
exports.tokenize = tokenize;
|
|
||||||
exports.tokenizeBy = tokenizeBy;
|
|
||||||
exports.walk = walk;
|
|
||||||
|
|
||||||
Object.defineProperty(exports, '__esModule', { value: true });
|
|
||||||
|
|
||||||
return exports;
|
|
||||||
})({});
|
|
||||||
Loading…
Add table
Reference in a new issue