postcss-nested 5.0.5 → 6.0.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.
package/README.md CHANGED
@@ -59,10 +59,12 @@ html {
59
59
 
60
60
  Related plugins:
61
61
 
62
+ * Use [`postcss-atroot`] for `@at-root` at-rule to move nested child
63
+ to the CSS root.
62
64
  * Use [`postcss-current-selector`] **after** this plugin if you want
63
- to use current selector in properties or variables values.
65
+ to use current selector in properties or variables values.
64
66
  * Use [`postcss-nested-ancestors`] **before** this plugin if you want
65
- to reference any ancestor element directly in your selectors with `^&`.
67
+ to reference any ancestor element directly in your selectors with `^&`.
66
68
 
67
69
  Alternatives:
68
70
 
@@ -75,121 +77,14 @@ Alternatives:
75
77
  alt="Sponsored by Evil Martians" width="236" height="54">
76
78
  </a>
77
79
 
80
+ [`postcss-atroot`]: https://github.com/OEvgeny/postcss-atroot
78
81
  [`postcss-current-selector`]: https://github.com/komlev/postcss-current-selector
79
82
  [`postcss-nested-ancestors`]: https://github.com/toomuchdesign/postcss-nested-ancestors
80
83
  [`postcss-nested-props`]: https://github.com/jedmao/postcss-nested-props
81
- [`postcss-nesting`]: https://github.com/jonathantneal/postcss-nesting
84
+ [`postcss-nesting`]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting
82
85
  [CSSWG draft]: https://drafts.csswg.org/css-nesting-1/
83
86
  [PostCSS]: https://github.com/postcss/postcss
84
87
 
85
88
 
86
- ## Usage
87
-
88
- **Step 1:** Install plugin:
89
-
90
- ```sh
91
- npm install --save-dev postcss postcss-nested
92
- ```
93
-
94
- **Step 2:** Check your project for existing PostCSS config: `postcss.config.js`
95
- in the project root, `"postcss"` section in `package.json`
96
- or `postcss` in bundle config.
97
-
98
- If you do not use PostCSS, add it according to [official docs]
99
- and set this plugin in settings.
100
-
101
- **Step 3:** Add the plugin to plugins list:
102
-
103
- ```diff
104
- module.exports = {
105
- plugins: [
106
- + require('postcss-nested'),
107
- require('autoprefixer')
108
- ]
109
- }
110
- ```
111
-
112
- [official docs]: https://github.com/postcss/postcss#usage
113
-
114
-
115
- ## Options
116
-
117
- ### `bubble`
118
-
119
- By default, plugin will bubble only `@media` and `@supports` at-rules.
120
- You can add your custom at-rules to this list by `bubble` option:
121
-
122
- ```js
123
- postcss([ require('postcss-nested')({ bubble: ['phone'] }) ])
124
- ```
125
-
126
- ```css
127
- /* input */
128
- a {
129
- color: white;
130
- @phone {
131
- color: black;
132
- }
133
- }
134
- /* output */
135
- a {
136
- color: white;
137
- }
138
- @phone {
139
- a {
140
- color: black;
141
- }
142
- }
143
- ```
144
-
145
-
146
- ### `unwrap`
147
-
148
- By default, plugin will unwrap only `@font-face`, `@keyframes` and `@document`
149
- at-rules. You can add your custom at-rules to this list by `unwrap` option:
150
-
151
- ```js
152
- postcss([ require('postcss-nested')({ unwrap: ['phone'] }) ])
153
- ```
154
-
155
- ```css
156
- /* input */
157
- a {
158
- color: white;
159
- @phone {
160
- color: black;
161
- }
162
- }
163
- /* output */
164
- a {
165
- color: white;
166
- }
167
- @phone {
168
- color: black;
169
- }
170
- ```
171
-
172
-
173
- ### `preserveEmpty`
174
-
175
- By default, plugin will strip out any empty selector generated by intermediate
176
- nesting levels. You can set `preserveEmpty` to `true` to preserve them.
177
-
178
- ```css
179
- .a {
180
- .b {
181
- color: black;
182
- }
183
- }
184
- ```
185
-
186
- Will be compiled to:
187
-
188
- ```css
189
- .a { }
190
- .a .b {
191
- color: black;
192
- }
193
- ```
194
-
195
- This is especially useful if you want to export the empty classes with `postcss-modules`.
89
+ ## Docs
90
+ Read **[full docs](https://github.com/postcss/postcss-nested#readme)** on GitHub.
package/index.d.ts CHANGED
@@ -1,30 +1,41 @@
1
- // Original definitions (@types/postcss-nested) by Maxim Vorontsov <https://github.com/VorontsovMaxim>
1
+ // Original definitions (@types/postcss-nested)
2
+ // by Maxim Vorontsov <https://github.com/VorontsovMaxim>
2
3
 
3
- import { PluginCreator } from 'postcss';
4
+ import { PluginCreator } from 'postcss'
4
5
 
5
6
  declare namespace nested {
6
7
  interface Options {
7
8
  /**
8
- * By default, plugin will bubble only @media and @supports at-rules.
9
- * You can add your custom at-rules to this list by this option.
9
+ * By default, plugin will bubble only `@media`, `@supports` and `@layer`
10
+ * at-rules. Use this option to add your custom at-rules to this list.
10
11
  */
11
- bubble?: string[];
12
+ bubble?: string[]
12
13
 
13
14
  /**
14
- * By default, plugin will unwrap only @font-face, @keyframes and @document at-rules.
15
- * You can add your custom at-rules to this list by this option.
15
+ * By default, plugin will unwrap only `@font-face`, `@keyframes`,
16
+ * and `@document` at-rules. You can add your custom at-rules
17
+ * to this list by this option.
16
18
  */
17
- unwrap?: string[];
19
+ unwrap?: string[]
18
20
 
19
21
  /**
20
- * By default, plugin will strip out any empty selector generated by intermediate nesting
21
- * levels. You can set this option to true to preserve them.
22
+ * By default, plugin will strip out any empty selector generated
23
+ * by intermediate nesting levels. You can set this option to `true`
24
+ * to preserve them.
22
25
  */
23
- preserveEmpty?: boolean;
26
+ preserveEmpty?: boolean
27
+
28
+ /**
29
+ * The plugin supports the SCSS custom at-rule `@at-root` which breaks
30
+ * rule blocks out of their nested position. If you want, you can choose
31
+ * a new custom name for this rule in your code.
32
+ */
33
+ rootRuleName?: string
24
34
  }
25
35
 
26
- type Nested = PluginCreator<Options>;
36
+ type Nested = PluginCreator<Options>
27
37
  }
28
38
 
29
- declare const nested: nested.Nested;
30
- export = nested;
39
+ declare const nested: nested.Nested
40
+
41
+ export = nested
package/index.js CHANGED
@@ -1,14 +1,17 @@
1
+ const { Rule, AtRule } = require('postcss')
1
2
  let parser = require('postcss-selector-parser')
2
3
 
3
- function parse (str, rule) {
4
+ /**
5
+ * Run a selector string through postcss-selector-parser
6
+ */
7
+ function parse(rawSelector, rule) {
4
8
  let nodes
5
- let saver = parser(parsed => {
6
- nodes = parsed
7
- })
8
9
  try {
9
- saver.processSync(str)
10
+ parser(parsed => {
11
+ nodes = parsed
12
+ }).processSync(rawSelector)
10
13
  } catch (e) {
11
- if (str.includes(':')) {
14
+ if (rawSelector.includes(':')) {
12
15
  throw rule ? rule.error('Missed semicolon') : e
13
16
  } else {
14
17
  throw rule ? rule.error(e.message) : e
@@ -17,19 +20,27 @@ function parse (str, rule) {
17
20
  return nodes.at(0)
18
21
  }
19
22
 
20
- function replace (nodes, parent) {
23
+ /**
24
+ * Replaces the "&" token in a node's selector with the parent selector
25
+ * similar to what SCSS does.
26
+ *
27
+ * Mutates the nodes list
28
+ */
29
+ function interpolateAmpInSelector(nodes, parent) {
21
30
  let replaced = false
22
- nodes.each(i => {
23
- if (i.type === 'nesting') {
24
- let clonedParent = parent.clone()
25
- if (i.value !== '&') {
26
- i.replaceWith(parse(i.value.replace('&', clonedParent.toString())))
31
+ nodes.each(node => {
32
+ if (node.type === 'nesting') {
33
+ let clonedParent = parent.clone({})
34
+ if (node.value !== '&') {
35
+ node.replaceWith(
36
+ parse(node.value.replace('&', clonedParent.toString()))
37
+ )
27
38
  } else {
28
- i.replaceWith(clonedParent)
39
+ node.replaceWith(clonedParent)
29
40
  }
30
41
  replaced = true
31
- } else if (i.nodes) {
32
- if (replace(i, parent)) {
42
+ } else if ('nodes' in node && node.nodes) {
43
+ if (interpolateAmpInSelector(node, parent)) {
33
44
  replaced = true
34
45
  }
35
46
  }
@@ -37,89 +48,228 @@ function replace (nodes, parent) {
37
48
  return replaced
38
49
  }
39
50
 
40
- function selectors (parent, child) {
41
- let result = []
42
- parent.selectors.forEach(i => {
43
- let parentNode = parse(i, parent)
44
-
45
- child.selectors.forEach(j => {
46
- if (j.length) {
47
- let node = parse(j, child)
48
- let replaced = replace(node, parentNode)
49
- if (!replaced) {
50
- node.prepend(parser.combinator({ value: ' ' }))
51
- node.prepend(parentNode.clone())
52
- }
53
- result.push(node.toString())
51
+ /**
52
+ * Combines parent and child selectors, in a SCSS-like way
53
+ */
54
+ function mergeSelectors(parent, child) {
55
+ let merged = []
56
+ parent.selectors.forEach(sel => {
57
+ let parentNode = parse(sel, parent)
58
+
59
+ child.selectors.forEach(selector => {
60
+ if (!selector) {
61
+ return
62
+ }
63
+ let node = parse(selector, child)
64
+ let replaced = interpolateAmpInSelector(node, parentNode)
65
+ if (!replaced) {
66
+ node.prepend(parser.combinator({ value: ' ' }))
67
+ node.prepend(parentNode.clone({}))
54
68
  }
69
+ merged.push(node.toString())
55
70
  })
56
71
  })
57
- return result
72
+ return merged
58
73
  }
59
74
 
60
- function pickComment (comment, after) {
61
- if (comment && comment.type === 'comment') {
62
- after.after(comment)
63
- return comment
64
- } else {
65
- return after
75
+ /**
76
+ * Move a child and its preceeding comment(s) to after "after"
77
+ */
78
+ function breakOut(child, after) {
79
+ let prev = child.prev()
80
+ after.after(child)
81
+ while (prev && prev.type === 'comment') {
82
+ let nextPrev = prev.prev()
83
+ after.after(prev)
84
+ prev = nextPrev
66
85
  }
86
+ return child
67
87
  }
68
88
 
69
- function atruleChilds (rule, atrule, bubbling) {
70
- let children = []
71
- atrule.each(child => {
72
- if (child.type === 'comment') {
73
- children.push(child)
74
- } else if (child.type === 'decl') {
75
- children.push(child)
76
- } else if (child.type === 'rule' && bubbling) {
77
- child.selectors = selectors(rule, child)
78
- } else if (child.type === 'atrule') {
79
- atruleChilds(rule, child, bubbling)
80
- }
81
- })
82
- if (bubbling) {
83
- if (children.length) {
84
- let clone = rule.clone({ nodes: [] })
85
- for (let child of children) {
86
- clone.append(child)
89
+ function createFnAtruleChilds(bubble) {
90
+ return function atruleChilds(rule, atrule, bubbling, mergeSels = bubbling) {
91
+ let children = []
92
+ atrule.each(child => {
93
+ if (child.type === 'rule' && bubbling) {
94
+ if (mergeSels) {
95
+ child.selectors = mergeSelectors(rule, child)
96
+ }
97
+ } else if (child.type === 'atrule' && child.nodes) {
98
+ if (bubble[child.name]) {
99
+ atruleChilds(rule, child, mergeSels)
100
+ } else if (atrule[rootRuleMergeSel] !== false) {
101
+ children.push(child)
102
+ }
103
+ } else {
104
+ children.push(child)
105
+ }
106
+ })
107
+ if (bubbling) {
108
+ if (children.length) {
109
+ let clone = rule.clone({ nodes: [] })
110
+ for (let child of children) {
111
+ clone.append(child)
112
+ }
113
+ atrule.prepend(clone)
87
114
  }
88
- atrule.prepend(clone)
89
115
  }
90
116
  }
91
117
  }
92
118
 
93
- function pickDeclarations (selector, declarations, after, Rule) {
119
+ function pickDeclarations(selector, declarations, after) {
94
120
  let parent = new Rule({
95
121
  selector,
96
122
  nodes: []
97
123
  })
98
-
99
- for (let declaration of declarations) {
100
- parent.append(declaration)
101
- }
102
-
124
+ parent.append(declarations)
103
125
  after.after(parent)
104
126
  return parent
105
127
  }
106
128
 
107
- function atruleNames (defaults, custom) {
129
+ function atruleNames(defaults, custom) {
108
130
  let list = {}
109
- for (let i of defaults) {
110
- list[i] = true
131
+ for (let name of defaults) {
132
+ list[name] = true
111
133
  }
112
134
  if (custom) {
113
- for (let i of custom) {
114
- let name = i.replace(/^@/, '')
115
- list[name] = true
135
+ for (let name of custom) {
136
+ list[name.replace(/^@/, '')] = true
116
137
  }
117
138
  }
118
139
  return list
119
140
  }
120
141
 
142
+ function parseRootRuleParams(params) {
143
+ params = params.trim()
144
+ let braceBlock = params.match(/^\((.*)\)$/)
145
+ if (!braceBlock) {
146
+ return { type: 'basic', selector: params }
147
+ }
148
+ let bits = braceBlock[1].match(/^(with(?:out)?):(.+)$/)
149
+ if (bits) {
150
+ let allowlist = bits[1] === 'with'
151
+ let rules = Object.fromEntries(
152
+ bits[2]
153
+ .trim()
154
+ .split(/\s+/)
155
+ .map(name => [name, true])
156
+ )
157
+ if (allowlist && rules.all) {
158
+ return { type: 'noop' }
159
+ }
160
+ let escapes = rule => !!rules[rule]
161
+ if (rules.all) {
162
+ escapes = () => true
163
+ } else if (allowlist) {
164
+ escapes = rule => (rule === 'all' ? false : !rules[rule])
165
+ }
166
+
167
+ return {
168
+ type: 'withrules',
169
+ escapes
170
+ }
171
+ }
172
+ // Unrecognized brace block
173
+ return { type: 'unknown' }
174
+ }
175
+
176
+ function getAncestorRules(leaf) {
177
+ let lineage = []
178
+ let parent = leaf.parent
179
+
180
+ while (parent && parent instanceof AtRule) {
181
+ lineage.push(parent)
182
+ parent = parent.parent
183
+ }
184
+ return lineage
185
+ }
186
+
187
+ function unwrapRootRule(rule) {
188
+ let escapes = rule[rootRuleEscapes]
189
+
190
+ if (!escapes) {
191
+ rule.after(rule.nodes)
192
+ } else {
193
+ let nodes = rule.nodes
194
+
195
+ let topEscaped
196
+ let topEscapedIdx = -1
197
+ let breakoutLeaf
198
+ let breakoutRoot
199
+ let clone
200
+
201
+ let lineage = getAncestorRules(rule)
202
+ lineage.forEach((parent, i) => {
203
+ if (escapes(parent.name)) {
204
+ topEscaped = parent
205
+ topEscapedIdx = i
206
+ breakoutRoot = clone
207
+ } else {
208
+ let oldClone = clone
209
+ clone = parent.clone({ nodes: [] })
210
+ oldClone && clone.append(oldClone)
211
+ breakoutLeaf = breakoutLeaf || clone
212
+ }
213
+ })
214
+
215
+ if (!topEscaped) {
216
+ rule.after(nodes)
217
+ } else if (!breakoutRoot) {
218
+ topEscaped.after(nodes)
219
+ } else {
220
+ let leaf = breakoutLeaf
221
+ leaf.append(nodes)
222
+ topEscaped.after(breakoutRoot)
223
+ }
224
+
225
+ if (rule.next() && topEscaped) {
226
+ let restRoot
227
+ lineage.slice(0, topEscapedIdx + 1).forEach((parent, i, arr) => {
228
+ let oldRoot = restRoot
229
+ restRoot = parent.clone({ nodes: [] })
230
+ oldRoot && restRoot.append(oldRoot)
231
+
232
+ let nextSibs = []
233
+ let _child = arr[i - 1] || rule
234
+ let next = _child.next()
235
+ while (next) {
236
+ nextSibs.push(next)
237
+ next = next.next()
238
+ }
239
+ restRoot.append(nextSibs)
240
+ })
241
+ restRoot && (breakoutRoot || nodes[nodes.length - 1]).after(restRoot)
242
+ }
243
+ }
244
+
245
+ rule.remove()
246
+ }
247
+
248
+ const rootRuleMergeSel = Symbol('rootRuleMergeSel')
249
+ const rootRuleEscapes = Symbol('rootRuleEscapes')
250
+
251
+ function normalizeRootRule(rule) {
252
+ let { params } = rule
253
+ let { type, selector, escapes } = parseRootRuleParams(params)
254
+ if (type === 'unknown') {
255
+ throw rule.error(
256
+ `Unknown @${rule.name} parameter ${JSON.stringify(params)}`
257
+ )
258
+ }
259
+ if (type === 'basic' && selector) {
260
+ let selectorBlock = new Rule({ selector, nodes: rule.nodes })
261
+ rule.removeAll()
262
+ rule.append(selectorBlock)
263
+ }
264
+ rule[rootRuleEscapes] = escapes
265
+ rule[rootRuleMergeSel] = escapes ? !escapes('all') : type === 'noop'
266
+ }
267
+
268
+ const hasRootRule = Symbol('hasRootRule')
269
+
121
270
  module.exports = (opts = {}) => {
122
- let bubble = atruleNames(['media', 'supports'], opts.bubble)
271
+ let bubble = atruleNames(['media', 'supports', 'layer'], opts.bubble)
272
+ let atruleChilds = createFnAtruleChilds(bubble)
123
273
  let unwrap = atruleNames(
124
274
  [
125
275
  'document',
@@ -130,11 +280,20 @@ module.exports = (opts = {}) => {
130
280
  ],
131
281
  opts.unwrap
132
282
  )
283
+ let rootRuleName = (opts.rootRuleName || 'at-root').replace(/^@/, '')
133
284
  let preserveEmpty = opts.preserveEmpty
134
285
 
135
286
  return {
136
287
  postcssPlugin: 'postcss-nested',
137
- Rule (rule, { Rule }) {
288
+
289
+ Once(root) {
290
+ root.walkAtRules(rootRuleName, node => {
291
+ normalizeRootRule(node)
292
+ root[hasRootRule] = true
293
+ })
294
+ },
295
+
296
+ Rule(rule) {
138
297
  let unwrapped = false
139
298
  let after = rule
140
299
  let copyDeclarations = false
@@ -143,48 +302,33 @@ module.exports = (opts = {}) => {
143
302
  rule.each(child => {
144
303
  if (child.type === 'rule') {
145
304
  if (declarations.length) {
146
- after = pickDeclarations(rule.selector, declarations, after, Rule)
305
+ after = pickDeclarations(rule.selector, declarations, after)
147
306
  declarations = []
148
307
  }
149
308
 
150
309
  copyDeclarations = true
151
310
  unwrapped = true
152
- child.selectors = selectors(rule, child)
153
- after = pickComment(child.prev(), after)
154
- after.after(child)
155
- after = child
311
+ child.selectors = mergeSelectors(rule, child)
312
+ after = breakOut(child, after)
156
313
  } else if (child.type === 'atrule') {
157
314
  if (declarations.length) {
158
- after = pickDeclarations(rule.selector, declarations, after, Rule)
315
+ after = pickDeclarations(rule.selector, declarations, after)
159
316
  declarations = []
160
317
  }
161
-
162
- if (child.name === 'at-root') {
318
+ if (child.name === rootRuleName) {
163
319
  unwrapped = true
164
- atruleChilds(rule, child, false)
165
-
166
- let nodes = child.nodes
167
- if (child.params) {
168
- nodes = new Rule({ selector: child.params, nodes })
169
- }
170
-
171
- after.after(nodes)
172
- after = nodes
173
- child.remove()
320
+ atruleChilds(rule, child, true, child[rootRuleMergeSel])
321
+ after = breakOut(child, after)
174
322
  } else if (bubble[child.name]) {
175
323
  copyDeclarations = true
176
324
  unwrapped = true
177
325
  atruleChilds(rule, child, true)
178
- after = pickComment(child.prev(), after)
179
- after.after(child)
180
- after = child
326
+ after = breakOut(child, after)
181
327
  } else if (unwrap[child.name]) {
182
328
  copyDeclarations = true
183
329
  unwrapped = true
184
330
  atruleChilds(rule, child, false)
185
- after = pickComment(child.prev(), after)
186
- after.after(child)
187
- after = child
331
+ after = breakOut(child, after)
188
332
  } else if (copyDeclarations) {
189
333
  declarations.push(child)
190
334
  }
@@ -194,13 +338,20 @@ module.exports = (opts = {}) => {
194
338
  })
195
339
 
196
340
  if (declarations.length) {
197
- after = pickDeclarations(rule.selector, declarations, after, Rule)
341
+ after = pickDeclarations(rule.selector, declarations, after)
198
342
  }
199
343
 
200
344
  if (unwrapped && preserveEmpty !== true) {
201
345
  rule.raws.semicolon = true
202
346
  if (rule.nodes.length === 0) rule.remove()
203
347
  }
348
+ },
349
+
350
+ RootExit(root) {
351
+ if (root[hasRootRule]) {
352
+ root.walkAtRules(rootRuleName, unwrapRootRule)
353
+ root[hasRootRule] = false
354
+ }
204
355
  }
205
356
  }
206
357
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postcss-nested",
3
- "version": "5.0.5",
3
+ "version": "6.0.0",
4
4
  "description": "PostCSS plugin to unwrap nested rules like how Sass does it",
5
5
  "keywords": [
6
6
  "postcss",
@@ -13,16 +13,16 @@
13
13
  "license": "MIT",
14
14
  "repository": "postcss/postcss-nested",
15
15
  "engines": {
16
- "node": ">=10.0"
16
+ "node": ">=12.0"
17
17
  },
18
18
  "funding": {
19
19
  "type": "opencollective",
20
20
  "url": "https://opencollective.com/postcss/"
21
21
  },
22
22
  "peerDependencies": {
23
- "postcss": "^8.1.13"
23
+ "postcss": "^8.2.14"
24
24
  },
25
25
  "dependencies": {
26
- "postcss-selector-parser": "^6.0.4"
26
+ "postcss-selector-parser": "^6.0.10"
27
27
  }
28
28
  }
package/CHANGELOG.md DELETED
@@ -1,110 +0,0 @@
1
- # Change Log
2
- This project adheres to [Semantic Versioning](http://semver.org/).
3
-
4
- ## 5.0.5
5
- * Fixed `,` at the tail (by Jesse de Boer).
6
-
7
- ## 5.0.4
8
- * Fixed nested `&` at the tail (by Raphael Luba).
9
- * Fixed docs (by Samuel Charpentier).
10
-
11
- ## 5.0.3
12
- * Fixed compability with Autoprefixer by adding `@-webkit-keyframes` support.
13
-
14
- ## 5.0.2
15
- * Fixed compatibility with `postcss-mixins` by moving to visitor API.
16
-
17
- ## 5.0.1
18
- * Fixed PostCSS 8.1 compatibility.
19
- * Added funding links.
20
-
21
- ## 5.0
22
- * Moved to PostCSS 8.
23
- * Moved `postcss` to `peerDependencies`.
24
-
25
- ## 4.2.3
26
- * Fixed declarations after nested rule and before at-rule (by Rodion Demikhov).
27
-
28
- ## 4.2.2
29
- * Fixed wrong specificity order of declarations (by Rodion Demikhov).
30
-
31
- ## 4.2.1
32
- * Fix TypeScript definitions (by Avi Vahl).
33
-
34
- ## 4.2
35
- * Add `@at-root` support (by Jason Quense).
36
-
37
- ## 4.1.2
38
- * Improve error messsage on broken selector
39
-
40
- ## 4.1.1
41
- * Add `&(:hover)` support (by Ben Delarre).
42
-
43
- ## 4.1
44
- * Add `unwrap` option.
45
-
46
- ## 4.0
47
- * Use PostCSS 7 (by Aleks Hudochenkov).
48
- * Remove Node.js 4 support.
49
-
50
- ## 3.0
51
- * Add `@font-face` to bubbling at-rules (by Valeriy Komlev).
52
- * Add special logic for `@font-face` bubbling (by Phanindra Pydisetty).
53
- * Use PostCSS selector parser 3.0.
54
-
55
- ## 2.1.2
56
- * Fix replacing multiple `&`.
57
-
58
- ## 2.1.1
59
- * Fix `:not(&)` support.
60
-
61
- ## 2.1
62
- * Add `preserveEmpty` option (by Federico Zivolo).
63
-
64
- ## 2.0.4
65
- * Fix finding `&` in some selectors (by Stepan Mikhaylyuk).
66
-
67
- ## 2.0.3
68
- * Doesn’t replace `&` inside string (by Paul Kiddle).
69
-
70
- ## 2.0.2
71
- * Fix comments moving regression.
72
-
73
- ## 2.0.1
74
- * Fix rules order regression (by Dmitry Vibe).
75
-
76
- ## 2.0
77
- * Use PostCSS 6 API.
78
-
79
- ## 1.0.1
80
- * Clean up npm package.
81
-
82
- ## 1.0
83
- * Use PostCSS 5.0 API.
84
- * Do not add spaces to selector in compressed CSS.
85
- * Move nodes with its comment above.
86
-
87
- ## 0.3.2
88
- * Fix `@supports` at-rule support (by Ben Briggs).
89
-
90
- ## 0.3.1
91
- * Pass PostCSS Plugin Guidelines.
92
-
93
- ## 0.3
94
- * Do not unwrap custom at-rules.
95
- * Add `bubble` option to unwrap some custom at-rules.
96
- * Support PostCSS 4.1 API.
97
- * Fix last semicolon after unwrapping.
98
-
99
- ## 0.2.2
100
- * Module returns function to have common PostCSS API.
101
-
102
- ## 0.2.1
103
- * Add comma support to selectors unwrap.
104
-
105
- ## 0.2
106
- * Use PostCSS 4.0.
107
- * Fix indent, when move rules to other parent.
108
-
109
- ## 0.1
110
- * Initial release.