├── config
├── environment
│ ├── .env
│ ├── .env.development
│ └── .env.production
└── webpack
│ ├── addons
│ ├── webpack.bundlevisualizer.js
│ └── webpack.bundleanalyze.js
│ ├── webpack.common.js
│ ├── paths.js
│ ├── webpack.development.js
│ ├── webpack.production.js
│ └── rules.js
├── src
├── images
│ └── favicon.png
├── view
│ ├── components
│ │ ├── page-layout
│ │ │ ├── styles.scss
│ │ │ └── index.js
│ │ ├── splash-loader
│ │ │ ├── index.js
│ │ │ └── styles.scss
│ │ ├── maintenance-layout
│ │ │ ├── styles.scss
│ │ │ └── index.js
│ │ └── sample-component
│ │ │ └── index.js
│ ├── pages
│ │ └── landing-page.js
│ └── routes.js
├── index.html
├── app.scss
└── index.js
├── prettier.config.js
├── .gitignore
├── _config.yml
├── babel.config.js
├── webpack.config.js
├── .eslintrc.js
├── LICENSE
├── package.json
└── README.md
/config/environment/.env:
--------------------------------------------------------------------------------
1 | BASE_URL=http://localhost.base.com
--------------------------------------------------------------------------------
/config/environment/.env.development:
--------------------------------------------------------------------------------
1 | BASE_URL=http://localhost.dev.com
2 | BASE_URL_EXPAND=$BASE_URL/expanded
--------------------------------------------------------------------------------
/config/environment/.env.production:
--------------------------------------------------------------------------------
1 | BASE_URL=http://localhost.prod.com
2 | BASE_URL_EXPAND=$BASE_URL/expanded
--------------------------------------------------------------------------------
/src/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dhinesh03/mithril-starter-kit/HEAD/src/images/favicon.png
--------------------------------------------------------------------------------
/src/view/components/page-layout/styles.scss:
--------------------------------------------------------------------------------
1 | .page-layout {
2 | max-width: 800px;
3 | margin: 1em auto;
4 | }
--------------------------------------------------------------------------------
/config/webpack/addons/webpack.bundlevisualizer.js:
--------------------------------------------------------------------------------
1 | const Visualizer = require('webpack-visualizer-plugin');
2 |
3 | module.exports = {
4 | plugins: [
5 | new Visualizer()
6 | ]
7 | };
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 110,
3 | singleQuote: true,
4 | tabWidth: 4,
5 | arrowParens: 'always',
6 | semi: true,
7 | trailingComma: 'es5',
8 | };
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 |
4 | # dependencies
5 | /node_modules
6 | /bower_components
7 |
8 | # misc
9 | .sass-cache
10 | .eslintcache
11 | npm-debug.log*
12 |
13 | # mac
14 | .DS_Store
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Mithril Starter Kit
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
2 | show_downloads: true
3 | title: Mithril Starter Kit
4 | description: A boilerplate Mithril application using ES6, Babel, Webpack 3, Sass/SCSS, Webpack dev server hot reload and eslint
5 | author: Dhinesh Kumar
6 | logo: /src/styles/images/favicon.png
7 | plugins:
8 | - jekyll-seo-tag
9 |
--------------------------------------------------------------------------------
/src/view/components/page-layout/index.js:
--------------------------------------------------------------------------------
1 | import './styles.scss';
2 |
3 | /**
4 | * A component that wraps another component with some common
5 | * page layout markup and styles.
6 | */
7 | export default function() {
8 | return {
9 | view: vnode => m('.page-layout', vnode.children),
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/config/webpack/addons/webpack.bundleanalyze.js:
--------------------------------------------------------------------------------
1 | const WebpackBundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
2 | const paths = require('../paths');
3 | module.exports = {
4 | plugins: [
5 | new WebpackBundleAnalyzer({
6 | analyzerMode: 'static',
7 | reportFilename: paths.bundleReportPath,
8 | openAnalyzer: false
9 | })
10 | ]
11 | };
--------------------------------------------------------------------------------
/src/app.scss:
--------------------------------------------------------------------------------
1 | @import-normalize;/* bring in normalize.css styles */
2 |
3 | html, body, #root {
4 | position: relative;
5 | margin: 0;
6 | width: 100%;
7 | height: 100%;
8 | }
9 |
10 | body, #root {
11 | font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
12 | font-size: 16px;
13 | line-height: 1.4;
14 | color: #333;
15 | background-color: #FFFFFF;
16 | overflow-x: hidden;
17 | }
18 |
--------------------------------------------------------------------------------
/src/view/components/splash-loader/index.js:
--------------------------------------------------------------------------------
1 | import './styles.scss';
2 |
3 | export default {
4 | view(/*vnode*/) {
5 | return m('.holder', [
6 | m('.preloader', [
7 | m('div'),
8 | m('div'),
9 | m('div'),
10 | m('div'),
11 | m('div'),
12 | m('div'),
13 | m('div'),
14 | ]),
15 | ]);
16 | },
17 | };
--------------------------------------------------------------------------------
/src/view/components/maintenance-layout/styles.scss:
--------------------------------------------------------------------------------
1 | .maintenance {
2 | text-align: center;
3 | padding: 150px;
4 | font: 20px Helvetica, sans-serif;
5 | color: #333;
6 | h1 {
7 | font-size: 50px;
8 | }
9 | article {
10 | display: block;
11 | text-align: left;
12 | width: 650px;
13 | margin: 0 auto;
14 | }
15 | a {
16 | color: #dc8100;
17 | text-decoration: none;
18 | }
19 | a:hover {
20 | color: #333;
21 | text-decoration: none;
22 | }
23 | }
--------------------------------------------------------------------------------
/src/view/components/sample-component/index.js:
--------------------------------------------------------------------------------
1 | export default function() {
2 | var count = 0; // added a variable
3 | return {
4 | view: function() {
5 | return m('main', [
6 | m('h1', {
7 | class: 'title',
8 | }, 'My first component'),
9 | // changed the next line
10 | m('button', {
11 | onclick: function() {
12 | count++;
13 | },
14 | }, count + ' clicks'),
15 | ]);
16 | },
17 | };
18 | }
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(false);
3 | const presets = [
4 | [
5 | '@babel/preset-env',
6 | {
7 | useBuiltIns: 'entry',
8 | corejs: { version: '3.3' }
9 | //debug: true
10 | }
11 | ]
12 | ];
13 |
14 | let plugins = [
15 | [
16 | 'transform-react-jsx',
17 | {
18 | pragma: 'm'
19 | }
20 | ],
21 | '@babel/plugin-proposal-class-properties',
22 | '@babel/plugin-transform-runtime'
23 | ];
24 | return {
25 | presets,
26 | plugins
27 | };
28 | };
29 |
--------------------------------------------------------------------------------
/src/view/components/maintenance-layout/index.js:
--------------------------------------------------------------------------------
1 | import './styles.scss';
2 |
3 | /**
4 | * A component that will show the maintenance banner.
5 | */
6 | export default function() {
7 | return {
8 | view: () => {
9 | return m('.maintenance', m('article', [
10 | m('h1', 'We\'ll be back soon!'),
11 | m('div',[
12 | m('p', m.trust('Sorry for the inconvenience but we\'re performing some maintenance at the moment. If you need to you can always contact us, otherwise we\'ll be back online shortly!')),
13 | m('p', '- The Team'),
14 | ]),
15 | ]));
16 | },
17 | };
18 | }
--------------------------------------------------------------------------------
/src/view/pages/landing-page.js:
--------------------------------------------------------------------------------
1 | import SampleComponent from '../components/sample-component';
2 |
3 | export default function() {
4 | return {
5 | view() {
6 | /* return m('div', [
7 | m('h2', 'Congratulations, you made it!'),
8 | m('p', 'You\'ve spun up your very first Mithril app :-)'),
9 | m(SampleComponent),
10 | ]); */
11 | return (
12 |
13 |
Congratulations, you made it!
14 |
You've spun up your very first Mithril app :-)
15 |
16 |
17 | );
18 | },
19 | };
20 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import 'core-js/stable';
2 | import 'regenerator-runtime/runtime';
3 |
4 | /* Import all vendor.scss / css here[e.g. Import 'font-awesome/scss/font-awesome.scss';] */
5 | import { Routes, DefaultRoute } from './view/routes';
6 |
7 | /* Include global app styles here, so that it will over ride component's css styles*/
8 | import './app.scss';
9 |
10 | if (module.hot) {
11 | module.hot.accept();
12 | }
13 |
14 | if (process.env.NODE_ENV !== 'production') {
15 | console.log('Looks like we are in development mode!');
16 | }
17 |
18 | console.log('Env var test ===>', process.env.BASE_URL, process.env.BASE_URL_EXPAND);
19 | // Wire up mithril app to DOM
20 | const $root = document.body.querySelector('#root');
21 | m.route($root, DefaultRoute, Routes);
22 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpackMerge = require('webpack-merge');
2 | const common = require('./config/webpack/webpack.common');
3 | /* eslint-disable global-require,import/no-dynamic-require */
4 | const env = process.env.NODE_ENV || 'development';
5 | const envConfig = require(`./config/webpack/webpack.${env}`);
6 |
7 | const getAddons = addonsArgs => {
8 | const addons = Array.isArray(addonsArgs) ? addonsArgs : [addonsArgs];
9 | return addons
10 | .filter(Boolean)
11 | .map(name => require(`./config/webpack/addons/webpack.${name}.js`));
12 | };
13 |
14 | module.exports = ({ addon } = {}) => {
15 | console.log('process.env.NODE_ENV===>', env, 'addon ==>', addon);
16 | return webpackMerge(common, envConfig, ...getAddons(addon));
17 | };
18 |
--------------------------------------------------------------------------------
/config/webpack/webpack.common.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 | const paths = require('./paths');
4 | const rules = require('./rules');
5 |
6 | module.exports = {
7 | context: paths.contextPath,
8 | entry: {
9 | main: paths.entryPath,
10 | },
11 | module: {
12 | rules,
13 | },
14 | resolve: {
15 | modules: ['src', 'node_modules'],
16 | extensions: ['*', '.js', '.scss', '.css'],
17 | },
18 | plugins: [
19 | new HtmlWebpackPlugin({
20 | template: paths.templatePath,
21 | favicon: paths.faviconPath,
22 | }),
23 | new webpack.ProvidePlugin({
24 | m: 'mithril', //Global access
25 | }),
26 | ],
27 | };
28 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | commonjs: true,
5 | es6: true,
6 | node: true,
7 | },
8 | globals: {
9 | 'm': true
10 | },
11 | extends: [
12 | 'eslint:recommended',
13 | 'plugin:mithril/recommended'
14 | ],
15 | parserOptions: {
16 | sourceType: 'module',
17 | ecmaVersion: 9,
18 | ecmaFeatures: {
19 | jsx: true
20 | }
21 | },
22 | rules: {
23 | 'comma-dangle': ['error', 'always-multiline'],
24 | 'indent': ['error', 4],
25 | 'linebreak-style': ['error', 'unix'],
26 | 'quotes': ['error', 'single'],
27 | 'semi': ['error', 'always'],
28 | 'no-unused-vars': ['warn'],
29 | 'no-console': 0,
30 | 'rest-spread-spacing': ["error", "never"],
31 | },
32 | };
--------------------------------------------------------------------------------
/config/webpack/paths.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path');
2 |
3 | const contextPath = resolve(__dirname, '../', '../', 'src');
4 | const outputPath = resolve(__dirname, '../', '../', 'dist');
5 | const bundleReportPath = resolve(outputPath, 'report.html');
6 | const entryPath = resolve(contextPath, 'index.js');
7 | const templatePath = resolve(contextPath, 'index.html');
8 | const envDevPath = resolve(__dirname, '../', 'environment', '.env.development');
9 | const envProdPath = resolve(__dirname, '../', 'environment', '.env.production');
10 | const envPath = resolve(__dirname, '../', 'environment', '.env');
11 | const eslintConfigPath = resolve(__dirname, '../', '../', '.eslintrc.js');
12 | const nodeModulesPath = resolve(__dirname, '../', '../', 'node_modules');
13 | const faviconPath = resolve(contextPath, 'images', 'favicon.png');
14 |
15 | module.exports = {
16 | contextPath,
17 | outputPath,
18 | bundleReportPath,
19 | entryPath,
20 | templatePath,
21 | envDevPath,
22 | envProdPath,
23 | envPath,
24 | eslintConfigPath,
25 | nodeModulesPath,
26 | faviconPath,
27 | };
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Dhinesh Kumar
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 |
--------------------------------------------------------------------------------
/src/view/routes.js:
--------------------------------------------------------------------------------
1 | import PageLayout from './components/page-layout';
2 |
3 | // Individual pages
4 | import IndexPage from './pages/landing-page';
5 | import Splash from './components/splash-loader/index';
6 | import MaintenancePage from './components/maintenance-layout/index';
7 |
8 | function loadSpinner() {
9 | let $splashDiv = document.getElementById('splash');
10 | if (!$splashDiv) {
11 | $splashDiv = document.createElement('div');
12 | $splashDiv.setAttribute('id', 'splash');
13 | const $root = document.body.querySelector('#root');
14 | $root.appendChild($splashDiv);
15 | }
16 | m.render($splashDiv, m(Splash));
17 | }
18 | function hideSpinner() {
19 | let $splashDiv = document.getElementById('splash');
20 | if ($splashDiv) {
21 | m.render($splashDiv, null);
22 | }
23 | }
24 |
25 | const Routes = {
26 | '/splash': {
27 | render: function() {
28 | return m(PageLayout, m(Splash));
29 | },
30 | },
31 | '/index': {
32 | onmatch() {
33 | // Show Loader until the promise has been resolved or rejected.
34 | loadSpinner();
35 | return new Promise((resolve /*, reject*/) => {
36 | //Fetch all necessary data here
37 | setTimeout(function() {
38 | //m.render($root, null);
39 | resolve();
40 | }, 2000);
41 | }).catch((/* e */) => {
42 | // In case of server error we can show the maintenance page.
43 | return MaintenancePage;
44 | });
45 | },
46 | render(vnode) {
47 | hideSpinner();
48 | if (typeof vnode.tag === 'function') {
49 | //If onmatch returns a component or a promise that resolves to a component, comes here.
50 | return vnode;
51 | }
52 | return m(PageLayout, m(IndexPage));
53 | },
54 | },
55 | };
56 |
57 | const DefaultRoute = '/index';
58 |
59 | export { Routes, DefaultRoute };
60 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mithril-starter-kit",
3 | "version": "2.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js",
8 | "build": "cross-env NODE_ENV=production webpack --progress --config webpack.config.js",
9 | "build:analyze": "npm run build -- --env.addon=bundleanalyze --env.addon=bundlevisualizer",
10 | "lint:js": "eslint src/ webpack.*.js --cache --fix"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "devDependencies": {
16 | "@babel/core": "^7.9.6",
17 | "@babel/plugin-proposal-class-properties": "^7.8.3",
18 | "@babel/plugin-transform-runtime": "^7.9.6",
19 | "@babel/preset-env": "^7.9.6",
20 | "babel-loader": "^8.1.0",
21 | "babel-plugin-transform-react-jsx": "^6.24.1",
22 | "clean-webpack-plugin": "^3.0.0",
23 | "core-js": "^3.6.5",
24 | "cross-env": "^7.0.2",
25 | "css-loader": "^3.5.3",
26 | "dotenv-webpack": "^1.7.0",
27 | "eslint": "^6.8.0",
28 | "eslint-loader": "^4.0.2",
29 | "eslint-plugin-mithril": "^0.1.1",
30 | "file-loader": "^6.0.0",
31 | "html-loader": "^1.1.0",
32 | "html-webpack-plugin": "^4.3.0",
33 | "mini-css-extract-plugin": "^0.9.0",
34 | "node-sass": "^7.0.0",
35 | "optimize-css-assets-webpack-plugin": "^5.0.3",
36 | "postcss-flexbugs-fixes": "^4.2.1",
37 | "postcss-loader": "^3.0.0",
38 | "postcss-normalize": "^9.0.0",
39 | "postcss-preset-env": "^6.7.0",
40 | "prettier": "^2.0.5",
41 | "sass-loader": "^8.0.2",
42 | "terser-webpack-plugin": "^3.0.0",
43 | "url-loader": "^4.1.0",
44 | "webpack": "^4.43.0",
45 | "webpack-bundle-analyzer": "^4.7.0",
46 | "webpack-cli": "^3.3.11",
47 | "webpack-dev-server": "^3.10.3",
48 | "webpack-merge": "^4.2.2",
49 | "webpack-visualizer-plugin": "^0.1.11"
50 | },
51 | "dependencies": {
52 | "@babel/runtime": "^7.9.6",
53 | "mithril": "^2.0.4",
54 | "regenerator-runtime": "^0.13.5"
55 | },
56 | "browserslist": [
57 | "IE 10"
58 | ]
59 | }
60 |
--------------------------------------------------------------------------------
/config/webpack/webpack.development.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
3 | const Dotenv = require('dotenv-webpack');
4 | const paths = require('./paths');
5 |
6 | module.exports = {
7 | mode: 'development',
8 | devtool: 'cheap-module-source-map',
9 | devServer: {
10 | hot: true,
11 | contentBase: paths.outputPath,
12 | port: 3000,
13 | proxy: {
14 | '/api': 'http://localhost:3001',
15 | },
16 | open: true,
17 | /*overlay: {
18 | errors: true,
19 | warnings: true,
20 | },*/
21 | compress: true,
22 | historyApiFallback: true,
23 | },
24 | module: {
25 | rules: [
26 | {
27 | test: /\.js$/,
28 | enforce: 'pre',
29 | exclude: /node_modules/,
30 | loader: 'eslint-loader',
31 | options: {
32 | cache: false,
33 | configFile: paths.eslintConfigPath,
34 | emitWarning: true,
35 | // Fail only on errors
36 | failOnWarning: false,
37 | failOnError: false,
38 | // Toggle autofix
39 | fix: false,
40 | formatter: require('eslint/lib/cli-engine/formatters/stylish'),
41 | },
42 | },
43 | ],
44 | },
45 | output: {
46 | path: paths.outputPath,
47 | filename: 'js/[name].js',
48 | chunkFilename: 'js/[name].js',
49 | },
50 | plugins: [
51 | new Dotenv({
52 | path: paths.envDevPath, // Path to .env.development file
53 | expand: true,
54 | }),
55 | new Dotenv({
56 | path: paths.envPath, // Path to .env file
57 | expand: true,
58 | }),
59 | new MiniCssExtractPlugin({
60 | filename: 'css/[name].css',
61 | chunkFilename: 'css/[id].css',
62 | }),
63 | new webpack.HotModuleReplacementPlugin(),
64 | ],
65 | optimization: {
66 | splitChunks: {
67 | chunks: 'all',
68 | },
69 | },
70 | };
71 |
--------------------------------------------------------------------------------
/src/view/components/splash-loader/styles.scss:
--------------------------------------------------------------------------------
1 | $count: 7;
2 | $time: 2; //in seconds
3 | $size: 100;
4 | $color: #ffffff;
5 |
6 | .holder {
7 | position: absolute;
8 | left: 0px;
9 | top: 0px;
10 | bottom: 0px;
11 | right: 0px;
12 | width: 100%;
13 | height: 100%;
14 | background-color: #2D2F48;
15 |
16 | .preloader {
17 | /* size */
18 | width: $size + px;
19 | height: $size + px;
20 | position: absolute;
21 | left: 50%;
22 | top: 50%;
23 | transform: translateX(-50%) translateY(-50%);
24 | animation: rotatePreloader $time + s infinite ease-in;
25 | }
26 | @keyframes rotatePreloader {
27 | 0% {
28 | transform: translateX(-50%) translateY(-50%) rotateZ(0deg);
29 | }
30 | 100% {
31 | transform: translateX(-50%) translateY(-50%) rotateZ(-360deg);
32 | }
33 | }
34 | .preloader div {
35 | position: absolute;
36 | width: 100%;
37 | height: 100%;
38 | opacity: 0;
39 | }
40 | .preloader div:before {
41 | content: "";
42 | position: absolute;
43 | left: 50%;
44 | top: 0%;
45 | width: 10%;
46 | height: 10%;
47 | background-color: $color;
48 | transform: translateX(-50%);
49 | border-radius: 50%;
50 | }
51 |
52 | @for $i from 1 through $count {
53 | .preloader div:nth-child(#{$i}) {
54 | transform: rotateZ(((360 / $count) * ($i - 1)) + deg);
55 | animation: rotateCircle + $i $time + s infinite linear;
56 | z-index: $count - $i;
57 | }
58 | @keyframes rotateCircle#{$i} {
59 | #{percentage(((50 / $count) * ($i - 1)) / 100)} {
60 | opacity: 0;
61 | }
62 | #{percentage((((50 / $count) + 0.0001) * ($i - 1)) / 100)} {
63 | opacity: 1;
64 | transform: rotateZ((0 - ((360 / $count) * ($i - 2))) + deg);
65 | }
66 | #{percentage((((50 / $count) * ($i - 0)) + 2) / 100)} {
67 | transform: rotateZ((0 - ((360 / $count) * ($i - 1))) + deg);
68 | }
69 | #{percentage(((50 + ((50 / $count) * ($i - 0))) + 2) / 100)} {
70 | transform: rotateZ((0 - ((360 / $count) * ($i - 1))) + deg);
71 | }
72 | 100% {
73 | transform: rotateZ((0 - ((360 / $count) * ($count - 1))) + deg);
74 | opacity: 1;
75 | }
76 | }
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/config/webpack/webpack.production.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
3 | const TerserPlugin = require('terser-webpack-plugin');
4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
5 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
6 | const Dotenv = require('dotenv-webpack');
7 | const paths = require('./paths');
8 |
9 | module.exports = {
10 | mode: 'production',
11 | devtool: 'source-map',
12 | stats: {
13 | //https://github.com/webpack-contrib/mini-css-extract-plugin/issues/138
14 | children: false,
15 | assets: false,
16 | warnings: false,
17 | },
18 | output: {
19 | path: paths.outputPath,
20 | filename: 'js/[name]-[contenthash:8].js',
21 | chunkFilename: 'js/[name]-[contenthash:8].js',
22 | },
23 | plugins: [
24 | new webpack.HashedModuleIdsPlugin(), // so that file hashes don't change unexpectedly
25 | new CleanWebpackPlugin(),
26 | new Dotenv({
27 | path: paths.envProdPath, // Path to .env.production file
28 | expand: true,
29 | }),
30 | new Dotenv({
31 | path: paths.envPath, // Path to .env file
32 | expand: true,
33 | }),
34 | new MiniCssExtractPlugin({
35 | filename: 'css/[name]-[contenthash:8].css',
36 | chunkFilename: 'css/[name]-[contenthash:8].css',
37 | }),
38 | ],
39 | optimization: {
40 | runtimeChunk: 'single',
41 | splitChunks: {
42 | chunks: 'all',
43 | maxInitialRequests: Infinity,
44 | minSize: 30000,
45 | cacheGroups: {
46 | vendor: {
47 | test: /[\\/]node_modules[\\/]/,
48 | name(module) {
49 | // get the name. E.g. node_modules/packageName/not/this/part.js
50 | // or node_modules/packageName
51 | const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
52 |
53 | // npm package names are URL-safe, but some servers don't like @ symbols
54 | return `vendor.${packageName.replace('@', '')}`;
55 | },
56 | },
57 | },
58 | },
59 | minimizer: [new TerserPlugin({}), new OptimizeCSSAssetsPlugin({})],
60 | },
61 | };
62 |
--------------------------------------------------------------------------------
/config/webpack/rules.js:
--------------------------------------------------------------------------------
1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
2 | const postcssNormalize = require('postcss-normalize');
3 | const imageInlineSizeLimit = 10000;
4 | const fontInlineSizeLimit = 10000;
5 |
6 | module.exports = [
7 | {
8 | test: /\.(ttf|otf|eot|woff(2)?)(\?[a-z0-9]+)?$/,
9 | loader: 'url-loader',
10 | options: {
11 | limit: fontInlineSizeLimit,
12 | name: 'fonts/[name].[ext]',
13 | },
14 | },
15 | {
16 | test: /\.(sa|sc|c)ss$/,
17 | use: [
18 | {
19 | loader: MiniCssExtractPlugin.loader,
20 | options: {
21 | hmr: process.env.NODE_ENV === 'development',
22 | // if hmr does not work, this is a forceful method.
23 | reloadAll: process.env.NODE_ENV === 'development',
24 | publicPath: '../',
25 | },
26 | },
27 | {
28 | loader: 'css-loader',
29 | },
30 | {
31 | loader: 'postcss-loader',
32 | options: {
33 | ident: 'postcss',
34 | plugins: () => [
35 | require('postcss-flexbugs-fixes'),
36 | require('postcss-preset-env')({
37 | autoprefixer: {
38 | flexbox: 'no-2009',
39 | },
40 | stage: 3,
41 | }),
42 | postcssNormalize(),
43 | ],
44 | },
45 | },
46 | {
47 | loader: 'sass-loader',
48 | },
49 | ],
50 | },
51 | {
52 | test: /\.(jpe?g|png|gif)$/,
53 | use: {
54 | loader: 'url-loader',
55 | options: {
56 | limit: imageInlineSizeLimit,
57 | name: 'images/[name].[contenthash:8].[ext]',
58 | },
59 | },
60 | },
61 | {
62 | test: /\.js$/,
63 | exclude: /node_modules/,
64 | use: {
65 | loader: 'babel-loader',
66 | },
67 | },
68 | {
69 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
70 | loader: 'url-loader',
71 | options: {
72 | limit: imageInlineSizeLimit,
73 | name: 'images/[name].[contenthash:8].[ext]',
74 | },
75 | },
76 | ];
77 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mithril Starter Kit
2 | A boilerplate Mithril application using ES6, Babel, Webpack 4, Sass/SCSS, Webpack dev server hot reload and eslint
3 |
4 | ### How to use
5 | By using [degit](https://github.com/Rich-Harris/degit)
6 | ```shell
7 | npx degit dhinesh03/mithril-starter-kit mithril-starter-kit
8 | or
9 | git clone https://github.com/dhinesh03/mithril-starter-kit
10 | ```
11 | or
12 | [Download the source](https://github.com/dhinesh03/mithril-starter-kit/zipball/master)
13 | ```shell
14 | cd mithril-starter-kit
15 |
16 | npm install
17 |
18 | npm start
19 | # Webpack dev server will run and opens the app on the browser with HRM,
20 | npm run build
21 | # Compiles the app for production and all compiled files lies on dist dir.
22 | # To deploy an the application simply transfer the dist to a web server's public directory.
23 | npm run build:analyze
24 | # Compiles the app for production and we will have a report and stats for the bundle on dist folder.
25 | ```
26 |
27 | ## Directory Structure:
28 | ```shell
29 | ├── dist/ # Compiled application
30 | │ ├── css/
31 | │ │ ├── *.css
32 | │ │ └── *.css.map
33 | │ ├── fonts/
34 | │ ├── images/
35 | │ ├── js/
36 | │ │ ├── *.js
37 | │ │ └── *.js.map
38 | │ └── index.html
39 | │
40 | ├── src/ # Application source files
41 | │ │
42 | │ ├── images/ # Image files that are copied to build production output (e.g. favicon.ico)
43 | │ │
44 | │ ├── view/ # All your application view logic files
45 | │ │ │
46 | │ │ ├── components/ # All your view components
47 | │ │ │ │
48 | │ │ │ ├── your-component/ # A single view component
49 | │ │ │ │ ├── index.js # The view component code
50 | │ │ │ │ ├── styles.scss # The view component styles
51 | │ │ │ │ └── ...
52 | │ │ │ └── ...
53 | │ │ │
54 | │ │ ├── pages/ # All your top level page components
55 | │ │ │ ├── your-pages.js
56 | │ │ │ └── ...
57 | │ │ │
58 | │ │ └── routes.js # Application routing definition
59 | │ │
60 | │ ├── index.html # Application HTML template
61 | │ └── index.js # Application entry point
62 | │
63 | ├── eslintrc.js # ESLint configuration
64 | ├── package.json # NPM configuration and scripts
65 | ├── config/environment/env.* # Holding environment variables
66 | └── config/webpack.*.js # Webpack configuration
67 | ```
68 |
--------------------------------------------------------------------------------