├── .babelrc ├── .gitignore ├── .storybook ├── addons.js ├── config.js ├── presets.js └── webpack.config.js ├── README.md ├── package.json ├── public ├── favicon.png ├── global.css └── index.html ├── src ├── App.svelte ├── common │ └── helpers.ts ├── components │ └── button │ │ ├── Button.svelte │ │ ├── Button2.scss │ │ ├── Button2.svelte │ │ └── ButtonData.ts ├── main.ts └── styles │ ├── defaults.scss │ ├── icons.scss │ └── variables.scss ├── stories ├── button.stories.ts └── button2.stories.ts ├── svelte.config.js ├── tsconfig.json ├── webpack.common.js ├── webpack.dev.js ├── webpack.parts.js └── webpack.prod.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-typescript", { "targets": "last 2 versions, ie 11"}] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | package-lock.json 4 | yarn.lock 5 | .idea/ 6 | 7 | build/ 8 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register'; 2 | import '@storybook/addon-links/register'; 3 | import '@storybook/addon-viewport/register'; 4 | import '@storybook/addon-knobs/register'; 5 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/svelte'; 2 | 3 | // automatically import all files ending in *.stories.js 4 | configure(require.context('../stories', true, /\.stories\.[jt]s$/), module); 5 | -------------------------------------------------------------------------------- /.storybook/presets.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | "@storybook/preset-typescript", 3 | { 4 | name: '@storybook/preset-scss', 5 | options: { 6 | cssLoaderOptions: { 7 | sourceMap: true 8 | } 9 | } 10 | }]; 11 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const {aliases, scssAliases, onwarn} = require("../webpack.parts"); 2 | const merge = require('webpack-merge'); 3 | const {CheckerPlugin} = require('awesome-typescript-loader'); 4 | 5 | 6 | module.exports = ({config, mode}) => { 7 | // console.dir(config, { depth: null }); 8 | let mergedConfig = merge.smart(config, { 9 | module: 10 | { 11 | rules: [ 12 | { 13 | test: /\.(svelte|html)$/, 14 | loader: 'svelte-loader', 15 | options: { 16 | onwarn: onwarn, 17 | preprocess: require('svelte-preprocess')({ 18 | scss: { 19 | importer: [ 20 | scssAliases(aliases), 21 | ], 22 | } 23 | }) 24 | } 25 | }, 26 | ] 27 | }, 28 | }); 29 | mergedConfig.resolve.alias = {...mergedConfig.resolve.alias, ...aliases}; 30 | mergedConfig.plugins.push(new CheckerPlugin()); 31 | //console.dir(mergedConfig, {depth: null}); 32 | return mergedConfig; 33 | }; 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Svelte, Typescript, SASS, Storybook, Webpack template 2 | 3 | This project template was based on the [Svelte](https://svelte.dev) webpack template. It lives at https://github.com/sveltejs/template-webpack. 4 | 5 | It was modified to include 6 | - Typescript 7 | - Sass 8 | - Storybook 9 | 10 | To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit): 11 | 12 | ```bash 13 | npx degit nitro52/svelte-typescript-sass-template svelte-app 14 | cd svelte-app 15 | ``` 16 | 17 | *Note that you will need to have [Node.js](https://nodejs.org) installed.* 18 | 19 | 20 | ## Get started 21 | 22 | Install the dependencies... 23 | 24 | ```bash 25 | cd svelte-app 26 | npm install 27 | ``` 28 | 29 | ...then start webpack: 30 | 31 | ```bash 32 | npm run dev 33 | ``` 34 | 35 | Navigate to [localhost:4000](http://localhost:4000). You should see your app running. Edit a component file in `src`, save it, and the page should reload with your changes. 36 | 37 | ### Limitations 38 | #### Typescript/Sass 39 | Typescript and Sass support for `.svelte` files are added via the `svelte-preprocessor`. 40 | It requires using the `lang` or `type` attributes on the `style` and `script` elements. 41 | Within the script block this should work but inside `.svelte` files support varies based on the IDE you use. It may build but your IDE still shows errors 42 | 43 | Importing separate Typescript and Sass files should work fine. 44 | 45 | #### Aliases 46 | Currently to use webpack style aliases during imports they need to be defined in `webpack.config', as a custom sass scssAliases to enable in sass and in tsconfig.json to enable importing in typescript 47 | 48 | I'm currently looking for a way to share this logic but for now you must update aliases in `webpack.common.js` as well as `packages.json` 49 | 50 | ## Storybook 51 | [Storybook](https://storybook.js.org/) has been setup to allow designing components in isolation. There are many Story book [addons](https://storybook.js.org/addons/) that can be added to extend the features 52 | 53 | Run storybook with 54 | 55 | ```bash 56 | npm run storybook 57 | ``` 58 | Navigate to [localhost:4061](http://localhost:4060). You should see your component stories. 59 | 60 | ### Stories 61 | Storybook is configured to load stories from the `stories` folder that end in `.stories.js` or `.stories.ts` 62 | 63 | This project shows 2 button examples, one button uses scss within a svelte file, the other imports a scss file using the src attribute 64 | 65 | ### Removing Storybook 66 | If you don't want storybook remove the following 67 | 68 | **Folders** 69 | - `.storybook` 70 | - `stories` 71 | 72 | **NPM Packages** 73 | ``` 74 | "@storybook/addon-actions": "^5.2.4", 75 | "@storybook/addon-knobs": "^5.2.5", 76 | "@storybook/addon-links": "^5.2.4", 77 | "@storybook/addon-viewport": "^5.2.5", 78 | "@storybook/addons": "^5.2.4", 79 | "@storybook/preset-scss": "^1.0.2", 80 | "@storybook/preset-typescript": "^1.1.0", 81 | "@storybook/svelte": "^5.2.4", 82 | "react-docgen-typescript-loader": "^3.3.0", 83 | ``` 84 | Note - `react-docgen-typescript-loader` is a dependency of the @storybook/preset-typescript and will be removed in a future version 85 | 86 | Lastly remove the `storybook` script from your `packages.json` `scripts` section 87 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-app", 3 | "version": "1.0.0", 4 | "devDependencies": { 5 | "@babel/cli": "^7.7.4", 6 | "@babel/core": "^7.7.4", 7 | "@babel/preset-typescript": "^7.7.4", 8 | "@fortawesome/fontawesome-free": "^5.11.2", 9 | "@pyoner/svelte-types": "^3.4.4-2", 10 | "@storybook/addon-actions": "^5.2.8", 11 | "@storybook/addon-knobs": "^5.2.8", 12 | "@storybook/addon-links": "^5.2.8", 13 | "@storybook/addon-viewport": "^5.2.8", 14 | "@storybook/addons": "^5.2.8", 15 | "@storybook/preset-scss": "^1.0.2", 16 | "@storybook/preset-typescript": "^1.1.0", 17 | "@storybook/svelte": "^5.2.8", 18 | "awesome-typescript-loader": "^5.2.1", 19 | "babel-loader": "^8.0.6", 20 | "clean-webpack-plugin": "^3.0.0", 21 | "copy-webpack-plugin": "^5.0.5", 22 | "css-loader": "^3.2.1", 23 | "mini-css-extract-plugin": "^0.8.0", 24 | "node-sass": "^4.13.0", 25 | "node-sass-magic-importer": "^5.3.2", 26 | "react-docgen-typescript-loader": "^3.6.0", 27 | "sass-loader": "^8.0.0", 28 | "style-loader": "^1.0.1", 29 | "svelte": "^3.16.0", 30 | "svelte-loader": "^2.13.6", 31 | "svelte-preprocess": "^3.2.6", 32 | "ts-loader": "^6.2.1", 33 | "typescript": "^3.7.2", 34 | "webpack": "^4.41.2", 35 | "webpack-cli": "^3.3.10", 36 | "webpack-dev-server": "^3.9.0", 37 | "webpack-merge": "^4.2.2" 38 | }, 39 | "scripts": { 40 | "build": "webpack --config webpack.prod.js", 41 | "dev": "webpack-dev-server --open --config webpack.dev.js", 42 | "storybook": "start-storybook -p 4061" 43 | }, 44 | "dependencies": {} 45 | } 46 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rburnham52/svelte-typescript-sass-template/047757a8e4b9d7df5e76632e9a9516b4f765c042/public/favicon.png -------------------------------------------------------------------------------- /public/global.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | position: relative; 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | body { 8 | color: #333; 9 | margin: 0; 10 | padding: 8px; 11 | box-sizing: border-box; 12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 13 | } 14 | 15 | a { 16 | color: rgb(0,100,200); 17 | text-decoration: none; 18 | } 19 | 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | 24 | a:visited { 25 | color: rgb(0,80,160); 26 | } 27 | 28 | label { 29 | display: block; 30 | } 31 | 32 | input, button, select, textarea { 33 | font-family: inherit; 34 | font-size: inherit; 35 | padding: 0.4em; 36 | margin: 0 0 0.5em 0; 37 | box-sizing: border-box; 38 | border: 1px solid #ccc; 39 | border-radius: 2px; 40 | } 41 | 42 | input:disabled { 43 | color: #ccc; 44 | } 45 | 46 | input[type="range"] { 47 | height: 0; 48 | } 49 | 50 | button { 51 | background-color: #f4f4f4; 52 | outline: none; 53 | } 54 | 55 | button:active { 56 | background-color: #ddd; 57 | } 58 | 59 | button:focus { 60 | border-color: #666; 61 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Svelte app 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/App.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 |

Hello {name}!

15 | 5 | 6 | 14 | 15 | 27 | -------------------------------------------------------------------------------- /src/components/button/Button2.scss: -------------------------------------------------------------------------------- 1 | @import '~@styles/defaults'; 2 | 3 | button.primary { 4 | background-color: $primary-colour1; 5 | color: $lightest; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/button/Button2.svelte: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 20 | -------------------------------------------------------------------------------- /src/components/button/ButtonData.ts: -------------------------------------------------------------------------------- 1 | import {toUpper} from "@common/helpers"; 2 | 3 | 4 | export let label : string = toUpper('World'); 5 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import App from './App.svelte'; 2 | 3 | const app = new App({ 4 | target: document.body, 5 | props: { 6 | name: 'world' 7 | } 8 | }); 9 | 10 | declare global { 11 | interface Window { app: any; } 12 | } 13 | window.app = app; 14 | export default app; 15 | -------------------------------------------------------------------------------- /src/styles/defaults.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import "variables"; 3 | @import "icons"; 4 | 5 | button { 6 | background: $primary-colour1; 7 | color: $lightest; 8 | box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12); 9 | box-sizing: border-box; 10 | position: relative; 11 | -webkit-user-select: none; 12 | -moz-user-select: none; 13 | -ms-user-select: none; 14 | user-select: none; 15 | cursor: pointer; 16 | outline: 0; 17 | border: none; 18 | -webkit-tap-highlight-color: transparent; 19 | display: inline-block; 20 | white-space: nowrap; 21 | text-decoration: none; 22 | vertical-align: baseline; 23 | text-align: center; 24 | margin: 0; 25 | min-width: 64px; 26 | line-height: 36px; 27 | padding: 0 0.25rem; 28 | border-radius: 4px; 29 | overflow: visible; 30 | transform: translate3d(0,0,0); 31 | transition: background .4s cubic-bezier(.25,.8,.25,1),box-shadow 280ms cubic-bezier(.4,0,.2,1); 32 | } 33 | 34 | button:hover { 35 | transition: background .4s cubic-bezier(.25,.8,.25,1),box-shadow 280ms cubic-bezier(.4,0,.2,1); 36 | background: lighten($primary-colour1, 15%); 37 | } 38 | -------------------------------------------------------------------------------- /src/styles/icons.scss: -------------------------------------------------------------------------------- 1 | // setup font awesome - must be before imports 2 | $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; 3 | 4 | @import '~@fortawesome/fontawesome-free/scss/fontawesome'; 5 | @import "~@fortawesome/fontawesome-free/scss/solid"; 6 | @import "~@fortawesome/fontawesome-free/scss/regular"; 7 | @import "~@fortawesome/fontawesome-free/scss/brands"; 8 | 9 | 10 | .icon { 11 | &.ok:after { 12 | @extend %fa-icon; 13 | @extend .fas; 14 | content: fa-content($fa-var-check); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | //Global SASS variables 2 | $lightest: #fff; 3 | $darkest: #000; 4 | 5 | $primary-colour1: #296ac8; 6 | $primary-colour2: #ff3807; 7 | -------------------------------------------------------------------------------- /stories/button.stories.ts: -------------------------------------------------------------------------------- 1 | import { action } from '@storybook/addon-actions'; 2 | 3 | import Button from '../src/components/button/Button.svelte'; 4 | 5 | export default { 6 | title: 'Button', 7 | }; 8 | 9 | export const withSassStyleTag = () => ({ 10 | Component: Button, 11 | props: { text: 'Hello' }, 12 | on: { click: action('clicked') }, 13 | }); 14 | -------------------------------------------------------------------------------- /stories/button2.stories.ts: -------------------------------------------------------------------------------- 1 | import { action } from '@storybook/addon-actions'; 2 | 3 | import Button2 from '@src/components/button/Button2.svelte'; 4 | 5 | export default { 6 | title: 'Button2', 7 | }; 8 | 9 | 10 | export const withSassSource = () => ({ 11 | Component: Button2, 12 | props: { text: 'Hello Button2' }, 13 | on: { click: action('clicked') }, 14 | }); 15 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | const {aliases, scssAliases} = require("./webpack.parts"); 2 | const magicImporter = require('node-sass-magic-importer'); 3 | const sveltePreprocess = require('svelte-preprocess'); 4 | 5 | module.exports = { 6 | preprocess: sveltePreprocess({ 7 | scss: { 8 | importer: [ 9 | //scssAliases(aliases), 10 | magicImporter() 11 | ], 12 | } 13 | }), 14 | }; 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "extendedDiagnostics": true, 4 | "outDir": "build/lib", 5 | "module": "es6", 6 | "target": "es6", 7 | "lib": ["es5", "es6", "es7", "es2017", "dom"], 8 | "sourceMap": true, 9 | "allowJs": false, 10 | "types": ["@pyoner/svelte-types"], 11 | "moduleResolution": "node", 12 | "rootDirs": ["src", "stories"], 13 | "baseUrl": "src", 14 | "paths": { 15 | "@styles/*": ["styles/*"], 16 | "@common/*": ["common/*"] 17 | }, 18 | "forceConsistentCasingInFileNames": true, 19 | "noImplicitReturns": true, 20 | "noImplicitThis": true, 21 | "noImplicitAny": false, 22 | "strict": true, 23 | "strictNullChecks": true, 24 | "suppressImplicitAnyIndexErrors": true, 25 | "noUnusedLocals": false, 26 | "allowSyntheticDefaultImports": true, 27 | "esModuleInterop": true, 28 | "experimentalDecorators": true, 29 | "emitDecoratorMetadata": true 30 | }, 31 | "awesomeTypescriptLoaderOptions": { 32 | "useBabel": true, 33 | "babelCore": "@babel/core" 34 | }, 35 | "include": ["src/**/*"], 36 | "exclude": ["node_modules", "build"] 37 | } 38 | -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | const {aliases, scssAliases, onwarn} = require( "./webpack.parts"); 2 | 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | const {CheckerPlugin} = require('awesome-typescript-loader'); 5 | const CopyPlugin = require('copy-webpack-plugin'); 6 | const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 7 | const path = require('path'); 8 | const magicImporter = require('node-sass-magic-importer'); 9 | 10 | module.exports = { 11 | entry: { 12 | bundle: ['./src/main.ts'] 13 | }, 14 | resolve: { 15 | alias: aliases, 16 | extensions: ['.mjs', '.ts', '.tsx', '.js', '.svelte', 'scss', 'css'], 17 | mainFields: ['svelte', 'browser', 'module', 'main'], 18 | modules: [path.resolve(__dirname, 'src'), 'node_modules'] 19 | }, 20 | output: { 21 | path: path.resolve(__dirname, 'build'), 22 | filename: '[name].js', 23 | chunkFilename: '[name].[id].js' 24 | }, 25 | module: { 26 | rules: [ 27 | { 28 | test: /\.(ts|tsx)$/, 29 | use: [{loader: require.resolve('awesome-typescript-loader')}], 30 | }, 31 | { 32 | test: /\.svelte$/, 33 | use: { 34 | loader: 'svelte-loader', 35 | options: { 36 | emitCss: true, 37 | hotReload: true, 38 | onwarn: onwarn, 39 | preprocess: require('svelte-preprocess')({ 40 | scss: { 41 | importer: [ 42 | scssAliases(aliases), 43 | //magicImporter() 44 | ], 45 | } 46 | }) 47 | } 48 | } 49 | }, 50 | { 51 | test: /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/, 52 | loader: 'file-loader', 53 | }, 54 | /*Style rules are customised in separate dev/prod configs*/ 55 | ] 56 | }, 57 | plugins: [ 58 | new CleanWebpackPlugin(), 59 | new MiniCssExtractPlugin({ 60 | filename: '[name].css' 61 | }), 62 | new CheckerPlugin(), 63 | new CopyPlugin([ 64 | {from: 'public'} 65 | ]) 66 | ] 67 | }; 68 | -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | const magicImporter = require('node-sass-magic-importer'); 4 | 5 | /** 6 | merge.smart replaces matches 7 | You can debug the output using 8 | console.dir(config, { depth: null }); 9 | ref: https://github.com/survivejs/webpack-merge 10 | */ 11 | 12 | module.exports = merge.smart(common, { 13 | mode: 'development', 14 | devtool: 'inline-source-map', 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.(scss|sass|css)$/, 19 | use: [ 20 | /** 21 | * MiniCssExtractPlugin doesn't support HMR. 22 | * For developing, use 'style-loader' instead. 23 | * */ 24 | 'style-loader', 25 | { loader: 'css-loader', options: { sourceMap: true } }, 26 | { 27 | loader: 'sass-loader', 28 | options: { 29 | sassOptions: { 30 | importer: magicImporter() 31 | } 32 | } 33 | } 34 | ] 35 | } 36 | ] 37 | }, 38 | devServer: { 39 | contentBase: './build', 40 | port: 4000 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /webpack.parts.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | /** 4 | * Disables some sass warnings that are not really warnings 5 | */ 6 | const onwarn = (warning, onwarn) => warning.code === 'css-unused-selector' || onwarn(warning); 7 | 8 | /** 9 | * Custom Sass Importer to enable the use of aliases in sass. 10 | * This will fall back to node_modules if the path starts with ~ and can not be matched to an alias 11 | */ 12 | const scssAliases = aliases => { 13 | return url => { 14 | // console.log('attempting to resolve: '+ url); 15 | // sass-loader normally requires you to add a ~ character to the start of your aliases 16 | if (url.startsWith("~")) { 17 | // we want to remove the ~ character before comparing the url to an alias 18 | this.url = url.slice(1); 19 | for (const [alias, aliasPath] of Object.entries(aliases)) { 20 | if (this.url.indexOf(alias) === 0) { 21 | const filePath = path.resolve(this.url.replace(alias, aliasPath)); 22 | // console.log('found alias: '+ alias + '; at ' + filePath); 23 | return { 24 | file: filePath, 25 | }; 26 | } 27 | } 28 | //If there was nothing found fall back to node_modules 29 | const filePath = path.resolve(process.cwd(), "node_modules", this.url); 30 | // console.log('Attempting to resolve', filePath); 31 | //if we can't find anything fall back to node_modules 32 | return { 33 | file: filePath 34 | }; 35 | } 36 | // console.log('could not match: ' + url); 37 | //if there is no match return null to allow other importers a chance to resolve. 38 | return null; 39 | }; 40 | }; 41 | 42 | /** 43 | * Aliases used during import, shared between webpack and sass-loader 44 | */ 45 | const aliases = { 46 | //TODO: Look at a way to share tsconfig.json paths and these aliases 47 | svelte: path.resolve('node_modules', 'svelte'), 48 | '@src': path.resolve(__dirname, 'src/'), 49 | '@styles': path.resolve(__dirname, 'src/styles/'), 50 | '@common': path.resolve(__dirname, 'src/common/'), 51 | }; 52 | 53 | 54 | module.exports = { scssAliases, aliases, onwarn }; 55 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const {aliases, scssAliases, onwarn} = require("./webpack.parts"); 2 | const merge = require('webpack-merge'); 3 | const common = require('./webpack.common.js'); 4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 5 | const magicImporter = require('node-sass-magic-importer'); 6 | 7 | /** 8 | merge.smart replaces matches 9 | You can debug the output using 10 | console.dir(config, { depth: null }); 11 | ref: https://github.com/survivejs/webpack-merge 12 | */ 13 | let config = merge.smart(common, { 14 | mode: 'production', 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.svelte$/, 19 | use: { 20 | loader: 'svelte-loader', 21 | options: { 22 | emitCss: true, 23 | hotReload: false, 24 | onwarn: onwarn, 25 | preprocess: require('svelte-preprocess')({ 26 | scss: { 27 | importer: [ 28 | // scssAliases(aliases), 29 | magicImporter() 30 | ], 31 | } 32 | }) 33 | } 34 | } 35 | }, 36 | { 37 | test: /\.(scss|sass|css)$/, 38 | use: [ 39 | /** 40 | * MiniCssExtractPlugin doesn't support HMR. 41 | * For developing, use 'style-loader' instead. 42 | * */ 43 | MiniCssExtractPlugin.loader, 44 | { loader: 'css-loader', options: { sourceMap: true } }, 45 | { 46 | loader: 'sass-loader', 47 | options: { 48 | sassOptions: { 49 | importer: magicImporter() 50 | } 51 | } 52 | } 53 | ] 54 | } 55 | ] 56 | }, 57 | }); 58 | //console.dir(config, { depth: null }); 59 | module.exports = config; 60 | --------------------------------------------------------------------------------