postcss 8.4.49 → 8.5.10

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.
package/lib/node.js CHANGED
@@ -34,11 +34,8 @@ function cloneNode(obj, parent) {
34
34
 
35
35
  function sourceOffset(inputCSS, position) {
36
36
  // Not all custom syntaxes support `offset` in `source.start` and `source.end`
37
- if (
38
- position &&
39
- typeof position.offset !== 'undefined'
40
- ) {
41
- return position.offset;
37
+ if (position && typeof position.offset !== 'undefined') {
38
+ return position.offset
42
39
  }
43
40
 
44
41
  let column = 1
@@ -63,6 +60,10 @@ function sourceOffset(inputCSS, position) {
63
60
  }
64
61
 
65
62
  class Node {
63
+ get proxyOf() {
64
+ return this
65
+ }
66
+
66
67
  constructor(defaults = {}) {
67
68
  this.raws = {}
68
69
  this[isClean] = false
@@ -204,14 +205,18 @@ class Node {
204
205
  return this.parent.nodes[index + 1]
205
206
  }
206
207
 
207
- positionBy(opts) {
208
+ positionBy(opts = {}) {
208
209
  let pos = this.source.start
209
210
  if (opts.index) {
210
211
  pos = this.positionInside(opts.index)
211
212
  } else if (opts.word) {
212
- let stringRepresentation = this.source.input.css.slice(
213
- sourceOffset(this.source.input.css, this.source.start),
214
- sourceOffset(this.source.input.css, this.source.end)
213
+ let inputString =
214
+ 'document' in this.source.input
215
+ ? this.source.input.document
216
+ : this.source.input.css
217
+ let stringRepresentation = inputString.slice(
218
+ sourceOffset(inputString, this.source.start),
219
+ sourceOffset(inputString, this.source.end)
215
220
  )
216
221
  let index = stringRepresentation.indexOf(opts.word)
217
222
  if (index !== -1) pos = this.positionInside(index)
@@ -222,11 +227,15 @@ class Node {
222
227
  positionInside(index) {
223
228
  let column = this.source.start.column
224
229
  let line = this.source.start.line
225
- let offset = sourceOffset(this.source.input.css, this.source.start)
230
+ let inputString =
231
+ 'document' in this.source.input
232
+ ? this.source.input.document
233
+ : this.source.input.css
234
+ let offset = sourceOffset(inputString, this.source.start)
226
235
  let end = offset + index
227
236
 
228
237
  for (let i = offset; i < end; i++) {
229
- if (this.source.input.css[i] === '\n') {
238
+ if (inputString[i] === '\n') {
230
239
  column = 1
231
240
  line += 1
232
241
  } else {
@@ -234,7 +243,7 @@ class Node {
234
243
  }
235
244
  }
236
245
 
237
- return { column, line }
246
+ return { column, line, offset: end }
238
247
  }
239
248
 
240
249
  prev() {
@@ -243,38 +252,51 @@ class Node {
243
252
  return this.parent.nodes[index - 1]
244
253
  }
245
254
 
246
- rangeBy(opts) {
255
+ rangeBy(opts = {}) {
256
+ let inputString =
257
+ 'document' in this.source.input
258
+ ? this.source.input.document
259
+ : this.source.input.css
247
260
  let start = {
248
261
  column: this.source.start.column,
249
- line: this.source.start.line
262
+ line: this.source.start.line,
263
+ offset: sourceOffset(inputString, this.source.start)
250
264
  }
251
265
  let end = this.source.end
252
266
  ? {
253
267
  column: this.source.end.column + 1,
254
- line: this.source.end.line
268
+ line: this.source.end.line,
269
+ offset:
270
+ typeof this.source.end.offset === 'number'
271
+ ? // `source.end.offset` is exclusive, so we don't need to add 1
272
+ this.source.end.offset
273
+ : // Since line/column in this.source.end is inclusive,
274
+ // the `sourceOffset(... , this.source.end)` returns an inclusive offset.
275
+ // So, we add 1 to convert it to exclusive.
276
+ sourceOffset(inputString, this.source.end) + 1
255
277
  }
256
278
  : {
257
279
  column: start.column + 1,
258
- line: start.line
280
+ line: start.line,
281
+ offset: start.offset + 1
259
282
  }
260
283
 
261
284
  if (opts.word) {
262
- let stringRepresentation = this.source.input.css.slice(
263
- sourceOffset(this.source.input.css, this.source.start),
264
- sourceOffset(this.source.input.css, this.source.end)
285
+ let stringRepresentation = inputString.slice(
286
+ sourceOffset(inputString, this.source.start),
287
+ sourceOffset(inputString, this.source.end)
265
288
  )
266
289
  let index = stringRepresentation.indexOf(opts.word)
267
290
  if (index !== -1) {
268
291
  start = this.positionInside(index)
269
- end = this.positionInside(
270
- index + opts.word.length,
271
- )
292
+ end = this.positionInside(index + opts.word.length)
272
293
  }
273
294
  } else {
274
295
  if (opts.start) {
275
296
  start = {
276
297
  column: opts.start.column,
277
- line: opts.start.line
298
+ line: opts.start.line,
299
+ offset: sourceOffset(inputString, opts.start)
278
300
  }
279
301
  } else if (opts.index) {
280
302
  start = this.positionInside(opts.index)
@@ -283,7 +305,8 @@ class Node {
283
305
  if (opts.end) {
284
306
  end = {
285
307
  column: opts.end.column,
286
- line: opts.end.line
308
+ line: opts.end.line,
309
+ offset: sourceOffset(inputString, opts.end)
287
310
  }
288
311
  } else if (typeof opts.endIndex === 'number') {
289
312
  end = this.positionInside(opts.endIndex)
@@ -296,7 +319,11 @@ class Node {
296
319
  end.line < start.line ||
297
320
  (end.line === start.line && end.column <= start.column)
298
321
  ) {
299
- end = { column: start.column + 1, line: start.line }
322
+ end = {
323
+ column: start.column + 1,
324
+ line: start.line,
325
+ offset: start.offset + 1
326
+ }
300
327
  }
301
328
 
302
329
  return { end, start }
@@ -371,6 +398,7 @@ class Node {
371
398
  } else if (typeof value === 'object' && value.toJSON) {
372
399
  fixed[name] = value.toJSON(null, inputs)
373
400
  } else if (name === 'source') {
401
+ if (value == null) continue
374
402
  let inputId = inputs.get(value.input)
375
403
  if (inputId == null) {
376
404
  inputId = inputsNextIndex
@@ -410,15 +438,11 @@ class Node {
410
438
  return result
411
439
  }
412
440
 
413
- warn(result, text, opts) {
441
+ warn(result, text, opts = {}) {
414
442
  let data = { node: this }
415
443
  for (let i in opts) data[i] = opts[i]
416
444
  return result.warn(text, data)
417
445
  }
418
-
419
- get proxyOf() {
420
- return this
421
- }
422
446
  }
423
447
 
424
448
  module.exports = Node
package/lib/parse.d.ts CHANGED
@@ -4,6 +4,6 @@ interface Parse extends Parser {
4
4
  default: Parse
5
5
  }
6
6
 
7
- declare const parse: Parse
7
+ declare let parse: Parse
8
8
 
9
9
  export = parse
package/lib/parser.js CHANGED
@@ -176,7 +176,7 @@ class Parser {
176
176
  node.source.end.offset++
177
177
 
178
178
  let text = token[1].slice(2, -2)
179
- if (/^\s*$/.test(text)) {
179
+ if (!text.trim()) {
180
180
  node.text = ''
181
181
  node.raws.left = text
182
182
  node.raws.right = ''
@@ -347,6 +347,8 @@ class Parser {
347
347
  if (prev && prev.type === 'rule' && !prev.raws.ownSemicolon) {
348
348
  prev.raws.ownSemicolon = this.spaces
349
349
  this.spaces = ''
350
+ prev.source.end = this.getPosition(token[2])
351
+ prev.source.end.offset += prev.raws.ownSemicolon.length
350
352
  }
351
353
  }
352
354
  }
@@ -591,7 +593,7 @@ class Parser {
591
593
 
592
594
  unknownWord(tokens) {
593
595
  throw this.input.error(
594
- 'Unknown word',
596
+ 'Unknown word ' + tokens[0][1],
595
597
  { offset: tokens[0][2] },
596
598
  { offset: tokens[0][2] + tokens[0][1].length }
597
599
  )
package/lib/postcss.d.mts CHANGED
@@ -1,69 +1,66 @@
1
1
  export {
2
- // postcss function / namespace
3
- default,
4
-
5
- // Value exports from postcss.mjs
6
- stringify,
7
- fromJSON,
8
- // @ts-expect-error This value exists, but it’s untyped.
9
- plugin,
10
- parse,
11
- list,
12
- document,
13
- comment,
14
- atRule,
15
- rule,
16
- decl,
17
- root,
18
- CssSyntaxError,
19
- Declaration,
20
- Container,
21
- Processor,
22
- Document,
23
- Comment,
24
- Warning,
25
- AtRule,
26
- Result,
27
- Input,
28
- Rule,
29
- Root,
30
- Node,
31
-
32
2
  // Type-only exports
33
3
  AcceptedPlugin,
34
4
  AnyNode,
5
+ atRule,
6
+ AtRule,
35
7
  AtRuleProps,
36
8
  Builder,
37
9
  ChildNode,
38
10
  ChildProps,
11
+ comment,
12
+ Comment,
39
13
  CommentProps,
14
+ Container,
40
15
  ContainerProps,
16
+ CssSyntaxError,
17
+ decl,
18
+ Declaration,
41
19
  DeclarationProps,
20
+ // postcss function / namespace
21
+ default,
22
+ document,
23
+ Document,
42
24
  DocumentProps,
43
25
  FilePosition,
26
+ fromJSON,
44
27
  Helpers,
28
+ Input,
45
29
  JSONHydrator,
30
+ // This is a class, but it’s not re-exported. That’s why it’s exported as type-only here.
31
+ type LazyResult,
32
+ list,
46
33
  Message,
34
+ Node,
47
35
  NodeErrorOptions,
48
36
  NodeProps,
49
37
  OldPlugin,
38
+ parse,
50
39
  Parser,
40
+ // @ts-expect-error This value exists, but it’s untyped.
41
+ plugin,
51
42
  Plugin,
52
43
  PluginCreator,
53
44
  Position,
54
45
  Postcss,
55
46
  ProcessOptions,
47
+ Processor,
48
+ Result,
49
+ root,
50
+ Root,
56
51
  RootProps,
52
+ rule,
53
+ Rule,
57
54
  RuleProps,
58
55
  Source,
59
56
  SourceMap,
60
57
  SourceMapOptions,
61
58
  Stringifier,
59
+ // Value exports from postcss.mjs
60
+ stringify,
62
61
  Syntax,
63
62
  TransformCallback,
64
63
  Transformer,
65
- WarningOptions,
66
-
67
- // This is a class, but it’s not re-exported. That’s why it’s exported as type-only here.
68
- type LazyResult
64
+ Warning,
65
+ WarningOptions
69
66
  } from './postcss.js'
package/lib/postcss.d.ts CHANGED
@@ -229,7 +229,7 @@ declare namespace postcss {
229
229
  export interface Parser<RootNode = Document | Root> {
230
230
  (
231
231
  css: { toString(): string } | string,
232
- opts?: Pick<ProcessOptions, 'from' | 'map'>
232
+ opts?: Pick<ProcessOptions, 'document' | 'from' | 'map'>
233
233
  ): RootNode
234
234
  }
235
235
 
@@ -315,6 +315,11 @@ declare namespace postcss {
315
315
  }
316
316
 
317
317
  export interface ProcessOptions<RootNode = Document | Root> {
318
+ /**
319
+ * Input file if it is not simple CSS file, but HTML with <style> or JS with CSS-in-JS blocks.
320
+ */
321
+ document?: string
322
+
318
323
  /**
319
324
  * The path of the CSS source file. You should always set `from`,
320
325
  * because it is used in source map generation and syntax error messages.
@@ -445,9 +450,7 @@ declare namespace postcss {
445
450
  * @param plugins PostCSS plugins.
446
451
  * @return Processor to process multiple CSS.
447
452
  */
448
- declare function postcss(
449
- plugins?: readonly postcss.AcceptedPlugin[]
450
- ): Processor
453
+ declare function postcss(plugins?: readonly postcss.AcceptedPlugin[]): Processor
451
454
  declare function postcss(...plugins: postcss.AcceptedPlugin[]): Processor
452
455
 
453
456
  export = postcss
@@ -3,7 +3,6 @@ import { SourceMapConsumer } from 'source-map-js'
3
3
  import { ProcessOptions } from './postcss.js'
4
4
 
5
5
  declare namespace PreviousMap {
6
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
7
6
  export { PreviousMap_ as default }
8
7
  }
9
8
 
@@ -51,7 +51,8 @@ class PreviousMap {
51
51
  return fromBase64(text.substr(baseUriMatch[0].length))
52
52
  }
53
53
 
54
- let encoding = text.match(/data:application\/json;([^,]+),/)[1]
54
+ let encoding = text.slice('data:application/json;'.length)
55
+ encoding = encoding.slice(0, encoding.indexOf(','))
55
56
  throw new Error('Unsupported source map encoding ' + encoding)
56
57
  }
57
58
 
@@ -12,7 +12,6 @@ import Result from './result.js'
12
12
  import Root from './root.js'
13
13
 
14
14
  declare namespace Processor {
15
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
16
15
  export { Processor_ as default }
17
16
  }
18
17
 
package/lib/processor.js CHANGED
@@ -7,7 +7,7 @@ let Root = require('./root')
7
7
 
8
8
  class Processor {
9
9
  constructor(plugins = []) {
10
- this.version = '8.4.49'
10
+ this.version = '8.5.10'
11
11
  this.plugins = this.normalize(plugins)
12
12
  }
13
13
 
package/lib/result.d.ts CHANGED
@@ -39,7 +39,6 @@ declare namespace Result {
39
39
  plugin?: string
40
40
  }
41
41
 
42
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
43
42
  export { Result_ as default }
44
43
  }
45
44
 
@@ -142,6 +141,16 @@ declare class Result_<RootNode = Document | Root> {
142
141
  */
143
142
  root: RootNode
144
143
 
144
+ /**
145
+ * An alias for the `Result#css` property.
146
+ * Use it with syntaxes that generate non-CSS output.
147
+ *
148
+ * ```js
149
+ * result.css === result.content
150
+ * ```
151
+ */
152
+ get content(): string
153
+
145
154
  /**
146
155
  * @param processor Processor used for this transformation.
147
156
  * @param root Root node after all transformations.
@@ -188,16 +197,6 @@ declare class Result_<RootNode = Document | Root> {
188
197
  * @return Warnings from plugins.
189
198
  */
190
199
  warnings(): Warning[]
191
-
192
- /**
193
- * An alias for the `Result#css` property.
194
- * Use it with syntaxes that generate non-CSS output.
195
- *
196
- * ```js
197
- * result.css === result.content
198
- * ```
199
- */
200
- get content(): string
201
200
  }
202
201
 
203
202
  declare class Result<RootNode = Document | Root> extends Result_<RootNode> {}
package/lib/result.js CHANGED
@@ -3,12 +3,16 @@
3
3
  let Warning = require('./warning')
4
4
 
5
5
  class Result {
6
+ get content() {
7
+ return this.css
8
+ }
9
+
6
10
  constructor(processor, root, opts) {
7
11
  this.processor = processor
8
12
  this.messages = []
9
13
  this.root = root
10
14
  this.opts = opts
11
- this.css = undefined
15
+ this.css = ''
12
16
  this.map = undefined
13
17
  }
14
18
 
@@ -32,10 +36,6 @@ class Result {
32
36
  warnings() {
33
37
  return this.messages.filter(i => i.type === 'warning')
34
38
  }
35
-
36
- get content() {
37
- return this.css
38
- }
39
39
  }
40
40
 
41
41
  module.exports = Result
package/lib/root.d.ts CHANGED
@@ -40,7 +40,6 @@ declare namespace Root {
40
40
  raws?: RootRaws
41
41
  }
42
42
 
43
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
44
43
  export { Root_ as default }
45
44
  }
46
45
 
package/lib/rule.d.ts CHANGED
@@ -22,7 +22,7 @@ declare namespace Rule {
22
22
  between?: string
23
23
 
24
24
  /**
25
- * Contains `true` if there is semicolon after rule.
25
+ * Contains the text of the semicolon after this rule.
26
26
  */
27
27
  ownSemicolon?: string
28
28
 
@@ -44,19 +44,19 @@ declare namespace Rule {
44
44
  /** Information used to generate byte-to-byte equal node string as it was in the origin input. */
45
45
  raws?: RuleRaws
46
46
  } & (
47
- | {
48
- /** Selector or selectors of the rule. */
49
- selector: string
50
- selectors?: never
51
- }
52
- | {
53
- selector?: never
54
- /** Selectors of the rule represented as an array of strings. */
55
- selectors: readonly string[]
56
- }
57
- ) & ContainerProps
47
+ | {
48
+ /** Selector or selectors of the rule. */
49
+ selector: string
50
+ selectors?: never
51
+ }
52
+ | {
53
+ selector?: never
54
+ /** Selectors of the rule represented as an array of strings. */
55
+ selectors: readonly string[]
56
+ }
57
+ ) &
58
+ ContainerProps
58
59
 
59
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
60
60
  export { Rule_ as default }
61
61
  }
62
62
 
@@ -83,14 +83,6 @@ declare class Rule_ extends Container {
83
83
  parent: ContainerWithChildren | undefined
84
84
  raws: Rule.RuleRaws
85
85
  type: 'rule'
86
- constructor(defaults?: Rule.RuleProps)
87
-
88
- assign(overrides: object | Rule.RuleProps): this
89
- clone(overrides?: Partial<Rule.RuleProps>): this
90
-
91
- cloneAfter(overrides?: Partial<Rule.RuleProps>): this
92
-
93
- cloneBefore(overrides?: Partial<Rule.RuleProps>): this
94
86
  /**
95
87
  * The rule’s full selector represented as a string.
96
88
  *
@@ -101,6 +93,7 @@ declare class Rule_ extends Container {
101
93
  * ```
102
94
  */
103
95
  get selector(): string
96
+
104
97
  set selector(value: string)
105
98
  /**
106
99
  * An array containing the rule’s individual selectors.
@@ -118,7 +111,14 @@ declare class Rule_ extends Container {
118
111
  * ```
119
112
  */
120
113
  get selectors(): string[]
114
+
121
115
  set selectors(values: string[])
116
+
117
+ constructor(defaults?: Rule.RuleProps)
118
+ assign(overrides: object | Rule.RuleProps): this
119
+ clone(overrides?: Partial<Rule.RuleProps>): this
120
+ cloneAfter(overrides?: Partial<Rule.RuleProps>): this
121
+ cloneBefore(overrides?: Partial<Rule.RuleProps>): this
122
122
  }
123
123
 
124
124
  declare class Rule extends Rule_ {}
package/lib/rule.js CHANGED
@@ -4,12 +4,6 @@ let Container = require('./container')
4
4
  let list = require('./list')
5
5
 
6
6
  class Rule extends Container {
7
- constructor(defaults) {
8
- super(defaults)
9
- this.type = 'rule'
10
- if (!this.nodes) this.nodes = []
11
- }
12
-
13
7
  get selectors() {
14
8
  return list.comma(this.selector)
15
9
  }
@@ -19,6 +13,12 @@ class Rule extends Container {
19
13
  let sep = match ? match[0] : ',' + this.raw('between', 'beforeOpen')
20
14
  this.selector = values.join(sep)
21
15
  }
16
+
17
+ constructor(defaults) {
18
+ super(defaults)
19
+ this.type = 'rule'
20
+ if (!this.nodes) this.nodes = []
21
+ }
22
22
  }
23
23
 
24
24
  module.exports = Rule
@@ -11,7 +11,6 @@ import {
11
11
  } from './postcss.js'
12
12
 
13
13
  declare namespace Stringifier {
14
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
15
14
  export { Stringifier_ as default }
16
15
  }
17
16
 
@@ -25,7 +24,7 @@ declare class Stringifier_ {
25
24
  comment(node: Comment): void
26
25
  decl(node: Declaration, semicolon?: boolean): void
27
26
  document(node: Document): void
28
- raw(node: AnyNode, own: null | string, detect?: string): string
27
+ raw(node: AnyNode, own: null | string, detect?: string): boolean | string
29
28
  rawBeforeClose(root: Root): string | undefined
30
29
  rawBeforeComment(root: Root, node: Comment): string | undefined
31
30
  rawBeforeDecl(root: Root, node: Declaration): string | undefined
@@ -35,7 +34,7 @@ declare class Stringifier_ {
35
34
  rawEmptyBody(root: Root): string | undefined
36
35
  rawIndent(root: Root): string | undefined
37
36
  rawSemicolon(root: Root): boolean | undefined
38
- rawValue(node: AnyNode, prop: string): string
37
+ rawValue(node: AnyNode, prop: string): number | string
39
38
  root(node: Root): void
40
39
  rule(node: Rule): void
41
40
  stringify(node: AnyNode, semicolon?: boolean): void
@@ -1,5 +1,17 @@
1
1
  'use strict'
2
2
 
3
+ // Escapes sequences that could break out of an HTML <style> context.
4
+ // Uses CSS unicode escaping (\3c = '<') which is valid CSS and parsed
5
+ // correctly by all compliant CSS consumers.
6
+ const STYLE_TAG = /(<)(\/?style\b)/gi
7
+ const COMMENT_OPEN = /(<)(!--)/g
8
+
9
+ function escapeHTMLInCSS(str) {
10
+ if (typeof str !== 'string') return str
11
+ if (!str.includes('<')) return str
12
+ return str.replace(STYLE_TAG, '\\3c $2').replace(COMMENT_OPEN, '\\3c $2')
13
+ }
14
+
3
15
  const DEFAULT_RAW = {
4
16
  after: '\n',
5
17
  beforeClose: '\n',
@@ -38,7 +50,7 @@ class Stringifier {
38
50
  this.block(node, name + params)
39
51
  } else {
40
52
  let end = (node.raws.between || '') + (semicolon ? ';' : '')
41
- this.builder(name + params + end, node)
53
+ this.builder(escapeHTMLInCSS(name + params + end), node)
42
54
  }
43
55
  }
44
56
 
@@ -73,7 +85,7 @@ class Stringifier {
73
85
 
74
86
  block(node, start) {
75
87
  let between = this.raw(node, 'between', 'beforeOpen')
76
- this.builder(start + between + '{', node, 'start')
88
+ this.builder(escapeHTMLInCSS(start + between) + '{', node, 'start')
77
89
 
78
90
  let after
79
91
  if (node.nodes && node.nodes.length) {
@@ -83,7 +95,7 @@ class Stringifier {
83
95
  after = this.raw(node, 'after', 'emptyBody')
84
96
  }
85
97
 
86
- if (after) this.builder(after)
98
+ if (after) this.builder(escapeHTMLInCSS(after))
87
99
  this.builder('}', node, 'end')
88
100
  }
89
101
 
@@ -95,10 +107,11 @@ class Stringifier {
95
107
  }
96
108
 
97
109
  let semicolon = this.raw(node, 'semicolon')
110
+ let isDocument = node.type === 'document'
98
111
  for (let i = 0; i < node.nodes.length; i++) {
99
112
  let child = node.nodes[i]
100
113
  let before = this.raw(child, 'before')
101
- if (before) this.builder(before)
114
+ if (before) this.builder(isDocument ? before : escapeHTMLInCSS(before))
102
115
  this.stringify(child, last !== i || semicolon)
103
116
  }
104
117
  }
@@ -106,7 +119,7 @@ class Stringifier {
106
119
  comment(node) {
107
120
  let left = this.raw(node, 'left', 'commentLeft')
108
121
  let right = this.raw(node, 'right', 'commentRight')
109
- this.builder('/*' + left + node.text + right + '*/', node)
122
+ this.builder(escapeHTMLInCSS('/*' + left + node.text + right + '*/'), node)
110
123
  }
111
124
 
112
125
  decl(node, semicolon) {
@@ -118,7 +131,7 @@ class Stringifier {
118
131
  }
119
132
 
120
133
  if (semicolon) string += ';'
121
- this.builder(string, node)
134
+ this.builder(escapeHTMLInCSS(string), node)
122
135
  }
123
136
 
124
137
  document(node) {
@@ -324,13 +337,17 @@ class Stringifier {
324
337
 
325
338
  root(node) {
326
339
  this.body(node)
327
- if (node.raws.after) this.builder(node.raws.after)
340
+ if (node.raws.after) {
341
+ let after = node.raws.after
342
+ let isDocument = node.parent && node.parent.type === 'document'
343
+ this.builder(isDocument ? after : escapeHTMLInCSS(after))
344
+ }
328
345
  }
329
346
 
330
347
  rule(node) {
331
348
  this.block(node, this.rawValue(node, 'selector'))
332
349
  if (node.raws.ownSemicolon) {
333
- this.builder(node.raws.ownSemicolon, node, 'end')
350
+ this.builder(escapeHTMLInCSS(node.raws.ownSemicolon), node, 'end')
334
351
  }
335
352
  }
336
353