postcss 0.1.0 → 0.2.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.

Potentially problematic release.


This version of postcss might be problematic. Click here for more details.

package/ChangeLog.md ADDED
@@ -0,0 +1,12 @@
1
+ ## 0.2 “Duke Dantalion”
2
+ * Add source map support.
3
+ * Add shortcuts to create nodes.
4
+ * Method `process()` now returns object with `css` and `map` keys.
5
+ * Origin CSS file option was renamed from `file` to `from`.
6
+ * Rename `Node#remove()` method to `removeSelf()` to fix name conflict.
7
+ * Node source was moved to `source` property with origin file
8
+ and node end position.
9
+ * You can set own stringify function.
10
+
11
+ ## 0.1 “Count Andromalius”
12
+ * Initial release.
package/README.md CHANGED
@@ -1,22 +1,47 @@
1
1
  # PostCSS
2
2
 
3
- PostCSS is a framework for CSS postprocessors. You get a custom JS function
4
- to modify CSS, and PostCSS parses CSS, gives you usable JS API to edit CSS node
5
- tree and then save modified node tree to new CSS.
3
+ PostCSS is a framework for CSS postprocessors,
4
+ to modify CSS by your JS function.
6
5
 
7
- For example, let's fix forgotten `content` propery in `::before` and `::after`:
6
+ It takes care of most common CSS tool tasks:
7
+
8
+ 1. parses CSS;
9
+ 2. gives you usable JS API to edit CSS node tree;
10
+ 3. saves modified node tree to new CSS;
11
+ 4. generates (or modifies existent) source map for your changes;
12
+
13
+ You can use this framework to write you own:
14
+
15
+ * CSS minifier or beautifizer.
16
+ * Grunt plugin to generate sprites, include `data-uri` images
17
+ or any other works.
18
+ * Text editor plugin to automate CSS routine.
19
+ * Command-line CSS tool.
20
+
21
+ Sponsored by [Evil Martians](http://evilmartians.com/).
22
+
23
+ ## Build with PostCSS
24
+
25
+ * [Autoprefixer](https://github.com/ai/autoprefixer)
26
+ * [grunt-pixrem](https://github.com/robwierzbowski/grunt-pixrem)
27
+
28
+ ## Quick Example
29
+
30
+ Let’s fix forgotten `content` property in `::before` and `::after`:
8
31
 
9
32
  ```js
10
33
  var postcss = require('postcss');
11
34
 
12
- var postprocessor = postcss(function (css) {
35
+ var contenter = postcss(function (css) {
13
36
  css.eachRule(function (rule) {
14
37
  if ( rule.selector.match(/::(before|after)/) ) {
38
+ // In every ::before/::after rule
39
+
40
+ // Did we forget content property?
41
+ var good = rule.some(function (i) { return i.prop == 'content'; });
15
42
 
16
- var good = rule.some(function (i) {
17
- return i.prop == 'content';
18
- });
19
43
  if ( !good ) {
44
+ // Add content: '' if we forget it
20
45
  rule.prepend({ prop: 'content', value: '""' });
21
46
  }
22
47
 
@@ -30,15 +55,14 @@ And then CSS with forgotten `content`:
30
55
  ```css
31
56
  a::before {
32
57
  width: 10px;
33
- height: 10px;
34
- background: black
58
+ height: 10px
35
59
  }
36
60
  ```
37
61
 
38
- will be fixed by our new `postprocessor`:
62
+ will be fixed by our new `contenter`:
39
63
 
40
64
  ```js
41
- var fixed = postprocessor.process(css);
65
+ var fixed = contenter.process(css).css;
42
66
  ```
43
67
 
44
68
  to:
@@ -47,33 +71,501 @@ to:
47
71
  a::before {
48
72
  content: "";
49
73
  width: 10px;
50
- height: 10px;
51
- background: black
74
+ height: 10px
52
75
  }
53
76
  ```
54
77
 
55
- Sponsored by [Evil Martians](http://evilmartians.com/).
56
-
57
78
  ## Features
58
79
 
80
+ ### Source Map
81
+
82
+ PostCSS generates source map for its transformations:
83
+
84
+ ```js
85
+ result = processor.process(css, { map: true, from: 'from.css', to: 'to.css' });
86
+ result.css // String with processed CSS
87
+ result.map // Source map
88
+ ```
89
+
90
+ And modifies source map from previous step (like Sass preprocessor):
91
+
92
+ ```js
93
+ var sassMap = fs.readFileSync('from.sass.map');
94
+ processor.process(css, { map: sassMap, from: 'from.sass.css', to: 'to.css' });
95
+ ```
96
+
59
97
  ### Preserves code formatting and indentations
60
98
 
61
- PostCSS saves all spaces if you dont change CSS node and try to copy your
62
- coding style if you modify it.
99
+ PostCSS will not change any byte of rule if you don't modify node:
100
+
101
+ ```js
102
+ postcss(function (css) { }).process(css).css == css;
103
+ ```
104
+
105
+ And when you modify CSS nodes, PostCSS will try to copy coding style:
106
+
107
+ ```js
108
+ contenter.process("a::before{color: black}")
109
+ // a::before{content: '';color: black}
110
+
111
+ contenter.process("a::before {\n color: black;\n }")
112
+ // a::before {
113
+ // content: '';
114
+ // color: black;
115
+ // }
116
+ ```
117
+
118
+ ## Why PostCSS Better Than …
119
+
120
+ ### Preprocessors
121
+
122
+ Preprocessors (like Sass or Stylus) give us special language with variables,
123
+ mixins, statements and compile it to CSS. Compass, nib and other mixins
124
+ libraries use this languages to work with prefixes, sprites and inline images.
125
+
126
+ But Sass and Stylus languages were created to be syntax-sugar for CSS.
127
+ Writing really complicated programs using preporcessor languages is very difficult.
128
+ [Autoprefixer] is absolutely impossible on Sass.
129
+
130
+ PostCSS gives you comfort and power of JS or CoffeeScript to working with CSS.
131
+ You can do really magic things with wide range of [npm] libraries.
132
+
133
+ But postprocessors are not enemies for preprocessors. Sass and Stylus is still
134
+ the best way to improve readability and add some syntax sugar to CSS. You can easily combine preprocessors and postprocessors.
135
+
136
+ [Autoprefixer]: https://github.com/ai/autoprefixer
137
+ [npm]: https://npmjs.org/
138
+
139
+ ### RegExp
140
+
141
+ Some Grunt plugins modify CSS with regular expressions. But CSS parser and
142
+ node tree are much safer way to edit CSS. Also regexps will break source maps
143
+ generated by preprocessors.
144
+
145
+ ### CSS Parsers
146
+
147
+ There are a lot of good CSS parsers, like [Gonzales]. But they help you only
148
+ with first step.
149
+
150
+ Unlike them PostCSS gives you useful high level API (for example,
151
+ safe iterators) and changes source map generator (or modifier for existing
152
+ source map from preprocessors).
153
+
154
+ [Gonzales]: https://github.com/css/gonzales
155
+
156
+ ### Rework
157
+
158
+ [Rework] was a first CSS postprocessors framework. PostCSS is very similar
159
+ to it.
160
+
161
+ But Rework has no high level API and rewrite your CSS code style
162
+ and indentations. So it can’t be used in text editor plugins.
163
+
164
+ Unlike it PostCSS preserves all spaces and code formatting.
165
+ If you don't change rule, output will be byte‑to‑byte equal.
166
+
167
+ [Rework]: https://github.com/visionmedia/rework
168
+
169
+ ## Usage
170
+
171
+ ### Processor
172
+
173
+ Function `postcss(fn)` creates processor by your function:
174
+
175
+ ```js
176
+ var postcss = require('postcss');
177
+
178
+ var processor = postcss(function (css) {
179
+ // Code to modify CSS
180
+ });
181
+ ```
182
+
183
+ If you want to combine multiple processors (and parse CSS only once),
184
+ you can create empty processor and add several functions by `use(fn)` method:
185
+
186
+ ```js
187
+ var all = postcss().
188
+ use(prefixer).
189
+ use(minifing);
190
+ ```
191
+
192
+ Processor function can just change current CSS node tree:
193
+
194
+ ```js
195
+ postcss(function (css) {
196
+ css.append( /* new rule */ )
197
+ });
198
+ ```
199
+
200
+ or create totally new CSS root and return it:
201
+
202
+ ```js
203
+ postcss(function (css) {
204
+ var newCSS = postcss.root()
205
+ // Add rules and declarations
206
+ return newCSS;
207
+ });
208
+ ```
209
+
210
+ Processor will transform some CSS by `process(css, opts)` method:
211
+
212
+ ```js
213
+ var doubler = postcss(function (css) {
214
+ // Clone each declaration
215
+ css.eachDecl(function (decl) {
216
+ decl.parent.prepend( decl.clone() );
217
+ });
218
+ });
219
+
220
+ var css = "a { color: black; }";
221
+ var result = processor.process(css);
222
+
223
+ result.css //=> "a { color: black; color: black; }"
224
+ ```
225
+
226
+ You can set original CSS filename by `from` options and make syntax error
227
+ messages much more helpful:
228
+
229
+ ```js
230
+ var wrong = "a {";
231
+ processor.process(wrong, { from: 'main.css' });
232
+ //=> Can't parse CSS: Unclosed block at line 1:1 in main.css
233
+ ```
234
+ ### Source Map
235
+
236
+ PostCSS will generate source map, if you set `map` option to `true`
237
+ in `process(css, opts)` method.
238
+
239
+ You must set input and output CSS files paths (by `from` and `to` options)
240
+ to generate correct map.
241
+
242
+ ```js
243
+ var result = processor.process(css, {
244
+ map: true,
245
+ from: 'main.css',
246
+ to: 'main.out.css'
247
+ });
248
+
249
+ result.map //=> '{"version":3,"file":"main.out.css","sources":["main.css"],"names":[],"mappings":"AAAA,KAAI"}'
250
+
251
+ fs.writeFileSync('main.out.map', result.map);
252
+ ```
253
+
254
+ PostCSS can also modify previous source map (for example, from Sass
255
+ compilation). So, if you compile: Sass to CSS and then minify CSS
256
+ by postprocessor, final source map will contain mapping from Sass code
257
+ to minified CSS.
258
+
259
+ Just set original source map content (as string or JS object)
260
+ to `map` option:
261
+
262
+ ```js
263
+ var result = minifier.process(css, {
264
+ map: fs.readFileSync('main.sass.map'),
265
+ from: 'main.sass.css',
266
+ to: 'main.min.css'
267
+ });
268
+
269
+ result.map //=> Source map from main.sass to main.min.css
270
+ ```
271
+
272
+ ### Nodes
273
+
274
+ Processor function will receive `Root` node with CSS node tree inside.
275
+
276
+ ```js
277
+ var processor = postcss(function (cssRoot) {
278
+ });
279
+ ```
280
+
281
+ There are 3 types of child nodes: `AtRule`, `Rule` and `Declaration`.
282
+ All nodes contain `toString()` and `clone()` methods.
283
+
284
+ You can parse CSS and get `Root` node by `postcss.parse(css, opts)` method:
285
+
286
+ ```js
287
+ var postcss = require('postcss');
288
+
289
+ var cssRoot = postcss.parse('a { }');
290
+ ```
291
+
292
+ ### Node Source
293
+
294
+ Every node stores it origin file (if you set `from` option to `process`
295
+ or `parse` method) and position at `source` property:
296
+
297
+ ```
298
+ var root = postcss.parse(css, { from: 'main.css' });
299
+ var rule = root.rules[1];
300
+
301
+ rule.source.file //=> 'main.css'
302
+ rule.source.start //=> { line: 5, position: 1 }
303
+ rule.source.end //=> { line: 10, position: 5 }
304
+ ```
305
+
306
+ ### Whitespaces
307
+
308
+ All nodes (exclude `Root`) have `before` property with all earlier spaces and comments.
309
+
310
+ Nodes with children (`Root`, `AtRule` and `Rule`) contain also `after` property
311
+ with spaces after last child and before `}` or end of file.
312
+
313
+ ```js
314
+ var root = postcss.parse("a {\n color: black;\n}\n");
315
+
316
+ root.after //=> "\n" from end of file
317
+ root.rules[0].after //=> "\n" before }
318
+ root.rules[0].decls[0].before //=> "\n " before color: black
319
+ ```
320
+
321
+ So, the simplest way to minify CSS is to clean `before` and `after` properties:
322
+
323
+ ```js
324
+ var minifier = postcss(function (css) {
325
+ css.eachDecl(function (decl) {
326
+ decl.before = '';
327
+ });
328
+ css.eachRule(function (rule) {
329
+ rule.before = '';
330
+ rule.after = '';
331
+ });
332
+ css.eachAtRule(function (atRule) {
333
+ atRule.before = '';
334
+ atRule.after = '';
335
+ });
336
+ });
337
+
338
+ var css = "a{\n color:black\n}\n";
339
+ minifier.process(css).css //=> "a{color:black}"
340
+ ```
341
+
342
+ ### Raw Properties
343
+
344
+ Some CSS values (like selectors, at-rule params and declaration values) can
345
+ contain comments. PostCSS will clean them for you:
346
+
347
+ ```js
348
+ var root = postcss.parse("a /**/ b {}");
349
+ var ab = root.rules[0];
350
+
351
+ ab.selector //=> 'a b' trimmed and cleaned from comments
352
+ ```
353
+
354
+ But PostCSS saves raw content to stringify it to CSS, if you don’t
355
+ set new value. As you can remember, PostCSS tries to save origin CSS
356
+ byte-to-byte, when it’s possible:
357
+
358
+ ```js
359
+ ab.toString() //=> 'a /**/ b {}' with comment
360
+
361
+ ab.selector = '.link b';
362
+ ab.toString() //=> '.link b' you change value and magic was gone
363
+ ```
364
+
365
+ ### Containers
366
+
367
+ `Root`, `AtRule` and `Rule` nodes can contain children in `rules` or `decls`
368
+ property.
369
+
370
+ There are common method to work with children:
371
+
372
+ * `append(newChild)` to add child at the end of children list.
373
+ * `prepend(newChild)` to add child at the beginning of children list.
374
+ * `insertBefore(existsChild, newChild)` to insert new child before some
375
+ existent children.
376
+ * `insertAfter(existsChild, newChild)` to insert new child after some
377
+ existent children.
378
+ * `remove(child)` to remove child.
379
+ * `index(child)` to return child index.
380
+ * `some(fn)` to return true if `fn` return true on any child.
381
+ * `every(fn)` to return true if `fn` return true on all children.
382
+
383
+ Methods `insertBefore`, `insertAfter` and `remove` can receive child node
384
+ or child index number as existent child argument.
385
+ Have in mind that `index` works much faster.
386
+
387
+ ### Children
388
+
389
+ `AtRule`, `Rule` and `Declaration` nodes should be wrapped in other nodes.
390
+
391
+ All children contain `parent` property with parent node:
392
+
393
+ ```js
394
+ rule.decls[0].parent == rule;
395
+ ```
396
+
397
+ All children has `removeSelf()` method:
398
+
399
+ ```js
400
+ rule.decls[0].removeSelf();
401
+ ```
402
+
403
+ But `remove(index)` in parent with child index is much faster:
404
+
405
+ ```js
406
+ rule.each(function (decl, i) {
407
+ rule.remove(i);
408
+ });
409
+ ```
410
+
411
+ ### Iterators
412
+
413
+ All parent nodes have `each` method to iterate through children nodes:
414
+
415
+ ```js
416
+ root = postcss.parse('a { color: black; display: none }');
417
+
418
+ root.each(function (rule, i) {
419
+ console.log(rule.selector, i); // Will log "a 0"
420
+ });
421
+
422
+ root.rules[0].each(function (decl, i) {
423
+ console.log(decl.prop, i); // Will log "color 0" and "display 1"
424
+ });
425
+ ```
426
+
427
+ Instead of simple `for` or `Array#forEach()` this iterator is safe.
428
+ You can change children inside iteration and it will fix current index:
429
+
430
+ ```js
431
+ rule.rules.forEach(function (decl, i) {
432
+ rule.prepend( decl.clone() );
433
+ // Will be infinity cycle, because on prepend current declaration become
434
+ // second and next index will go to current declaration again
435
+ });
436
+
437
+ rule.each(function (decl, i) {
438
+ rule.prepend( decl.clone() );
439
+ // Will work correct (once clone each declaration), because after prepend
440
+ // iterator index will be recalculated
441
+ });
442
+ ```
443
+
444
+ Because CSS is nested structure, PostCSS contains recursive iterator
445
+ by node type:
446
+
447
+ ```js
448
+ root.eachDecl(function (decl, i) {
449
+ // Each declaration inside root
450
+ });
451
+
452
+ root.eachRule(function (rule, i) {
453
+ // Each rule inside root and any nested at-rules
454
+ });
455
+
456
+ root.eachAtRule(function (atRule, i) {
457
+ // Each at-rule inside root and any nested at-rules
458
+ });
459
+ ```
460
+
461
+ ### Root Node
462
+
463
+ `Root` node contains all CSS tree. Its children can be only `AtRule` or `Rule`
464
+ nodes in `rules` property.
63
465
 
64
- ### Parses everything
466
+ You can create new root by shortcut:
65
467
 
66
- In addition to the unit tests, PostCSS has integration tests to check
67
- CSS parser on real-world sites. Right now parser is tested on GitHub, Twitter,
68
- Bootstrap and Habrahabr styles.
468
+ ```js
469
+ var root = postcss.root();
470
+ ```
471
+
472
+ Method `toString()` will stringify all current CSS:
473
+
474
+ ```js
475
+ root = postcss.parse(css);
476
+ root.toString() == css;
477
+ ```
69
478
 
70
- Also PostCSS parser is very flexible and, for example, can parse any custom
71
- or future at-rules, instead of built-in list.
479
+ ### AtRule Node
480
+
481
+ ```css
482
+ @charset 'utf-8';
72
483
 
73
- ### High-level API
484
+ @font-face {
485
+ font-family: 'Cool'
486
+ }
487
+
488
+ @media print {
489
+ img { display: none }
490
+ }
491
+ ```
492
+
493
+ `AtRule` has two own properties: `name` and `params`.
494
+
495
+ As you see, some at-rules don’t contain any children (like `@charset`
496
+ or `@import`), some of at-rules can contain only declarations
497
+ (like `@font-face` or `@page`), but most of them can contain rules
498
+ and nested at-rules (like `@media`, `@keyframes` and others).
499
+
500
+ Parser select `AtRule` content type by its name. If you create `AtRule`
501
+ node manually, it will detect own content type with new child type on first
502
+ `append` or other add method call:
503
+
504
+ ```js
505
+ var atRule = postcss.atRule({ name: '-x-animations' });
506
+ atRule.rules //=> undefined
507
+ atRule.decls //=> undefined
508
+
509
+ atRule.append( postcss.rule({ selector: 'from' }) );
510
+ atRule.rules.length //=> 1
511
+ atRule.decls //=> undefined
512
+ ```
513
+
514
+ You can create new at-rule by shortcut:
515
+
516
+ ```js
517
+ var atRule = postcss.atRule({ name: 'charset', params: 'utf-8' });
518
+ ```
74
519
 
75
- PostCSS is not only parser and stringifier. It contains useful tools, which
76
- can be used in most of postprocessor:
520
+ ### Rule Node
77
521
 
78
- 1. Safe iterator, which allow to change list inside iteration.
79
- 2. Module to split value list by spaces or commas.
522
+ ```css
523
+ a {
524
+ color: black;
525
+ }
526
+ ```
527
+
528
+ `Rule` node has `selector` property and contains `Declaration` children
529
+ in `decls` property.
530
+
531
+ You can miss `Declaration` constructor in `append` and other insert methods:
532
+
533
+ ```js
534
+ rule.append({ prop: 'color', value: 'black' });
535
+ ```
536
+
537
+ Property `semicolon` marks does last declaration in rule has semicolon or not:
538
+
539
+ ```js
540
+ var root = postcss.parse('a { color: black }');
541
+ root.rules[0].semicolon //=> false
542
+
543
+ var root = postcss.parse('a { color: black; }');
544
+ root.rules[0].semicolon //=> true
545
+ ```
546
+
547
+ You can create new rule by shortcut:
548
+
549
+ ```js
550
+ var rule = postcss.rule({ selector: 'a' });
551
+ ```
552
+
553
+ ### Declaration Node
554
+
555
+ ```css
556
+ color: black
557
+ ```
558
+
559
+ `Declaration` node has `prop` and `value` properties.
560
+
561
+ You can create new declaration by shortcut:
562
+
563
+ ```js
564
+ var decl = postcss.decl({ prop: 'color', value: 'black' });
565
+ ```
566
+
567
+ Or use short form in `append()` and other add methods:
568
+
569
+ ```js
570
+ rule.append({ prop: 'color', value: 'black' });
571
+ ```
package/lib/at-rule.js CHANGED
@@ -32,19 +32,22 @@
32
32
 
33
33
  AtRule.raw('params');
34
34
 
35
- AtRule.prototype.toString = function(last) {
36
- var name, semicolon;
37
- name = (this.before || '') + '@' + this.name;
35
+ AtRule.prototype.stringify = function(builder, last) {
36
+ var params, semicolon;
38
37
  if (this.rules || this.decls) {
39
- return name + this._params.stringify({
38
+ params = this._params.stringify({
40
39
  before: ' ',
41
40
  after: ' '
42
- }) + this.stringifyContent();
41
+ });
42
+ return this.stringifyBlock(builder, '@' + this.name + params + '{');
43
43
  } else {
44
+ if (this.before) {
45
+ builder(this.before);
46
+ }
44
47
  semicolon = !last || this.semicolon ? ';' : '';
45
- return name + this._params.stringify({
48
+ return builder('@' + this.name + this._params.stringify({
46
49
  before: ' '
47
- }) + semicolon;
50
+ }) + semicolon, this);
48
51
  }
49
52
  };
50
53
 
package/lib/container.js CHANGED
@@ -15,28 +15,35 @@
15
15
  return _ref;
16
16
  }
17
17
 
18
- Container.prototype.stringifyContent = function(brackets) {
19
- var inside,
18
+ Container.prototype.stringifyContent = function(builder) {
19
+ var last,
20
20
  _this = this;
21
- if (brackets == null) {
22
- brackets = true;
23
- }
24
21
  if (!this.rules && !this.decls) {
25
22
  return;
26
23
  }
27
- inside = this.rules ? this.rules.map(function(rule, i) {
28
- return rule.toString(_this.rules.length - 1 === i);
29
- }).join('') : this.decls ? this.decls.map(function(i) {
30
- return i.toString();
31
- }).join(';') + (this.semicolon ? ';' : '') : void 0;
32
- if (this.after != null) {
33
- inside += this.after;
24
+ if (this.rules) {
25
+ last = this.rules.length - 1;
26
+ return this.rules.map(function(rule, i) {
27
+ return rule.stringify(builder, last === i);
28
+ });
29
+ } else if (this.decls) {
30
+ last = this.decls.length - 1;
31
+ return this.decls.map(function(decl, i) {
32
+ return decl.stringify(builder, last !== i || _this.semicolon);
33
+ });
34
34
  }
35
- if (brackets) {
36
- return '{' + inside + '}';
37
- } else {
38
- return inside;
35
+ };
36
+
37
+ Container.prototype.stringifyBlock = function(builder, start) {
38
+ if (this.before) {
39
+ builder(this.before);
40
+ }
41
+ builder(start, this, 'start');
42
+ this.stringifyContent(builder);
43
+ if (this.after) {
44
+ builder(this.after);
39
45
  }
46
+ return builder('}', this, 'end');
40
47
  };
41
48
 
42
49
  Container.prototype.push = function(child) {
@@ -17,10 +17,26 @@
17
17
 
18
18
  Declaration.raw('value');
19
19
 
20
- Declaration.prototype.toString = function() {
21
- return (this.before || '') + this.prop + (this.between || '') + ':' + this._value.stringify({
20
+ Declaration.prototype.stringify = function(builder, semicolon) {
21
+ var string;
22
+ if (this.before) {
23
+ builder(this.before);
24
+ }
25
+ string = this.prop + (this.between || '') + ':' + this._value.stringify({
22
26
  before: ' '
23
27
  });
28
+ if (semicolon) {
29
+ string += ';';
30
+ }
31
+ return builder(string, this);
32
+ };
33
+
34
+ Declaration.prototype.removeSelf = function() {
35
+ if (!this.parent) {
36
+ return;
37
+ }
38
+ this.parent.remove(this);
39
+ return this;
24
40
  };
25
41
 
26
42
  Declaration.prototype.clone = function(obj) {
@@ -0,0 +1,65 @@
1
+ (function() {
2
+ var Result, SourceMap, generateMap;
3
+
4
+ SourceMap = require('source-map');
5
+
6
+ Result = require('./result');
7
+
8
+ generateMap = function(css, opts) {
9
+ var builder, column, line, map, prev, result;
10
+ map = new SourceMap.SourceMapGenerator({
11
+ file: opts.to || 'to.css'
12
+ });
13
+ result = new Result(css, '');
14
+ line = 1;
15
+ column = 1;
16
+ builder = function(str, node, type) {
17
+ var last, lines, _ref, _ref1;
18
+ result.css += str;
19
+ if ((node != null ? (_ref = node.source) != null ? _ref.start : void 0 : void 0) && type !== 'end') {
20
+ map.addMapping({
21
+ source: node.source.file || 'from.css',
22
+ original: {
23
+ line: node.source.start.line,
24
+ column: node.source.start.column - 1
25
+ },
26
+ generated: {
27
+ line: line,
28
+ column: column - 1
29
+ }
30
+ });
31
+ }
32
+ lines = str.match(/\n/g);
33
+ if (lines) {
34
+ line += lines.length;
35
+ last = str.lastIndexOf("\n");
36
+ column = str.length - last;
37
+ } else {
38
+ column = column + str.length;
39
+ }
40
+ if ((node != null ? (_ref1 = node.source) != null ? _ref1.end : void 0 : void 0) && type !== 'start') {
41
+ return map.addMapping({
42
+ source: node.source.file || 'from.css',
43
+ original: {
44
+ line: node.source.end.line,
45
+ column: node.source.end.column
46
+ },
47
+ generated: {
48
+ line: line,
49
+ column: column
50
+ }
51
+ });
52
+ }
53
+ };
54
+ css.stringify(builder);
55
+ if (typeof opts.map === 'string') {
56
+ prev = new SourceMap.SourceMapConsumer(opts.map);
57
+ map.applySourceMap(prev);
58
+ }
59
+ result.map = map.toString();
60
+ return result;
61
+ };
62
+
63
+ module.exports = generateMap;
64
+
65
+ }).call(this);
package/lib/node.js CHANGED
@@ -66,7 +66,7 @@
66
66
  });
67
67
  };
68
68
 
69
- Node.prototype.remove = function() {
69
+ Node.prototype.removeSelf = function() {
70
70
  if (!this.parent) {
71
71
  return;
72
72
  }
@@ -74,6 +74,16 @@
74
74
  return this;
75
75
  };
76
76
 
77
+ Node.prototype.toString = function() {
78
+ var builder, result;
79
+ result = '';
80
+ builder = function(str) {
81
+ return result += str;
82
+ };
83
+ this.stringify(builder);
84
+ return result;
85
+ };
86
+
77
87
  Node.prototype.clone = function(overrides) {
78
88
  var cloned, name, value;
79
89
  if (overrides == null) {
package/lib/parse.js CHANGED
@@ -14,8 +14,8 @@
14
14
  Raw = require('./raw');
15
15
 
16
16
  Parser = (function() {
17
- function Parser(source, options) {
18
- this.options = options;
17
+ function Parser(source, opts) {
18
+ this.opts = opts;
19
19
  this.source = source.toString();
20
20
  this.root = new Root();
21
21
  this.current = this.root;
@@ -24,6 +24,7 @@
24
24
  this.types = [this.type];
25
25
  this.pos = -1;
26
26
  this.line = 1;
27
+ this.lines = [];
27
28
  this.column = 0;
28
29
  this.buffer = '';
29
30
  }
@@ -107,24 +108,24 @@
107
108
  }
108
109
  };
109
110
 
110
- Parser.prototype.inAtrule = function(finish) {
111
+ Parser.prototype.inAtrule = function(close) {
111
112
  if (this.inside('atrule-name')) {
112
113
  if (this.space()) {
113
114
  this.checkAtruleName();
114
115
  this.buffer = this.buffer.slice(this.current.name.length);
115
116
  this.trimmed = '';
116
117
  this.setType('atrule-param');
117
- } else if (this.letter === ';' || this.letter === '{' || finish) {
118
+ } else if (this.letter === ';' || this.letter === '{' || close) {
118
119
  this.checkAtruleName();
119
- this.endAtruleParams(finish);
120
+ this.endAtruleParams();
120
121
  } else {
121
122
  this.current.name += this.letter;
122
123
  }
123
124
  return true;
124
125
  } else if (this.inside('atrule-param')) {
125
- if (this.letter === ';' || this.letter === '{' || finish) {
126
+ if (this.letter === ';' || this.letter === '{' || close) {
126
127
  this.current.params = new Raw(this.prevBuffer(), this.trim(this.trimmed));
127
- this.endAtruleParams(finish);
128
+ this.endAtruleParams();
128
129
  } else {
129
130
  this.trimmed += this.letter;
130
131
  }
@@ -164,17 +165,14 @@
164
165
  };
165
166
 
166
167
  Parser.prototype.isBlockEnd = function() {
167
- var after, start;
168
168
  if (this.letter === '}') {
169
169
  if (this.parents.length === 1) {
170
170
  this.error('Unexpected }');
171
171
  } else {
172
172
  if (this.inside('value')) {
173
- start = this.buffer.search(/\s*\}$/);
174
- after = this.buffer.slice(start, -1);
175
- this.buffer = this.buffer.slice(0, +start + 1 || 9e9);
176
- this.inValue(true);
177
- this.current.after = after;
173
+ this.fixEnd(function() {
174
+ return this.inValue('close');
175
+ });
178
176
  } else {
179
177
  if (this.semicolon) {
180
178
  this.current.semicolon = true;
@@ -220,14 +218,14 @@
220
218
  }
221
219
  };
222
220
 
223
- Parser.prototype.inValue = function(finish) {
221
+ Parser.prototype.inValue = function(close) {
224
222
  if (this.inside('value')) {
225
223
  if (this.letter === '(') {
226
224
  this.inBrackets = true;
227
225
  } else if (this.inBrackets && this.letter === ')') {
228
226
  this.inBrackets = false;
229
227
  }
230
- if ((this.letter === ';' && !this.inBrackets) || finish) {
228
+ if ((this.letter === ';' && !this.inBrackets) || close) {
231
229
  if (this.letter === ';') {
232
230
  this.semicolon = true;
233
231
  }
@@ -248,27 +246,29 @@
248
246
 
249
247
  Parser.prototype.endFile = function() {
250
248
  if (this.inside('atrule-param') || this.inside('atrule-name')) {
251
- this.inAtrule(true);
249
+ this.fixEnd(function() {
250
+ return this.inAtrule('close');
251
+ });
252
252
  }
253
253
  if (this.parents.length > 1) {
254
- return this.error('Unclosed block', this.current.line, this.current.column);
254
+ return this.error('Unclosed block', this.current.source.start);
255
255
  } else if (this.inside('comment')) {
256
- return this.error('Unclosed comment', this.commentPos.line, this.commentPos.column);
256
+ return this.error('Unclosed comment', this.commentPos);
257
257
  } else if (this.quote) {
258
- return this.error('Unclosed quote', this.quotePos.line, this.quotePos.column);
258
+ return this.error('Unclosed quote', this.quotePos);
259
259
  } else {
260
260
  return this.root.after = this.buffer;
261
261
  }
262
262
  };
263
263
 
264
- Parser.prototype.error = function(message, line, column) {
265
- if (line == null) {
266
- line = this.line;
267
- }
268
- if (column == null) {
269
- column = this.column;
264
+ Parser.prototype.error = function(message, position) {
265
+ if (position == null) {
266
+ position = {
267
+ line: this.line,
268
+ column: this.column
269
+ };
270
270
  }
271
- throw new SyntexError(message, this.source, line, column, this.options.file);
271
+ throw new SyntexError(message, this.source, position, this.opts.from);
272
272
  };
273
273
 
274
274
  Parser.prototype.move = function() {
@@ -277,6 +277,7 @@
277
277
  this.letter = this.source[this.pos];
278
278
  this.buffer += this.letter;
279
279
  if (this.letter === "\n") {
280
+ this.lines[this.line] = this.column - 1;
280
281
  this.line += 1;
281
282
  return this.column = 0;
282
283
  }
@@ -302,13 +303,52 @@
302
303
  this.current.push(node);
303
304
  this.parents.push(node);
304
305
  this.current = node;
305
- node.line = this.line;
306
- node.column = this.column;
307
- node.before = this.buffer.slice(0, -1);
306
+ this.current.source = {
307
+ start: {
308
+ line: this.line,
309
+ column: this.column
310
+ }
311
+ };
312
+ if (this.opts.from) {
313
+ this.current.source.file = this.opts.from;
314
+ }
315
+ this.current.before = this.buffer.slice(0, -1);
308
316
  return this.buffer = '';
309
317
  };
310
318
 
319
+ Parser.prototype.fixEnd = function(callback) {
320
+ var after, all, el, last, lines, start;
321
+ if (this.letter === '}') {
322
+ start = this.buffer.search(/\s*\}$/);
323
+ after = this.buffer.slice(start, -1);
324
+ } else {
325
+ start = this.buffer.search(/\s*$/);
326
+ after = this.buffer.slice(start);
327
+ }
328
+ this.buffer = this.buffer.slice(0, +start + 1 || 9e9);
329
+ el = this.current;
330
+ callback.apply(this);
331
+ lines = after.match(/\n/g);
332
+ if (lines) {
333
+ el.source.end.line -= lines.length;
334
+ all = this.lines[el.source.end.line];
335
+ last = after.indexOf("\n");
336
+ if (last === -1) {
337
+ last = after.length;
338
+ }
339
+ el.source.end.column = all - last;
340
+ } else {
341
+ el.source.end.column -= after.length;
342
+ }
343
+ this.current.after = after;
344
+ return this.buffer = after;
345
+ };
346
+
311
347
  Parser.prototype.pop = function() {
348
+ this.current.source.end = {
349
+ line: this.line,
350
+ column: this.column
351
+ };
312
352
  this.popType();
313
353
  this.parents.pop();
314
354
  this.current = this.parents[this.parents.length - 1];
@@ -340,7 +380,7 @@
340
380
  }
341
381
  };
342
382
 
343
- Parser.prototype.endAtruleParams = function(finish) {
383
+ Parser.prototype.endAtruleParams = function() {
344
384
  var type;
345
385
  if (this.letter === '{') {
346
386
  type = this.atruleType();
@@ -351,10 +391,7 @@
351
391
  if (this.letter === ';') {
352
392
  this.current.semicolon = true;
353
393
  }
354
- this.pop();
355
- if (this.letter !== ';') {
356
- return this.buffer = this.letter;
357
- }
394
+ return this.pop();
358
395
  }
359
396
  };
360
397
 
@@ -372,12 +409,12 @@
372
409
 
373
410
  })();
374
411
 
375
- module.exports = function(source, options) {
412
+ module.exports = function(source, opts) {
376
413
  var parser;
377
- if (options == null) {
378
- options = {};
414
+ if (opts == null) {
415
+ opts = {};
379
416
  }
380
- parser = new Parser(source, options);
417
+ parser = new Parser(source, opts);
381
418
  parser.loop();
382
419
  return parser.root;
383
420
  };
package/lib/postcss.js CHANGED
@@ -1,7 +1,17 @@
1
1
  (function() {
2
- var PostCSS, Root, postcss,
2
+ var AtRule, Declaration, PostCSS, Result, Root, Rule, generateMap, postcss,
3
3
  __slice = [].slice;
4
4
 
5
+ generateMap = require('./generate-map');
6
+
7
+ Declaration = require('./declaration');
8
+
9
+ AtRule = require('./at-rule');
10
+
11
+ Result = require('./result');
12
+
13
+ Rule = require('./rule');
14
+
5
15
  Root = require('./root');
6
16
 
7
17
  PostCSS = (function() {
@@ -14,12 +24,12 @@
14
24
  return this;
15
25
  };
16
26
 
17
- PostCSS.prototype.process = function(css, options) {
27
+ PostCSS.prototype.process = function(css, opts) {
18
28
  var parsed, processor, returned, _i, _len, _ref;
19
- if (options == null) {
20
- options = {};
29
+ if (opts == null) {
30
+ opts = {};
21
31
  }
22
- parsed = postcss.parse(css, options);
32
+ parsed = postcss.parse(css, opts);
23
33
  _ref = this.processors;
24
34
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
25
35
  processor = _ref[_i];
@@ -28,7 +38,11 @@
28
38
  parsed = returned;
29
39
  }
30
40
  }
31
- return parsed.toString();
41
+ if (opts.map) {
42
+ return generateMap(parsed, opts);
43
+ } else {
44
+ return new Result(parsed, parsed.toString());
45
+ }
32
46
  };
33
47
 
34
48
  return PostCSS;
@@ -43,6 +57,22 @@
43
57
 
44
58
  postcss.parse = require('./parse');
45
59
 
60
+ postcss.decl = function(defaults) {
61
+ return new Declaration(defaults);
62
+ };
63
+
64
+ postcss.atRule = function(defaults) {
65
+ return new AtRule(defaults);
66
+ };
67
+
68
+ postcss.rule = function(defaults) {
69
+ return new Rule(defaults);
70
+ };
71
+
72
+ postcss.root = function(defaults) {
73
+ return new Root(defaults);
74
+ };
75
+
46
76
  module.exports = postcss;
47
77
 
48
78
  }).call(this);
package/lib/result.js ADDED
@@ -0,0 +1,20 @@
1
+ (function() {
2
+ var Result;
3
+
4
+ Result = (function() {
5
+ function Result(parsed, css) {
6
+ this.parsed = parsed;
7
+ this.css = css;
8
+ }
9
+
10
+ Result.prototype.toString = function() {
11
+ return this.css;
12
+ };
13
+
14
+ return Result;
15
+
16
+ })();
17
+
18
+ module.exports = Result;
19
+
20
+ }).call(this);
package/lib/root.js CHANGED
@@ -22,8 +22,11 @@
22
22
  return child;
23
23
  };
24
24
 
25
- Root.prototype.toString = function() {
26
- return this.stringifyContent(false);
25
+ Root.prototype.stringify = function(builder) {
26
+ this.stringifyContent(builder);
27
+ if (this.after) {
28
+ return builder(this.after);
29
+ }
27
30
  };
28
31
 
29
32
  return Root;
package/lib/rule.js CHANGED
@@ -17,10 +17,10 @@
17
17
 
18
18
  Rule.raw('selector');
19
19
 
20
- Rule.prototype.toString = function() {
21
- return (this.before || '') + this._selector.stringify({
20
+ Rule.prototype.stringify = function(builder) {
21
+ return this.stringifyBlock(builder, this._selector.stringify({
22
22
  after: ' '
23
- }) + this.stringifyContent();
23
+ }) + '{');
24
24
  };
25
25
 
26
26
  return Rule;
@@ -6,12 +6,13 @@
6
6
  SyntaxError = (function(_super) {
7
7
  __extends(SyntaxError, _super);
8
8
 
9
- function SyntaxError(text, source, line, column, file) {
9
+ function SyntaxError(text, source, pos, file) {
10
10
  this.source = source;
11
- this.line = line;
12
- this.column = column;
13
11
  this.file = file;
14
- this.message = "Can't parse CSS: " + text + " at line " + this.line + ":" + this.column;
12
+ this.line = pos.line;
13
+ this.column = pos.column;
14
+ this.message = "Can't parse CSS: " + text;
15
+ this.message += " at line " + pos.line + ":" + pos.column;
15
16
  if (this.file) {
16
17
  this.message += " in " + this.file;
17
18
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postcss",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Framework for CSS postprocessors",
5
5
  "keywords": ["css", "parser", "postproccessor"],
6
6
  "author": "Andrey Sitnik <andrey@sitnik.ru>",
@@ -10,12 +10,13 @@
10
10
  "url": "https://github.com/ai/postcss.git"
11
11
  },
12
12
  "dependencies": {
13
+ "source-map": "*"
13
14
  },
14
15
  "devDependencies": {
15
16
  "coffee-script": "1.6.3",
16
17
  "fs-extra": "0.8.1",
17
- "should": "2.0.2",
18
- "mocha": "1.14.0"
18
+ "should": "2.1.1",
19
+ "mocha": "1.15.1"
19
20
  },
20
21
  "main": "lib/postcss",
21
22
  "scripts": {
package/ChangeLog DELETED
@@ -1,2 +0,0 @@
1
- == 0.1 (Count Andromalius)
2
- * Initial release.