securityos/node_modules/postcss-styled-syntax/lib/parser.js

473 lines
10 KiB
JavaScript
Raw Normal View History

2024-09-06 15:32:35 +00:00
// @ts-nocheck
'use strict';
let { Declaration, Rule } = require('postcss');
let PostCSSParser = require('postcss/lib/parser');
let tokenizer = require('./tokenizer');
// eslint-disable-next-line consistent-return
function findLastWithPosition(tokens) {
for (let i = tokens.length - 1; i >= 0; i--) {
let token = tokens[i];
let pos = token[3] || token[2];
if (pos) return pos;
}
}
class Parser extends PostCSSParser {
constructor(input, options) {
super(input);
this.interpolations = options.interpolations;
this.createTokenizer(options);
this.root.source = {
input,
start: {
offset: options.raws.rangeStart,
line: options.raws.locationStart.line,
column: options.raws.locationStart.column,
},
};
this.root.raws.styledSyntaxRangeStart = options.raws.rangeStart;
this.root.raws.styledSyntaxRangeEnd = options.raws.rangeEnd;
// Add a flag to enable PostCSS and Stylelint to adjust to CSS-in-JS quirks
// E. g. if something processes only rules, it could also process `root` if this flag is present
this.root.raws.isRuleLike = true;
if (options.isComponent) {
this.root.raws.styledSyntaxIsComponent = true;
} else {
this.root.raws.styledOriginalContent = options.raws.originalContent;
}
}
createTokenizer(tokenizerOptions) {
this.tokenizer = tokenizer(this.input, tokenizerOptions);
}
parse() {
// STYLED PATCH The only difference from PostCSS parser is try-catch wrap
// Catching errors here to catch errors from both parser and tokenizer and fix reported error positions
try {
let token;
while (!this.tokenizer.endOfFile()) {
token = this.tokenizer.nextToken();
switch (token[0]) {
case 'space':
this.spaces += token[1];
break;
case ';':
this.freeSemicolon(token);
break;
case '}':
this.end(token);
break;
case 'comment':
this.comment(token);
break;
case 'at-word':
this.atrule(token);
break;
case '{':
this.emptyRule(token);
break;
default:
this.other(token);
break;
}
}
this.endFile();
} catch (error) {
throw this.fixErrorPosition(error);
}
}
other(start) {
let end = false;
let type = null;
let colon = false;
let bracket = null;
let brackets = [];
let customProperty = start[1].startsWith('--');
let tokens = [];
let token = start;
while (token) {
type = token[0];
tokens.push(token);
if (type === '(' || type === '[') {
if (!bracket) bracket = token;
brackets.push(type === '(' ? ')' : ']');
} else if (customProperty && colon && type === '{') {
if (!bracket) bracket = token;
brackets.push('}');
} else if (brackets.length === 0) {
if (type === ';') {
if (colon) {
this.decl(tokens, customProperty);
return;
}
break;
} else if (type === '{') {
this.rule(tokens);
return;
} else if (type === '}') {
this.tokenizer.back(tokens.pop());
end = true;
break;
} else if (type === ':') {
colon = true;
// STYLED PATCH {
} else if (type === 'at-word') {
this.tokenizer.back(tokens.pop());
end = true;
break;
// } STYLED PATCH
}
} else if (type === brackets[brackets.length - 1]) {
brackets.pop();
if (brackets.length === 0) bracket = null;
}
token = this.tokenizer.nextToken();
}
if (this.tokenizer.endOfFile()) end = true;
if (brackets.length > 0) this.unclosedBracket(bracket);
if (end && colon) {
if (!customProperty) {
while (tokens.length) {
token = tokens[tokens.length - 1][0];
if (token !== 'space' && token !== 'comment') break;
this.tokenizer.back(tokens.pop());
}
}
this.decl(tokens, customProperty);
// STYLED PATCH {
return;
// } STYLED PATCH
}
// STYLED PATCH {
for (token of tokens) {
if (this.isInterpolation(token) || token[0] === 'space' || token[0] === ';') {
this.spaces += token[1];
} else if (token[0] === 'comment') {
this.comment(token);
} else {
this.unknownWord([token]);
}
}
// } STYLED PATCH
}
rule(tokens) {
// Removes {
tokens.pop();
// STYLED PATCH {
this.spaces += this.spacesAndInterpolationsFromStart(tokens);
// } STYLED PATCH
let node = new Rule();
this.init(node, tokens[0][2]);
node.raws.between = this.spacesAndCommentsFromEnd(tokens);
this.raw(node, 'selector', tokens);
this.current = node;
}
decl(tokens, customProperty) {
let node = new Declaration();
this.init(node, tokens[0][2]);
let last = tokens[tokens.length - 1];
if (last[0] === ';') {
this.semicolon = true;
tokens.pop();
}
node.source.end = this.getPosition(last[3] || last[2] || findLastWithPosition(tokens));
// STYLED PATCH {
// Add all “lose” words to node raws
while (
tokens[0][0] !== 'word' ||
(this.isInterpolation(tokens[0]) && tokens[1][0] === 'space') ||
(this.isInterpolation(tokens[0]) && this.isInterpolation(tokens[1]))
) {
// } STYLED PATCH
if (tokens.length === 1) this.unknownWord(tokens);
node.raws.before += tokens.shift()[1];
}
node.source.start = this.getPosition(tokens[0][2]);
node.prop = '';
while (tokens.length) {
let type = tokens[0][0];
if (type === ':' || type === 'space' || type === 'comment') {
break;
}
node.prop += tokens.shift()[1];
}
node.raws.between = '';
let token;
while (tokens.length) {
token = tokens.shift();
if (token[0] === ':') {
node.raws.between += token[1];
break;
} else {
if (token[0] === 'word' && /\w/.test(token[1])) {
this.unknownWord([token]);
}
node.raws.between += token[1];
}
}
if (node.prop[0] === '_' || node.prop[0] === '*') {
node.raws.before += node.prop[0];
node.prop = node.prop.slice(1);
}
let firstSpaces = [];
let next;
while (tokens.length) {
next = tokens[0][0];
if (next !== 'space' && next !== 'comment') break;
firstSpaces.push(tokens.shift());
}
this.precheckMissedSemicolon(tokens);
for (let i = tokens.length - 1; i >= 0; i--) {
token = tokens[i];
if (token[1].toLowerCase() === '!important') {
node.important = true;
let string = this.stringFrom(tokens, i);
string = this.spacesFromEnd(tokens) + string;
if (string !== ' !important') node.raws.important = string;
break;
} else if (token[1].toLowerCase() === 'important') {
let cache = [...tokens];
let str = '';
for (let j = i; j > 0; j--) {
let type = cache[j][0];
if (str.trim().indexOf('!') === 0 && type !== 'space') {
break;
}
str = cache.pop()[1] + str;
}
if (str.trim().indexOf('!') === 0) {
node.important = true;
node.raws.important = str;
tokens = cache; // eslint-disable-line no-param-reassign
}
}
if (token[0] !== 'space' && token[0] !== 'comment') {
break;
}
}
let hasWord = tokens.some((i) => i[0] !== 'space' && i[0] !== 'comment');
if (hasWord) {
node.raws.between += firstSpaces.map((i) => i[1]).join('');
firstSpaces = [];
}
this.raw(node, 'value', firstSpaces.concat(tokens), customProperty); // eslint-disable-line unicorn/prefer-spread
if (node.value.includes(':') && !customProperty) {
this.checkMissedSemicolon(tokens);
}
}
// Helpers
fixLine(line) {
return this.root.source.start.line - 1 + line;
}
fixColumn(line, column) {
let isSameLineAsRootStart = line === 1;
return isSameLineAsRootStart ? this.root.source.start.column - 1 + column : column;
}
unfixPosition(position) {
return {
offset: position.offset - this.root.source.start.offset,
line: position.line - this.root.source.start.line + 1,
column:
position.line === this.root.source.start.line
? position.column - this.root.source.start.column + 1
: position.column,
};
}
getPosition(offset) {
let pos = this.input.fromOffset(offset);
// STYLED PATCH {
return {
offset: this.root.source.start.offset + offset,
line: this.fixLine(pos.line),
column: this.fixColumn(pos.line, pos.col),
};
// } STYLED PATCH
}
isInterpolation(token) {
if (token[0] !== 'word') {
return false;
}
return this.interpolations?.some(
(item) => item.start === token[2] && item.end === token[3]
);
}
spacesAndInterpolationsFromStart(tokens) {
let spaces = '';
let removedTokens = [];
let hasInterpolation = false;
let tokensLength = tokens.length;
let index;
for (index = 0; index < tokensLength; index++) {
let next = tokens.shift();
spaces += next[1];
removedTokens.push(next);
if (index === 0 && this.isInterpolation(next)) {
hasInterpolation = true;
}
if (next[0] === 'space' && next[1].includes('\n')) {
break;
}
}
// If all tokens were cycled through, then it means there were no interpolation on a separate line
if (index === tokensLength || !hasInterpolation) {
tokens.unshift(...removedTokens);
return '';
}
return spaces;
}
// Errors
fixErrorPosition(rawError) {
let error = rawError;
if (error.name === 'CssSyntaxError') {
if (error.line) {
error.line = this.fixLine(error.line);
if (error.column) {
error.column = this.fixColumn(error.line, error.column);
}
}
if (error.endLine) {
error.endLine = this.fixLine(error.endLine);
if (error.endColumn) {
error.endColumn = this.fixColumn(error.endLine, error.endColumn);
}
}
if (error.input) {
if (error.input.line) {
error.input.line = this.fixLine(error.input.line);
if (error.input.column) {
error.input.column = this.fixColumn(error.input.line, error.input.column);
}
}
if (error.input.endLine) {
error.input.endLine = this.fixLine(error.input.endLine);
if (error.input.endColumn) {
error.input.endColumn = this.fixColumn(
error.input.endLine,
error.input.endColumn
);
}
}
}
error.message = error.message.replace(/:\d+:\d+:/, `:${error.line}:${error.column}:`);
}
throw error;
}
unclosedBlock() {
// STYLED PATCH {
let pos = this.unfixPosition(this.current.source.start);
// } STYLED PATCH
throw this.input.error('Unclosed block', pos.line, pos.column);
}
}
module.exports = Parser;