2.0 – ESM support, no white space nodes on AST, auto encode/decode string and url values, and more
ES2020 syntax and support for ESM
The source code was refactored to use ES2020 syntax and ES modules by default. However, Commonjs version of library is still supported, so the package became a dual module. Using ESM allowed to reduce bundle size from 167Kb down to 164Kb despite that mdn-data
grew up in size by 11Kb.
In case only a part of CSSTree functionality is used (for instance only for a parsing), it's possible now to use specific exports like css-tree/parser
(see the full list of exports) to reduce a bundle size. As new package mechanics are used, the minimal version for Node.js was changed to 14.16+
.
No white space nodes on AST anymore
Previously white spaces was preserved on CSS parsing as a WhiteSpace
node with a single space. This was mostly needed to avoid combining the CSS tokens into one when generating back into a CSS string. This is no longer necessary, as the generator has been reworked to independently determine when to use spaces in output. This simplify analysis and construction of the AST, and also improves the performance and memory consumption a bit.
Some changes have been made to the AST construction during parsing. First of all, white spaces in selectors are now producing { type: 'Combinator', name: ' ' }
nodes when appropriate. Second, white spaces surrounding operators -
and +
are now replacing with a single space and appending to operators to preserve a behaviour of expressions in calc()
functions. Finally, the only case a WhiteSpace
node is now created is a custom property declaration with a single whitespace token as the value, since --var: ;
and --var:;
have different behaviour in terms of CSS.
Improved generator
CSS Syntax Module defines rules for CSS serialization that it must "round-trip" with parsing. Starting with this release the CSSTree's generator follows these rules and determines itself when to output the space to avoid unintended CSS tokens combining.
The spec rules allow to omit whitespaces in most cases. However, some older browsers fail to parse the resulting CSS because they didn't follow the spec. For this reason, the generator supports two modes:
safe
(by default) which adds an extra space in some edge cases;spec
which completely follows the spec.
import { parse, generate } from 'css-tree';
const ast = parse('a { border: calc(1px) solid #ff0000 }');
// safe mode is by default
// the same as console.log(generate(ast, { mode: 'safe' }));
console.log(generate(ast));
// a{border:calc(1px) solid#ff0000}
// spec mode
console.log(generate(ast, { mode: 'spec' }));
// a{border:calc(1px)solid#ff0000}
These changes to the generator bring it closer to the pretty print output that will be implemented in future releases.
Auto encoding and decoding of values
For string and url values an auto encoding and decoding was implemented. This means you no longer need to do any preprocessing on string or url values before analyzing or transforming of it. Most noticeable simplification with Url
nodes:
// CSSTree 1.x
csstree.walk(ast, function(node) {
if (node.type === 'Url') {
if (node.value.type === 'String') {
urls.push(node.value.value.substring(1, node.value.value.length - 1));
} else {
urls.push(node.value.value);
}
}
});
// CSSTree 2.0
csstree.walk(ast, function(node) {
if (node.type === 'Url') {
urls.push(node.value);
}
});
It is worth noting that despite the fact that in many cases the example above will give the same results for both versions, a solution with CSSTree 1.x still lacks decoding of escaped sequences, that is, additional processing of values is needed.
Additionaly, encode and decode functions for string
, url
and ident
values are available as utils:
import { string, url, ident } from 'css-tree';
string.decode('"hello\\9 \\"world\\""') // hello\t "world"
string.decode('\'hello\\9 "world"\'') // hello\t "world"
string.encode('hello\t "world"') // "hello\9 \"world\""
string.encode('hello\t "world"', true) // 'hello\9 "world"'
url.decode('url(file\ \(1\).ext)') // file (1).ext
url.encode('file (1).ext') // url(file\ \(1\).ext)
ident.decode('hello\\9 \\ world') // hello\t world
ident.encode('hello\t world') // hello\9 \ world
Changes
- Package
- Dropped support for Node.js prior
14.16
(following patch versions changed it to^10 || ^12.20.0 || ^14.13.0 || >=15.0.0
) - Converted to ES modules. However, CommonJS is supported as well (dual module)
- Added exports for standalone parts instead of internal paths usage (use as
import * as parser from "css-tree/parser"
orrequire("css-tree/parser")
):css-tree/tokenizer
css-tree/parser
css-tree/walker
css-tree/generator
css-tree/lexer
css-tree/definition-syntax
css-tree/utils
- Changed bundle set to provide
dist/csstree.js
(an IIFE version withcsstree
as a global name) anddist/csstree.esm.js
(as ES module). Both are minified - Bumped
mdn-data
to2.0.23
- Dropped support for Node.js prior
- Tokenizer
- Changed
tokenize()
to take a function as second argument, which will be called for every token. No stream instance is creating when second argument is ommited. - Changed
TokenStream#getRawLength()
to take second parameter as a function (rule) that check a char code to stop a scanning - Added
TokenStream#forEachToken(fn)
method - Removed
TokenStream#skipWS()
method - Removed
TokenStream#getTokenLength()
method
- Changed
- Parser
- Moved
SyntaxError
(custom parser's error class) from root of public API to parser viaparse.SyntaxError
- Removed
parseError
field in parser'sSyntaxError
- Changed selector parsing to produce
{ type: 'Combinator', name: ' ' }
node instead ofWhiteSpace
node - Removed producing of
WhiteSpace
nodes with the single exception for a custom property declaration with a single white space token as a value - Parser adds a whitespace to
+
and-
operators, when a whitespace is before and/or after an operator - Exposed parser's inner configuration as
parse.config
- Added
consumeUntilBalanceEnd()
,consumeUntilLeftCurlyBracket()
,consumeUntilLeftCurlyBracketOrSemicolon()
,consumeUntilExclamationMarkOrSemicolon()
andconsumeUntilSemicolonIncluded()
methods to parser's inner API to use withRaw
instead ofRaw.mode
- Changed
Nth
to always consumeof
clause when presented, so it became more general and moves validation to lexer - Changed
String
node type to store decoded string value, i.e. with no quotes and escape sequences - Changed
Url
node type to store decoded url value as a string instead ofString
orRaw
node, i.e. with no quotes, escape sequences andurl()
wrapper
- Moved
- Generator
- Generator is now determines itself when a white space required between emitting tokens
- Changed
chunk()
handler totoken()
(output a single token) andtokenize()
(split a string into tokens and output each of them) - Added
mode
option forgenerate()
to specify a mode of token separation:spec
orsafe
(by default) - Added
emit(token, type, auto)
handler as implementation specific token processor - Changed
Nth
to serialize+n
asn
- Added auto-encoding for a
string
andurl
tokens on serialization
- Lexer
- Removed
Lexer#matchDeclaration()
method
- Removed
- Utils
- Added
ident
,string
andurl
helpers to decode/encode corresponding values, e.g.url.decode('url("image.jpg")')
==='image.jpg'
- List
- Changed
List
to be iterable (iterates data) - Changed
List#first
,List#last
andList#isEmpty
to getters - Changed
List#getSize()
method toList#size
getter - Removed
List#each()
andList#eachRight()
methods,List#forEach()
andList#forEachRight()
should be used instead
- Changed
- Added