├── .gitignore ├── dist ├── main.d.ts ├── classnames-template-literals.esm.js ├── classnames-template-literals.cjs.js └── classnames-template-literals.umd.js ├── src └── main.js ├── test └── test.js ├── rollup.config.js ├── LICENSE ├── .github └── workflows │ └── fossa.yml ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /dist/main.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Format long classnames with template literals 3 | */ 4 | export default function ctl(template: string): string; 5 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | export default function ctl(template) { 2 | var trimmedClassnames = template.replace(/\s+/gm, " "); 3 | var formattedClassnames = trimmedClassnames 4 | .split(" ") 5 | .filter((c) => c !== "false" && c !== "true" && c !== "undefined") 6 | .join(" ") 7 | .trim(); 8 | 9 | return formattedClassnames; 10 | } 11 | -------------------------------------------------------------------------------- /dist/classnames-template-literals.esm.js: -------------------------------------------------------------------------------- 1 | function ctl(template) { 2 | var trimmedClassnames = template.replace(/\s+/gm, " "); 3 | var formattedClassnames = trimmedClassnames 4 | .split(" ") 5 | .filter((c) => c !== "false" && c !== "true" && c !== "undefined") 6 | .join(" ") 7 | .trim(); 8 | 9 | return formattedClassnames; 10 | } 11 | 12 | export default ctl; 13 | -------------------------------------------------------------------------------- /dist/classnames-template-literals.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function ctl(template) { 4 | var trimmedClassnames = template.replace(/\s+/gm, " "); 5 | var formattedClassnames = trimmedClassnames 6 | .split(" ") 7 | .filter((c) => c !== "false" && c !== "true" && c !== "undefined") 8 | .join(" ") 9 | .trim(); 10 | 11 | return formattedClassnames; 12 | } 13 | 14 | module.exports = ctl; 15 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const ctl = require(".."); 2 | 3 | test("should remove new lines and whitespaces", () => { 4 | const classname = ` 5 | bg-black 6 | text-small 7 | mt-2 8 | `; 9 | expect(ctl(classname)).toBe("bg-black text-small mt-2"); 10 | }); 11 | 12 | test("should handle template literals", () => { 13 | const someTruthyState = true; 14 | const someFalsyState = false; 15 | const classname = ` 16 | bg-black 17 | text-small 18 | mt-2 19 | ${someTruthyState && `mb-3`} 20 | ${someFalsyState && `ml-3`} 21 | `; 22 | expect(ctl(classname)).toBe("bg-black text-small mt-2 mb-3"); 23 | expect(ctl(classname)).not.toBe("bg-black text-small mt-2 mb-3 ml-3"); 24 | }); 25 | -------------------------------------------------------------------------------- /dist/classnames-template-literals.umd.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 | typeof define === 'function' && define.amd ? define(factory) : 4 | (global = global || self, global.howLongUntilLunch = factory()); 5 | }(this, (function () { 'use strict'; 6 | 7 | function ctl(template) { 8 | var trimmedClassnames = template.replace(/\s+/gm, " "); 9 | var formattedClassnames = trimmedClassnames 10 | .split(" ") 11 | .filter((c) => c !== "false" && c !== "true" && c !== "undefined") 12 | .join(" ") 13 | .trim(); 14 | 15 | return formattedClassnames; 16 | } 17 | 18 | return ctl; 19 | 20 | }))); 21 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import pkg from './package.json'; 4 | 5 | export default [ 6 | // browser-friendly UMD build 7 | { 8 | input: 'src/main.js', 9 | output: { 10 | name: 'howLongUntilLunch', 11 | file: pkg.browser, 12 | format: 'umd' 13 | }, 14 | plugins: [ 15 | resolve(), // so Rollup can find `ms` 16 | commonjs() // so Rollup can convert `ms` to an ES module 17 | ] 18 | }, 19 | 20 | // CommonJS (for Node) and ES module (for bundlers) build. 21 | // (We could have three entries in the configuration array 22 | // instead of two, but it's quicker to generate multiple 23 | // builds from a single configuration where possible, using 24 | // an array for the `output` option, where we can specify 25 | // `file` and `format` for each target) 26 | { 27 | input: 'src/main.js', 28 | external: ['ms'], 29 | output: [ 30 | { file: pkg.main, format: 'cjs' }, 31 | { file: pkg.module, format: 'es' } 32 | ] 33 | } 34 | ]; 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 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. -------------------------------------------------------------------------------- /.github/workflows/fossa.yml: -------------------------------------------------------------------------------- 1 | name: Dependency License Scanning 2 | 3 | on: 4 | push: 5 | branches: 6 | - chore/fossa-workflow 7 | - main 8 | 9 | defaults: 10 | run: 11 | shell: bash 12 | 13 | jobs: 14 | fossa: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v2 19 | - name: Download fossa cli 20 | run: |- 21 | curl -L https://github.com/fossas/fossa-cli/releases/download/v1.1.2/fossa-cli_1.1.2_linux_amd64.tar.gz > fossa-cli.tar.gz 22 | tar -xvzf fossa-cli.tar.gz 23 | mkdir -p $HOME/.local/bin 24 | echo "$HOME/.local/bin" >> $GITHUB_PATH 25 | mv fossa $HOME/.local/bin/fossa 26 | - name: Fossa init 27 | run: fossa init 28 | - name: Set env 29 | run: echo "line_number=$(grep -n "project" .fossa.yml | cut -f1 -d:)" >> $GITHUB_ENV 30 | - name: Configuration 31 | run: |- 32 | sed -i "${line_number}s|.*| project: git@github.com:${GITHUB_REPOSITORY}.git|" .fossa.yml 33 | cat .fossa.yml 34 | - name: Upload dependencies 35 | run: fossa analyze --debug 36 | env: 37 | FOSSA_API_KEY: ${{ secrets.FOSSA_API_KEY }} 38 | 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@netlify/classnames-template-literals", 3 | "description": "Small utility to format long classnames with template literals", 4 | "version": "1.0.3", 5 | "author": "Netlify", 6 | "contributors": [ 7 | "Charlie Gerard (https://charliegerard.dev)" 8 | ], 9 | "bugs": { 10 | "url": "https://github.com/netlify/classnames-template-literals/issues" 11 | }, 12 | "main": "dist/classnames-template-literals.cjs.js", 13 | "module": "dist/classnames-template-literals.esm.js", 14 | "browser": "dist/classnames-template-literals.umd.js", 15 | "typings": "dist/main.d.ts", 16 | "devDependencies": { 17 | "@rollup/plugin-commonjs": "^11.0.1", 18 | "@rollup/plugin-node-resolve": "^7.0.0", 19 | "jest": "^26.6.3", 20 | "rollup": "^1.29.0" 21 | }, 22 | "scripts": { 23 | "build": "rollup -c", 24 | "dev": "rollup -c -w", 25 | "test": "jest", 26 | "pretest": "npm run build" 27 | }, 28 | "homepage": "https://github.com/netlify/classnames-template-literals#readme", 29 | "keywords": [ 30 | "CSS", 31 | "netlify", 32 | "Tailwind" 33 | ], 34 | "license": "MIT", 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/netlify/classnames-template-literals.git" 38 | }, 39 | "files": [ 40 | "dist" 41 | ], 42 | "dependencies": {} 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # classnames-template-literals 2 | 3 | Utility originally created to deal with long Tailwind classnames. 4 | 5 | To make our code more readable, we extract our Tailwind classes into variables, for example: 6 | 7 | ```javascript 8 | const twClasses = ` 9 | tw-sr-only 10 | focus:tw-not-sr-only 11 | tw-bg-teal-darker 12 | tw-text-white 13 | tw-block 14 | tw-text-sm 15 | tw-l-0 16 | `; 17 | ``` 18 | 19 | The issue with writing classes this way is that they are rendered as-is in the DOM. 20 | 21 | Example: 22 | 23 | ```javascript 24 |

34 | Hello world 35 |

36 | ``` 37 | 38 | Using the `ctl` util formats classnames so they are rendered in a more standard way in the DOM. 39 | 40 | Example: 41 | 42 | ```javascript 43 |

44 | Hello world 45 |

46 | ``` 47 | 48 | ## Installation 49 | 50 | ```javascript 51 | npm install @netlify/classnames-template-literals 52 | ``` 53 | 54 | ## Usage 55 | 56 | Wrap your classnames inside `ctl`. 57 | 58 | ```javascript 59 | import ctl from "@netlify/classnames-template-literals"; 60 | 61 | const buttonClasses = ctl(` 62 | bg-black 63 | text-white 64 | p-1 65 | rounded-sm 66 | `); 67 | ``` 68 | 69 | You can also use conditional classes: 70 | 71 | ```javascript 72 | import ctl from "@netlify/classnames-template-literals"; 73 | 74 | const buttonClasses = ctl(` 75 | bg-black 76 | text-white 77 | p-1 78 | rounded-sm 79 | ${someState && "bg-pink"} 80 | `); 81 | ``` 82 | --------------------------------------------------------------------------------