You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
7.4 KiB
209 lines
7.4 KiB
|
2 years ago
|
"use strict";
|
||
|
|
|
||
|
|
Object.defineProperty(exports, "__esModule", {
|
||
|
|
value: true
|
||
|
|
});
|
||
|
|
exports["default"] = findCommentsInRaws;
|
||
|
|
/**
|
||
|
|
* Finds comments, both CSS comments and double slash ones, in a CSS string
|
||
|
|
* This helper exists because PostCSS drops some inline comments (those
|
||
|
|
* between selectors, property values, etc.)
|
||
|
|
* https://github.com/postcss/postcss/issues/845#issuecomment-232306259
|
||
|
|
*
|
||
|
|
* @param {string} rawString -- the source raw CSS string
|
||
|
|
* @return {array} array of objects with these props:
|
||
|
|
* � type -- "css" or "double-slash"
|
||
|
|
* � source: { start, end }
|
||
|
|
* IMPORTANT: the function itself considers \r as a character, and counts
|
||
|
|
* it for `start` and `end`. But if their values are passed to PostCSS's
|
||
|
|
* result.warn(), than "\r\n" is consideren ONE CHAR (newline)!
|
||
|
|
* � raws
|
||
|
|
* raws.startToken -- `/*`, `/**`, `/**!`, etc.
|
||
|
|
* raws.left -- whitespace after the comment opening marker
|
||
|
|
* raws.text -- the full comment, including markers (//, /*)
|
||
|
|
* raws.right -- whitespace before the comment closing marker
|
||
|
|
* raws.endToken -- `*\/`, `**\/` for CSS comments
|
||
|
|
* � text -- the comment text only, excluding //, /*, trailing whitespaces
|
||
|
|
* � inlineAfter -- true, if there is something before the comment on
|
||
|
|
* the same line
|
||
|
|
* � inlineBefore -- true, if there is something after the comment on
|
||
|
|
* the same line
|
||
|
|
*/
|
||
|
|
|
||
|
|
function findCommentsInRaws(rawString) {
|
||
|
|
var result = [];
|
||
|
|
var comment = {};
|
||
|
|
// Keeps track of which structure the parser is inside (string, comment,
|
||
|
|
// url function, parens). E.g., /* comment */ inside a string doesn't
|
||
|
|
// constitute a comment, so as url(//path)
|
||
|
|
var modesEntered = [{
|
||
|
|
mode: "normal",
|
||
|
|
character: null
|
||
|
|
}];
|
||
|
|
var commentStart = null;
|
||
|
|
|
||
|
|
// postcss-scss transforms //-comments into CSS comments, like so:
|
||
|
|
// `// comment` -> `/* comment*/`. So to have a correct intex we need to
|
||
|
|
// keep track on the added `*/` sequences
|
||
|
|
var offset = 0;
|
||
|
|
for (var i = 0; i < rawString.length; i++) {
|
||
|
|
var character = rawString[i];
|
||
|
|
var prevChar = i > 0 ? rawString[i - 1] : null;
|
||
|
|
var nextChar = i + 1 < rawString.length ? rawString[i + 1] : null;
|
||
|
|
var lastModeIndex = modesEntered.length - 1;
|
||
|
|
var mode = modesEntered[lastModeIndex] && modesEntered[lastModeIndex].mode;
|
||
|
|
switch (character) {
|
||
|
|
// If entering/exiting a string
|
||
|
|
case '"':
|
||
|
|
case "'":
|
||
|
|
{
|
||
|
|
if (mode === "comment") {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (mode === "string" && modesEntered[lastModeIndex].character === character && prevChar !== "\\") {
|
||
|
|
// Exiting a string
|
||
|
|
modesEntered.pop();
|
||
|
|
} else {
|
||
|
|
// Entering a string
|
||
|
|
modesEntered.push({
|
||
|
|
mode: "string",
|
||
|
|
character: character
|
||
|
|
});
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// Entering url, other function or parens (only url matters)
|
||
|
|
case "(":
|
||
|
|
{
|
||
|
|
if (mode === "comment" || mode === "string") {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
var functionNameRegSearch = /(?:^|[\n\r]|\s-|[:\s,.(){}*+/%])([\w-]*)$/.exec(rawString.substring(0, i));
|
||
|
|
|
||
|
|
// A `\S(` can be in, say, `@media(`
|
||
|
|
if (!functionNameRegSearch) {
|
||
|
|
modesEntered.push({
|
||
|
|
mode: "parens",
|
||
|
|
character: "("
|
||
|
|
});
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
var functionName = functionNameRegSearch[1];
|
||
|
|
modesEntered.push({
|
||
|
|
mode: functionName === "url" ? "url" : "parens",
|
||
|
|
character: "("
|
||
|
|
});
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// Exiting url, other function or parens
|
||
|
|
case ")":
|
||
|
|
{
|
||
|
|
if (mode === "comment" || mode === "string") {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
modesEntered.pop();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// checking for comment
|
||
|
|
case "/":
|
||
|
|
{
|
||
|
|
// Break if the / is inside a comment because we leap over the second
|
||
|
|
// slash in // and in */, so the / is not from a marker. Also break
|
||
|
|
// if inside a string
|
||
|
|
if (mode === "comment" || mode === "string") {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (nextChar === "*") {
|
||
|
|
modesEntered.push({
|
||
|
|
mode: "comment",
|
||
|
|
character: "/*"
|
||
|
|
});
|
||
|
|
comment = {
|
||
|
|
type: "css",
|
||
|
|
source: {
|
||
|
|
start: i + offset
|
||
|
|
},
|
||
|
|
// If i is 0 then the file/the line starts with this comment
|
||
|
|
inlineAfter: i > 0 && rawString.substring(0, i).search(/\n\s*$/) === -1
|
||
|
|
};
|
||
|
|
commentStart = i;
|
||
|
|
// Skip the next iteration as the * is already checked
|
||
|
|
i++;
|
||
|
|
} else if (nextChar === "/") {
|
||
|
|
// `url(//path/to/file)` has no comment
|
||
|
|
if (mode === "url") {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
modesEntered.push({
|
||
|
|
mode: "comment",
|
||
|
|
character: "//"
|
||
|
|
});
|
||
|
|
comment = {
|
||
|
|
type: "double-slash",
|
||
|
|
source: {
|
||
|
|
start: i + offset
|
||
|
|
},
|
||
|
|
// If i is 0 then the file/the line starts with this comment
|
||
|
|
inlineAfter: i > 0 && rawString.substring(0, i).search(/\n\s*$/) === -1
|
||
|
|
};
|
||
|
|
commentStart = i;
|
||
|
|
// Skip the next iteration as the second slash in // is already checked
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
// Might be a closing `*/`
|
||
|
|
case "*":
|
||
|
|
{
|
||
|
|
if (mode === "comment" && modesEntered[lastModeIndex].character === "/*" && nextChar === "/") {
|
||
|
|
comment.source.end = i + 1 + offset;
|
||
|
|
var commentRaw = rawString.substring(commentStart, i + 2);
|
||
|
|
var matches = /^(\/\*+[!#]?)(\s*)([\s\S]*?)(\s*)(\*+\/)$/.exec(commentRaw);
|
||
|
|
modesEntered.pop();
|
||
|
|
comment.raws = {
|
||
|
|
startToken: matches[1],
|
||
|
|
left: matches[2],
|
||
|
|
text: commentRaw,
|
||
|
|
right: matches[4],
|
||
|
|
endToken: matches[5]
|
||
|
|
};
|
||
|
|
comment.text = matches[3];
|
||
|
|
comment.inlineBefore = rawString.substring(i + 2).search(/^\s*\S+\s*?\n/) !== -1;
|
||
|
|
result.push(Object.assign({}, comment));
|
||
|
|
comment = {};
|
||
|
|
// Skip the next loop as the / in */ is already checked
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
{
|
||
|
|
var isNewline = character === "\r" && rawString[i + 1] === "\n" || character === "\n" && rawString[i - 1] !== "\r";
|
||
|
|
|
||
|
|
// //-comments end before newline and if the code string ends
|
||
|
|
if (isNewline || i === rawString.length - 1) {
|
||
|
|
if (mode === "comment" && modesEntered[lastModeIndex].character === "//") {
|
||
|
|
comment.source.end = (isNewline ? i - 1 : i) + offset;
|
||
|
|
var _commentRaw = rawString.substring(commentStart, isNewline ? i : i + 1);
|
||
|
|
var _matches = /^(\/+)(\s*)(.*?)(\s*)$/.exec(_commentRaw);
|
||
|
|
modesEntered.pop();
|
||
|
|
comment.raws = {
|
||
|
|
startToken: _matches[1],
|
||
|
|
left: _matches[2],
|
||
|
|
text: _commentRaw,
|
||
|
|
right: _matches[4]
|
||
|
|
};
|
||
|
|
comment.text = _matches[3];
|
||
|
|
comment.inlineBefore = false;
|
||
|
|
result.push(Object.assign({}, comment));
|
||
|
|
comment = {};
|
||
|
|
// Compensate for the `*/` added by postcss-scss
|
||
|
|
offset += 2;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|