├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── index.js ├── package.json └── test ├── compose.spec.css ├── index.spec.js └── test.spec.css /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml,*.md}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac 2 | .DS_Store 3 | 4 | # Node 5 | node_modules 6 | npm-debug.log 7 | .nyc_output 8 | coverage 9 | 10 | # Swap 11 | .swp 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | *.swp 4 | coverage 5 | .nyc_output 6 | test 7 | .editorconfig 8 | .gitattributes 9 | .travis.yml 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'node' 4 | - 'lts/*' 5 | - '8' 6 | 7 | after_success: 8 | - npm run coverage 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License (MIT) 2 | 3 | Copyright (c) 2016 PostHTML Aleksandr Yakunichev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NPM][npm]][npm-url] 2 | [![Deps][deps]][deps-url] 3 | [![Tests][travis]][travis-url] 4 | [![Coverage][cover]][cover-url] 5 | [![XO Code Style][style]][style-url] 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 |

PostCSS Modules Plugin

14 |

A plugin to modules resources with ease

15 |

16 | 17 |

Install

18 | 19 | ```bash 20 | npm i -D posthtml-postcss-modules 21 | ``` 22 | 23 |

Usage

24 | 25 | ```js 26 | options = { 27 | root: './', // root path for links lookup 28 | from: '' // pathname to resolving file (it's always better to provide it) 29 | plugins: [], // postcss plugins to apply for every link 30 | generateScopedName: genericNames // function to process css names or string 31 | }; 32 | ``` 33 | 34 | ### Related 35 | 36 | [CSS Modules](https://github.com/css-modules/css-modules) 37 | 38 |

Example

39 | 40 | ```html 41 | 42 | 43 | 44 | 47 | 48 |
49 | 51 |
52 | ``` 53 | 54 | ```css 55 | /* header.css */ 56 | .root {background-color: white;} 57 | ``` 58 | 59 | ```js 60 | const { readFileSync } = require('fs') 61 | const posthtml = require('posthtml') 62 | 63 | posthtml() 64 | .use(require('posthtml-postcss-modules')({ 65 | generateScopedName: '[hash:base64:5]' 66 | })) 67 | .process(readFileSync('header.html', 'utf8')) 68 | .then((result) => result) 69 | }) 70 | ``` 71 | 72 | ```html 73 | 74 | 75 | 76 |
77 | 78 |
79 | ``` 80 | 81 |

LICENSE

82 | 83 | MIT License (MIT) 84 | 85 | Copyright (c) 2016 PostHTML [Aleksandr Yakunichev](https://github.com/canvaskisa) 86 | 87 | > Permission is hereby granted, free of charge, to any person obtaining a copy 88 | of this software and associated documentation files (the "Software"), to deal 89 | in the Software without restriction, including without limitation the rights 90 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 91 | copies of the Software, and to permit persons to whom the Software is 92 | furnished to do so, subject to the following conditions: 93 | 94 | > The above copyright notice and this permission notice shall be included in all 95 | copies or substantial portions of the Software. 96 | 97 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 98 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 99 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 100 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 101 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 102 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 103 | SOFTWARE. 104 | 105 | [npm]: https://img.shields.io/npm/v/posthtml-postcss-modules.svg 106 | [npm-url]: https://npmjs.com/package/posthtml-postcss-modules 107 | 108 | [deps]: https://david-dm.org/posthtml/posthtml-postcss-modules.svg 109 | [deps-url]: https://david-dm.org/posthtml/posthtml-postcss-modules 110 | 111 | [style]: https://img.shields.io/badge/code_style-XO-5ed9c7.svg 112 | [style-url]: https://github.com/sindresorhus/xo 113 | 114 | [travis]: http://img.shields.io/travis/posthtml/posthtml-postcss-modules.svg 115 | [travis-url]: https://travis-ci.org/posthtml/posthtml-postcss-modules 116 | 117 | [cover]: https://coveralls.io/repos/github/posthtml/posthtml-postcss-modules/badge.svg?branch=master 118 | [cover-url]: https://coveralls.io/github/posthtml/posthtml-postcss-modules?branch=master 119 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const match = require('posthtml-match-helper'); 6 | const genericNames = require('generic-names'); 7 | 8 | const postcss = require('postcss'); 9 | const Values = require('postcss-modules-values'); 10 | const LocalByDefault = require('postcss-modules-local-by-default'); 11 | const ExtractImports = require('postcss-modules-extract-imports'); 12 | const Scope = require('postcss-modules-scope'); 13 | const Parser = require('postcss-modules-parser'); 14 | 15 | /** 16 | * Retieves content of node element 17 | * or reads file from href attribute of 18 | * @param {Object} options [plugin's options] 19 | * @param {Object} node [ast node] 20 | * @return {Promise} contents of file or `; 14 | 15 | const {html} = await posthtml().use(plugin({ 16 | generateScopedName: '[local]' 17 | })).process(actual); 18 | 19 | t.is(html, expected); 20 | }); 21 | 22 | test('Must replace html classes with processed ones', async t => { 23 | const actual = '
'; 24 | const expected = `
`; 25 | 26 | const {html} = await posthtml().use(plugin({ 27 | generateScopedName: '[local]' 28 | })).process(actual); 29 | 30 | t.is(html, expected); 31 | }); 32 | 33 | test('Must keep previous classes on html elements', async t => { 34 | const actual = '
'; 35 | const expected = `
`; 36 | 37 | const {html} = await posthtml().use(plugin({ 38 | generateScopedName: '[local]' 39 | })).process(actual); 40 | 41 | t.is(html, expected); 42 | }); 43 | 44 | test('Must fail when module\'s href cannot be found', async t => { 45 | const source = '
'; 46 | const error = await t.throwsAsync(posthtml().use(plugin()).process(source)); 47 | t.is(error.message, 'ENOENT: no such file or directory, open \'undefined.css\''); 48 | }); 49 | 50 | test('Must process
'; 52 | const expected = '
'; 53 | const {html} = await posthtml().use(plugin({generateScopedName: '[local]-test'})).process(source); 54 | t.is(html, expected); 55 | }); 56 | 57 | test('Must match classes properly', async t => { 58 | const source = '
'; 59 | const expected = '
'; 60 | const {html} = await posthtml().use(plugin({generateScopedName: '[local]-test'})).process(source); 61 | t.is(html, expected); 62 | }); 63 | 64 | test('Must be able to compose styles', async t => { 65 | const source = '
'; 66 | const expected = '
'; 67 | const {html} = await posthtml().use(plugin({generateScopedName: '[local]-test'})).process(source); 68 | t.is(html, expected); 69 | }); 70 | 71 | test('Must be able to compose styles from file', async t => { 72 | const source = '
'; 73 | const expected = '
'; 74 | const {html} = await posthtml().use(plugin({from: __filename, generateScopedName: '[local]-test'})).process(source); 75 | t.is(html, expected); 76 | }); 77 | 78 | test('Must generate default classnames if generateScopedName is undefined', async t => { 79 | const actual = '
'; 80 | const expected = '
'; 81 | const {html} = await posthtml().use(plugin({from: __filename})).process(actual); 82 | t.is(html.replace(/(\n|\t)/g, ''), expected); 83 | }); 84 | -------------------------------------------------------------------------------- /test/test.spec.css: -------------------------------------------------------------------------------- 1 | .root {background-color: white;} 2 | .test {color: red;} 3 | --------------------------------------------------------------------------------