@mapcss/preset-svg
Version:
SVG as CSS for MapCSS
316 lines (315 loc) • 10.8 kB
JavaScript
import { Buffer } from "./deps.js";
import { SourceMapConsumer, SourceMapGenerator } from "./source_map.js";
import { dirname, relative, resolve, sep } from "./deps.js";
import { pathToFileURL } from "./deps.js";
import Input from "./input.js";
let sourceMapAvailable = Boolean(SourceMapConsumer && SourceMapGenerator);
let pathAvailable = Boolean(dirname && resolve && relative && sep);
class MapGenerator {
constructor(stringify, root, opts, cssString) {
this.stringify = stringify;
this.mapOpts = opts.map || {};
this.root = root;
this.opts = opts;
this.css = cssString;
}
isMap() {
if (typeof this.opts.map !== "undefined") {
return !!this.opts.map;
}
return this.previous().length > 0;
}
previous() {
if (!this.previousMaps) {
this.previousMaps = [];
if (this.root) {
this.root.walk((node) => {
if (node.source && node.source.input.map) {
let map = node.source.input.map;
if (!this.previousMaps.includes(map)) {
this.previousMaps.push(map);
}
}
});
}
else {
let input = new Input(this.css, this.opts);
if (input.map)
this.previousMaps.push(input.map);
}
}
return this.previousMaps;
}
isInline() {
if (typeof this.mapOpts.inline !== "undefined") {
return this.mapOpts.inline;
}
let annotation = this.mapOpts.annotation;
if (typeof annotation !== "undefined" && annotation !== true) {
return false;
}
if (this.previous().length) {
return this.previous().some((i) => i.inline);
}
return true;
}
isSourcesContent() {
if (typeof this.mapOpts.sourcesContent !== "undefined") {
return this.mapOpts.sourcesContent;
}
if (this.previous().length) {
return this.previous().some((i) => i.withContent());
}
return true;
}
clearAnnotation() {
if (this.mapOpts.annotation === false)
return;
if (this.root) {
let node;
for (let i = this.root.nodes.length - 1; i >= 0; i--) {
node = this.root.nodes[i];
if (node.type !== "comment")
continue;
if (node.text.indexOf("# sourceMappingURL=") === 0) {
this.root.removeChild(i);
}
}
}
else if (this.css) {
this.css = this.css.replace(/(\n)?\/\*#[\S\s]*?\*\/$/gm, "");
}
}
setSourcesContent() {
let already = {};
if (this.root) {
this.root.walk((node) => {
if (node.source) {
let from = node.source.input.from;
if (from && !already[from]) {
already[from] = true;
this.map.setSourceContent(this.toUrl(this.path(from)), node.source.input.css);
}
}
});
}
else if (this.css) {
let from = this.opts.from
? this.toUrl(this.path(this.opts.from))
: "<no source>";
this.map.setSourceContent(from, this.css);
}
}
applyPrevMaps() {
for (let prev of this.previous()) {
let from = this.toUrl(this.path(prev.file));
let root = prev.root || dirname(prev.file);
let map;
if (this.mapOpts.sourcesContent === false) {
map = new SourceMapConsumer(prev.text);
if (map.sourcesContent) {
map.sourcesContent = map.sourcesContent.map(() => null);
}
}
else {
map = prev.consumer();
}
this.map.applySourceMap(map, from, this.toUrl(this.path(root)));
}
}
isAnnotation() {
if (this.isInline()) {
return true;
}
if (typeof this.mapOpts.annotation !== "undefined") {
return this.mapOpts.annotation;
}
if (this.previous().length) {
return this.previous().some((i) => i.annotation);
}
return true;
}
toBase64(str) {
if (Buffer) {
return Buffer.from(str).toString("base64");
}
else {
return globalThis.btoa(unescape(encodeURIComponent(str)));
}
}
addAnnotation() {
let content;
if (this.isInline()) {
content = "data:application/json;base64," +
this.toBase64(this.map.toString());
}
else if (typeof this.mapOpts.annotation === "string") {
content = this.mapOpts.annotation;
}
else if (typeof this.mapOpts.annotation === "function") {
content = this.mapOpts.annotation(this.opts.to, this.root);
}
else {
content = this.outputFile() + ".map";
}
let eol = "\n";
if (this.css.includes("\r\n"))
eol = "\r\n";
this.css += eol + "/*# sourceMappingURL=" + content + " */";
}
outputFile() {
if (this.opts.to) {
return this.path(this.opts.to);
}
else if (this.opts.from) {
return this.path(this.opts.from);
}
else {
return "to.css";
}
}
generateMap() {
if (this.root) {
this.generateString();
}
else if (this.previous().length === 1) {
let prev = this.previous()[0].consumer();
prev.file = this.outputFile();
this.map = SourceMapGenerator.fromSourceMap(prev);
}
else {
this.map = new SourceMapGenerator({ file: this.outputFile() });
this.map.addMapping({
source: this.opts.from
? this.toUrl(this.path(this.opts.from))
: "<no source>",
generated: { line: 1, column: 0 },
original: { line: 1, column: 0 },
});
}
if (this.isSourcesContent())
this.setSourcesContent();
if (this.root && this.previous().length > 0)
this.applyPrevMaps();
if (this.isAnnotation())
this.addAnnotation();
if (this.isInline()) {
return [this.css];
}
else {
return [this.css, this.map];
}
}
path(file) {
if (file.indexOf("<") === 0)
return file;
if (/^\w+:\/\//.test(file))
return file;
if (this.mapOpts.absolute)
return file;
let from = this.opts.to ? dirname(this.opts.to) : ".";
if (typeof this.mapOpts.annotation === "string") {
from = dirname(resolve(from, this.mapOpts.annotation));
}
file = relative(from, file);
return file;
}
toUrl(path) {
if (sep === "\\") {
path = path.replace(/\\/g, "/");
}
return encodeURI(path).replace(/[#?]/g, encodeURIComponent);
}
sourcePath(node) {
if (this.mapOpts.from) {
return this.toUrl(this.mapOpts.from);
}
else if (this.mapOpts.absolute) {
if (pathToFileURL) {
return pathToFileURL(node.source.input.from).toString();
}
else {
throw new Error("`map.absolute` option is not available in this PostCSS build");
}
}
else {
return this.toUrl(this.path(node.source.input.from));
}
}
generateString() {
this.css = "";
this.map = new SourceMapGenerator({ file: this.outputFile() });
let line = 1;
let column = 1;
let noSource = "<no source>";
let mapping = {
source: "",
generated: { line: 0, column: 0 },
original: { line: 0, column: 0 },
};
let lines, last;
this.stringify(this.root, (str, node, type) => {
this.css += str;
if (node && type !== "end") {
mapping.generated.line = line;
mapping.generated.column = column - 1;
if (node.source && node.source.start) {
mapping.source = this.sourcePath(node);
mapping.original.line = node.source.start.line;
mapping.original.column = node.source.start.column - 1;
this.map.addMapping(mapping);
}
else {
mapping.source = noSource;
mapping.original.line = 1;
mapping.original.column = 0;
this.map.addMapping(mapping);
}
}
lines = str.match(/\n/g);
if (lines) {
line += lines.length;
last = str.lastIndexOf("\n");
column = str.length - last;
}
else {
column += str.length;
}
if (node && type !== "start") {
let p = node.parent || { raws: {} };
if (node.type !== "decl" || node !== p.last || p.raws.semicolon) {
if (node.source && node.source.end) {
mapping.source = this.sourcePath(node);
mapping.original.line = node.source.end.line;
mapping.original.column = node.source.end.column - 1;
mapping.generated.line = line;
mapping.generated.column = column - 2;
this.map.addMapping(mapping);
}
else {
mapping.source = noSource;
mapping.original.line = 1;
mapping.original.column = 0;
mapping.generated.line = line;
mapping.generated.column = column - 1;
this.map.addMapping(mapping);
}
}
}
});
}
generate() {
this.clearAnnotation();
if (pathAvailable && sourceMapAvailable && this.isMap()) {
return this.generateMap();
}
else {
let result = "";
this.stringify(this.root, (i) => {
result += i;
});
return [result];
}
}
}
export default MapGenerator;