├── LICENSE ├── README.md ├── __info.js ├── __run.js └── stuff.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jacob Babich 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 |

🕶 Add SCSS to Svelte

2 | 3 | [![GitHub issues by-label](https://img.shields.io/github/issues/svelte-add/svelte-add/confirmed%20bug?color=%23DC2626)](https://github.com/svelte-add/svelte-add/issues?q=is%3Aopen+is%3Aissue+label%3A%22confirmed+bug%22) 4 | [![GitHub issues by-label](https://img.shields.io/github/issues/svelte-add/svelte-add/support%20question?color=%23FACC15)](https://github.com/svelte-add/svelte-add/issues?q=is%3Aopen+is%3Aissue+label%3A%22support+question%22) 5 | 6 | This is an adder for `svelte-add`; you should [read its `README`](https://github.com/svelte-add/svelte-add#readme) before continuing here. 7 | 8 | ## ➕ Adding SCSS 9 | 10 | This adder's codename is `scss`, and can be used like so: 11 | 12 | ```sh 13 | npx svelte-add@latest scss 14 | ``` 15 | 16 | ### 🏞 Supported environments 17 | 18 | This adder supports SvelteKit and Vite-powered Svelte apps (all the environments `svelte-add` currently supports). 19 | 20 | ### ⚙️ Options 21 | 22 | This adder doesn't take any options of its own. 23 | 24 | ## 🛠 Using SCSS 25 | 26 | After the adder runs, 27 | 28 | - You can write SCSS syntax in the `style lang="scss"` blocks in Svelte files. 29 | 30 | - You can write SCSS syntax in the `src/variables.scss` file. 31 | 32 | Variables and mixins written here are automatically available to all other SCSS files and `style lang="scss"` blocks in Svelte files without needing to import this file. 33 | 34 | - You can write SCSS syntax in the `src/app.scss` file. 35 | 36 | This is your global stylesheet because it will be active on every page of your site. 37 | -------------------------------------------------------------------------------- /__info.js: -------------------------------------------------------------------------------- 1 | import { getViteConfigFilePath } from "../../adder-tools.js"; 2 | import { extension } from "./stuff.js"; 3 | 4 | export const name = "SCSS"; 5 | 6 | export const emoji = "🕶"; 7 | 8 | export const usageMarkdown = ['You can write SCSS syntax in the `style lang="scss"` blocks in Svelte files.', 'You can write SCSS syntax in the `src/variables.scss` file.\n\n Variables and mixins written here are automatically available to all other SCSS files and `style lang="scss"` blocks in Svelte files without needing to import this file.', "You can write SCSS syntax in the `src/app.scss` file.\n\n This is your global stylesheet because it will be active on every page of your site."]; 9 | 10 | /** @type {import("../..").Gatekeep} */ 11 | export const gatekeep = async () => { 12 | return { able: true }; 13 | }; 14 | 15 | /** @typedef {{}} Options */ 16 | 17 | /** @type {import("../..").AdderOptions} */ 18 | export const options = {}; 19 | 20 | /** @type {import("../..").Heuristic[]} */ 21 | export const heuristics = [ 22 | { 23 | description: "`sass` is installed", 24 | async detector({ folderInfo }) { 25 | return "sass" in folderInfo.allDependencies; 26 | }, 27 | }, 28 | { 29 | description: "`vitePreprocess` is set up for SCSS in `svelte.config.js`", 30 | async detector({ readFile }) { 31 | /** @param {string} text */ 32 | const preprocessIsProbablySetup = (text) => { 33 | if (!text.includes("vitePreprocess")) return false; 34 | 35 | return true; 36 | }; 37 | 38 | const js = await readFile({ path: "/svelte.config.js" }); 39 | const cjs = await readFile({ path: "/svelte.config.cjs" }); 40 | 41 | if (js.exists) { 42 | return preprocessIsProbablySetup(js.text); 43 | } else if (cjs.exists) { 44 | return preprocessIsProbablySetup(cjs.text); 45 | } 46 | 47 | return false; 48 | }, 49 | }, 50 | { 51 | description: "`src/app.scss` exists", 52 | async detector({ readFile }) { 53 | const scss = await readFile({ path: "/src/app.scss" }); 54 | 55 | return scss.exists; 56 | }, 57 | }, 58 | { 59 | description: "`src/variables.scss` exists", 60 | async detector({ readFile }) { 61 | const scss = await readFile({ path: "/src/variables.scss" }); 62 | 63 | return scss.exists; 64 | }, 65 | }, 66 | { 67 | description: "Vite is set up to automatically import variables.scss", 68 | async detector({ folderInfo, readFile }) { 69 | /** @param {string} text */ 70 | const preprocessIsProbablySetup = (text) => { 71 | if (!text.includes("additionalData")) return false; 72 | if (!text.includes("@use")) return false; 73 | if (!text.includes("src/variables.scss")) return false; 74 | 75 | return true; 76 | }; 77 | 78 | const vite = await readFile({ path: `/${getViteConfigFilePath(folderInfo)}` }); 79 | 80 | if (preprocessIsProbablySetup(vite.text)) return true; 81 | 82 | return false; 83 | }, 84 | }, 85 | { 86 | description: "The main file (`src/routes/+layout.svelte` for SvelteKit, `src/main.js` or `src/main.ts` for Vite) imports `src/app.scss`", 87 | async detector({ folderInfo, readFile }) { 88 | if (folderInfo.kit) { 89 | const { text } = await readFile({ path: "/src/routes/+layout.svelte" }); 90 | 91 | return text.includes(`../app.${extension}`); 92 | } 93 | 94 | const ts = await readFile({ path: "/src/main.ts" }); 95 | if (ts.exists) return ts.text.includes(`./app.${extension}`); 96 | 97 | const js = await readFile({ path: "/src/main.js" }); 98 | return js.text.includes(`./app.${extension}`); 99 | }, 100 | }, 101 | ]; 102 | -------------------------------------------------------------------------------- /__run.js: -------------------------------------------------------------------------------- 1 | import { Comment } from "postcss"; 2 | import { setupStyleLanguage, updateViteConfig } from "../../adder-tools.js"; 3 | import { setDefault, setPropertyValue } from "../../ast-tools.js"; 4 | import { extension, stylesHint, variablesHint } from "./stuff.js"; 5 | 6 | /** @type {import("../..").AdderRun} */ 7 | export const run = async ({ folderInfo, install, updateCss, updateJavaScript, updateSvelte }) => { 8 | const importVariables = '@use "src/variables.scss" as *;'; 9 | 10 | await setupStyleLanguage({ 11 | extension, 12 | folderInfo, 13 | mutateSveltePreprocessArgs() {}, 14 | stylesHint, 15 | updateCss, 16 | updateJavaScript, 17 | updateSvelte, 18 | }); 19 | 20 | await updateViteConfig({ 21 | mutateViteConfig(viteConfig) { 22 | const cssConfigObject = setDefault({ 23 | object: viteConfig, 24 | default: { 25 | type: "ObjectExpression", 26 | properties: [], 27 | }, 28 | property: "css", 29 | }); 30 | 31 | if (cssConfigObject.type !== "ObjectExpression") throw new Error("css in Vite config must be an object"); 32 | 33 | const preprocessorOptionsConfigObject = setDefault({ 34 | object: cssConfigObject, 35 | default: { 36 | type: "ObjectExpression", 37 | properties: [], 38 | }, 39 | property: "preprocessorOptions", 40 | }); 41 | 42 | if (preprocessorOptionsConfigObject.type !== "ObjectExpression") throw new Error("preprocessorOptions in css in Vite config must be an object"); 43 | 44 | const scssConfigObject = setDefault({ 45 | object: preprocessorOptionsConfigObject, 46 | default: { 47 | type: "ObjectExpression", 48 | properties: [], 49 | }, 50 | property: "scss", 51 | }); 52 | 53 | if (scssConfigObject.type !== "ObjectExpression") throw new Error("scss in preprocessorOptions in css in Vite config must be an object"); 54 | 55 | setPropertyValue({ 56 | object: scssConfigObject, 57 | property: "additionalData", 58 | value: { 59 | type: "Literal", 60 | value: importVariables, 61 | }, 62 | }); 63 | }, 64 | updateJavaScript, 65 | folderInfo, 66 | }); 67 | 68 | await updateCss({ 69 | path: `/src/variables.${extension}`, 70 | async style({ postcss }) { 71 | postcss.prepend( 72 | new Comment({ 73 | text: variablesHint, 74 | }), 75 | ); 76 | 77 | return { 78 | postcss, 79 | }; 80 | }, 81 | }); 82 | 83 | await install({ package: "sass" }); 84 | 85 | if (!folderInfo.kit) await install({ package: "@sveltejs/vite-plugin-svelte" }); 86 | }; 87 | -------------------------------------------------------------------------------- /stuff.js: -------------------------------------------------------------------------------- 1 | export const extension = "scss"; 2 | export const stylesHint = "Write your global styles here, in SCSS syntax. Variables and mixins from the src/variables.scss file are available here without importing"; 3 | export const variablesHint = "Variables and mixins declared here will be available in all other SCSS files"; 4 | --------------------------------------------------------------------------------