UNPKG

@mapcss/preset-svg

Version:
316 lines (315 loc) 10.8 kB
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;