├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __tests__ ├── config.ts ├── files │ ├── config.json │ ├── fixtures │ │ ├── css │ │ │ ├── css-attributes.css │ │ │ ├── css-variables.css │ │ │ ├── style.css │ │ │ ├── style2.css │ │ │ └── subdirectory │ │ │ │ └── style.css │ │ ├── html │ │ │ ├── index-with-style.html │ │ │ └── index.html │ │ ├── js │ │ │ ├── complex.js │ │ │ ├── main.js │ │ │ └── react.js │ │ └── pug │ │ │ └── index.pug │ ├── issue21 │ │ ├── fixtures │ │ │ ├── index.html │ │ │ └── style.css │ │ └── results │ │ │ ├── index.html │ │ │ └── style.css │ ├── issue70 │ │ ├── fixtures │ │ │ ├── index.html │ │ │ └── style.css │ │ └── results │ │ │ ├── index.html │ │ │ └── style.css │ └── results │ │ ├── css │ │ ├── css-attributes-ignore.css │ │ ├── css-attributes-pre-suffix.css │ │ ├── css-attributes.css │ │ ├── css-variables-ignore.css │ │ ├── css-variables.css │ │ ├── style-prefix.css │ │ ├── style-with-config.css │ │ ├── style.css │ │ ├── style2.css │ │ └── subdirectory │ │ │ └── style.css │ │ ├── html │ │ ├── index-with-style.html │ │ └── index.html │ │ ├── js │ │ ├── complex.js │ │ ├── main.js │ │ └── react.js │ │ └── pug │ │ └── index.pug ├── generateMapping.ts ├── helpers │ └── reset.ts ├── integration.ts ├── integration_mapping.ts ├── loadMapping.ts ├── processAuto.ts ├── processAutoSync.ts ├── processCss.ts ├── processCssSync.ts ├── processJs.ts ├── processJsSync.ts ├── processPug.ts ├── processPugSync.ts ├── save.ts └── saveSync.ts ├── docs ├── api │ ├── config.md │ ├── loadMapping.md │ ├── mapping.md │ ├── processAny.md │ ├── processAuto.md │ ├── processCss.md │ ├── processHtml.md │ ├── processJs.md │ └── processPug.md └── caveats.md ├── global.d.ts ├── jest.config.js ├── jest.setup.js ├── lib ├── Config.ts ├── helper │ ├── save.ts │ └── saveSync.ts ├── index.ts ├── mapping │ ├── generateMapping.ts │ └── loadMapping.ts └── process │ ├── defaults.ts │ ├── process.ts │ ├── processSync.ts │ ├── replaceData.ts │ └── typeChooser │ ├── typeChooser.ts │ └── typeChooserSync.ts ├── package-lock.json ├── package.json └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "add-module-exports" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | __tests__/files 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | jest: true, 6 | }, 7 | extends: [ 8 | 'plugin:@typescript-eslint/recommended', 9 | 'airbnb-base' 10 | ], 11 | plugins: ['@typescript-eslint'], 12 | parser: '@typescript-eslint/parser', 13 | globals: { 14 | Atomics: 'readonly', 15 | SharedArrayBuffer: 'readonly', 16 | }, 17 | parserOptions: { 18 | ecmaVersion: 2018, 19 | sourceType: 'module', 20 | }, 21 | settings: { 22 | 'import/resolver': { 23 | node: { 24 | extensions: ['.js', '.ts'], 25 | }, 26 | }, 27 | }, 28 | overrides: [ 29 | { 30 | files: ['*.ts', '*.tsx'], 31 | rules: { 32 | 'no-dupe-class-members': 'off', 33 | }, 34 | }, 35 | ], 36 | rules: { 37 | '@typescript-eslint/explicit-function-return-type': ['warn', { 38 | allowTypedFunctionExpressions: true, 39 | allowExpressions: true, 40 | }], 41 | 'import/extensions': ['error', 'ignorePackages', { 42 | js: 'never', 43 | ts: 'never', 44 | }], 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: jpeer 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | 8 | jobs: 9 | test: 10 | strategy: 11 | matrix: 12 | node: [12.x, 14.x, 16.x] 13 | os: [ubuntu-latest] 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Use Node.js ${{ matrix.node }} 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: ${{ matrix.node }} 21 | - run: npm install 22 | - run: npm test 23 | - name: Coveralls Parallel 24 | uses: coverallsapp/github-action@master 25 | with: 26 | github-token: ${{ secrets.github_token }} 27 | flag-name: run-${{ matrix.test_number }} 28 | parallel: true 29 | finish: 30 | runs-on: ubuntu-latest 31 | needs: test 32 | steps: 33 | - name: Coveralls 34 | uses: coverallsapp/github-action@master 35 | with: 36 | github-token: ${{ secrets.GITHUB_TOKEN }} 37 | parallel-finished: true 38 | 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # modules 2 | node_modules 3 | npm-debug.log 4 | 5 | **/testCache/** 6 | coverage 7 | .vscode 8 | .nyc_output 9 | dest 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | __tests__/ 3 | .travis.yml 4 | lib/ 5 | docs 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 4.1.0 - November, 23 2021 2 | 3 | * 7e32f5c Feat: enable optimization from rcs-core by default (closes #80) (#81) (Jan Peer Stöcklmair) 4 | * 6a10373 Chore: add github actions (JPeer264) 5 | * 704d808 4.0.0 (JPeer264) 6 | * 04ecf36 Chore: update babel (JPeer264) 7 | * f5bff64 Feat: move to previous .config instead of .Config.getInstance() (JPeer264) 8 | * 9931075 Chore(deps): bump pug-code-gen from 2.0.2 to 2.0.3 (#72) (dependabot[bot]) 9 | * e002f00 Chore(deps): bump lodash from 4.17.15 to 4.17.21 (#73) (dependabot[bot]) 10 | * 2c14a98 Chore(deps): bump hosted-git-info from 2.8.5 to 2.8.9 (#74) (dependabot[bot]) 11 | * bac2aef Chore(deps): bump ws from 7.2.1 to 7.4.6 (#75) (dependabot[bot]) 12 | * 9e03949 Chore(deps): bump postcss from 7.0.35 to 7.0.36 (#76) (dependabot[bot]) 13 | 14 | 4.0.0 - June, 22 2021 15 | 16 | * 04ecf36 Chore: update babel (JPeer264) 17 | * f5bff64 Feat: move to previous .config instead of .Config.getInstance() (JPeer264) 18 | * 9931075 Chore(deps): bump pug-code-gen from 2.0.2 to 2.0.3 (#72) (dependabot[bot]) 19 | * e002f00 Chore(deps): bump lodash from 4.17.15 to 4.17.21 (#73) (dependabot[bot]) 20 | * 2c14a98 Chore(deps): bump hosted-git-info from 2.8.5 to 2.8.9 (#74) (dependabot[bot]) 21 | * bac2aef Chore(deps): bump ws from 7.2.1 to 7.4.6 (#75) (dependabot[bot]) 22 | * 9e03949 Chore(deps): bump postcss from 7.0.35 to 7.0.36 (#76) (dependabot[bot]) 23 | 24 | 4.0.0-rc.3 - January, 07 2021 25 | 26 | * 9f74c0b Feat: use rcs-core mappings (JPeer264) 27 | * adf10d1 Test: add test case for issue 70 (closes #70) (JPeer264) 28 | * 8b68a2a Chore: update rcs-core in dev (JPeer264) 29 | * f1ddcef Docs: add rcs-core to install (JPeer264) 30 | * 3e14683 Docs: move caveats up (JPeer264) 31 | * 7f612f1 Docs: add rcsCore usage (closes #65) (JPeer264) 32 | 33 | 4.0.0-rc.2 - May, 12 2020 34 | 35 | * 265c6c8 Style: remove eslint errors (JPeer264) 36 | 37 | 4.0.0-rc.1 - May, 11 2020 38 | 39 | * a43e6a7 Feat: add caveats (JPeer264) 40 | * d320168 Feat: add support for warning the user about bad lib usage (JPeer264) 41 | * 21af8a5 Feat: ignore files with config file (JPeer264) 42 | * 7fe282c Chore: add funding.yml (Jan Peer Stöcklmair) 43 | * 4ceb812 Feat: add handlebars to html fileExt (JPeer264) 44 | 45 | 4.0.0-rc.0 - February, 20 2020 46 | 47 | * 6fd491e Docs: add plugin notice (JPeer264) 48 | * 5aa9bb5 Refactor: make save and generateMapping promise first (JPeer264) 49 | * 321e95f Refactor: remove lodash.merge (JPeer264) 50 | * 963dbf3 Chore(deps): bump lodash from 4.17.4 to 4.17.15 (#47) (dependabot[bot]) 51 | * aa5dee0 Refactor: tests to ts (#61) (Jan Peer Stöcklmair) 52 | * 07e3461 Refactor: add default export to lib index (JPeer264) 53 | * 53a20a1 Chore: add lint-staged and husky (JPeer264) 54 | * 1425784 Chore: add declarations for built files (JPeer264) 55 | * 3341fca Test: fix tests (JPeer264) 56 | * 09114ad Refactor: outsource type from options into own parameter (JPeer264) 57 | * 4173604 Test: add missing tests with tmp dir (JPeer264) 58 | * 894a2d5 Test: speeding up tests by generating proper tmp folders (JPeer264) 59 | * 7bd2910 Fix: remove unnecessary throw (JPeer264) 60 | * 53bcb68 Refactor: make process promise based (JPeer264) 61 | * 04b7a6a Refactor: lib to typescript (#60) (Jan Peer Stöcklmair) 62 | * fa652c1 Chore: update packagelock (JPeer264) 63 | * f3861da Chore: drop Node v6 and v8 support | add v12 support (JPeer264) 64 | * f5c68fa Chore: update npmignore (JPeer264) 65 | * 7b28317 Chore: remove yarn and switch to npm (JPeer264) 66 | * 9a92c13 Chore: update eslint paths (JPeer264) 67 | * 953b48c Test: update wrong path (JPeer264) 68 | * 0992d8f Test: update test files based on new rcs-core nameGenerator library (JPeer264) 69 | * 5477b72 Refactor: remove extended option (JPeer264) 70 | * 96ba334 Test: changing way of error handling in generateMapping (JPeer264) 71 | * 477e4e3 Chore: remove ava config from package.json (JPeer264) 72 | * c3085dc Chore: update to babel 7 (JPeer264) 73 | * c439166 Chore: move to jest (#57) (Jan Peer Stöcklmair) 74 | * 0622182 Chore: fix eslint errors (JPeer264) 75 | * b5fc01e Chore: auto fix eslint (JPeer264) 76 | * a0d1e5d Chore: change to import / export (JPeer264) 77 | * 94373c2 Chore: update eslintrc (JPeer264) 78 | * 862b193 Chore: new deploy method (JPeer264) 79 | * 3da64d6 Chore: change to typescript (JPeer264) 80 | * f4a7fb0 Chore: update rcs-core to 3.0.1 (JPeer264) 81 | 82 | 3.2.8 - February, 13 2020 83 | 84 | * 36cb329 Chore: update rcs-core (JPeer264) 85 | 86 | 3.2.5 - August, 01 2019 87 | 88 | * 5f6bf01 Test: add testcase for #43 (JPeer264) 89 | * 50388ff Chore: update rcs-core to 2.6.2 (JPeer264) 90 | 91 | 3.2.4 - July, 23 2019 92 | 93 | * 912f491 Chore: update rcs-core (JPeer264) 94 | 95 | 3.2.3 - June, 22 2019 96 | 97 | * 13610be Fix: process fill html for sync variant (JPeer264) 98 | 99 | 3.2.2 - June, 22 2019 100 | 101 | * 10b644a Fix: fill library from html files (#41) (Jan Peer Stöcklmair) 102 | * 3a28005 Fix: forgot to pass ignoreCssVariables options (closes #39) (#40) (Jan Peer Stöcklmair) 103 | 104 | 3.2.1 - May, 10 2019 105 | 106 | * ce44c98 Chore: seems like something went wrong with the rcs-core release (JPeer264) 107 | 108 | 3.2.0 - May, 10 2019 109 | 110 | * 0b52450 Feat: css variables support / update rcs-core (closes #37) (JPeer264) 111 | 112 | 3.1.4 - February, 06 2019 113 | 114 | * 4c02258 Chore: update rcs-core (JPeer264) 115 | 116 | 3.1.3 - December, 23 2018 117 | 118 | * 7a16dbb Chore: update rcs-core to 2.4.3 (JPeer264) 119 | 120 | 3.1.2 - December, 16 2018 121 | 122 | * fbb35f2 Chore: update rcs-core (JPeer264) 123 | * abaf6d4 Docs: show how to include rcsrc (#33) (Jan Peer Stöcklmair) 124 | 125 | 3.1.1 - September, 01 2018 126 | 127 | * c05c023 Chore: update rcs-core to 2.4.1 (JPeer264) 128 | 129 | 3.1.0 - August, 28 2018 130 | 131 | * 368693c Feat: rcs.process.pug (closes #22) (#29) (Jan Peer Stöcklmair) 132 | 133 | 3.0.0 - August, 26 2018 134 | 135 | * 10c303b Docs: improve readme (JPeer264) 136 | * 6c5eb44 Test: add tests for html (closes #21) (#28) (Jan Peer Stöcklmair) 137 | * ec8f026 Fix: rename if path is Array and includes just one element (closes #19) (#27) (Jan Peer Stöcklmair) 138 | * d05ba8b Feat: refactor process (#26) (Jan Peer Stöcklmair) 139 | * e0e728e Style: other way of exporting (JPeer264) 140 | * 8e7001f Docs: update to new docs structure (closes #17) (#24) (Jan Peer Stöcklmair) 141 | * 6625141 Feat: add promise functionality (#25) (Jan Peer Stöcklmair) 142 | * f241fd0 Style: consistent code (JPeer264) 143 | * ed9eb26 Test: mocha to ava (#23) (Jan Peer Stöcklmair) 144 | 145 | 2.0.0 - June, 18 2018 146 | 147 | * b9a751d Feat: update rcs-core and add parserOptions in processJs (closes #16) (JPeer264) 148 | * 448747a Style: remove eslint warnings (JPeer264) 149 | * 465aab5 Chore: remove Node 4 support, add v8 and v10 (JPeer264) 150 | * 569dae2 Style: eslint fix (JPeer264) 151 | * f1e5e56 Feat: update to rcs-core@2.0.1 (JPeer264) 152 | 153 | 1.3.5 - April, 26 2018 154 | 155 | * 691303b Chore: update to rcs-core@1.0.5 (JPeer264) 156 | 157 | 1.3.4 - February, 11 2018 158 | 159 | * adb9f09 Chore: update rcs-core to 1.0.4 (closes #14) (#15) (Jan Peer Stöcklmair) 160 | 161 | 1.3.3 - February, 09 2018 162 | 163 | * 6c9b922 Chore: update rcs-core to 1.0.3 (#13) (Jan Peer Stöcklmair) 164 | * 857d458 1.3.2 (JPeer264) 165 | * e5cbea5 Chore: update to newest rcs-core (JPeer264) 166 | * 91f64a2 Style: fix eslint errors (JPeer264) 167 | * e9805bc Chore: add eslint to workflow (JPeer264) 168 | 169 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jan Peer Stöcklmair 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 | # Rename CSS Selectors (RCS) 2 | 3 | [![Build Status](https://travis-ci.org/JPeer264/node-rename-css-selectors.svg?branch=master)](https://travis-ci.org/JPeer264/node-rename-css-selectors) 4 | [![Coverage Status](https://coveralls.io/repos/github/JPeer264/node-rename-css-selectors/badge.svg?branch=master)](https://coveralls.io/github/JPeer264/node-rename-css-selectors?branch=master) 5 | 6 | > **Note:** Please make sure your files are not minified/uglified. Do that after processing it with `rename-css-selectors` 7 | 8 | This module renames all CSS selectors in the given files. It will collect all selectors from the given CSS files. Do not worry about your selectors, `rcs` will do it for you. 9 | 10 | You can also use a config file with the combination of [generateMapping](#generateMapping) and [loadMapping](#loadMapping), if you already had other projects with the same classes. So all your projects have the same minified selector names - always. 11 | 12 | This is a plugin of [rcs-core](https://github.com/JPeer264/node-rcs-core) 13 | 14 | ## Contents 15 | 16 | - [Installation](#installation) 17 | - [Usage](#usage) 18 | - [Methods](#methods) 19 | - [Caveats](#caveats) 20 | - [LICENSE](#license) 21 | 22 | ## Installation 23 | 24 | Install with [npm](https://docs.npmjs.com/cli/install) or [yarn](https://yarnpkg.com/en/docs/install) 25 | 26 | ```sh 27 | npm i rename-css-selectors rcs-core 28 | ``` 29 | or 30 | ```sh 31 | yarn add rename-css-selectors rcs-core 32 | ``` 33 | 34 | ## Usage 35 | 36 | Async: 37 | 38 | > There are 3 different ways of writing async `rcs` code: callbacks, promises and async/await 39 | 40 | ```js 41 | // you can use every method of `rcs-core` on top 42 | const rcsCore = require('rcs-core'); 43 | const rcs = require('rename-css-selectors') 44 | 45 | // if you want to include the .rcsrc config 46 | rcs.config.load(); 47 | 48 | // if you have some generated mappings - load them! 49 | // you can also specify the string although it does not exist yet. 50 | rcs.mapping.load('./renaming_map.json'); 51 | 52 | // now with rcsCore you could e.g. ignore single variables (optional) 53 | rcsCore.baseLibrary.setExclude(/<%=[\s\S]+%>/); 54 | 55 | // callback 56 | rcs.process.auto(['**/*.js', '**/*.html', '**/*.css'], options, (err) => { 57 | // all css files are now saved, renamed and stored in the selectorLibrary 58 | // also other files are not renamed 59 | // that's it 60 | 61 | // maybe you want to add the new selectors to your previous generated mappings 62 | // do not worry, your old settings are still here, in case you used `rcs.mapping.load` 63 | rcs.mapping.generate('./', { overwrite: true }, (err) => { 64 | // the mapping file is now saved 65 | }); 66 | }); 67 | 68 | // promise 69 | rcs.process.auto(['**/*.js', '**/*.html', '**/*.css'], options) 70 | .then(() => rcs.mapping.generate('./', { overwrite: true })) 71 | .catch(console.error); 72 | 73 | // async/await 74 | (async () => { 75 | try { 76 | await rcs.process.auto(['**/*.js', '**/*.html', '**/*.css'], options); 77 | await rcs.mapping.generate('./', { overwrite: true }); 78 | } catch (err) { 79 | console.error(err); 80 | } 81 | })(); 82 | ``` 83 | 84 | Sync: 85 | 86 | ```js 87 | const rcs = require('rename-css-selectors'); 88 | 89 | rcs.mapping.load('./renaming_map.json'); 90 | 91 | try { 92 | rcs.process.autoSync(['**/*.js', '**/*.html', '**/*.css'], options); 93 | rcs.mapping.generateSync('./', { overwrite: true }); 94 | } catch (err) { 95 | console.error(err); 96 | } 97 | ``` 98 | 99 | ## Methods 100 | 101 | - [rcs.process.auto](docs/api/processAuto.md) 102 | - [rcs.process.css](docs/api/processCss.md) 103 | - [rcs.process.js](docs/api/processJs.md) 104 | - [rcs.process.html](docs/api/processHtml.md) 105 | - [rcs.process.pug](docs/api/processPug.md) 106 | - [rcs.process.any](docs/api/processAny.md) 107 | - [rcs.mapping](docs/api/mapping.md) 108 | - [rcs.config](docs/api/config.md) 109 | 110 | ## Caveats 111 | 112 | Correctly using `rename-css-selectors` on large project means few rules should be followed. 113 | [This document](https://github.com/JPeer264/node-rcs-core/blob/main/docs/caveats.md) explains most of them. 114 | 115 | # LICENSE 116 | 117 | MIT © [Jan Peer Stöcklmair](https://www.jpeer.at) 118 | -------------------------------------------------------------------------------- /__tests__/config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs-extra'; 3 | import rcsCore from 'rcs-core'; 4 | import rcs from '../lib'; 5 | 6 | const testFiles = path.join(process.cwd(), '/__tests__/files'); 7 | 8 | beforeEach(() => { 9 | rcsCore.selectorsLibrary.getClassSelector().nameGenerator.setAlphabet('#abcdefghijklmnopqrstuvwxyz'); 10 | rcsCore.selectorsLibrary.getIdSelector().nameGenerator.setAlphabet('#abcdefghijklmnopqrstuvwxyz'); 11 | rcsCore.selectorsLibrary.reset(); 12 | rcsCore.keyframesLibrary.reset(); 13 | }); 14 | 15 | it('should set the config with package.json', () => { 16 | // include config 17 | rcs.config.load(); 18 | 19 | // include new settings 20 | rcsCore.selectorsLibrary.set(['.js', '.any-value']); 21 | 22 | expect(rcsCore.selectorsLibrary.get('js')).toBe('js'); 23 | expect(rcsCore.selectorsLibrary.get('any-value')).toBe('a'); 24 | }); 25 | 26 | it('should set the config with .rcsrc', () => { 27 | const file = '.rcsrc'; 28 | 29 | fs.writeFileSync(file, `{ 30 | "exclude": [ 31 | "flexbox", 32 | "no-js" 33 | ], 34 | "reserve": [ 35 | "ad" 36 | ] 37 | }`, { 38 | encoding: 'utf8', 39 | }); 40 | 41 | // include config 42 | rcs.config.load(); 43 | 44 | // include new settings 45 | rcsCore.selectorsLibrary.set(['.flexbox', '.any-value']); 46 | 47 | expect(rcsCore.selectorsLibrary.get('flexbox')).toBe('flexbox'); 48 | expect(rcsCore.selectorsLibrary.get('any-value')).toBe('a'); 49 | 50 | fs.removeSync(file); 51 | }); 52 | 53 | it('should set the config with package.json', () => { 54 | // include config 55 | rcs.config.load(path.join(testFiles, '/config.json')); 56 | 57 | // include new settings 58 | rcsCore.selectorsLibrary.set(['.own-file', '.any-value']); 59 | 60 | expect(rcsCore.selectorsLibrary.get('own-file')).toBe('own-file'); 61 | expect(rcsCore.selectorsLibrary.get('any-value')).toBe('a'); 62 | }); 63 | 64 | it('should load ignored patterns', () => { 65 | const file = '.rcsrc'; 66 | 67 | fs.writeFileSync(file, `{ 68 | "ignore": [ 69 | "a.js", 70 | "**.min.js" 71 | ] 72 | }`, { 73 | encoding: 'utf8', 74 | }); 75 | 76 | // include config 77 | rcs.config.load(); 78 | 79 | expect(rcs.config.isIgnored('a.js')).toBe(true); 80 | expect(rcs.config.isIgnored('b.min.js')).toBe(true); 81 | expect(rcs.config.isIgnored('b.js')).toBe(false); 82 | 83 | fs.removeSync(file); 84 | }); 85 | -------------------------------------------------------------------------------- /__tests__/files/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": [ 3 | "own-file", 4 | "no-js" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/css/css-attributes.css: -------------------------------------------------------------------------------- 1 | .selector[class*="block"] { 2 | } 3 | 4 | .block[class*="block"] { 5 | } 6 | 7 | .class[id^="pre-"] { 8 | } 9 | 10 | .another-class[class$="ctor"] { 11 | } 12 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/css/css-variables.css: -------------------------------------------------------------------------------- 1 | .theme { 2 | --theme-primary: #111; 3 | --theme-secondary: #f0f0f0; 4 | --theme-tertiary: #999; 5 | } 6 | 7 | .theme--variant { 8 | --theme-primary: red; 9 | --theme-secondary: white; 10 | --theme-tertiary: black; 11 | } 12 | 13 | .box { 14 | color: var(--theme-primary); 15 | background-color: var(--theme-secondary); 16 | font-size: 1.2em; 17 | line-height: 1.4; 18 | width: 100%; 19 | max-width: 400px; 20 | padding: 5px; 21 | border: 1px solid var(--theme-tertiary); 22 | margin: 0 0 20px; 23 | } 24 | 25 | .jp-block { 26 | content: 'block'; 27 | } 28 | 29 | .jp-block__element { 30 | content: 'block__element'; 31 | } 32 | 33 | .jp-block__element--modifier { 34 | content: 'block__element--modifier'; 35 | } 36 | 37 | .jp-block--modifier{ 38 | content: 'block--modifier'; 39 | } 40 | 41 | .jp-pseudo:before { 42 | content: 'before'; 43 | } 44 | 45 | .jp-block__element--modifier { 46 | content: 'block__element--modifier'; 47 | } 48 | 49 | #id { 50 | content: 'id'; 51 | } 52 | 53 | .jp-attribute[src] { 54 | content: 'attribute'; 55 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 56 | } 57 | 58 | @media screen and (min-width: .40em) { 59 | #id { 60 | height: .20px; 61 | width: .20em; 62 | } 63 | } 64 | 65 | /** 66 | * a description 67 | */ 68 | @media screen and (min-width: 40em) 69 | 70 | { 71 | #id 72 | { 73 | height: .20px; 74 | width: .20em; 75 | } 76 | } 77 | 78 | h1 { 79 | color: red; 80 | } 81 | 82 | .reveal { 83 | content: 'reveal'; 84 | color: #eee; 85 | font-size: .5rem; 86 | } 87 | 88 | .js.reveal { 89 | content: 'js reveal'; 90 | margin-right: calc(var(--theme-primary, var(--0px)) - var(--theme-secondary, var(--0px))); 91 | margin-bottom: calc(var(--theme-tertiary, var(--0px)) - var(--theme-primary, var(--0px))); 92 | } 93 | 94 | .no-js.reveal { 95 | content: 'js reveal'; 96 | } 97 | 98 | .js-reveal { 99 | content: 'js-reveal'; 100 | } 101 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/css/style.css: -------------------------------------------------------------------------------- 1 | .jp-block { 2 | content: 'block'; 3 | } 4 | 5 | .jp-block__element { 6 | content: 'block__element'; 7 | } 8 | 9 | .jp-block__element--modifier { 10 | content: 'block__element--modifier'; 11 | } 12 | 13 | .jp-block--modifier{ 14 | content: 'block--modifier'; 15 | } 16 | 17 | .jp-pseudo:before { 18 | content: 'before'; 19 | } 20 | 21 | .jp-block__element--modifier { 22 | content: 'block__element--modifier'; 23 | } 24 | 25 | #id { 26 | content: 'id'; 27 | } 28 | 29 | .jp-attribute[src] { 30 | content: 'attribute'; 31 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 32 | } 33 | 34 | @media screen and (min-width: .40em) { 35 | #id { 36 | height: .20px; 37 | width: .20em; 38 | } 39 | } 40 | 41 | /** 42 | * a description 43 | */ 44 | @media screen and (min-width: 40em) 45 | 46 | { 47 | #id 48 | { 49 | height: .20px; 50 | width: .20em; 51 | } 52 | } 53 | 54 | h1 { 55 | color: red; 56 | } 57 | 58 | .reveal { 59 | content: 'reveal'; 60 | color: #eee; 61 | font-size: .5rem; 62 | } 63 | 64 | .js.reveal { 65 | content: 'js reveal'; 66 | } 67 | 68 | .no-js.reveal { 69 | content: 'js reveal'; 70 | } 71 | 72 | .js-reveal { 73 | content: 'js-reveal'; 74 | } 75 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/css/style2.css: -------------------------------------------------------------------------------- 1 | #id { 2 | content: 'id'; 3 | } 4 | 5 | .jp-block__element { 6 | content: 'block__element'; 7 | } 8 | 9 | .jp-block__element--modifier { 10 | content: 'block__element--modifier'; 11 | } 12 | 13 | .jp-block__element--modifier { 14 | content: 'block__element--modifier'; 15 | } 16 | 17 | .jp-block--modifier{ 18 | content: 'block--modifier'; 19 | } 20 | 21 | .jp-block { 22 | content: 'block'; 23 | } 24 | 25 | .jp-pseudo:before { 26 | content: 'before'; 27 | } 28 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/css/subdirectory/style.css: -------------------------------------------------------------------------------- 1 | .jp-block { 2 | content: 'block'; 3 | } 4 | 5 | .jp-block__element--modifier { 6 | content: 'block__element--modifier'; 7 | } 8 | 9 | .jp-block__element { 10 | content: 'block__element'; 11 | } 12 | 13 | .jp-block--modifier{ 14 | content: 'block--modifier'; 15 | } 16 | 17 | .jp-block__element--modifier { 18 | content: 'block__element--modifier'; 19 | } 20 | 21 | .jp-pseudo:before { 22 | content: 'before'; 23 | } 24 | 25 | #id { 26 | content: 'id'; 27 | } 28 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/html/index-with-style.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test Document 6 | 7 | 8 |
9 |
10 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test Document 6 | 7 | 8 |
9 |
10 | 11 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/js/complex.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Finds the index of the first character 4 | * that's not common between the two given strings. 5 | * 6 | * @return {number} the index of the character where the strings diverge 7 | */ 8 | function firstDifferenceIndex(string1, string2) { 9 | var minLen = Math.min(string1.length, string2.length); 10 | for (var i = 0; i < minLen; i++) { 11 | if (string1.charAt(i) !== string2.charAt(i)) { 12 | return i; 13 | } 14 | } 15 | return string1.length === string2.length ? -1 : minLen; 16 | } 17 | 18 | /***/ (function(module, exports) { 19 | 20 | // removed by extract-text-webpack-plugin 21 | module.exports = {"table":"jp-block","key":"jp-pseudo"}; 22 | 23 | /***/ }), 24 | /* 523 */ 25 | /***/ (function(module, exports, __webpack_require__) { 26 | 27 | var defineProperty = __webpack_require__(531); 28 | 29 | /** 30 | * The base implementation of `assignValue` and `assignMergeValue` without 31 | * value checks. 32 | * 33 | * @private 34 | * @param {Object} object The object to modify. 35 | * @param {string} key The key of the property to assign. 36 | * @param {*} value The value to assign. 37 | */ 38 | function baseAssignValue(object, key, value) { 39 | if (key == '__proto__' && defineProperty) { 40 | defineProperty(object, key, { 41 | 'configurable': true, 42 | 'enumerable': true, 43 | 'value': value, 44 | 'writable': true 45 | }); 46 | } else { 47 | object[key] = value; 48 | } 49 | } 50 | 51 | module.exports = baseAssignValue; 52 | 53 | 54 | /***/ }) 55 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/js/main.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // jQuery example 3 | $(".jp-block"); 4 | 5 | // vanillaJS example 6 | document.getElementsByClassName("jp-block__element"); 7 | document.getElementById("jp-block__element--modifier"); 8 | 9 | const restSpread = { 10 | weirdFormatting: true, 11 | shouldRest: true, 12 | }; 13 | 14 | const extended = { ...restSpread }; 15 | const { ...options } = extended; 16 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/js/react.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import PropTypes from 'prop-types'; 3 | import React, { Component } from 'react'; 4 | import { bindActionCreators } from 'redux'; 5 | import { connect } from 'react-redux'; 6 | 7 | import ListView from '../components/ListView'; 8 | import eventActions from '../redux/eventsRedux'; 9 | import { getAllEvents } from '../selectors'; 10 | 11 | class Events extends Component { 12 | componentWillMount() { 13 | this.props.startup(); 14 | } 15 | 16 | componentWillReceiveProps(nextProps) { 17 | this.props.events = nextProps.events; 18 | } 19 | 20 | renderEvent() { 21 | if (this.props.events.length === 0) { 22 | return

is

; 23 | } 24 | 25 | return ; 26 | } 27 | 28 | render() { 29 | return ( 30 |
31 | { this.renderEvent() } 32 |
33 | ); 34 | } 35 | } 36 | 37 | Events.propTypes = { 38 | events: PropTypes.arrayOf(PropTypes.shape({ 39 | name: PropTypes.string.isRequired, 40 | })), 41 | startup: PropTypes.func.isRequired, 42 | }; 43 | 44 | Events.defaultProps = { 45 | events: [ 46 | { 47 | name: 'No Event', 48 | }, 49 | ], 50 | }; 51 | 52 | const mapStateToProps = state => ({ 53 | events: getAllEvents(state), 54 | }); 55 | 56 | const mapDispatchToProps = dispatch => bindActionCreators({ 57 | startup: eventActions.eventsRequest, 58 | }, dispatch); 59 | 60 | export default connect(mapStateToProps, mapDispatchToProps)(Events); 61 | -------------------------------------------------------------------------------- /__tests__/files/fixtures/pug/index.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | head 3 | meta(charset='UTF-8') 4 | title Test Document 5 | .jp-block 6 | .jp-block__element 7 | -------------------------------------------------------------------------------- /__tests__/files/issue21/fixtures/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ' 7 | 8 | 9 | 10 | 11 |

'

12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /__tests__/files/issue21/fixtures/style.css: -------------------------------------------------------------------------------- 1 | .wf-active { 2 | color: red; 3 | } 4 | 5 | .test { 6 | font-weight: bold; 7 | } 8 | -------------------------------------------------------------------------------- /__tests__/files/issue21/results/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ' 7 | 8 | 9 | 10 | 11 |

'

12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /__tests__/files/issue21/results/style.css: -------------------------------------------------------------------------------- 1 | .a { 2 | color: red; 3 | } 4 | 5 | .b { 6 | font-weight: bold; 7 | } 8 | -------------------------------------------------------------------------------- /__tests__/files/issue70/fixtures/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Animals 6 | 7 | 8 | 9 |
10 |
11 |

Hello this is dog

12 |
14 |
15 |
16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /__tests__/files/issue70/fixtures/style.css: -------------------------------------------------------------------------------- 1 | .container {} 2 | .text-center {} 3 | .d-flex {} 4 | .flex-column {} 5 | .justify-content-center {} 6 | .align-items-center {} 7 | .align-content-center {} 8 | .ct-fluid {} 9 | -------------------------------------------------------------------------------- /__tests__/files/issue70/results/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Animals 6 | 7 | 8 | 9 |
10 |
11 |

Hello this is dog

12 |
14 |
15 |
16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /__tests__/files/issue70/results/style.css: -------------------------------------------------------------------------------- 1 | .a {} 2 | .b {} 3 | .c {} 4 | .d {} 5 | .e {} 6 | .f {} 7 | .g {} 8 | .h {} 9 | -------------------------------------------------------------------------------- /__tests__/files/results/css/css-attributes-ignore.css: -------------------------------------------------------------------------------- 1 | .prefix-a-suffix[class*="block"] { 2 | } 3 | 4 | .prefix-b-suffix[class*="block"] { 5 | } 6 | 7 | .prefix-c-suffix[id^="prefix-pre-"] { 8 | } 9 | 10 | .prefix-d-suffix[class$="ctor-suffix"] { 11 | } 12 | -------------------------------------------------------------------------------- /__tests__/files/results/css/css-attributes-pre-suffix.css: -------------------------------------------------------------------------------- 1 | .prefix-tctor-suffix[class*="block"] { 2 | } 3 | 4 | .prefix-tblockn-suffix[class*="block"] { 5 | } 6 | 7 | .prefix-a-suffix[id^="prefix-pre-"] { 8 | } 9 | 10 | .prefix-b-suffix[class$="ctor-suffix"] { 11 | } 12 | -------------------------------------------------------------------------------- /__tests__/files/results/css/css-attributes.css: -------------------------------------------------------------------------------- 1 | .tctor[class*="block"] { 2 | } 3 | 4 | .tblockn[class*="block"] { 5 | } 6 | 7 | .a[id^="pre-"] { 8 | } 9 | 10 | .b[class$="ctor"] { 11 | } 12 | -------------------------------------------------------------------------------- /__tests__/files/results/css/css-variables-ignore.css: -------------------------------------------------------------------------------- 1 | .a { 2 | --theme-primary: #111; 3 | --theme-secondary: #f0f0f0; 4 | --theme-tertiary: #999; 5 | } 6 | 7 | .b { 8 | --theme-primary: red; 9 | --theme-secondary: white; 10 | --theme-tertiary: black; 11 | } 12 | 13 | .c { 14 | color: var(--theme-primary); 15 | background-color: var(--theme-secondary); 16 | font-size: 1.2em; 17 | line-height: 1.4; 18 | width: 100%; 19 | max-width: 400px; 20 | padding: 5px; 21 | border: 1px solid var(--theme-tertiary); 22 | margin: 0 0 20px; 23 | } 24 | 25 | .d { 26 | content: 'block'; 27 | } 28 | 29 | .e { 30 | content: 'block__element'; 31 | } 32 | 33 | .f { 34 | content: 'block__element--modifier'; 35 | } 36 | 37 | .g{ 38 | content: 'block--modifier'; 39 | } 40 | 41 | .h:before { 42 | content: 'before'; 43 | } 44 | 45 | .f { 46 | content: 'block__element--modifier'; 47 | } 48 | 49 | #a { 50 | content: 'id'; 51 | } 52 | 53 | .i[src] { 54 | content: 'attribute'; 55 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 56 | } 57 | 58 | @media screen and (min-width: .40em) { 59 | #a { 60 | height: .20px; 61 | width: .20em; 62 | } 63 | } 64 | 65 | /** 66 | * a description 67 | */ 68 | @media screen and (min-width: 40em) 69 | 70 | { 71 | #a 72 | { 73 | height: .20px; 74 | width: .20em; 75 | } 76 | } 77 | 78 | h1 { 79 | color: red; 80 | } 81 | 82 | .j { 83 | content: 'reveal'; 84 | color: #eee; 85 | font-size: .5rem; 86 | } 87 | 88 | .k.j { 89 | content: 'js reveal'; 90 | margin-right: calc(var(--theme-primary, var(--0px)) - var(--theme-secondary, var(--0px))); 91 | margin-bottom: calc(var(--theme-tertiary, var(--0px)) - var(--theme-primary, var(--0px))); 92 | } 93 | 94 | .l.j { 95 | content: 'js reveal'; 96 | } 97 | 98 | .m { 99 | content: 'js-reveal'; 100 | } 101 | -------------------------------------------------------------------------------- /__tests__/files/results/css/css-variables.css: -------------------------------------------------------------------------------- 1 | .a { 2 | --a: #111; 3 | --b: #f0f0f0; 4 | --c: #999; 5 | } 6 | 7 | .b { 8 | --a: red; 9 | --b: white; 10 | --c: black; 11 | } 12 | 13 | .c { 14 | color: var(--a); 15 | background-color: var(--b); 16 | font-size: 1.2em; 17 | line-height: 1.4; 18 | width: 100%; 19 | max-width: 400px; 20 | padding: 5px; 21 | border: 1px solid var(--c); 22 | margin: 0 0 20px; 23 | } 24 | 25 | .d { 26 | content: 'block'; 27 | } 28 | 29 | .e { 30 | content: 'block__element'; 31 | } 32 | 33 | .f { 34 | content: 'block__element--modifier'; 35 | } 36 | 37 | .g{ 38 | content: 'block--modifier'; 39 | } 40 | 41 | .h:before { 42 | content: 'before'; 43 | } 44 | 45 | .f { 46 | content: 'block__element--modifier'; 47 | } 48 | 49 | #a { 50 | content: 'id'; 51 | } 52 | 53 | .i[src] { 54 | content: 'attribute'; 55 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 56 | } 57 | 58 | @media screen and (min-width: .40em) { 59 | #a { 60 | height: .20px; 61 | width: .20em; 62 | } 63 | } 64 | 65 | /** 66 | * a description 67 | */ 68 | @media screen and (min-width: 40em) 69 | 70 | { 71 | #a 72 | { 73 | height: .20px; 74 | width: .20em; 75 | } 76 | } 77 | 78 | h1 { 79 | color: red; 80 | } 81 | 82 | .j { 83 | content: 'reveal'; 84 | color: #eee; 85 | font-size: .5rem; 86 | } 87 | 88 | .k.j { 89 | content: 'js reveal'; 90 | margin-right: calc(var(--a, var(--0px)) - var(--b, var(--0px))); 91 | margin-bottom: calc(var(--c, var(--0px)) - var(--a, var(--0px))); 92 | } 93 | 94 | .l.j { 95 | content: 'js reveal'; 96 | } 97 | 98 | .m { 99 | content: 'js-reveal'; 100 | } 101 | -------------------------------------------------------------------------------- /__tests__/files/results/css/style-prefix.css: -------------------------------------------------------------------------------- 1 | .prefixed-a { 2 | content: 'block'; 3 | } 4 | 5 | .prefixed-b { 6 | content: 'block__element'; 7 | } 8 | 9 | .prefixed-c { 10 | content: 'block__element--modifier'; 11 | } 12 | 13 | .prefixed-d{ 14 | content: 'block--modifier'; 15 | } 16 | 17 | .prefixed-e:before { 18 | content: 'before'; 19 | } 20 | 21 | .prefixed-c { 22 | content: 'block__element--modifier'; 23 | } 24 | 25 | #prefixed-a { 26 | content: 'id'; 27 | } 28 | 29 | .prefixed-f[src] { 30 | content: 'attribute'; 31 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 32 | } 33 | 34 | @media screen and (min-width: .40em) { 35 | #prefixed-a { 36 | height: .20px; 37 | width: .20em; 38 | } 39 | } 40 | 41 | /** 42 | * a description 43 | */ 44 | @media screen and (min-width: 40em) 45 | 46 | { 47 | #prefixed-a 48 | { 49 | height: .20px; 50 | width: .20em; 51 | } 52 | } 53 | 54 | h1 { 55 | color: red; 56 | } 57 | 58 | .prefixed-g { 59 | content: 'reveal'; 60 | color: #eee; 61 | font-size: .5rem; 62 | } 63 | 64 | .prefixed-h.prefixed-g { 65 | content: 'js reveal'; 66 | } 67 | 68 | .prefixed-i.prefixed-g { 69 | content: 'js reveal'; 70 | } 71 | 72 | .prefixed-j { 73 | content: 'js-reveal'; 74 | } 75 | -------------------------------------------------------------------------------- /__tests__/files/results/css/style-with-config.css: -------------------------------------------------------------------------------- 1 | .a { 2 | content: 'block'; 3 | } 4 | 5 | .b { 6 | content: 'block__element'; 7 | } 8 | 9 | .c { 10 | content: 'block__element--modifier'; 11 | } 12 | 13 | .d{ 14 | content: 'block--modifier'; 15 | } 16 | 17 | .e:before { 18 | content: 'before'; 19 | } 20 | 21 | .c { 22 | content: 'block__element--modifier'; 23 | } 24 | 25 | #a { 26 | content: 'id'; 27 | } 28 | 29 | .f[src] { 30 | content: 'attribute'; 31 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 32 | } 33 | 34 | @media screen and (min-width: .40em) { 35 | #a { 36 | height: .20px; 37 | width: .20em; 38 | } 39 | } 40 | 41 | /** 42 | * a description 43 | */ 44 | @media screen and (min-width: 40em) 45 | 46 | { 47 | #a 48 | { 49 | height: .20px; 50 | width: .20em; 51 | } 52 | } 53 | 54 | h1 { 55 | color: red; 56 | } 57 | 58 | .g { 59 | content: 'reveal'; 60 | color: #eee; 61 | font-size: .5rem; 62 | } 63 | 64 | .js.g { 65 | content: 'js.reveal'; 66 | } 67 | 68 | .no-js.g { 69 | content: 'js.reveal'; 70 | } 71 | 72 | .h { 73 | content: 'js-reveal'; 74 | } 75 | -------------------------------------------------------------------------------- /__tests__/files/results/css/style.css: -------------------------------------------------------------------------------- 1 | .a { 2 | content: 'block'; 3 | } 4 | 5 | .b { 6 | content: 'block__element'; 7 | } 8 | 9 | .c { 10 | content: 'block__element--modifier'; 11 | } 12 | 13 | .d{ 14 | content: 'block--modifier'; 15 | } 16 | 17 | .e:before { 18 | content: 'before'; 19 | } 20 | 21 | .c { 22 | content: 'block__element--modifier'; 23 | } 24 | 25 | #a { 26 | content: 'id'; 27 | } 28 | 29 | .f[src] { 30 | content: 'attribute'; 31 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 32 | } 33 | 34 | @media screen and (min-width: .40em) { 35 | #a { 36 | height: .20px; 37 | width: .20em; 38 | } 39 | } 40 | 41 | /** 42 | * a description 43 | */ 44 | @media screen and (min-width: 40em) 45 | 46 | { 47 | #a 48 | { 49 | height: .20px; 50 | width: .20em; 51 | } 52 | } 53 | 54 | h1 { 55 | color: red; 56 | } 57 | 58 | .g { 59 | content: 'reveal'; 60 | color: #eee; 61 | font-size: .5rem; 62 | } 63 | 64 | .h.g { 65 | content: 'js reveal'; 66 | } 67 | 68 | .i.g { 69 | content: 'js reveal'; 70 | } 71 | 72 | .j { 73 | content: 'js-reveal'; 74 | } 75 | -------------------------------------------------------------------------------- /__tests__/files/results/css/style2.css: -------------------------------------------------------------------------------- 1 | #a { 2 | content: 'id'; 3 | } 4 | 5 | .b { 6 | content: 'block__element'; 7 | } 8 | 9 | .c { 10 | content: 'block__element--modifier'; 11 | } 12 | 13 | .c { 14 | content: 'block__element--modifier'; 15 | } 16 | 17 | .d{ 18 | content: 'block--modifier'; 19 | } 20 | 21 | .a { 22 | content: 'block'; 23 | } 24 | 25 | .e:before { 26 | content: 'before'; 27 | } 28 | -------------------------------------------------------------------------------- /__tests__/files/results/css/subdirectory/style.css: -------------------------------------------------------------------------------- 1 | .a { 2 | content: 'block'; 3 | } 4 | 5 | .c { 6 | content: 'block__element--modifier'; 7 | } 8 | 9 | .b { 10 | content: 'block__element'; 11 | } 12 | 13 | .d{ 14 | content: 'block--modifier'; 15 | } 16 | 17 | .c { 18 | content: 'block__element--modifier'; 19 | } 20 | 21 | .e:before { 22 | content: 'before'; 23 | } 24 | 25 | #a { 26 | content: 'id'; 27 | } 28 | -------------------------------------------------------------------------------- /__tests__/files/results/html/index-with-style.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test Document 6 | 7 | 8 |
9 |
10 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /__tests__/files/results/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test Document 6 | 7 | 8 |
9 |
10 | 11 | -------------------------------------------------------------------------------- /__tests__/files/results/js/complex.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Finds the index of the first character 4 | * that's not common between the two given strings. 5 | * 6 | * @return {number} the index of the character where the strings diverge 7 | */ 8 | function firstDifferenceIndex(string1, string2) { 9 | var minLen = Math.min(string1.length, string2.length); 10 | for (var i = 0; i < minLen; i++) { 11 | if (string1.charAt(i) !== string2.charAt(i)) { 12 | return i; 13 | } 14 | } 15 | return string1.length === string2.length ? -1 : minLen; 16 | } 17 | 18 | /***/ (function(module, exports) { 19 | 20 | // removed by extract-text-webpack-plugin 21 | module.exports = {"table":"a","key":"e"}; 22 | 23 | /***/ }), 24 | /* 523 */ 25 | /***/ (function(module, exports, __webpack_require__) { 26 | 27 | var defineProperty = __webpack_require__(531); 28 | 29 | /** 30 | * The base implementation of `assignValue` and `assignMergeValue` without 31 | * value checks. 32 | * 33 | * @private 34 | * @param {Object} object The object to modify. 35 | * @param {string} key The key of the property to assign. 36 | * @param {*} value The value to assign. 37 | */ 38 | function baseAssignValue(object, key, value) { 39 | if (key == '__proto__' && defineProperty) { 40 | defineProperty(object, key, { 41 | 'configurable': true, 42 | 'enumerable': true, 43 | 'value': value, 44 | 'writable': true 45 | }); 46 | } else { 47 | object[key] = value; 48 | } 49 | } 50 | 51 | module.exports = baseAssignValue; 52 | 53 | 54 | /***/ }) 55 | -------------------------------------------------------------------------------- /__tests__/files/results/js/main.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // jQuery example 3 | $(".a"); 4 | 5 | // vanillaJS example 6 | document.getElementsByClassName("b"); 7 | document.getElementById("c"); 8 | 9 | const restSpread = { 10 | weirdFormatting: true, 11 | shouldRest: true, 12 | }; 13 | 14 | const extended = { ...restSpread }; 15 | const { ...options } = extended; 16 | -------------------------------------------------------------------------------- /__tests__/files/results/js/react.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import PropTypes from 'prop-types'; 3 | import React, { Component } from 'react'; 4 | import { bindActionCreators } from 'redux'; 5 | import { connect } from 'react-redux'; 6 | 7 | import ListView from '../components/ListView'; 8 | import eventActions from '../redux/eventsRedux'; 9 | import { getAllEvents } from '../selectors'; 10 | 11 | class Events extends Component { 12 | componentWillMount() { 13 | this.props.startup(); 14 | } 15 | 16 | componentWillReceiveProps(nextProps) { 17 | this.props.events = nextProps.events; 18 | } 19 | 20 | renderEvent() { 21 | if (this.props.events.length === 0) { 22 | return

is

; 23 | } 24 | 25 | return ; 26 | } 27 | 28 | render() { 29 | return ( 30 |
31 | { this.renderEvent() } 32 |
33 | ); 34 | } 35 | } 36 | 37 | Events.propTypes = { 38 | events: PropTypes.arrayOf(PropTypes.shape({ 39 | name: PropTypes.string.isRequired, 40 | })), 41 | startup: PropTypes.func.isRequired, 42 | }; 43 | 44 | Events.defaultProps = { 45 | events: [ 46 | { 47 | name: 'No Event', 48 | }, 49 | ], 50 | }; 51 | 52 | const mapStateToProps = state => ({ 53 | events: getAllEvents(state), 54 | }); 55 | 56 | const mapDispatchToProps = dispatch => bindActionCreators({ 57 | startup: eventActions.eventsRequest, 58 | }, dispatch); 59 | 60 | export default connect(mapStateToProps, mapDispatchToProps)(Events); 61 | -------------------------------------------------------------------------------- /__tests__/files/results/pug/index.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | head 3 | meta(charset!='UTF-8') 4 | title Test Document 5 | .a 6 | .b 7 | -------------------------------------------------------------------------------- /__tests__/generateMapping.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import json from 'json-extra'; 5 | 6 | import reset from './helpers/reset'; 7 | import rcs from '../lib'; 8 | 9 | let testCwd; 10 | const fixturesCwd = path.join(process.cwd(), '/__tests__/files/fixtures'); 11 | 12 | beforeEach(async () => { 13 | testCwd = tmp.dirSync(); 14 | 15 | reset(); 16 | 17 | await rcs.process.css('**/style*.css', { 18 | newPath: testCwd.name, 19 | cwd: fixturesCwd, 20 | }); 21 | }); 22 | 23 | afterEach(() => { 24 | testCwd.removeCallback(); 25 | }); 26 | 27 | test('should create the normal mapping file', (done) => { 28 | rcs.mapping.generate(testCwd.name, (err) => { 29 | const cssMapping = json.readToObjSync(path.join(testCwd.name, '/renaming_map.json'), 'utf8'); 30 | 31 | expect(err).toBeFalsy(); 32 | expect(cssMapping.selectors['.jp-block']).toBe('a'); 33 | expect(cssMapping.selectors['.jp-block__element']).toBe('b'); 34 | 35 | done(); 36 | }); 37 | }); 38 | 39 | test('should create the minified mapping file', (done) => { 40 | rcs.mapping.generate(testCwd.name, { 41 | origValues: false, 42 | }, (err) => { 43 | const cssMappingMin = json.readToObjSync(path.join(testCwd.name, '/renaming_map_min.json'), 'utf8'); 44 | 45 | expect(err).toBeFalsy(); 46 | expect(cssMappingMin.selectors['.a']).toBe('jp-block'); 47 | expect(cssMappingMin.selectors['.b']).toBe('jp-block__element'); 48 | 49 | done(); 50 | }); 51 | }); 52 | 53 | test('should create the minified mapping file with a custom name', (done) => { 54 | rcs.mapping.generate(testCwd.name, { 55 | origValues: false, 56 | fileName: 'custom-name', 57 | }, (err) => { 58 | const cssMappingMin = json.readToObjSync(path.join(testCwd.name, '/custom-name.json'), 'utf8'); 59 | 60 | expect(err).toBeFalsy(); 61 | expect(cssMappingMin.selectors['.a']).toBe('jp-block'); 62 | expect(cssMappingMin.selectors['.b']).toBe('jp-block__element'); 63 | 64 | done(); 65 | }); 66 | }); 67 | 68 | test('should create the minified mapping js file', (done) => { 69 | rcs.mapping.generate(testCwd.name, { 70 | json: false, 71 | }, (err) => { 72 | const cssMapping = fs.readFileSync(path.join(testCwd.name, '/renaming_map.js'), 'utf8'); 73 | 74 | expect(err).toBeFalsy(); 75 | expect(cssMapping).toMatch(new RegExp(/var CSS_NAME_MAPPING = {/)); 76 | 77 | done(); 78 | }); 79 | }); 80 | 81 | test('should overwrite mapping files', (done) => { 82 | rcs.mapping.generate(testCwd.name, (err) => { 83 | rcs.mapping.generate(testCwd.name, { overwrite: true }, (err2) => { 84 | expect(err).toBeFalsy(); 85 | expect(err2).toBeFalsy(); 86 | 87 | done(); 88 | }); 89 | }); 90 | }); 91 | 92 | test('should not overwrite mapping files', async () => { 93 | await expect(rcs.mapping.generate(testCwd.name)).resolves.toBe(undefined); 94 | await expect(rcs.mapping.generate(testCwd.name)).rejects.toBeTruthy(); 95 | }); 96 | 97 | test('should create the custom names minified mapping file', (done) => { 98 | rcs.mapping.generate(testCwd.name, { 99 | fileName: 'custom-name', 100 | }, (err) => { 101 | const cssMapping = json.readToObjSync(path.join(testCwd.name, '/custom-name.json'), 'utf8'); 102 | 103 | expect(err).toBeFalsy(); 104 | expect(cssMapping.selectors['.jp-block']).toBe('a'); 105 | expect(cssMapping.selectors['.jp-block__element']).toBe('b'); 106 | 107 | done(); 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /__tests__/helpers/reset.ts: -------------------------------------------------------------------------------- 1 | import rcsCore from 'rcs-core'; 2 | 3 | const reset = (): void => { 4 | rcsCore.keyframesLibrary.setAlphabet('#abcdefghijklmnopqrstuvwxyz'); 5 | rcsCore.cssVariablesLibrary.setAlphabet('#abcdefghijklmnopqrstuvwxyz'); 6 | rcsCore.selectorsLibrary.setAlphabet('#abcdefghijklmnopqrstuvwxyz'); 7 | rcsCore.selectorsLibrary.selectors[0].nameGenerator.reset(); 8 | rcsCore.selectorsLibrary.selectors[1].nameGenerator.reset(); 9 | rcsCore.keyframesLibrary.nameGenerator.reset(); 10 | rcsCore.cssVariablesLibrary.nameGenerator.reset(); 11 | rcsCore.selectorsLibrary.reset(); 12 | rcsCore.keyframesLibrary.reset(); 13 | rcsCore.cssVariablesLibrary.reset(); 14 | }; 15 | 16 | export default reset; 17 | -------------------------------------------------------------------------------- /__tests__/integration.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import { minify } from 'html-minifier'; 5 | 6 | import rcs from '../lib'; 7 | import reset from './helpers/reset'; 8 | 9 | let testCwd; 10 | const testFiles = '__tests__/files'; 11 | const fixturesCwd = path.join(testFiles, 'fixtures'); 12 | 13 | beforeEach(() => { 14 | testCwd = tmp.dirSync(); 15 | 16 | reset(); 17 | }); 18 | 19 | afterEach(() => { 20 | testCwd.removeCallback(); 21 | }); 22 | 23 | test('issue #19 | detect one file as array', async () => { 24 | await rcs.process.css('**/style.css', { 25 | newPath: testCwd.name, 26 | cwd: fixturesCwd, 27 | }); 28 | 29 | await rcs.process.html(['html/index.html'], { 30 | newPath: testCwd.name, 31 | cwd: fixturesCwd, 32 | }); 33 | 34 | expect(fs.existsSync(path.join(testCwd.name, '/html/index.html'))).toBe(true); 35 | expect(fs.existsSync(path.join(testCwd.name, '/css/style.css'))).toBe(true); 36 | expect(fs.existsSync(path.join(testCwd.name, '/css/subdirectory/style.css'))).toBe(true); 37 | }); 38 | 39 | test('issue #19 | detect one file', async () => { 40 | await rcs.process.css('**/style.css', { 41 | newPath: testCwd.name, 42 | cwd: fixturesCwd, 43 | }); 44 | 45 | await rcs.process.html('html/index.html', { 46 | newPath: testCwd.name, 47 | cwd: fixturesCwd, 48 | }); 49 | 50 | expect(fs.existsSync(path.join(testCwd.name, '/html/index.html'))).toBe(true); 51 | expect(fs.existsSync(path.join(testCwd.name, '/css/style.css'))).toBe(true); 52 | expect(fs.existsSync(path.join(testCwd.name, '/css/subdirectory/style.css'))).toBe(true); 53 | }); 54 | 55 | test('issue #21 | with auto', async () => { 56 | const issueFixture = path.join(testFiles, 'issue21/fixtures'); 57 | const issueResults = path.join(testFiles, 'issue21/results'); 58 | 59 | await rcs.process.auto(['style.css', 'index.html'], { 60 | newPath: testCwd.name, 61 | cwd: issueFixture, 62 | }); 63 | 64 | const newCss = fs.readFileSync(path.join(testCwd.name, '/style.css'), 'utf8'); 65 | const newHtml = fs.readFileSync(path.join(testCwd.name, '/index.html'), 'utf8'); 66 | const expectedCss = fs.readFileSync(path.join(issueResults, '/style.css'), 'utf8'); 67 | const expectedHtml = fs.readFileSync(path.join(issueResults, '/index.html'), 'utf8'); 68 | 69 | expect(newCss).toBe(expectedCss); 70 | expect(minify(newHtml, { collapseWhitespace: true })) 71 | .toBe(minify(expectedHtml, { collapseWhitespace: true })); 72 | }); 73 | 74 | test('issue #21 | with seperated process functions', async () => { 75 | const issueFixture = path.join(testFiles, 'issue21/fixtures'); 76 | const issueResults = path.join(testFiles, 'issue21/results'); 77 | 78 | await rcs.process.css('style.css', { 79 | newPath: testCwd.name, 80 | cwd: issueFixture, 81 | }); 82 | 83 | await rcs.process.html('index.html', { 84 | newPath: testCwd.name, 85 | cwd: issueFixture, 86 | }); 87 | 88 | const newCss = fs.readFileSync(path.join(testCwd.name, '/style.css'), 'utf8'); 89 | const newHtml = fs.readFileSync(path.join(testCwd.name, '/index.html'), 'utf8'); 90 | const expectedCss = fs.readFileSync(path.join(issueResults, '/style.css'), 'utf8'); 91 | const expectedHtml = fs.readFileSync(path.join(issueResults, '/index.html'), 'utf8'); 92 | 93 | expect(newCss).toBe(expectedCss); 94 | expect(minify(newHtml, { collapseWhitespace: true })) 95 | .toBe(minify(expectedHtml, { collapseWhitespace: true })); 96 | }); 97 | 98 | test('issue #70 | line breaks', async () => { 99 | const issueFixture = path.join(testFiles, 'issue70/fixtures'); 100 | const issueResults = path.join(testFiles, 'issue70/results'); 101 | 102 | await rcs.process.css('style.css', { 103 | newPath: testCwd.name, 104 | cwd: issueFixture, 105 | }); 106 | 107 | await rcs.process.html('index.html', { 108 | newPath: testCwd.name, 109 | cwd: issueFixture, 110 | }); 111 | 112 | const newCss = fs.readFileSync(path.join(testCwd.name, '/style.css'), 'utf8'); 113 | const newHtml = fs.readFileSync(path.join(testCwd.name, '/index.html'), 'utf8'); 114 | const expectedCss = fs.readFileSync(path.join(issueResults, '/style.css'), 'utf8'); 115 | const expectedHtml = fs.readFileSync(path.join(issueResults, '/index.html'), 'utf8'); 116 | 117 | expect(newCss).toBe(expectedCss); 118 | expect(minify(newHtml, { collapseWhitespace: true })) 119 | .toBe(minify(expectedHtml, { collapseWhitespace: true })); 120 | }); 121 | -------------------------------------------------------------------------------- /__tests__/integration_mapping.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import fs from 'fs-extra'; 3 | import path from 'path'; 4 | import json from 'json-extra'; 5 | import { minify } from 'html-minifier'; 6 | 7 | import rcs from '../lib'; 8 | import reset from './helpers/reset'; 9 | 10 | let testCwd; 11 | const fixturesCwd = './__tests__/files/fixtures'; 12 | const resultsCwd = './__tests__/files/results'; 13 | 14 | beforeEach(async () => { 15 | testCwd = tmp.dirSync(); 16 | 17 | reset(); 18 | 19 | await rcs.process.css('**/style*.css', { 20 | newPath: testCwd.name, 21 | cwd: fixturesCwd, 22 | }); 23 | await rcs.mapping.generate(testCwd.name); 24 | 25 | reset(); 26 | }); 27 | 28 | afterEach(() => { 29 | testCwd.removeCallback(); 30 | }); 31 | 32 | test('should load from an object', async () => { 33 | const cssMapping = json.readToObjSync(path.join(testCwd.name, '/renaming_map.json'), 'utf8'); 34 | 35 | rcs.mapping.load(cssMapping); 36 | 37 | await rcs.process.html('**/*.html', { 38 | newPath: testCwd.name, 39 | cwd: fixturesCwd, 40 | }); 41 | 42 | const newFile = fs.readFileSync(path.join(testCwd.name, '/html/index.html'), 'utf8'); 43 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/html/index.html'), 'utf8'); 44 | 45 | expect(minify(newFile, { collapseWhitespace: true })) 46 | .toBe(minify(expectedFile, { collapseWhitespace: true })); 47 | }); 48 | 49 | test('should load from a filestring', async () => { 50 | await rcs.mapping.load(path.join(testCwd.name, '/renaming_map.json')); 51 | 52 | await rcs.process.html('**/*.html', { 53 | newPath: testCwd.name, 54 | cwd: fixturesCwd, 55 | }); 56 | 57 | const newFile = fs.readFileSync(path.join(testCwd.name, '/html/index.html'), 'utf8'); 58 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/html/index.html'), 'utf8'); 59 | 60 | expect(minify(newFile, { collapseWhitespace: true })) 61 | .toBe(minify(expectedFile, { collapseWhitespace: true })); 62 | }); 63 | 64 | test('should load nothing as it does not exist', async () => { 65 | await rcs.mapping.load(path.join(testCwd.name, '/doesnotexist.json')); 66 | 67 | await rcs.process.html('**/*.html', { 68 | newPath: testCwd.name, 69 | cwd: fixturesCwd, 70 | }); 71 | 72 | const newFile = fs.readFileSync(path.join(testCwd.name, '/html/index.html'), 'utf8'); 73 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/html/index.html'), 'utf8'); 74 | 75 | expect(minify(newFile, { collapseWhitespace: true })) 76 | .not.toBe(minify(expectedFile, { collapseWhitespace: true })); 77 | }); 78 | 79 | test('should load from a filestring', async () => { 80 | await rcs.process.css('**/style*.css', { 81 | newPath: testCwd.name, 82 | cwd: fixturesCwd, 83 | }); 84 | 85 | await rcs.mapping.generate(testCwd.name, { origValues: false }); 86 | 87 | reset(); 88 | 89 | await rcs.mapping.load(path.join(testCwd.name, '/renaming_map_min.json'), { origValues: false }); 90 | 91 | await rcs.process.html('**/*.html', { 92 | newPath: testCwd.name, 93 | cwd: fixturesCwd, 94 | }); 95 | 96 | const newFile = fs.readFileSync(path.join(testCwd.name, '/html/index.html'), 'utf8'); 97 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/html/index.html'), 'utf8'); 98 | 99 | expect(minify(newFile, { collapseWhitespace: true })) 100 | .toBe(minify(expectedFile, { collapseWhitespace: true })); 101 | }); 102 | -------------------------------------------------------------------------------- /__tests__/loadMapping.ts: -------------------------------------------------------------------------------- 1 | import rcsCore from 'rcs-core'; 2 | 3 | import rcs from '../lib'; 4 | import reset from './helpers/reset'; 5 | 6 | beforeEach(() => { 7 | reset(); 8 | }); 9 | 10 | test('should load from an object', () => { 11 | rcs.mapping.load({ 12 | selectors: { 13 | '.jp-block': 'a-class', 14 | '#compressed': 'b', 15 | }, 16 | }); 17 | 18 | expect(rcsCore.selectorsLibrary.get('jp-block')).toBe('a-class'); 19 | expect(rcsCore.selectorsLibrary.get('#compressed')).toBe('b'); 20 | }); 21 | -------------------------------------------------------------------------------- /__tests__/processAuto.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import rcsCore from 'rcs-core'; 5 | import { minify } from 'html-minifier'; 6 | 7 | import rcs from '../lib'; 8 | import reset from './helpers/reset'; 9 | 10 | const fixturesCwd = '__tests__/files/fixtures'; 11 | const resultsCwd = '__tests__/files/results'; 12 | 13 | let testCwd; 14 | 15 | beforeEach(() => { 16 | testCwd = tmp.dirSync(); 17 | 18 | reset(); 19 | }); 20 | 21 | afterEach(() => { 22 | testCwd.removeCallback(); 23 | }); 24 | 25 | test('should process css files', async () => { 26 | await rcs.process.auto('**/style*.css', { 27 | newPath: testCwd.name, 28 | cwd: fixturesCwd, 29 | }); 30 | 31 | const newFile = fs.readFileSync(path.join(testCwd.name, '/css/style.css'), 'utf8'); 32 | const newFile2 = fs.readFileSync(path.join(testCwd.name, '/css/style2.css'), 'utf8'); 33 | const newFile3 = fs.readFileSync(path.join(testCwd.name, '/css/subdirectory/style.css'), 'utf8'); 34 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/style.css'), 'utf8'); 35 | const expectedFile2 = fs.readFileSync(path.join(resultsCwd, '/css/style2.css'), 'utf8'); 36 | const expectedFile3 = fs.readFileSync(path.join(resultsCwd, '/css/subdirectory/style.css'), 'utf8'); 37 | 38 | expect(newFile).toBe(expectedFile); 39 | expect(newFile2).toBe(expectedFile2); 40 | expect(newFile3).toBe(expectedFile3); 41 | }); 42 | 43 | test('processing | should process all files automatically', async () => { 44 | await rcs.process.auto(['**/*.{js,html}', 'css/style.css'], { 45 | newPath: testCwd.name, 46 | cwd: fixturesCwd, 47 | }); 48 | 49 | const newFile = fs.readFileSync(path.join(testCwd.name, '/js/main.js'), 'utf8'); 50 | const newFile2 = fs.readFileSync(path.join(testCwd.name, '/css/style.css'), 'utf8'); 51 | const newFile3 = fs.readFileSync(path.join(testCwd.name, '/html/index.html'), 'utf8'); 52 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/js/main.js'), 'utf8'); 53 | const expectedFile2 = fs.readFileSync(path.join(resultsCwd, '/css/style.css'), 'utf8'); 54 | const expectedFile3 = fs.readFileSync(path.join(resultsCwd, '/html/index.html'), 'utf8'); 55 | 56 | expect(newFile).toBe(expectedFile); 57 | expect(newFile2).toBe(expectedFile2); 58 | expect(minify(newFile3, { collapseWhitespace: true })) 59 | .toBe(minify(expectedFile3, { collapseWhitespace: true })); 60 | }); 61 | 62 | test('should process css files as arrays', async () => { 63 | await rcs.process.auto(['**/style.css', '**/style2.css'], { 64 | newPath: testCwd.name, 65 | cwd: fixturesCwd, 66 | }); 67 | 68 | const newFile = fs.readFileSync(path.join(testCwd.name, '/css/style.css'), 'utf8'); 69 | const newFile2 = fs.readFileSync(path.join(testCwd.name, '/css/style2.css'), 'utf8'); 70 | const newFile3 = fs.readFileSync(path.join(testCwd.name, '/css/subdirectory/style.css'), 'utf8'); 71 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/style.css'), 'utf8'); 72 | const expectedFile2 = fs.readFileSync(path.join(resultsCwd, '/css/style2.css'), 'utf8'); 73 | const expectedFile3 = fs.readFileSync(path.join(resultsCwd, '/css/subdirectory/style.css'), 'utf8'); 74 | 75 | expect(newFile).toBe(expectedFile); 76 | expect(newFile2).toBe(expectedFile2); 77 | expect(newFile3).toBe(expectedFile3); 78 | }); 79 | 80 | test('should not overwrite original files', async () => { 81 | await expect(( 82 | rcs.process.auto(['**/style.css', '**/style2.css'], { 83 | newPath: fixturesCwd, 84 | cwd: fixturesCwd, 85 | }) 86 | )).rejects.toEqual(new Error('File exist and cannot be overwritten. Set the option overwrite to true to overwrite files.')); 87 | }); 88 | 89 | test('should fail', async () => { 90 | await expect(rcs.process.auto('path/**/with/nothing/in/it')) 91 | .rejects.toEqual(new Error('No files found')); 92 | }); 93 | 94 | test('should process auto file with css variables', async () => { 95 | await rcs.process.auto('css/css-variables.css', { 96 | newPath: testCwd.name, 97 | cwd: fixturesCwd, 98 | }); 99 | 100 | const newFile = fs.readFileSync(path.join(testCwd.name, '/css/css-variables.css'), 'utf8'); 101 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/css-variables.css'), 'utf8'); 102 | 103 | expect(newFile).toBe(expectedFile); 104 | }); 105 | 106 | test('should not process auto file with css variables', async () => { 107 | await rcs.process.auto('css/css-variables.css', { 108 | newPath: testCwd.name, 109 | cwd: fixturesCwd, 110 | ignoreCssVariables: true, 111 | }); 112 | 113 | const newFile = fs.readFileSync(path.join(testCwd.name, '/css/css-variables.css'), 'utf8'); 114 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/css-variables-ignore.css'), 'utf8'); 115 | 116 | expect(newFile).toBe(expectedFile); 117 | }); 118 | 119 | test('should fillLibraries from html and css | issue #38', async () => { 120 | await rcs.process.auto(['**/*.{js,html}', 'css/style.css'], { 121 | newPath: testCwd.name, 122 | cwd: fixturesCwd, 123 | }); 124 | 125 | const newFile = fs.readFileSync(path.join(testCwd.name, '/html/index-with-style.html'), 'utf8'); 126 | const newFile2 = fs.readFileSync(path.join(testCwd.name, '/css/style.css'), 'utf8'); 127 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/html/index-with-style.html'), 'utf8'); 128 | const expectedFile2 = fs.readFileSync(path.join(resultsCwd, '/css/style.css'), 'utf8'); 129 | 130 | expect(minify(newFile, { collapseWhitespace: true })) 131 | .toBe(minify(expectedFile, { collapseWhitespace: true })); 132 | expect(minify(newFile2, { collapseWhitespace: true })) 133 | .toBe(minify(expectedFile2, { collapseWhitespace: true })); 134 | }); 135 | 136 | test('should check if optimize has been called', async () => { 137 | const optimizeSpy = jest.spyOn(rcsCore, 'optimize'); 138 | 139 | await rcs.process.auto(['**/*.{js,html}', 'css/style.css'], { 140 | newPath: testCwd.name, 141 | cwd: fixturesCwd, 142 | }); 143 | 144 | expect(optimizeSpy).toHaveBeenCalledTimes(1); 145 | 146 | optimizeSpy.mockRestore(); 147 | }); 148 | 149 | test('should check if optimize has been called test', async () => { 150 | const optimizeSpy = jest.spyOn(rcsCore, 'optimize'); 151 | 152 | await rcs.process.auto(['**/*.{js,html}', 'css/style.css'], { 153 | newPath: testCwd.name, 154 | cwd: fixturesCwd, 155 | optimize: false, 156 | }); 157 | 158 | expect(optimizeSpy).not.toHaveBeenCalled(); 159 | 160 | optimizeSpy.mockRestore(); 161 | }); 162 | -------------------------------------------------------------------------------- /__tests__/processAutoSync.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import { minify } from 'html-minifier'; 5 | 6 | import rcs from '../lib'; 7 | import reset from './helpers/reset'; 8 | 9 | let testCwd; 10 | const fixturesCwd = '__tests__/files/fixtures'; 11 | const resultsCwd = '__tests__/files/results'; 12 | 13 | beforeEach(() => { 14 | testCwd = tmp.dirSync(); 15 | 16 | reset(); 17 | }); 18 | 19 | afterEach(() => { 20 | testCwd.removeCallback(); 21 | }); 22 | 23 | test('should process all files synchronously', () => { 24 | let newFile; 25 | let expectedFile; 26 | 27 | rcs.process.autoSync(['**/*.js', 'css/style.css'], { 28 | newPath: testCwd.name, 29 | cwd: fixturesCwd, 30 | }); 31 | 32 | newFile = fs.readFileSync(path.join(testCwd.name, '/js/main.js'), 'utf8'); 33 | expectedFile = fs.readFileSync(path.join(resultsCwd, '/js/main.js'), 'utf8'); 34 | 35 | expect(newFile).toBe(expectedFile); 36 | 37 | newFile = fs.readFileSync(path.join(testCwd.name, '/css/style.css'), 'utf8'); 38 | expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/style.css'), 'utf8'); 39 | 40 | expect(newFile).toBe(expectedFile); 41 | }); 42 | 43 | test('should fillLibraries from html and css | issue #38', () => { 44 | rcs.process.autoSync(['**/*.{js,html}', 'css/style.css'], { 45 | newPath: testCwd.name, 46 | cwd: fixturesCwd, 47 | }); 48 | 49 | const newFile = fs.readFileSync(path.join(testCwd.name, '/html/index-with-style.html'), 'utf8'); 50 | const newFile2 = fs.readFileSync(path.join(testCwd.name, '/css/style.css'), 'utf8'); 51 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/html/index-with-style.html'), 'utf8'); 52 | const expectedFile2 = fs.readFileSync(path.join(resultsCwd, '/css/style.css'), 'utf8'); 53 | 54 | expect(minify(newFile, { collapseWhitespace: true })) 55 | .toBe(minify(expectedFile, { collapseWhitespace: true })); 56 | expect(minify(newFile2, { collapseWhitespace: true })) 57 | .toBe(minify(expectedFile2, { collapseWhitespace: true })); 58 | }); 59 | -------------------------------------------------------------------------------- /__tests__/processCss.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | 5 | import rcs from '../lib'; 6 | import reset from './helpers/reset'; 7 | 8 | let testCwd; 9 | const fixturesCwd = '__tests__/files/fixtures'; 10 | const resultsCwd = '__tests__/files/results'; 11 | 12 | beforeEach(() => { 13 | testCwd = tmp.dirSync(); 14 | 15 | reset(); 16 | }); 17 | 18 | afterEach(() => { 19 | testCwd.removeCallback(); 20 | }); 21 | 22 | test('should process css files and prefix them', async () => { 23 | await rcs.process.css('**/style*.css', { 24 | newPath: testCwd.name, 25 | cwd: fixturesCwd, 26 | prefix: 'prefixed-', 27 | }); 28 | 29 | const newFile = fs.readFileSync(path.join(testCwd.name, '/css/style.css'), 'utf8'); 30 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/style-prefix.css'), 'utf8'); 31 | 32 | expect(newFile).toBe(expectedFile); 33 | }); 34 | 35 | test('should process css files with rcs.process.css', async () => { 36 | await rcs.process.css('**/style*.css', { 37 | newPath: testCwd.name, 38 | cwd: fixturesCwd, 39 | }); 40 | 41 | const newFile = fs.readFileSync(path.join(testCwd.name, '/css/style.css'), 'utf8'); 42 | const newFile2 = fs.readFileSync(path.join(testCwd.name, '/css/style2.css'), 'utf8'); 43 | const newFile3 = fs.readFileSync(path.join(testCwd.name, '/css/subdirectory/style.css'), 'utf8'); 44 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/style.css'), 'utf8'); 45 | const expectedFile2 = fs.readFileSync(path.join(resultsCwd, '/css/style2.css'), 'utf8'); 46 | const expectedFile3 = fs.readFileSync(path.join(resultsCwd, '/css/subdirectory/style.css'), 'utf8'); 47 | 48 | expect(newFile).toBe(expectedFile); 49 | expect(newFile2).toBe(expectedFile2); 50 | expect(newFile3).toBe(expectedFile3); 51 | }); 52 | 53 | test('should process css files without options', async () => { 54 | await rcs.process.css(path.join(fixturesCwd, 'css/**/style*.css')); 55 | 56 | const newFile = fs.readFileSync(path.join(process.cwd(), 'rcs', fixturesCwd, '/css/style.css'), 'utf8'); 57 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/style.css'), 'utf8'); 58 | 59 | expect(newFile).toBe(expectedFile); 60 | 61 | fs.removeSync(path.join(process.cwd(), 'rcs')); 62 | }); 63 | 64 | test('should replace the selector attributes correctly', async () => { 65 | await rcs.process.css('css/css-attributes.css', { 66 | newPath: testCwd.name, 67 | cwd: fixturesCwd, 68 | }); 69 | 70 | expect(fs.readFileSync(path.join(testCwd.name, '/css/css-attributes.css'), 'utf8')) 71 | .toBe(fs.readFileSync(path.join(resultsCwd, '/css/css-attributes.css'), 'utf8')); 72 | }); 73 | 74 | test('should replace the selector attributes with pre and suffixes correctly', async () => { 75 | await rcs.process.css('css/css-attributes.css', { 76 | prefix: 'prefix-', 77 | suffix: '-suffix', 78 | newPath: testCwd.name, 79 | cwd: fixturesCwd, 80 | }); 81 | 82 | expect(fs.readFileSync(path.join(testCwd.name, '/css/css-attributes.css'), 'utf8')) 83 | .toBe(fs.readFileSync(path.join(resultsCwd, '/css/css-attributes-pre-suffix.css'), 'utf8')); 84 | }); 85 | 86 | test('should replace the selector attributes without caring about attribute selectors', async () => { 87 | await rcs.process.css('css/css-attributes.css', { 88 | prefix: 'prefix-', 89 | suffix: '-suffix', 90 | ignoreAttributeSelectors: true, 91 | newPath: testCwd.name, 92 | cwd: fixturesCwd, 93 | }); 94 | 95 | expect(fs.readFileSync(path.join(testCwd.name, '/css/css-attributes.css'), 'utf8')) 96 | .toBe(fs.readFileSync(path.join(resultsCwd, '/css/css-attributes-ignore.css'), 'utf8')); 97 | }); 98 | 99 | test('should process css file with css variables', async () => { 100 | await rcs.process.css('css/css-variables.css', { 101 | newPath: testCwd.name, 102 | cwd: fixturesCwd, 103 | }); 104 | 105 | const newFile = fs.readFileSync(path.join(testCwd.name, '/css/css-variables.css'), 'utf8'); 106 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/css-variables.css'), 'utf8'); 107 | 108 | expect(newFile).toBe(expectedFile); 109 | }); 110 | -------------------------------------------------------------------------------- /__tests__/processCssSync.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | 5 | import rcs from '../lib'; 6 | import reset from './helpers/reset'; 7 | 8 | let testCwd; 9 | const fixturesCwd = '__tests__/files/fixtures'; 10 | const resultsCwd = '__tests__/files/results'; 11 | 12 | beforeEach(() => { 13 | testCwd = tmp.dirSync(); 14 | 15 | reset(); 16 | }); 17 | 18 | afterEach(() => { 19 | testCwd.removeCallback(); 20 | }); 21 | 22 | test('should process css files synchornously', () => { 23 | rcs.process.cssSync('**/style*.css', { 24 | newPath: testCwd.name, 25 | cwd: fixturesCwd, 26 | }); 27 | 28 | const newFile = fs.readFileSync(path.join(testCwd.name, '/css/style.css'), 'utf8'); 29 | const newFile2 = fs.readFileSync(path.join(testCwd.name, '/css/style2.css'), 'utf8'); 30 | const newFile3 = fs.readFileSync(path.join(testCwd.name, '/css/subdirectory/style.css'), 'utf8'); 31 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/style.css'), 'utf8'); 32 | const expectedFile2 = fs.readFileSync(path.join(resultsCwd, '/css/style2.css'), 'utf8'); 33 | const expectedFile3 = fs.readFileSync(path.join(resultsCwd, '/css/subdirectory/style.css'), 'utf8'); 34 | 35 | expect(newFile).toBe(expectedFile); 36 | expect(newFile2).toBe(expectedFile2); 37 | expect(newFile3).toBe(expectedFile3); 38 | }); 39 | 40 | test('should process css files as arrays synchornously', () => { 41 | rcs.process.cssSync(['**/style.css', '**/style2.css'], { 42 | newPath: testCwd.name, 43 | cwd: fixturesCwd, 44 | }); 45 | 46 | const newFile = fs.readFileSync(path.join(testCwd.name, '/css/style.css'), 'utf8'); 47 | const newFile2 = fs.readFileSync(path.join(testCwd.name, '/css/style2.css'), 'utf8'); 48 | const newFile3 = fs.readFileSync(path.join(testCwd.name, '/css/subdirectory/style.css'), 'utf8'); 49 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/css/style.css'), 'utf8'); 50 | const expectedFile2 = fs.readFileSync(path.join(resultsCwd, '/css/style2.css'), 'utf8'); 51 | const expectedFile3 = fs.readFileSync(path.join(resultsCwd, '/css/subdirectory/style.css'), 'utf8'); 52 | 53 | expect(newFile).toBe(expectedFile); 54 | expect(newFile2).toBe(expectedFile2); 55 | expect(newFile3).toBe(expectedFile3); 56 | }); 57 | -------------------------------------------------------------------------------- /__tests__/processJs.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import rcsCore from 'rcs-core'; 5 | 6 | import rcs from '../lib'; 7 | import reset from './helpers/reset'; 8 | 9 | let testCwd; 10 | const fixturesCwd = '__tests__/files/fixtures'; 11 | const resultsCwd = '__tests__/files/results'; 12 | 13 | beforeAll(() => { 14 | testCwd = tmp.dirSync(); 15 | 16 | reset(); 17 | 18 | rcsCore.selectorsLibrary.fillLibrary(fs.readFileSync(path.join(fixturesCwd, '/css/style.css'), 'utf8')); 19 | }); 20 | 21 | afterEach(() => { 22 | testCwd.removeCallback(); 23 | }); 24 | 25 | test('should process js files', async () => { 26 | await rcs.process.js('js/main.js', { 27 | newPath: testCwd.name, 28 | cwd: fixturesCwd, 29 | }); 30 | 31 | const newFile = fs.readFileSync(path.join(testCwd.name, '/js/main.js'), 'utf8'); 32 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/js/main.js'), 'utf8'); 33 | 34 | expect(newFile).toBe(expectedFile); 35 | }); 36 | 37 | test('should process jsx files', async () => { 38 | await rcs.process.js('js/react.js', { 39 | newPath: testCwd.name, 40 | cwd: fixturesCwd, 41 | }); 42 | 43 | const newFile = fs.readFileSync(path.join(testCwd.name, '/js/react.js'), 'utf8'); 44 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/js/react.js'), 'utf8'); 45 | 46 | expect(newFile).toBe(expectedFile); 47 | }); 48 | 49 | test('should not process jsx files', async () => { 50 | await expect(( 51 | rcs.process.js('js/react.js', { 52 | newPath: testCwd.name, 53 | cwd: fixturesCwd, 54 | espreeOptions: { 55 | ecmaFeatures: { 56 | jsx: false, 57 | }, 58 | }, 59 | }) 60 | )).rejects.toBeTruthy(); 61 | }); 62 | 63 | test('should process complex files', async () => { 64 | await rcs.process.js('js/complex.js', { 65 | newPath: testCwd.name, 66 | cwd: fixturesCwd, 67 | }); 68 | 69 | const newFile = fs.readFileSync(path.join(testCwd.name, '/js/complex.js'), 'utf8'); 70 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/js/complex.js'), 'utf8'); 71 | 72 | expect(newFile).toBe(expectedFile); 73 | }); 74 | 75 | test('should not process multiple files', async () => { 76 | await rcs.process.js('js/*.js', { 77 | newPath: testCwd.name, 78 | cwd: fixturesCwd, 79 | espreeOptions: { 80 | ecmaFeatures: { 81 | jsx: true, 82 | }, 83 | }, 84 | }); 85 | 86 | const newFile = fs.readFileSync(path.join(testCwd.name, '/js/complex.js'), 'utf8'); 87 | const newFileTwo = fs.readFileSync(path.join(testCwd.name, '/js/main.js'), 'utf8'); 88 | const newFileThree = fs.readFileSync(path.join(testCwd.name, '/js/react.js'), 'utf8'); 89 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/js/complex.js'), 'utf8'); 90 | const expectedFileTwo = fs.readFileSync(path.join(resultsCwd, '/js/main.js'), 'utf8'); 91 | const expectedFileThree = fs.readFileSync(path.join(resultsCwd, '/js/react.js'), 'utf8'); 92 | 93 | expect(newFile).toBe(expectedFile); 94 | expect(newFileTwo).toBe(expectedFileTwo); 95 | expect(newFileThree).toBe(expectedFileThree); 96 | }); 97 | -------------------------------------------------------------------------------- /__tests__/processJsSync.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import fs from 'fs-extra'; 3 | import rcsCore from 'rcs-core'; 4 | import path from 'path'; 5 | 6 | import rcs from '../lib'; 7 | import reset from './helpers/reset'; 8 | 9 | let testCwd; 10 | const fixturesCwd = '__tests__/files/fixtures'; 11 | const resultsCwd = '__tests__/files/results'; 12 | 13 | beforeAll(() => { 14 | testCwd = tmp.dirSync(); 15 | 16 | reset(); 17 | 18 | rcsCore.selectorsLibrary.fillLibrary(fs.readFileSync(path.join(fixturesCwd, '/css/style.css'), 'utf8')); 19 | }); 20 | 21 | afterEach(() => { 22 | testCwd.removeCallback(); 23 | }); 24 | 25 | test('should process js files', () => { 26 | rcs.process.jsSync('js/main.js', { 27 | newPath: testCwd.name, 28 | cwd: fixturesCwd, 29 | }); 30 | 31 | const newFile = fs.readFileSync(path.join(testCwd.name, '/js/main.js'), 'utf8'); 32 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/js/main.js'), 'utf8'); 33 | 34 | expect(newFile).toBe(expectedFile); 35 | }); 36 | 37 | test('should process jsx files', () => { 38 | rcs.process.jsSync('js/react.js', { 39 | newPath: testCwd.name, 40 | cwd: fixturesCwd, 41 | espreeOptions: { 42 | ecmaFeatures: { 43 | jsx: true, 44 | }, 45 | }, 46 | }); 47 | 48 | const newFile = fs.readFileSync(path.join(testCwd.name, '/js/react.js'), 'utf8'); 49 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/js/react.js'), 'utf8'); 50 | 51 | expect(newFile).toBe(expectedFile); 52 | }); 53 | 54 | test('should not process jsx files', () => { 55 | rcs.process.jsSync('js/react.js', { 56 | newPath: testCwd.name, 57 | cwd: fixturesCwd, 58 | }); 59 | 60 | const newFile = fs.readFileSync(path.join(testCwd.name, '/js/react.js'), 'utf8'); 61 | const expectedFile = fs.readFileSync(path.join(testCwd.name, '/js/react.js'), 'utf8'); 62 | 63 | expect(newFile).toBe(expectedFile); 64 | }); 65 | 66 | test('should process complex files', () => { 67 | rcs.process.jsSync('js/complex.js', { 68 | newPath: testCwd.name, 69 | cwd: fixturesCwd, 70 | }); 71 | 72 | const newFile = fs.readFileSync(path.join(testCwd.name, '/js/complex.js'), 'utf8'); 73 | const expectedFile = fs.readFileSync(path.join(testCwd.name, '/js/complex.js'), 'utf8'); 74 | 75 | expect(newFile).toBe(expectedFile); 76 | }); 77 | 78 | test('should not process multiple files', () => { 79 | rcs.process.jsSync('js/*.js', { 80 | newPath: testCwd.name, 81 | cwd: fixturesCwd, 82 | espreeOptions: { 83 | ecmaFeatures: { 84 | jsx: true, 85 | }, 86 | }, 87 | }); 88 | 89 | const newFile = fs.readFileSync(path.join(testCwd.name, '/js/complex.js'), 'utf8'); 90 | const newFileTwo = fs.readFileSync(path.join(testCwd.name, '/js/main.js'), 'utf8'); 91 | const newFileThree = fs.readFileSync(path.join(testCwd.name, '/js/react.js'), 'utf8'); 92 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/js/complex.js'), 'utf8'); 93 | const expectedFileTwo = fs.readFileSync(path.join(resultsCwd, '/js/main.js'), 'utf8'); 94 | const expectedFileThree = fs.readFileSync(path.join(resultsCwd, '/js/react.js'), 'utf8'); 95 | 96 | expect(newFile).toBe(expectedFile); 97 | expect(newFileTwo).toBe(expectedFileTwo); 98 | expect(newFileThree).toBe(expectedFileThree); 99 | }); 100 | -------------------------------------------------------------------------------- /__tests__/processPug.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import rcsCore from 'rcs-core'; 5 | 6 | import rcs from '../lib'; 7 | import reset from './helpers/reset'; 8 | 9 | let testCwd; 10 | const fixturesCwd = '__tests__/files/fixtures'; 11 | const resultsCwd = '__tests__/files/results'; 12 | 13 | beforeAll(() => { 14 | testCwd = tmp.dirSync(); 15 | 16 | reset(); 17 | 18 | rcsCore.selectorsLibrary.fillLibrary(fs.readFileSync(path.join(fixturesCwd, '/css/style.css'), 'utf8')); 19 | }); 20 | 21 | afterEach(() => { 22 | testCwd.removeCallback(); 23 | }); 24 | 25 | test('should process pug files', async () => { 26 | await rcs.process.pug('pug/index.pug', { 27 | newPath: testCwd.name, 28 | cwd: fixturesCwd, 29 | }); 30 | 31 | const newFile = fs.readFileSync(path.join(testCwd.name, '/pug/index.pug'), 'utf8'); 32 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/pug/index.pug'), 'utf8'); 33 | 34 | expect(newFile.trim()).toBe(expectedFile.trim()); 35 | }); 36 | -------------------------------------------------------------------------------- /__tests__/processPugSync.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import path from 'path'; 3 | import fs from 'fs-extra'; 4 | import rcsCore from 'rcs-core'; 5 | 6 | import rcs from '../lib'; 7 | import reset from './helpers/reset'; 8 | 9 | let testCwd; 10 | const fixturesCwd = '__tests__/files/fixtures'; 11 | const resultsCwd = '__tests__/files/results'; 12 | 13 | 14 | beforeAll(() => { 15 | testCwd = tmp.dirSync(); 16 | 17 | reset(); 18 | 19 | rcsCore.selectorsLibrary.fillLibrary(fs.readFileSync(path.join(fixturesCwd, '/css/style.css'), 'utf8')); 20 | }); 21 | 22 | afterEach(() => { 23 | testCwd.removeCallback(); 24 | }); 25 | 26 | test('should process pug files', () => { 27 | rcs.process.pugSync('pug/index.pug', { 28 | newPath: testCwd.name, 29 | cwd: fixturesCwd, 30 | }); 31 | 32 | const newFile = fs.readFileSync(path.join(testCwd.name, '/pug/index.pug'), 'utf8'); 33 | const expectedFile = fs.readFileSync(path.join(resultsCwd, '/pug/index.pug'), 'utf8'); 34 | 35 | expect(newFile.trim()).toBe(expectedFile.trim()); 36 | }); 37 | -------------------------------------------------------------------------------- /__tests__/save.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import fs from 'fs-extra'; 3 | import path from 'path'; 4 | 5 | import save from '../lib/helper/save'; 6 | 7 | let testCwd; 8 | const testFiles = '__tests__/files'; 9 | 10 | beforeEach(() => { 11 | testCwd = tmp.dirSync(); 12 | }); 13 | 14 | afterEach(() => { 15 | testCwd.removeCallback(); 16 | }); 17 | 18 | 19 | test('should create a file within a non existing dir', (done) => { 20 | const filePath = path.join(testCwd.name, '/a/non/existing/path/test.txt'); 21 | 22 | save(filePath, 'test content', (err) => { 23 | expect(err).not.toBe(undefined); 24 | 25 | expect(fs.existsSync(filePath)).toBe(true); 26 | expect(fs.readFileSync(filePath, 'utf8')).toBe('test content'); 27 | 28 | done(); 29 | }); 30 | }); 31 | 32 | test('should not overwrite the same file', async () => { 33 | const filePath = path.join(testFiles, '/config.json'); 34 | const filePathTest = path.join(testCwd.name, '/config.json'); 35 | const oldFile = fs.readFileSync(filePath, 'utf8'); 36 | 37 | await save(filePathTest, 'test content'); 38 | 39 | await expect(save(filePathTest, 'test content')) 40 | .rejects 41 | .toEqual(new Error('File exist and cannot be overwritten. Set the option overwrite to true to overwrite files.')); 42 | expect(fs.readFileSync(filePath, 'utf8')).toBe(oldFile); 43 | }); 44 | -------------------------------------------------------------------------------- /__tests__/saveSync.ts: -------------------------------------------------------------------------------- 1 | import tmp from 'tmp'; 2 | import fs from 'fs-extra'; 3 | import path from 'path'; 4 | 5 | import saveSync from '../lib/helper/saveSync'; 6 | 7 | let testCwd; 8 | const testFiles = '__tests__/files'; 9 | 10 | beforeEach(() => { 11 | testCwd = tmp.dirSync(); 12 | }); 13 | 14 | afterEach(() => { 15 | testCwd.removeCallback(); 16 | }); 17 | 18 | 19 | test('saveSync | should save', () => { 20 | const filePath = path.join(testCwd.name, '/config.txt'); 21 | 22 | saveSync(filePath, 'test content'); 23 | 24 | expect(fs.readFileSync(filePath, 'utf8')).toBe('test content'); 25 | }); 26 | 27 | test('saveSync | should not overwrite the same file', () => { 28 | const filePath = path.join(testFiles, '/config.json'); 29 | const filePathTest = path.join(testCwd.name, '/config.json'); 30 | const oldFile = fs.readFileSync(filePath, 'utf8'); 31 | let failed = false; 32 | 33 | saveSync(filePathTest, 'test content'); 34 | 35 | try { 36 | saveSync(filePathTest, 'test content'); 37 | 38 | // if no error is thrown before it should fail here 39 | failed = true; 40 | } catch (e) { 41 | expect(e.message).toBe('File exist and cannot be overwritten. Set the option overwrite to true to overwrite files.'); 42 | } 43 | 44 | expect(failed).toBe(false); 45 | expect(fs.readFileSync(filePath, 'utf8')).toBe(oldFile); 46 | }); 47 | -------------------------------------------------------------------------------- /docs/api/config.md: -------------------------------------------------------------------------------- 1 | # Config 2 | 3 | **config.load([pathLocation])** 4 | 5 | > All available configs [here](#rcs-config) 6 | 7 | RCS will lookup first for a `.rcsrc` of the current directory. If there is no such file, it will lookup for a `package.json` with a `"rcsrc": {}` in it. You can also write any path in the parameters and write your own config file. This function is synchronous. 8 | 9 | Parameters: 10 | - pathLocation `` *optional* 11 | 12 | Example: 13 | 14 | ```js 15 | const rcs = require('rename-css-selectors'); 16 | 17 | rcs.config.load(); 18 | ``` 19 | 20 | ## RCS config 21 | 22 | > Just create a `.rcsrc` in your project root or you can add everything in your `package.json` with the value `rcs` 23 | 24 | - [Example](#example) 25 | - [Exclude](#exclude-classes-and-ids) 26 | - [Ignore](#ignore-files) 27 | 28 | ### Example 29 | 30 | The `.rcsrc` or the a own config file: 31 | 32 | ```json 33 | { 34 | "exclude": [ 35 | "js", 36 | "flexbox" 37 | ] 38 | } 39 | ``` 40 | 41 | The `package.json`: 42 | 43 | ```json 44 | { 45 | "name": "Your application name", 46 | "rcs": { 47 | "exclude": [ 48 | "js", 49 | "flexbox" 50 | ] 51 | } 52 | } 53 | ``` 54 | 55 | ### Exclude Classes and IDs 56 | 57 | `exclude` 58 | 59 | What if you are using something such as Modernizr and you do not want to minify some selectors? 60 | 61 | Let's exclude 4 classes and id's: `js`, `flexbox`, `canvas` and `svg` 62 | 63 | ```json 64 | { 65 | "exclude": [ 66 | "js", 67 | "flexbox", 68 | "canvas", 69 | "svg" 70 | ] 71 | } 72 | ``` 73 | 74 | ### Ignore files 75 | 76 | `ignore` 77 | 78 | If you need to ignore some file or some file pattern from processing, this is how to do it using minimatch pattern (glob) 79 | Please notice that filepathes are matched absolutely. 80 | 81 | ```json 82 | { 83 | "ignore": [ 84 | "relativeFile.js", 85 | "**/*.min.js", 86 | ] 87 | } 88 | ``` 89 | -------------------------------------------------------------------------------- /docs/api/loadMapping.md: -------------------------------------------------------------------------------- 1 | # loadMapping 2 | 3 | **loadMapping(mapping[, options])** 4 | 5 | > *Note:* If you include a file, it **MUST** be the json generated mapping. 6 | 7 | Loads the previous generated mapping. This ensures that all your projects have all the time the same renamed selectors. 8 | 9 | Parameters: 10 | - mapping ``: can be either a path to the mapping or a mapping object 11 | - options `` *optional* 12 | 13 | Options: 14 | 15 | - origValues (boolean): Wether the cssMappingMin (`false`) or cssMapping (`true`) should get loaded. Default is `true` 16 | 17 | Example: 18 | 19 | ```js 20 | const rcs = require('rename-css-selectors'); 21 | 22 | // loadMapping is synchronous 23 | // the first parameter can be either a string to the file 24 | // or the json object directly 25 | await rcs.loadMapping('./renaming_map.json', options); 26 | // or 27 | rcs.loadMapping({ selectors: {} }, options); 28 | 29 | rcs.process('**/*.html', (err) => { 30 | ... 31 | }); 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/api/mapping.md: -------------------------------------------------------------------------------- 1 | # rcs.mapping 2 | 3 | ## Methods 4 | - [generate](#generate) 5 | - [load](#load) 6 | 7 | ### generate 8 | 9 | **rcs.mapping.generate(pathLocation[, options][, callback])** 10 | 11 | > *Note:* if you are using the options either `cssMapping` or `cssMappingMin` must be set to true. Both to `true` at the same time are not valid. 12 | 13 | Generates mapping files: all minified, all original selectors or both. They are stored as object in a variable. The file is named as `renaming_map.json` or `renaming_map_min.json`. 14 | 15 | Parameters: 16 | - pathLocation `` 17 | - options `` *optional* 18 | - callback `` *optional* 19 | 20 | Options: 21 | 22 | - fileName ``: alternative filename. Default `renaming_map` or `renaming_map_min` if `origValues: true` 23 | - json ``: writes a `json` instead of a `js`. Default `true` 24 | - overwrite ``: if it should overwrite the existing mapping. Default `false` 25 | - origValues ``: if `false` it the keys and values are switched: `keys` are the minified values. Default `false` 26 | 27 | Example: 28 | 29 | ```js 30 | const rcs = require('rename-css-selectors'); 31 | 32 | // callback 33 | rcs.generateMapping('./mappings', options, (err) => { 34 | if (err) { 35 | return console.error(err); 36 | } 37 | 38 | console.log('Successfully wrote mapping files'); 39 | }; 40 | 41 | // promise 42 | rcs.generateMapping('./mappings', options) 43 | .then(() => console.log('Successfully wrote mapping files)) 44 | .catch(console.error); 45 | 46 | // async/await 47 | (async () => { 48 | try { 49 | await rcs.generateMapping('./mappings', options); 50 | 51 | console.log('Successfully wrote mapping files'); 52 | } catch (err) { 53 | console.error(err); 54 | } 55 | })(); 56 | ``` 57 | 58 | Output in `renaming_map_min.js`: 59 | 60 | ```js 61 | const CSS_NAME_MAPPING_MIN = { 62 | '.e': 'any-class', 63 | '.t': 'another-class' 64 | }; 65 | ``` 66 | 67 | ### load 68 | 69 | **rcs.mapping.load(mapping[, options])** 70 | 71 | > *Note:* If you include a file, it **MUST** be the json generated mapping. 72 | 73 | Loads the previous generated mapping. This ensures that all your projects have all the time the same renamed selectors. 74 | 75 | Parameters: 76 | - mapping ``: can be either a path to the mapping or a mapping object 77 | - options `` *optional* 78 | 79 | Options: 80 | 81 | - origValues (boolean): Wether the cssMappingMin (`false`) or cssMapping (`true`) should get loaded. Default is `true` 82 | 83 | Example: 84 | 85 | ```js 86 | const rcs = require('rename-css-selectors'); 87 | 88 | // load is synchronous 89 | // the first parameter can be either a string to the file 90 | // or the json object directly 91 | await rcs.mapping.load('./renaming_map.json', options); 92 | // or 93 | rcs.mapping.load({ selectors: {} }, options); 94 | 95 | rcs.process('**/*.html', (err) => { 96 | ... 97 | }); 98 | ``` 99 | -------------------------------------------------------------------------------- /docs/api/processAny.md: -------------------------------------------------------------------------------- 1 | # rcs.process.any 2 | 3 | **rcs.process.any(src[, options][, callback])** 4 | 5 | > **Important!** process.any should run first, otherwise there are no minified selectors 6 | 7 | Matches all strings (`" "` or `' '`) and replaces all matching words which are the same as the stored CSS selectors. 8 | 9 | Sync: `process.anySync` 10 | 11 | Parameters: 12 | - src `` 13 | - options `` *optional* 14 | - callback `` *optional* 15 | 16 | Options: 17 | 18 | - *all options of [rcs.process](process.md)* 19 | 20 | Example: 21 | 22 | ```js 23 | const rcs = require('rename-css-selectors'); 24 | 25 | // callback 26 | rcs.process.any('**/*.txt', options, (err) => { 27 | if (err) { 28 | return console.error(err); 29 | } 30 | 31 | console.log('Successfully wrote new files and stored values'); 32 | }); 33 | 34 | // promise 35 | rcs.process.any('**/*.txt', options) 36 | .then(() => console.log('Successfully wrote new files and stored values')); 37 | .catch(console.error); 38 | 39 | // async/await 40 | (async () => { 41 | try { 42 | await rcs.process.any('**/*.txt', options); 43 | 44 | console.log('Successfully wrote new files and stored values') 45 | } catch (err) { 46 | console.error(err); 47 | } 48 | })(); 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/api/processAuto.md: -------------------------------------------------------------------------------- 1 | # rcs.process.auto 2 | 3 | **rcs.process.auto(src[, options][, callback])** 4 | 5 | > *Note:* JavaScript, HTML and CSS files are detected automatically. If you want to make sure that JavaScript files or others are detected correctly, you can use `process.js` for JavaScript files or `process.html` for HTML files manually to ensure a correct renaming within files. 6 | 7 | Not supported files are renamed by [`replace.any`](replaceAny.md). 8 | 9 | Sync: `process.autoSync` 10 | 11 | Parameters: 12 | - src `` 13 | - options `` *optional* 14 | - callback `` *optional* 15 | 16 | Options: 17 | 18 | - overwrite ``: ensures that it does not overwrite the same file accidently. Default is `false` 19 | - cwd ``: the working directory in which to serach. Default is `process.cwd()` 20 | - newPath ``: in which folder the new files should go. Default is `rcs` 21 | - optimize ``: checks if the selectors should be [optimized](https://github.com/JPeer264/node-rcs-core/blob/main/docs/api/optimize.md). Default is `true` 22 | 23 | Example: 24 | 25 | ```js 26 | const rcs = require('rename-css-selectors'); 27 | 28 | // callback 29 | rcs.process.auto('**/*.js', options, (err) => { 30 | if (err) { 31 | return console.error(err); 32 | } 33 | 34 | console.log('Successfully wrote new files'); 35 | }); 36 | 37 | // promise 38 | rcs.process.auto('**/*.js', options) 39 | .then(() => console.log('Successfully wrote new files')) 40 | .catch(console.error); 41 | 42 | // async/await 43 | (async () => { 44 | try { 45 | await rcs.process.auto('**/*.js', options); 46 | 47 | console.log('Successfully wrote new files'); 48 | } catch (err) { 49 | console.log(err); 50 | } 51 | }); 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/api/processCss.md: -------------------------------------------------------------------------------- 1 | # rcs.process.css 2 | 3 | **rcs.process.css(src[, options][, callback])** 4 | 5 | Store all matched selectors into the library and saves the new generated file with all renamed selectors. 6 | 7 | Sync: `process.cssSync` 8 | 9 | Parameters: 10 | - src `` 11 | - options `` *optional* 12 | - callback `` *optional* 13 | 14 | Options: 15 | 16 | - optimize ``: checks if the selectors should be [optimized](https://github.com/JPeer264/node-rcs-core/blob/main/docs/api/optimize.md). Default is `true` 17 | - *all options of [rcs.process](process.md)* 18 | - *plus options [rcsCore.fillLibrary](https://github.com/JPeer264/node-rcs-core/blob/master/docs/api/filllibraries.md)* 19 | 20 | Example: 21 | 22 | ```js 23 | const rcs = require('rename-css-selectors'); 24 | 25 | // callback 26 | rcs.process.css('**/*.css', options, (err) => { 27 | if (err) { 28 | return console.error(err); 29 | } 30 | 31 | console.log('Successfully wrote new files and stored values'); 32 | }); 33 | 34 | // promise 35 | rcs.process.css('**/*.css', options) 36 | .then(() => console.log('Successfully wrote new files and stored values')); 37 | .catch(console.error); 38 | 39 | // async/await 40 | (async () => { 41 | try { 42 | await rcs.process.css('**/*.css', options); 43 | 44 | console.log('Successfully wrote new files and stored values') 45 | } catch (err) { 46 | console.error(err); 47 | } 48 | })(); 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/api/processHtml.md: -------------------------------------------------------------------------------- 1 | # rcs.process.html 2 | 3 | **rcs.process.html(src[, options][, callback])** 4 | 5 | > **Important!** process.css should run first, otherwise there are no minified selectors 6 | 7 | Sync: `process.htmlSync` 8 | 9 | Parameters: 10 | - src `` 11 | - options `` *optional* 12 | - callback `` *optional* 13 | 14 | Options: 15 | 16 | - optimize ``: checks if the selectors should be [optimized](https://github.com/JPeer264/node-rcs-core/blob/main/docs/api/optimize.md). Default is `true` 17 | - *all options of [rcs.process](process.md)* 18 | - *plus options [rcsCore.replace.html](https://github.com/JPeer264/node-rcs-core/blob/master/docs/api/replace.md#html)* 19 | 20 | Example: 21 | 22 | ```js 23 | const rcs = require('rename-css-selectors'); 24 | 25 | // callback 26 | rcs.process.html('**/*.html', options, (err) => { 27 | if (err) { 28 | return console.error(err); 29 | } 30 | 31 | console.log('Successfully wrote new HTML files'); 32 | }); 33 | 34 | // promise 35 | rcs.process.html('**/*.html', options) 36 | .then(() => console.log('Successfully wrote new HTML files')) 37 | .catch(console.error); 38 | 39 | // async/await 40 | (async () => { 41 | try { 42 | await rcs.process.html('**/*.html', options); 43 | 44 | console.log('Successfully wrote new HTML files'); 45 | } catch (err) { 46 | console.error(err); 47 | } 48 | })(); 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/api/processJs.md: -------------------------------------------------------------------------------- 1 | # rcs.process.js 2 | 3 | **rcs.process.js(src[, options][, callback])** 4 | 5 | > **Important!** process.css should run first, otherwise there are no minified selectors 6 | 7 | Sync: `process.jsSync` 8 | 9 | Parameters: 10 | - src `` 11 | - options `` *optional* 12 | - callback `` *optional* 13 | 14 | Options: 15 | 16 | - *plus options [rcsCore.replace.js](https://github.com/JPeer264/node-rcs-core/blob/master/docs/api/replace.md#js)* 17 | 18 | Example: 19 | 20 | ```js 21 | const rcs = require('rename-css-selectors'); 22 | 23 | // callback 24 | rcs.process.js('**/*.js', options, (err) => { 25 | if (err) { 26 | return console.error(err); 27 | } 28 | 29 | console.log('Successfully wrote new JavaScript files'); 30 | }); 31 | 32 | // promise 33 | rcs.process.js('**/*.js', options) 34 | .then(() => console.log('Successfully wrote new JavaScript files')) 35 | .catch(console.error); 36 | 37 | // async/await 38 | (async () => { 39 | try { 40 | await rcs.process.js('**/*.js', options); 41 | 42 | console.log('Successfully wrote new JavaScript files'); 43 | } catch (err) { 44 | console.error(err); 45 | } 46 | })(); 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/api/processPug.md: -------------------------------------------------------------------------------- 1 | # rcs.process.pug 2 | 3 | **rcs.process.pug(src[, options][, callback])** 4 | 5 | > **Important!** process.css should run first, otherwise there are no minified selectors 6 | 7 | Sync: `process.pugSync` 8 | 9 | Parameters: 10 | - src `` 11 | - options `` *optional* 12 | - callback `` *optional* 13 | 14 | Options: 15 | 16 | - *all options of [rcs.process.html](processHtml.md)* 17 | 18 | Example: 19 | 20 | ```js 21 | const rcs = require('rename-css-selectors'); 22 | 23 | // callback 24 | rcs.process.pug('**/*.pug', options, (err) => { 25 | if (err) { 26 | return console.error(err); 27 | } 28 | 29 | console.log('Successfully wrote new pug files'); 30 | }); 31 | 32 | // promise 33 | rcs.process.pug('**/*.pug', options) 34 | .then(() => console.log('Successfully wrote new pug files')) 35 | .catch(console.error); 36 | 37 | // async/await 38 | (async () => { 39 | try { 40 | await rcs.process.pug('**/*.pug', options); 41 | 42 | console.log('Successfully wrote new pug files'); 43 | } catch (err) { 44 | console.error(err); 45 | } 46 | })(); 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/caveats.md: -------------------------------------------------------------------------------- 1 | # Using rename-css-selectors correctly and patterns to avoid 2 | 3 | `rename-css-selectors` is using `rcs-core` to perform its CSS selectors optimization. 4 | The core is performing three steps on your files: 5 | 1. Extracting CSS's class and id selectors, keyframes, variables and attributes selectors 6 | 2. Assigning minified value for each entry it has found 7 | 3. Trying to replace all elements that could match in your files with the minified version 8 | 9 | ## Caveats for step 1 10 | 11 | > **tl;dr:** Selectors are just renamed if they appear in your CSS 12 | 13 | Currently, the first step is done on CSS syntax only (either from CSS files or extracted from HTML's ` 44 | 45 |
46 | Yeah 47 |
48 | 49 | 54 | ``` 55 | Here, the *key* `'b'` in the JavaScript code can refer to the tag name `` (when being called by `document.querySelector`) or to the class `.b` (when being called by `document.getElementsByClassName`. 56 | The core does not run your code (it only parses it) and, as such, can't figure out when a variable is being used for the former or latter case. 57 | Please notice that the core can't expect a *class* selection by looking at `getElementsByClassName` since you can be using any library that's abstracting this (like jQuery, React...). 58 | 59 | So, to avoid this caveat, you can either: 60 | * Avoid conflicting class name with potential HTML tag name (don't name your CSS class like `.b` or `.i` or `.span`, ...) 61 | * Avoid using `getElementById`, `getElementsByClassName` in your JavaScript code (and only fallback to `querySelector` and `querySelectorsAll`) 62 | * Reserve some mapping so the core can't use them (check the `config.reserve` array in the documentation about how to do that) 63 | * Exclude any selector that's conflicting with any HTML tag name (check the `config.exclude` array in the documentation about how to do that) 64 | 65 | 66 | ### Warning: 67 | 68 | In the example above, `
` can be what the core is generating for your initial class `something`. So the example above could have been generated from this source: 69 | 70 | ```html 71 | 75 |
76 | Yeah 77 |
78 | 83 | ``` 84 | In that case, the initial code is semantically wrong since the `querySelector` should return `undefined`. The minified CSS selector code will thus return a different result in that case. 85 | 86 | If you had followed the advice above and excluded (or reserved) all potential HTML tag name (see below), then `something` won't be mapped to `b`, but, for example, `t` and the initial code behavior will be preserved. 87 | 88 | Similarly, if your initial code was: 89 | ```js 90 | ... 91 | const a = 'b'; 92 | ... 93 | ``` 94 | then the core will generate a warning telling you that `b` is already used in the renaming map and that you should fix the ambiguity. 95 | 96 | By default all HTML and CSS attributes are excluded, see [here](https://github.com/JPeer264/node-rcs-core/blob/master/lib/helpers/excludeList.ts) 97 | 98 | The incovenient being that such selectors won't be minified (think about `header` for example, it's quite common for a class name) 99 | 100 | ## Caveat for step 3 101 | 102 | > **tl;dr:** Be careful with CSS selectors in JavaScript. Do not use string concatenation with your CSS selectors. 103 | 104 | Replacement is relatively safe for CSS and HTML (again, because such format are explicit enough to avoiding ambiguity). 105 | 106 | Replacements inside JavaScript code is much harder because a string for a selector can be generated from many small parts (like in `const a = '.' + objClass;`), can be used in functions that have different semantics (like `getElementById` and `querySelector`), etc... 107 | 108 | So, here's the way the core is trying to resolve ambiguities (but as seen above, not all ambiguities can be solved): 109 | 110 | 1. ECMAScript template, like in ``const a = `some text ${jsConst}`;`` is parsed like HTML, looking for `class="X"` or `id='Y'` or `for="Z"`. 111 | If you use template code to make your CSS selector from each part, it'll fail: 112 | Don't write ``const myClass = 'something'; const sel = `.${myClass}`;``, but instead `const myClass = '.something'`. 113 | 114 | 2. Each string literal is extracted from your JavaScript code and: 115 | - If it contains some CSS specific selectors chars (like `#` or `.` or ` ` etc...), then it's parsed as a CSS selector. **This is safer and usually gives the expected result.** 116 | - If it does not contain some CSS specific selectors (like a single word), then it's tried as an ID selector first and if not found as a class selector (or not replaced if not found in either one). 117 | **This can lead to the ambiguity shown above.** 118 | 119 | So, to avoid such ambiguity, try to store your class or id with their respective prefix (`.` or `#`) in your code, and rely on `querySelector` et al. 120 | 121 | For example, here are some replacement selected by the library, with these CSS selector: 122 | 123 | ```css 124 | .something {} /* Mapped to .a */ 125 | #test {} /* Mapped to #a */ 126 | .test {} /* Mapped to .b */ 127 | ``` 128 | | Initial code | Replaced code | Reason | 129 | |---|---|---| 130 | |`const a = 'something';`|`const a = 'b';`| The core could not deduce the string literal is a selector, so fallback to searching for any rule with `something`, class had one| 131 | |`const a = 'test';`|`const a = 'a';`| The core could not deduce the string literal is a selector and in fallback to search for any rule, it selected #test=>#a since it was searched first| 132 | |`const a = '.something';`|`const a = '.a';`| The core deduced a class selector was required and replaced it| 133 | |`const a = '.test';`|`const a = '.b';`| The core deduced a class selector was required and replaced it| 134 | |`const a = ' something';`|`const a = ' something';`| The core deduced the string literal is a CSS selector, but could not find any match| 135 | |`const a = 'div[class=something]';`|`const a = 'div[class=a]';`| The core deduced the attribute selector was related to a class selector and replaced it| 136 | |`const a = 'input[name=something]';`|`const a = 'input[name=something]';`| The core deduced the attribute selector wasn't related to a class, id or for attribute, no replacement was done| 137 | |`const a = 'b #test, .something';`|`const a = 'b #a, .a';`| The core parsed all selectors and produced correct replacement for each of them| 138 | 139 | ### Another remark: 140 | Some are writing CSS selector this way: 141 | ```css 142 | div[class = something] 143 | { 144 | color: red; 145 | } 146 | ``` 147 | This is perfectly safe from JavaScript code (if you have `const a = 'div[class=something]';`), then this will be replaced correctly by the core (*to* `const a = 'div[class=b]';`). 148 | 149 | This however requires much more work from the developper so this is not a solution we recommend. 150 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'json-extra'; 2 | 3 | declare type RCSError = null | { message: string; error?: string }; 4 | 5 | declare type Callback = ( 6 | err: null | { message: string; error?: string } | NodeJS.ErrnoException, successMessage?: string 7 | ) => void; 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | transform: { 4 | '^.+\\.(t|j)sx?$': 'ts-jest', 5 | }, 6 | testPathIgnorePatterns: ['/node_modules/', '/__tests__/files/', '/__tests__/helpers/', '/test/'], 7 | setupFilesAfterEnv: ['./jest.setup.js'], 8 | globals: { 9 | 'ts-jest': { 10 | isolatedModules: true, 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | jest.setTimeout(30000); 2 | -------------------------------------------------------------------------------- /lib/Config.ts: -------------------------------------------------------------------------------- 1 | import rcs from 'rcs-core'; 2 | import path from 'path'; 3 | import json from 'json-extra'; 4 | import minimatch from 'minimatch'; 5 | 6 | class Config { 7 | private static singleton: Config; 8 | 9 | ignorePatterns: string[] = []; 10 | 11 | public load(pathString = path.join(process.cwd(), '.rcsrc')): void { 12 | let configObject; 13 | 14 | configObject = json.readToObjSync(pathString); 15 | 16 | if (!configObject) { 17 | // package.json .rcs if no other config is found 18 | configObject = json.readToObjSync(path.join(process.cwd(), 'package.json')).rcs; 19 | } 20 | 21 | if (configObject.exclude) { 22 | rcs.selectorsLibrary.setExclude(configObject.exclude); 23 | } 24 | 25 | if (configObject.reserve) { 26 | rcs.selectorsLibrary.setReserved(configObject.reserve); 27 | } 28 | 29 | if (configObject.ignore) { 30 | this.ignorePatterns = configObject.ignore; 31 | } 32 | } 33 | 34 | public isIgnored(filePath: string): boolean { 35 | return this.ignorePatterns.some((pattern) => minimatch(filePath, pattern)); 36 | } 37 | 38 | public static getInstance(): Config { 39 | if (!this.singleton) { 40 | this.singleton = new this(); 41 | } 42 | 43 | return this.singleton; 44 | } 45 | } 46 | 47 | export default Config; 48 | -------------------------------------------------------------------------------- /lib/helper/save.ts: -------------------------------------------------------------------------------- 1 | import { fromPromise } from 'universalify'; 2 | import fs from 'fs-extra'; 3 | import path from 'path'; 4 | 5 | interface SaveOptions { 6 | overwrite?: boolean; 7 | } 8 | 9 | const save = async ( 10 | destinationPath: string, 11 | data: string, 12 | options: SaveOptions = {}, 13 | ): Promise => { 14 | // @todo check if the filepath has an .ext 15 | if (!options.overwrite && fs.existsSync(destinationPath)) { 16 | throw new Error('File exist and cannot be overwritten. Set the option overwrite to true to overwrite files.'); 17 | } 18 | 19 | await fs.mkdirs(path.dirname(destinationPath)); 20 | await fs.writeFile(destinationPath, data); 21 | }; // /save 22 | 23 | export default fromPromise(save); 24 | -------------------------------------------------------------------------------- /lib/helper/saveSync.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | import path from 'path'; 3 | 4 | const saveSync = ( 5 | destinationPath: string, 6 | data: string, 7 | options: { overwrite?: boolean } = {}, 8 | ): void => { 9 | if (!options.overwrite && fs.existsSync(destinationPath)) { 10 | throw new Error('File exist and cannot be overwritten. Set the option overwrite to true to overwrite files.'); 11 | } 12 | 13 | fs.mkdirsSync(path.dirname(destinationPath)); 14 | fs.writeFileSync(destinationPath, data); 15 | }; // /saveSync 16 | 17 | export default saveSync; 18 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | // process 2 | import typeChooserSync from './process/typeChooser/typeChooserSync'; 3 | import typeChooser from './process/typeChooser/typeChooser'; 4 | 5 | // mapping 6 | import generateMapping from './mapping/generateMapping'; 7 | import loadMapping from './mapping/loadMapping'; 8 | 9 | // config 10 | import Config from './Config'; 11 | 12 | export default { 13 | process: { 14 | cssSync: typeChooserSync('css'), 15 | css: typeChooser('css'), 16 | jsSync: typeChooserSync('js'), 17 | js: typeChooser('js'), 18 | htmlSync: typeChooserSync('html'), 19 | html: typeChooser('html'), 20 | pugSync: typeChooserSync('pug'), 21 | pug: typeChooser('pug'), 22 | anySync: typeChooserSync('any'), 23 | any: typeChooser('any'), 24 | autoSync: typeChooserSync('auto'), 25 | auto: typeChooser('auto'), 26 | }, 27 | mapping: { 28 | generate: generateMapping, 29 | load: loadMapping, 30 | }, 31 | config: Config.getInstance(), 32 | }; 33 | -------------------------------------------------------------------------------- /lib/mapping/generateMapping.ts: -------------------------------------------------------------------------------- 1 | import { fromPromise } from 'universalify'; 2 | import rcs from 'rcs-core'; 3 | import path from 'path'; 4 | import json from 'json-extra'; 5 | import { GenerateMappingOptions } from 'rcs-core/dest/mapping/generate'; 6 | 7 | import save from '../helper/save'; 8 | 9 | export interface Options extends GenerateMappingOptions { 10 | json?: boolean; 11 | overwrite?: boolean; 12 | fileName?: string; 13 | } 14 | 15 | const generateMapping = async ( 16 | pathString: string, 17 | opts: Options, 18 | ): Promise => { 19 | let fileName = 'renaming_map'; 20 | let fileNameExt = '.json'; 21 | let mappingName = 'CSS_NAME_MAPPING'; 22 | 23 | const options: Options = { 24 | json: true, 25 | origValues: true, 26 | overwrite: false, 27 | ...opts, 28 | }; 29 | 30 | if (!options.origValues) { 31 | options.origValues = false; 32 | mappingName = 'CSS_NAME_MAPPING_MIN'; 33 | fileName = `${fileName}_min`; 34 | } 35 | 36 | if (options.fileName) { 37 | fileName = options.fileName; 38 | mappingName = options.fileName; 39 | } 40 | 41 | const cssMappingArray = rcs.mapping.generate(opts); 42 | 43 | let mapping = json.stringify(cssMappingArray, null, '\t'); 44 | const newPath = path.join(pathString, fileName); 45 | 46 | // no json 47 | if (!options.json) { 48 | mapping = `var ${mappingName} = ${mapping};`; 49 | fileNameExt = '.js'; 50 | } 51 | 52 | await save(`${newPath}${fileNameExt}`, mapping, { overwrite: options.overwrite }); 53 | }; // /generateMapping 54 | 55 | export default fromPromise(generateMapping); 56 | -------------------------------------------------------------------------------- /lib/mapping/loadMapping.ts: -------------------------------------------------------------------------------- 1 | import rcs from 'rcs-core'; 2 | import fs from 'fs-extra'; 3 | import { LoadMappingOptions, Mapping } from 'rcs-core/dest/mapping/load'; 4 | 5 | async function loadMapping( 6 | pathString: string, 7 | opts?: LoadMappingOptions, 8 | ): Promise; 9 | 10 | function loadMapping( 11 | pathString: Mapping, 12 | opts?: LoadMappingOptions, 13 | ): void; 14 | 15 | async function loadMapping( 16 | pathString: string | Mapping, 17 | opts?: LoadMappingOptions, 18 | ): Promise { 19 | let selectors: Mapping; 20 | 21 | if (typeof pathString === 'string') { 22 | try { 23 | selectors = await fs.readJSON(pathString, { encoding: 'utf8' }); 24 | } catch { 25 | selectors = {}; 26 | } 27 | } else { 28 | selectors = pathString; 29 | } 30 | 31 | rcs.mapping.load(selectors, opts); 32 | } 33 | 34 | export default loadMapping; 35 | -------------------------------------------------------------------------------- /lib/process/defaults.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | availableTypes: ['auto', 'js', 'html', 'pug', 'css', 'any'], 3 | fileExt: { 4 | js: ['.js', '.jsx'], 5 | css: ['.css', '.scss', '.sass', '.less'], 6 | html: ['.html', '.htm', '.handlebars', '.hbs'], 7 | pug: ['.pug'], 8 | }, 9 | optionsDefault: { 10 | type: 'auto', 11 | overwrite: false, 12 | cwd: process.cwd(), 13 | newPath: 'rcs', 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /lib/process/process.ts: -------------------------------------------------------------------------------- 1 | import { fromPromise } from 'universalify'; 2 | import fs from 'fs'; 3 | import rcs from 'rcs-core'; 4 | import path from 'path'; 5 | import glob from 'glob'; 6 | import { eachSeries, each } from 'async'; 7 | import assert from 'assert'; 8 | 9 | import { ReplaceStringOptions } from 'rcs-core/dest/replace/string'; 10 | import { ReplaceHtmlOptions } from 'rcs-core/dest/replace/html'; 11 | import { ReplacePugOptions } from 'rcs-core/dest/replace/pug'; 12 | import { ReplaceCssOptions } from 'rcs-core/dest/replace/css'; 13 | import { ReplaceJsOptions } from 'rcs-core/dest/replace/js'; 14 | import { FillLibrariesOptions as RcsFillLibrariesOptions } from 'rcs-core/dest/fillLibraries'; 15 | 16 | import save from '../helper/save'; 17 | import replaceData from './replaceData'; 18 | import defaults from './defaults'; 19 | import Config from '../Config'; 20 | 21 | const { fileExt, availableTypes, optionsDefault } = defaults; 22 | 23 | export interface BaseOptions { 24 | cwd?: string; 25 | newPath?: string; 26 | overwrite?: boolean; 27 | } 28 | 29 | export interface FillLibrariesOptions extends RcsFillLibrariesOptions { 30 | optimize?: boolean; 31 | } 32 | 33 | export interface AllOptions { 34 | pug: BaseOptions & ReplacePugOptions; 35 | any: BaseOptions & ReplaceStringOptions; 36 | js: BaseOptions & ReplaceJsOptions; 37 | html: BaseOptions & FillLibrariesOptions & ReplaceHtmlOptions; 38 | css: BaseOptions & FillLibrariesOptions & ReplaceCssOptions; 39 | auto: ( 40 | & BaseOptions 41 | & FillLibrariesOptions 42 | & ReplaceCssOptions 43 | & ReplacePugOptions 44 | & ReplaceHtmlOptions 45 | & ReplaceStringOptions 46 | & ReplaceJsOptions 47 | ); 48 | } 49 | 50 | async function rcsProcess(type: 'auto', pathString: string | string[], opts?: AllOptions['auto']): Promise; 51 | async function rcsProcess(type: 'css', pathString: string | string[], opts?: AllOptions['css']): Promise; 52 | async function rcsProcess(type: 'js', pathString: string | string[], opts?: AllOptions['js']): Promise; 53 | async function rcsProcess(type: 'html', pathString: string | string[], opts?: AllOptions['html']): Promise; 54 | async function rcsProcess(type: 'pug', pathString: string | string[], opts?: AllOptions['pug']): Promise; 55 | async function rcsProcess(type: 'any', pathString: string | string[], opts?: AllOptions['any']): Promise; 56 | async function rcsProcess(type: any, pathString: string | string[], opts: any = {}): Promise { 57 | const options = { ...optionsDefault, ...opts }; 58 | const shouldOptimize = options.optimize ?? true; 59 | 60 | let globString: string; 61 | 62 | assert( 63 | availableTypes.includes(type), 64 | `type must be one of the following: ${availableTypes}`, 65 | ); 66 | 67 | if (Array.isArray(pathString)) { 68 | globString = pathString.length > 1 69 | ? `{${pathString.join(',')}}` 70 | : pathString[0]; 71 | } else { 72 | globString = pathString; 73 | } 74 | 75 | const cwd = options.cwd || process.cwd(); 76 | const filesArray: string[] = await new Promise((res, rej) => ( 77 | glob(globString, { cwd, ignore: Config.getInstance().ignorePatterns }, (error, allFiles) => ( 78 | error 79 | ? rej(error) 80 | : res(allFiles) 81 | )) 82 | )); 83 | 84 | // fail if nothing is found 85 | if (filesArray.length <= 0) { 86 | throw new Error('No files found'); 87 | } 88 | 89 | // sort in case of 'auto' 90 | const cssHtmlFiles = filesArray.filter((file) => ( 91 | fileExt.css.includes(path.extname(file)) 92 | || fileExt.html.includes(path.extname(file)) 93 | )); 94 | 95 | const fillLibraryFiles = type === 'auto' 96 | ? cssHtmlFiles 97 | : filesArray; 98 | 99 | // call in series 100 | // not all selectors are stored, maybe some selectors are duplicated in different files 101 | await new Promise((res, rej) => ( 102 | eachSeries(fillLibraryFiles, (filePath, asyncCb) => { 103 | // skip if it is not meant to fill the library 104 | if (type !== 'auto' && type !== 'css' && type !== 'html') { 105 | return asyncCb(); 106 | } 107 | 108 | return fs.readFile(path.join(cwd, filePath), (errReadFile, bufferData) => { 109 | if (errReadFile) { 110 | return asyncCb(errReadFile); 111 | } 112 | 113 | // add here again so typescript gets the correct option types 114 | if (type !== 'auto' && type !== 'css' && type !== 'html') { 115 | return undefined; 116 | } 117 | 118 | const isHtml = fileExt.html.includes(path.extname(filePath)); 119 | 120 | rcs.fillLibraries( 121 | bufferData.toString(), 122 | { 123 | prefix: options.prefix, 124 | suffix: options.suffix, 125 | replaceKeyframes: options.replaceKeyframes, 126 | preventRandomName: options.preventRandomName, 127 | ignoreAttributeSelectors: options.ignoreAttributeSelectors, 128 | ignoreCssVariables: options.ignoreCssVariables, 129 | codeType: isHtml ? 'html' : 'css', 130 | }, 131 | ); 132 | 133 | return asyncCb(); 134 | }); 135 | }, (err) => ( 136 | err ? rej(err) : res() 137 | )) 138 | )); 139 | 140 | if (shouldOptimize) { 141 | rcs.optimize(); 142 | } 143 | 144 | await new Promise((res, rej) => ( 145 | // now all files can be renamed and saved 146 | // can be fired parallel 147 | // all selectors are collected 148 | // ⚡️ speed it up with async/each ⚡️ 149 | each(filesArray, (filePath, asyncEachCb) => { 150 | fs.readFile(path.join(cwd, filePath), 'utf8', (err, bufferData) => { 151 | let data; 152 | let shouldOverwrite = options.overwrite; 153 | 154 | if (err) { 155 | return asyncEachCb(err); 156 | } 157 | 158 | try { 159 | data = replaceData(type, filePath, bufferData, options); 160 | } catch (e) { 161 | return asyncEachCb(e); 162 | } 163 | 164 | const joinedPath = path.join(options.newPath || cwd, filePath); 165 | 166 | if (!options.overwrite) { 167 | shouldOverwrite = joinedPath !== path.join(cwd, filePath); 168 | } 169 | 170 | return save(joinedPath, data, { overwrite: shouldOverwrite }, (errSave: any) => { 171 | if (errSave) { 172 | return asyncEachCb(errSave); 173 | } 174 | 175 | return asyncEachCb(null); 176 | }); 177 | }); 178 | }, (errProcess) => { 179 | rcs.warnings.warn(); 180 | 181 | if (errProcess) { 182 | rej(errProcess); 183 | } else { 184 | res(); 185 | } 186 | }) 187 | )); 188 | } // /process 189 | 190 | export default fromPromise(rcsProcess); 191 | -------------------------------------------------------------------------------- /lib/process/processSync.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import rcs from 'rcs-core'; 3 | import path from 'path'; 4 | import glob from 'glob'; 5 | import assert from 'assert'; 6 | 7 | import saveSync from '../helper/saveSync'; 8 | import replaceData from './replaceData'; 9 | import defaults from './defaults'; 10 | import { AllOptions } from './process'; 11 | import Config from '../Config'; 12 | 13 | const { fileExt, availableTypes, optionsDefault } = defaults; 14 | 15 | /** 16 | * The synchronous method for process 17 | */ 18 | function processSync(type: 'auto', pathString: string | string[], opts?: AllOptions['auto']): void; 19 | function processSync(type: 'css', pathString: string | string[], opts?: AllOptions['css']): void; 20 | function processSync(type: 'js', pathString: string | string[], opts?: AllOptions['js']): void; 21 | function processSync(type: 'html', pathString: string | string[], opts?: AllOptions['html']): void; 22 | function processSync(type: 'pug', pathString: string | string[], opts?: AllOptions['pug']): void; 23 | function processSync(type: 'any', pathString: string | string[], opts?: AllOptions['any']): void; 24 | function processSync(type: any, pathString: string | string[], opts: any = {}): void { 25 | let globString: string; 26 | const options = { ...optionsDefault, ...opts }; 27 | 28 | assert( 29 | availableTypes.includes(type), 30 | `type must be one of the following: ${availableTypes}`, 31 | ); 32 | 33 | if (Array.isArray(pathString)) { 34 | globString = `{${pathString.join(',')}}`; 35 | } else { 36 | globString = pathString; 37 | } 38 | 39 | const globArray = glob.sync(globString, { 40 | cwd: options.cwd, 41 | ignore: Config.getInstance().ignorePatterns, 42 | }); 43 | const cssHtmlFiles = globArray.filter((file) => ( 44 | fileExt.css.includes(path.extname(file)) 45 | || fileExt.html.includes(path.extname(file)) 46 | )); 47 | const fillLibraryFiles = type === 'auto' 48 | ? cssHtmlFiles 49 | : globArray; 50 | 51 | if (type === 'auto' || type === 'css' || type === 'html') { 52 | fillLibraryFiles.forEach((filePath) => { 53 | const fileData = fs.readFileSync(path.join(options.cwd, filePath), 'utf8'); 54 | const isHtml = fileExt.html.includes(path.extname(filePath)); 55 | 56 | rcs.fillLibraries( 57 | fileData.toString(), 58 | { 59 | prefix: options.prefix, 60 | suffix: options.suffix, 61 | replaceKeyframes: options.replaceKeyframes, 62 | preventRandomName: options.preventRandomName, 63 | ignoreAttributeSelectors: options.ignoreAttributeSelectors, 64 | ignoreCssVariables: options.ignoreCssVariables, 65 | codeType: isHtml ? 'html' : 'css', 66 | }, 67 | ); 68 | }); 69 | } 70 | 71 | globArray.forEach((filePath) => { 72 | let shouldOverwrite = options.overwrite; 73 | const joinedPath = path.join(options.newPath, filePath); 74 | 75 | if (!options.overwrite) { 76 | shouldOverwrite = joinedPath !== path.join(options.cwd, filePath); 77 | } 78 | 79 | const fileData = fs.readFileSync(path.join(options.cwd, filePath), 'utf8'); 80 | const data = replaceData(type, filePath, fileData, options); 81 | 82 | saveSync(joinedPath, data, { overwrite: shouldOverwrite }); 83 | }); 84 | 85 | rcs.warnings.warn(); 86 | } // /processSync 87 | 88 | export default processSync; 89 | -------------------------------------------------------------------------------- /lib/process/replaceData.ts: -------------------------------------------------------------------------------- 1 | import rcs from 'rcs-core'; 2 | import path from 'path'; 3 | 4 | import { AllOptions } from './process'; 5 | import defaults from './defaults'; 6 | 7 | function replaceData(type: 'auto', filePath: string, fileData: string, options: AllOptions['auto']): string; 8 | function replaceData(type: 'css', filePath: string, fileData: string, options: AllOptions['css']): string; 9 | function replaceData(type: 'js', filePath: string, fileData: string, options: AllOptions['js']): string; 10 | function replaceData(type: 'html', filePath: string, fileData: string, options: AllOptions['html']): string; 11 | function replaceData(type: 'pug', filePath: string, fileData: string, options: AllOptions['pug']): string; 12 | function replaceData(type: 'any', filePath: string, fileData: string, options: AllOptions['any']): string; 13 | function replaceData(type: any, filePath: string, fileData: string, options: any): string { 14 | let data; 15 | 16 | if ( 17 | type === 'js' 18 | || ( 19 | type === 'auto' 20 | && defaults.fileExt.js.includes(path.extname(filePath)) 21 | ) 22 | ) { 23 | data = rcs.replace.js(fileData, { ...options.espreeOptions, sourceFile: filePath }); 24 | } else if ( 25 | type === 'css' 26 | || ( 27 | type === 'auto' 28 | && defaults.fileExt.css.includes(path.extname(filePath)) 29 | ) 30 | ) { 31 | data = rcs.replace.css(fileData, { ...options, sourceFile: filePath }); 32 | } else if ( 33 | type === 'html' 34 | || ( 35 | type === 'auto' 36 | && defaults.fileExt.html.includes(path.extname(filePath)) 37 | ) 38 | ) { 39 | data = rcs.replace.html(fileData, { ...options, sourceFile: filePath }); 40 | } else if ( 41 | type === 'pug' 42 | || ( 43 | type === 'auto' 44 | && defaults.fileExt.pug.includes(path.extname(filePath)) 45 | ) 46 | ) { 47 | data = rcs.replace.pug(fileData, { ...options, sourceFile: filePath }); 48 | } else { 49 | data = rcs.replace.any(fileData, { ...options, sourceFile: filePath }); 50 | } 51 | 52 | return data; 53 | } 54 | 55 | export default replaceData; 56 | -------------------------------------------------------------------------------- /lib/process/typeChooser/typeChooser.ts: -------------------------------------------------------------------------------- 1 | import rcsProcess, { AllOptions } from '../process'; 2 | 3 | /** 4 | * typeChooser will be obsolete as soon as 5 | * `process.bind(null, 'js')` works with TypeScript method overloads 6 | * https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-2.html#caveats 7 | * "[...] when used on a function with overloads, only the last overload will ever be modeled." 8 | */ 9 | function typeChooser(processType: 'pug'): (pathString: string | string[], opts?: AllOptions['pug']) => Promise; 10 | function typeChooser(processType: 'any'): (pathString: string | string[], opts?: AllOptions['any']) => Promise; 11 | function typeChooser(processType: 'js'): (pathString: string | string[], opts?: AllOptions['js']) => Promise; 12 | function typeChooser(processType: 'html'): (pathString: string | string[], opts?: AllOptions['html']) => Promise; 13 | function typeChooser(processType: 'css'): (pathString: string | string[], opts?: AllOptions['css']) => Promise; 14 | function typeChooser(processType: 'auto'): (pathString: string | string[], opts?: AllOptions['auto']) => Promise; 15 | function typeChooser(processType: any): any { 16 | return (...args: any) => rcsProcess(processType, ...args); 17 | } // /typeChooser 18 | 19 | export default typeChooser; 20 | -------------------------------------------------------------------------------- /lib/process/typeChooser/typeChooserSync.ts: -------------------------------------------------------------------------------- 1 | import rcsProcessSync from '../processSync'; 2 | import { AllOptions } from '../process'; 3 | 4 | function typeChooserSync(processType: 'pug'): (pathString: string | string[], opts?: AllOptions['html']) => void; 5 | function typeChooserSync(processType: 'any'): (pathString: string | string[], opts?: AllOptions['any']) => void; 6 | function typeChooserSync(processType: 'js'): (pathString: string | string[], opts?: AllOptions['js']) => void; 7 | function typeChooserSync(processType: 'html'): (pathString: string | string[], opts?: AllOptions['html']) => void; 8 | function typeChooserSync(processType: 'css'): (pathString: string | string[], opts?: AllOptions['css']) => void; 9 | function typeChooserSync(processType: 'auto'): (pathString: string | string[], opts?: AllOptions['auto']) => void; 10 | function typeChooserSync(processType: any): any { 11 | return (pathString: any, opts: any) => rcsProcessSync(processType, pathString, opts); 12 | } // /typeChooserSync 13 | 14 | export default typeChooserSync; 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rename-css-selectors", 3 | "version": "4.1.0", 4 | "description": "Rename css classes and id's in files", 5 | "main": "dest", 6 | "scripts": { 7 | "test": "jest --coverage", 8 | "build": "tsc && babel dest -d dest", 9 | "lint": "eslint lib/**/*.ts __tests__/**/*.ts", 10 | "fix": "npm run lint -- --fix", 11 | "prepublish": "npm run build" 12 | }, 13 | "husky": { 14 | "hooks": { 15 | "pre-push": "npm test", 16 | "pre-commit": "lint-staged" 17 | } 18 | }, 19 | "lint-staged": { 20 | "*.{js,ts}": [ 21 | "eslint --fix" 22 | ] 23 | }, 24 | "rcs": { 25 | "exclude": [ 26 | "js", 27 | "no-js" 28 | ] 29 | }, 30 | "keywords": [ 31 | "css", 32 | "minify", 33 | "rename", 34 | "selectors", 35 | "shrink", 36 | "classes", 37 | "compress", 38 | "styles", 39 | "munch" 40 | ], 41 | "repository": { 42 | "url": "https://www.github.com/JPeer264/rename-css-selectors", 43 | "type": "git" 44 | }, 45 | "author": "Jan Peer Stöcklmair", 46 | "license": "MIT", 47 | "dependencies": { 48 | "async": "^2.6.3", 49 | "fs-extra": "^8.1.0", 50 | "glob": "^7.1.6", 51 | "json-extra": "^0.5.0", 52 | "lodash.merge": "^4.6.2", 53 | "minimatch": "^3.0.4", 54 | "universalify": "^2.0.0" 55 | }, 56 | "peerDependencies": { 57 | "rcs-core": "^3.*" 58 | }, 59 | "devDependencies": { 60 | "@babel/cli": "^7.14.5", 61 | "@types/async": "^3.0.8", 62 | "@types/fs-extra": "^8.0.1", 63 | "@types/glob": "^7.1.1", 64 | "@types/jest": "^25.1.2", 65 | "@types/lodash.merge": "^4.6.6", 66 | "@types/node": "^13.7.1", 67 | "@types/parse5": "^5.0.2", 68 | "@types/tmp": "^0.1.0", 69 | "@types/universalify": "^0.1.0", 70 | "@typescript-eslint/eslint-plugin": "^2.19.2", 71 | "@typescript-eslint/parser": "^2.19.2", 72 | "babel-plugin-add-module-exports": "^1.0.2", 73 | "eslint": "^6.8.0", 74 | "eslint-config-airbnb-base": "^14.0.0", 75 | "eslint-plugin-import": "^2.20.1", 76 | "html-minifier": "^3.5.20", 77 | "husky": "^4.2.3", 78 | "jest": "^25.1.0", 79 | "lint-staged": "^10.0.7", 80 | "rcs-core": "^3.5.1", 81 | "tmp": "^0.1.0", 82 | "ts-jest": "^25.2.0", 83 | "typescript": "^3.7.5" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "lib": [ 7 | "es2017", 8 | "es2016", 9 | "dom" 10 | ], 11 | "allowJs": true, 12 | "checkJs": false, 13 | "strict": true, 14 | "esModuleInterop": true, 15 | "outDir": "dest", 16 | "rootDir": "lib" 17 | }, 18 | "include": [ 19 | "lib" 20 | ], 21 | "files": [ 22 | "./global.d.ts" 23 | ] 24 | } 25 | --------------------------------------------------------------------------------