Comparing version 1.0.15 to 1.0.16
@@ -1,4 +0,1 @@ | ||
import { compileScope } from "./src/compiler/lexer"; | ||
@@ -8,3 +5,2 @@ import { Config, VoidCSSConfiguration } from "./src/config"; | ||
function VoidCSS(config?: VoidCSSConfiguration) { | ||
@@ -11,0 +7,0 @@ const configInstance = new Config(config ?? {}); |
{ | ||
"name": "void-css", | ||
"version": "1.0.15", | ||
"version": "1.0.16", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -16,2 +16,3 @@ | ||
}); | ||
} | ||
} | ||
@@ -7,2 +7,3 @@ | ||
/** | ||
@@ -9,0 +10,0 @@ * Wrap css arithmetic operations in calc |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const crypto_1 = __importDefault(require("crypto")); | ||
const newLine = `\n`; | ||
@@ -53,3 +49,3 @@ function compile(ctx) { | ||
if (!compress) | ||
shift = shift.substr(0, shift.length - shiftBy.length); | ||
shift = shift.slice(0, shift.length - shiftBy.length); | ||
css += `${shift}}${compress ? "" : newLine}`; | ||
@@ -72,3 +68,3 @@ } | ||
// TODO: cache | ||
hash: normalizeBase64(crypto_1.default.createHash("sha1").update(css).digest("base64")), | ||
//hash: normalizeBase64(crypto.createHash("sha1").update(css).digest("base64")), | ||
}); | ||
@@ -80,2 +76,4 @@ } | ||
} | ||
if (ctx.keyframes.length) | ||
css += createKeyframesScope(ctx, ctx.keyframes); | ||
return { | ||
@@ -87,2 +85,47 @@ classes: tree.classes, | ||
} | ||
function createKeyframesScope(ctx, tokens) { | ||
const compress = ctx.config.compressed; | ||
let css = ""; | ||
const shiftBy = compress ? "" : ctx.config.indentationStyle; | ||
for (const [i, token] of tokens.entries()) { | ||
const name = token.renamed || token.name; | ||
if (["atSymbol", "keyframes", "keyframesWhiteSpace"].includes(name)) { | ||
css += `${token.match}`; | ||
} | ||
else if (["keyframesName"].includes(name)) { | ||
css += `${token.match}${compress ? "" : " "}{${compress ? "" : newLine}`; | ||
} | ||
else if (["keyframesSelector"].includes(name)) { | ||
if (tokens[i - 1].domain !== "selector") | ||
css += shiftBy; | ||
css += `${token.match.trim()}`; | ||
} | ||
else if (["keyframesSeparator"].includes(name)) { | ||
css += `,${compress ? "" : " "}`; | ||
} | ||
else if (["keyframesBlock"].includes(name)) { | ||
css += `${compress ? "" : " "}{${compress ? "" : newLine}`; | ||
} | ||
else if (["keyframesBlockPropertyName"].includes(name)) { | ||
css += `${shiftBy + shiftBy}${token.match}:${compress ? "" : " "}`; | ||
} | ||
else if (["keyframesPropertyValue"].includes(name)) { | ||
const semiColon = compress && tokens[i + 1]?.match.toString() === "}" ? "" : ";"; | ||
css += `${token.match.trim()}${semiColon}${compress ? "" : newLine}`; | ||
} | ||
else if (["keyframesBlockEnd"].includes(name)) { | ||
const match = token.match.trim(); | ||
if (/}/.test(match)) { | ||
css += `${shiftBy}}${compress ? "" : newLine}`; | ||
} | ||
} | ||
else if (["keyframesScopeEnd"].includes(name)) { | ||
const match = token.match.trim(); | ||
if (/}/.test(match)) { | ||
css += `}${compress ? "" : newLine}`; | ||
} | ||
} | ||
} | ||
return css; | ||
} | ||
/** This removes non css friendly characters from base64 string */ | ||
@@ -89,0 +132,0 @@ function normalizeBase64(input) { |
@@ -17,2 +17,3 @@ import Context, { Token } from "../context"; | ||
const newLine = `\n`; | ||
@@ -39,3 +40,3 @@ | ||
dependencies: string[]; | ||
hash: string; | ||
//hash: string; | ||
exportedClasses: { | ||
@@ -84,3 +85,3 @@ newClassName: string; | ||
if (!compress) | ||
shift = shift.substr(0, shift.length - shiftBy.length); | ||
shift = shift.slice(0, shift.length - shiftBy.length); | ||
css += `${shift}}${compress ? "" : newLine}`; | ||
@@ -103,3 +104,3 @@ } | ||
// TODO: cache | ||
hash: normalizeBase64(crypto.createHash("sha1").update(css).digest("base64")), | ||
//hash: normalizeBase64(crypto.createHash("sha1").update(css).digest("base64")), | ||
}); | ||
@@ -111,2 +112,6 @@ } else { | ||
if (ctx.keyframes.length) | ||
css += createKeyframesScope(ctx, ctx.keyframes); | ||
return { | ||
@@ -119,2 +124,45 @@ classes: tree.classes, | ||
function createKeyframesScope(ctx: Context, tokens: Token[]) { | ||
const compress = ctx.config.compressed; | ||
let css: string = ""; | ||
const shiftBy = compress ? "" : ctx.config.indentationStyle; | ||
for (const [i, token] of tokens.entries()) { | ||
const name = token.renamed || token.name; | ||
if (["atSymbol", "keyframes", "keyframesWhiteSpace"].includes(name)) { | ||
css += `${token.match}`; | ||
} else if (["keyframesName"].includes(name)) { | ||
css += `${token.match}${compress ? "" : " "}{${compress ? "" : newLine}`; | ||
} else if (["keyframesSelector"].includes(name)) { | ||
if (tokens[i - 1].domain !== "selector") | ||
css += shiftBy; | ||
css += `${token.match.trim()}`; | ||
} else if (["keyframesSeparator"].includes(name)) { | ||
css += `,${compress ? "" : " "}`; | ||
} else if (["keyframesBlock"].includes(name)) { | ||
css += `${compress ? "" : " "}{${compress ? "" : newLine}`; | ||
} else if (["keyframesBlockPropertyName"].includes(name)) { | ||
css += `${shiftBy + shiftBy}${token.match}:${compress ? "" : " "}`; | ||
} else if (["keyframesPropertyValue"].includes(name)) { | ||
const semiColon = compress && tokens[i + 1]?.match.toString() === "}" ? "" : ";"; | ||
css += `${token.match.trim()}${semiColon}${compress ? "" : newLine}`; | ||
} else if (["keyframesBlockEnd"].includes(name)) { | ||
const match = token.match.trim(); | ||
if (/}/.test(match)) { | ||
css += `${shiftBy}}${compress ? "" : newLine}` | ||
} | ||
} else if (["keyframesScopeEnd"].includes(name)) { | ||
const match = token.match.trim(); | ||
if (/}/.test(match)) { | ||
css += `}${compress ? "" : newLine}` | ||
} | ||
} | ||
} | ||
return css; | ||
} | ||
/** This removes non css friendly characters from base64 string */ | ||
@@ -121,0 +169,0 @@ function normalizeBase64(input: string) { |
@@ -19,2 +19,16 @@ "use strict"; | ||
const globalTokens = [tokens_1.inlineComment, tokens_1.blockComment]; | ||
/** | ||
* Void CSS lexer — Tokenizes given Void CSS input and then, if valid, compiles it, | ||
* otherwise throws syntax error. | ||
* | ||
* Recursive and declarative design. Each case on the main switch statement represents the next step | ||
* after the given (current token). Each self call of the lex function represents what the lexer should | ||
* expect after the current token. Using attributes which live on the function recursively (as an argument), | ||
* the lexer can call itself with possibly conditional expectations using helper functions such as `_if`. | ||
* | ||
* @param ctx Compile context | ||
* @param token Current token (unless beginning) | ||
* @param attributes Makes the lexer aware of its state | ||
* @returns compiled Void CSS. see `compile` function | ||
*/ | ||
function lex(ctx, token, attributes) { | ||
@@ -36,2 +50,4 @@ const { lastToken, index, css: preTokenizedCSS } = ctx; | ||
const enclosures = attributes?.enclosures || []; | ||
const notOperatorDepth = attributes?.notOperatorDepth ?? 0; | ||
/** The depth refers to the css rule or "scope" nesting level */ | ||
const depth = (attributes?.depth ?? 0) + (() => { | ||
@@ -59,8 +75,9 @@ // The only way to descend a depth is to close it with the `}` token `styleScopeEnd` | ||
/^\s*(?:~|>|\+|\|\|)/.test(section) ? | ||
"Selectors cannot begin with a selector combinator ('~', '>', '+', '||'). Did you forget to add left side selector combinator operand?" : | ||
"Root level selectors cannot begin with a selector combinator ('~', '>', '+', '||'). Did you forget to add left side selector combinator operand?" : | ||
"Instead found invalid or missing selector."; | ||
return lex(ctx, expect(slice, ...globals, tokens_1.atSymbol, tokens_1.whiteSpace, ...enclosures, ..._if(enclosures.length === 0, [tokens_1.rootSelector]), ..._if(depth > 0, [tokens_1.currentSelector]), tokens_1.classNameInitiator, tokens_1.selectorName, tokens_1.idInitiator, tokens_1.wildcard, tokens_1.notOperator), { | ||
return lex(ctx, expect(slice, ...globals, tokens_1.atSymbol, tokens_1.whiteSpace, ...enclosures, ..._if(enclosures.length === 0, [tokens_1.rootSelector]), ..._if(depth > 0, [tokens_1.currentSelector]), tokens_1.attributeSelectorInitiator, tokens_1.classNameInitiator, tokens_1.selectorName, tokens_1.idInitiator, tokens_1.wildcard, tokens_1.notOperator), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [css selector]. " + hint, start, end), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -72,15 +89,137 @@ case "notOperator": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "notOperatorParenthesis": | ||
return lex(ctx, expect(slice, ...globals, tokens_1.notOperatorParenthesis), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [selector].", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
return lex(ctx, expect(slice, ...globals, ..._if(enclosures.length === 0, [tokens_1.rootSelector]), ..._if(depth > 0, [tokens_1.currentSelector]), tokens_1.notOperator, tokens_1.attributeSelectorInitiator, tokens_1.classNameInitiator, tokens_1.selectorName, tokens_1.idInitiator, tokens_1.wildcard), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [selector].", ctx.index, ctx.index + 1), | ||
enclosures, | ||
depth, | ||
notOperatorDepth: notOperatorDepth + 1, | ||
}); | ||
case "notOperatorParenthesisEndWhiteSpace": | ||
case "notOperatorParenthesisEnd": | ||
const newDepth = notOperatorDepth - (resolvedType === "notOperatorParenthesisEnd" ? 1 : 0); | ||
return lex(ctx, expect(slice, ...globals, (0, tokens_1.renameToken)(tokens_1.whiteSpace, "notOperatorParenthesisEndWhiteSpace"), ..._if(enclosures.length === 0, [tokens_1.rootSelector]), ..._if(depth > 0, [tokens_1.currentSelector]), ..._if(newDepth > 0, [tokens_1.notOperatorParenthesisEnd]), tokens_1.colon, tokens_1.selectorSeparator, tokens_1.styleScope, tokens_1.attributeSelectorInitiator, tokens_1.classNameInitiator, tokens_1.selectorName, tokens_1.idInitiator, tokens_1.wildcard), { | ||
onError: (0, errors_1.createErrorContext)(newDepth === 0 ? | ||
"Unexpected token. Expected: [selector]." : | ||
"Unexpected token. Expected: ')' or [selector].", ctx.index, ctx.index + 1), | ||
enclosures, | ||
depth, | ||
notOperatorDepth: newDepth, | ||
}); | ||
case "atSymbol": | ||
return lex(ctx, expect(slice, ...globals, tokens_1.nestedAtRuleName), { | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)(tokens_1.keyframes), tokens_1.nestedAtRuleName), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: 'color-profile', 'counter-style', 'document', 'font-face', 'font-feature-values', 'keyframes', 'media', 'page', 'property' or 'supports'. It's possible that this list is outdated, if so please make an issue on the repository.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframes": | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.selectorChild, "keyframesWhiteSpace"))), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [whitespace].", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesWhiteSpace": | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.selectorName, "keyframesName"), tokens_1.keyframesSpecialNames)), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [keyframes name].", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesSpecialNames": | ||
case "keyframesNameWhiteSpace": | ||
case "keyframesName": | ||
if (resolvedType === "keyframesName" && ["None", "initial"].includes(token.match.toString())) { | ||
return lex(ctx, expect(slice, ...globals), { | ||
onError: (0, errors_1.createErrorContext)(`Invalid keyframes name. A keyframes name cannot be '${token.match}' as per "CSS Animations Level 2" w3c specification. You can wrap '${token.match}' in double quotes ('"') like this: '"${token.match}"' to still use this name.`, Math.max(ctx.index - token.match.toString().length, 0), ctx.index), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
} | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.selectorChild, "keyframesNameWhiteSpace"), (0, tokens_1.renameToken)(tokens_1.styleScope, "keyframesScope"))), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: '{'.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesScopeWhitespace": | ||
case "keyframesScope": | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.whiteSpace, "keyframesScopeWhitespace"), tokens_1.keyframesSelector, (0, tokens_1.renameToken)(tokens_1.styleScopeEnd, "keyframesScopeEnd"))), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [percentage selector], 'to', 'from' or '}'.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesBlockEndWhitespace": | ||
case "keyframesBlockEnd": | ||
return lex(ctx, expect(slice, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.whiteSpace, "keyframesBlockEndWhitespace"), tokens_1.keyframesSelector, (0, tokens_1.renameToken)(tokens_1.styleScopeEnd, "keyframesScopeEnd"))), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [keyframes selector] or '}'.", ctx.index, ctx.index + 1), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesScopeEnd": | ||
return lex(ctx, expect(slice, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.whiteSpace, "keyframesScopeEnd"), tokens_1.currentSelector, tokens_1.propertyName, tokens_1.attributeSelectorClose, tokens_1.selectorName, tokens_1.idInitiator, tokens_1.classNameInitiator, tokens_1.atSymbol, tokens_1.whiteSpaceOrNothing)), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [css].", ctx.index, ctx.index + 1), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesSelector": | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.whiteSpace, "keyframesSelector"), (0, tokens_1.renameToken)(tokens_1.selectorSeparator, "keyframesSeparator"), (0, tokens_1.renameToken)(tokens_1.styleScope, "keyframesBlock"))), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: ',' or '{'.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesSeparator": | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.whiteSpace, "keyframesSeparator"), tokens_1.keyframesSelector)), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [percentage selector], 'to' or 'from'.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesBlockWhitespace": | ||
case "keyframesBlock": | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.whiteSpace, "keyframesBlockWhitespace"), (0, tokens_1.renameToken)(tokens_1.propertyName, "keyframesBlockPropertyName"), (0, tokens_1.renameToken)(tokens_1.styleScopeEnd, "keyframesScopeEnd"))), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [property name] or '}'.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesBlockPropertyName": | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.whiteSpace, "keyframesBlockPropertyName"), (0, tokens_1.renameToken)(tokens_1.propertyColon, "keyframesPropertyColon"))), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: ':'.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesPropertyColon": | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.whiteSpace, "keyframesPropertyColon"), ...(0, tokens_1.renameTokens)([tokens_1.propertyValueWithSemiColon, tokens_1.propertyValueWithoutSemiColon], "keyframesPropertyValue"))), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [property value]. Did you forget ';'?", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "keyframesPropertyValue": { | ||
const hasSemiColon = false; // todo: fix | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.whiteSpace, "keyframesPropertyValue"), (0, tokens_1.renameToken)(tokens_1.valueSemiColon, "keyframesValueSemiColon"), (0, tokens_1.renameToken)(tokens_1.styleScopeEnd, "keyframesBlockEnd"))), { | ||
onError: (0, errors_1.createErrorContext)(hasSemiColon ? | ||
"Unexpected token. Expected: '}'." : | ||
"Unexpected token. Expected: ';' or '}'.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
} | ||
case "keyframesValueSemiColon": | ||
return lex(ctx, expect(slice, ...globals, ...(0, tokens_1.kf)((0, tokens_1.renameToken)(tokens_1.whiteSpace, "keyframesValueSemiColon"), (0, tokens_1.renameToken)(tokens_1.propertyName, "keyframesBlockPropertyName"), (0, tokens_1.renameToken)(tokens_1.styleScopeEnd, "keyframesBlockEnd"))), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [property name] or '}'.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "nestedAtRuleName": | ||
@@ -91,2 +230,3 @@ return lex(ctx, expect(slice, ...globals, tokens_1.nestedAtRule), { | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -98,14 +238,17 @@ case "nestedAtRule": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "idInitiator": | ||
return lex(ctx, expect(slice, ...globals, tokens_1.selectorName), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: ID name", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: ID name.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "currentSelector": | ||
return lex(ctx, expect(slice, ...globals, tokens_1.selectorChild, tokens_1.classNameInitiator, tokens_1.colon, tokens_1.selectorName, tokens_1.selectorCombinator, tokens_1.selectorSeparator, tokens_1.styleScope), { | ||
return lex(ctx, expect(slice, ...globals, tokens_1.notOperator, tokens_1.selectorChild, tokens_1.classNameInitiator, tokens_1.colon, tokens_1.selectorName, tokens_1.selectorCombinator, tokens_1.selectorSeparator, tokens_1.styleScope), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: ' ', '>', '+', '||', '.', ':', '::', '[...]', ',' or '{' directly proceeding current selector ('&').", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -117,2 +260,3 @@ case "attributeSelectorInitiator": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -124,2 +268,3 @@ case "attributeSelectorName": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -131,2 +276,3 @@ case "attributeSelectorModifier": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -138,2 +284,3 @@ case "attributeSelectorSingleQuoteInitiator": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -145,2 +292,3 @@ case "attributeSelectorDoubleQuoteInitiator": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -150,11 +298,15 @@ case "attributeSelectorDoubleQuoteBody": | ||
return lex(ctx, expect(slice, ...globals, tokens_1.attributeSelectorClose), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [selector operand]", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: 'i]', 'I]', 's]', 'S]', or ']'.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "attributeSelectorClose": | ||
return lex(ctx, expect(slice, ...globals, (0, tokens_1.renameToken)(tokens_1.whiteSpace, "attributeSelectorClose"), tokens_1.classNameInitiator, tokens_1.colon, tokens_1.selectorName, tokens_1.idInitiator, tokens_1.selectorSeparator, tokens_1.attributeSelectorInitiator, tokens_1.styleScope), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: 'i]', 'I]', 's]', 'S]', or ']'.", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
return lex(ctx, expect(slice, ...globals, (0, tokens_1.renameToken)(tokens_1.whiteSpace, "attributeSelectorClose"), ..._if(notOperatorDepth > 0, [tokens_1.notOperatorParenthesisEnd]), tokens_1.notOperator, tokens_1.selectorCombinator, tokens_1.wildcard, tokens_1.classNameInitiator, tokens_1.colon, tokens_1.selectorName, tokens_1.idInitiator, tokens_1.selectorSeparator, tokens_1.attributeSelectorInitiator, tokens_1.styleScope), { | ||
onError: (0, errors_1.createErrorContext)(notOperatorDepth === 0 ? | ||
"Unexpected token. Expected: [css selector] or '{'" : | ||
"Unexpected token. Expected: ')', [css selector] or '{'", ctx.index, ctx.index + (0, errors_1.resolveSelectorNameNotFound)(slice)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -166,2 +318,3 @@ case "classNameInitiator": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -173,14 +326,19 @@ case "exportClassName": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "wildcard": | ||
return lex(ctx, expect(slice, ...globals, tokens_1.styleScope, tokens_1.colon, tokens_1.selectorChild, tokens_1.selectorSeparator, tokens_1.selectorCombinator), { | ||
return lex(ctx, expect(slice, ...globals, tokens_1.notOperator, tokens_1.styleScope, tokens_1.colon, tokens_1.selectorChild, tokens_1.selectorSeparator, tokens_1.selectorCombinator), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token directly proceeding universal selector (*). Expected: '{' or [css selector]. ", ctx.index, ctx.index + (/\s/.exec(slice)?.index || 1)), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "combinator": | ||
return lex(ctx, expect(slice, ...globals, (0, tokens_1.renameToken)(tokens_1.whiteSpace, "combinator"), tokens_1.selectorName, tokens_1.idInitiator, tokens_1.attributeSelectorInitiator, tokens_1.classNameInitiator, tokens_1.wildcard), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token proceeding selector combinator. Expected: [css selector]. Combinators ('~', '>', '+', '||') cannot directly follow previous combinator. Did you forgot a [css selector] after a selector combinator?", ctx.index, ctx.index + (/\s/.exec(slice)?.index || 1)), | ||
const previousIsCombinator = ctx.strippedTokens?.[ctx.strippedTokens.length - 2]?.name === "combinator"; | ||
const combinatorHint = previousIsCombinator ? "Combinators ('~', '>', '+', '||') cannot directly follow previous combinator. " : ""; | ||
return lex(ctx, expect(slice, ...globals, (0, tokens_1.renameToken)(tokens_1.whiteSpace, "combinator"), tokens_1.notOperator, tokens_1.selectorName, tokens_1.idInitiator, tokens_1.attributeSelectorInitiator, tokens_1.classNameInitiator, tokens_1.wildcard), { | ||
onError: (0, errors_1.createErrorContext)(`Unexpected token proceeding selector combinator. Expected: [combinator operand]. ${combinatorHint}Did you forgot a [combinator operand] after the selector combinator?`, ctx.index, ctx.index + 1), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -192,15 +350,26 @@ case "rootSelector": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "className": | ||
case "selectorName": | ||
return lex(ctx, expect(slice, ...globals, tokens_1.attributeSelectorInitiator, tokens_1.colon, tokens_1.styleScope, tokens_1.selectorChild, tokens_1.selectorSeparator, tokens_1.classNameInitiator, tokens_1.selectorCombinator), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token in selector. Expected: '{' or [css selector].", ctx.index, ctx.index + (/\s/.exec(slice)?.index || 1)), | ||
return lex(ctx, expect(slice, ...globals, tokens_1.notOperator, ..._if(notOperatorDepth > 0, [tokens_1.notOperatorParenthesisEnd]), tokens_1.attributeSelectorInitiator, tokens_1.colon, tokens_1.styleScope, tokens_1.selectorChild, tokens_1.selectorSeparator, tokens_1.classNameInitiator, tokens_1.selectorCombinator), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token in selector. Expected: '{' or [css selector].", ctx.index, ctx.index + 1), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "selectorSeparator": | ||
if (notOperatorDepth !== 0) { | ||
return lex(ctx, expect(slice, ...globals), { | ||
onError: (0, errors_1.createErrorContext)(`Unexpected selector separator ','. Selector branching is not valid inside ':not(...)' and ':has(...)' operators. Did you forget '${")".repeat(Math.max(1, notOperatorDepth))}'?`, Math.max(0, ctx.index - 1), ctx.index), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
} | ||
return lex(ctx, expect(slice, ...globals, ..._if(enclosures.length === 0, [tokens_1.rootSelector]), tokens_1.currentSelector, tokens_1.classNameInitiator, tokens_1.selectorName, tokens_1.idInitiator), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token following selector separator (,). Expected: [css selector].", ctx.index, ctx.index + 1), | ||
onError: (0, errors_1.createErrorContext)("Unexpected token following selector separator (','). Expected: [css selector].", ctx.index, ctx.index + 1), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -212,5 +381,14 @@ case "selectorChild": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "styleScopeWhiteSpace": | ||
case "styleScope": | ||
if (notOperatorDepth !== 0) { | ||
return lex(ctx, expect(slice, ...globals), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token in style scope. Expected: ')'. Did you forget to end your ':not(...)' or ':has(...)' operator?", Math.max(0, ctx.index - 1), ctx.index), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
} | ||
return lex(ctx, expect(slice, ...globals, (0, tokens_1.renameToken)(tokens_1.whiteSpace, "styleScopeWhiteSpace"), tokens_1.atSymbol, tokens_1.styleScopeEnd, tokens_1.propertyName, tokens_1.selectorName, tokens_1.attributeSelectorClose, tokens_1.wildcard, tokens_1.idInitiator, tokens_1.classNameInitiator, tokens_1.currentSelector), { | ||
@@ -220,2 +398,3 @@ onError: (0, errors_1.createErrorContext)("Unexpected token in style scope. Expected: '}', [css selector] or [css property].", ctx.index, ctx.index + 1), | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -226,5 +405,6 @@ case "styleScopeEnd": | ||
expect(slice, (0, tokens_1.renameToken)(tokens_1.whiteSpace, "styleScopeEnd"), ..._if(depth > 0, [tokens_1.styleScopeEnd]), tokens_1.currentSelector, tokens_1.propertyName, tokens_1.attributeSelectorClose, tokens_1.selectorName, tokens_1.idInitiator, tokens_1.classNameInitiator, tokens_1.atSymbol, tokens_1.whiteSpaceOrNothing), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Undefined behaviour." + (depth), ctx.index, ctx.index + 1), | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Undefined behavior." + (depth), ctx.index, ctx.index + 1), | ||
enclosures: enclosures.slice(1), | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -236,8 +416,10 @@ case "colon": | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "propertyName": | ||
return lex(ctx, expect(slice, ...globals, (0, tokens_1.renameToken)(tokens_1.whiteSpace, "propertyName"), tokens_1.propertyColon), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: property name.", ctx.index, ctx.index + 1), | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [property name].", ctx.index, ctx.index + 1), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -247,17 +429,20 @@ case "propertyColon": | ||
return lex(ctx, expect(slice, ...globals, tokens_1.propertyValueWithSemiColon, tokens_1.propertyValueWithoutSemiColon), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: ';' or '}'.", errorStartEstimate, errorStartEstimate), | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: [property value].", errorStartEstimate, errorStartEstimate), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "propertyValue": | ||
return lex(ctx, expect(slice, ...globals, tokens_1.styleScopeEnd, tokens_1.valueSemiColon), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: property value.", ctx.index, ctx.index + 1), | ||
onError: (0, errors_1.createErrorContext)("Unexpected token. Expected: ';' or '}'.", ctx.index, ctx.index + 1), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "valueSemiColon": | ||
return lex(ctx, expect(slice, ...globals, (0, tokens_1.renameToken)(tokens_1.whiteSpace, "valueSemiColon"), tokens_1.currentSelector, tokens_1.valueSemiColon, tokens_1.propertyName, tokens_1.styleScopeEnd, tokens_1.classNameInitiator, tokens_1.idInitiator, tokens_1.selectorName), { | ||
return lex(ctx, expect(slice, ...globals, (0, tokens_1.renameToken)(tokens_1.whiteSpace, "valueSemiColon"), tokens_1.atKeyframes, tokens_1.selectorCombinator, tokens_1.currentSelector, tokens_1.notOperator, tokens_1.valueSemiColon, tokens_1.propertyName, tokens_1.styleScopeEnd, tokens_1.classNameInitiator, tokens_1.idInitiator, tokens_1.selectorName), { | ||
onError: (0, errors_1.createErrorContext)("Unexpected token in style scope. Expected: '}'.", ctx.index, ctx.index + 1), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
@@ -264,0 +449,0 @@ default: |
@@ -1,2 +0,2 @@ | ||
import { whiteSpace, Match, currentSelector, classNameInitiator, selectorName, idInitiator, wildcard, selectorChild, selectorCombinator, selectorSeparator, styleScope, renameToken, attributeSelectorCloseWithoutOperator, attributeSelectorModifier, attributeSelectorSingleQuoteInitiator, attributeSelectorDoubleQuoteInitiator, attributeSelectorSingleQuoteBody, attributeSelectorDoubleQuoteBody, attributeSelectorClose, attributeSelectorInitiator, styleScopeEnd, propertyName, valueSemiColon, propertyColon, propertyValueWithSemiColon, propertyValueWithoutSemiColon, TokenMatcher, inlineComment, renameTokens, blockComment, atSymbol, nestedAtRuleName, nestedAtRule, endStyleSheet, whiteSpaceOrNothing, exportClassName, colon, pseudoSelector, rootSelector, notOperatorParenthesis, notOperator } from "../tokens"; | ||
import { whiteSpace, Match, currentSelector, classNameInitiator, selectorName, idInitiator, wildcard, selectorChild, selectorCombinator, selectorSeparator, styleScope, renameToken, attributeSelectorCloseWithoutOperator, attributeSelectorModifier, attributeSelectorSingleQuoteInitiator, attributeSelectorDoubleQuoteInitiator, attributeSelectorSingleQuoteBody, attributeSelectorDoubleQuoteBody, attributeSelectorClose, attributeSelectorInitiator, styleScopeEnd, propertyName, valueSemiColon, propertyColon, propertyValueWithSemiColon, propertyValueWithoutSemiColon, TokenMatcher, inlineComment, renameTokens, blockComment, atSymbol, nestedAtRuleName, nestedAtRule, endStyleSheet, whiteSpaceOrNothing, exportClassName, colon, pseudoSelector, rootSelector, notOperatorParenthesis, notOperator, notOperatorParenthesisEnd, keyframes, requiredWhiteSpace, keyframesSpecialNames, keyframesSelector, kf, atKeyframes } from "../tokens"; | ||
import { Config } from "../config"; | ||
@@ -21,6 +21,29 @@ import Context from "../context"; | ||
/** | ||
* Void CSS lexer — Tokenizes given Void CSS input and then, if valid, compiles it, | ||
* otherwise throws syntax error. | ||
* | ||
* Recursive and declarative design. Each case on the main switch statement represents the next step | ||
* after the given (current token). Each self call of the lex function represents what the lexer should | ||
* expect after the current token. Using attributes which live on the function recursively (as an argument), | ||
* the lexer can call itself with possibly conditional expectations using helper functions such as `_if`. | ||
* | ||
* @param ctx Compile context | ||
* @param token Current token (unless beginning) | ||
* @param attributes Makes the lexer aware of its state | ||
* @returns compiled Void CSS. see `compile` function | ||
*/ | ||
function lex(ctx: Context, token: Match | undefined, attributes?: { | ||
onError: ReturnType<typeof createErrorContext>; | ||
/** Requires enclosures to resolve such as a rule which resolve with '}' */ | ||
enclosures: TokenMatcher[]; | ||
/** The current css rule or "scope" depth (levels nested) */ | ||
depth: number; | ||
/** The depth of the :not() syntax. Required since you can nest them */ | ||
notOperatorDepth: number; | ||
}): ReturnType<typeof compile> | undefined { | ||
@@ -41,2 +64,5 @@ const { lastToken, index, css: preTokenizedCSS } = ctx; | ||
const notOperatorDepth = attributes?.notOperatorDepth ?? 0; | ||
/** The depth refers to the css rule or "scope" nesting level */ | ||
const depth = (attributes?.depth ?? 0) + (() => { | ||
@@ -66,7 +92,7 @@ // The only way to descend a depth is to close it with the `}` token `styleScopeEnd` | ||
/^\s*(?:~|>|\+|\|\|)/.test(section) ? | ||
"Selectors cannot begin with a selector combinator ('~', '>', '+', '||'). Did you forget to add left side selector combinator operand?" : | ||
"Root level selectors cannot begin with a selector combinator ('~', '>', '+', '||'). Did you forget to add left side selector combinator operand?" : | ||
"Instead found invalid or missing selector."; | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, atSymbol, whiteSpace, ...enclosures, ..._if(enclosures.length === 0, [rootSelector]), ..._if(depth > 0, [currentSelector]), classNameInitiator, selectorName, idInitiator, wildcard, notOperator), | ||
expect(slice, ...globals, atSymbol, whiteSpace, ...enclosures, ..._if(enclosures.length === 0, [rootSelector]), ..._if(depth > 0, [currentSelector]), attributeSelectorInitiator, classNameInitiator, selectorName, idInitiator, wildcard, notOperator), | ||
{ | ||
@@ -80,2 +106,3 @@ onError: createErrorContext( | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -95,2 +122,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -101,3 +129,3 @@ ); | ||
ctx, | ||
expect(slice, ...globals, notOperatorParenthesis), | ||
expect(slice, ...globals, ..._if(enclosures.length === 0, [rootSelector]), ..._if(depth > 0, [currentSelector]), notOperator, attributeSelectorInitiator, classNameInitiator, selectorName, idInitiator, wildcard), | ||
{ | ||
@@ -107,12 +135,32 @@ onError: createErrorContext( | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
ctx.index + 1, | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth: notOperatorDepth + 1, | ||
}, | ||
); | ||
case "notOperatorParenthesisEndWhiteSpace": | ||
case "notOperatorParenthesisEnd": | ||
const newDepth = notOperatorDepth - (resolvedType === "notOperatorParenthesisEnd" ? 1 : 0); | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, renameToken(whiteSpace, "notOperatorParenthesisEndWhiteSpace"), ..._if(enclosures.length === 0, [rootSelector]), ..._if(depth > 0, [currentSelector]), ..._if(newDepth > 0, [notOperatorParenthesisEnd]), colon, selectorSeparator, styleScope, attributeSelectorInitiator, classNameInitiator, selectorName, idInitiator, wildcard), | ||
{ | ||
onError: createErrorContext( | ||
newDepth === 0 ? | ||
"Unexpected token. Expected: [selector]." : | ||
"Unexpected token. Expected: ')' or [selector].", | ||
ctx.index, | ||
ctx.index + 1, | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth: newDepth, | ||
}, | ||
); | ||
case "atSymbol": | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, nestedAtRuleName), | ||
expect(slice, ...globals, ...kf(keyframes), nestedAtRuleName), | ||
{ | ||
@@ -126,4 +174,225 @@ onError: createErrorContext( | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframes": | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(selectorChild, "keyframesWhiteSpace"))), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: [whitespace].", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesWhiteSpace": | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(selectorName, "keyframesName"), keyframesSpecialNames)), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: [keyframes name].", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesSpecialNames": | ||
case "keyframesNameWhiteSpace": | ||
case "keyframesName": | ||
if (resolvedType === "keyframesName" && ["None", "initial"].includes(token.match.toString())) { | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals), | ||
{ | ||
onError: createErrorContext( | ||
`Invalid keyframes name. A keyframes name cannot be '${token.match}' as per "CSS Animations Level 2" w3c specification. You can wrap '${token.match}' in double quotes ('"') like this: '"${token.match}"' to still use this name.`, | ||
Math.max(ctx.index - token.match.toString().length, 0), | ||
ctx.index, | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
} | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(selectorChild, "keyframesNameWhiteSpace"), renameToken(styleScope, "keyframesScope"))), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: '{'.", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesScopeWhitespace": | ||
case "keyframesScope": | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(whiteSpace, "keyframesScopeWhitespace"), keyframesSelector, renameToken(styleScopeEnd, "keyframesScopeEnd"))), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: [percentage selector], 'to', 'from' or '}'.", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesBlockEndWhitespace": | ||
case "keyframesBlockEnd": | ||
return lex( | ||
ctx, | ||
expect(slice, ...kf(renameToken(whiteSpace, "keyframesBlockEndWhitespace"), keyframesSelector, renameToken(styleScopeEnd, "keyframesScopeEnd"))), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: [keyframes selector] or '}'.", | ||
ctx.index, | ||
ctx.index + 1, | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesScopeEnd": | ||
return lex( | ||
ctx, | ||
expect(slice, ...kf(renameToken(whiteSpace, "keyframesScopeEnd"), currentSelector, propertyName, attributeSelectorClose, selectorName, idInitiator, classNameInitiator, atSymbol, whiteSpaceOrNothing)), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: [css].", | ||
ctx.index, | ||
ctx.index + 1, | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesSelector": | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(whiteSpace, "keyframesSelector"), renameToken(selectorSeparator, "keyframesSeparator"), renameToken(styleScope, "keyframesBlock"))), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: ',' or '{'.", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesSeparator": | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(whiteSpace, "keyframesSeparator"), keyframesSelector)), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: [percentage selector], 'to' or 'from'.", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesBlockWhitespace": | ||
case "keyframesBlock": | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(whiteSpace, "keyframesBlockWhitespace"), renameToken(propertyName, "keyframesBlockPropertyName"), renameToken(styleScopeEnd, "keyframesScopeEnd"))), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: [property name] or '}'.", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesBlockPropertyName": | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(whiteSpace, "keyframesBlockPropertyName"), renameToken(propertyColon, "keyframesPropertyColon"))), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: ':'.", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesPropertyColon": | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(whiteSpace, "keyframesPropertyColon"), ...renameTokens([propertyValueWithSemiColon, propertyValueWithoutSemiColon], "keyframesPropertyValue"))), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: [property value]. Did you forget ';'?", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "keyframesPropertyValue": { | ||
const hasSemiColon = false; // todo: fix | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(whiteSpace, "keyframesPropertyValue"), renameToken(valueSemiColon, "keyframesValueSemiColon"), renameToken(styleScopeEnd, "keyframesBlockEnd"))), | ||
{ | ||
onError: createErrorContext( | ||
hasSemiColon ? | ||
"Unexpected token. Expected: '}'." : | ||
"Unexpected token. Expected: ';' or '}'.", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
} | ||
case "keyframesValueSemiColon": | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, ...kf(renameToken(whiteSpace, "keyframesValueSemiColon"), renameToken(propertyName, "keyframesBlockPropertyName"), renameToken(styleScopeEnd, "keyframesBlockEnd"))), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: [property name] or '}'.", | ||
ctx.index, | ||
ctx.index + resolveSelectorNameNotFound(slice), | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "nestedAtRuleName": | ||
@@ -141,2 +410,3 @@ return lex( | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -156,2 +426,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -165,3 +436,3 @@ ); | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: ID name", | ||
"Unexpected token. Expected: ID name.", | ||
ctx.index, | ||
@@ -172,2 +443,3 @@ ctx.index + resolveSelectorNameNotFound(slice), | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -178,3 +450,3 @@ ); | ||
ctx, | ||
expect(slice, ...globals, selectorChild, classNameInitiator, colon, selectorName, selectorCombinator, selectorSeparator, styleScope), | ||
expect(slice, ...globals, notOperator, selectorChild, classNameInitiator, colon, selectorName, selectorCombinator, selectorSeparator, styleScope), | ||
{ | ||
@@ -188,2 +460,3 @@ onError: createErrorContext( | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -203,2 +476,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -218,2 +492,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -233,2 +508,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -248,2 +524,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -263,2 +540,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -273,3 +551,3 @@ ); | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: [selector operand]", | ||
"Unexpected token. Expected: 'i]', 'I]', 's]', 'S]', or ']'.", | ||
ctx.index, | ||
@@ -280,2 +558,3 @@ ctx.index + resolveSelectorNameNotFound(slice), | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -286,6 +565,8 @@ ); | ||
ctx, | ||
expect(slice, ...globals, renameToken(whiteSpace, "attributeSelectorClose"), classNameInitiator, colon, selectorName, idInitiator, selectorSeparator, attributeSelectorInitiator, styleScope), | ||
expect(slice, ...globals, renameToken(whiteSpace, "attributeSelectorClose"), ..._if(notOperatorDepth > 0, [notOperatorParenthesisEnd]), notOperator, selectorCombinator, wildcard, classNameInitiator, colon, selectorName, idInitiator, selectorSeparator, attributeSelectorInitiator, styleScope), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: 'i]', 'I]', 's]', 'S]', or ']'.", | ||
notOperatorDepth === 0 ? | ||
"Unexpected token. Expected: [css selector] or '{'" : | ||
"Unexpected token. Expected: ')', [css selector] or '{'", | ||
ctx.index, | ||
@@ -296,2 +577,3 @@ ctx.index + resolveSelectorNameNotFound(slice), | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -311,2 +593,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -326,2 +609,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -332,3 +616,3 @@ ); | ||
ctx, | ||
expect(slice, ...globals, styleScope, colon, selectorChild, selectorSeparator, selectorCombinator), | ||
expect(slice, ...globals, notOperator, styleScope, colon, selectorChild, selectorSeparator, selectorCombinator), | ||
{ | ||
@@ -342,16 +626,21 @@ onError: createErrorContext( | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "combinator": | ||
const previousIsCombinator = ctx.strippedTokens?.[ctx.strippedTokens.length - 2]?.name === "combinator"; | ||
const combinatorHint = previousIsCombinator ? "Combinators ('~', '>', '+', '||') cannot directly follow previous combinator. " : ""; | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals, renameToken(whiteSpace, "combinator"), selectorName, idInitiator, attributeSelectorInitiator, classNameInitiator, wildcard), | ||
expect(slice, ...globals, renameToken(whiteSpace, "combinator"), notOperator, selectorName, idInitiator, attributeSelectorInitiator, classNameInitiator, wildcard), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token proceeding selector combinator. Expected: [css selector]. Combinators ('~', '>', '+', '||') cannot directly follow previous combinator. Did you forgot a [css selector] after a selector combinator?", | ||
`Unexpected token proceeding selector combinator. Expected: [combinator operand]. ${combinatorHint}Did you forgot a [combinator operand] after the selector combinator?`, | ||
ctx.index, | ||
ctx.index + (/\s/.exec(slice)?.index || 1), | ||
ctx.index + 1, | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -371,2 +660,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -378,3 +668,3 @@ ); | ||
ctx, | ||
expect(slice, ...globals, attributeSelectorInitiator, colon, styleScope, selectorChild, selectorSeparator, classNameInitiator, selectorCombinator), | ||
expect(slice, ...globals, notOperator, ..._if(notOperatorDepth > 0, [notOperatorParenthesisEnd]), attributeSelectorInitiator, colon, styleScope, selectorChild, selectorSeparator, classNameInitiator, selectorCombinator), | ||
{ | ||
@@ -384,9 +674,26 @@ onError: createErrorContext( | ||
ctx.index, | ||
ctx.index + (/\s/.exec(slice)?.index || 1), | ||
ctx.index + 1, | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
case "selectorSeparator": | ||
if (notOperatorDepth !== 0) { | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals), | ||
{ | ||
onError: createErrorContext( | ||
`Unexpected selector separator ','. Selector branching is not valid inside ':not(...)' and ':has(...)' operators. Did you forget '${")".repeat(Math.max(1, notOperatorDepth))}'?`, | ||
Math.max(0, ctx.index - 1), | ||
ctx.index, | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
} | ||
return lex( | ||
@@ -397,3 +704,3 @@ ctx, | ||
onError: createErrorContext( | ||
"Unexpected token following selector separator (,). Expected: [css selector].", | ||
"Unexpected token following selector separator (','). Expected: [css selector].", | ||
ctx.index, | ||
@@ -404,2 +711,3 @@ ctx.index + 1, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -419,5 +727,22 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}); | ||
case "styleScopeWhiteSpace": | ||
case "styleScope": | ||
if (notOperatorDepth !== 0) { | ||
return lex( | ||
ctx, | ||
expect(slice, ...globals), | ||
{ | ||
onError: createErrorContext( | ||
"Unexpected token in style scope. Expected: ')'. Did you forget to end your ':not(...)' or ':has(...)' operator?", | ||
Math.max(0, ctx.index - 1), | ||
ctx.index, | ||
), | ||
enclosures, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
); | ||
} | ||
return lex( | ||
@@ -434,2 +759,3 @@ ctx, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -444,3 +770,3 @@ ); | ||
onError: createErrorContext( | ||
"Unexpected token. Undefined behaviour." + (depth), | ||
"Unexpected token. Undefined behavior." + (depth), | ||
ctx.index, | ||
@@ -451,2 +777,3 @@ ctx.index + 1, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -464,2 +791,3 @@ ); | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -471,3 +799,3 @@ ); | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: property name.", | ||
"Unexpected token. Expected: [property name].", | ||
ctx.index, | ||
@@ -478,2 +806,3 @@ ctx.index + 1, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -488,3 +817,3 @@ ); | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: ';' or '}'.", | ||
"Unexpected token. Expected: [property value].", | ||
errorStartEstimate, | ||
@@ -495,2 +824,3 @@ errorStartEstimate, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -503,3 +833,3 @@ ); | ||
onError: createErrorContext( | ||
"Unexpected token. Expected: property value.", | ||
"Unexpected token. Expected: ';' or '}'.", | ||
ctx.index, | ||
@@ -510,2 +840,3 @@ ctx.index + 1, | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -516,3 +847,3 @@ ); | ||
ctx, | ||
expect(slice, ...globals, renameToken(whiteSpace, "valueSemiColon"), currentSelector, valueSemiColon, propertyName, styleScopeEnd, classNameInitiator, idInitiator, selectorName), | ||
expect(slice, ...globals, renameToken(whiteSpace, "valueSemiColon"), atKeyframes, selectorCombinator, currentSelector, notOperator, valueSemiColon, propertyName, styleScopeEnd, classNameInitiator, idInitiator, selectorName), | ||
{ | ||
@@ -526,2 +857,3 @@ onError: createErrorContext( | ||
depth, | ||
notOperatorDepth, | ||
}, | ||
@@ -528,0 +860,0 @@ ); |
@@ -21,2 +21,3 @@ "use strict"; | ||
_extensions = []; | ||
_keyframes = []; | ||
_config; | ||
@@ -38,2 +39,5 @@ classesCache = new Map(); | ||
} | ||
get keyframes() { | ||
return this._keyframes; | ||
} | ||
get strippedTokens() { | ||
@@ -70,5 +74,10 @@ return this._strippedTokens; | ||
void (this._index += match.length ?? 0); | ||
if (token.attributes?.includes("keyframes") && token.domain !== "void") { | ||
void this._tokens.push(formalizeMatch(this._index, match, token, depth)); | ||
this._keyframes.push(formalizeMatch(this._index, match, token, depth)); | ||
return this; | ||
} | ||
void this._tokens.push(formalizeMatch(this._index, match, token, depth)); | ||
if (token.domain !== "void") | ||
return (this._strippedTokens.push(formalizeMatch(this._index, match, token, depth)), this); | ||
return (void this._strippedTokens.push(formalizeMatch(this._index, match, token, depth)), this); | ||
return this; | ||
@@ -75,0 +84,0 @@ } |
@@ -26,2 +26,3 @@ import { Match } from "./tokens"; | ||
private _extensions: Extension[] = []; | ||
private _keyframes: Token[] = []; | ||
private readonly _config!: Config; | ||
@@ -44,2 +45,5 @@ public readonly classesCache = new Map<string, string>(); | ||
} | ||
get keyframes() { | ||
return this._keyframes; | ||
} | ||
get strippedTokens() { | ||
@@ -76,9 +80,17 @@ return this._strippedTokens; | ||
const match = token?.match?.[0]; | ||
if (typeof match !== "string") return this; | ||
void (this._index += match.length ?? 0); | ||
if (token.attributes?.includes("keyframes") && token.domain !== "void") { | ||
void this._tokens.push(formalizeMatch(this._index, match, token, depth)); | ||
this._keyframes.push(formalizeMatch(this._index, match, token, depth)); | ||
return this; | ||
} | ||
void this._tokens.push(formalizeMatch(this._index, match, token, depth)); | ||
if (token.domain !== "void") return ( | ||
this._strippedTokens.push(formalizeMatch(this._index, match, token, depth)), this | ||
void this._strippedTokens.push(formalizeMatch(this._index, match, token, depth)), this | ||
); | ||
return this; | ||
@@ -85,0 +97,0 @@ } |
@@ -9,7 +9,12 @@ "use strict"; | ||
function createCompilationError(ctx, error) { | ||
return `\n${highlightFormattedArea(ctx.css, error)} | ||
return `\n${highlightFormattedArea(ctx.css.replace(/\t/g, " "), error)} | ||
${chalk_1.default.yellow(`${chalk_1.default.white(chalk_1.default.bgRed("Error during Void CSS compilation:"))} ${error.message.replace(/'(.*?)'/g, (_, g) => { | ||
${chalk_1.default.yellow(`${chalk_1.default.white(chalk_1.default.bgRed("Error during Void CSS compilation:"))} ${error.message | ||
.replace(/\[([^\]]+)\]/g, (_, g) => { | ||
return chalk_1.default.grey(`[`) + chalk_1.default.bold(chalk_1.default.cyan(g)) + chalk_1.default.grey(`]`); | ||
}) | ||
.replace(/'(.*?)'/g, (_, g) => { | ||
return chalk_1.default.grey(`‘`) + chalk_1.default.bold(chalk_1.default.cyan(g)) + chalk_1.default.grey(`’`); | ||
}).replace(/(^.*?\.)/g, chalk_1.default.red("$1"))}`)}`; | ||
}) | ||
.replace(/(^.*?\.)/g, chalk_1.default.red("$1"))}`)}`; | ||
} | ||
@@ -22,5 +27,5 @@ exports.createCompilationError = createCompilationError; | ||
function resolveEndOfPropertyValue(slice) { | ||
const property = slice.substr(0, /[a-zA-Z-]+:/.exec(slice)?.index ?? slice.length); | ||
const property = slice.slice(0, /[a-zA-Z-]+:/.exec(slice)?.index ?? slice.length); | ||
const newLines = property.match(/\n/g); | ||
const lastNewLine = (newLines?.reduce(t => t + (property.substr(t).match(/\n/)?.index ?? 0) + 1, 0) ?? 1) - 1; | ||
const lastNewLine = (newLines?.reduce(t => t + (property.slice(t).match(/\n/)?.index ?? 0) + 1, 0) ?? 1) - 1; | ||
return lastNewLine || (slice.match(/\:\s*[a-zA-Z0-9_-]+/)?.length ?? 0); | ||
@@ -45,3 +50,3 @@ } | ||
function highlightFormattedArea(rawFile, { start, end }) { | ||
const file = rawFile.slice(0, start) + interpolationStartId + rawFile.slice(start, end - start) + (start === end ? "" : interpolationEndId) + rawFile.slice(end); | ||
const file = rawFile.slice(0, start) + interpolationStartId + rawFile.slice(start, end - start) + rawFile.slice(start); | ||
const n = 2; | ||
@@ -62,5 +67,5 @@ const affectedLines = { | ||
} | ||
const numbering = `${" ".repeat(Math.max((highestNumber + "").length - (i + affectedLines.start + "").length, 0))}${i + affectedLines.start - 1 + initial}| `; | ||
const numbering = `${" ".repeat(Math.max((highestNumber + "").length - (i + affectedLines.start + "").length, 0))}${i + affectedLines.start - 1 + initial} | `; | ||
const match = interpolRegex.exec(line); | ||
const composedLine = `${numbering}${line}`; | ||
const composedLine = ` ${numbering}${line}`; | ||
if (!match) | ||
@@ -73,4 +78,4 @@ return composedLine; | ||
return [ | ||
numbering + newLine, | ||
new Array(numbering.length + match.index + 1).fill("").join(" ") + chalk_1.default.red("~".repeat(repeatTilde)), | ||
`${chalk_1.default.red(">") + chalk_1.default.reset()} ${numbering}${newLine}`, | ||
new Array(numbering.length + match.index + 3).fill("").join(" ") + chalk_1.default.red("^") + chalk_1.default.red("~".repeat(repeatTilde - 1)), | ||
]; | ||
@@ -77,0 +82,0 @@ }) |
@@ -5,7 +5,12 @@ import Context from "../context"; | ||
export function createCompilationError(ctx: Context, error: ReturnType<typeof createErrorContext>) { | ||
return `\n${highlightFormattedArea(ctx.css, error)} | ||
return `\n${highlightFormattedArea(ctx.css.replace(/\t/g, " "), error)} | ||
${chalk.yellow(`${chalk.white(chalk.bgRed("Error during Void CSS compilation:"))} ${error.message.replace(/'(.*?)'/g, (_, g) => { | ||
return chalk.grey(`‘`) + chalk.bold(chalk.cyan(g)) + chalk.grey(`’`); | ||
}).replace(/(^.*?\.)/g, chalk.red("$1"))}`)}`; | ||
${chalk.yellow(`${chalk.white(chalk.bgRed("Error during Void CSS compilation:"))} ${error.message | ||
.replace(/\[([^\]]+)\]/g, (_, g) => { | ||
return chalk.grey(`[`) + chalk.bold(chalk.cyan(g)) + chalk.grey(`]`); | ||
}) | ||
.replace(/'(.*?)'/g, (_, g) => { | ||
return chalk.grey(`‘`) + chalk.bold(chalk.cyan(g)) + chalk.grey(`’`); | ||
}) | ||
.replace(/(^.*?\.)/g, chalk.red("$1"))}`)}`; | ||
} | ||
@@ -18,5 +23,5 @@ | ||
export function resolveEndOfPropertyValue(slice: string): number { | ||
const property = slice.substr(0, /[a-zA-Z-]+:/.exec(slice)?.index ?? slice.length); | ||
const property = slice.slice(0, /[a-zA-Z-]+:/.exec(slice)?.index ?? slice.length); | ||
const newLines = property.match(/\n/g); | ||
const lastNewLine = (newLines?.reduce<number>(t => t + (property.substr(t).match(/\n/)?.index ?? 0) + 1, 0) ?? 1) - 1; | ||
const lastNewLine = (newLines?.reduce<number>(t => t + (property.slice(t).match(/\n/)?.index ?? 0) + 1, 0) ?? 1) - 1; | ||
return lastNewLine || (slice.match(/\:\s*[a-zA-Z0-9_-]+/)?.length ?? 0); | ||
@@ -41,3 +46,3 @@ } | ||
function highlightFormattedArea(rawFile: string, { start, end }: ReturnType<typeof createErrorContext>) { | ||
const file = rawFile.slice(0, start) + interpolationStartId + rawFile.slice(start, end - start) + (start === end ? "" : interpolationEndId) + rawFile.slice(end); | ||
const file = rawFile.slice(0, start) + interpolationStartId + rawFile.slice(start, end - start) + rawFile.slice(start); | ||
const n = 2; | ||
@@ -48,2 +53,3 @@ const affectedLines = { | ||
} | ||
const lines = file.split(/\n/); | ||
@@ -60,5 +66,5 @@ const interpolRegex = new RegExp(`${interpolationStartId}|${interpolationEndId}`, "g"); | ||
} | ||
const numbering = `${" ".repeat(Math.max((highestNumber + "").length - (i + affectedLines.start + "").length, 0))}${i + affectedLines.start - 1 + initial}| `; | ||
const numbering = `${" ".repeat(Math.max((highestNumber + "").length - (i + affectedLines.start + "").length, 0))}${i + affectedLines.start - 1 + initial} | `; | ||
const match = interpolRegex.exec(line); | ||
const composedLine = `${numbering}${line}`; | ||
const composedLine = ` ${numbering}${line}`; | ||
if (!match) return composedLine; | ||
@@ -69,9 +75,9 @@ const newLine = line.replace(interpolRegex, start === end ? " " : ""); | ||
return [ | ||
numbering + newLine, | ||
new Array(numbering.length + match.index + 1).fill("").join(" ") + chalk.red("~".repeat(repeatTilde)), | ||
`${chalk.red(">") + chalk.reset()} ${numbering}${newLine}`, | ||
new Array(numbering.length + match.index + 3).fill("").join(" ") + chalk.red("^") + chalk.red("~".repeat(repeatTilde - 1)), | ||
]; | ||
}) | ||
.flat(2); | ||
return finalLines/* .filter(line => !/^\d*\|*\s*$/.test(line)) */.join("\n"); | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.currentSelector = exports.wildcard = exports.selectorCombinator = exports.attributeSelectorCloseWithoutOperator = exports.attributeSelectorClose = exports.attributeSelectorDoubleQuoteBody = exports.attributeSelectorDoubleQuoteInitiator = exports.attributeSelectorSingleQuoteBody = exports.attributeSelectorSingleQuoteInitiator = exports.attributeSelectorModifier = exports.attributeSelectorInitiator = exports.valueSemiColon = exports.propertyValueWithoutSemiColon = exports.propertyValueWithSemiColon = exports.propertyColon = exports.propertyName = exports.styleScopeEnd = exports.styleScope = exports.selectorName = exports.selectorChild = exports.selectorSeparator = exports.idInitiator = exports.exportClassName = exports.classNameInitiator = exports.whiteSpaceOrNothing = exports.pseudoSelector = exports.colon = exports.whiteSpace = exports.blockComment = exports.inlineComment = exports.rootSelector = exports.endStyleSheet = exports.nestedAtRule = exports.nestedAtRuleName = exports.atSymbol = exports.notOperatorParenthesis = exports.notOperator = exports.serializePropertyValues = exports.renameTokens = exports.renameToken = void 0; | ||
exports.currentSelector = exports.wildcard = exports.selectorCombinator = exports.attributeSelectorCloseWithoutOperator = exports.attributeSelectorClose = exports.attributeSelectorDoubleQuoteBody = exports.attributeSelectorDoubleQuoteInitiator = exports.attributeSelectorSingleQuoteBody = exports.attributeSelectorSingleQuoteInitiator = exports.attributeSelectorModifier = exports.attributeSelectorInitiator = exports.valueSemiColon = exports.propertyValueWithoutSemiColon = exports.propertyValueWithSemiColon = exports.propertyColon = exports.propertyName = exports.styleScopeEnd = exports.requiredWhiteSpace = exports.styleScope = exports.selectorName = exports.selectorChild = exports.selectorSeparator = exports.idInitiator = exports.exportClassName = exports.classNameInitiator = exports.whiteSpaceOrNothing = exports.pseudoSelector = exports.colon = exports.whiteSpace = exports.blockComment = exports.inlineComment = exports.rootSelector = exports.endStyleSheet = exports.nestedAtRule = exports.nestedAtRuleName = exports.keyframesSelector = exports.keyframesSpecialNames = exports.atKeyframes = exports.keyframes = exports.atSymbol = exports.notOperatorParenthesisEnd = exports.notOperatorParenthesis = exports.notOperator = exports.ifOperator = exports.serializePropertyValues = exports.kf = exports.renameTokens = exports.renameToken = void 0; | ||
/** | ||
@@ -37,2 +37,26 @@ * Renames a token. Useful for borrowing logic while preserving the current grammar branch. | ||
exports.renameTokens = renameTokens; | ||
/** | ||
* Utility to borrow logic while still restricting syntax for the special keyframes case; | ||
* where for example, nested DOM (non-keyframe) selectors are not allowed. | ||
* @param token old token | ||
* @param type new name | ||
* @returns new token with keyframes attribute | ||
*/ | ||
function kf(...tokens) { | ||
return attributeAdder("keyframes", tokens); | ||
} | ||
exports.kf = kf; | ||
function attributeAdder(attribute, tokens) { | ||
return tokens.map(token => { | ||
return (input) => { | ||
const match = token(input); | ||
if (!match) | ||
return undefined; | ||
return { | ||
...match, | ||
attributes: [...new Set(["keyframes", ...(match.attributes ?? [])])], | ||
}; | ||
}; | ||
}); | ||
} | ||
function serializePropertyValues(match) { | ||
@@ -46,2 +70,11 @@ if (!match) | ||
exports.serializePropertyValues = serializePropertyValues; | ||
function ifOperator(input) { | ||
return { | ||
type: "ifOperator", | ||
domain: "flow", | ||
attributes: [], | ||
match: input.match(/^@if/), | ||
}; | ||
} | ||
exports.ifOperator = ifOperator; | ||
function notOperator(input) { | ||
@@ -65,2 +98,11 @@ return { | ||
exports.notOperatorParenthesis = notOperatorParenthesis; | ||
function notOperatorParenthesisEnd(input) { | ||
return { | ||
type: "notOperatorParenthesisEnd", | ||
domain: "selector", | ||
attributes: [], | ||
match: input.match(/^\)/), | ||
}; | ||
} | ||
exports.notOperatorParenthesisEnd = notOperatorParenthesisEnd; | ||
function atSymbol(input) { | ||
@@ -70,3 +112,3 @@ return { | ||
domain: "selector", | ||
attributes: ["atRule"], | ||
attributes: ["keyframes"], | ||
match: input.match(/^@/), | ||
@@ -76,3 +118,40 @@ }; | ||
exports.atSymbol = atSymbol; | ||
// this intentionally does not include @import, @charset and @namespace | ||
function keyframes(input) { | ||
return { | ||
type: "keyframes", | ||
domain: "selector", | ||
attributes: ["keyframes"], | ||
match: input.match(/^keyframes/i), | ||
}; | ||
} | ||
exports.keyframes = keyframes; | ||
function atKeyframes(input) { | ||
return { | ||
type: "keyframes", | ||
domain: "selector", | ||
attributes: ["keyframes"], | ||
match: input.match(/^@keyframes/i), | ||
}; | ||
} | ||
exports.atKeyframes = atKeyframes; | ||
function keyframesSpecialNames(input) { | ||
return { | ||
type: "keyframesSpecialNames", | ||
domain: "selector", | ||
attributes: ["keyframes"], | ||
match: input.match(/^"initial"|^"None"/), | ||
}; | ||
} | ||
exports.keyframesSpecialNames = keyframesSpecialNames; | ||
/** basically a percentage, to or from */ | ||
function keyframesSelector(input) { | ||
return { | ||
type: "keyframesSelector", | ||
domain: "selector", | ||
attributes: ["keyframes"], | ||
match: input.match(/^\d+%|to|from/i), | ||
}; | ||
} | ||
exports.keyframesSelector = keyframesSelector; | ||
// this intentionally does not include @import, @charset, @keyframes (look up) and @namespace | ||
function nestedAtRuleName(input) { | ||
@@ -83,3 +162,3 @@ return { | ||
attributes: ["atRule"], | ||
match: input.match(/^(?:color-profile|counter-style|document|font-face|font-feature-values|keyframes|media|page|property|supports)/i), | ||
match: input.match(/^(?:color-profile|counter-style|document|font-face|font-feature-values|media|page|property|supports)/i), | ||
}; | ||
@@ -222,2 +301,10 @@ } | ||
exports.styleScope = styleScope; | ||
function requiredWhiteSpace(input) { | ||
return { | ||
type: "requiredWhiteSpace", | ||
domain: "void", | ||
match: input.match(/^\s+/), | ||
}; | ||
} | ||
exports.requiredWhiteSpace = requiredWhiteSpace; | ||
function styleScopeEnd(input) { | ||
@@ -345,3 +432,3 @@ return { | ||
domain: "selector", | ||
match: input.match(/(^>|^~|^\+|^\|\|)\s+/), | ||
match: input.match(/(^>|^~|^\+|^\|\|)/), | ||
}; | ||
@@ -348,0 +435,0 @@ } |
@@ -1,2 +0,2 @@ | ||
export type Domains = "selector" | "style" | "void"; | ||
export type Domains = "selector" | "style" | "void" | "flow"; | ||
export type Match = { | ||
@@ -45,3 +45,27 @@ type: string; | ||
/** | ||
* Utility to borrow logic while still restricting syntax for the special keyframes case; | ||
* where for example, nested DOM (non-keyframe) selectors are not allowed. | ||
* @param token old token | ||
* @param type new name | ||
* @returns new token with keyframes attribute | ||
*/ | ||
export function kf(...tokens: TokenMatcher[]): TokenMatcher[] { | ||
return attributeAdder("keyframes", tokens); | ||
} | ||
function attributeAdder(attribute: string, tokens: TokenMatcher[]): TokenMatcher[] { | ||
return tokens.map(token => { | ||
return (input: string) => { | ||
const match = token(input); | ||
if (!match) return undefined; | ||
return { | ||
...match, | ||
attributes: [...new Set(["keyframes", ...(match.attributes ?? [])])], | ||
}; | ||
} | ||
}); | ||
} | ||
export function serializePropertyValues(match: RegExpMatchArray | null) { | ||
@@ -53,2 +77,11 @@ if (!match) return match; | ||
export function ifOperator(input: string): Match { | ||
return { | ||
type: "ifOperator", | ||
domain: "flow", | ||
attributes: [], | ||
match: input.match(/^@if/), | ||
} | ||
} | ||
export function notOperator(input: string): Match { | ||
@@ -70,2 +103,10 @@ return { | ||
} | ||
export function notOperatorParenthesisEnd(input: string): Match { | ||
return { | ||
type: "notOperatorParenthesisEnd", | ||
domain: "selector", | ||
attributes: [], | ||
match: input.match(/^\)/), | ||
} | ||
} | ||
@@ -76,8 +117,42 @@ export function atSymbol(input: string): Match { | ||
domain: "selector", | ||
attributes: ["atRule"], | ||
attributes: ["keyframes"], | ||
match: input.match(/^@/), | ||
} | ||
} | ||
export function keyframes(input: string): Match { | ||
return { | ||
type: "keyframes", | ||
domain: "selector", | ||
attributes: ["keyframes"], | ||
match: input.match(/^keyframes/i), | ||
} | ||
} | ||
export function atKeyframes(input: string): Match { | ||
return { | ||
type: "keyframes", | ||
domain: "selector", | ||
attributes: ["keyframes"], | ||
match: input.match(/^@keyframes/i), | ||
} | ||
} | ||
export function keyframesSpecialNames(input: string): Match { | ||
return { | ||
type: "keyframesSpecialNames", | ||
domain: "selector", | ||
attributes: ["keyframes"], | ||
match: input.match(/^"initial"|^"None"/), | ||
} | ||
} | ||
// this intentionally does not include @import, @charset and @namespace | ||
/** basically a percentage, to or from */ | ||
export function keyframesSelector(input: string): Match { | ||
return { | ||
type: "keyframesSelector", | ||
domain: "selector", | ||
attributes: ["keyframes"], | ||
match: input.match(/^\d+%|to|from/i), | ||
} | ||
} | ||
// this intentionally does not include @import, @charset, @keyframes (look up) and @namespace | ||
export function nestedAtRuleName(input: string): Match { | ||
@@ -88,3 +163,3 @@ return { | ||
attributes: ["atRule"], | ||
match: input.match(/^(?:color-profile|counter-style|document|font-face|font-feature-values|keyframes|media|page|property|supports)/i), | ||
match: input.match(/^(?:color-profile|counter-style|document|font-face|font-feature-values|media|page|property|supports)/i), | ||
} | ||
@@ -221,2 +296,9 @@ } | ||
} | ||
export function requiredWhiteSpace(input: string): Match { | ||
return { | ||
type: "requiredWhiteSpace", | ||
domain: "void", | ||
match: input.match(/^\s+/), | ||
} | ||
} | ||
export function styleScopeEnd(input: string): Match { | ||
@@ -263,3 +345,3 @@ return { | ||
} | ||
export function valueSemiColon(input: string): Match { | ||
export function valueSemiColon(input: string): Match { // todo: rename to Semicolon | ||
return { | ||
@@ -333,3 +415,3 @@ type: "valueSemiColon", | ||
domain: "selector", | ||
match: input.match(/(^>|^~|^\+|^\|\|)\s+/), | ||
match: input.match(/(^>|^~|^\+|^\|\|)/), | ||
} | ||
@@ -336,0 +418,0 @@ } |
@@ -10,5 +10,6 @@ "use strict"; | ||
const compile = (0, index_1.default)(); | ||
(0, globals_1.test)("class name compilation", () => { | ||
const name = "className"; | ||
(0, globals_1.test)((0, utilts_1.buildDesc)(name), () => { | ||
(0, globals_1.expect)((0, utilts_1.stripNewLines)(compile((0, utilts_1.read)("./className/className.test.vcss"))?.static ?? "")) | ||
.toEqual((0, utilts_1.stripNewLines)((0, utilts_1.read)("./className/className.test.css"))); | ||
}); |
import VoidCSS from "../../index"; | ||
import { expect, test } from "@jest/globals"; | ||
import { read, stripNewLines } from "../utilts"; | ||
import { buildDesc, read, stripNewLines } from "../utilts"; | ||
@@ -8,7 +8,7 @@ | ||
const name = "className"; | ||
test("class name compilation", () => { | ||
test(buildDesc(name), () => { | ||
expect(stripNewLines(compile(read("./className/className.test.vcss"))?.static ?? "")) | ||
.toEqual(stripNewLines(read("./className/className.test.css"))); | ||
}); |
@@ -12,3 +12,4 @@ "use strict"; | ||
(0, globals_1.test)((0, utilts_1.buildDesc)(name), () => { | ||
console.log(compile((0, utilts_1.read)(`./${name}/${name}.test.vcss`))); | ||
const compiled = compile((0, utilts_1.read)(`./${name}/${name}.test.vcss`)); | ||
(0, globals_1.expect)(Object.keys(compiled?.classes ?? {})).toStrictEqual(["testClass"]); | ||
}); |
@@ -13,4 +13,6 @@ import VoidCSS from "../../index"; | ||
console.log(compile(read(`./${name}/${name}.test.vcss`))); | ||
const compiled = compile(read(`./${name}/${name}.test.vcss`)); | ||
expect(Object.keys(compiled?.classes ?? {})).toStrictEqual(["testClass"]) | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
205555
81
4292