summaryrefslogtreecommitdiff
path: root/node_modules/postcss/lib/map-generator.js
diff options
context:
space:
mode:
authorPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:54:57 +0100
committerPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:57:48 +0100
commitb1e2c8fd5cb5dfa46bc440a12eafaf56cd844b1c (patch)
tree49d360fd6cbc6a2754efe93524ac47ff0fbe0f7d /node_modules/postcss/lib/map-generator.js
Docs
Diffstat (limited to 'node_modules/postcss/lib/map-generator.js')
-rw-r--r--node_modules/postcss/lib/map-generator.js359
1 files changed, 359 insertions, 0 deletions
diff --git a/node_modules/postcss/lib/map-generator.js b/node_modules/postcss/lib/map-generator.js
new file mode 100644
index 0000000..523b463
--- /dev/null
+++ b/node_modules/postcss/lib/map-generator.js
@@ -0,0 +1,359 @@
+'use strict'
+
+let { SourceMapConsumer, SourceMapGenerator } = require('source-map-js')
+let { dirname, relative, resolve, sep } = require('path')
+let { pathToFileURL } = require('url')
+
+let Input = require('./input')
+
+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
+ this.usesFileUrls = !this.mapOpts.from && this.mapOpts.absolute
+
+ this.memoizedFileURLs = new Map()
+ this.memoizedPaths = new Map()
+ this.memoizedURLs = new Map()
+ }
+
+ 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 + ' */'
+ }
+
+ 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)))
+ }
+ }
+
+ 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, '')
+ }
+ }
+
+ generate() {
+ this.clearAnnotation()
+ if (pathAvailable && sourceMapAvailable && this.isMap()) {
+ return this.generateMap()
+ } else {
+ let result = ''
+ this.stringify(this.root, i => {
+ result += i
+ })
+ return [result]
+ }
+ }
+
+ 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({
+ generated: { column: 0, line: 1 },
+ original: { column: 0, line: 1 },
+ source: this.opts.from
+ ? this.toUrl(this.path(this.opts.from))
+ : '<no source>'
+ })
+ }
+
+ 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]
+ }
+ }
+
+ generateString() {
+ this.css = ''
+ this.map = new SourceMapGenerator({ file: this.outputFile() })
+
+ let line = 1
+ let column = 1
+
+ let noSource = '<no source>'
+ let mapping = {
+ generated: { column: 0, line: 0 },
+ original: { column: 0, line: 0 },
+ source: ''
+ }
+
+ 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: {} }
+ let childless =
+ node.type === 'decl' || (node.type === 'atrule' && !node.nodes)
+ if (!childless || 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)
+ }
+ }
+ }
+ })
+ }
+
+ 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
+ }
+
+ 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
+ }
+
+ isMap() {
+ if (typeof this.opts.map !== 'undefined') {
+ return !!this.opts.map
+ }
+ return this.previous().length > 0
+ }
+
+ isSourcesContent() {
+ if (typeof this.mapOpts.sourcesContent !== 'undefined') {
+ return this.mapOpts.sourcesContent
+ }
+ if (this.previous().length) {
+ return this.previous().some(i => i.withContent())
+ }
+ return true
+ }
+
+ 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'
+ }
+ }
+
+ path(file) {
+ if (this.mapOpts.absolute) return file
+ if (file.charCodeAt(0) === 60 /* `<` */) return file
+ if (/^\w+:\/\//.test(file)) return file
+ let cached = this.memoizedPaths.get(file)
+ if (cached) return cached
+
+ let from = this.opts.to ? dirname(this.opts.to) : '.'
+
+ if (typeof this.mapOpts.annotation === 'string') {
+ from = dirname(resolve(from, this.mapOpts.annotation))
+ }
+
+ let path = relative(from, file)
+ this.memoizedPaths.set(file, path)
+
+ return path
+ }
+
+ 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
+ }
+
+ 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
+ let fromUrl = this.usesFileUrls
+ ? this.toFileUrl(from)
+ : this.toUrl(this.path(from))
+ this.map.setSourceContent(fromUrl, 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)
+ }
+ }
+
+ sourcePath(node) {
+ if (this.mapOpts.from) {
+ return this.toUrl(this.mapOpts.from)
+ } else if (this.usesFileUrls) {
+ return this.toFileUrl(node.source.input.from)
+ } else {
+ return this.toUrl(this.path(node.source.input.from))
+ }
+ }
+
+ toBase64(str) {
+ if (Buffer) {
+ return Buffer.from(str).toString('base64')
+ } else {
+ return window.btoa(unescape(encodeURIComponent(str)))
+ }
+ }
+
+ toFileUrl(path) {
+ let cached = this.memoizedFileURLs.get(path)
+ if (cached) return cached
+
+ if (pathToFileURL) {
+ let fileURL = pathToFileURL(path).toString()
+ this.memoizedFileURLs.set(path, fileURL)
+
+ return fileURL
+ } else {
+ throw new Error(
+ '`map.absolute` option is not available in this PostCSS build'
+ )
+ }
+ }
+
+ toUrl(path) {
+ let cached = this.memoizedURLs.get(path)
+ if (cached) return cached
+
+ if (sep === '\\') {
+ path = path.replace(/\\/g, '/')
+ }
+
+ let url = encodeURI(path).replace(/[#?]/g, encodeURIComponent)
+ this.memoizedURLs.set(path, url)
+
+ return url
+ }
+}
+
+module.exports = MapGenerator