A PostCSS-compatible CSS and Sass parser with full expression support.
Warning: sass-parser
is still in active development, and is not yet
suitable for production use. At time of writing it only supports a small subset
of CSS and Sass syntax. In addition, it does not yet support parsing raws
(metadata about the original formatting of the document), which makes it
unsuitable for certain source-to-source transformations.
Using sass-parser
-
Install the sass-parser
package from the npm repository:
npm install sass-parser
-
Use the scss
, sass
, or css
Syntax
objects exports to parse a file.
const sassParser = require('sass-parser');
const root = sassParser.scss.parse(`
@use 'colors';
body {
color: colors.$midnight-blue;
}
`);
-
Use the standard PostCSS API to inspect and edit the stylesheet:
const styleRule = root.nodes[1];
styleRule.selector = '.container';
console.log(root.toString());
-
Use new PostCSS-style APIs to inspect and edit expressions and Sass-specific
rules:
root.nodes[0].namespace = 'c';
const variable = styleRule.nodes[0].valueExpression;
variable.namespace = 'c';
console.log(root.toString());
Why sass-parser
?
We decided to expose Dart Sass's parser as a JS API because we saw two needs
that were going unmet.
First, there was no fully-compatible Sass parser. Although a postcss-scss
plugin did exist, its author requested we create this package to fix
compatibility issues, support the indented syntax, and provide first-class
support for Sass-specific rules without needing them to be manually parsed by
each user.
Moreover, there was no robust solution for parsing the expressions that are used
as the values of CSS declarations (as well as Sass variable values). This was
true even for plain CSS, and doubly problematic for Sass's particularly complex
expression syntax. The postcss-value-parser
package hasn't been updated
since 2021, the postcss-values-parser
since January 2022, and even the
excellent @csstools/css-parser-algorithms
had limited PostCSS integration
and no intention of ever supporting Sass.
The sass-parser
package intends to solve these problems by providing a parser
that's battle-tested by millions of Sass users and flexible enough to support
use-cases that don't involve Sass at all. We intend it to be usable as a drop-in
replacement for the standard PostCSS parser, and for the new expression-level
APIs to feel highly familiar to anyone used to PostCSS.
API Documentation
The source code is fully documented using TypeDoc. Hosted, formatted
documentation will be coming soon.
PostCSS Compatibility
PostCSS is the most popular and long-lived CSS post-processing framework in
the world, and this package aims to be fully compatible with its API. Where we
add new features, we do so in a way that's as similar to PostCSS as possible,
re-using its types and even implementation wherever possible.
Statement API
All statement-level AST nodes produced by sass-parser
—style rules, at-rules,
declarations, statement-level comments, and the root node—extend the
corresponding PostCSS node types (Rule
, AtRule
, Declaration
,
Comment
, and Root
). However, sass-parser
has multiple subclasses for
many of its PostCSS superclasses. For example, sassParser.PropertyDeclaration
extends postcss.Declaration
, but so does sassParser.VariableDeclaration
. The
different sass-parser
node types may be distinguished using the
sassParser.Node.sassType
field.
In addition to supporting the standard PostCSS properties like
Declaration.value
and Rule.selector
, sass-parser
provides more detailed
parsed values. For example, sassParser.Declaration.valueExpression
provides
the declaration's value as a fully-parsed syntax tree rather than a string, and
sassParser.Rule.selectorInterpolation
provides access to any interpolated
expressions as in .prefix-#{$variable} { /*...*/ }
. These parsed values are
automatically kept up-to-date with the standard PostCSS properties.
Expression API
The expression-level AST nodes inherit from PostCSS's Node
class but not any
of its more specific nodes. Nor do expressions support all the PostCSS Node
APIs: unlike statements, expressions that contain other expressions don't always
contain them as a clearly-ordered list, so methods like Node.before()
and
Node.next
aren't available. Just like with sass-parser
statements, you can
distinguish between expressions using the sassType
field.
Just like standard PostCSS nodes, expression nodes can be modified in-place and
these modifications will be reflected in the CSS output. Each expression type
has its own specific set of properties which can be read about in the expression
documentation.
Constructing New Nodes
All Sass nodes, whether expressions, statements, or miscellaneous nodes like
Interpolation
s, can be constructed as standard JavaScript objects:
const sassParser = require('sass-parser');
const root = new sassParser.Root();
root.append(new sassParser.Declaration({
prop: 'content',
valueExpression: new sassParser.StringExpression({
quotes: true,
text: new sassParser.Interpolation({
nodes: ["hello, world!"],
}),
}),
}));
However, the same shorthands can be used as when adding new nodes in standard
PostCSS, as well as a few new ones. Anything that takes an Interpolation
can
be passed a string instead to represent plain text with no Sass expressions:
const sassParser = require('sass-parser');
const root = new sassParser.Root();
root.append(new sassParser.Declaration({
prop: 'content',
valueExpression: new sassParser.StringExpression({
quotes: true,
text: "hello, world!",
}),
}));
Because the mandatory properties for all node types are unambiguous, you can
leave out the new ...()
call and just pass the properties directly:
const sassParser = require('sass-parser');
const root = new sassParser.Root();
root.append({
prop: 'content',
valueExpression: {quotes: true, text: "hello, world!"},
});
You can even pass a string in place of a statement and PostCSS will parse it for
you! Warning: This currently uses the standard PostCSS parser, not the Sass
parser, and as such it does not support Sass-specific constructs.
const sassParser = require('sass-parser');
const root = new sassParser.Root();
root.append('content: "hello, world!"');
Known Incompatibilities
There are a few cases where an operation that's valid in PostCSS won't work with
sass-parser
:
-
Trying to convert a Sass-specific at-rule like @if
or @mixin
into a
different at-rule by changing its name is not supported.
-
Trying to add child nodes to a Sass statement that doesn't support children
like @use
or @error
is not supported.
Contributing
Before sending out a pull request, please run the following commands from the
pkg/sass-parser
directory:
-
npm run check
- Runs eslint
, and then tries to compile the package with
tsc
.
-
npm run test
- Runs all the tests in the package.
Note: You should run dart run grinder before-test
from the dart-sass
directory beforehand to ensure you're running sass-parser
against the latest
version of dart-sass
JavaScript API.