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>
149 lines
3.6 KiB
JavaScript
149 lines
3.6 KiB
JavaScript
'use strict';
|
|
|
|
const types = require('../../tokenizer/types.cjs');
|
|
|
|
const DOLLARSIGN = 0x0024; // U+0024 DOLLAR SIGN ($)
|
|
const ASTERISK = 0x002A; // U+002A ASTERISK (*)
|
|
const EQUALSSIGN = 0x003D; // U+003D EQUALS SIGN (=)
|
|
const CIRCUMFLEXACCENT = 0x005E; // U+005E (^)
|
|
const VERTICALLINE = 0x007C; // U+007C VERTICAL LINE (|)
|
|
const TILDE = 0x007E; // U+007E TILDE (~)
|
|
|
|
function getAttributeName() {
|
|
if (this.eof) {
|
|
this.error('Unexpected end of input');
|
|
}
|
|
|
|
const start = this.tokenStart;
|
|
let expectIdent = false;
|
|
|
|
if (this.isDelim(ASTERISK)) {
|
|
expectIdent = true;
|
|
this.next();
|
|
} else if (!this.isDelim(VERTICALLINE)) {
|
|
this.eat(types.Ident);
|
|
}
|
|
|
|
if (this.isDelim(VERTICALLINE)) {
|
|
if (this.charCodeAt(this.tokenStart + 1) !== EQUALSSIGN) {
|
|
this.next();
|
|
this.eat(types.Ident);
|
|
} else if (expectIdent) {
|
|
this.error('Identifier is expected', this.tokenEnd);
|
|
}
|
|
} else if (expectIdent) {
|
|
this.error('Vertical line is expected');
|
|
}
|
|
|
|
return {
|
|
type: 'Identifier',
|
|
loc: this.getLocation(start, this.tokenStart),
|
|
name: this.substrToCursor(start)
|
|
};
|
|
}
|
|
|
|
function getOperator() {
|
|
const start = this.tokenStart;
|
|
const code = this.charCodeAt(start);
|
|
|
|
if (code !== EQUALSSIGN && // =
|
|
code !== TILDE && // ~=
|
|
code !== CIRCUMFLEXACCENT && // ^=
|
|
code !== DOLLARSIGN && // $=
|
|
code !== ASTERISK && // *=
|
|
code !== VERTICALLINE // |=
|
|
) {
|
|
this.error('Attribute selector (=, ~=, ^=, $=, *=, |=) is expected');
|
|
}
|
|
|
|
this.next();
|
|
|
|
if (code !== EQUALSSIGN) {
|
|
if (!this.isDelim(EQUALSSIGN)) {
|
|
this.error('Equal sign is expected');
|
|
}
|
|
|
|
this.next();
|
|
}
|
|
|
|
return this.substrToCursor(start);
|
|
}
|
|
|
|
// '[' <wq-name> ']'
|
|
// '[' <wq-name> <attr-matcher> [ <string-token> | <ident-token> ] <attr-modifier>? ']'
|
|
const name = 'AttributeSelector';
|
|
const structure = {
|
|
name: 'Identifier',
|
|
matcher: [String, null],
|
|
value: ['String', 'Identifier', null],
|
|
flags: [String, null]
|
|
};
|
|
|
|
function parse() {
|
|
const start = this.tokenStart;
|
|
let name;
|
|
let matcher = null;
|
|
let value = null;
|
|
let flags = null;
|
|
|
|
this.eat(types.LeftSquareBracket);
|
|
this.skipSC();
|
|
|
|
name = getAttributeName.call(this);
|
|
this.skipSC();
|
|
|
|
if (this.tokenType !== types.RightSquareBracket) {
|
|
// avoid case `[name i]`
|
|
if (this.tokenType !== types.Ident) {
|
|
matcher = getOperator.call(this);
|
|
|
|
this.skipSC();
|
|
|
|
value = this.tokenType === types.String
|
|
? this.String()
|
|
: this.Identifier();
|
|
|
|
this.skipSC();
|
|
}
|
|
|
|
// attribute flags
|
|
if (this.tokenType === types.Ident) {
|
|
flags = this.consume(types.Ident);
|
|
|
|
this.skipSC();
|
|
}
|
|
}
|
|
|
|
this.eat(types.RightSquareBracket);
|
|
|
|
return {
|
|
type: 'AttributeSelector',
|
|
loc: this.getLocation(start, this.tokenStart),
|
|
name,
|
|
matcher,
|
|
value,
|
|
flags
|
|
};
|
|
}
|
|
|
|
function generate(node) {
|
|
this.token(types.Delim, '[');
|
|
this.node(node.name);
|
|
|
|
if (node.matcher !== null) {
|
|
this.tokenize(node.matcher);
|
|
this.node(node.value);
|
|
}
|
|
|
|
if (node.flags !== null) {
|
|
this.token(types.Ident, node.flags);
|
|
}
|
|
|
|
this.token(types.Delim, ']');
|
|
}
|
|
|
|
exports.generate = generate;
|
|
exports.name = name;
|
|
exports.parse = parse;
|
|
exports.structure = structure;
|