Fix code quality violations and exclude Manifest from checks
Document application modes (development/debug/production) Add global file drop handler, order column normalization, SPA hash fix Serve CDN assets via /_vendor/ URLs instead of merging into bundles Add production minification with license preservation Improve JSON formatting for debugging and production optimization Add CDN asset caching with CSS URL inlining for production builds Add three-mode system (development, debug, production) Update Manifest CLAUDE.md to reflect helper class architecture Refactor Manifest.php into helper classes for better organization Pre-manifest-refactor checkpoint: Add app_mode documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
517
node_modules/css-tree/cjs/lexer/Lexer.cjs
generated
vendored
Normal file
517
node_modules/css-tree/cjs/lexer/Lexer.cjs
generated
vendored
Normal file
@@ -0,0 +1,517 @@
|
||||
'use strict';
|
||||
|
||||
const error = require('./error.cjs');
|
||||
const names = require('../utils/names.cjs');
|
||||
const genericConst = require('./generic-const.cjs');
|
||||
const generic = require('./generic.cjs');
|
||||
const units = require('./units.cjs');
|
||||
const prepareTokens = require('./prepare-tokens.cjs');
|
||||
const matchGraph = require('./match-graph.cjs');
|
||||
const match = require('./match.cjs');
|
||||
const trace = require('./trace.cjs');
|
||||
const search = require('./search.cjs');
|
||||
const structure = require('./structure.cjs');
|
||||
const parse = require('../definition-syntax/parse.cjs');
|
||||
const generate = require('../definition-syntax/generate.cjs');
|
||||
const walk = require('../definition-syntax/walk.cjs');
|
||||
|
||||
function dumpMapSyntax(map, compact, syntaxAsAst) {
|
||||
const result = {};
|
||||
|
||||
for (const name in map) {
|
||||
if (map[name].syntax) {
|
||||
result[name] = syntaxAsAst
|
||||
? map[name].syntax
|
||||
: generate.generate(map[name].syntax, { compact });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function dumpAtruleMapSyntax(map, compact, syntaxAsAst) {
|
||||
const result = {};
|
||||
|
||||
for (const [name, atrule] of Object.entries(map)) {
|
||||
result[name] = {
|
||||
prelude: atrule.prelude && (
|
||||
syntaxAsAst
|
||||
? atrule.prelude.syntax
|
||||
: generate.generate(atrule.prelude.syntax, { compact })
|
||||
),
|
||||
descriptors: atrule.descriptors && dumpMapSyntax(atrule.descriptors, compact, syntaxAsAst)
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function valueHasVar(tokens) {
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
if (tokens[i].value.toLowerCase() === 'var(') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function syntaxHasTopLevelCommaMultiplier(syntax) {
|
||||
const singleTerm = syntax.terms[0];
|
||||
|
||||
return (
|
||||
syntax.explicit === false &&
|
||||
syntax.terms.length === 1 &&
|
||||
singleTerm.type === 'Multiplier' &&
|
||||
singleTerm.comma === true
|
||||
);
|
||||
}
|
||||
|
||||
function buildMatchResult(matched, error, iterations) {
|
||||
return {
|
||||
matched,
|
||||
iterations,
|
||||
error,
|
||||
...trace
|
||||
};
|
||||
}
|
||||
|
||||
function matchSyntax(lexer, syntax, value, useCssWideKeywords) {
|
||||
const tokens = prepareTokens(value, lexer.syntax);
|
||||
let result;
|
||||
|
||||
if (valueHasVar(tokens)) {
|
||||
return buildMatchResult(null, new Error('Matching for a tree with var() is not supported'));
|
||||
}
|
||||
|
||||
if (useCssWideKeywords) {
|
||||
result = match.matchAsTree(tokens, lexer.cssWideKeywordsSyntax, lexer);
|
||||
}
|
||||
|
||||
if (!useCssWideKeywords || !result.match) {
|
||||
result = match.matchAsTree(tokens, syntax.match, lexer);
|
||||
if (!result.match) {
|
||||
return buildMatchResult(
|
||||
null,
|
||||
new error.SyntaxMatchError(result.reason, syntax.syntax, value, result),
|
||||
result.iterations
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return buildMatchResult(result.match, null, result.iterations);
|
||||
}
|
||||
|
||||
class Lexer {
|
||||
constructor(config, syntax, structure$1) {
|
||||
this.cssWideKeywords = genericConst.cssWideKeywords;
|
||||
this.syntax = syntax;
|
||||
this.generic = false;
|
||||
this.units = { ...units };
|
||||
this.atrules = Object.create(null);
|
||||
this.properties = Object.create(null);
|
||||
this.types = Object.create(null);
|
||||
this.structure = structure$1 || structure.getStructureFromConfig(config);
|
||||
|
||||
if (config) {
|
||||
if (config.cssWideKeywords) {
|
||||
this.cssWideKeywords = config.cssWideKeywords;
|
||||
}
|
||||
|
||||
if (config.units) {
|
||||
for (const group of Object.keys(units)) {
|
||||
if (Array.isArray(config.units[group])) {
|
||||
this.units[group] = config.units[group];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.types) {
|
||||
for (const [name, type] of Object.entries(config.types)) {
|
||||
this.addType_(name, type);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.generic) {
|
||||
this.generic = true;
|
||||
for (const [name, value] of Object.entries(generic.createGenericTypes(this.units))) {
|
||||
this.addType_(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.atrules) {
|
||||
for (const [name, atrule] of Object.entries(config.atrules)) {
|
||||
this.addAtrule_(name, atrule);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.properties) {
|
||||
for (const [name, property] of Object.entries(config.properties)) {
|
||||
this.addProperty_(name, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.cssWideKeywordsSyntax = matchGraph.buildMatchGraph(this.cssWideKeywords.join(' | '));
|
||||
}
|
||||
|
||||
checkStructure(ast) {
|
||||
function collectWarning(node, message) {
|
||||
warns.push({ node, message });
|
||||
}
|
||||
|
||||
const structure = this.structure;
|
||||
const warns = [];
|
||||
|
||||
this.syntax.walk(ast, function(node) {
|
||||
if (structure.hasOwnProperty(node.type)) {
|
||||
structure[node.type].check(node, collectWarning);
|
||||
} else {
|
||||
collectWarning(node, 'Unknown node type `' + node.type + '`');
|
||||
}
|
||||
});
|
||||
|
||||
return warns.length ? warns : false;
|
||||
}
|
||||
|
||||
createDescriptor(syntax, type, name, parent = null) {
|
||||
const ref = {
|
||||
type,
|
||||
name
|
||||
};
|
||||
const descriptor = {
|
||||
type,
|
||||
name,
|
||||
parent,
|
||||
serializable: typeof syntax === 'string' || (syntax && typeof syntax.type === 'string'),
|
||||
syntax: null,
|
||||
match: null,
|
||||
matchRef: null // used for properties when a syntax referenced as <'property'> in other syntax definitions
|
||||
};
|
||||
|
||||
if (typeof syntax === 'function') {
|
||||
descriptor.match = matchGraph.buildMatchGraph(syntax, ref);
|
||||
} else {
|
||||
if (typeof syntax === 'string') {
|
||||
// lazy parsing on first access
|
||||
Object.defineProperty(descriptor, 'syntax', {
|
||||
get() {
|
||||
Object.defineProperty(descriptor, 'syntax', {
|
||||
value: parse.parse(syntax)
|
||||
});
|
||||
|
||||
return descriptor.syntax;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
descriptor.syntax = syntax;
|
||||
}
|
||||
|
||||
// lazy graph build on first access
|
||||
Object.defineProperty(descriptor, 'match', {
|
||||
get() {
|
||||
Object.defineProperty(descriptor, 'match', {
|
||||
value: matchGraph.buildMatchGraph(descriptor.syntax, ref)
|
||||
});
|
||||
|
||||
return descriptor.match;
|
||||
}
|
||||
});
|
||||
|
||||
if (type === 'Property') {
|
||||
Object.defineProperty(descriptor, 'matchRef', {
|
||||
get() {
|
||||
const syntax = descriptor.syntax;
|
||||
const value = syntaxHasTopLevelCommaMultiplier(syntax)
|
||||
? matchGraph.buildMatchGraph({
|
||||
...syntax,
|
||||
terms: [syntax.terms[0].term]
|
||||
}, ref)
|
||||
: null;
|
||||
|
||||
Object.defineProperty(descriptor, 'matchRef', {
|
||||
value
|
||||
});
|
||||
|
||||
return value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
addAtrule_(name, syntax) {
|
||||
if (!syntax) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.atrules[name] = {
|
||||
type: 'Atrule',
|
||||
name: name,
|
||||
prelude: syntax.prelude ? this.createDescriptor(syntax.prelude, 'AtrulePrelude', name) : null,
|
||||
descriptors: syntax.descriptors
|
||||
? Object.keys(syntax.descriptors).reduce(
|
||||
(map, descName) => {
|
||||
map[descName] = this.createDescriptor(syntax.descriptors[descName], 'AtruleDescriptor', descName, name);
|
||||
return map;
|
||||
},
|
||||
Object.create(null)
|
||||
)
|
||||
: null
|
||||
};
|
||||
}
|
||||
addProperty_(name, syntax) {
|
||||
if (!syntax) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.properties[name] = this.createDescriptor(syntax, 'Property', name);
|
||||
}
|
||||
addType_(name, syntax) {
|
||||
if (!syntax) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.types[name] = this.createDescriptor(syntax, 'Type', name);
|
||||
}
|
||||
|
||||
checkAtruleName(atruleName) {
|
||||
if (!this.getAtrule(atruleName)) {
|
||||
return new error.SyntaxReferenceError('Unknown at-rule', '@' + atruleName);
|
||||
}
|
||||
}
|
||||
checkAtrulePrelude(atruleName, prelude) {
|
||||
const error = this.checkAtruleName(atruleName);
|
||||
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
const atrule = this.getAtrule(atruleName);
|
||||
|
||||
if (!atrule.prelude && prelude) {
|
||||
return new SyntaxError('At-rule `@' + atruleName + '` should not contain a prelude');
|
||||
}
|
||||
|
||||
if (atrule.prelude && !prelude) {
|
||||
if (!matchSyntax(this, atrule.prelude, '', false).matched) {
|
||||
return new SyntaxError('At-rule `@' + atruleName + '` should contain a prelude');
|
||||
}
|
||||
}
|
||||
}
|
||||
checkAtruleDescriptorName(atruleName, descriptorName) {
|
||||
const error$1 = this.checkAtruleName(atruleName);
|
||||
|
||||
if (error$1) {
|
||||
return error$1;
|
||||
}
|
||||
|
||||
const atrule = this.getAtrule(atruleName);
|
||||
const descriptor = names.keyword(descriptorName);
|
||||
|
||||
if (!atrule.descriptors) {
|
||||
return new SyntaxError('At-rule `@' + atruleName + '` has no known descriptors');
|
||||
}
|
||||
|
||||
if (!atrule.descriptors[descriptor.name] &&
|
||||
!atrule.descriptors[descriptor.basename]) {
|
||||
return new error.SyntaxReferenceError('Unknown at-rule descriptor', descriptorName);
|
||||
}
|
||||
}
|
||||
checkPropertyName(propertyName) {
|
||||
if (!this.getProperty(propertyName)) {
|
||||
return new error.SyntaxReferenceError('Unknown property', propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
matchAtrulePrelude(atruleName, prelude) {
|
||||
const error = this.checkAtrulePrelude(atruleName, prelude);
|
||||
|
||||
if (error) {
|
||||
return buildMatchResult(null, error);
|
||||
}
|
||||
|
||||
const atrule = this.getAtrule(atruleName);
|
||||
|
||||
if (!atrule.prelude) {
|
||||
return buildMatchResult(null, null);
|
||||
}
|
||||
|
||||
return matchSyntax(this, atrule.prelude, prelude || '', false);
|
||||
}
|
||||
matchAtruleDescriptor(atruleName, descriptorName, value) {
|
||||
const error = this.checkAtruleDescriptorName(atruleName, descriptorName);
|
||||
|
||||
if (error) {
|
||||
return buildMatchResult(null, error);
|
||||
}
|
||||
|
||||
const atrule = this.getAtrule(atruleName);
|
||||
const descriptor = names.keyword(descriptorName);
|
||||
|
||||
return matchSyntax(this, atrule.descriptors[descriptor.name] || atrule.descriptors[descriptor.basename], value, false);
|
||||
}
|
||||
matchDeclaration(node) {
|
||||
if (node.type !== 'Declaration') {
|
||||
return buildMatchResult(null, new Error('Not a Declaration node'));
|
||||
}
|
||||
|
||||
return this.matchProperty(node.property, node.value);
|
||||
}
|
||||
matchProperty(propertyName, value) {
|
||||
// don't match syntax for a custom property at the moment
|
||||
if (names.property(propertyName).custom) {
|
||||
return buildMatchResult(null, new Error('Lexer matching doesn\'t applicable for custom properties'));
|
||||
}
|
||||
|
||||
const error = this.checkPropertyName(propertyName);
|
||||
|
||||
if (error) {
|
||||
return buildMatchResult(null, error);
|
||||
}
|
||||
|
||||
return matchSyntax(this, this.getProperty(propertyName), value, true);
|
||||
}
|
||||
matchType(typeName, value) {
|
||||
const typeSyntax = this.getType(typeName);
|
||||
|
||||
if (!typeSyntax) {
|
||||
return buildMatchResult(null, new error.SyntaxReferenceError('Unknown type', typeName));
|
||||
}
|
||||
|
||||
return matchSyntax(this, typeSyntax, value, false);
|
||||
}
|
||||
match(syntax, value) {
|
||||
if (typeof syntax !== 'string' && (!syntax || !syntax.type)) {
|
||||
return buildMatchResult(null, new error.SyntaxReferenceError('Bad syntax'));
|
||||
}
|
||||
|
||||
if (typeof syntax === 'string' || !syntax.match) {
|
||||
syntax = this.createDescriptor(syntax, 'Type', 'anonymous');
|
||||
}
|
||||
|
||||
return matchSyntax(this, syntax, value, false);
|
||||
}
|
||||
|
||||
findValueFragments(propertyName, value, type, name) {
|
||||
return search.matchFragments(this, value, this.matchProperty(propertyName, value), type, name);
|
||||
}
|
||||
findDeclarationValueFragments(declaration, type, name) {
|
||||
return search.matchFragments(this, declaration.value, this.matchDeclaration(declaration), type, name);
|
||||
}
|
||||
findAllFragments(ast, type, name) {
|
||||
const result = [];
|
||||
|
||||
this.syntax.walk(ast, {
|
||||
visit: 'Declaration',
|
||||
enter: (declaration) => {
|
||||
result.push.apply(result, this.findDeclarationValueFragments(declaration, type, name));
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getAtrule(atruleName, fallbackBasename = true) {
|
||||
const atrule = names.keyword(atruleName);
|
||||
const atruleEntry = atrule.vendor && fallbackBasename
|
||||
? this.atrules[atrule.name] || this.atrules[atrule.basename]
|
||||
: this.atrules[atrule.name];
|
||||
|
||||
return atruleEntry || null;
|
||||
}
|
||||
getAtrulePrelude(atruleName, fallbackBasename = true) {
|
||||
const atrule = this.getAtrule(atruleName, fallbackBasename);
|
||||
|
||||
return atrule && atrule.prelude || null;
|
||||
}
|
||||
getAtruleDescriptor(atruleName, name) {
|
||||
return this.atrules.hasOwnProperty(atruleName) && this.atrules.declarators
|
||||
? this.atrules[atruleName].declarators[name] || null
|
||||
: null;
|
||||
}
|
||||
getProperty(propertyName, fallbackBasename = true) {
|
||||
const property = names.property(propertyName);
|
||||
const propertyEntry = property.vendor && fallbackBasename
|
||||
? this.properties[property.name] || this.properties[property.basename]
|
||||
: this.properties[property.name];
|
||||
|
||||
return propertyEntry || null;
|
||||
}
|
||||
getType(name) {
|
||||
return hasOwnProperty.call(this.types, name) ? this.types[name] : null;
|
||||
}
|
||||
|
||||
validate() {
|
||||
function syntaxRef(name, isType) {
|
||||
return isType ? `<${name}>` : `<'${name}'>`;
|
||||
}
|
||||
|
||||
function validate(syntax, name, broken, descriptor) {
|
||||
if (broken.has(name)) {
|
||||
return broken.get(name);
|
||||
}
|
||||
|
||||
broken.set(name, false);
|
||||
if (descriptor.syntax !== null) {
|
||||
walk.walk(descriptor.syntax, function(node) {
|
||||
if (node.type !== 'Type' && node.type !== 'Property') {
|
||||
return;
|
||||
}
|
||||
|
||||
const map = node.type === 'Type' ? syntax.types : syntax.properties;
|
||||
const brokenMap = node.type === 'Type' ? brokenTypes : brokenProperties;
|
||||
|
||||
if (!hasOwnProperty.call(map, node.name)) {
|
||||
errors.push(`${syntaxRef(name, broken === brokenTypes)} used missed syntax definition ${syntaxRef(node.name, node.type === 'Type')}`);
|
||||
broken.set(name, true);
|
||||
} else if (validate(syntax, node.name, brokenMap, map[node.name])) {
|
||||
errors.push(`${syntaxRef(name, broken === brokenTypes)} used broken syntax definition ${syntaxRef(node.name, node.type === 'Type')}`);
|
||||
broken.set(name, true);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
const errors = [];
|
||||
let brokenTypes = new Map();
|
||||
let brokenProperties = new Map();
|
||||
|
||||
for (const key in this.types) {
|
||||
validate(this, key, brokenTypes, this.types[key]);
|
||||
}
|
||||
|
||||
for (const key in this.properties) {
|
||||
validate(this, key, brokenProperties, this.properties[key]);
|
||||
}
|
||||
|
||||
const brokenTypesArray = [...brokenTypes.keys()].filter(name => brokenTypes.get(name));
|
||||
const brokenPropertiesArray = [...brokenProperties.keys()].filter(name => brokenProperties.get(name));
|
||||
|
||||
if (brokenTypesArray.length || brokenPropertiesArray.length) {
|
||||
return {
|
||||
errors,
|
||||
types: brokenTypesArray,
|
||||
properties: brokenPropertiesArray
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
dump(syntaxAsAst, pretty) {
|
||||
return {
|
||||
generic: this.generic,
|
||||
cssWideKeywords: this.cssWideKeywords,
|
||||
units: this.units,
|
||||
types: dumpMapSyntax(this.types, !pretty, syntaxAsAst),
|
||||
properties: dumpMapSyntax(this.properties, !pretty, syntaxAsAst),
|
||||
atrules: dumpAtruleMapSyntax(this.atrules, !pretty, syntaxAsAst)
|
||||
};
|
||||
}
|
||||
toString() {
|
||||
return JSON.stringify(this.dump());
|
||||
}
|
||||
}
|
||||
|
||||
exports.Lexer = Lexer;
|
||||
128
node_modules/css-tree/cjs/lexer/error.cjs
generated
vendored
Normal file
128
node_modules/css-tree/cjs/lexer/error.cjs
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
'use strict';
|
||||
|
||||
const createCustomError = require('../utils/create-custom-error.cjs');
|
||||
const generate = require('../definition-syntax/generate.cjs');
|
||||
|
||||
const defaultLoc = { offset: 0, line: 1, column: 1 };
|
||||
|
||||
function locateMismatch(matchResult, node) {
|
||||
const tokens = matchResult.tokens;
|
||||
const longestMatch = matchResult.longestMatch;
|
||||
const mismatchNode = longestMatch < tokens.length ? tokens[longestMatch].node || null : null;
|
||||
const badNode = mismatchNode !== node ? mismatchNode : null;
|
||||
let mismatchOffset = 0;
|
||||
let mismatchLength = 0;
|
||||
let entries = 0;
|
||||
let css = '';
|
||||
let start;
|
||||
let end;
|
||||
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i].value;
|
||||
|
||||
if (i === longestMatch) {
|
||||
mismatchLength = token.length;
|
||||
mismatchOffset = css.length;
|
||||
}
|
||||
|
||||
if (badNode !== null && tokens[i].node === badNode) {
|
||||
if (i <= longestMatch) {
|
||||
entries++;
|
||||
} else {
|
||||
entries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
css += token;
|
||||
}
|
||||
|
||||
if (longestMatch === tokens.length || entries > 1) { // last
|
||||
start = fromLoc(badNode || node, 'end') || buildLoc(defaultLoc, css);
|
||||
end = buildLoc(start);
|
||||
} else {
|
||||
start = fromLoc(badNode, 'start') ||
|
||||
buildLoc(fromLoc(node, 'start') || defaultLoc, css.slice(0, mismatchOffset));
|
||||
end = fromLoc(badNode, 'end') ||
|
||||
buildLoc(start, css.substr(mismatchOffset, mismatchLength));
|
||||
}
|
||||
|
||||
return {
|
||||
css,
|
||||
mismatchOffset,
|
||||
mismatchLength,
|
||||
start,
|
||||
end
|
||||
};
|
||||
}
|
||||
|
||||
function fromLoc(node, point) {
|
||||
const value = node && node.loc && node.loc[point];
|
||||
|
||||
if (value) {
|
||||
return 'line' in value ? buildLoc(value) : value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildLoc({ offset, line, column }, extra) {
|
||||
const loc = {
|
||||
offset,
|
||||
line,
|
||||
column
|
||||
};
|
||||
|
||||
if (extra) {
|
||||
const lines = extra.split(/\n|\r\n?|\f/);
|
||||
|
||||
loc.offset += extra.length;
|
||||
loc.line += lines.length - 1;
|
||||
loc.column = lines.length === 1 ? loc.column + extra.length : lines.pop().length + 1;
|
||||
}
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
const SyntaxReferenceError = function(type, referenceName) {
|
||||
const error = createCustomError.createCustomError(
|
||||
'SyntaxReferenceError',
|
||||
type + (referenceName ? ' `' + referenceName + '`' : '')
|
||||
);
|
||||
|
||||
error.reference = referenceName;
|
||||
|
||||
return error;
|
||||
};
|
||||
|
||||
const SyntaxMatchError = function(message, syntax, node, matchResult) {
|
||||
const error = createCustomError.createCustomError('SyntaxMatchError', message);
|
||||
const {
|
||||
css,
|
||||
mismatchOffset,
|
||||
mismatchLength,
|
||||
start,
|
||||
end
|
||||
} = locateMismatch(matchResult, node);
|
||||
|
||||
error.rawMessage = message;
|
||||
error.syntax = syntax ? generate.generate(syntax) : '<generic>';
|
||||
error.css = css;
|
||||
error.mismatchOffset = mismatchOffset;
|
||||
error.mismatchLength = mismatchLength;
|
||||
error.message = message + '\n' +
|
||||
' syntax: ' + error.syntax + '\n' +
|
||||
' value: ' + (css || '<empty string>') + '\n' +
|
||||
' --------' + new Array(error.mismatchOffset + 1).join('-') + '^';
|
||||
|
||||
Object.assign(error, start);
|
||||
error.loc = {
|
||||
source: (node && node.loc && node.loc.source) || '<unknown>',
|
||||
start,
|
||||
end
|
||||
};
|
||||
|
||||
return error;
|
||||
};
|
||||
|
||||
exports.SyntaxMatchError = SyntaxMatchError;
|
||||
exports.SyntaxReferenceError = SyntaxReferenceError;
|
||||
235
node_modules/css-tree/cjs/lexer/generic-an-plus-b.cjs
generated
vendored
Normal file
235
node_modules/css-tree/cjs/lexer/generic-an-plus-b.cjs
generated
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
'use strict';
|
||||
|
||||
const charCodeDefinitions = require('../tokenizer/char-code-definitions.cjs');
|
||||
const types = require('../tokenizer/types.cjs');
|
||||
const utils = require('../tokenizer/utils.cjs');
|
||||
|
||||
const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
|
||||
const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
|
||||
const N = 0x006E; // U+006E LATIN SMALL LETTER N (n)
|
||||
const DISALLOW_SIGN = true;
|
||||
const ALLOW_SIGN = false;
|
||||
|
||||
function isDelim(token, code) {
|
||||
return token !== null && token.type === types.Delim && token.value.charCodeAt(0) === code;
|
||||
}
|
||||
|
||||
function skipSC(token, offset, getNextToken) {
|
||||
while (token !== null && (token.type === types.WhiteSpace || token.type === types.Comment)) {
|
||||
token = getNextToken(++offset);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
function checkInteger(token, valueOffset, disallowSign, offset) {
|
||||
if (!token) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const code = token.value.charCodeAt(valueOffset);
|
||||
|
||||
if (code === PLUSSIGN || code === HYPHENMINUS) {
|
||||
if (disallowSign) {
|
||||
// Number sign is not allowed
|
||||
return 0;
|
||||
}
|
||||
valueOffset++;
|
||||
}
|
||||
|
||||
for (; valueOffset < token.value.length; valueOffset++) {
|
||||
if (!charCodeDefinitions.isDigit(token.value.charCodeAt(valueOffset))) {
|
||||
// Integer is expected
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return offset + 1;
|
||||
}
|
||||
|
||||
// ... <signed-integer>
|
||||
// ... ['+' | '-'] <signless-integer>
|
||||
function consumeB(token, offset_, getNextToken) {
|
||||
let sign = false;
|
||||
let offset = skipSC(token, offset_, getNextToken);
|
||||
|
||||
token = getNextToken(offset);
|
||||
|
||||
if (token === null) {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
if (token.type !== types.Number) {
|
||||
if (isDelim(token, PLUSSIGN) || isDelim(token, HYPHENMINUS)) {
|
||||
sign = true;
|
||||
offset = skipSC(getNextToken(++offset), offset, getNextToken);
|
||||
token = getNextToken(offset);
|
||||
|
||||
if (token === null || token.type !== types.Number) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return offset_;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sign) {
|
||||
const code = token.value.charCodeAt(0);
|
||||
if (code !== PLUSSIGN && code !== HYPHENMINUS) {
|
||||
// Number sign is expected
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return checkInteger(token, sign ? 0 : 1, sign, offset);
|
||||
}
|
||||
|
||||
// An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb
|
||||
function anPlusB(token, getNextToken) {
|
||||
/* eslint-disable brace-style*/
|
||||
let offset = 0;
|
||||
|
||||
if (!token) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// <integer>
|
||||
if (token.type === types.Number) {
|
||||
return checkInteger(token, 0, ALLOW_SIGN, offset); // b
|
||||
}
|
||||
|
||||
// -n
|
||||
// -n <signed-integer>
|
||||
// -n ['+' | '-'] <signless-integer>
|
||||
// -n- <signless-integer>
|
||||
// <dashndashdigit-ident>
|
||||
else if (token.type === types.Ident && token.value.charCodeAt(0) === HYPHENMINUS) {
|
||||
// expect 1st char is N
|
||||
if (!utils.cmpChar(token.value, 1, N)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (token.value.length) {
|
||||
// -n
|
||||
// -n <signed-integer>
|
||||
// -n ['+' | '-'] <signless-integer>
|
||||
case 2:
|
||||
return consumeB(getNextToken(++offset), offset, getNextToken);
|
||||
|
||||
// -n- <signless-integer>
|
||||
case 3:
|
||||
if (token.value.charCodeAt(2) !== HYPHENMINUS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = skipSC(getNextToken(++offset), offset, getNextToken);
|
||||
token = getNextToken(offset);
|
||||
|
||||
return checkInteger(token, 0, DISALLOW_SIGN, offset);
|
||||
|
||||
// <dashndashdigit-ident>
|
||||
default:
|
||||
if (token.value.charCodeAt(2) !== HYPHENMINUS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return checkInteger(token, 3, DISALLOW_SIGN, offset);
|
||||
}
|
||||
}
|
||||
|
||||
// '+'? n
|
||||
// '+'? n <signed-integer>
|
||||
// '+'? n ['+' | '-'] <signless-integer>
|
||||
// '+'? n- <signless-integer>
|
||||
// '+'? <ndashdigit-ident>
|
||||
else if (token.type === types.Ident || (isDelim(token, PLUSSIGN) && getNextToken(offset + 1).type === types.Ident)) {
|
||||
// just ignore a plus
|
||||
if (token.type !== types.Ident) {
|
||||
token = getNextToken(++offset);
|
||||
}
|
||||
|
||||
if (token === null || !utils.cmpChar(token.value, 0, N)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (token.value.length) {
|
||||
// '+'? n
|
||||
// '+'? n <signed-integer>
|
||||
// '+'? n ['+' | '-'] <signless-integer>
|
||||
case 1:
|
||||
return consumeB(getNextToken(++offset), offset, getNextToken);
|
||||
|
||||
// '+'? n- <signless-integer>
|
||||
case 2:
|
||||
if (token.value.charCodeAt(1) !== HYPHENMINUS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = skipSC(getNextToken(++offset), offset, getNextToken);
|
||||
token = getNextToken(offset);
|
||||
|
||||
return checkInteger(token, 0, DISALLOW_SIGN, offset);
|
||||
|
||||
// '+'? <ndashdigit-ident>
|
||||
default:
|
||||
if (token.value.charCodeAt(1) !== HYPHENMINUS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return checkInteger(token, 2, DISALLOW_SIGN, offset);
|
||||
}
|
||||
}
|
||||
|
||||
// <ndashdigit-dimension>
|
||||
// <ndash-dimension> <signless-integer>
|
||||
// <n-dimension>
|
||||
// <n-dimension> <signed-integer>
|
||||
// <n-dimension> ['+' | '-'] <signless-integer>
|
||||
else if (token.type === types.Dimension) {
|
||||
let code = token.value.charCodeAt(0);
|
||||
let sign = code === PLUSSIGN || code === HYPHENMINUS ? 1 : 0;
|
||||
let i = sign;
|
||||
|
||||
for (; i < token.value.length; i++) {
|
||||
if (!charCodeDefinitions.isDigit(token.value.charCodeAt(i))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i === sign) {
|
||||
// Integer is expected
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!utils.cmpChar(token.value, i, N)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// <n-dimension>
|
||||
// <n-dimension> <signed-integer>
|
||||
// <n-dimension> ['+' | '-'] <signless-integer>
|
||||
if (i + 1 === token.value.length) {
|
||||
return consumeB(getNextToken(++offset), offset, getNextToken);
|
||||
} else {
|
||||
if (token.value.charCodeAt(i + 1) !== HYPHENMINUS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// <ndash-dimension> <signless-integer>
|
||||
if (i + 2 === token.value.length) {
|
||||
offset = skipSC(getNextToken(++offset), offset, getNextToken);
|
||||
token = getNextToken(offset);
|
||||
|
||||
return checkInteger(token, 0, DISALLOW_SIGN, offset);
|
||||
}
|
||||
// <ndashdigit-dimension>
|
||||
else {
|
||||
return checkInteger(token, i + 2, DISALLOW_SIGN, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module.exports = anPlusB;
|
||||
12
node_modules/css-tree/cjs/lexer/generic-const.cjs
generated
vendored
Normal file
12
node_modules/css-tree/cjs/lexer/generic-const.cjs
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
// https://drafts.csswg.org/css-cascade-5/
|
||||
const cssWideKeywords = [
|
||||
'initial',
|
||||
'inherit',
|
||||
'unset',
|
||||
'revert',
|
||||
'revert-layer'
|
||||
];
|
||||
|
||||
exports.cssWideKeywords = cssWideKeywords;
|
||||
149
node_modules/css-tree/cjs/lexer/generic-urange.cjs
generated
vendored
Normal file
149
node_modules/css-tree/cjs/lexer/generic-urange.cjs
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
'use strict';
|
||||
|
||||
const charCodeDefinitions = require('../tokenizer/char-code-definitions.cjs');
|
||||
const types = require('../tokenizer/types.cjs');
|
||||
const utils = require('../tokenizer/utils.cjs');
|
||||
|
||||
const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
|
||||
const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
|
||||
const QUESTIONMARK = 0x003F; // U+003F QUESTION MARK (?)
|
||||
const U = 0x0075; // U+0075 LATIN SMALL LETTER U (u)
|
||||
|
||||
function isDelim(token, code) {
|
||||
return token !== null && token.type === types.Delim && token.value.charCodeAt(0) === code;
|
||||
}
|
||||
|
||||
function startsWith(token, code) {
|
||||
return token.value.charCodeAt(0) === code;
|
||||
}
|
||||
|
||||
function hexSequence(token, offset, allowDash) {
|
||||
let hexlen = 0;
|
||||
|
||||
for (let pos = offset; pos < token.value.length; pos++) {
|
||||
const code = token.value.charCodeAt(pos);
|
||||
|
||||
if (code === HYPHENMINUS && allowDash && hexlen !== 0) {
|
||||
hexSequence(token, offset + hexlen + 1, false);
|
||||
return 6; // dissallow following question marks
|
||||
}
|
||||
|
||||
if (!charCodeDefinitions.isHexDigit(code)) {
|
||||
return 0; // not a hex digit
|
||||
}
|
||||
|
||||
if (++hexlen > 6) {
|
||||
return 0; // too many hex digits
|
||||
} }
|
||||
|
||||
return hexlen;
|
||||
}
|
||||
|
||||
function withQuestionMarkSequence(consumed, length, getNextToken) {
|
||||
if (!consumed) {
|
||||
return 0; // nothing consumed
|
||||
}
|
||||
|
||||
while (isDelim(getNextToken(length), QUESTIONMARK)) {
|
||||
if (++consumed > 6) {
|
||||
return 0; // too many question marks
|
||||
}
|
||||
|
||||
length++;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-syntax/#urange
|
||||
// Informally, the <urange> production has three forms:
|
||||
// U+0001
|
||||
// Defines a range consisting of a single code point, in this case the code point "1".
|
||||
// U+0001-00ff
|
||||
// Defines a range of codepoints between the first and the second value, in this case
|
||||
// the range between "1" and "ff" (255 in decimal) inclusive.
|
||||
// U+00??
|
||||
// Defines a range of codepoints where the "?" characters range over all hex digits,
|
||||
// in this case defining the same as the value U+0000-00ff.
|
||||
// In each form, a maximum of 6 digits is allowed for each hexadecimal number (if you treat "?" as a hexadecimal digit).
|
||||
//
|
||||
// <urange> =
|
||||
// u '+' <ident-token> '?'* |
|
||||
// u <dimension-token> '?'* |
|
||||
// u <number-token> '?'* |
|
||||
// u <number-token> <dimension-token> |
|
||||
// u <number-token> <number-token> |
|
||||
// u '+' '?'+
|
||||
function urange(token, getNextToken) {
|
||||
let length = 0;
|
||||
|
||||
// should start with `u` or `U`
|
||||
if (token === null || token.type !== types.Ident || !utils.cmpChar(token.value, 0, U)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
token = getNextToken(++length);
|
||||
if (token === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// u '+' <ident-token> '?'*
|
||||
// u '+' '?'+
|
||||
if (isDelim(token, PLUSSIGN)) {
|
||||
token = getNextToken(++length);
|
||||
if (token === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (token.type === types.Ident) {
|
||||
// u '+' <ident-token> '?'*
|
||||
return withQuestionMarkSequence(hexSequence(token, 0, true), ++length, getNextToken);
|
||||
}
|
||||
|
||||
if (isDelim(token, QUESTIONMARK)) {
|
||||
// u '+' '?'+
|
||||
return withQuestionMarkSequence(1, ++length, getNextToken);
|
||||
}
|
||||
|
||||
// Hex digit or question mark is expected
|
||||
return 0;
|
||||
}
|
||||
|
||||
// u <number-token> '?'*
|
||||
// u <number-token> <dimension-token>
|
||||
// u <number-token> <number-token>
|
||||
if (token.type === types.Number) {
|
||||
const consumedHexLength = hexSequence(token, 1, true);
|
||||
if (consumedHexLength === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
token = getNextToken(++length);
|
||||
if (token === null) {
|
||||
// u <number-token> <eof>
|
||||
return length;
|
||||
}
|
||||
|
||||
if (token.type === types.Dimension || token.type === types.Number) {
|
||||
// u <number-token> <dimension-token>
|
||||
// u <number-token> <number-token>
|
||||
if (!startsWith(token, HYPHENMINUS) || !hexSequence(token, 1, false)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return length + 1;
|
||||
}
|
||||
|
||||
// u <number-token> '?'*
|
||||
return withQuestionMarkSequence(consumedHexLength, length, getNextToken);
|
||||
}
|
||||
|
||||
// u <dimension-token> '?'*
|
||||
if (token.type === types.Dimension) {
|
||||
return withQuestionMarkSequence(hexSequence(token, 1, true), ++length, getNextToken);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module.exports = urange;
|
||||
589
node_modules/css-tree/cjs/lexer/generic.cjs
generated
vendored
Normal file
589
node_modules/css-tree/cjs/lexer/generic.cjs
generated
vendored
Normal file
@@ -0,0 +1,589 @@
|
||||
'use strict';
|
||||
|
||||
const genericConst = require('./generic-const.cjs');
|
||||
const genericAnPlusB = require('./generic-an-plus-b.cjs');
|
||||
const genericUrange = require('./generic-urange.cjs');
|
||||
const charCodeDefinitions = require('../tokenizer/char-code-definitions.cjs');
|
||||
const types = require('../tokenizer/types.cjs');
|
||||
const utils = require('../tokenizer/utils.cjs');
|
||||
|
||||
const calcFunctionNames = ['calc(', '-moz-calc(', '-webkit-calc('];
|
||||
const balancePair = new Map([
|
||||
[types.Function, types.RightParenthesis],
|
||||
[types.LeftParenthesis, types.RightParenthesis],
|
||||
[types.LeftSquareBracket, types.RightSquareBracket],
|
||||
[types.LeftCurlyBracket, types.RightCurlyBracket]
|
||||
]);
|
||||
|
||||
// safe char code getter
|
||||
function charCodeAt(str, index) {
|
||||
return index < str.length ? str.charCodeAt(index) : 0;
|
||||
}
|
||||
|
||||
function eqStr(actual, expected) {
|
||||
return utils.cmpStr(actual, 0, actual.length, expected);
|
||||
}
|
||||
|
||||
function eqStrAny(actual, expected) {
|
||||
for (let i = 0; i < expected.length; i++) {
|
||||
if (eqStr(actual, expected[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// IE postfix hack, i.e. 123\0 or 123px\9
|
||||
function isPostfixIeHack(str, offset) {
|
||||
if (offset !== str.length - 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
charCodeAt(str, offset) === 0x005C && // U+005C REVERSE SOLIDUS (\)
|
||||
charCodeDefinitions.isDigit(charCodeAt(str, offset + 1))
|
||||
);
|
||||
}
|
||||
|
||||
function outOfRange(opts, value, numEnd) {
|
||||
if (opts && opts.type === 'Range') {
|
||||
const num = Number(
|
||||
numEnd !== undefined && numEnd !== value.length
|
||||
? value.substr(0, numEnd)
|
||||
: value
|
||||
);
|
||||
|
||||
if (isNaN(num)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: when opts.min is a string it's a dimension, skip a range validation
|
||||
// for now since it requires a type covertation which is not implmented yet
|
||||
if (opts.min !== null && num < opts.min && typeof opts.min !== 'string') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: when opts.max is a string it's a dimension, skip a range validation
|
||||
// for now since it requires a type covertation which is not implmented yet
|
||||
if (opts.max !== null && num > opts.max && typeof opts.max !== 'string') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function consumeFunction(token, getNextToken) {
|
||||
let balanceCloseType = 0;
|
||||
let balanceStash = [];
|
||||
let length = 0;
|
||||
|
||||
// balanced token consuming
|
||||
scan:
|
||||
do {
|
||||
switch (token.type) {
|
||||
case types.RightCurlyBracket:
|
||||
case types.RightParenthesis:
|
||||
case types.RightSquareBracket:
|
||||
if (token.type !== balanceCloseType) {
|
||||
break scan;
|
||||
}
|
||||
|
||||
balanceCloseType = balanceStash.pop();
|
||||
|
||||
if (balanceStash.length === 0) {
|
||||
length++;
|
||||
break scan;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case types.Function:
|
||||
case types.LeftParenthesis:
|
||||
case types.LeftSquareBracket:
|
||||
case types.LeftCurlyBracket:
|
||||
balanceStash.push(balanceCloseType);
|
||||
balanceCloseType = balancePair.get(token.type);
|
||||
break;
|
||||
}
|
||||
|
||||
length++;
|
||||
} while (token = getNextToken(length));
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
// can be used wherever <length>, <frequency>, <angle>, <time>, <percentage>, <number>, or <integer> values are allowed
|
||||
// https://drafts.csswg.org/css-values/#calc-notation
|
||||
function calc(next) {
|
||||
return function(token, getNextToken, opts) {
|
||||
if (token === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (token.type === types.Function && eqStrAny(token.value, calcFunctionNames)) {
|
||||
return consumeFunction(token, getNextToken);
|
||||
}
|
||||
|
||||
return next(token, getNextToken, opts);
|
||||
};
|
||||
}
|
||||
|
||||
function tokenType(expectedTokenType) {
|
||||
return function(token) {
|
||||
if (token === null || token.type !== expectedTokenType) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
};
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Complex types
|
||||
//
|
||||
|
||||
// https://drafts.csswg.org/css-values-4/#custom-idents
|
||||
// 4.2. Author-defined Identifiers: the <custom-ident> type
|
||||
// Some properties accept arbitrary author-defined identifiers as a component value.
|
||||
// This generic data type is denoted by <custom-ident>, and represents any valid CSS identifier
|
||||
// that would not be misinterpreted as a pre-defined keyword in that property’s value definition.
|
||||
//
|
||||
// See also: https://developer.mozilla.org/en-US/docs/Web/CSS/custom-ident
|
||||
function customIdent(token) {
|
||||
if (token === null || token.type !== types.Ident) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const name = token.value.toLowerCase();
|
||||
|
||||
// The CSS-wide keywords are not valid <custom-ident>s
|
||||
if (eqStrAny(name, genericConst.cssWideKeywords)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The default keyword is reserved and is also not a valid <custom-ident>
|
||||
if (eqStr(name, 'default')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: ignore property specific keywords (as described https://developer.mozilla.org/en-US/docs/Web/CSS/custom-ident)
|
||||
// Specifications using <custom-ident> must specify clearly what other keywords
|
||||
// are excluded from <custom-ident>, if any—for example by saying that any pre-defined keywords
|
||||
// in that property’s value definition are excluded. Excluded keywords are excluded
|
||||
// in all ASCII case permutations.
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-values-4/#dashed-idents
|
||||
// The <dashed-ident> production is a <custom-ident>, with all the case-sensitivity that implies,
|
||||
// with the additional restriction that it must start with two dashes (U+002D HYPHEN-MINUS).
|
||||
function dashedIdent(token) {
|
||||
if (token === null || token.type !== types.Ident) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ... must start with two dashes (U+002D HYPHEN-MINUS)
|
||||
if (charCodeAt(token.value, 0) !== 0x002D || charCodeAt(token.value, 1) !== 0x002D) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-variables/#typedef-custom-property-name
|
||||
// A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo.
|
||||
// The <custom-property-name> production corresponds to this: it’s defined as any <dashed-ident>
|
||||
// (a valid identifier that starts with two dashes), except -- itself, which is reserved for future use by CSS.
|
||||
function customPropertyName(token) {
|
||||
// ... it’s defined as any <dashed-ident>
|
||||
if (!dashedIdent(token)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ... except -- itself, which is reserved for future use by CSS
|
||||
if (token.value === '--') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-color-4/#hex-notation
|
||||
// The syntax of a <hex-color> is a <hash-token> token whose value consists of 3, 4, 6, or 8 hexadecimal digits.
|
||||
// In other words, a hex color is written as a hash character, "#", followed by some number of digits 0-9 or
|
||||
// letters a-f (the case of the letters doesn’t matter - #00ff00 is identical to #00FF00).
|
||||
function hexColor(token) {
|
||||
if (token === null || token.type !== types.Hash) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const length = token.value.length;
|
||||
|
||||
// valid values (length): #rgb (4), #rgba (5), #rrggbb (7), #rrggbbaa (9)
|
||||
if (length !== 4 && length !== 5 && length !== 7 && length !== 9) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (let i = 1; i < length; i++) {
|
||||
if (!charCodeDefinitions.isHexDigit(charCodeAt(token.value, i))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
function idSelector(token) {
|
||||
if (token === null || token.type !== types.Hash) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!charCodeDefinitions.isIdentifierStart(charCodeAt(token.value, 1), charCodeAt(token.value, 2), charCodeAt(token.value, 3))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-syntax/#any-value
|
||||
// It represents the entirety of what a valid declaration can have as its value.
|
||||
function declarationValue(token, getNextToken) {
|
||||
if (!token) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let balanceCloseType = 0;
|
||||
let balanceStash = [];
|
||||
let length = 0;
|
||||
|
||||
// The <declaration-value> production matches any sequence of one or more tokens,
|
||||
// so long as the sequence does not contain ...
|
||||
scan:
|
||||
do {
|
||||
switch (token.type) {
|
||||
// ... <bad-string-token>, <bad-url-token>,
|
||||
case types.BadString:
|
||||
case types.BadUrl:
|
||||
break scan;
|
||||
|
||||
// ... unmatched <)-token>, <]-token>, or <}-token>,
|
||||
case types.RightCurlyBracket:
|
||||
case types.RightParenthesis:
|
||||
case types.RightSquareBracket:
|
||||
if (token.type !== balanceCloseType) {
|
||||
break scan;
|
||||
}
|
||||
|
||||
balanceCloseType = balanceStash.pop();
|
||||
break;
|
||||
|
||||
// ... or top-level <semicolon-token> tokens
|
||||
case types.Semicolon:
|
||||
if (balanceCloseType === 0) {
|
||||
break scan;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// ... or <delim-token> tokens with a value of "!"
|
||||
case types.Delim:
|
||||
if (balanceCloseType === 0 && token.value === '!') {
|
||||
break scan;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case types.Function:
|
||||
case types.LeftParenthesis:
|
||||
case types.LeftSquareBracket:
|
||||
case types.LeftCurlyBracket:
|
||||
balanceStash.push(balanceCloseType);
|
||||
balanceCloseType = balancePair.get(token.type);
|
||||
break;
|
||||
}
|
||||
|
||||
length++;
|
||||
} while (token = getNextToken(length));
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-syntax/#any-value
|
||||
// The <any-value> production is identical to <declaration-value>, but also
|
||||
// allows top-level <semicolon-token> tokens and <delim-token> tokens
|
||||
// with a value of "!". It represents the entirety of what valid CSS can be in any context.
|
||||
function anyValue(token, getNextToken) {
|
||||
if (!token) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let balanceCloseType = 0;
|
||||
let balanceStash = [];
|
||||
let length = 0;
|
||||
|
||||
// The <any-value> production matches any sequence of one or more tokens,
|
||||
// so long as the sequence ...
|
||||
scan:
|
||||
do {
|
||||
switch (token.type) {
|
||||
// ... does not contain <bad-string-token>, <bad-url-token>,
|
||||
case types.BadString:
|
||||
case types.BadUrl:
|
||||
break scan;
|
||||
|
||||
// ... unmatched <)-token>, <]-token>, or <}-token>,
|
||||
case types.RightCurlyBracket:
|
||||
case types.RightParenthesis:
|
||||
case types.RightSquareBracket:
|
||||
if (token.type !== balanceCloseType) {
|
||||
break scan;
|
||||
}
|
||||
|
||||
balanceCloseType = balanceStash.pop();
|
||||
break;
|
||||
|
||||
case types.Function:
|
||||
case types.LeftParenthesis:
|
||||
case types.LeftSquareBracket:
|
||||
case types.LeftCurlyBracket:
|
||||
balanceStash.push(balanceCloseType);
|
||||
balanceCloseType = balancePair.get(token.type);
|
||||
break;
|
||||
}
|
||||
|
||||
length++;
|
||||
} while (token = getNextToken(length));
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Dimensions
|
||||
//
|
||||
|
||||
function dimension(type) {
|
||||
if (type) {
|
||||
type = new Set(type);
|
||||
}
|
||||
|
||||
return function(token, getNextToken, opts) {
|
||||
if (token === null || token.type !== types.Dimension) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const numberEnd = utils.consumeNumber(token.value, 0);
|
||||
|
||||
// check unit
|
||||
if (type !== null) {
|
||||
// check for IE postfix hack, i.e. 123px\0 or 123px\9
|
||||
const reverseSolidusOffset = token.value.indexOf('\\', numberEnd);
|
||||
const unit = reverseSolidusOffset === -1 || !isPostfixIeHack(token.value, reverseSolidusOffset)
|
||||
? token.value.substr(numberEnd)
|
||||
: token.value.substring(numberEnd, reverseSolidusOffset);
|
||||
|
||||
if (type.has(unit.toLowerCase()) === false) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// check range if specified
|
||||
if (outOfRange(opts, token.value, numberEnd)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
};
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Percentage
|
||||
//
|
||||
|
||||
// §5.5. Percentages: the <percentage> type
|
||||
// https://drafts.csswg.org/css-values-4/#percentages
|
||||
function percentage(token, getNextToken, opts) {
|
||||
// ... corresponds to the <percentage-token> production
|
||||
if (token === null || token.type !== types.Percentage) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check range if specified
|
||||
if (outOfRange(opts, token.value, token.value.length - 1)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Numeric
|
||||
//
|
||||
|
||||
// https://drafts.csswg.org/css-values-4/#numbers
|
||||
// The value <zero> represents a literal number with the value 0. Expressions that merely
|
||||
// evaluate to a <number> with the value 0 (for example, calc(0)) do not match <zero>;
|
||||
// only literal <number-token>s do.
|
||||
function zero(next) {
|
||||
if (typeof next !== 'function') {
|
||||
next = function() {
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
return function(token, getNextToken, opts) {
|
||||
if (token !== null && token.type === types.Number) {
|
||||
if (Number(token.value) === 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return next(token, getNextToken, opts);
|
||||
};
|
||||
}
|
||||
|
||||
// § 5.3. Real Numbers: the <number> type
|
||||
// https://drafts.csswg.org/css-values-4/#numbers
|
||||
// Number values are denoted by <number>, and represent real numbers, possibly with a fractional component.
|
||||
// ... It corresponds to the <number-token> production
|
||||
function number(token, getNextToken, opts) {
|
||||
if (token === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const numberEnd = utils.consumeNumber(token.value, 0);
|
||||
const isNumber = numberEnd === token.value.length;
|
||||
if (!isNumber && !isPostfixIeHack(token.value, numberEnd)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check range if specified
|
||||
if (outOfRange(opts, token.value, numberEnd)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// §5.2. Integers: the <integer> type
|
||||
// https://drafts.csswg.org/css-values-4/#integers
|
||||
function integer(token, getNextToken, opts) {
|
||||
// ... corresponds to a subset of the <number-token> production
|
||||
if (token === null || token.type !== types.Number) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The first digit of an integer may be immediately preceded by `-` or `+` to indicate the integer’s sign.
|
||||
let i = charCodeAt(token.value, 0) === 0x002B || // U+002B PLUS SIGN (+)
|
||||
charCodeAt(token.value, 0) === 0x002D ? 1 : 0; // U+002D HYPHEN-MINUS (-)
|
||||
|
||||
// When written literally, an integer is one or more decimal digits 0 through 9 ...
|
||||
for (; i < token.value.length; i++) {
|
||||
if (!charCodeDefinitions.isDigit(charCodeAt(token.value, i))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// check range if specified
|
||||
if (outOfRange(opts, token.value, i)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// token types
|
||||
const tokenTypes = {
|
||||
'ident-token': tokenType(types.Ident),
|
||||
'function-token': tokenType(types.Function),
|
||||
'at-keyword-token': tokenType(types.AtKeyword),
|
||||
'hash-token': tokenType(types.Hash),
|
||||
'string-token': tokenType(types.String),
|
||||
'bad-string-token': tokenType(types.BadString),
|
||||
'url-token': tokenType(types.Url),
|
||||
'bad-url-token': tokenType(types.BadUrl),
|
||||
'delim-token': tokenType(types.Delim),
|
||||
'number-token': tokenType(types.Number),
|
||||
'percentage-token': tokenType(types.Percentage),
|
||||
'dimension-token': tokenType(types.Dimension),
|
||||
'whitespace-token': tokenType(types.WhiteSpace),
|
||||
'CDO-token': tokenType(types.CDO),
|
||||
'CDC-token': tokenType(types.CDC),
|
||||
'colon-token': tokenType(types.Colon),
|
||||
'semicolon-token': tokenType(types.Semicolon),
|
||||
'comma-token': tokenType(types.Comma),
|
||||
'[-token': tokenType(types.LeftSquareBracket),
|
||||
']-token': tokenType(types.RightSquareBracket),
|
||||
'(-token': tokenType(types.LeftParenthesis),
|
||||
')-token': tokenType(types.RightParenthesis),
|
||||
'{-token': tokenType(types.LeftCurlyBracket),
|
||||
'}-token': tokenType(types.RightCurlyBracket)
|
||||
};
|
||||
|
||||
// token production types
|
||||
const productionTypes = {
|
||||
// token type aliases
|
||||
'string': tokenType(types.String),
|
||||
'ident': tokenType(types.Ident),
|
||||
|
||||
// percentage
|
||||
'percentage': calc(percentage),
|
||||
|
||||
// numeric
|
||||
'zero': zero(),
|
||||
'number': calc(number),
|
||||
'integer': calc(integer),
|
||||
|
||||
// complex types
|
||||
'custom-ident': customIdent,
|
||||
'dashed-ident': dashedIdent,
|
||||
'custom-property-name': customPropertyName,
|
||||
'hex-color': hexColor,
|
||||
'id-selector': idSelector, // element( <id-selector> )
|
||||
'an-plus-b': genericAnPlusB,
|
||||
'urange': genericUrange,
|
||||
'declaration-value': declarationValue,
|
||||
'any-value': anyValue
|
||||
};
|
||||
|
||||
// dimensions types depend on units set
|
||||
function createDemensionTypes(units) {
|
||||
const {
|
||||
angle,
|
||||
decibel,
|
||||
frequency,
|
||||
flex,
|
||||
length,
|
||||
resolution,
|
||||
semitones,
|
||||
time
|
||||
} = units || {};
|
||||
|
||||
return {
|
||||
'dimension': calc(dimension(null)),
|
||||
'angle': calc(dimension(angle)),
|
||||
'decibel': calc(dimension(decibel)),
|
||||
'frequency': calc(dimension(frequency)),
|
||||
'flex': calc(dimension(flex)),
|
||||
'length': calc(zero(dimension(length))),
|
||||
'resolution': calc(dimension(resolution)),
|
||||
'semitones': calc(dimension(semitones)),
|
||||
'time': calc(dimension(time))
|
||||
};
|
||||
}
|
||||
|
||||
function createGenericTypes(units) {
|
||||
return {
|
||||
...tokenTypes,
|
||||
...productionTypes,
|
||||
...createDemensionTypes(units)
|
||||
};
|
||||
}
|
||||
|
||||
exports.createDemensionTypes = createDemensionTypes;
|
||||
exports.createGenericTypes = createGenericTypes;
|
||||
exports.productionTypes = productionTypes;
|
||||
exports.tokenTypes = tokenTypes;
|
||||
7
node_modules/css-tree/cjs/lexer/index.cjs
generated
vendored
Normal file
7
node_modules/css-tree/cjs/lexer/index.cjs
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const Lexer = require('./Lexer.cjs');
|
||||
|
||||
|
||||
|
||||
exports.Lexer = Lexer.Lexer;
|
||||
530
node_modules/css-tree/cjs/lexer/match-graph.cjs
generated
vendored
Normal file
530
node_modules/css-tree/cjs/lexer/match-graph.cjs
generated
vendored
Normal file
@@ -0,0 +1,530 @@
|
||||
'use strict';
|
||||
|
||||
const parse = require('../definition-syntax/parse.cjs');
|
||||
|
||||
const MATCH = { type: 'Match' };
|
||||
const MISMATCH = { type: 'Mismatch' };
|
||||
const DISALLOW_EMPTY = { type: 'DisallowEmpty' };
|
||||
|
||||
const LEFTPARENTHESIS = 40; // (
|
||||
const RIGHTPARENTHESIS = 41; // )
|
||||
|
||||
function createCondition(match, thenBranch, elseBranch) {
|
||||
// reduce node count
|
||||
if (thenBranch === MATCH && elseBranch === MISMATCH) {
|
||||
return match;
|
||||
}
|
||||
|
||||
if (match === MATCH && thenBranch === MATCH && elseBranch === MATCH) {
|
||||
return match;
|
||||
}
|
||||
|
||||
if (match.type === 'If' && match.else === MISMATCH && thenBranch === MATCH) {
|
||||
thenBranch = match.then;
|
||||
match = match.match;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'If',
|
||||
match,
|
||||
then: thenBranch,
|
||||
else: elseBranch
|
||||
};
|
||||
}
|
||||
|
||||
function isFunctionType(name) {
|
||||
return (
|
||||
name.length > 2 &&
|
||||
name.charCodeAt(name.length - 2) === LEFTPARENTHESIS &&
|
||||
name.charCodeAt(name.length - 1) === RIGHTPARENTHESIS
|
||||
);
|
||||
}
|
||||
|
||||
function isEnumCapatible(term) {
|
||||
return (
|
||||
term.type === 'Keyword' ||
|
||||
term.type === 'AtKeyword' ||
|
||||
term.type === 'Function' ||
|
||||
term.type === 'Type' && isFunctionType(term.name)
|
||||
);
|
||||
}
|
||||
|
||||
function groupNode(terms, combinator = ' ', explicit = false) {
|
||||
return {
|
||||
type: 'Group',
|
||||
terms,
|
||||
combinator,
|
||||
disallowEmpty: false,
|
||||
explicit
|
||||
};
|
||||
}
|
||||
|
||||
function replaceTypeInGraph(node, replacements, visited = new Set()) {
|
||||
if (!visited.has(node)) {
|
||||
visited.add(node);
|
||||
|
||||
switch (node.type) {
|
||||
case 'If':
|
||||
node.match = replaceTypeInGraph(node.match, replacements, visited);
|
||||
node.then = replaceTypeInGraph(node.then, replacements, visited);
|
||||
node.else = replaceTypeInGraph(node.else, replacements, visited);
|
||||
break;
|
||||
|
||||
case 'Type':
|
||||
return replacements[node.name] || node;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function buildGroupMatchGraph(combinator, terms, atLeastOneTermMatched) {
|
||||
switch (combinator) {
|
||||
case ' ': {
|
||||
// Juxtaposing components means that all of them must occur, in the given order.
|
||||
//
|
||||
// a b c
|
||||
// =
|
||||
// match a
|
||||
// then match b
|
||||
// then match c
|
||||
// then MATCH
|
||||
// else MISMATCH
|
||||
// else MISMATCH
|
||||
// else MISMATCH
|
||||
let result = MATCH;
|
||||
|
||||
for (let i = terms.length - 1; i >= 0; i--) {
|
||||
const term = terms[i];
|
||||
|
||||
result = createCondition(
|
||||
term,
|
||||
result,
|
||||
MISMATCH
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
case '|': {
|
||||
// A bar (|) separates two or more alternatives: exactly one of them must occur.
|
||||
//
|
||||
// a | b | c
|
||||
// =
|
||||
// match a
|
||||
// then MATCH
|
||||
// else match b
|
||||
// then MATCH
|
||||
// else match c
|
||||
// then MATCH
|
||||
// else MISMATCH
|
||||
|
||||
let result = MISMATCH;
|
||||
let map = null;
|
||||
|
||||
for (let i = terms.length - 1; i >= 0; i--) {
|
||||
let term = terms[i];
|
||||
|
||||
// reduce sequence of keywords into a Enum
|
||||
if (isEnumCapatible(term)) {
|
||||
if (map === null && i > 0 && isEnumCapatible(terms[i - 1])) {
|
||||
map = Object.create(null);
|
||||
result = createCondition(
|
||||
{
|
||||
type: 'Enum',
|
||||
map
|
||||
},
|
||||
MATCH,
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
if (map !== null) {
|
||||
const key = (isFunctionType(term.name) ? term.name.slice(0, -1) : term.name).toLowerCase();
|
||||
if (key in map === false) {
|
||||
map[key] = term;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
map = null;
|
||||
|
||||
// create a new conditonal node
|
||||
result = createCondition(
|
||||
term,
|
||||
MATCH,
|
||||
result
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
case '&&': {
|
||||
// A double ampersand (&&) separates two or more components,
|
||||
// all of which must occur, in any order.
|
||||
|
||||
// Use MatchOnce for groups with a large number of terms,
|
||||
// since &&-groups produces at least N!-node trees
|
||||
if (terms.length > 5) {
|
||||
return {
|
||||
type: 'MatchOnce',
|
||||
terms,
|
||||
all: true
|
||||
};
|
||||
}
|
||||
|
||||
// Use a combination tree for groups with small number of terms
|
||||
//
|
||||
// a && b && c
|
||||
// =
|
||||
// match a
|
||||
// then [b && c]
|
||||
// else match b
|
||||
// then [a && c]
|
||||
// else match c
|
||||
// then [a && b]
|
||||
// else MISMATCH
|
||||
//
|
||||
// a && b
|
||||
// =
|
||||
// match a
|
||||
// then match b
|
||||
// then MATCH
|
||||
// else MISMATCH
|
||||
// else match b
|
||||
// then match a
|
||||
// then MATCH
|
||||
// else MISMATCH
|
||||
// else MISMATCH
|
||||
let result = MISMATCH;
|
||||
|
||||
for (let i = terms.length - 1; i >= 0; i--) {
|
||||
const term = terms[i];
|
||||
let thenClause;
|
||||
|
||||
if (terms.length > 1) {
|
||||
thenClause = buildGroupMatchGraph(
|
||||
combinator,
|
||||
terms.filter(function(newGroupTerm) {
|
||||
return newGroupTerm !== term;
|
||||
}),
|
||||
false
|
||||
);
|
||||
} else {
|
||||
thenClause = MATCH;
|
||||
}
|
||||
|
||||
result = createCondition(
|
||||
term,
|
||||
thenClause,
|
||||
result
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
case '||': {
|
||||
// A double bar (||) separates two or more options:
|
||||
// one or more of them must occur, in any order.
|
||||
|
||||
// Use MatchOnce for groups with a large number of terms,
|
||||
// since ||-groups produces at least N!-node trees
|
||||
if (terms.length > 5) {
|
||||
return {
|
||||
type: 'MatchOnce',
|
||||
terms,
|
||||
all: false
|
||||
};
|
||||
}
|
||||
|
||||
// Use a combination tree for groups with small number of terms
|
||||
//
|
||||
// a || b || c
|
||||
// =
|
||||
// match a
|
||||
// then [b || c]
|
||||
// else match b
|
||||
// then [a || c]
|
||||
// else match c
|
||||
// then [a || b]
|
||||
// else MISMATCH
|
||||
//
|
||||
// a || b
|
||||
// =
|
||||
// match a
|
||||
// then match b
|
||||
// then MATCH
|
||||
// else MATCH
|
||||
// else match b
|
||||
// then match a
|
||||
// then MATCH
|
||||
// else MATCH
|
||||
// else MISMATCH
|
||||
let result = atLeastOneTermMatched ? MATCH : MISMATCH;
|
||||
|
||||
for (let i = terms.length - 1; i >= 0; i--) {
|
||||
const term = terms[i];
|
||||
let thenClause;
|
||||
|
||||
if (terms.length > 1) {
|
||||
thenClause = buildGroupMatchGraph(
|
||||
combinator,
|
||||
terms.filter(function(newGroupTerm) {
|
||||
return newGroupTerm !== term;
|
||||
}),
|
||||
true
|
||||
);
|
||||
} else {
|
||||
thenClause = MATCH;
|
||||
}
|
||||
|
||||
result = createCondition(
|
||||
term,
|
||||
thenClause,
|
||||
result
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildMultiplierMatchGraph(node) {
|
||||
let result = MATCH;
|
||||
let matchTerm = buildMatchGraphInternal(node.term);
|
||||
|
||||
if (node.max === 0) {
|
||||
// disable repeating of empty match to prevent infinite loop
|
||||
matchTerm = createCondition(
|
||||
matchTerm,
|
||||
DISALLOW_EMPTY,
|
||||
MISMATCH
|
||||
);
|
||||
|
||||
// an occurrence count is not limited, make a cycle;
|
||||
// to collect more terms on each following matching mismatch
|
||||
result = createCondition(
|
||||
matchTerm,
|
||||
null, // will be a loop
|
||||
MISMATCH
|
||||
);
|
||||
|
||||
result.then = createCondition(
|
||||
MATCH,
|
||||
MATCH,
|
||||
result // make a loop
|
||||
);
|
||||
|
||||
if (node.comma) {
|
||||
result.then.else = createCondition(
|
||||
{ type: 'Comma', syntax: node },
|
||||
result,
|
||||
MISMATCH
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// create a match node chain for [min .. max] interval with optional matches
|
||||
for (let i = node.min || 1; i <= node.max; i++) {
|
||||
if (node.comma && result !== MATCH) {
|
||||
result = createCondition(
|
||||
{ type: 'Comma', syntax: node },
|
||||
result,
|
||||
MISMATCH
|
||||
);
|
||||
}
|
||||
|
||||
result = createCondition(
|
||||
matchTerm,
|
||||
createCondition(
|
||||
MATCH,
|
||||
MATCH,
|
||||
result
|
||||
),
|
||||
MISMATCH
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (node.min === 0) {
|
||||
// allow zero match
|
||||
result = createCondition(
|
||||
MATCH,
|
||||
MATCH,
|
||||
result
|
||||
);
|
||||
} else {
|
||||
// create a match node chain to collect [0 ... min - 1] required matches
|
||||
for (let i = 0; i < node.min - 1; i++) {
|
||||
if (node.comma && result !== MATCH) {
|
||||
result = createCondition(
|
||||
{ type: 'Comma', syntax: node },
|
||||
result,
|
||||
MISMATCH
|
||||
);
|
||||
}
|
||||
|
||||
result = createCondition(
|
||||
matchTerm,
|
||||
result,
|
||||
MISMATCH
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function buildMatchGraphInternal(node) {
|
||||
if (typeof node === 'function') {
|
||||
return {
|
||||
type: 'Generic',
|
||||
fn: node
|
||||
};
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case 'Group': {
|
||||
let result = buildGroupMatchGraph(
|
||||
node.combinator,
|
||||
node.terms.map(buildMatchGraphInternal),
|
||||
false
|
||||
);
|
||||
|
||||
if (node.disallowEmpty) {
|
||||
result = createCondition(
|
||||
result,
|
||||
DISALLOW_EMPTY,
|
||||
MISMATCH
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case 'Multiplier':
|
||||
return buildMultiplierMatchGraph(node);
|
||||
|
||||
// https://drafts.csswg.org/css-values-5/#boolean
|
||||
case 'Boolean': {
|
||||
const term = buildMatchGraphInternal(node.term);
|
||||
// <boolean-expr[ <test> ]> = not <boolean-expr-group> | <boolean-expr-group> [ [ and <boolean-expr-group> ]* | [ or <boolean-expr-group> ]* ]
|
||||
const matchNode = buildMatchGraphInternal(groupNode([
|
||||
groupNode([
|
||||
{ type: 'Keyword', name: 'not' },
|
||||
{ type: 'Type', name: '!boolean-group' }
|
||||
]),
|
||||
groupNode([
|
||||
{ type: 'Type', name: '!boolean-group' },
|
||||
groupNode([
|
||||
{ type: 'Multiplier', comma: false, min: 0, max: 0, term: groupNode([
|
||||
{ type: 'Keyword', name: 'and' },
|
||||
{ type: 'Type', name: '!boolean-group' }
|
||||
]) },
|
||||
{ type: 'Multiplier', comma: false, min: 0, max: 0, term: groupNode([
|
||||
{ type: 'Keyword', name: 'or' },
|
||||
{ type: 'Type', name: '!boolean-group' }
|
||||
]) }
|
||||
], '|')
|
||||
])
|
||||
], '|'));
|
||||
// <boolean-expr-group> = <test> | ( <boolean-expr[ <test> ]> ) | <general-enclosed>
|
||||
const booleanGroup = buildMatchGraphInternal(
|
||||
groupNode([
|
||||
{ type: 'Type', name: '!term' },
|
||||
groupNode([
|
||||
{ type: 'Token', value: '(' },
|
||||
{ type: 'Type', name: '!self' },
|
||||
{ type: 'Token', value: ')' }
|
||||
]),
|
||||
{ type: 'Type', name: 'general-enclosed' }
|
||||
], '|')
|
||||
);
|
||||
|
||||
replaceTypeInGraph(booleanGroup, { '!term': term, '!self': matchNode });
|
||||
replaceTypeInGraph(matchNode, { '!boolean-group': booleanGroup });
|
||||
|
||||
return matchNode;
|
||||
}
|
||||
|
||||
case 'Type':
|
||||
case 'Property':
|
||||
return {
|
||||
type: node.type,
|
||||
name: node.name,
|
||||
syntax: node
|
||||
};
|
||||
|
||||
case 'Keyword':
|
||||
return {
|
||||
type: node.type,
|
||||
name: node.name.toLowerCase(),
|
||||
syntax: node
|
||||
};
|
||||
|
||||
case 'AtKeyword':
|
||||
return {
|
||||
type: node.type,
|
||||
name: '@' + node.name.toLowerCase(),
|
||||
syntax: node
|
||||
};
|
||||
|
||||
case 'Function':
|
||||
return {
|
||||
type: node.type,
|
||||
name: node.name.toLowerCase() + '(',
|
||||
syntax: node
|
||||
};
|
||||
|
||||
case 'String':
|
||||
// convert a one char length String to a Token
|
||||
if (node.value.length === 3) {
|
||||
return {
|
||||
type: 'Token',
|
||||
value: node.value.charAt(1),
|
||||
syntax: node
|
||||
};
|
||||
}
|
||||
|
||||
// otherwise use it as is
|
||||
return {
|
||||
type: node.type,
|
||||
value: node.value.substr(1, node.value.length - 2).replace(/\\'/g, '\''),
|
||||
syntax: node
|
||||
};
|
||||
|
||||
case 'Token':
|
||||
return {
|
||||
type: node.type,
|
||||
value: node.value,
|
||||
syntax: node
|
||||
};
|
||||
|
||||
case 'Comma':
|
||||
return {
|
||||
type: node.type,
|
||||
syntax: node
|
||||
};
|
||||
|
||||
default:
|
||||
throw new Error('Unknown node type:', node.type);
|
||||
}
|
||||
}
|
||||
|
||||
function buildMatchGraph(syntaxTree, ref) {
|
||||
if (typeof syntaxTree === 'string') {
|
||||
syntaxTree = parse.parse(syntaxTree);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'MatchGraph',
|
||||
match: buildMatchGraphInternal(syntaxTree),
|
||||
syntax: ref || null,
|
||||
source: syntaxTree
|
||||
};
|
||||
}
|
||||
|
||||
exports.DISALLOW_EMPTY = DISALLOW_EMPTY;
|
||||
exports.MATCH = MATCH;
|
||||
exports.MISMATCH = MISMATCH;
|
||||
exports.buildMatchGraph = buildMatchGraph;
|
||||
632
node_modules/css-tree/cjs/lexer/match.cjs
generated
vendored
Normal file
632
node_modules/css-tree/cjs/lexer/match.cjs
generated
vendored
Normal file
@@ -0,0 +1,632 @@
|
||||
'use strict';
|
||||
|
||||
const matchGraph = require('./match-graph.cjs');
|
||||
const types = require('../tokenizer/types.cjs');
|
||||
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
const STUB = 0;
|
||||
const TOKEN = 1;
|
||||
const OPEN_SYNTAX = 2;
|
||||
const CLOSE_SYNTAX = 3;
|
||||
|
||||
const EXIT_REASON_MATCH = 'Match';
|
||||
const EXIT_REASON_MISMATCH = 'Mismatch';
|
||||
const EXIT_REASON_ITERATION_LIMIT = 'Maximum iteration number exceeded (please fill an issue on https://github.com/csstree/csstree/issues)';
|
||||
|
||||
const ITERATION_LIMIT = 15000;
|
||||
|
||||
function reverseList(list) {
|
||||
let prev = null;
|
||||
let next = null;
|
||||
let item = list;
|
||||
|
||||
while (item !== null) {
|
||||
next = item.prev;
|
||||
item.prev = prev;
|
||||
prev = item;
|
||||
item = next;
|
||||
}
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
function areStringsEqualCaseInsensitive(testStr, referenceStr) {
|
||||
if (testStr.length !== referenceStr.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < testStr.length; i++) {
|
||||
const referenceCode = referenceStr.charCodeAt(i);
|
||||
let testCode = testStr.charCodeAt(i);
|
||||
|
||||
// testCode.toLowerCase() for U+0041 LATIN CAPITAL LETTER A (A) .. U+005A LATIN CAPITAL LETTER Z (Z).
|
||||
if (testCode >= 0x0041 && testCode <= 0x005A) {
|
||||
testCode = testCode | 32;
|
||||
}
|
||||
|
||||
if (testCode !== referenceCode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isContextEdgeDelim(token) {
|
||||
if (token.type !== types.Delim) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fix matching for unicode-range: U+30??, U+FF00-FF9F
|
||||
// Probably we need to check out previous match instead
|
||||
return token.value !== '?';
|
||||
}
|
||||
|
||||
function isCommaContextStart(token) {
|
||||
if (token === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
token.type === types.Comma ||
|
||||
token.type === types.Function ||
|
||||
token.type === types.LeftParenthesis ||
|
||||
token.type === types.LeftSquareBracket ||
|
||||
token.type === types.LeftCurlyBracket ||
|
||||
isContextEdgeDelim(token)
|
||||
);
|
||||
}
|
||||
|
||||
function isCommaContextEnd(token) {
|
||||
if (token === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
token.type === types.RightParenthesis ||
|
||||
token.type === types.RightSquareBracket ||
|
||||
token.type === types.RightCurlyBracket ||
|
||||
(token.type === types.Delim && token.value === '/')
|
||||
);
|
||||
}
|
||||
|
||||
function internalMatch(tokens, state, syntaxes) {
|
||||
function moveToNextToken() {
|
||||
do {
|
||||
tokenIndex++;
|
||||
token = tokenIndex < tokens.length ? tokens[tokenIndex] : null;
|
||||
} while (token !== null && (token.type === types.WhiteSpace || token.type === types.Comment));
|
||||
}
|
||||
|
||||
function getNextToken(offset) {
|
||||
const nextIndex = tokenIndex + offset;
|
||||
|
||||
return nextIndex < tokens.length ? tokens[nextIndex] : null;
|
||||
}
|
||||
|
||||
function stateSnapshotFromSyntax(nextState, prev) {
|
||||
return {
|
||||
nextState,
|
||||
matchStack,
|
||||
syntaxStack,
|
||||
thenStack,
|
||||
tokenIndex,
|
||||
prev
|
||||
};
|
||||
}
|
||||
|
||||
function pushThenStack(nextState) {
|
||||
thenStack = {
|
||||
nextState,
|
||||
matchStack,
|
||||
syntaxStack,
|
||||
prev: thenStack
|
||||
};
|
||||
}
|
||||
|
||||
function pushElseStack(nextState) {
|
||||
elseStack = stateSnapshotFromSyntax(nextState, elseStack);
|
||||
}
|
||||
|
||||
function addTokenToMatch() {
|
||||
matchStack = {
|
||||
type: TOKEN,
|
||||
syntax: state.syntax,
|
||||
token,
|
||||
prev: matchStack
|
||||
};
|
||||
|
||||
moveToNextToken();
|
||||
syntaxStash = null;
|
||||
|
||||
if (tokenIndex > longestMatch) {
|
||||
longestMatch = tokenIndex;
|
||||
}
|
||||
}
|
||||
|
||||
function openSyntax() {
|
||||
syntaxStack = {
|
||||
syntax: state.syntax,
|
||||
opts: state.syntax.opts || (syntaxStack !== null && syntaxStack.opts) || null,
|
||||
prev: syntaxStack
|
||||
};
|
||||
|
||||
matchStack = {
|
||||
type: OPEN_SYNTAX,
|
||||
syntax: state.syntax,
|
||||
token: matchStack.token,
|
||||
prev: matchStack
|
||||
};
|
||||
}
|
||||
|
||||
function closeSyntax() {
|
||||
if (matchStack.type === OPEN_SYNTAX) {
|
||||
matchStack = matchStack.prev;
|
||||
} else {
|
||||
matchStack = {
|
||||
type: CLOSE_SYNTAX,
|
||||
syntax: syntaxStack.syntax,
|
||||
token: matchStack.token,
|
||||
prev: matchStack
|
||||
};
|
||||
}
|
||||
|
||||
syntaxStack = syntaxStack.prev;
|
||||
}
|
||||
|
||||
let syntaxStack = null;
|
||||
let thenStack = null;
|
||||
let elseStack = null;
|
||||
|
||||
// null – stashing allowed, nothing stashed
|
||||
// false – stashing disabled, nothing stashed
|
||||
// anithing else – fail stashable syntaxes, some syntax stashed
|
||||
let syntaxStash = null;
|
||||
|
||||
let iterationCount = 0; // count iterations and prevent infinite loop
|
||||
let exitReason = null;
|
||||
|
||||
let token = null;
|
||||
let tokenIndex = -1;
|
||||
let longestMatch = 0;
|
||||
let matchStack = {
|
||||
type: STUB,
|
||||
syntax: null,
|
||||
token: null,
|
||||
prev: null
|
||||
};
|
||||
|
||||
moveToNextToken();
|
||||
|
||||
while (exitReason === null && ++iterationCount < ITERATION_LIMIT) {
|
||||
// function mapList(list, fn) {
|
||||
// const result = [];
|
||||
// while (list) {
|
||||
// result.unshift(fn(list));
|
||||
// list = list.prev;
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
// console.log('--\n',
|
||||
// '#' + iterationCount,
|
||||
// require('util').inspect({
|
||||
// match: mapList(matchStack, x => x.type === TOKEN ? x.token && x.token.value : x.syntax ? ({ [OPEN_SYNTAX]: '<', [CLOSE_SYNTAX]: '</' }[x.type] || x.type) + '!' + x.syntax.name : null),
|
||||
// token: token && token.value,
|
||||
// tokenIndex,
|
||||
// syntax: syntax.type + (syntax.id ? ' #' + syntax.id : '')
|
||||
// }, { depth: null })
|
||||
// );
|
||||
switch (state.type) {
|
||||
case 'Match':
|
||||
if (thenStack === null) {
|
||||
// turn to MISMATCH when some tokens left unmatched
|
||||
if (token !== null) {
|
||||
// doesn't mismatch if just one token left and it's an IE hack
|
||||
if (tokenIndex !== tokens.length - 1 || (token.value !== '\\0' && token.value !== '\\9')) {
|
||||
state = matchGraph.MISMATCH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// break the main loop, return a result - MATCH
|
||||
exitReason = EXIT_REASON_MATCH;
|
||||
break;
|
||||
}
|
||||
|
||||
// go to next syntax (`then` branch)
|
||||
state = thenStack.nextState;
|
||||
|
||||
// check match is not empty
|
||||
if (state === matchGraph.DISALLOW_EMPTY) {
|
||||
if (thenStack.matchStack === matchStack) {
|
||||
state = matchGraph.MISMATCH;
|
||||
break;
|
||||
} else {
|
||||
state = matchGraph.MATCH;
|
||||
}
|
||||
}
|
||||
|
||||
// close syntax if needed
|
||||
while (thenStack.syntaxStack !== syntaxStack) {
|
||||
closeSyntax();
|
||||
}
|
||||
|
||||
// pop stack
|
||||
thenStack = thenStack.prev;
|
||||
break;
|
||||
|
||||
case 'Mismatch':
|
||||
// when some syntax is stashed
|
||||
if (syntaxStash !== null && syntaxStash !== false) {
|
||||
// there is no else branches or a branch reduce match stack
|
||||
if (elseStack === null || tokenIndex > elseStack.tokenIndex) {
|
||||
// restore state from the stash
|
||||
elseStack = syntaxStash;
|
||||
syntaxStash = false; // disable stashing
|
||||
}
|
||||
} else if (elseStack === null) {
|
||||
// no else branches -> break the main loop
|
||||
// return a result - MISMATCH
|
||||
exitReason = EXIT_REASON_MISMATCH;
|
||||
break;
|
||||
}
|
||||
|
||||
// go to next syntax (`else` branch)
|
||||
state = elseStack.nextState;
|
||||
|
||||
// restore all the rest stack states
|
||||
thenStack = elseStack.thenStack;
|
||||
syntaxStack = elseStack.syntaxStack;
|
||||
matchStack = elseStack.matchStack;
|
||||
tokenIndex = elseStack.tokenIndex;
|
||||
token = tokenIndex < tokens.length ? tokens[tokenIndex] : null;
|
||||
|
||||
// pop stack
|
||||
elseStack = elseStack.prev;
|
||||
break;
|
||||
|
||||
case 'MatchGraph':
|
||||
state = state.match;
|
||||
break;
|
||||
|
||||
case 'If':
|
||||
// IMPORTANT: else stack push must go first,
|
||||
// since it stores the state of thenStack before changes
|
||||
if (state.else !== matchGraph.MISMATCH) {
|
||||
pushElseStack(state.else);
|
||||
}
|
||||
|
||||
if (state.then !== matchGraph.MATCH) {
|
||||
pushThenStack(state.then);
|
||||
}
|
||||
|
||||
state = state.match;
|
||||
break;
|
||||
|
||||
case 'MatchOnce':
|
||||
state = {
|
||||
type: 'MatchOnceBuffer',
|
||||
syntax: state,
|
||||
index: 0,
|
||||
mask: 0
|
||||
};
|
||||
break;
|
||||
|
||||
case 'MatchOnceBuffer': {
|
||||
const terms = state.syntax.terms;
|
||||
|
||||
if (state.index === terms.length) {
|
||||
// no matches at all or it's required all terms to be matched
|
||||
if (state.mask === 0 || state.syntax.all) {
|
||||
state = matchGraph.MISMATCH;
|
||||
break;
|
||||
}
|
||||
|
||||
// a partial match is ok
|
||||
state = matchGraph.MATCH;
|
||||
break;
|
||||
}
|
||||
|
||||
// all terms are matched
|
||||
if (state.mask === (1 << terms.length) - 1) {
|
||||
state = matchGraph.MATCH;
|
||||
break;
|
||||
}
|
||||
|
||||
for (; state.index < terms.length; state.index++) {
|
||||
const matchFlag = 1 << state.index;
|
||||
|
||||
if ((state.mask & matchFlag) === 0) {
|
||||
// IMPORTANT: else stack push must go first,
|
||||
// since it stores the state of thenStack before changes
|
||||
pushElseStack(state);
|
||||
pushThenStack({
|
||||
type: 'AddMatchOnce',
|
||||
syntax: state.syntax,
|
||||
mask: state.mask | matchFlag
|
||||
});
|
||||
|
||||
// match
|
||||
state = terms[state.index++];
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'AddMatchOnce':
|
||||
state = {
|
||||
type: 'MatchOnceBuffer',
|
||||
syntax: state.syntax,
|
||||
index: 0,
|
||||
mask: state.mask
|
||||
};
|
||||
break;
|
||||
|
||||
case 'Enum':
|
||||
if (token !== null) {
|
||||
let name = token.value.toLowerCase();
|
||||
|
||||
// drop \0 and \9 hack from keyword name
|
||||
if (name.indexOf('\\') !== -1) {
|
||||
name = name.replace(/\\[09].*$/, '');
|
||||
}
|
||||
|
||||
if (hasOwnProperty.call(state.map, name)) {
|
||||
state = state.map[name];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state = matchGraph.MISMATCH;
|
||||
break;
|
||||
|
||||
case 'Generic': {
|
||||
const opts = syntaxStack !== null ? syntaxStack.opts : null;
|
||||
const lastTokenIndex = tokenIndex + Math.floor(state.fn(token, getNextToken, opts));
|
||||
|
||||
if (!isNaN(lastTokenIndex) && lastTokenIndex > tokenIndex) {
|
||||
while (tokenIndex < lastTokenIndex) {
|
||||
addTokenToMatch();
|
||||
}
|
||||
|
||||
state = matchGraph.MATCH;
|
||||
} else {
|
||||
state = matchGraph.MISMATCH;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Type':
|
||||
case 'Property': {
|
||||
const syntaxDict = state.type === 'Type' ? 'types' : 'properties';
|
||||
const dictSyntax = hasOwnProperty.call(syntaxes, syntaxDict) ? syntaxes[syntaxDict][state.name] : null;
|
||||
|
||||
if (!dictSyntax || !dictSyntax.match) {
|
||||
throw new Error(
|
||||
'Bad syntax reference: ' +
|
||||
(state.type === 'Type'
|
||||
? '<' + state.name + '>'
|
||||
: '<\'' + state.name + '\'>')
|
||||
);
|
||||
}
|
||||
|
||||
// stash a syntax for types with low priority
|
||||
if (syntaxStash !== false && token !== null && state.type === 'Type') {
|
||||
const lowPriorityMatching =
|
||||
// https://drafts.csswg.org/css-values-4/#custom-idents
|
||||
// When parsing positionally-ambiguous keywords in a property value, a <custom-ident> production
|
||||
// can only claim the keyword if no other unfulfilled production can claim it.
|
||||
(state.name === 'custom-ident' && token.type === types.Ident) ||
|
||||
|
||||
// https://drafts.csswg.org/css-values-4/#lengths
|
||||
// ... if a `0` could be parsed as either a <number> or a <length> in a property (such as line-height),
|
||||
// it must parse as a <number>
|
||||
(state.name === 'length' && token.value === '0');
|
||||
|
||||
if (lowPriorityMatching) {
|
||||
if (syntaxStash === null) {
|
||||
syntaxStash = stateSnapshotFromSyntax(state, elseStack);
|
||||
}
|
||||
|
||||
state = matchGraph.MISMATCH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
openSyntax();
|
||||
state = dictSyntax.matchRef || dictSyntax.match;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Keyword': {
|
||||
const name = state.name;
|
||||
|
||||
if (token !== null) {
|
||||
let keywordName = token.value;
|
||||
|
||||
// drop \0 and \9 hack from keyword name
|
||||
if (keywordName.indexOf('\\') !== -1) {
|
||||
keywordName = keywordName.replace(/\\[09].*$/, '');
|
||||
}
|
||||
|
||||
if (areStringsEqualCaseInsensitive(keywordName, name)) {
|
||||
addTokenToMatch();
|
||||
state = matchGraph.MATCH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state = matchGraph.MISMATCH;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'AtKeyword':
|
||||
case 'Function':
|
||||
if (token !== null && areStringsEqualCaseInsensitive(token.value, state.name)) {
|
||||
addTokenToMatch();
|
||||
state = matchGraph.MATCH;
|
||||
break;
|
||||
}
|
||||
|
||||
state = matchGraph.MISMATCH;
|
||||
break;
|
||||
|
||||
case 'Token':
|
||||
if (token !== null && token.value === state.value) {
|
||||
addTokenToMatch();
|
||||
state = matchGraph.MATCH;
|
||||
break;
|
||||
}
|
||||
|
||||
state = matchGraph.MISMATCH;
|
||||
break;
|
||||
|
||||
case 'Comma':
|
||||
if (token !== null && token.type === types.Comma) {
|
||||
if (isCommaContextStart(matchStack.token)) {
|
||||
state = matchGraph.MISMATCH;
|
||||
} else {
|
||||
addTokenToMatch();
|
||||
state = isCommaContextEnd(token) ? matchGraph.MISMATCH : matchGraph.MATCH;
|
||||
}
|
||||
} else {
|
||||
state = isCommaContextStart(matchStack.token) || isCommaContextEnd(token) ? matchGraph.MATCH : matchGraph.MISMATCH;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'String':
|
||||
let string = '';
|
||||
let lastTokenIndex = tokenIndex;
|
||||
|
||||
for (; lastTokenIndex < tokens.length && string.length < state.value.length; lastTokenIndex++) {
|
||||
string += tokens[lastTokenIndex].value;
|
||||
}
|
||||
|
||||
if (areStringsEqualCaseInsensitive(string, state.value)) {
|
||||
while (tokenIndex < lastTokenIndex) {
|
||||
addTokenToMatch();
|
||||
}
|
||||
|
||||
state = matchGraph.MATCH;
|
||||
} else {
|
||||
state = matchGraph.MISMATCH;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error('Unknown node type: ' + state.type);
|
||||
}
|
||||
}
|
||||
|
||||
switch (exitReason) {
|
||||
case null:
|
||||
console.warn('[csstree-match] BREAK after ' + ITERATION_LIMIT + ' iterations');
|
||||
exitReason = EXIT_REASON_ITERATION_LIMIT;
|
||||
matchStack = null;
|
||||
break;
|
||||
|
||||
case EXIT_REASON_MATCH:
|
||||
while (syntaxStack !== null) {
|
||||
closeSyntax();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
matchStack = null;
|
||||
}
|
||||
|
||||
return {
|
||||
tokens,
|
||||
reason: exitReason,
|
||||
iterations: iterationCount,
|
||||
match: matchStack,
|
||||
longestMatch
|
||||
};
|
||||
}
|
||||
|
||||
function matchAsList(tokens, matchGraph, syntaxes) {
|
||||
const matchResult = internalMatch(tokens, matchGraph, syntaxes || {});
|
||||
|
||||
if (matchResult.match !== null) {
|
||||
let item = reverseList(matchResult.match).prev;
|
||||
|
||||
matchResult.match = [];
|
||||
|
||||
while (item !== null) {
|
||||
switch (item.type) {
|
||||
case OPEN_SYNTAX:
|
||||
case CLOSE_SYNTAX:
|
||||
matchResult.match.push({
|
||||
type: item.type,
|
||||
syntax: item.syntax
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
matchResult.match.push({
|
||||
token: item.token.value,
|
||||
node: item.token.node
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
item = item.prev;
|
||||
}
|
||||
}
|
||||
|
||||
return matchResult;
|
||||
}
|
||||
|
||||
function matchAsTree(tokens, matchGraph, syntaxes) {
|
||||
const matchResult = internalMatch(tokens, matchGraph, syntaxes || {});
|
||||
|
||||
if (matchResult.match === null) {
|
||||
return matchResult;
|
||||
}
|
||||
|
||||
let item = matchResult.match;
|
||||
let host = matchResult.match = {
|
||||
syntax: matchGraph.syntax || null,
|
||||
match: []
|
||||
};
|
||||
const hostStack = [host];
|
||||
|
||||
// revert a list and start with 2nd item since 1st is a stub item
|
||||
item = reverseList(item).prev;
|
||||
|
||||
// build a tree
|
||||
while (item !== null) {
|
||||
switch (item.type) {
|
||||
case OPEN_SYNTAX:
|
||||
host.match.push(host = {
|
||||
syntax: item.syntax,
|
||||
match: []
|
||||
});
|
||||
hostStack.push(host);
|
||||
break;
|
||||
|
||||
case CLOSE_SYNTAX:
|
||||
hostStack.pop();
|
||||
host = hostStack[hostStack.length - 1];
|
||||
break;
|
||||
|
||||
default:
|
||||
host.match.push({
|
||||
syntax: item.syntax || null,
|
||||
token: item.token.value,
|
||||
node: item.token.node
|
||||
});
|
||||
}
|
||||
|
||||
item = item.prev;
|
||||
}
|
||||
|
||||
return matchResult;
|
||||
}
|
||||
|
||||
exports.matchAsList = matchAsList;
|
||||
exports.matchAsTree = matchAsTree;
|
||||
54
node_modules/css-tree/cjs/lexer/prepare-tokens.cjs
generated
vendored
Normal file
54
node_modules/css-tree/cjs/lexer/prepare-tokens.cjs
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
const index = require('../tokenizer/index.cjs');
|
||||
|
||||
const astToTokens = {
|
||||
decorator(handlers) {
|
||||
const tokens = [];
|
||||
let curNode = null;
|
||||
|
||||
return {
|
||||
...handlers,
|
||||
node(node) {
|
||||
const tmp = curNode;
|
||||
curNode = node;
|
||||
handlers.node.call(this, node);
|
||||
curNode = tmp;
|
||||
},
|
||||
emit(value, type, auto) {
|
||||
tokens.push({
|
||||
type,
|
||||
value,
|
||||
node: auto ? null : curNode
|
||||
});
|
||||
},
|
||||
result() {
|
||||
return tokens;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function stringToTokens(str) {
|
||||
const tokens = [];
|
||||
|
||||
index.tokenize(str, (type, start, end) =>
|
||||
tokens.push({
|
||||
type,
|
||||
value: str.slice(start, end),
|
||||
node: null
|
||||
})
|
||||
);
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
function prepareTokens(value, syntax) {
|
||||
if (typeof value === 'string') {
|
||||
return stringToTokens(value);
|
||||
}
|
||||
|
||||
return syntax.generate(value, astToTokens);
|
||||
}
|
||||
|
||||
module.exports = prepareTokens;
|
||||
65
node_modules/css-tree/cjs/lexer/search.cjs
generated
vendored
Normal file
65
node_modules/css-tree/cjs/lexer/search.cjs
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
'use strict';
|
||||
|
||||
const List = require('../utils/List.cjs');
|
||||
|
||||
function getFirstMatchNode(matchNode) {
|
||||
if ('node' in matchNode) {
|
||||
return matchNode.node;
|
||||
}
|
||||
|
||||
return getFirstMatchNode(matchNode.match[0]);
|
||||
}
|
||||
|
||||
function getLastMatchNode(matchNode) {
|
||||
if ('node' in matchNode) {
|
||||
return matchNode.node;
|
||||
}
|
||||
|
||||
return getLastMatchNode(matchNode.match[matchNode.match.length - 1]);
|
||||
}
|
||||
|
||||
function matchFragments(lexer, ast, match, type, name) {
|
||||
function findFragments(matchNode) {
|
||||
if (matchNode.syntax !== null &&
|
||||
matchNode.syntax.type === type &&
|
||||
matchNode.syntax.name === name) {
|
||||
const start = getFirstMatchNode(matchNode);
|
||||
const end = getLastMatchNode(matchNode);
|
||||
|
||||
lexer.syntax.walk(ast, function(node, item, list) {
|
||||
if (node === start) {
|
||||
const nodes = new List.List();
|
||||
|
||||
do {
|
||||
nodes.appendData(item.data);
|
||||
|
||||
if (item.data === end) {
|
||||
break;
|
||||
}
|
||||
|
||||
item = item.next;
|
||||
} while (item !== null);
|
||||
|
||||
fragments.push({
|
||||
parent: list,
|
||||
nodes
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(matchNode.match)) {
|
||||
matchNode.match.forEach(findFragments);
|
||||
}
|
||||
}
|
||||
|
||||
const fragments = [];
|
||||
|
||||
if (match.matched !== null) {
|
||||
findFragments(match.matched);
|
||||
}
|
||||
|
||||
return fragments;
|
||||
}
|
||||
|
||||
exports.matchFragments = matchFragments;
|
||||
173
node_modules/css-tree/cjs/lexer/structure.cjs
generated
vendored
Normal file
173
node_modules/css-tree/cjs/lexer/structure.cjs
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
'use strict';
|
||||
|
||||
const List = require('../utils/List.cjs');
|
||||
|
||||
const { hasOwnProperty } = Object.prototype;
|
||||
|
||||
function isValidNumber(value) {
|
||||
// Number.isInteger(value) && value >= 0
|
||||
return (
|
||||
typeof value === 'number' &&
|
||||
isFinite(value) &&
|
||||
Math.floor(value) === value &&
|
||||
value >= 0
|
||||
);
|
||||
}
|
||||
|
||||
function isValidLocation(loc) {
|
||||
return (
|
||||
Boolean(loc) &&
|
||||
isValidNumber(loc.offset) &&
|
||||
isValidNumber(loc.line) &&
|
||||
isValidNumber(loc.column)
|
||||
);
|
||||
}
|
||||
|
||||
function createNodeStructureChecker(type, fields) {
|
||||
return function checkNode(node, warn) {
|
||||
if (!node || node.constructor !== Object) {
|
||||
return warn(node, 'Type of node should be an Object');
|
||||
}
|
||||
|
||||
for (let key in node) {
|
||||
let valid = true;
|
||||
|
||||
if (hasOwnProperty.call(node, key) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key === 'type') {
|
||||
if (node.type !== type) {
|
||||
warn(node, 'Wrong node type `' + node.type + '`, expected `' + type + '`');
|
||||
}
|
||||
} else if (key === 'loc') {
|
||||
if (node.loc === null) {
|
||||
continue;
|
||||
} else if (node.loc && node.loc.constructor === Object) {
|
||||
if (typeof node.loc.source !== 'string') {
|
||||
key += '.source';
|
||||
} else if (!isValidLocation(node.loc.start)) {
|
||||
key += '.start';
|
||||
} else if (!isValidLocation(node.loc.end)) {
|
||||
key += '.end';
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
valid = false;
|
||||
} else if (fields.hasOwnProperty(key)) {
|
||||
valid = false;
|
||||
|
||||
for (let i = 0; !valid && i < fields[key].length; i++) {
|
||||
const fieldType = fields[key][i];
|
||||
|
||||
switch (fieldType) {
|
||||
case String:
|
||||
valid = typeof node[key] === 'string';
|
||||
break;
|
||||
|
||||
case Boolean:
|
||||
valid = typeof node[key] === 'boolean';
|
||||
break;
|
||||
|
||||
case null:
|
||||
valid = node[key] === null;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (typeof fieldType === 'string') {
|
||||
valid = node[key] && node[key].type === fieldType;
|
||||
} else if (Array.isArray(fieldType)) {
|
||||
valid = node[key] instanceof List.List;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn(node, 'Unknown field `' + key + '` for ' + type + ' node type');
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
warn(node, 'Bad value for `' + type + '.' + key + '`');
|
||||
}
|
||||
}
|
||||
|
||||
for (const key in fields) {
|
||||
if (hasOwnProperty.call(fields, key) &&
|
||||
hasOwnProperty.call(node, key) === false) {
|
||||
warn(node, 'Field `' + type + '.' + key + '` is missed');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function genTypesList(fieldTypes, path) {
|
||||
const docsTypes = [];
|
||||
|
||||
for (let i = 0; i < fieldTypes.length; i++) {
|
||||
const fieldType = fieldTypes[i];
|
||||
if (fieldType === String || fieldType === Boolean) {
|
||||
docsTypes.push(fieldType.name.toLowerCase());
|
||||
} else if (fieldType === null) {
|
||||
docsTypes.push('null');
|
||||
} else if (typeof fieldType === 'string') {
|
||||
docsTypes.push(fieldType);
|
||||
} else if (Array.isArray(fieldType)) {
|
||||
docsTypes.push('List<' + (genTypesList(fieldType, path) || 'any') + '>'); // TODO: use type enum
|
||||
} else {
|
||||
throw new Error('Wrong value `' + fieldType + '` in `' + path + '` structure definition');
|
||||
}
|
||||
}
|
||||
|
||||
return docsTypes.join(' | ');
|
||||
}
|
||||
|
||||
function processStructure(name, nodeType) {
|
||||
const structure = nodeType.structure;
|
||||
const fields = {
|
||||
type: String,
|
||||
loc: true
|
||||
};
|
||||
const docs = {
|
||||
type: '"' + name + '"'
|
||||
};
|
||||
|
||||
for (const key in structure) {
|
||||
if (hasOwnProperty.call(structure, key) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fieldTypes = fields[key] = Array.isArray(structure[key])
|
||||
? structure[key].slice()
|
||||
: [structure[key]];
|
||||
|
||||
docs[key] = genTypesList(fieldTypes, name + '.' + key);
|
||||
}
|
||||
|
||||
return {
|
||||
docs,
|
||||
check: createNodeStructureChecker(name, fields)
|
||||
};
|
||||
}
|
||||
|
||||
function getStructureFromConfig(config) {
|
||||
const structure = {};
|
||||
|
||||
if (config.node) {
|
||||
for (const name in config.node) {
|
||||
if (hasOwnProperty.call(config.node, name)) {
|
||||
const nodeType = config.node[name];
|
||||
|
||||
if (nodeType.structure) {
|
||||
structure[name] = processStructure(name, nodeType);
|
||||
} else {
|
||||
throw new Error('Missed `structure` field in `' + name + '` node type definition');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return structure;
|
||||
}
|
||||
|
||||
exports.getStructureFromConfig = getStructureFromConfig;
|
||||
73
node_modules/css-tree/cjs/lexer/trace.cjs
generated
vendored
Normal file
73
node_modules/css-tree/cjs/lexer/trace.cjs
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
'use strict';
|
||||
|
||||
function getTrace(node) {
|
||||
function shouldPutToTrace(syntax) {
|
||||
if (syntax === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
syntax.type === 'Type' ||
|
||||
syntax.type === 'Property' ||
|
||||
syntax.type === 'Keyword'
|
||||
);
|
||||
}
|
||||
|
||||
function hasMatch(matchNode) {
|
||||
if (Array.isArray(matchNode.match)) {
|
||||
// use for-loop for better perfomance
|
||||
for (let i = 0; i < matchNode.match.length; i++) {
|
||||
if (hasMatch(matchNode.match[i])) {
|
||||
if (shouldPutToTrace(matchNode.syntax)) {
|
||||
result.unshift(matchNode.syntax);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (matchNode.node === node) {
|
||||
result = shouldPutToTrace(matchNode.syntax)
|
||||
? [matchNode.syntax]
|
||||
: [];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
let result = null;
|
||||
|
||||
if (this.matched !== null) {
|
||||
hasMatch(this.matched);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function isType(node, type) {
|
||||
return testNode(this, node, match => match.type === 'Type' && match.name === type);
|
||||
}
|
||||
|
||||
function isProperty(node, property) {
|
||||
return testNode(this, node, match => match.type === 'Property' && match.name === property);
|
||||
}
|
||||
|
||||
function isKeyword(node) {
|
||||
return testNode(this, node, match => match.type === 'Keyword');
|
||||
}
|
||||
|
||||
function testNode(match, node, fn) {
|
||||
const trace = getTrace.call(match, node);
|
||||
|
||||
if (trace === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return trace.some(fn);
|
||||
}
|
||||
|
||||
exports.getTrace = getTrace;
|
||||
exports.isKeyword = isKeyword;
|
||||
exports.isProperty = isProperty;
|
||||
exports.isType = isType;
|
||||
38
node_modules/css-tree/cjs/lexer/units.cjs
generated
vendored
Normal file
38
node_modules/css-tree/cjs/lexer/units.cjs
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
const length = [
|
||||
// absolute length units https://www.w3.org/TR/css-values-3/#lengths
|
||||
'cm', 'mm', 'q', 'in', 'pt', 'pc', 'px',
|
||||
// font-relative length units https://drafts.csswg.org/css-values-4/#font-relative-lengths
|
||||
'em', 'rem',
|
||||
'ex', 'rex',
|
||||
'cap', 'rcap',
|
||||
'ch', 'rch',
|
||||
'ic', 'ric',
|
||||
'lh', 'rlh',
|
||||
// viewport-percentage lengths https://drafts.csswg.org/css-values-4/#viewport-relative-lengths
|
||||
'vw', 'svw', 'lvw', 'dvw',
|
||||
'vh', 'svh', 'lvh', 'dvh',
|
||||
'vi', 'svi', 'lvi', 'dvi',
|
||||
'vb', 'svb', 'lvb', 'dvb',
|
||||
'vmin', 'svmin', 'lvmin', 'dvmin',
|
||||
'vmax', 'svmax', 'lvmax', 'dvmax',
|
||||
// container relative lengths https://drafts.csswg.org/css-contain-3/#container-lengths
|
||||
'cqw', 'cqh', 'cqi', 'cqb', 'cqmin', 'cqmax'
|
||||
];
|
||||
const angle = ['deg', 'grad', 'rad', 'turn']; // https://www.w3.org/TR/css-values-3/#angles
|
||||
const time = ['s', 'ms']; // https://www.w3.org/TR/css-values-3/#time
|
||||
const frequency = ['hz', 'khz']; // https://www.w3.org/TR/css-values-3/#frequency
|
||||
const resolution = ['dpi', 'dpcm', 'dppx', 'x']; // https://www.w3.org/TR/css-values-3/#resolution
|
||||
const flex = ['fr']; // https://drafts.csswg.org/css-grid/#fr-unit
|
||||
const decibel = ['db']; // https://www.w3.org/TR/css3-speech/#mixing-props-voice-volume
|
||||
const semitones = ['st']; // https://www.w3.org/TR/css3-speech/#voice-props-voice-pitch
|
||||
|
||||
exports.angle = angle;
|
||||
exports.decibel = decibel;
|
||||
exports.flex = flex;
|
||||
exports.frequency = frequency;
|
||||
exports.length = length;
|
||||
exports.resolution = resolution;
|
||||
exports.semitones = semitones;
|
||||
exports.time = time;
|
||||
Reference in New Issue
Block a user