Spaces:
Running
Running
| let { isClean, my } = require('./symbols') | |
| let CssSyntaxError = require('./css-syntax-error') | |
| let Stringifier = require('./stringifier') | |
| let stringify = require('./stringify') | |
| function cloneNode(obj, parent) { | |
| let cloned = new obj.constructor() | |
| for (let i in obj) { | |
| if (!Object.prototype.hasOwnProperty.call(obj, i)) { | |
| /* c8 ignore next 2 */ | |
| continue | |
| } | |
| if (i === 'proxyCache') continue | |
| let value = obj[i] | |
| let type = typeof value | |
| if (i === 'parent' && type === 'object') { | |
| if (parent) cloned[i] = parent | |
| } else if (i === 'source') { | |
| cloned[i] = value | |
| } else if (Array.isArray(value)) { | |
| cloned[i] = value.map(j => cloneNode(j, cloned)) | |
| } else { | |
| if (type === 'object' && value !== null) value = cloneNode(value) | |
| cloned[i] = value | |
| } | |
| } | |
| return cloned | |
| } | |
| class Node { | |
| constructor(defaults = {}) { | |
| this.raws = {} | |
| this[isClean] = false | |
| this[my] = true | |
| for (let name in defaults) { | |
| if (name === 'nodes') { | |
| this.nodes = [] | |
| for (let node of defaults[name]) { | |
| if (typeof node.clone === 'function') { | |
| this.append(node.clone()) | |
| } else { | |
| this.append(node) | |
| } | |
| } | |
| } else { | |
| this[name] = defaults[name] | |
| } | |
| } | |
| } | |
| error(message, opts = {}) { | |
| if (this.source) { | |
| let { start, end } = this.rangeBy(opts) | |
| return this.source.input.error( | |
| message, | |
| { line: start.line, column: start.column }, | |
| { line: end.line, column: end.column }, | |
| opts | |
| ) | |
| } | |
| return new CssSyntaxError(message) | |
| } | |
| warn(result, text, opts) { | |
| let data = { node: this } | |
| for (let i in opts) data[i] = opts[i] | |
| return result.warn(text, data) | |
| } | |
| remove() { | |
| if (this.parent) { | |
| this.parent.removeChild(this) | |
| } | |
| this.parent = undefined | |
| return this | |
| } | |
| toString(stringifier = stringify) { | |
| if (stringifier.stringify) stringifier = stringifier.stringify | |
| let result = '' | |
| stringifier(this, i => { | |
| result += i | |
| }) | |
| return result | |
| } | |
| assign(overrides = {}) { | |
| for (let name in overrides) { | |
| this[name] = overrides[name] | |
| } | |
| return this | |
| } | |
| clone(overrides = {}) { | |
| let cloned = cloneNode(this) | |
| for (let name in overrides) { | |
| cloned[name] = overrides[name] | |
| } | |
| return cloned | |
| } | |
| cloneBefore(overrides = {}) { | |
| let cloned = this.clone(overrides) | |
| this.parent.insertBefore(this, cloned) | |
| return cloned | |
| } | |
| cloneAfter(overrides = {}) { | |
| let cloned = this.clone(overrides) | |
| this.parent.insertAfter(this, cloned) | |
| return cloned | |
| } | |
| replaceWith(...nodes) { | |
| if (this.parent) { | |
| let bookmark = this | |
| let foundSelf = false | |
| for (let node of nodes) { | |
| if (node === this) { | |
| foundSelf = true | |
| } else if (foundSelf) { | |
| this.parent.insertAfter(bookmark, node) | |
| bookmark = node | |
| } else { | |
| this.parent.insertBefore(bookmark, node) | |
| } | |
| } | |
| if (!foundSelf) { | |
| this.remove() | |
| } | |
| } | |
| return this | |
| } | |
| next() { | |
| if (!this.parent) return undefined | |
| let index = this.parent.index(this) | |
| return this.parent.nodes[index + 1] | |
| } | |
| prev() { | |
| if (!this.parent) return undefined | |
| let index = this.parent.index(this) | |
| return this.parent.nodes[index - 1] | |
| } | |
| before(add) { | |
| this.parent.insertBefore(this, add) | |
| return this | |
| } | |
| after(add) { | |
| this.parent.insertAfter(this, add) | |
| return this | |
| } | |
| root() { | |
| let result = this | |
| while (result.parent && result.parent.type !== 'document') { | |
| result = result.parent | |
| } | |
| return result | |
| } | |
| raw(prop, defaultType) { | |
| let str = new Stringifier() | |
| return str.raw(this, prop, defaultType) | |
| } | |
| cleanRaws(keepBetween) { | |
| delete this.raws.before | |
| delete this.raws.after | |
| if (!keepBetween) delete this.raws.between | |
| } | |
| toJSON(_, inputs) { | |
| let fixed = {} | |
| let emitInputs = inputs == null | |
| inputs = inputs || new Map() | |
| let inputsNextIndex = 0 | |
| for (let name in this) { | |
| if (!Object.prototype.hasOwnProperty.call(this, name)) { | |
| /* c8 ignore next 2 */ | |
| continue | |
| } | |
| if (name === 'parent' || name === 'proxyCache') continue | |
| let value = this[name] | |
| if (Array.isArray(value)) { | |
| fixed[name] = value.map(i => { | |
| if (typeof i === 'object' && i.toJSON) { | |
| return i.toJSON(null, inputs) | |
| } else { | |
| return i | |
| } | |
| }) | |
| } else if (typeof value === 'object' && value.toJSON) { | |
| fixed[name] = value.toJSON(null, inputs) | |
| } else if (name === 'source') { | |
| let inputId = inputs.get(value.input) | |
| if (inputId == null) { | |
| inputId = inputsNextIndex | |
| inputs.set(value.input, inputsNextIndex) | |
| inputsNextIndex++ | |
| } | |
| fixed[name] = { | |
| inputId, | |
| start: value.start, | |
| end: value.end | |
| } | |
| } else { | |
| fixed[name] = value | |
| } | |
| } | |
| if (emitInputs) { | |
| fixed.inputs = [...inputs.keys()].map(input => input.toJSON()) | |
| } | |
| return fixed | |
| } | |
| positionInside(index) { | |
| let string = this.toString() | |
| let column = this.source.start.column | |
| let line = this.source.start.line | |
| for (let i = 0; i < index; i++) { | |
| if (string[i] === '\n') { | |
| column = 1 | |
| line += 1 | |
| } else { | |
| column += 1 | |
| } | |
| } | |
| return { line, column } | |
| } | |
| positionBy(opts) { | |
| let pos = this.source.start | |
| if (opts.index) { | |
| pos = this.positionInside(opts.index) | |
| } else if (opts.word) { | |
| let index = this.toString().indexOf(opts.word) | |
| if (index !== -1) pos = this.positionInside(index) | |
| } | |
| return pos | |
| } | |
| rangeBy(opts) { | |
| let start = { | |
| line: this.source.start.line, | |
| column: this.source.start.column | |
| } | |
| let end = this.source.end | |
| ? { | |
| line: this.source.end.line, | |
| column: this.source.end.column + 1 | |
| } | |
| : { | |
| line: start.line, | |
| column: start.column + 1 | |
| } | |
| if (opts.word) { | |
| let index = this.toString().indexOf(opts.word) | |
| if (index !== -1) { | |
| start = this.positionInside(index) | |
| end = this.positionInside(index + opts.word.length) | |
| } | |
| } else { | |
| if (opts.start) { | |
| start = { | |
| line: opts.start.line, | |
| column: opts.start.column | |
| } | |
| } else if (opts.index) { | |
| start = this.positionInside(opts.index) | |
| } | |
| if (opts.end) { | |
| end = { | |
| line: opts.end.line, | |
| column: opts.end.column | |
| } | |
| } else if (opts.endIndex) { | |
| end = this.positionInside(opts.endIndex) | |
| } else if (opts.index) { | |
| end = this.positionInside(opts.index + 1) | |
| } | |
| } | |
| if ( | |
| end.line < start.line || | |
| (end.line === start.line && end.column <= start.column) | |
| ) { | |
| end = { line: start.line, column: start.column + 1 } | |
| } | |
| return { start, end } | |
| } | |
| getProxyProcessor() { | |
| return { | |
| set(node, prop, value) { | |
| if (node[prop] === value) return true | |
| node[prop] = value | |
| if ( | |
| prop === 'prop' || | |
| prop === 'value' || | |
| prop === 'name' || | |
| prop === 'params' || | |
| prop === 'important' || | |
| /* c8 ignore next */ | |
| prop === 'text' | |
| ) { | |
| node.markDirty() | |
| } | |
| return true | |
| }, | |
| get(node, prop) { | |
| if (prop === 'proxyOf') { | |
| return node | |
| } else if (prop === 'root') { | |
| return () => node.root().toProxy() | |
| } else { | |
| return node[prop] | |
| } | |
| } | |
| } | |
| } | |
| toProxy() { | |
| if (!this.proxyCache) { | |
| this.proxyCache = new Proxy(this, this.getProxyProcessor()) | |
| } | |
| return this.proxyCache | |
| } | |
| addToError(error) { | |
| error.postcssNode = this | |
| if (error.stack && this.source && /\n\s{4}at /.test(error.stack)) { | |
| let s = this.source | |
| error.stack = error.stack.replace( | |
| /\n\s{4}at /, | |
| `$&${s.input.from}:${s.start.line}:${s.start.column}$&` | |
| ) | |
| } | |
| return error | |
| } | |
| markDirty() { | |
| if (this[isClean]) { | |
| this[isClean] = false | |
| let next = this | |
| while ((next = next.parent)) { | |
| next[isClean] = false | |
| } | |
| } | |
| } | |
| get proxyOf() { | |
| return this | |
| } | |
| } | |
| module.exports = Node | |
| Node.default = Node | |