├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── index.test.js ├── package.json └── tests ├── each.css ├── expected ├── each.css ├── function.css ├── import.css ├── mixin.css ├── stack.css └── variable.css ├── function.css ├── import.css ├── mixin.css ├── stack.css └── variable.css /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = crlf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | cache: yarn 4 | 5 | node_js: 6 | # run based on all LTS builds 7 | - 22 8 | - 20 9 | - 18 10 | - 16 11 | - 14 12 | - 12 13 | - 10 14 | 15 | install: 16 | - npm install 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 3.0.0 - 2021-03-07 4 | Breaking changes: 5 | * Drop support for Node < 10. 6 | * Upgrading Postcss to version 8. 7 | * Postcss is not a peer dependency so now required to install in project. 8 | 9 | ## 2.1.4 - 2017-12-20 10 | * Updated readme 11 | 12 | ## 2.1.3 - 2017-12-20 13 | * Updated node dependencies for travis 14 | 15 | ## 2.1.2 - 2017-12-20 16 | * Updated node dependency version 17 | 18 | ## 2.1.1 - 2017-12-20 19 | * Fixed ignore files 20 | 21 | ## 2.1.0 - 2017-12-20 22 | * Fixed issues from 2.0.0 23 | * Added editor config 24 | * Added tests 25 | * Added linting 26 | 27 | ## 2.0.0 - 2017-12-01 28 | * New version, rewritten to use maps better and to be able to pass in settings 29 | 30 | ## 1.2.0 - 2017-08-23 31 | * Added error message when compilation fails so that the plugin doesn't breaking streams 32 | 33 | ## 1.1.2 - 2017-07-21 34 | * Changed dev dependencies to dependencies 35 | 36 | ## 1.1.1 - 2017-07-14 37 | * Fixed issue with multiple plugins used at the same time 38 | 39 | ## 1.0.0 - 2017-07-11 40 | * Initial release 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2017 AUTHOR_NAME 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PostCSS Node Sass [PostCSS Logo][PostCSS] 2 | [![NPM Version](https://img.shields.io/npm/v/postcss-node-sass.svg)](https://www.npmjs.com/package/postcss-node-sass) 3 | [![Build Status](https://travis-ci.org/arpadHegedus/postcss-node-sass.svg?branch=master)](https://travis-ci.org/arpadHegedus/postcss-node-sass) 4 | [![BGitter Chat](https://img.shields.io/badge/chat-gitter-blue.svg)](https://gitter.im/postcss/postcss) 5 | 6 | A [PostCSS] plugin to parse styles with [node-sass] 7 | 8 | 9 | [PostCSS]: https://github.com/postcss/postcss 10 | [Gulp]: https://github.com/gulpjs/gulp 11 | [node-sass]: https://github.com/sass/node-sass 12 | 13 | ## Installation 14 | 15 | ```js 16 | npm install postcss-node-sass 17 | ``` 18 | 19 | ## Usage 20 | 21 | After installation, you can process Sass via PostCSS like this 22 | 23 | ```js 24 | postcss([require('postcss-node-sass')]).process(yourCSS); 25 | ``` 26 | 27 | or using [Gulp] a typical gulpfile might look like: 28 | 29 | ```js 30 | let gulp = require('gulp'), 31 | postcss = require('gulp-postcss'), 32 | sass = require('postcss-node-sass'); 33 | gulp.task('css', () => { 34 | gulp.src('path/to/dev/css') 35 | .pipe(postcss([ 36 | /* postcss plugins before parsing sass */ 37 | sass() 38 | /* postcss plugins after parsing sass */ 39 | ])) 40 | .pipe(gulp.dest('path/to/build/css')); 41 | }); 42 | 43 | /* rest of gulp file */ 44 | ``` 45 | 46 | ## Options 47 | 48 | The [Node Sass options](https://github.com/sass/node-sass#options) can be passed in to [this plugin](https://github.com/arpadHegedus/postcss-node-sass) except for `data`, `file`, `importer`, `omitSourceMapUrl`, `outFile`, `sourceMap`, `sourceMapContents` as these are handled by the plugin. Furthermore, by default the processor will use `outputStyle:'expanded'` and `indentWidth:4`. 49 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * POSTCSS-NODE-SASS 3 | * A PostCSS plugin to parse styles with node-sass 4 | */ 5 | 6 | let postcss = require('postcss'), 7 | defaultNodeSass = require('node-sass'); 8 | 9 | module.exports = opt => ({ 10 | postcssPlugin: 'postcss-node-sass', 11 | Once (root, { result }) { 12 | let sass = opt.sass || defaultNodeSass; 13 | let map = typeof result.opts.map === 'object' ? result.opts.map : {} 14 | let css = root.toResult(Object.assign(result.opts, { 15 | map: Object.assign({ 16 | annotation: false, 17 | inline: false, 18 | sourcesContent: true 19 | }, map) 20 | })); 21 | opt = Object.assign({ 22 | indentWidth: 4, 23 | omitSourceMapUrl: true, 24 | outputStyle: 'expanded', 25 | sourceMap: true, 26 | sourceMapContents: true 27 | }, opt, { 28 | data: css.css, 29 | file: result.opts.from, 30 | outFile: result.opts.to 31 | }); 32 | let includedFiles 33 | return new Promise((resolve, reject) => sass.render( 34 | opt, 35 | (err, res) => err ? reject(err) : resolve(res) 36 | )).then(res => { 37 | includedFiles = res.stats.includedFiles.filter((item, pos, array) => array.indexOf(item) === pos) 38 | return postcss.parse(res.css.toString(), { 39 | from: result.opts.from, 40 | map: { 41 | prev: res.map ? JSON.parse(res.map.toString()) : '' 42 | } 43 | }) 44 | }).then(res => { 45 | result.root = res; 46 | result.messages = includedFiles.map(file => ({ type: 'dependency', parent: result.opts.from, file })) 47 | }); 48 | } 49 | }); 50 | 51 | module.exports.postcss = true; 52 | -------------------------------------------------------------------------------- /index.test.js: -------------------------------------------------------------------------------- 1 | let fs = require('fs'), 2 | plugin = require('./'), 3 | postcss = require('postcss'), 4 | testFile = (file, options = {}, plugins = []) => () => { 5 | let inputFile = `./tests/${file}`, 6 | outputFile = `./tests/expected/${file}`, 7 | input = fs.readFileSync(inputFile, 'utf8').toString(), 8 | expected = fs.readFileSync(outputFile, 'utf8').toString(); 9 | options.from = inputFile; 10 | options.to = outputFile; 11 | if (plugins.length === 0) plugins.push(plugin(options)); 12 | return expect(postcss(plugins).process(input, options) 13 | .then(result => { 14 | return result.css; 15 | }) 16 | .catch(error => error)).resolves.toEqual(expected); 17 | }; 18 | 19 | it('can stack selectors', testFile('stack.css')); 20 | it('can import other files', testFile('import.css')); 21 | it('can use variables', testFile('variable.css')); 22 | it('can use functions', testFile('function.css')); 23 | it('can use mixins', testFile('mixin.css')); 24 | it('can use @each', testFile('each.css')); 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-node-sass", 3 | "version": "3.1.4", 4 | "description": "A PostCSS plugin to parse styles with node-sass", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "repository": "https://github.com/arpadHegedus/postcss-node-sass.git", 8 | "homepage": "https://github.com/arpadHegedus/postcss-node-sass#readme", 9 | "bugs": "https://github.com/arpadHegedus/postcss-node-sass/issues", 10 | "keywords": [ 11 | "sass", 12 | "postcss", 13 | "postcss-plugin", 14 | "node-sass", 15 | "preprocessor", 16 | "libsass", 17 | "parse", 18 | "plugin", 19 | "scss", 20 | "css", 21 | "style" 22 | ], 23 | "author": { 24 | "name": "Arpad Hegedus", 25 | "email": "hegedus.arpad@gmail.com", 26 | "web": "http://arpadhegedus.com" 27 | }, 28 | "contributors": [ 29 | { 30 | "name": "Arpad Hegedus", 31 | "email": "hegedus.arpad@gmail.com", 32 | "web": "http://arpadhegedus.com" 33 | }, 34 | { 35 | "name": "Jonathan Neal", 36 | "email": "jonathantneal@hotmail.com", 37 | "web": "https://jonathantneal.github.io" 38 | } 39 | ], 40 | "maintainers": [ 41 | { 42 | "name": "Arpad Hegedus", 43 | "email": "hegedus.arpad@gmail.com", 44 | "web": "http://arpadhegedus.com" 45 | } 46 | ], 47 | "engines": { 48 | "node": ">=10.0.0 <23.0.0" 49 | }, 50 | "scripts": { 51 | "test": "jest" 52 | }, 53 | "eslintConfig": { 54 | "extends": "eslint-config-postcss/es5", 55 | "env": { 56 | "jest": true 57 | } 58 | }, 59 | "jest": { 60 | "testURL": "http://localhost" 61 | }, 62 | "dependencies": { 63 | "merge-source-map": "^1.1.0", 64 | "node-sass": "^6.0.1" 65 | }, 66 | "devDependencies": { 67 | "babel-eslint": "^10.0.1", 68 | "eslint": "^5.9.0", 69 | "eslint-config-postcss": "^3.0.6", 70 | "jest": "^23.6.0", 71 | "postcss": "^8.2.7" 72 | }, 73 | "peerDependencies": { 74 | "postcss": "^8.x.x" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/each.css: -------------------------------------------------------------------------------- 1 | @each $item in p a div { 2 | '#{$item}' { 3 | background: white; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/expected/each.css: -------------------------------------------------------------------------------- 1 | p { 2 | background: white; 3 | } 4 | 5 | a { 6 | background: white; 7 | } 8 | 9 | div { 10 | background: white; 11 | } 12 | -------------------------------------------------------------------------------- /tests/expected/function.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 13px; 3 | } 4 | -------------------------------------------------------------------------------- /tests/expected/import.css: -------------------------------------------------------------------------------- 1 | div { 2 | background: white; 3 | } 4 | 5 | div p { 6 | color: black; 7 | } 8 | 9 | a { 10 | color: blue; 11 | } 12 | -------------------------------------------------------------------------------- /tests/expected/mixin.css: -------------------------------------------------------------------------------- 1 | div.wrap { 2 | max-width: 1200px; 3 | margin-left: auto; 4 | margin-right: auto; 5 | } 6 | -------------------------------------------------------------------------------- /tests/expected/stack.css: -------------------------------------------------------------------------------- 1 | div { 2 | background: white; 3 | } 4 | 5 | div p { 6 | color: black; 7 | } 8 | -------------------------------------------------------------------------------- /tests/expected/variable.css: -------------------------------------------------------------------------------- 1 | a { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /tests/function.css: -------------------------------------------------------------------------------- 1 | @function addTen($number: 1px) { 2 | @return $number + 10; 3 | } 4 | body { 5 | padding: addTen(3px); 6 | } 7 | -------------------------------------------------------------------------------- /tests/import.css: -------------------------------------------------------------------------------- 1 | @import 'stack'; 2 | 3 | a { 4 | color: blue; 5 | } 6 | -------------------------------------------------------------------------------- /tests/mixin.css: -------------------------------------------------------------------------------- 1 | @mixin wrap($width: 1024px) { 2 | max-width: $width; 3 | margin-left: auto; 4 | margin-right: auto; 5 | } 6 | div.wrap { 7 | @include wrap(1200px); 8 | } 9 | -------------------------------------------------------------------------------- /tests/stack.css: -------------------------------------------------------------------------------- 1 | div { 2 | background: white; 3 | p { 4 | color: black; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/variable.css: -------------------------------------------------------------------------------- 1 | $color: red; 2 | a { 3 | color: red; 4 | } 5 | --------------------------------------------------------------------------------