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:
394
node_modules/css-tree/lib/parser/create.js
generated
vendored
394
node_modules/css-tree/lib/parser/create.js
generated
vendored
@@ -1,25 +1,33 @@
|
||||
var OffsetToLocation = require('../common/OffsetToLocation');
|
||||
var SyntaxError = require('../common/SyntaxError');
|
||||
var TokenStream = require('../common/TokenStream');
|
||||
var List = require('../common/List');
|
||||
var tokenize = require('../tokenizer');
|
||||
var constants = require('../tokenizer/const');
|
||||
var { findWhiteSpaceStart, cmpStr } = require('../tokenizer/utils');
|
||||
var sequence = require('./sequence');
|
||||
var noop = function() {};
|
||||
import { List } from '../utils/List.js';
|
||||
import { SyntaxError } from './SyntaxError.js';
|
||||
import {
|
||||
tokenize,
|
||||
OffsetToLocation,
|
||||
TokenStream,
|
||||
tokenNames,
|
||||
|
||||
var TYPE = constants.TYPE;
|
||||
var NAME = constants.NAME;
|
||||
var WHITESPACE = TYPE.WhiteSpace;
|
||||
var COMMENT = TYPE.Comment;
|
||||
var IDENT = TYPE.Ident;
|
||||
var FUNCTION = TYPE.Function;
|
||||
var URL = TYPE.Url;
|
||||
var HASH = TYPE.Hash;
|
||||
var PERCENTAGE = TYPE.Percentage;
|
||||
var NUMBER = TYPE.Number;
|
||||
var NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#)
|
||||
var NULL = 0;
|
||||
consumeNumber,
|
||||
findWhiteSpaceStart,
|
||||
cmpChar,
|
||||
cmpStr,
|
||||
|
||||
WhiteSpace,
|
||||
Comment,
|
||||
Ident,
|
||||
Function as FunctionToken,
|
||||
Url,
|
||||
Hash,
|
||||
Percentage,
|
||||
Number as NumberToken
|
||||
} from '../tokenizer/index.js';
|
||||
import { readSequence } from './sequence.js';
|
||||
|
||||
const NOOP = () => {};
|
||||
const EXCLAMATIONMARK = 0x0021; // U+0021 EXCLAMATION MARK (!)
|
||||
const NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#)
|
||||
const SEMICOLON = 0x003B; // U+003B SEMICOLON (;)
|
||||
const LEFTCURLYBRACKET = 0x007B; // U+007B LEFT CURLY BRACKET ({)
|
||||
const NULL = 0;
|
||||
|
||||
function createParseContext(name) {
|
||||
return function() {
|
||||
@@ -27,116 +35,120 @@ function createParseContext(name) {
|
||||
};
|
||||
}
|
||||
|
||||
function processConfig(config) {
|
||||
var parserConfig = {
|
||||
context: {},
|
||||
scope: {},
|
||||
atrule: {},
|
||||
pseudo: {}
|
||||
};
|
||||
function fetchParseValues(dict) {
|
||||
const result = Object.create(null);
|
||||
|
||||
if (config.parseContext) {
|
||||
for (var name in config.parseContext) {
|
||||
switch (typeof config.parseContext[name]) {
|
||||
case 'function':
|
||||
parserConfig.context[name] = config.parseContext[name];
|
||||
break;
|
||||
for (const name of Object.keys(dict)) {
|
||||
const item = dict[name];
|
||||
const fn = item.parse || item;
|
||||
|
||||
case 'string':
|
||||
parserConfig.context[name] = createParseContext(config.parseContext[name]);
|
||||
break;
|
||||
}
|
||||
if (fn) {
|
||||
result[name] = fn;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.scope) {
|
||||
for (var name in config.scope) {
|
||||
parserConfig.scope[name] = config.scope[name];
|
||||
}
|
||||
}
|
||||
|
||||
if (config.atrule) {
|
||||
for (var name in config.atrule) {
|
||||
var atrule = config.atrule[name];
|
||||
|
||||
if (atrule.parse) {
|
||||
parserConfig.atrule[name] = atrule.parse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.pseudo) {
|
||||
for (var name in config.pseudo) {
|
||||
var pseudo = config.pseudo[name];
|
||||
|
||||
if (pseudo.parse) {
|
||||
parserConfig.pseudo[name] = pseudo.parse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.node) {
|
||||
for (var name in config.node) {
|
||||
parserConfig[name] = config.node[name].parse;
|
||||
}
|
||||
}
|
||||
|
||||
return parserConfig;
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = function createParser(config) {
|
||||
var parser = {
|
||||
scanner: new TokenStream(),
|
||||
locationMap: new OffsetToLocation(),
|
||||
function processConfig(config) {
|
||||
const parseConfig = {
|
||||
context: Object.create(null),
|
||||
features: Object.assign(Object.create(null), config.features),
|
||||
scope: Object.assign(Object.create(null), config.scope),
|
||||
atrule: fetchParseValues(config.atrule),
|
||||
pseudo: fetchParseValues(config.pseudo),
|
||||
node: fetchParseValues(config.node)
|
||||
};
|
||||
|
||||
filename: '<unknown>',
|
||||
needPositions: false,
|
||||
onParseError: noop,
|
||||
onParseErrorThrow: false,
|
||||
for (const [name, context] of Object.entries(config.parseContext)) {
|
||||
switch (typeof context) {
|
||||
case 'function':
|
||||
parseConfig.context[name] = context;
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
parseConfig.context[name] = createParseContext(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
config: parseConfig,
|
||||
...parseConfig,
|
||||
...parseConfig.node
|
||||
};
|
||||
}
|
||||
|
||||
export function createParser(config) {
|
||||
let source = '';
|
||||
let filename = '<unknown>';
|
||||
let needPositions = false;
|
||||
let onParseError = NOOP;
|
||||
let onParseErrorThrow = false;
|
||||
|
||||
const locationMap = new OffsetToLocation();
|
||||
const parser = Object.assign(new TokenStream(), processConfig(config || {}), {
|
||||
parseAtrulePrelude: true,
|
||||
parseRulePrelude: true,
|
||||
parseValue: true,
|
||||
parseCustomProperty: false,
|
||||
|
||||
readSequence: sequence,
|
||||
readSequence,
|
||||
|
||||
createList: function() {
|
||||
consumeUntilBalanceEnd: () => 0,
|
||||
consumeUntilLeftCurlyBracket(code) {
|
||||
return code === LEFTCURLYBRACKET ? 1 : 0;
|
||||
},
|
||||
consumeUntilLeftCurlyBracketOrSemicolon(code) {
|
||||
return code === LEFTCURLYBRACKET || code === SEMICOLON ? 1 : 0;
|
||||
},
|
||||
consumeUntilExclamationMarkOrSemicolon(code) {
|
||||
return code === EXCLAMATIONMARK || code === SEMICOLON ? 1 : 0;
|
||||
},
|
||||
consumeUntilSemicolonIncluded(code) {
|
||||
return code === SEMICOLON ? 2 : 0;
|
||||
},
|
||||
|
||||
createList() {
|
||||
return new List();
|
||||
},
|
||||
createSingleNodeList: function(node) {
|
||||
createSingleNodeList(node) {
|
||||
return new List().appendData(node);
|
||||
},
|
||||
getFirstListNode: function(list) {
|
||||
return list && list.first();
|
||||
getFirstListNode(list) {
|
||||
return list && list.first;
|
||||
},
|
||||
getLastListNode: function(list) {
|
||||
return list.last();
|
||||
getLastListNode(list) {
|
||||
return list && list.last;
|
||||
},
|
||||
|
||||
parseWithFallback: function(consumer, fallback) {
|
||||
var startToken = this.scanner.tokenIndex;
|
||||
parseWithFallback(consumer, fallback) {
|
||||
const startIndex = this.tokenIndex;
|
||||
|
||||
try {
|
||||
return consumer.call(this);
|
||||
} catch (e) {
|
||||
if (this.onParseErrorThrow) {
|
||||
if (onParseErrorThrow) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
var fallbackNode = fallback.call(this, startToken);
|
||||
this.skip(startIndex - this.tokenIndex);
|
||||
const fallbackNode = fallback.call(this);
|
||||
|
||||
this.onParseErrorThrow = true;
|
||||
this.onParseError(e, fallbackNode);
|
||||
this.onParseErrorThrow = false;
|
||||
onParseErrorThrow = true;
|
||||
onParseError(e, fallbackNode);
|
||||
onParseErrorThrow = false;
|
||||
|
||||
return fallbackNode;
|
||||
}
|
||||
},
|
||||
|
||||
lookupNonWSType: function(offset) {
|
||||
lookupNonWSType(offset) {
|
||||
let type;
|
||||
|
||||
do {
|
||||
var type = this.scanner.lookupType(offset++);
|
||||
if (type !== WHITESPACE) {
|
||||
type = this.lookupType(offset++);
|
||||
if (type !== WhiteSpace && type !== Comment) {
|
||||
return type;
|
||||
}
|
||||
} while (type !== NULL);
|
||||
@@ -144,145 +156,174 @@ module.exports = function createParser(config) {
|
||||
return NULL;
|
||||
},
|
||||
|
||||
eat: function(tokenType) {
|
||||
if (this.scanner.tokenType !== tokenType) {
|
||||
var offset = this.scanner.tokenStart;
|
||||
var message = NAME[tokenType] + ' is expected';
|
||||
charCodeAt(offset) {
|
||||
return offset >= 0 && offset < source.length ? source.charCodeAt(offset) : 0;
|
||||
},
|
||||
substring(offsetStart, offsetEnd) {
|
||||
return source.substring(offsetStart, offsetEnd);
|
||||
},
|
||||
substrToCursor(start) {
|
||||
return this.source.substring(start, this.tokenStart);
|
||||
},
|
||||
|
||||
cmpChar(offset, charCode) {
|
||||
return cmpChar(source, offset, charCode);
|
||||
},
|
||||
cmpStr(offsetStart, offsetEnd, str) {
|
||||
return cmpStr(source, offsetStart, offsetEnd, str);
|
||||
},
|
||||
|
||||
consume(tokenType) {
|
||||
const start = this.tokenStart;
|
||||
|
||||
this.eat(tokenType);
|
||||
|
||||
return this.substrToCursor(start);
|
||||
},
|
||||
consumeFunctionName() {
|
||||
const name = source.substring(this.tokenStart, this.tokenEnd - 1);
|
||||
|
||||
this.eat(FunctionToken);
|
||||
|
||||
return name;
|
||||
},
|
||||
consumeNumber(type) {
|
||||
const number = source.substring(this.tokenStart, consumeNumber(source, this.tokenStart));
|
||||
|
||||
this.eat(type);
|
||||
|
||||
return number;
|
||||
},
|
||||
|
||||
eat(tokenType) {
|
||||
if (this.tokenType !== tokenType) {
|
||||
const tokenName = tokenNames[tokenType].slice(0, -6).replace(/-/g, ' ').replace(/^./, m => m.toUpperCase());
|
||||
let message = `${/[[\](){}]/.test(tokenName) ? `"${tokenName}"` : tokenName} is expected`;
|
||||
let offset = this.tokenStart;
|
||||
|
||||
// tweak message and offset
|
||||
switch (tokenType) {
|
||||
case IDENT:
|
||||
case Ident:
|
||||
// when identifier is expected but there is a function or url
|
||||
if (this.scanner.tokenType === FUNCTION || this.scanner.tokenType === URL) {
|
||||
offset = this.scanner.tokenEnd - 1;
|
||||
if (this.tokenType === FunctionToken || this.tokenType === Url) {
|
||||
offset = this.tokenEnd - 1;
|
||||
message = 'Identifier is expected but function found';
|
||||
} else {
|
||||
message = 'Identifier is expected';
|
||||
}
|
||||
break;
|
||||
|
||||
case HASH:
|
||||
if (this.scanner.isDelim(NUMBERSIGN)) {
|
||||
this.scanner.next();
|
||||
case Hash:
|
||||
if (this.isDelim(NUMBERSIGN)) {
|
||||
this.next();
|
||||
offset++;
|
||||
message = 'Name is expected';
|
||||
}
|
||||
break;
|
||||
|
||||
case PERCENTAGE:
|
||||
if (this.scanner.tokenType === NUMBER) {
|
||||
offset = this.scanner.tokenEnd;
|
||||
case Percentage:
|
||||
if (this.tokenType === NumberToken) {
|
||||
offset = this.tokenEnd;
|
||||
message = 'Percent sign is expected';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// when test type is part of another token show error for current position + 1
|
||||
// e.g. eat(HYPHENMINUS) will fail on "-foo", but pointing on "-" is odd
|
||||
if (this.scanner.source.charCodeAt(this.scanner.tokenStart) === tokenType) {
|
||||
offset = offset + 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.error(message, offset);
|
||||
}
|
||||
|
||||
this.scanner.next();
|
||||
this.next();
|
||||
},
|
||||
eatIdent(name) {
|
||||
if (this.tokenType !== Ident || this.lookupValue(0, name) === false) {
|
||||
this.error(`Identifier "${name}" is expected`);
|
||||
}
|
||||
|
||||
this.next();
|
||||
},
|
||||
eatDelim(code) {
|
||||
if (!this.isDelim(code)) {
|
||||
this.error(`Delim "${String.fromCharCode(code)}" is expected`);
|
||||
}
|
||||
|
||||
this.next();
|
||||
},
|
||||
|
||||
consume: function(tokenType) {
|
||||
var value = this.scanner.getTokenValue();
|
||||
|
||||
this.eat(tokenType);
|
||||
|
||||
return value;
|
||||
},
|
||||
consumeFunctionName: function() {
|
||||
var name = this.scanner.source.substring(this.scanner.tokenStart, this.scanner.tokenEnd - 1);
|
||||
|
||||
this.eat(FUNCTION);
|
||||
|
||||
return name;
|
||||
},
|
||||
|
||||
getLocation: function(start, end) {
|
||||
if (this.needPositions) {
|
||||
return this.locationMap.getLocationRange(
|
||||
getLocation(start, end) {
|
||||
if (needPositions) {
|
||||
return locationMap.getLocationRange(
|
||||
start,
|
||||
end,
|
||||
this.filename
|
||||
filename
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
getLocationFromList: function(list) {
|
||||
if (this.needPositions) {
|
||||
var head = this.getFirstListNode(list);
|
||||
var tail = this.getLastListNode(list);
|
||||
return this.locationMap.getLocationRange(
|
||||
head !== null ? head.loc.start.offset - this.locationMap.startOffset : this.scanner.tokenStart,
|
||||
tail !== null ? tail.loc.end.offset - this.locationMap.startOffset : this.scanner.tokenStart,
|
||||
this.filename
|
||||
getLocationFromList(list) {
|
||||
if (needPositions) {
|
||||
const head = this.getFirstListNode(list);
|
||||
const tail = this.getLastListNode(list);
|
||||
return locationMap.getLocationRange(
|
||||
head !== null ? head.loc.start.offset - locationMap.startOffset : this.tokenStart,
|
||||
tail !== null ? tail.loc.end.offset - locationMap.startOffset : this.tokenStart,
|
||||
filename
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
error: function(message, offset) {
|
||||
var location = typeof offset !== 'undefined' && offset < this.scanner.source.length
|
||||
? this.locationMap.getLocation(offset)
|
||||
: this.scanner.eof
|
||||
? this.locationMap.getLocation(findWhiteSpaceStart(this.scanner.source, this.scanner.source.length - 1))
|
||||
: this.locationMap.getLocation(this.scanner.tokenStart);
|
||||
error(message, offset) {
|
||||
const location = typeof offset !== 'undefined' && offset < source.length
|
||||
? locationMap.getLocation(offset)
|
||||
: this.eof
|
||||
? locationMap.getLocation(findWhiteSpaceStart(source, source.length - 1))
|
||||
: locationMap.getLocation(this.tokenStart);
|
||||
|
||||
throw new SyntaxError(
|
||||
message || 'Unexpected input',
|
||||
this.scanner.source,
|
||||
source,
|
||||
location.offset,
|
||||
location.line,
|
||||
location.column
|
||||
location.column,
|
||||
locationMap.startLine,
|
||||
locationMap.startColumn
|
||||
);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
config = processConfig(config || {});
|
||||
for (var key in config) {
|
||||
parser[key] = config[key];
|
||||
}
|
||||
|
||||
return function(source, options) {
|
||||
const parse = function(source_, options) {
|
||||
source = source_;
|
||||
options = options || {};
|
||||
|
||||
var context = options.context || 'default';
|
||||
var onComment = options.onComment;
|
||||
var ast;
|
||||
|
||||
tokenize(source, parser.scanner);
|
||||
parser.locationMap.setSource(
|
||||
parser.setSource(source, tokenize);
|
||||
locationMap.setSource(
|
||||
source,
|
||||
options.offset,
|
||||
options.line,
|
||||
options.column
|
||||
);
|
||||
|
||||
parser.filename = options.filename || '<unknown>';
|
||||
parser.needPositions = Boolean(options.positions);
|
||||
parser.onParseError = typeof options.onParseError === 'function' ? options.onParseError : noop;
|
||||
parser.onParseErrorThrow = false;
|
||||
filename = options.filename || '<unknown>';
|
||||
needPositions = Boolean(options.positions);
|
||||
onParseError = typeof options.onParseError === 'function' ? options.onParseError : NOOP;
|
||||
onParseErrorThrow = false;
|
||||
|
||||
parser.parseAtrulePrelude = 'parseAtrulePrelude' in options ? Boolean(options.parseAtrulePrelude) : true;
|
||||
parser.parseRulePrelude = 'parseRulePrelude' in options ? Boolean(options.parseRulePrelude) : true;
|
||||
parser.parseValue = 'parseValue' in options ? Boolean(options.parseValue) : true;
|
||||
parser.parseCustomProperty = 'parseCustomProperty' in options ? Boolean(options.parseCustomProperty) : false;
|
||||
|
||||
if (!parser.context.hasOwnProperty(context)) {
|
||||
const { context = 'default', onComment } = options;
|
||||
|
||||
if (context in parser.context === false) {
|
||||
throw new Error('Unknown context `' + context + '`');
|
||||
}
|
||||
|
||||
if (typeof onComment === 'function') {
|
||||
parser.scanner.forEachToken((type, start, end) => {
|
||||
if (type === COMMENT) {
|
||||
parser.forEachToken((type, start, end) => {
|
||||
if (type === Comment) {
|
||||
const loc = parser.getLocation(start, end);
|
||||
const value = cmpStr(source, end - 2, end, '*/')
|
||||
? source.slice(start + 2, end - 2)
|
||||
@@ -293,12 +334,17 @@ module.exports = function createParser(config) {
|
||||
});
|
||||
}
|
||||
|
||||
ast = parser.context[context].call(parser, options);
|
||||
const ast = parser.context[context].call(parser, options);
|
||||
|
||||
if (!parser.scanner.eof) {
|
||||
if (!parser.eof) {
|
||||
parser.error();
|
||||
}
|
||||
|
||||
return ast;
|
||||
};
|
||||
|
||||
return Object.assign(parse, {
|
||||
SyntaxError,
|
||||
config: parser.config
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user