Skip to content

Commit b4c3e57

Browse files
committed
Add entries for 4.0 to the changelog.
1 parent e26cb18 commit b4c3e57

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed

API.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ Arguments:
7878

7979
* `props (object)`: The new node's properties.
8080

81+
Notes:
82+
* **Descendant Combinators** The value of descendant combinators created by the
83+
parser always just a single space (`" "`). For descendant selectors with no
84+
comments, additional space is now stored in `node.spaces.before`. Depending
85+
on the location of comments, additional spaces may be stored in
86+
`node.raws.spaces.before`, `node.raws.spaces.after`, or `node.raws.value`.
87+
* **Named Combinators** Although, nonstandard and unlikely to ever become a standard,
88+
named combinators like `/deep/` and `/for/` are parsed as combinators. The
89+
`node.value` is name after being unescaped and normalized as lowercase. The
90+
original value for the combinator name is stored in `node.raws.value`.
91+
92+
8193
### `parser.comment([props])`
8294

8395
Creates a new comment.

CHANGELOG.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,174 @@
1+
# 4.0.1
2+
3+
This release has **BREAKING CHANGES** that were required to fix regressions in 4.0.0. Please read carefully.
4+
5+
* Combinators
6+
- **Descendant Combinators** The value of descendant combinators used to be all of the spaces found between the selectors.
7+
This made it hard to check for a descendant combinator in comparison to other types of combinators.
8+
In 4.0.1 this changed to being just a single space (`" "`). For descendant selectors with no comments,
9+
additional space is now stored in `node.spaces.before`. Depending on the location of comments,
10+
additional spaces are stored in `node.raws.spaces.before`, `node.raws.spaces.after`,
11+
or `node.raws.value`.
12+
Upgrade hints:
13+
* `node.type === "combinator" && / /.test(node.value)` => `node.type === "combinator" && node.value === " "`
14+
* `node.type === "combinator" && / /.test(node.value)` => `node.type === "combinator" && / /.test(node.toString())`
15+
- **Named Combinators** Although, nonstandard and unlikely to ever become a standard, named combinators
16+
like `/deep/` and `/for/` are now properly parsed as combinators. The value is unescaped and normalized as lowercase.
17+
The original value for the combinator name is stored in `node.raws.value`.
18+
19+
# 4.0.0
20+
21+
This release has **BREAKING CHANGES** that were required to fix bugs regarding values with escape sequences. Please read carefully.
22+
23+
* **Identifiers with escapes** - CSS escape sequences are now hidden from the public API by default.
24+
The normal value of a node like a class name or ID, or an aspect of a node such as attribute
25+
selector's value, is unescaped. Escapes representing Non-ascii characters are unescaped into
26+
unicode characters. For example: `bu\tton, .\31 00, #i\2764\FE0Fu, [attr="value is \"quoted\""]`
27+
will parse respectively to the values `button`, `100`, `i❤️u`, `value is "quoted"`.
28+
The original escape sequences for these values can be found in the corresponding property name
29+
in `node.raws`. Where possible, deprecation warnings were added, but the nature
30+
of escape handling makes it impossible to detect what is escaped or not. Our expectation is
31+
that most users are neither expecting nor handling escape sequences in their use of this library,
32+
and so for them, this is a bug fix. Users who are taking care to handle escapes correctly can
33+
now update their code to remove the escape handling and let us do it for them.
34+
35+
* **Mutating values with escapes** - When you make an update to a node property that has escape handling
36+
The value is assumed to be unescaped, and any special characters are escaped automatically and
37+
the corresponding `raws` value is immediately updated. This can result in changes to the original
38+
escape format. Where the exact value of the escape sequence is important there are methods that
39+
allow both values to be set in conjunction. There are a number of new convenience methods for
40+
manipulating values that involve escapes, especially for attributes values where the quote mark
41+
is involved. See https://github.com/postcss/postcss-selector-parser/pull/133 for an extensive
42+
write-up on these changes.
43+
44+
45+
**Upgrade/API Example**
46+
47+
In `3.x` there was no unescape handling and internal consistency of several properties was the caller's job to maintain. It was very easy for the developer
48+
to create a CSS file that did not parse correctly when some types of values
49+
were in use.
50+
51+
```js
52+
const selectorParser = require("postcss-selector-parser");
53+
let attr = selectorParser.attribute({attribute: "id", operator: "=", value: "a-value"});
54+
attr.value; // => "a-value"
55+
attr.toString(); // => [id=a-value]
56+
// Add quotes to an attribute's value.
57+
// All these values have to be set by the caller to be consistent:
58+
// no internal consistency is maintained.
59+
attr.raws.unquoted = attr.value
60+
attr.value = "'" + attr.value + "'";
61+
attr.value; // => "'a-value'"
62+
attr.quoted = true;
63+
attr.toString(); // => "[id='a-value']"
64+
```
65+
66+
In `4.0` there is a convenient API for setting and mutating values
67+
that may need escaping. Especially for attributes.
68+
69+
```js
70+
const selectorParser = require("postcss-selector-parser");
71+
72+
// The constructor requires you specify the exact escape sequence
73+
let className = selectorParser.className({value: "illegal class name", raws: {value: "illegal\\ class\\ name"}});
74+
className.toString(); // => '.illegal\\ class\\ name'
75+
76+
// So it's better to set the value as a property
77+
className = selectorParser.className();
78+
// Most properties that deal with identifiers work like this
79+
className.value = "escape for me";
80+
className.value; // => 'escape for me'
81+
className.toString(); // => '.escape\\ for\\ me'
82+
83+
// emoji and all non-ascii are escaped to ensure it works in every css file.
84+
className.value = "😱🦄😍";
85+
className.value; // => '😱🦄😍'
86+
className.toString(); // => '.\\1F631\\1F984\\1F60D'
87+
88+
// you can control the escape sequence if you want, or do bad bad things
89+
className.setPropertyAndEscape('value', 'xxxx', 'yyyy');
90+
className.value; // => "xxxx"
91+
className.toString(); // => ".yyyy"
92+
93+
// Pass a value directly through to the css output without escaping it.
94+
className.setPropertyWithoutEscape('value', '$REPLACE_ME$');
95+
className.value; // => "$REPLACE_ME$"
96+
className.toString(); // => ".$REPLACE_ME$"
97+
98+
// The biggest changes are to the Attribute class
99+
// passing quoteMark explicitly is required to avoid a deprecation warning.
100+
let attr = selectorParser.attribute({attribute: "id", operator: "=", value: "a-value", quoteMark: null});
101+
attr.toString(); // => "[id=a-value]"
102+
// Get the value with quotes on it and any necessary escapes.
103+
// This is the same as reading attr.value in 3.x.
104+
attr.getQuotedValue(); // => "a-value";
105+
attr.quoteMark; // => null
106+
107+
// Add quotes to an attribute's value.
108+
attr.quoteMark = "'"; // This is all that's required.
109+
attr.toString(); // => "[id='a-value']"
110+
attr.quoted; // => true
111+
// The value is still the same, only the quotes have changed.
112+
attr.value; // => a-value
113+
attr.getQuotedValue(); // => "'a-value'";
114+
115+
// deprecated assignment, no warning because there's no escapes
116+
attr.value = "new-value";
117+
// no quote mark is needed so it is removed
118+
attr.getQuotedValue(); // => "new-value";
119+
120+
// deprecated assignment,
121+
attr.value = "\"a 'single quoted' value\"";
122+
// > (node:27859) DeprecationWarning: Assigning an attribute a value containing characters that might need to be escaped is deprecated. Call attribute.setValue() instead.
123+
attr.getQuotedValue(); // => '"a \'single quoted\' value"';
124+
// quote mark inferred from first and last characters.
125+
attr.quoteMark; // => '"'
126+
127+
// setValue takes options to make manipulating the value simple.
128+
attr.setValue('foo', {smart: true});
129+
// foo doesn't require any escapes or quotes.
130+
attr.toString(); // => '[id=foo]'
131+
attr.quoteMark; // => null
132+
133+
// An explicit quote mark can be specified
134+
attr.setValue('foo', {quoteMark: '"'});
135+
attr.toString(); // => '[id="foo"]'
136+
137+
// preserves quote mark by default
138+
attr.setValue('bar');
139+
attr.toString(); // => '[id="bar"]'
140+
attr.quoteMark = null;
141+
attr.toString(); // => '[id=bar]'
142+
143+
// with no arguments, it preserves quote mark even when it's not a great idea
144+
attr.setValue('a value \n that should be quoted');
145+
attr.toString(); // => '[id=a\\ value\\ \\A\\ that\\ should\\ be\\ quoted]'
146+
147+
// smart preservation with a specified default
148+
attr.setValue('a value \n that should be quoted', {smart: true, preferCurrentQuoteMark: true, quoteMark: "'"});
149+
// => "[id='a value \\A that should be quoted']"
150+
attr.quoteMark = '"';
151+
// => '[id="a value \\A that should be quoted"]'
152+
153+
// this keeps double quotes because it wants to quote the value and the existing value has double quotes.
154+
attr.setValue('this should be quoted', {smart: true, preferCurrentQuoteMark: true, quoteMark: "'"});
155+
// => '[id="this should be quoted"]'
156+
157+
// picks single quotes because the value has double quotes
158+
attr.setValue('a "double quoted" value', {smart: true, preferCurrentQuoteMark: true, quoteMark: "'"});
159+
// => "[id='a "double quoted" value']"
160+
161+
// setPropertyAndEscape lets you do anything you want. Even things that are a bad idea and illegal.
162+
attr.setPropertyAndEscape('value', 'xxxx', 'the password is 42');
163+
attr.value; // => "xxxx"
164+
attr.toString(); // => "[id=the password is 42]"
165+
166+
// Pass a value directly through to the css output without escaping it.
167+
attr.setPropertyWithoutEscape('value', '$REPLACEMENT$');
168+
attr.value; // => "$REPLACEMENT$"
169+
attr.toString(); // => "[id=$REPLACEMENT$]"
170+
```
171+
1172
# 3.1.2
2173

3174
* Fix: Removed dot-prop dependency since it's no longer written in es5.

0 commit comments

Comments
 (0)