AK-21/Graphite-Industrial-Intelligence
0
1'use strict'2 3let { dirname, relative, resolve, sep } = require('path')4let { SourceMapConsumer, SourceMapGenerator } = require('source-map-js')5let { pathToFileURL } = require('url')6 7let Input = require('./input')8 9let sourceMapAvailable = Boolean(SourceMapConsumer && SourceMapGenerator)10let pathAvailable = Boolean(dirname && resolve && relative && sep)11 12class MapGenerator {13 constructor(stringify, root, opts, cssString) {14 this.stringify = stringify15 this.mapOpts = opts.map || {}16 this.root = root17 this.opts = opts18 this.css = cssString19 this.originalCSS = cssString20 this.usesFileUrls = !this.mapOpts.from && this.mapOpts.absolute21 22 this.memoizedFileURLs = new Map()23 this.memoizedPaths = new Map()24 this.memoizedURLs = new Map()25 }26 27 addAnnotation() {28 let content29 30 if (this.isInline()) {31 content =32 'data:application/json;base64,' + this.toBase64(this.map.toString())33 } else if (typeof this.mapOpts.annotation === 'string') {34 content = this.mapOpts.annotation35 } else if (typeof this.mapOpts.annotation === 'function') {36 content = this.mapOpts.annotation(this.opts.to, this.root)37 } else {38 content = this.outputFile() + '.map'39 }40 let eol = '\n'41 if (this.css.includes('\r\n')) eol = '\r\n'42 43 this.css += eol + '/*# sourceMappingURL=' + content + ' */'44 }45 46 applyPrevMaps() {47 for (let prev of this.previous()) {48 let from = this.toUrl(this.path(prev.file))49 let root = prev.root || dirname(prev.file)50 let map51 52 if (this.mapOpts.sourcesContent === false) {53 map = new SourceMapConsumer(prev.text)54 if (map.sourcesContent) {55 map.sourcesContent = null56 }57 } else {58 map = prev.consumer()59 }60 61 this.map.applySourceMap(map, from, this.toUrl(this.path(root)))62 }63 }64 65 clearAnnotation() {66 if (this.mapOpts.annotation === false) return67 68 if (this.root) {69 let node70 for (let i = this.root.nodes.length - 1; i >= 0; i--) {71 node = this.root.nodes[i]72 if (node.type !== 'comment') continue73 if (node.text.startsWith('# sourceMappingURL=')) {74 this.root.removeChild(i)75 }76 }77 } else if (this.css) {78 let startIndex79 while ((startIndex = this.css.lastIndexOf('/*#')) !== -1) {80 let endIndex = this.css.indexOf('*/', startIndex + 3)81 if (endIndex === -1) break82 while (startIndex > 0 && this.css[startIndex - 1] === '\n') {83 startIndex--84 }85 this.css = this.css.slice(0, startIndex) + this.css.slice(endIndex + 2)86 }87 }88 }89 90 generate() {91 this.clearAnnotation()92 if (pathAvailable && sourceMapAvailable && this.isMap()) {93 return this.generateMap()94 } else {95 let result = ''96 this.stringify(this.root, i => {97 result += i98 })99 return [result]100 }101 }102 103 generateMap() {104 if (this.root) {105 this.generateString()106 } else if (this.previous().length === 1) {107 let prev = this.previous()[0].consumer()108 prev.file = this.outputFile()109 this.map = SourceMapGenerator.fromSourceMap(prev, {110 ignoreInvalidMapping: true111 })112 } else {113 this.map = new SourceMapGenerator({114 file: this.outputFile(),115 ignoreInvalidMapping: true116 })117 this.map.addMapping({118 generated: { column: 0, line: 1 },119 original: { column: 0, line: 1 },120 source: this.opts.from121 ? this.toUrl(this.path(this.opts.from))122 : '<no source>'123 })124 }125 126 if (this.isSourcesContent()) this.setSourcesContent()127 if (this.root && this.previous().length > 0) this.applyPrevMaps()128 if (this.isAnnotation()) this.addAnnotation()129 130 if (this.isInline()) {131 return [this.css]132 } else {133 return [this.css, this.map]134 }135 }136 137 generateString() {138 this.css = ''139 this.map = new SourceMapGenerator({140 file: this.outputFile(),141 ignoreInvalidMapping: true142 })143 144 let line = 1145 let column = 1146 147 let noSource = '<no source>'148 let mapping = {149 generated: { column: 0, line: 0 },150 original: { column: 0, line: 0 },151 source: ''152 }153 154 let last, lines155 this.stringify(this.root, (str, node, type) => {156 this.css += str157 158 if (node && type !== 'end') {159 mapping.generated.line = line160 mapping.generated.column = column - 1161 if (node.source && node.source.start) {162 mapping.source = this.sourcePath(node)163 mapping.original.line = node.source.start.line164 mapping.original.column = node.source.start.column - 1165 this.map.addMapping(mapping)166 } else {167 mapping.source = noSource168 mapping.original.line = 1169 mapping.original.column = 0170 this.map.addMapping(mapping)171 }172 }173 174 lines = str.match(/\n/g)175 if (lines) {176 line += lines.length177 last = str.lastIndexOf('\n')178 column = str.length - last179 } else {180 column += str.length181 }182 183 if (node && type !== 'start') {184 let p = node.parent || { raws: {} }185 let childless =186 node.type === 'decl' || (node.type === 'atrule' && !node.nodes)187 if (!childless || node !== p.last || p.raws.semicolon) {188 if (node.source && node.source.end) {189 mapping.source = this.sourcePath(node)190 mapping.original.line = node.source.end.line191 mapping.original.column = node.source.end.column - 1192 mapping.generated.line = line193 mapping.generated.column = column - 2194 this.map.addMapping(mapping)195 } else {196 mapping.source = noSource197 mapping.original.line = 1198 mapping.original.column = 0199 mapping.generated.line = line200 mapping.generated.column = column - 1201 this.map.addMapping(mapping)202 }203 }204 }205 })206 }207 208 isAnnotation() {209 if (this.isInline()) {210 return true211 }212 if (typeof this.mapOpts.annotation !== 'undefined') {213 return this.mapOpts.annotation214 }215 if (this.previous().length) {216 return this.previous().some(i => i.annotation)217 }218 return true219 }220 221 isInline() {222 if (typeof this.mapOpts.inline !== 'undefined') {223 return this.mapOpts.inline224 }225 226 let annotation = this.mapOpts.annotation227 if (typeof annotation !== 'undefined' && annotation !== true) {228 return false229 }230 231 if (this.previous().length) {232 return this.previous().some(i => i.inline)233 }234 return true235 }236 237 isMap() {238 if (typeof this.opts.map !== 'undefined') {239 return !!this.opts.map240 }241 return this.previous().length > 0242 }243 244 isSourcesContent() {245 if (typeof this.mapOpts.sourcesContent !== 'undefined') {246 return this.mapOpts.sourcesContent247 }248 if (this.previous().length) {249 return this.previous().some(i => i.withContent())250 }251 return true252 }253 254 outputFile() {255 if (this.opts.to) {256 return this.path(this.opts.to)257 } else if (this.opts.from) {258 return this.path(this.opts.from)259 } else {260 return 'to.css'261 }262 }263 264 path(file) {265 if (this.mapOpts.absolute) return file266 if (file.charCodeAt(0) === 60 /* `<` */) return file267 if (/^\w+:\/\//.test(file)) return file268 let cached = this.memoizedPaths.get(file)269 if (cached) return cached270 271 let from = this.opts.to ? dirname(this.opts.to) : '.'272 273 if (typeof this.mapOpts.annotation === 'string') {274 from = dirname(resolve(from, this.mapOpts.annotation))275 }276 277 let path = relative(from, file)278 this.memoizedPaths.set(file, path)279 280 return path281 }282 283 previous() {284 if (!this.previousMaps) {285 this.previousMaps = []286 if (this.root) {287 this.root.walk(node => {288 if (node.source && node.source.input.map) {289 let map = node.source.input.map290 if (!this.previousMaps.includes(map)) {291 this.previousMaps.push(map)292 }293 }294 })295 } else {296 let input = new Input(this.originalCSS, this.opts)297 if (input.map) this.previousMaps.push(input.map)298 }299 }300 301 return this.previousMaps302 }303 304 setSourcesContent() {305 let already = {}306 if (this.root) {307 this.root.walk(node => {308 if (node.source) {309 let from = node.source.input.from310 if (from && !already[from]) {311 already[from] = true312 let fromUrl = this.usesFileUrls313 ? this.toFileUrl(from)314 : this.toUrl(this.path(from))315 this.map.setSourceContent(fromUrl, node.source.input.css)316 }317 }318 })319 } else if (this.css) {320 let from = this.opts.from321 ? this.toUrl(this.path(this.opts.from))322 : '<no source>'323 this.map.setSourceContent(from, this.css)324 }325 }326 327 sourcePath(node) {328 if (this.mapOpts.from) {329 return this.toUrl(this.mapOpts.from)330 } else if (this.usesFileUrls) {331 return this.toFileUrl(node.source.input.from)332 } else {333 return this.toUrl(this.path(node.source.input.from))334 }335 }336 337 toBase64(str) {338 if (Buffer) {339 return Buffer.from(str).toString('base64')340 } else {341 return window.btoa(unescape(encodeURIComponent(str)))342 }343 }344 345 toFileUrl(path) {346 let cached = this.memoizedFileURLs.get(path)347 if (cached) return cached348 349 if (pathToFileURL) {350 let fileURL = pathToFileURL(path).toString()351 this.memoizedFileURLs.set(path, fileURL)352 353 return fileURL354 } else {355 throw new Error(356 '`map.absolute` option is not available in this PostCSS build'357 )358 }359 }360 361 toUrl(path) {362 let cached = this.memoizedURLs.get(path)363 if (cached) return cached364 365 if (sep === '\\') {366 path = path.replace(/\\/g, '/')367 }368 369 let url = encodeURI(path).replace(/[#?]/g, encodeURIComponent)370 this.memoizedURLs.set(path, url)371 372 return url373 }374}375 376module.exports = MapGenerator377 