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.
202 lines
4.0 KiB
202 lines
4.0 KiB
"use strict";
|
|
|
|
const Input = require("postcss/lib/input");
|
|
const Document = require("./document");
|
|
|
|
const reNewLine = /\r?\n|\r/g;
|
|
|
|
class Locations {
|
|
constructor(source) {
|
|
let match;
|
|
const lines = [];
|
|
reNewLine.lastIndex = 0;
|
|
while ((match = reNewLine.exec(source))) {
|
|
lines.push(match.index);
|
|
}
|
|
lines.push(source.length);
|
|
|
|
this.lines = lines;
|
|
this.source = source;
|
|
}
|
|
|
|
getOffsetFromLoc(loc) {
|
|
const lineIndex = loc.line - 2;
|
|
return (lineIndex >= 0 ? this.lines[lineIndex] : -1) + loc.column;
|
|
}
|
|
|
|
getLocFromOffset(offset) {
|
|
const lines = this.lines;
|
|
for (let index = 0; index < lines.length; index++) {
|
|
const lineEndIndex = lines[index];
|
|
if (lineEndIndex >= offset) {
|
|
const before = this.lines[index - 1];
|
|
return {
|
|
line: index + 1,
|
|
column: offset - (before != null ? before : -1),
|
|
};
|
|
}
|
|
}
|
|
const before = this.lines[this.lines.length - 2];
|
|
return {
|
|
line: lines.length,
|
|
column: offset - (before != null ? before : -1),
|
|
};
|
|
}
|
|
}
|
|
|
|
class LocalFixer {
|
|
constructor(locations, style) {
|
|
const { line, column } = locations.getLocFromOffset(style.startIndex);
|
|
this.line = line - 1;
|
|
this.column = column - 1;
|
|
this.style = style;
|
|
}
|
|
|
|
fixLocation(object) {
|
|
if (object) {
|
|
if (object.line === 1) {
|
|
object.column += this.column;
|
|
}
|
|
object.line += this.line;
|
|
if (typeof object.offset === "number") {
|
|
object.offset += this.style.startIndex;
|
|
}
|
|
if (typeof object.endLine === "number") {
|
|
if (object.endLine === 1) {
|
|
object.endColumn += this.column;
|
|
}
|
|
object.endLine += this.line;
|
|
}
|
|
}
|
|
}
|
|
|
|
node(node) {
|
|
this.fixLocation(node.source.start);
|
|
this.fixLocation(node.source.end);
|
|
}
|
|
|
|
root(root) {
|
|
this.node(root);
|
|
root.walk((node) => {
|
|
this.node(node);
|
|
});
|
|
}
|
|
|
|
error(error) {
|
|
if (error && error.name === "CssSyntaxError") {
|
|
this.fixLocation(error);
|
|
this.fixLocation(error.input);
|
|
error.message = error.message.replace(
|
|
/:\d+:\d+:/,
|
|
`:${error.line}:${error.column}:`
|
|
);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
parse(opts) {
|
|
const style = this.style;
|
|
const syntax = style.syntax;
|
|
let root;
|
|
try {
|
|
root = syntax.parse(
|
|
style.content,
|
|
Object.assign(
|
|
{},
|
|
opts,
|
|
{
|
|
map: false,
|
|
},
|
|
style.opts
|
|
)
|
|
);
|
|
} catch (error) {
|
|
this.error(error);
|
|
|
|
throw error;
|
|
}
|
|
this.root(root);
|
|
|
|
root.source.inline = Boolean(style.inline);
|
|
root.source.lang = style.lang;
|
|
root.source.syntax = syntax;
|
|
patchRoot(root, syntax);
|
|
return root;
|
|
}
|
|
}
|
|
|
|
function docFixer(source, opts) {
|
|
const locations = new Locations(source);
|
|
return function parseStyle(style) {
|
|
return new LocalFixer(locations, style).parse(opts);
|
|
};
|
|
}
|
|
|
|
function parseStyles(source, opts, styles) {
|
|
const document = new Document();
|
|
|
|
let index = 0;
|
|
if (styles.length) {
|
|
const parseStyle = docFixer(source, opts);
|
|
styles
|
|
.sort((a, b) => a.startIndex - b.startIndex)
|
|
.forEach((style) => {
|
|
const root = parseStyle(style);
|
|
if (root) {
|
|
root.raws.codeBefore = source.slice(index, style.startIndex);
|
|
|
|
// Note: Stylelint is still using this property.
|
|
try {
|
|
Object.defineProperty(root.raws, "beforeStart", {
|
|
configurable: true,
|
|
get() {
|
|
return root.raws.codeBefore;
|
|
},
|
|
set(value) {
|
|
root.raws.codeBefore = value;
|
|
},
|
|
});
|
|
} catch {
|
|
// ignore
|
|
}
|
|
|
|
index =
|
|
style.startIndex + (style.content || root.source.input.css).length;
|
|
|
|
root.document = document;
|
|
document.nodes.push(root);
|
|
}
|
|
});
|
|
}
|
|
|
|
const last = document.nodes[document.nodes.length - 1];
|
|
if (last) {
|
|
last.raws.codeAfter = index ? source.slice(index) : source;
|
|
}
|
|
document.source = {
|
|
input: new Input(source, opts),
|
|
start: {
|
|
line: 1,
|
|
column: 1,
|
|
},
|
|
opts,
|
|
};
|
|
return document;
|
|
}
|
|
|
|
module.exports = parseStyles;
|
|
|
|
function patchRoot(root, syntax) {
|
|
const originalToString = root.toString;
|
|
try {
|
|
Object.defineProperty(root, "toString", {
|
|
configurable: true,
|
|
enumerable: false,
|
|
value(stringifier) {
|
|
return originalToString.call(this, stringifier || syntax);
|
|
},
|
|
});
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
|