Comparing version 4.0.1 to 5.0.0
@@ -0,4 +1,4 @@ | ||
let parse = require('./parse') | ||
let stringify = require('./stringify') | ||
let parse = require('./parse') | ||
module.exports = { stringify, parse } | ||
module.exports = { parse, stringify } |
{ | ||
"name": "sugarss", | ||
"version": "4.0.1", | ||
"version": "5.0.0", | ||
"description": "Indent-based CSS syntax for PostCSS", | ||
@@ -16,8 +16,14 @@ "keywords": [ | ||
"repository": "postcss/sugarss", | ||
"funding": { | ||
"type": "opencollective", | ||
"url": "https://opencollective.com/postcss/" | ||
}, | ||
"funding": [ | ||
{ | ||
"type": "opencollective", | ||
"url": "https://opencollective.com/postcss/" | ||
}, | ||
{ | ||
"type": "github", | ||
"url": "https://github.com/sponsors/ai" | ||
} | ||
], | ||
"engines": { | ||
"node": ">=12.0" | ||
"node": ">=18.0" | ||
}, | ||
@@ -24,0 +30,0 @@ "exports": { |
let { Input } = require('postcss') | ||
let liner = require('./liner') | ||
let Parser = require('./parser') | ||
let preprocess = require('./preprocess') | ||
let tokenizer = require('./tokenize') | ||
let Parser = require('./parser') | ||
let liner = require('./liner') | ||
@@ -8,0 +8,0 @@ module.exports = function parse(source, opts) { |
306
parser.js
@@ -1,2 +0,2 @@ | ||
let { Declaration, Comment, AtRule, Rule, Root } = require('postcss') | ||
let { AtRule, Comment, Declaration, Root, Rule } = require('postcss') | ||
@@ -16,58 +16,5 @@ module.exports = class Parser { | ||
this.root.source = { input, start: { line: 1, column: 1 } } | ||
this.root.source = { input, start: { column: 1, line: 1, offset: 0 } } | ||
} | ||
loop() { | ||
let part | ||
while (this.pos < this.parts.length) { | ||
part = this.parts[this.pos] | ||
if (part.comment) { | ||
this.comment(part) | ||
} else if (part.atrule) { | ||
this.atrule(part) | ||
} else if (part.colon) { | ||
let next = this.nextNonComment(this.pos) | ||
if (next.end || next.atrule) { | ||
this.decl(part) | ||
} else { | ||
let moreIndent = next.indent.length > part.indent.length | ||
if (!moreIndent) { | ||
this.decl(part) | ||
} else if (moreIndent && next.colon) { | ||
this.rule(part) | ||
} else if (moreIndent && !next.colon) { | ||
this.decl(part) | ||
} | ||
} | ||
} else if (part.end) { | ||
this.root.raws.after = part.before | ||
} else { | ||
this.rule(part) | ||
} | ||
this.pos += 1 | ||
} | ||
for (let i = this.tokens.length - 1; i >= 0; i--) { | ||
if (this.tokens[i].length > 3) { | ||
let last = this.tokens[i] | ||
this.root.source.end = { | ||
line: last[4] || last[2], | ||
column: last[5] || last[3] | ||
} | ||
break | ||
} | ||
} | ||
} | ||
comment(part) { | ||
let token = part.tokens[0] | ||
let node = new Comment() | ||
this.init(node, part) | ||
node.source.end = { line: token[4], column: token[5] } | ||
this.commentText(node, token) | ||
} | ||
atrule(part) { | ||
@@ -97,2 +44,57 @@ let atword = part.tokens[0] | ||
badProp(token) { | ||
let pos = this.getPosition(token[2]) | ||
this.error('Unexpected separator in property', pos.offset) | ||
} | ||
checkCurly(tokens) { | ||
for (let token of tokens) { | ||
if (token[0] === '{') { | ||
let pos = this.getPosition(token[2]) | ||
this.error('Unnecessary curly bracket', pos.offset) | ||
} | ||
} | ||
} | ||
checkSemicolon(tokens) { | ||
for (let token of tokens) { | ||
if (token[0] === ';') { | ||
let pos = this.getPosition(token[2]) | ||
this.error('Unnecessary semicolon', pos.offset) | ||
} | ||
} | ||
} | ||
comment(part) { | ||
let token = part.tokens[0] | ||
let node = new Comment() | ||
this.init(node, part) | ||
node.source.end = this.getPosition(token[3]) | ||
this.commentText(node, token) | ||
} | ||
/* Helpers */ | ||
commentText(node, token) { | ||
let text = token[1] | ||
if (token[4] === 'inline') { | ||
node.raws.inline = true | ||
text = text.slice(2) | ||
} else { | ||
text = text.slice(2, -2) | ||
} | ||
let match = text.match(/^(\s*)([^]*\S)(\s*)\n?$/) | ||
if (match) { | ||
node.text = match[2] | ||
node.raws.left = match[1] | ||
node.raws.inlineRight = match[3] | ||
} else { | ||
node.text = '' | ||
node.raws.left = '' | ||
node.raws.inlineRight = '' | ||
} | ||
} | ||
decl(part) { | ||
@@ -145,5 +147,5 @@ let node = new Declaration() | ||
comment.source = { | ||
end: this.getPosition(last[3]), | ||
input: this.input, | ||
start: { line: last[2], column: last[3] }, | ||
end: { line: last[4], column: last[5] } | ||
start: this.getPosition(last[2]) | ||
} | ||
@@ -180,23 +182,29 @@ let prev = value[value.length - 1] | ||
rule(part) { | ||
let node = new Rule() | ||
this.init(node, part) | ||
error(msg, offset) { | ||
let pos = this.getPosition(offset) | ||
throw this.input.error(msg, pos.line, pos.column) | ||
} | ||
let selector = part.tokens | ||
let next = this.parts[this.pos + 1] | ||
firstSpaces(tokens) { | ||
let result = '' | ||
for (let i = 0; i < tokens.length; i++) { | ||
if (tokens[i][0] === 'space' || tokens[i][0] === 'newline') { | ||
result += tokens.shift()[1] | ||
i -= 1 | ||
} else { | ||
break | ||
} | ||
} | ||
return result | ||
} | ||
while (!next.end && next.indent.length === part.indent.length) { | ||
selector.push(['space', next.before + next.indent]) | ||
selector = selector.concat(next.tokens) | ||
this.pos += 1 | ||
next = this.parts[this.pos + 1] | ||
getPosition(offset) { | ||
let pos = this.input.fromOffset(offset) | ||
return { | ||
column: pos.col, | ||
line: pos.line, | ||
offset | ||
} | ||
this.keepTrailingSpace(node, selector) | ||
this.checkCurly(selector) | ||
this.raw(node, 'selector', selector) | ||
} | ||
/* Helpers */ | ||
indent(part) { | ||
@@ -239,2 +247,7 @@ let indent = part.indent.length | ||
indentedFirstLine(part) { | ||
let pos = this.getPosition(part.tokens[0][2]) | ||
this.error('First line should not have indent', pos.offset) | ||
} | ||
init(node, part) { | ||
@@ -252,23 +265,7 @@ this.indent(part) | ||
node.source = { | ||
start: { line: part.tokens[0][2], column: part.tokens[0][3] }, | ||
input: this.input | ||
input: this.input, | ||
start: this.getPosition(part.tokens[0][2]) | ||
} | ||
} | ||
checkCurly(tokens) { | ||
for (let token of tokens) { | ||
if (token[0] === '{') { | ||
this.error('Unnecessary curly bracket', token[2], token[3]) | ||
} | ||
} | ||
} | ||
checkSemicolon(tokens) { | ||
for (let token of tokens) { | ||
if (token[0] === ';') { | ||
this.error('Unnecessary semicolon', token[2], token[3]) | ||
} | ||
} | ||
} | ||
keepTrailingSpace(node, tokens) { | ||
@@ -282,15 +279,57 @@ let lastSpace = tokens[tokens.length - 1] | ||
firstSpaces(tokens) { | ||
let result = '' | ||
for (let i = 0; i < tokens.length; i++) { | ||
if (tokens[i][0] === 'space' || tokens[i][0] === 'newline') { | ||
result += tokens.shift()[1] | ||
i -= 1 | ||
loop() { | ||
let part | ||
while (this.pos < this.parts.length) { | ||
part = this.parts[this.pos] | ||
if (part.comment) { | ||
this.comment(part) | ||
} else if (part.atrule) { | ||
this.atrule(part) | ||
} else if (part.colon) { | ||
let next = this.nextNonComment(this.pos) | ||
if (next.end || next.atrule) { | ||
this.decl(part) | ||
} else { | ||
let moreIndent = next.indent.length > part.indent.length | ||
if (!moreIndent) { | ||
this.decl(part) | ||
} else if (moreIndent && next.colon) { | ||
this.rule(part) | ||
} else if (moreIndent && !next.colon) { | ||
this.decl(part) | ||
} | ||
} | ||
} else if (part.end) { | ||
this.root.raws.after = part.before | ||
} else { | ||
this.rule(part) | ||
} | ||
this.pos += 1 | ||
} | ||
for (let i = this.tokens.length - 1; i >= 0; i--) { | ||
if (this.tokens[i].length > 3) { | ||
let last = this.tokens[i] | ||
this.root.source.end = this.getPosition(last[3]) | ||
break | ||
} | ||
} | ||
return result | ||
} | ||
// Errors | ||
nextNonComment(pos) { | ||
let next = pos | ||
let part | ||
while (next < this.parts.length) { | ||
next += 1 | ||
part = this.parts[next] | ||
if (part.end || !part.comment) break | ||
} | ||
return part | ||
} | ||
raw(node, prop, tokens, altLast) { | ||
@@ -313,3 +352,3 @@ let token, type | ||
let raw = tokens.reduce((all, i) => { | ||
if (i[0] === 'comment' && i[6] === 'inline') { | ||
if (i[0] === 'comment' && i[4] === 'inline') { | ||
return all + '/* ' + i[1].slice(2).trim() + ' */' | ||
@@ -320,3 +359,3 @@ } else { | ||
}, '') | ||
node.raws[prop] = { value, raw } | ||
node.raws[prop] = { raw, value } | ||
if (sss !== raw) node.raws[prop].sss = sss | ||
@@ -335,66 +374,39 @@ } | ||
node.source.end = { | ||
line: last[4] || last[2], | ||
column: last[5] || last[3] | ||
} | ||
node.source.end = this.getPosition(last[3]) | ||
} | ||
nextNonComment(pos) { | ||
let next = pos | ||
let part | ||
while (next < this.parts.length) { | ||
next += 1 | ||
part = this.parts[next] | ||
if (part.end || !part.comment) break | ||
} | ||
return part | ||
} | ||
rule(part) { | ||
let node = new Rule() | ||
this.init(node, part) | ||
commentText(node, token) { | ||
let text = token[1] | ||
if (token[6] === 'inline') { | ||
node.raws.inline = true | ||
text = text.slice(2) | ||
} else { | ||
text = text.slice(2, -2) | ||
} | ||
let selector = part.tokens | ||
let next = this.parts[this.pos + 1] | ||
let match = text.match(/^(\s*)([^]*\S)(\s*)\n?$/) | ||
if (match) { | ||
node.text = match[2] | ||
node.raws.left = match[1] | ||
node.raws.inlineRight = match[3] | ||
} else { | ||
node.text = '' | ||
node.raws.left = '' | ||
node.raws.inlineRight = '' | ||
while (!next.end && next.indent.length === part.indent.length) { | ||
selector.push(['space', next.before + next.indent]) | ||
selector = selector.concat(next.tokens) | ||
this.pos += 1 | ||
next = this.parts[this.pos + 1] | ||
} | ||
} | ||
// Errors | ||
error(msg, line, column) { | ||
throw this.input.error(msg, line, column) | ||
this.keepTrailingSpace(node, selector) | ||
this.checkCurly(selector) | ||
this.raw(node, 'selector', selector) | ||
} | ||
unnamedAtrule(token) { | ||
this.error('At-rule without name', token[2], token[3]) | ||
let pos = this.getPosition(token[2]) | ||
this.error('At-rule without name', pos.offset) | ||
} | ||
unnamedDecl(token) { | ||
this.error('Declaration without name', token[2], token[3]) | ||
let pos = this.getPosition(token[2]) | ||
this.error('Declaration without name', pos.offset) | ||
} | ||
indentedFirstLine(part) { | ||
this.error('First line should not have indent', part.number, 1) | ||
} | ||
wrongIndent(expected, real, part) { | ||
let pos = this.getPosition(part.tokens[0][2]) | ||
let msg = `Expected ${expected} indent, but get ${real}` | ||
this.error(msg, part.number, 1) | ||
this.error(msg, pos.offset) | ||
} | ||
badProp(token) { | ||
this.error('Unexpected separator in property', token[2], token[3]) | ||
} | ||
} |
@@ -81,10 +81,10 @@ function indentError(input, l, p) { | ||
return { | ||
number, | ||
indent, | ||
atrule, | ||
before: '', | ||
colon, | ||
tokens, | ||
atrule, | ||
comment, | ||
indent, | ||
lastComma, | ||
before: '' | ||
number, | ||
tokens | ||
} | ||
@@ -104,3 +104,3 @@ }) | ||
}, | ||
[{ end: true, before: '' }] | ||
[{ before: '', end: true }] | ||
) | ||
@@ -107,0 +107,0 @@ |
274
README.md
@@ -27,6 +27,6 @@ # SugarSS | ||
It was designed to be used with [PreCSS] and [postcss-nested-props]. | ||
It was designed to be used with [postcss-simple-vars] and [postcss-nested]. | ||
But you can use it with any PostCSS plugins | ||
or use it without any PostCSS plugins. | ||
With [gulp-sass-to-postcss-mixins] you can use `+mixin` syntax as in Sass. | ||
With [postcss-mixins] you can use `@mixin` syntax as in Sass. | ||
@@ -38,271 +38,11 @@ <a href="https://evilmartians.com/?utm_source=sugarss"> | ||
[gulp-sass-to-postcss-mixins]: https://github.com/akella/gulp-sass-to-postcss-mixins | ||
[postcss-nested-props]: https://github.com/jedmao/postcss-nested-props | ||
[postcss-mixins]: https://github.com/postcss/postcss-mixins | ||
[postcss-nested]: https://github.com/postcss/postcss-nested | ||
[postcss-simple-vars]: https://github.com/postcss/postcss-simple-vars | ||
[postcss-sorting]: https://github.com/hudochenkov/postcss-sorting | ||
[stylelint]: http://stylelint.io/ | ||
[PostCSS]: https://github.com/postcss/postcss | ||
[PreCSS]: https://github.com/jonathantneal/precss | ||
## Syntax | ||
SugarSS MIME-type is `text/x-sugarss` with `.sss` file extension. | ||
### Indent | ||
We recommend 2 spaces indent. However, SugarSS autodetects indent | ||
and can be used with tabs or spaces. | ||
But it is prohibited to mix spaces and tabs in SugarSS sources. | ||
### Multiline | ||
SugarSS was designed to have intuitively multiline selectors and declaration | ||
values. | ||
There are 3 rules for any types of nodes: | ||
```sass | ||
// 1. New line inside brackets will be ignored | ||
@supports ( (display: flex) and | ||
(display: grid) ) | ||
// 2. Comma at the end of the line | ||
@media (max-width: 400px), | ||
(max-height: 800px) | ||
// 3. Backslash before new line | ||
@media screen and \ | ||
(min-width: 600px) | ||
``` | ||
In a selector you can put a new line anywhere. Just keep same indent | ||
for every line of selector: | ||
```sass | ||
.parent > | ||
.child | ||
color: black | ||
``` | ||
In a declaration value you can put a new line anywhere. Just keep a bigger indent | ||
for the value: | ||
```sass | ||
.one | ||
background: linear-gradient(rgba(0, 0, 0, 0), black) | ||
linear-gradient(red, rgba(255, 0, 0, 0)) | ||
.two | ||
background: | ||
linear-gradient(rgba(0, 0, 0, 0), black) | ||
linear-gradient(red, rgba(255, 0, 0, 0)) | ||
``` | ||
### Comments | ||
SugarSS supports two types of comments: | ||
```sass | ||
/* | ||
Multiline comments | ||
*/ | ||
// Inline comments | ||
``` | ||
There is no “silent” comment in SugarSS. Output CSS will contain all comments | ||
from `.sss` source. But you can use [postcss-discard-comments] | ||
for Sass’s silent/loud comments behaviour. | ||
[postcss-discard-comments]: https://www.npmjs.com/package/postcss-discard-comments | ||
### Rule and Declarations | ||
SugarSS separates selectors and declarations by `:\s` or `:\n` token. | ||
So you must write a space after the property name: `color: black` is good, | ||
`color:black` is prohibited. | ||
### Other | ||
SugarSS is just a syntax, it change the way how you write CSS, | ||
but do not add preprocessor features build-in. | ||
Here are PostCSS plugins which could add you preprocessor features: | ||
* **[PreCSS]** adds variables, nested rules, extend rules, property lookup | ||
and CSS polyfills. | ||
* **[postcss-easy-import]** adds `@import` directive support with globbing. | ||
* **[postcss-mixins]** add `@mixin` support. | ||
* **[postcss-functions]** allows you to define own CSS functions in JS. | ||
[postcss-easy-import]: https://github.com/TrySound/postcss-easy-import | ||
[postcss-functions]: https://github.com/andyjansson/postcss-functions | ||
[postcss-mixins]: https://github.com/postcss/postcss-mixins | ||
[PreCSS]: https://github.com/jonathantneal/precss | ||
## Text Editors | ||
* SublimeText: [Syntax Highlighting for .SSS SugarSS] | ||
* Atom: [language-postcss], [source-preview-postcss] and [build-sugarss] | ||
* Vim: [vim-sugarss] | ||
* VSCode: [vetur], and [postcss-sugarss-language] | ||
We are working on syntax highlight support in text editors. | ||
Right now, you can set `Sass` or `Stylus` syntax highlight for `.sss` files. | ||
[Syntax Highlighting for .SSS SugarSS]: https://packagecontrol.io/packages/Syntax%20Highlighting%20for%20SSS%20SugarSS | ||
[source-preview-postcss]: https://atom.io/packages/source-preview-postcss | ||
[language-postcss]: https://atom.io/packages/language-postcss | ||
[build-sugarss]: https://atom.io/packages/build-sugarss | ||
[vim-sugarss]: https://github.com/hhsnopek/vim-sugarss | ||
## Usage | ||
SugarSS needs PostCSS compiler. Install [`postcss-loader`] for webpack, | ||
[`gulp-postcss`] for Gulp, [`postcss-cli`] for npm scripts. | ||
[Parcel] has build-in support for PostCSS. | ||
Then install SugarSS: `npm install --save-dev postcss sugarss` if you use npm | ||
and `yarn add --dev postcss sugarss` if you use Yarn. | ||
Then create `.postcssrc` file: | ||
```json | ||
{ | ||
"parser": "sugarss", | ||
"plugins": { | ||
"precss": {} | ||
} | ||
} | ||
``` | ||
[`postcss-loader`]: https://github.com/postcss/postcss-loader | ||
[`gulp-postcss`]: https://github.com/postcss/gulp-postcss | ||
[`postcss-cli`]: https://github.com/postcss/postcss-cli | ||
[Parcel]: https://parceljs.org/transforms.html | ||
### Imports | ||
If you doesn’t use Webpack or Parcel, you need some PostCSS plugin | ||
to process `@import` directives. | ||
[postcss-import] doesn’t support `.sss` file extension, because this plugin | ||
implements W3C specification. If you want smarter `@import`, you should | ||
use [postcss-easy-import] with the `extensions` option. | ||
```diff js | ||
{ | ||
"parser": "sugarss", | ||
"plugins": { | ||
+ "postcss-easy-import": { | ||
+ "extensions": [ | ||
+ ".sss" | ||
+ ] | ||
+ }, | ||
"precss": {}, | ||
} | ||
} | ||
``` | ||
[postcss-easy-import]: https://github.com/TrySound/postcss-easy-import | ||
[postcss-import]: https://github.com/postcss/postcss-import | ||
### Mixins | ||
For mixins support, install [postcss-mixins] and add it to `.postcssrc` file: | ||
```diff js | ||
{ | ||
"parser": "sugarss", | ||
"plugins": { | ||
+ "postcss-mixins": { | ||
+ "mixinsDir": "./mixins" | ||
+ }, | ||
"precss": {}, | ||
} | ||
} | ||
``` | ||
Now you can define your mixins in `mixins/` dir. | ||
For example create `mixins/circle.sss` with: | ||
```sss | ||
@define-mixin circle $size | ||
border-radius: 50% | ||
width: $size | ||
height: $size | ||
``` | ||
### Functions | ||
To define custom functions you need to install [postcss-functions] | ||
and add it to `.postcssrc` file: | ||
```diff js | ||
{ | ||
"parser": "sugarss", | ||
"plugins": { | ||
+ "postcss-functions": { | ||
+ "glob": "./functions" | ||
+ }, | ||
"precss": {}, | ||
} | ||
} | ||
``` | ||
Then you can define functions in `functions/` dir. For example, | ||
`functions/foo.js` will define `foo()` function in CSS: | ||
```js | ||
module.exports = function (args) { | ||
return 'foo' | ||
} | ||
``` | ||
### SugarSS to SugarSS | ||
Sometimes we use PostCSS not to build CSS, but to fix source files. | ||
For example, to sort properties by [postcss-sorting]. | ||
For this cases use the `syntax` option, instead of `parser`: | ||
```js | ||
gulp.task('sort', function () { | ||
return gulp.src('src/**/*.sss') | ||
.pipe(postcss([sorting], { syntax: sugarss })) | ||
.pipe(gulp.dest('src')); | ||
}); | ||
``` | ||
[postcss-sorting]: https://github.com/hudochenkov/postcss-sorting | ||
### CSS to SugarSS | ||
You can even compile existing CSS sources to SugarSS syntax. | ||
Just use `stringifier` option instead of `parser`: | ||
```js | ||
postcss().process(css, { stringifier: sugarss }).then(function (result) { | ||
result.content // Converted SugarSS content | ||
}); | ||
``` | ||
## Thanks | ||
Cute project logo was made by [Maria Keller](http://www.mariakellerac.com/). | ||
## Docs | ||
Read full docs **[here](https://github.com/postcss/sugarss#readme)**. |
const DEFAULT_RAWS = { | ||
colon: ': ', | ||
indent: ' ', | ||
commentLeft: ' ', | ||
commentRight: ' ' | ||
commentRight: ' ', | ||
indent: ' ' | ||
} | ||
@@ -13,11 +13,36 @@ | ||
stringify(node, semicolon) { | ||
this[node.type](node, semicolon) | ||
atrule(node) { | ||
let name = '@' + node.name | ||
let params = node.params ? this.rawValue(node, 'params') : '' | ||
if (this.has(node.raws.afterName)) { | ||
name += node.raws.afterName | ||
} else if (params) { | ||
name += ' ' | ||
} | ||
this.block(node, name + params) | ||
} | ||
root(node) { | ||
this.body(node) | ||
if (node.raws.after) this.builder(node.raws.after) | ||
block(node, start) { | ||
let between = node.raws.sssBetween || '' | ||
this.builder(start + between, node, 'start') | ||
if (this.has(node.nodes)) this.body(node) | ||
} | ||
body(node) { | ||
let indent = node.root().raws.indent || DEFAULT_RAWS.indent | ||
for (let i = 0; i < node.nodes.length; i++) { | ||
let child = node.nodes[i] | ||
let before = | ||
child.raws.before.replace(/[^\n]*$/, '') + this.indent(node, indent) | ||
if (child.type === 'comment' && !child.raws.before.includes('\n')) { | ||
before = child.raws.before | ||
} | ||
if (before) this.builder(before) | ||
this.stringify(child) | ||
} | ||
} | ||
comment(node) { | ||
@@ -55,40 +80,6 @@ let left = DEFAULT_RAWS.commentLeft | ||
rule(node) { | ||
this.block(node, this.rawValue(node, 'selector')) | ||
has(value) { | ||
return typeof value !== 'undefined' | ||
} | ||
atrule(node) { | ||
let name = '@' + node.name | ||
let params = node.params ? this.rawValue(node, 'params') : '' | ||
if (this.has(node.raws.afterName)) { | ||
name += node.raws.afterName | ||
} else if (params) { | ||
name += ' ' | ||
} | ||
this.block(node, name + params) | ||
} | ||
body(node) { | ||
let indent = node.root().raws.indent || DEFAULT_RAWS.indent | ||
for (let i = 0; i < node.nodes.length; i++) { | ||
let child = node.nodes[i] | ||
let before = | ||
child.raws.before.replace(/[^\n]*$/, '') + this.indent(node, indent) | ||
if (child.type === 'comment' && !child.raws.before.includes('\n')) { | ||
before = child.raws.before | ||
} | ||
if (before) this.builder(before) | ||
this.stringify(child) | ||
} | ||
} | ||
block(node, start) { | ||
let between = node.raws.sssBetween || '' | ||
this.builder(start + between, node, 'start') | ||
if (this.has(node.nodes)) this.body(node) | ||
} | ||
indent(node, step) { | ||
@@ -103,6 +94,2 @@ let result = '' | ||
has(value) { | ||
return typeof value !== 'undefined' | ||
} | ||
rawValue(node, prop) { | ||
@@ -117,2 +104,15 @@ let value = node[prop] | ||
} | ||
root(node) { | ||
this.body(node) | ||
if (node.raws.after) this.builder(node.raws.after) | ||
} | ||
rule(node) { | ||
this.block(node, this.rawValue(node, 'selector')) | ||
} | ||
stringify(node, semicolon) { | ||
this[node.type](node, semicolon) | ||
} | ||
} |
183
tokenize.js
@@ -29,23 +29,10 @@ const SINGLE_QUOTE = "'".charCodeAt(0) | ||
let code, | ||
next, | ||
quote, | ||
lines, | ||
last, | ||
content, | ||
escape, | ||
nextLine, | ||
nextOffset, | ||
escaped, | ||
escapePos, | ||
prev, | ||
n | ||
let code, content, escape, escaped, escapePos, n, next, prev, quote | ||
let length = css.length | ||
let offset = -1 | ||
let line = 1 | ||
let offset = 0 | ||
let pos = 0 | ||
function unclosed(what) { | ||
throw input.error('Unclosed ' + what, line, pos - offset) | ||
throw input.error('Unclosed ' + what, pos) | ||
} | ||
@@ -56,26 +43,18 @@ | ||
if ( | ||
code === NEWLINE || | ||
code === FEED || | ||
(code === CR && css.charCodeAt(pos + 1) !== NEWLINE) | ||
) { | ||
offset = pos | ||
line += 1 | ||
} | ||
switch (code) { | ||
case CR: | ||
if (css.charCodeAt(pos + 1) === NEWLINE) { | ||
offset = pos | ||
line += 1 | ||
tokens.push(['newline', '\r\n', offset, offset + 2]) | ||
offset += 2 | ||
pos += 1 | ||
tokens.push(['newline', '\r\n', line - 1]) | ||
} else { | ||
tokens.push(['newline', '\r', line - 1]) | ||
tokens.push(['newline', '\r', offset, offset + 1]) | ||
offset++ | ||
} | ||
break | ||
case NEWLINE: | ||
case FEED: | ||
case NEWLINE: | ||
tokens.push(['newline', css.slice(pos, pos + 1), line - 1]) | ||
tokens.push(['newline', css.slice(pos, pos + 1), offset, offset + 1]) | ||
offset++ | ||
break | ||
@@ -91,3 +70,9 @@ | ||
tokens.push(['space', css.slice(pos, next)]) | ||
tokens.push([ | ||
'space', | ||
css.slice(pos, next), | ||
offset, | ||
offset + (next - pos) | ||
]) | ||
offset += next - pos | ||
pos = next - 1 | ||
@@ -97,19 +82,24 @@ break | ||
case OPEN_CURLY: | ||
tokens.push(['{', '{', line, pos - offset]) | ||
tokens.push(['{', '{', offset, offset + 1]) | ||
offset++ | ||
break | ||
case CLOSE_CURLY: | ||
tokens.push(['}', '}', line, pos - offset]) | ||
tokens.push(['}', '}', offset, offset + 1]) | ||
offset++ | ||
break | ||
case COLON: | ||
tokens.push([':', ':', line, pos - offset]) | ||
tokens.push([':', ':', offset, offset + 1]) | ||
offset++ | ||
break | ||
case SEMICOLON: | ||
tokens.push([';', ';', line, pos - offset]) | ||
tokens.push([';', ';', offset, offset + 1]) | ||
offset++ | ||
break | ||
case COMMA: | ||
tokens.push([',', ',', line, pos - offset]) | ||
tokens.push([',', ',', offset, offset + 1]) | ||
offset++ | ||
break | ||
@@ -142,10 +132,4 @@ | ||
tokens.push([ | ||
'brackets', | ||
css.slice(pos, next + 1), | ||
line, | ||
pos - offset, | ||
line, | ||
next - offset | ||
]) | ||
tokens.push(['brackets', css.slice(pos, next + 1), offset, next + 1]) | ||
offset = next + 1 | ||
pos = next | ||
@@ -157,20 +141,15 @@ } else { | ||
if (next === -1 || RE_BAD_BRACKET.test(content)) { | ||
tokens.push(['(', '(', line, pos - offset]) | ||
tokens.push(['(', '(', offset]) | ||
offset++ | ||
} else { | ||
tokens.push([ | ||
'brackets', | ||
content, | ||
line, | ||
pos - offset, | ||
line, | ||
next - offset | ||
]) | ||
tokens.push(['brackets', content, offset, next + 1]) | ||
offset = next + 1 | ||
pos = next | ||
} | ||
} | ||
break | ||
case CLOSE_PARENTHESES: | ||
tokens.push([')', ')', line, pos - offset]) | ||
tokens.push([')', ')', offset]) | ||
offset++ | ||
break | ||
@@ -194,24 +173,6 @@ | ||
content = css.slice(pos, next + 1) | ||
lines = content.split('\n') | ||
last = lines.length - 1 | ||
if (last > 0) { | ||
nextLine = line + last | ||
nextOffset = next - lines[last].length | ||
} else { | ||
nextLine = line | ||
nextOffset = offset | ||
} | ||
tokens.push(['string', content, offset, offset + content.length]) | ||
tokens.push([ | ||
'string', | ||
css.slice(pos, next + 1), | ||
line, | ||
pos - offset, | ||
nextLine, | ||
next - nextOffset | ||
]) | ||
offset = nextOffset | ||
line = nextLine | ||
offset += content.length | ||
pos = next | ||
@@ -228,10 +189,11 @@ break | ||
} | ||
tokens.push([ | ||
'at-word', | ||
css.slice(pos, next + 1), | ||
line, | ||
pos - offset, | ||
line, | ||
next - offset | ||
offset, | ||
offset + (next - pos + 1) | ||
]) | ||
offset += next - pos + 1 | ||
pos = next | ||
@@ -244,4 +206,2 @@ break | ||
nextLine = line | ||
while (css.charCodeAt(next + 1) === BACKSLASH) { | ||
@@ -255,8 +215,4 @@ next += 1 | ||
next += 2 | ||
nextLine += 1 | ||
nextOffset = next | ||
} else if (code === CR || code === NEWLINE || code === FEED) { | ||
next += 1 | ||
nextLine += 1 | ||
nextOffset = next | ||
} else { | ||
@@ -266,14 +222,11 @@ next += 1 | ||
} | ||
tokens.push([ | ||
'word', | ||
css.slice(pos, next + 1), | ||
line, | ||
pos - offset, | ||
line, | ||
next - offset | ||
offset, | ||
offset + (next - pos + 1) | ||
]) | ||
if (nextLine !== line) { | ||
line = nextLine | ||
offset = nextOffset | ||
} | ||
offset += next - pos + 1 | ||
pos = next | ||
@@ -290,24 +243,6 @@ break | ||
content = css.slice(pos, next + 1) | ||
lines = content.split('\n') | ||
last = lines.length - 1 | ||
if (last > 0) { | ||
nextLine = line + last | ||
nextOffset = next - lines[last].length | ||
} else { | ||
nextLine = line | ||
nextOffset = offset | ||
} | ||
tokens.push(['comment', content, offset, offset + content.length]) | ||
tokens.push([ | ||
'comment', | ||
content, | ||
line, | ||
pos - offset, | ||
nextLine, | ||
next - nextOffset | ||
]) | ||
offset = nextOffset | ||
line = nextLine | ||
offset += content.length | ||
pos = next | ||
@@ -328,9 +263,8 @@ } else if (code === SLASH && n === SLASH) { | ||
content, | ||
line, | ||
pos - offset, | ||
line, | ||
next - offset, | ||
offset, | ||
offset + content.length, | ||
'inline' | ||
]) | ||
offset += content.length | ||
pos = next | ||
@@ -346,10 +280,7 @@ } else { | ||
tokens.push([ | ||
'word', | ||
css.slice(pos, next + 1), | ||
line, | ||
pos - offset, | ||
line, | ||
next - offset | ||
]) | ||
content = css.slice(pos, next + 1) | ||
tokens.push(['word', content, offset, offset + content.length]) | ||
offset += content.length | ||
pos = next | ||
@@ -356,0 +287,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
96559
13
830
47
1