206 lines
5.3 KiB
JavaScript
206 lines
5.3 KiB
JavaScript
'use strict';
|
|
|
|
const css_parser_error = require('./error.cjs');
|
|
const css_parser_strings = require('./strings.cjs');
|
|
const css_parser_text = require('./text.cjs');
|
|
|
|
function findTokens(code, tokens) {
|
|
const list = [];
|
|
const lc = code.toLowerCase();
|
|
tokens.forEach((token) => {
|
|
let index = 0;
|
|
while (true) {
|
|
index = lc.indexOf(token, index);
|
|
if (index === -1) {
|
|
return;
|
|
}
|
|
list.push({
|
|
token,
|
|
index
|
|
});
|
|
index++;
|
|
}
|
|
});
|
|
list.sort((a, b) => a.index - b.index);
|
|
return list;
|
|
}
|
|
function getTokens(css) {
|
|
const items = [];
|
|
let textQueue = [];
|
|
let start = 0;
|
|
let depth = 0;
|
|
try {
|
|
const checkRule = (text) => {
|
|
if (!textQueue.length) {
|
|
return;
|
|
}
|
|
const item = css_parser_text.textTokensToRule(textQueue);
|
|
if (item) {
|
|
items.push(item);
|
|
return;
|
|
}
|
|
const value = css_parser_text.mergeTextTokens(textQueue) + text;
|
|
if (!value.length) {
|
|
return;
|
|
}
|
|
throw css_parser_error.styleParseError("Invalid css rule", css, textQueue[0]?.index);
|
|
};
|
|
findTokens(css, ['"', "'", "/*", "{", "}", ";", "url(", "\\"]).forEach(
|
|
(token) => {
|
|
if (token.index < start) {
|
|
return;
|
|
}
|
|
switch (token.token) {
|
|
case "/*": {
|
|
textQueue.push({
|
|
type: "chunk",
|
|
text: css.slice(start, token.index),
|
|
index: start
|
|
});
|
|
start = token.index;
|
|
const end = css.indexOf("*/", start + 2);
|
|
if (end === -1) {
|
|
throw css_parser_error.styleParseError(
|
|
"Missing comment closing statement",
|
|
css,
|
|
start
|
|
);
|
|
}
|
|
start = end + 2;
|
|
break;
|
|
}
|
|
case "\\":
|
|
textQueue.push({
|
|
type: "chunk",
|
|
text: css.slice(start, token.index + 2),
|
|
index: start
|
|
});
|
|
start = token.index + 2;
|
|
break;
|
|
case "url(": {
|
|
textQueue.push({
|
|
type: "chunk",
|
|
text: css.slice(start, token.index),
|
|
index: start
|
|
});
|
|
start = token.index;
|
|
const end = css_parser_strings.findEndOfURL(css, start);
|
|
if (typeof end !== "number") {
|
|
throw end;
|
|
}
|
|
textQueue.push({
|
|
type: "url",
|
|
text: css.slice(start, end),
|
|
index: start
|
|
});
|
|
start = end;
|
|
break;
|
|
}
|
|
case '"':
|
|
case "'": {
|
|
textQueue.push({
|
|
type: "chunk",
|
|
text: css.slice(start, token.index),
|
|
index: start
|
|
});
|
|
start = token.index;
|
|
const end = css_parser_strings.findEndOfQuotedString(
|
|
css,
|
|
token.token,
|
|
start
|
|
);
|
|
if (end === null) {
|
|
throw css_parser_error.styleParseError(
|
|
"Missing closing " + token.token,
|
|
css,
|
|
start
|
|
);
|
|
}
|
|
textQueue.push({
|
|
type: "quoted-string",
|
|
text: css.slice(start, end),
|
|
index: start
|
|
});
|
|
start = end;
|
|
break;
|
|
}
|
|
case ";": {
|
|
textQueue.push({
|
|
type: "chunk",
|
|
text: css.slice(start, token.index),
|
|
index: start
|
|
});
|
|
checkRule(token.token);
|
|
start = token.index + 1;
|
|
textQueue = [];
|
|
break;
|
|
}
|
|
case "{": {
|
|
textQueue.push({
|
|
type: "chunk",
|
|
text: css.slice(start, token.index),
|
|
index: start
|
|
});
|
|
const item = css_parser_text.textTokensToSelector(textQueue);
|
|
if (!item) {
|
|
throw css_parser_error.styleParseError(
|
|
"Invalid css rule",
|
|
css,
|
|
start
|
|
);
|
|
}
|
|
items.push(item);
|
|
start = token.index + 1;
|
|
textQueue = [];
|
|
depth++;
|
|
break;
|
|
}
|
|
case "}": {
|
|
textQueue.push({
|
|
type: "chunk",
|
|
text: css.slice(start, token.index),
|
|
index: start
|
|
});
|
|
checkRule("");
|
|
items.push({
|
|
type: "close",
|
|
index: token.index
|
|
});
|
|
if (!depth) {
|
|
throw css_parser_error.styleParseError(
|
|
"Unexpected }",
|
|
css,
|
|
token.index
|
|
);
|
|
}
|
|
depth--;
|
|
start = token.index + 1;
|
|
textQueue = [];
|
|
break;
|
|
}
|
|
default:
|
|
throw new Error(
|
|
`Forgot to parse token: ${token.token}`
|
|
);
|
|
}
|
|
}
|
|
);
|
|
if (depth) {
|
|
return css_parser_error.styleParseError("Missing }", css);
|
|
}
|
|
textQueue.push({
|
|
type: "chunk",
|
|
text: css.slice(start),
|
|
index: start
|
|
});
|
|
checkRule("");
|
|
} catch (err) {
|
|
if (typeof err === "object" && err.type === "style-parse-error") {
|
|
return err;
|
|
}
|
|
throw err;
|
|
}
|
|
return items;
|
|
}
|
|
|
|
exports.getTokens = getTokens;
|