postcss 8.3.11 → 8.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of postcss might be problematic. Click here for more details.

@@ -181,10 +181,10 @@ export default abstract class Container<
181
181
  */
182
182
  walkRules(
183
183
  selectorFilter: string | RegExp,
184
- callback: (atRule: Rule, index: number) => false | void
184
+ callback: (rule: Rule, index: number) => false | void
185
185
  ): false | undefined
186
186
  walkRules(
187
- callback: (atRule: Rule, index: number) => false | void
187
+ callback: (rule: Rule, index: number) => false | void
188
188
  ): false | undefined
189
189
 
190
190
  /**
package/lib/container.js CHANGED
@@ -316,7 +316,7 @@ class Container extends Node {
316
316
  }
317
317
 
318
318
  let processed = nodes.map(i => {
319
- // istanbul ignore next
319
+ /* c8 ignore next */
320
320
  if (!i[my]) Container.rebuild(i)
321
321
  i = i.proxyOf
322
322
  if (i.parent) i.parent.removeChild(i)
@@ -410,7 +410,7 @@ Container.registerAtRule = dependant => {
410
410
  module.exports = Container
411
411
  Container.default = Container
412
412
 
413
- // istanbul ignore next
413
+ /* c8 ignore start */
414
414
  Container.rebuild = node => {
415
415
  if (node.type === 'atrule') {
416
416
  Object.setPrototypeOf(node, AtRule.prototype)
@@ -430,3 +430,4 @@ Container.rebuild = node => {
430
430
  })
431
431
  }
432
432
  }
433
+ /* c8 ignore stop */
@@ -1,5 +1,20 @@
1
1
  import { FilePosition } from './input.js'
2
2
 
3
+ /**
4
+ * A position that is part of a range.
5
+ */
6
+ export interface RangePosition {
7
+ /**
8
+ * The line number in the input.
9
+ */
10
+ line: number
11
+
12
+ /**
13
+ * The column number in the input.
14
+ */
15
+ column: number
16
+ }
17
+
3
18
  /**
4
19
  * The CSS parser throws this error for broken CSS.
5
20
  *
@@ -31,17 +46,21 @@ import { FilePosition } from './input.js'
31
46
  */
32
47
  export default class CssSyntaxError {
33
48
  /**
34
- * @param message Error message.
35
- * @param line Source line of the error.
36
- * @param column Source column of the error.
37
- * @param source Source code of the broken file.
38
- * @param file Absolute path to the broken file.
39
- * @param plugin PostCSS plugin name, if error came from plugin.
49
+ * Instantiates a CSS syntax error. Can be instantiated for a single position
50
+ * or for a range.
51
+ * @param message Error message.
52
+ * @param lineOrStartPos If for a single position, the line number, or if for
53
+ * a range, the inclusive start position of the error.
54
+ * @param columnOrEndPos If for a single position, the column number, or if for
55
+ * a range, the exclusive end position of the error.
56
+ * @param source Source code of the broken file.
57
+ * @param file Absolute path to the broken file.
58
+ * @param plugin PostCSS plugin name, if error came from plugin.
40
59
  */
41
60
  constructor(
42
61
  message: string,
43
- line?: number,
44
- column?: number,
62
+ lineOrStartPos?: number | RangePosition,
63
+ columnOrEndPos?: number | RangePosition,
45
64
  source?: string,
46
65
  file?: string,
47
66
  plugin?: string
@@ -121,6 +140,34 @@ export default class CssSyntaxError {
121
140
  */
122
141
  column?: number
123
142
 
143
+ /**
144
+ * Source line of the error's end, exclusive. Provided if the error pertains
145
+ * to a range.
146
+ *
147
+ * ```js
148
+ * error.endLine //=> 3
149
+ * error.input.endLine //=> 4
150
+ * ```
151
+ *
152
+ * PostCSS will use the input source map to detect the original location.
153
+ * If you need the position in the PostCSS input, use `error.input.endLine`.
154
+ */
155
+ endLine?: number
156
+
157
+ /**
158
+ * Source column of the error's end, exclusive. Provided if the error pertains
159
+ * to a range.
160
+ *
161
+ * ```js
162
+ * error.endColumn //=> 1
163
+ * error.input.endColumn //=> 4
164
+ * ```
165
+ *
166
+ * PostCSS will use the input source map to detect the original location.
167
+ * If you need the position in the PostCSS input, use `error.input.endColumn`.
168
+ */
169
+ endColumn?: number
170
+
124
171
  /**
125
172
  * Source code of the broken file.
126
173
  *
@@ -20,8 +20,15 @@ class CssSyntaxError extends Error {
20
20
  this.plugin = plugin
21
21
  }
22
22
  if (typeof line !== 'undefined' && typeof column !== 'undefined') {
23
- this.line = line
24
- this.column = column
23
+ if (typeof line === 'number') {
24
+ this.line = line
25
+ this.column = column
26
+ } else {
27
+ this.line = line.line
28
+ this.column = line.column
29
+ this.endLine = column.line
30
+ this.endColumn = column.column
31
+ }
25
32
  }
26
33
 
27
34
  this.setMessage()
package/lib/input.d.ts CHANGED
@@ -13,15 +13,25 @@ export interface FilePosition {
13
13
  file?: string
14
14
 
15
15
  /**
16
- * Line in source file.
16
+ * Line of inclusive start position in source file.
17
17
  */
18
18
  line: number
19
19
 
20
20
  /**
21
- * Column in source file.
21
+ * Column of inclusive start position in source file.
22
22
  */
23
23
  column: number
24
24
 
25
+ /**
26
+ * Line of exclusive end position in source file.
27
+ */
28
+ endLine?: number
29
+
30
+ /**
31
+ * Column of exclusive end position in source file.
32
+ */
33
+ endColumn?: number
34
+
25
35
  /**
26
36
  * Source code.
27
37
  */
@@ -108,18 +118,28 @@ export default class Input {
108
118
  /**
109
119
  * Reads the input source map and returns a symbol position
110
120
  * in the input source (e.g., in a Sass file that was compiled
111
- * to CSS before being passed to PostCSS).
121
+ * to CSS before being passed to PostCSS). Optionally takes an
122
+ * end position, exclusive.
112
123
  *
113
124
  * ```js
114
125
  * root.source.input.origin(1, 1) //=> { file: 'a.css', line: 3, column: 1 }
126
+ * root.source.input.origin(1, 1, 1, 4)
127
+ * //=> { file: 'a.css', line: 3, column: 1, endLine: 3, endColumn: 4 }
115
128
  * ```
116
129
  *
117
- * @param line Line in input CSS.
118
- * @param column Column in input CSS.
130
+ * @param line Line for inclusive start position in input CSS.
131
+ * @param column Column for inclusive start position in input CSS.
132
+ * @param endLine Line for exclusive end position in input CSS.
133
+ * @param endColumn Column for exclusive end position in input CSS.
119
134
  *
120
135
  * @return Position in input source.
121
136
  */
122
- origin(line: number, column: number): FilePosition | false
137
+ origin(
138
+ line: number,
139
+ column: number,
140
+ endLine?: number,
141
+ endColumn?: number
142
+ ): FilePosition | false
123
143
 
124
144
  /**
125
145
  * Converts source offset to line and column.
package/lib/input.js CHANGED
@@ -103,18 +103,43 @@ class Input {
103
103
  }
104
104
 
105
105
  error(message, line, column, opts = {}) {
106
- let result
107
- if (!column) {
106
+ let result, endLine, endColumn
107
+
108
+ if (line && typeof line === 'object') {
109
+ let start = line
110
+ let end = column
111
+ if (typeof line.offset === 'number') {
112
+ let pos = this.fromOffset(start.offset)
113
+ line = pos.line
114
+ column = pos.col
115
+ } else {
116
+ line = start.line
117
+ column = start.column
118
+ }
119
+ if (typeof end.offset === 'number') {
120
+ let pos = this.fromOffset(end.offset)
121
+ endLine = pos.line
122
+ endColumn = pos.col
123
+ } else {
124
+ endLine = end.line
125
+ endColumn = end.column
126
+ }
127
+ } else if (!column) {
108
128
  let pos = this.fromOffset(line)
109
129
  line = pos.line
110
130
  column = pos.col
111
131
  }
112
- let origin = this.origin(line, column)
132
+
133
+ let origin = this.origin(line, column, endLine, endColumn)
113
134
  if (origin) {
114
135
  result = new CssSyntaxError(
115
136
  message,
116
- origin.line,
117
- origin.column,
137
+ origin.endLine === undefined
138
+ ? origin.line
139
+ : { line: origin.line, column: origin.column },
140
+ origin.endLine === undefined
141
+ ? origin.column
142
+ : { line: origin.endLine, column: origin.endColumn },
118
143
  origin.source,
119
144
  origin.file,
120
145
  opts.plugin
@@ -122,15 +147,15 @@ class Input {
122
147
  } else {
123
148
  result = new CssSyntaxError(
124
149
  message,
125
- line,
126
- column,
150
+ endLine === undefined ? line : { line, column },
151
+ endLine === undefined ? column : { line: endLine, column: endColumn },
127
152
  this.css,
128
153
  this.file,
129
154
  opts.plugin
130
155
  )
131
156
  }
132
157
 
133
- result.input = { line, column, source: this.css }
158
+ result.input = { line, column, endLine, endColumn, source: this.css }
134
159
  if (this.file) {
135
160
  if (pathToFileURL) {
136
161
  result.input.url = pathToFileURL(this.file).toString()
@@ -141,13 +166,18 @@ class Input {
141
166
  return result
142
167
  }
143
168
 
144
- origin(line, column) {
169
+ origin(line, column, endLine, endColumn) {
145
170
  if (!this.map) return false
146
171
  let consumer = this.map.consumer()
147
172
 
148
173
  let from = consumer.originalPositionFor({ line, column })
149
174
  if (!from.source) return false
150
175
 
176
+ let to
177
+ if (typeof endLine === 'number') {
178
+ to = consumer.originalPositionFor({ line: endLine, column: endColumn })
179
+ }
180
+
151
181
  let fromUrl
152
182
 
153
183
  if (isAbsolute(from.source)) {
@@ -162,14 +192,16 @@ class Input {
162
192
  let result = {
163
193
  url: fromUrl.toString(),
164
194
  line: from.line,
165
- column: from.column
195
+ column: from.column,
196
+ endLine: to && to.line,
197
+ endColumn: to && to.column
166
198
  }
167
199
 
168
200
  if (fromUrl.protocol === 'file:') {
169
201
  if (fileURLToPath) {
170
202
  result.file = fileURLToPath(fromUrl)
171
203
  } else {
172
- // istanbul ignore next
204
+ /* c8 ignore next 2 */
173
205
  throw new Error(`file: protocol is not available in this PostCSS build`)
174
206
  }
175
207
  }
@@ -89,8 +89,9 @@ export default class LazyResult implements PromiseLike<Result> {
89
89
  *
90
90
  * This property will only work with synchronous plugins.
91
91
  * If the processor contains any asynchronous plugins
92
- * it will throw an error. This is why this method is only
93
- * for debug purpose, you should always use `LazyResult#then`.
92
+ * it will throw an error.
93
+ *
94
+ * PostCSS runners should always use `LazyResult#then`.
94
95
  */
95
96
  get css(): string
96
97
 
@@ -100,8 +101,9 @@ export default class LazyResult implements PromiseLike<Result> {
100
101
  *
101
102
  * This property will only work with synchronous plugins.
102
103
  * If the processor contains any asynchronous plugins
103
- * it will throw an error. This is why this method is only
104
- * for debug purpose, you should always use `LazyResult#then`.
104
+ * it will throw an error.
105
+ *
106
+ * PostCSS runners should always use `LazyResult#then`.
105
107
  */
106
108
  get content(): string
107
109
 
@@ -111,8 +113,9 @@ export default class LazyResult implements PromiseLike<Result> {
111
113
  *
112
114
  * This property will only work with synchronous plugins.
113
115
  * If the processor contains any asynchronous plugins
114
- * it will throw an error. This is why this method is only
115
- * for debug purpose, you should always use `LazyResult#then`.
116
+ * it will throw an error.
117
+ *
118
+ * PostCSS runners should always use `LazyResult#then`.
116
119
  */
117
120
  get map(): SourceMap
118
121
 
@@ -123,8 +126,7 @@ export default class LazyResult implements PromiseLike<Result> {
123
126
  * This property will only work with synchronous plugins. If the processor
124
127
  * contains any asynchronous plugins it will throw an error.
125
128
  *
126
- * This is why this method is only for debug purpose,
127
- * you should always use `LazyResult#then`.
129
+ * PostCSS runners should always use `LazyResult#then`.
128
130
  */
129
131
  get root(): Root
130
132
 
@@ -135,8 +137,7 @@ export default class LazyResult implements PromiseLike<Result> {
135
137
  * This property will only work with synchronous plugins. If the processor
136
138
  * contains any asynchronous plugins it will throw an error.
137
139
  *
138
- * This is why this method is only for debug purpose,
139
- * you should always use `LazyResult#then`.
140
+ * PostCSS runners should always use `LazyResult#then`.
140
141
  */
141
142
  get messages(): Message[]
142
143
 
@@ -137,7 +137,7 @@ class LazyResult {
137
137
  }
138
138
 
139
139
  if (root && !root[my]) {
140
- // istanbul ignore next
140
+ /* c8 ignore next 2 */
141
141
  Container.rebuild(root)
142
142
  }
143
143
  }
@@ -364,6 +364,7 @@ class LazyResult {
364
364
  let b = runtimeVer.split('.')
365
365
 
366
366
  if (a[0] !== b[0] || parseInt(a[1]) > parseInt(b[1])) {
367
+ // eslint-disable-next-line no-console
367
368
  console.error(
368
369
  'Unknown error from PostCSS plugin. Your current PostCSS ' +
369
370
  'version is ' +
@@ -378,7 +379,8 @@ class LazyResult {
378
379
  }
379
380
  }
380
381
  } catch (err) {
381
- // istanbul ignore next
382
+ /* c8 ignore next 3 */
383
+ // eslint-disable-next-line no-console
382
384
  if (console && console.error) console.error(err)
383
385
  }
384
386
  return error
@@ -8,11 +8,12 @@ let sourceMapAvailable = Boolean(SourceMapConsumer && SourceMapGenerator)
8
8
  let pathAvailable = Boolean(dirname && resolve && relative && sep)
9
9
 
10
10
  class MapGenerator {
11
- constructor(stringify, root, opts) {
11
+ constructor(stringify, root, opts, cssString) {
12
12
  this.stringify = stringify
13
13
  this.mapOpts = opts.map || {}
14
14
  this.root = root
15
15
  this.opts = opts
16
+ this.css = cssString
16
17
  }
17
18
 
18
19
  isMap() {
@@ -67,12 +68,16 @@ class MapGenerator {
67
68
  clearAnnotation() {
68
69
  if (this.mapOpts.annotation === false) return
69
70
 
70
- let node
71
- for (let i = this.root.nodes.length - 1; i >= 0; i--) {
72
- node = this.root.nodes[i]
73
- if (node.type !== 'comment') continue
74
- if (node.text.indexOf('# sourceMappingURL=') === 0) {
75
- this.root.removeChild(i)
71
+ if (!this.root && typeof this.css === 'string') {
72
+ this.css = this.css.replace(/(\n)?\/\*#[\S\s]*?\*\/$/gm, '')
73
+ } else {
74
+ let node
75
+ for (let i = this.root.nodes.length - 1; i >= 0; i--) {
76
+ node = this.root.nodes[i]
77
+ if (node.type !== 'comment') continue
78
+ if (node.text.indexOf('# sourceMappingURL=') === 0) {
79
+ this.root.removeChild(i)
80
+ }
76
81
  }
77
82
  }
78
83
  }
@@ -129,7 +134,7 @@ class MapGenerator {
129
134
  if (Buffer) {
130
135
  return Buffer.from(str).toString('base64')
131
136
  } else {
132
- // istanbul ignore next
137
+ /* c8 ignore next 2 */
133
138
  return window.btoa(unescape(encodeURIComponent(str)))
134
139
  }
135
140
  }
@@ -147,7 +152,7 @@ class MapGenerator {
147
152
  } else {
148
153
  content = this.outputFile() + '.map'
149
154
  }
150
-
155
+ /* c8 ignore next 6 */
151
156
  let eol = '\n'
152
157
  if (this.css.includes('\r\n')) eol = '\r\n'
153
158
 
@@ -176,6 +181,32 @@ class MapGenerator {
176
181
  return [this.css, this.map]
177
182
  }
178
183
 
184
+ generateSimpleMap() {
185
+ this.map = new SourceMapGenerator({ file: this.outputFile() })
186
+ this.previousMaps = []
187
+
188
+ let source
189
+ if (this.opts.from) {
190
+ source = this.toUrl(this.opts.from)
191
+ } else {
192
+ source = '<no source>'
193
+ }
194
+
195
+ this.map.addMapping({
196
+ source,
197
+ generated: { line: 1, column: 0 },
198
+ original: { line: 1, column: 0 }
199
+ })
200
+
201
+ if (this.isAnnotation()) this.addAnnotation()
202
+
203
+ if (this.isInline()) {
204
+ return [this.css]
205
+ }
206
+
207
+ return [this.cssString, this.map]
208
+ }
209
+
179
210
  path(file) {
180
211
  if (file.indexOf('<') === 0) return file
181
212
  if (/^\w+:\/\//.test(file)) return file
@@ -193,7 +224,7 @@ class MapGenerator {
193
224
 
194
225
  toUrl(path) {
195
226
  if (sep === '\\') {
196
- // istanbul ignore next
227
+ /* c8 ignore next 2 */
197
228
  path = path.replace(/\\/g, '/')
198
229
  }
199
230
  return encodeURI(path).replace(/[#?]/g, encodeURIComponent)
@@ -206,7 +237,7 @@ class MapGenerator {
206
237
  if (pathToFileURL) {
207
238
  return pathToFileURL(node.source.input.from).toString()
208
239
  } else {
209
- // istanbul ignore next
240
+ /* c8 ignore next 4 */
210
241
  throw new Error(
211
242
  '`map.absolute` option is not available in this PostCSS build'
212
243
  )
@@ -236,6 +267,7 @@ class MapGenerator {
236
267
 
237
268
  if (node && type !== 'end') {
238
269
  mapping.generated.line = line
270
+ /* c8 ignore next */
239
271
  mapping.generated.column = column - 1
240
272
  if (node.source && node.source.start) {
241
273
  mapping.source = this.sourcePath(node)
@@ -243,6 +275,7 @@ class MapGenerator {
243
275
  mapping.original.column = node.source.start.column - 1
244
276
  this.map.addMapping(mapping)
245
277
  } else {
278
+ /* c8 ignore next 8 */
246
279
  mapping.source = noSource
247
280
  mapping.original.line = 1
248
281
  mapping.original.column = 0
@@ -285,6 +318,10 @@ class MapGenerator {
285
318
  generate() {
286
319
  this.clearAnnotation()
287
320
 
321
+ if (pathAvailable && sourceMapAvailable && this.isMap() && !this.root) {
322
+ return this.generateSimpleMap()
323
+ }
324
+
288
325
  if (pathAvailable && sourceMapAvailable && this.isMap()) {
289
326
  return this.generateMap()
290
327
  }
@@ -0,0 +1,37 @@
1
+ import Result, { Message, ResultOptions } from './result.js'
2
+ import { SourceMap } from './postcss.js'
3
+ import Processor from './processor.js'
4
+ import Warning from './warning.js'
5
+ import Root from './root.js'
6
+ import LazyResult from './lazy-result.js'
7
+
8
+ /**
9
+ * A Promise proxy for the result of PostCSS transformations.
10
+ * This lazy result instance doesn't parse css unless `NoWorkResult#root` or `Result#root`
11
+ * are accessed. See the example below for details.
12
+ * A `NoWork` instance is returned by `Processor#process` ONLY when no plugins defined.
13
+ *
14
+ * ```js
15
+ * const noWorkResult = postcss().process(css) // No plugins are defined.
16
+ * // CSS is not parsed
17
+ * let root = noWorkResult.root // now css is parsed because we accessed the root
18
+ * ```
19
+ */
20
+ export default class NoWorkResult implements LazyResult {
21
+ then: Promise<Result>['then']
22
+ catch: Promise<Result>['catch']
23
+ finally: Promise<Result>['finally']
24
+ constructor(processor: Processor, css: string, opts: ResultOptions)
25
+ get [Symbol.toStringTag](): string
26
+ get processor(): Processor
27
+ get opts(): ResultOptions
28
+ get css(): string
29
+ get content(): string
30
+ get map(): SourceMap
31
+ get root(): Root
32
+ get messages(): Message[]
33
+ warnings(): Warning[]
34
+ toString(): string
35
+ sync(): Result
36
+ async(): Promise<Result>
37
+ }
@@ -0,0 +1,131 @@
1
+ 'use strict'
2
+
3
+ let MapGenerator = require('./map-generator')
4
+ let stringify = require('./stringify')
5
+ let warnOnce = require('./warn-once')
6
+ let parse = require('./parse')
7
+ const Result = require('./result')
8
+
9
+ class NoWorkResult {
10
+ constructor(processor, css, opts) {
11
+ this.stringified = false
12
+
13
+ this._processor = processor
14
+ this._css = css
15
+ this._opts = opts
16
+ this._map = undefined
17
+ let root
18
+
19
+ let str = stringify
20
+ this.result = new Result(this._processor, root, this._opts)
21
+ this.result.css = css
22
+
23
+ let self = this
24
+ Object.defineProperty(this.result, 'root', {
25
+ get() {
26
+ return self.root
27
+ }
28
+ })
29
+
30
+ if (this._opts.map) {
31
+ let map = new MapGenerator(str, root, this._opts, css)
32
+ let [generatedCSS, generatedMap] = map.generate()
33
+ if (generatedCSS) {
34
+ this.result.css = generatedCSS
35
+ }
36
+ if (generatedMap) {
37
+ this.result.map = generatedMap
38
+ }
39
+ }
40
+ }
41
+
42
+ get [Symbol.toStringTag]() {
43
+ return 'NoWorkResult'
44
+ }
45
+
46
+ get processor() {
47
+ return this.result.processor
48
+ }
49
+
50
+ get opts() {
51
+ return this.result.opts
52
+ }
53
+
54
+ get css() {
55
+ return this.result.css
56
+ }
57
+
58
+ get content() {
59
+ return this.result.css
60
+ }
61
+
62
+ get map() {
63
+ return this.result.map
64
+ }
65
+
66
+ get root() {
67
+ if (this._root) {
68
+ return this._root
69
+ }
70
+
71
+ let root
72
+ let parser = parse
73
+
74
+ try {
75
+ root = parser(this._css, this._opts)
76
+ } catch (error) {
77
+ this.error = error
78
+ }
79
+
80
+ this._root = root
81
+
82
+ return root
83
+ }
84
+
85
+ get messages() {
86
+ return []
87
+ }
88
+
89
+ warnings() {
90
+ return []
91
+ }
92
+
93
+ toString() {
94
+ return this._css
95
+ }
96
+
97
+ then(onFulfilled, onRejected) {
98
+ if (process.env.NODE_ENV !== 'production') {
99
+ if (!('from' in this._opts)) {
100
+ warnOnce(
101
+ 'Without `from` option PostCSS could generate wrong source map ' +
102
+ 'and will not find Browserslist config. Set it to CSS file path ' +
103
+ 'or to `undefined` to prevent this warning.'
104
+ )
105
+ }
106
+ }
107
+
108
+ return this.async().then(onFulfilled, onRejected)
109
+ }
110
+
111
+ catch(onRejected) {
112
+ return this.async().catch(onRejected)
113
+ }
114
+
115
+ finally(onFinally) {
116
+ return this.async().then(onFinally, onFinally)
117
+ }
118
+
119
+ async() {
120
+ if (this.error) return Promise.reject(this.error)
121
+ return Promise.resolve(this.result)
122
+ }
123
+
124
+ sync() {
125
+ if (this.error) throw this.error
126
+ return this.result
127
+ }
128
+ }
129
+
130
+ module.exports = NoWorkResult
131
+ NoWorkResult.default = NoWorkResult