AK-21/Graphite-Industrial-Intelligence
0
1'use strict'2 3let { nanoid } = require('nanoid/non-secure')4let { isAbsolute, resolve } = require('path')5let { SourceMapConsumer, SourceMapGenerator } = require('source-map-js')6let { fileURLToPath, pathToFileURL } = require('url')7 8let CssSyntaxError = require('./css-syntax-error')9let PreviousMap = require('./previous-map')10let terminalHighlight = require('./terminal-highlight')11 12let lineToIndexCache = Symbol('lineToIndexCache')13 14let sourceMapAvailable = Boolean(SourceMapConsumer && SourceMapGenerator)15let pathAvailable = Boolean(resolve && isAbsolute)16 17function getLineToIndex(input) {18 if (input[lineToIndexCache]) return input[lineToIndexCache]19 let lines = input.css.split('\n')20 let lineToIndex = new Array(lines.length)21 let prevIndex = 022 23 for (let i = 0, l = lines.length; i < l; i++) {24 lineToIndex[i] = prevIndex25 prevIndex += lines[i].length + 126 }27 28 input[lineToIndexCache] = lineToIndex29 return lineToIndex30}31 32class Input {33 get from() {34 return this.file || this.id35 }36 37 constructor(css, opts = {}) {38 if (39 css === null ||40 typeof css === 'undefined' ||41 (typeof css === 'object' && !css.toString)42 ) {43 throw new Error(`PostCSS received ${css} instead of CSS string`)44 }45 46 this.css = css.toString()47 48 if (this.css[0] === '\uFEFF' || this.css[0] === '\uFFFE') {49 this.hasBOM = true50 this.css = this.css.slice(1)51 } else {52 this.hasBOM = false53 }54 55 this.document = this.css56 if (opts.document) this.document = opts.document.toString()57 58 if (opts.from) {59 if (60 !pathAvailable ||61 /^\w+:\/\//.test(opts.from) ||62 isAbsolute(opts.from)63 ) {64 this.file = opts.from65 } else {66 this.file = resolve(opts.from)67 }68 }69 70 if (pathAvailable && sourceMapAvailable) {71 let map = new PreviousMap(this.css, opts)72 if (map.text) {73 this.map = map74 let file = map.consumer().file75 if (!this.file && file) this.file = this.mapResolve(file)76 }77 }78 79 if (!this.file) {80 this.id = '<input css ' + nanoid(6) + '>'81 }82 if (this.map) this.map.file = this.from83 }84 85 error(message, line, column, opts = {}) {86 let endColumn, endLine, endOffset, offset, result87 88 if (line && typeof line === 'object') {89 let start = line90 let end = column91 if (typeof start.offset === 'number') {92 offset = start.offset93 let pos = this.fromOffset(offset)94 line = pos.line95 column = pos.col96 } else {97 line = start.line98 column = start.column99 offset = this.fromLineAndColumn(line, column)100 }101 if (typeof end.offset === 'number') {102 endOffset = end.offset103 let pos = this.fromOffset(endOffset)104 endLine = pos.line105 endColumn = pos.col106 } else {107 endLine = end.line108 endColumn = end.column109 endOffset = this.fromLineAndColumn(end.line, end.column)110 }111 } else if (!column) {112 offset = line113 let pos = this.fromOffset(offset)114 line = pos.line115 column = pos.col116 } else {117 offset = this.fromLineAndColumn(line, column)118 }119 120 let origin = this.origin(line, column, endLine, endColumn)121 if (origin) {122 result = new CssSyntaxError(123 message,124 origin.endLine === undefined125 ? origin.line126 : { column: origin.column, line: origin.line },127 origin.endLine === undefined128 ? origin.column129 : { column: origin.endColumn, line: origin.endLine },130 origin.source,131 origin.file,132 opts.plugin133 )134 } else {135 result = new CssSyntaxError(136 message,137 endLine === undefined ? line : { column, line },138 endLine === undefined ? column : { column: endColumn, line: endLine },139 this.css,140 this.file,141 opts.plugin142 )143 }144 145 result.input = {146 column,147 endColumn,148 endLine,149 endOffset,150 line,151 offset,152 source: this.css153 }154 if (this.file) {155 if (pathToFileURL) {156 result.input.url = pathToFileURL(this.file).toString()157 }158 result.input.file = this.file159 }160 161 return result162 }163 164 fromLineAndColumn(line, column) {165 let lineToIndex = getLineToIndex(this)166 let index = lineToIndex[line - 1]167 return index + column - 1168 }169 170 fromOffset(offset) {171 let lineToIndex = getLineToIndex(this)172 let lastLine = lineToIndex[lineToIndex.length - 1]173 174 let min = 0175 if (offset >= lastLine) {176 min = lineToIndex.length - 1177 } else {178 let max = lineToIndex.length - 2179 let mid180 while (min < max) {181 mid = min + ((max - min) >> 1)182 if (offset < lineToIndex[mid]) {183 max = mid - 1184 } else if (offset >= lineToIndex[mid + 1]) {185 min = mid + 1186 } else {187 min = mid188 break189 }190 }191 }192 return {193 col: offset - lineToIndex[min] + 1,194 line: min + 1195 }196 }197 198 mapResolve(file) {199 if (/^\w+:\/\//.test(file)) {200 return file201 }202 return resolve(this.map.consumer().sourceRoot || this.map.root || '.', file)203 }204 205 origin(line, column, endLine, endColumn) {206 if (!this.map) return false207 let consumer = this.map.consumer()208 209 let from = consumer.originalPositionFor({ column: column - 1, line })210 if (!from.source) return false211 212 let to213 if (typeof endLine === 'number') {214 to = consumer.originalPositionFor({215 column: endColumn - 1,216 line: endLine217 })218 }219 220 let fromUrl221 222 if (isAbsolute(from.source)) {223 fromUrl = pathToFileURL(from.source)224 } else {225 fromUrl = new URL(226 from.source,227 this.map.consumer().sourceRoot || pathToFileURL(this.map.mapFile)228 )229 }230 231 let result = {232 column: from.column + 1,233 endColumn: to && to.column + 1,234 endLine: to && to.line,235 line: from.line,236 url: fromUrl.toString()237 }238 239 if (fromUrl.protocol === 'file:') {240 if (fileURLToPath) {241 result.file = fileURLToPath(fromUrl)242 } else {243 /* c8 ignore next 2 */244 throw new Error(`file: protocol is not available in this PostCSS build`)245 }246 }247 248 let source = consumer.sourceContentFor(from.source)249 if (source) result.source = source250 251 return result252 }253 254 toJSON() {255 let json = {}256 for (let name of ['hasBOM', 'css', 'file', 'id']) {257 if (this[name] != null) {258 json[name] = this[name]259 }260 }261 if (this.map) {262 json.map = { ...this.map }263 if (json.map.consumerCache) {264 json.map.consumerCache = undefined265 }266 }267 return json268 }269}270 271module.exports = Input272Input.default = Input273 274if (terminalHighlight && terminalHighlight.registerInput) {275 terminalHighlight.registerInput(Input)276}277 