diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4a4b0b1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+# START Do not modify the lines between here and #END, they will be regenerated by GitIgnoreEditor
+/betternavigator
+/cms
+/debugbar
+/framework
+/ideannotator
+/redirectedurls
+/reports
+/silverstripe-scaled-uploads
+/silverstripe-version-truncator
+/siteconfig
+# END of GitIgnoreEditor
+
+/node_modules
+/composer.lock
+/yarn.lock
+/vendor
+/site/dist
\ No newline at end of file
diff --git a/browserconfig.xml b/browserconfig.xml
new file mode 100644
index 0000000..36439de
--- /dev/null
+++ b/browserconfig.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+ #000
+
+
+
diff --git a/cache.appcache b/cache.appcache
new file mode 100755
index 0000000..a620a24
--- /dev/null
+++ b/cache.appcache
@@ -0,0 +1,10 @@
+CACHE MANIFEST
+
+FALLBACK:
+/
+/site/dist/css/app.css
+/site/dist/img/logo.png
+/site/dist/img/fonts/fontawesome-webfont.woff2?v=4.7.0
+/site/dist/img/fonts/fontawesome-webfont.woff?v=4.7.0
+/site/dist/img/fonts/fontawesome-webfont.ttf?v=4.7.0
+/site/dist/js/app.js
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100755
index 0000000..41733de
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,32 @@
+{
+ "name": "silverstripe/installer",
+ "description": "The SilverStripe webpack boilerplate",
+ "require": {
+ "php": ">=5.5.0",
+ "silverstripe/cms": "*",
+ "silverstripe/framework": "3.7.x-dev",
+ "silverstripe/redirectedurls": "*",
+ "axllent/silverstripe-scaled-uploads": "*",
+ "jonom/silverstripe-betternavigator": "*",
+ "axllent/silverstripe-version-truncator": "*"
+ },
+ "require-dev": {
+ "phpunit/PHPUnit": "~3.7",
+ "gdmedia/ss-auto-git-ignore": "^1.0",
+ "axyr/silverstripe-ideannotator": "dev-master",
+ "lekoala/silverstripe-debugbar": "^1.0"
+ },
+ "scripts": {
+ "post-update-cmd": "GDM\\SSAutoGitIgnore\\UpdateScript::Go"
+ },
+ "config": {
+ "process-timeout": 600,
+ "discard-changes": true
+ },
+ "extra": {
+ "branch-alias": {
+ "3.x-dev": "3.6.x-dev"
+ }
+ },
+ "minimum-stability": "dev"
+}
\ No newline at end of file
diff --git a/humans.txt b/humans.txt
new file mode 100755
index 0000000..7c664d2
--- /dev/null
+++ b/humans.txt
@@ -0,0 +1,11 @@
+/* AUTHOR */
+Name:
+
+/* TEAM */
+Site: https://bla-bla.com/
+
+/* SITE */
+Doctype: HTML5
+IDE: PHPStorm, Sublime Text, Photoshop, Transmit
+Framework: SilverStripe
+Language: English
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..a216f7a
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,49 @@
+{
+ "name": "SilverStripe",
+ "short_name": "SilverStripe website",
+ "description": "",
+ "dir": "auto",
+ "lang": "en-US",
+ "display": "standalone",
+ "orientation": "any",
+ "start_url": "/?homescreen=1",
+ "theme_color": "#000000",
+ "background_color": "#000000",
+ "icons": [{
+ "src": "/dist/icons/android-chrome-36x36.png",
+ "sizes": "36x36",
+ "type": "image/png"
+ }, {
+ "src": "/dist/icons/android-chrome-48x48.png",
+ "sizes": "48x48",
+ "type": "image/png"
+ }, {
+ "src": "/dist/icons/android-chrome-72x72.png",
+ "sizes": "72x72",
+ "type": "image/png"
+ }, {
+ "src": "/dist/icons/android-chrome-96x96.png",
+ "sizes": "96x96",
+ "type": "image/png"
+ }, {
+ "src": "/dist/icons/android-chrome-144x144.png",
+ "sizes": "144x144",
+ "type": "image/png"
+ }, {
+ "src": "/dist/icons/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ }, {
+ "src": "/dist/icons/android-chrome-256x256.png",
+ "sizes": "256x256",
+ "type": "image/png"
+ }, {
+ "src": "/dist/icons/android-chrome-384x384.png",
+ "sizes": "384x384",
+ "type": "image/png"
+ }, {
+ "src": "/dist/icons/android-chrome-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }]
+}
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100755
index 0000000..aa58383
--- /dev/null
+++ b/package.json
@@ -0,0 +1,110 @@
+{
+ "name": "ss-webpack-boilerplate",
+ "version": "1.0.0",
+ "description": "Lets you create SilverStripe faster",
+ "author": "Tony Air ",
+ "license": "MIT",
+ "private": false,
+ "engines": {
+ "yarn": ">= 1.0.0"
+ },
+ "scripts": {
+ "start": "cross-env NODE_ENV=development webpack-dev-server -d --config webpack.config.dev.js",
+ "dash": "cross-env NODE_ENV=development webpack-dashboard -- webpack-dev-server --config webpack.config.dev.js",
+ "prebuild": "rimraf build",
+ "build": "cross-env NODE_ENV=production webpack -p --config webpack.config.prod.js --progress"
+ },
+ "dependencies": {
+ "bootstrap": "twbs/bootstrap#>= 3.1.0",
+ "favicons-webpack-plugin": "^0.0.7",
+ "jquery": "^3.3.1",
+ "js-yaml": "^3.10.0",
+ "popper": "^1.0.1",
+ "popper.js": "^1.12.9"
+ },
+ "devDependencies": {
+ "exports-loader": "^0.7.0",
+ "favicons-webpack-plugin": "^0.0.7",
+ "@silverstripe/eslint-config": "0.0.2",
+ "autoprefixer": "^7.2.5",
+ "babel-core": "^6.26.0",
+ "babel-loader": "^7.1.2",
+ "babel-plugin-transform-react-jsx": "^6.24.1",
+ "babel-preset-es2015": "^6.24.1",
+ "babel-preset-react": "^6.24.1",
+ "babel-preset-stage-2": "^6.24.1",
+ "browser-sync": "^2.23.6",
+ "browser-sync-webpack-plugin": "^1.2.0",
+ "copy-webpack-plugin": "^4.3.1",
+ "copyfiles": "^1.2.0",
+ "cross-env": "^5.1.3",
+ "css-loader": "^0.28.9",
+ "extract-text-webpack-plugin": "^3.0.2",
+ "file-loader": "^1.1.5",
+ "html-webpack-plugin": "^2.30.1",
+ "laravel-mix": "^1.0",
+ "lost": "^8.2.0",
+ "node-sass": "^4.6.1",
+ "optimize-css-assets-webpack-plugin": "^3.2.0",
+ "postcss-loader": "^2.0.10",
+ "react": "^16.2.0",
+ "react-dom": "^16.2.0",
+ "react-hot-loader": "^3.1.3",
+ "redux": "^3.7.2",
+ "redux-devtools-extension": "^2.13.2",
+ "resolve-url-loader": "^2.2.1",
+ "rimraf": "^2.6.2",
+ "sass-loader": "^6.0.6",
+ "script-ext-html-webpack-plugin": "^1.8.8",
+ "style-loader": "^0.19.0",
+ "svg-url-loader": "^2.3.1",
+ "uglifyjs-webpack-plugin": "^1.1.6",
+ "url-loader": "^0.6.2",
+ "webpack": "^3.8.1",
+ "webpack-dashboard": "^1.1.1",
+ "webpack-dev-server": "^2.11.1",
+ "webpack-manifest-plugin": "^1.3.2",
+ "webpack-merge": "^4.1.1",
+ "worker-loader": "^1.1.0"
+ },
+ "stylelint": {
+ "rules": {
+ "block-no-empty": null,
+ "color-no-invalid-hex": true,
+ "comment-empty-line-before": [
+ "always", {
+ "ignore": [
+ "stylelint-commands",
+ "after-comment"
+ ]
+ }
+ ],
+ "declaration-colon-space-after": "always",
+ "indentation": [
+ 4, {
+ "except": [
+ "value"
+ ]
+ }
+ ],
+ "max-empty-lines": 2,
+ "rule-empty-line-before": [
+ "always", {
+ "except": [
+ "first-nested"
+ ],
+ "ignore": [
+ "after-comment"
+ ]
+ }
+ ],
+ "unit-whitelist": [
+ "em",
+ "rem",
+ "%",
+ "s",
+ "px"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100755
index 0000000..8fe95eb
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,25 @@
+
+
+ Coding standard for SilverStripe 3.x
+
+
+ */vendor/*
+ */thirdparty/*
+ */node_modules/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100755
index 0000000..3a1d051
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,18 @@
+
+
+
+ cms/tests
+ framework/tests
+
+
+
+
+
+
+
+
+ sanitychecks
+
+
+
+
diff --git a/robots.txt b/robots.txt
new file mode 100755
index 0000000..dfa00d6
--- /dev/null
+++ b/robots.txt
@@ -0,0 +1,7 @@
+User-agent: *
+Allow: /
+Crawl-delay: 10
+Disallow: /admin
+Disallow: /Security
+Sitemap: https://www.bla-bla.org/sitemap.xml
+Host: https://www.bla-bla.org
\ No newline at end of file
diff --git a/site/WebpackTemplateProvider.php b/site/WebpackTemplateProvider.php
new file mode 100644
index 0000000..0c94b65
--- /dev/null
+++ b/site/WebpackTemplateProvider.php
@@ -0,0 +1,90 @@
+ 'isActive',
+ 'WebpackCSS' => 'loadCSS',
+ 'WebpackJS' => 'loadJS',
+ ];
+ }
+
+ /**
+ * @var int port number
+ */
+ private static $port = 3000;
+
+ /**
+ * @var string host name
+ */
+ private static $hostname = 'localhost';
+
+ /**
+ * @var string assets static files directory
+ */
+ private static $distDir = 'site/dist';
+
+ /**
+ * Load CSS file
+ * @param $path
+ */
+ public static function loadCSS($path)
+ {
+ if (!self::isActive()) {
+ Requirements::css(self::_toPublicPath($path));
+ }
+ }
+
+ /**
+ * Load JS file
+ * @param $path
+ */
+ public static function loadJS($path)
+ {
+ $path = self::isActive() ?
+ self::_toDevServerPath($path) :
+ self::_toPublicPath($path);
+
+ Requirements::javascript($path);
+ }
+
+
+ /**
+ * Checks if dev mode is enabled and if webpack server is online
+ * @return bool
+ */
+ public static function isActive()
+ {
+ $class = __CLASS__;
+ return Director::isDev() && !!@fsockopen(
+ $class::config()->get('hostname'),
+ $class::config()->get('port')
+ );
+ }
+
+ protected static function _toDevServerPath($path)
+ {
+ $class = __CLASS__;
+ return sprintf(
+ 'http://%s:%s/%s',
+ $class::config()->get('hostname'),
+ $class::config()->get('port'),
+ $path
+ );
+ }
+
+ protected static function _toPublicPath($path)
+ {
+ $class = __CLASS__;
+ return $class::config()->get('distDir') . '/' . $path;
+ }
+}
diff --git a/site/_config/webpack.yml b/site/_config/webpack.yml
new file mode 100644
index 0000000..e610f7a
--- /dev/null
+++ b/site/_config/webpack.yml
@@ -0,0 +1,10 @@
+# that's important to place this file into /site/_config/webpack.yml
+# with all configuration variables presented
+# Cuz WebPack compiling script use it to set configuration
+
+WebpackTemplateProvider:
+ dist: site/dist
+ hostname: localhost
+ port: "3000"
+ pages: site/src/js/types
+ src: site/src
diff --git a/site/src/_config/webpack.yml b/site/src/_config/webpack.yml
new file mode 100644
index 0000000..e610f7a
--- /dev/null
+++ b/site/src/_config/webpack.yml
@@ -0,0 +1,10 @@
+# that's important to place this file into /site/_config/webpack.yml
+# with all configuration variables presented
+# Cuz WebPack compiling script use it to set configuration
+
+WebpackTemplateProvider:
+ dist: site/dist
+ hostname: localhost
+ port: "3000"
+ pages: site/src/js/types
+ src: site/src
diff --git a/site/src/favicon.png b/site/src/favicon.png
new file mode 100644
index 0000000..966a218
Binary files /dev/null and b/site/src/favicon.png differ
diff --git a/site/src/js/_components/_spinner.js b/site/src/js/_components/_spinner.js
new file mode 100644
index 0000000..95c3042
--- /dev/null
+++ b/site/src/js/_components/_spinner.js
@@ -0,0 +1,19 @@
+/**
+ * Just an example component
+ */
+import $ from "jquery";
+
+const SpinnerUI = (($) => {
+ class SpinnerUI {
+ static show(callback) {
+ $("#PageLoading").show(0, callback);
+ }
+
+ static hide(callback) {
+ $("#PageLoading").hide("slow", callback);
+ }
+ }
+ return SpinnerUI;
+})($);
+
+export default SpinnerUI;
\ No newline at end of file
diff --git a/site/src/js/_events.js b/site/src/js/_events.js
new file mode 100644
index 0000000..7f0c0ab
--- /dev/null
+++ b/site/src/js/_events.js
@@ -0,0 +1,8 @@
+/**
+ * Add your global events here
+ */
+
+module.exports = {
+ AJAX: "ajax-load",
+ LOADED: "load"
+};
\ No newline at end of file
diff --git a/site/src/js/_pageType_and_component_template.js b/site/src/js/_pageType_and_component_template.js
new file mode 100644
index 0000000..d2bc62b
--- /dev/null
+++ b/site/src/js/_pageType_and_component_template.js
@@ -0,0 +1,88 @@
+import $ from "jquery";
+
+const TypePage = (($) => {
+
+ // Constants
+ const NAME = "TypePage";
+ //const DATA_KEY = "pageUI." + NAME;
+
+ const Events = require("./_events");
+
+ class TypePage {
+ // Static methods
+ static init() {
+ console.log("Initializing: " + NAME);
+ }
+
+ static destroy() {
+ console.log("Destroying: " + NAME);
+ }
+
+ /**
+ * jQuery extension functions
+ // Constructor
+ constructor(element) {
+ console.log("Constructing: " + NAME + " elements");
+
+ this._element = element;
+ }
+
+ // Public methods
+ dispose() {
+ console.log("Disposing: " + NAME + " elements");
+
+ $.removeData(this._element, DATA_KEY);
+ this._element = null;
+ }
+
+ static _jQueryInterface() {
+ return this.each(function () {
+ // attach functionality to element
+ const $element = $(this);
+ let data = $element.data(DATA_KEY);
+
+ if (!data) {
+ data = new TypePage(this);
+ $element.data(DATA_KEY, data);
+ }
+ })
+ }
+ */
+ }
+
+ $(window).on(Events.AJAX + " " + Events.LOADED, function() {
+ TypePage.init();
+ });
+
+ // JQuery extension functions
+ /*$.fn[NAME] = TypePage._jQueryInterface;
+ $.fn[NAME].Constructor = TypePage;
+ $.fn[NAME].noConflict = function () {
+ $.fn[NAME] = JQUERY_NO_CONFLICT;
+ return TypePage._jQueryInterface;
+ };*/
+
+ return TypePage;
+})($);
+
+export default TypePage;
+
+
+/*
+import $ from 'jquery';
+
+(function (G) {
+ G.initPulsePage = function () {
+ G.destroyPulsePage();
+ };
+
+ G.destroyPulsePage = function () {};
+
+ $(window).on("ajax-content-loaded", function () {
+ G.initPulsePage();
+ });
+
+ $(document).ready(function () {
+ G.initPulsePage();
+ });
+}(this));*/
\ No newline at end of file
diff --git a/site/src/js/app.js b/site/src/js/app.js
new file mode 100644
index 0000000..2a20a9a
--- /dev/null
+++ b/site/src/js/app.js
@@ -0,0 +1,38 @@
+// import images
+function importAll(r) {
+ return r.keys().map(r);
+}
+
+const images = importAll(require.context("../img/", false, /\.(png|jpe?g|svg)$/));
+
+import "../scss/app.scss";
+
+// import Bootstrap
+import Popper from "popper.js";
+window.Popper = Popper;
+import "bootstrap/js/dist/util";
+import "bootstrap/js/dist/alert";
+import "bootstrap/js/dist/button";
+import "bootstrap/js/dist/carousel";
+import "bootstrap/js/dist/collapse";
+import "bootstrap/js/dist/dropdown";
+import "bootstrap/js/dist/modal";
+import "bootstrap/js/dist/tooltip";
+import "bootstrap/js/dist/popover";
+import "bootstrap/js/dist/scrollspy";
+import "bootstrap/js/dist/tab";
+//
+
+// import your custom UI components
+import "./main.js";
+
+// TODO: hot module update
+/*const Events = require("./_events");
+if (module.hot) {
+ module.hot.accept();
+ module.hot.addStatusHandler(status => {
+ if(status === "apply"){
+ $(window).trigger(Events.AJAX);
+ }
+ });
+}*/
\ No newline at end of file
diff --git a/site/src/js/main.js b/site/src/js/main.js
new file mode 100644
index 0000000..a0cdb55
--- /dev/null
+++ b/site/src/js/main.js
@@ -0,0 +1,38 @@
+import $ from "jquery";
+
+import Events from "./_events";
+// import an example component
+import Spinner from "./_components/_spinner";
+
+const MainUI = (($) => {
+
+ // Constants
+ const NAME = "MainUI";
+
+ class MainUI {
+ // Static methods
+ static init() {
+ this.destroy();
+ console.log("Initializing: " + NAME);
+
+ Spinner.hide(function() {
+ $("body").addClass("loaded");
+ });
+ }
+
+ static destroy() {
+ console.log("Destroying: " + NAME);
+ Spinner.show(function() {
+ $("body").removeClass("loaded");
+ });
+ }
+ }
+
+ $(window).on(Events.AJAX + " " + Events.LOADED, function() {
+ MainUI.init();
+ });
+
+ return MainUI;
+})($);
+
+export default MainUI;
\ No newline at end of file
diff --git a/site/src/scss/_layout.scss b/site/src/scss/_layout.scss
new file mode 100644
index 0000000..5ca54d0
--- /dev/null
+++ b/site/src/scss/_layout.scss
@@ -0,0 +1,7 @@
+/**
+ * Your custom style
+ */
+
+html,body {
+ background:#000;
+}
\ No newline at end of file
diff --git a/site/src/scss/_variables.scss b/site/src/scss/_variables.scss
new file mode 100644
index 0000000..48a987c
--- /dev/null
+++ b/site/src/scss/_variables.scss
@@ -0,0 +1,10 @@
+/*
+ * Your custom variables
+ */
+
+// bootstrap minify bugfix:
+$navbar-dark-toggler-icon-bg: none;
+$navbar-light-toggler-icon-bg: none;
+
+// IE > 9
+$enable-flex: true;
\ No newline at end of file
diff --git a/site/src/scss/app.scss b/site/src/scss/app.scss
new file mode 100644
index 0000000..c2eda41
--- /dev/null
+++ b/site/src/scss/app.scss
@@ -0,0 +1,42 @@
+// Your custom variables
+@import "variables";
+
+// Bootstrap basics
+@import "../../../node_modules/bootstrap/scss/functions";
+@import "../../../node_modules/bootstrap/scss/variables";
+@import "../../../node_modules/bootstrap/scss/mixins";
+@import "../../../node_modules/bootstrap/scss/root";
+@import "../../../node_modules/bootstrap/scss/reboot";
+@import "../../../node_modules/bootstrap/scss/type";
+@import "../../../node_modules/bootstrap/scss/images";
+@import "../../../node_modules/bootstrap/scss/code";
+@import "../../../node_modules/bootstrap/scss/grid";
+@import "../../../node_modules/bootstrap/scss/tables";
+@import "../../../node_modules/bootstrap/scss/forms";
+@import "../../../node_modules/bootstrap/scss/buttons";
+@import "../../../node_modules/bootstrap/scss/transitions";
+@import "../../../node_modules/bootstrap/scss/dropdown";
+@import "../../../node_modules/bootstrap/scss/button-group";
+@import "../../../node_modules/bootstrap/scss/input-group";
+@import "../../../node_modules/bootstrap/scss/custom-forms";
+@import "../../../node_modules/bootstrap/scss/nav";
+@import "../../../node_modules/bootstrap/scss/navbar";
+@import "../../../node_modules/bootstrap/scss/card";
+@import "../../../node_modules/bootstrap/scss/breadcrumb";
+@import "../../../node_modules/bootstrap/scss/pagination";
+@import "../../../node_modules/bootstrap/scss/badge";
+@import "../../../node_modules/bootstrap/scss/jumbotron";
+@import "../../../node_modules/bootstrap/scss/alert";
+@import "../../../node_modules/bootstrap/scss/progress";
+@import "../../../node_modules/bootstrap/scss/media";
+@import "../../../node_modules/bootstrap/scss/list-group";
+@import "../../../node_modules/bootstrap/scss/close";
+@import "../../../node_modules/bootstrap/scss/modal";
+@import "../../../node_modules/bootstrap/scss/tooltip";
+@import "../../../node_modules/bootstrap/scss/popover";
+@import "../../../node_modules/bootstrap/scss/carousel";
+@import "../../../node_modules/bootstrap/scss/utilities";
+@import "../../../node_modules/bootstrap/scss/print";
+
+// Your custom UI
+@import "layout";
\ No newline at end of file
diff --git a/site/templates/Page.ss b/site/templates/Page.ss
new file mode 100644
index 0000000..23012d1
--- /dev/null
+++ b/site/templates/Page.ss
@@ -0,0 +1,64 @@
+
+
+<%-- manifest="/cache.appcache" --%>
+
+ $MetaTags
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <% base_tag %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $Layout
+
+
+
+
+
+ $BetterNavigator
+
+
+ $WebpackJS('app.js')
+ $WebpackCSS('app.css')
+
+
+
\ No newline at end of file
diff --git a/webpack.config.common.js b/webpack.config.common.js
new file mode 100755
index 0000000..edbacf8
--- /dev/null
+++ b/webpack.config.common.js
@@ -0,0 +1,122 @@
+const path = require("path");
+const webpack = require("webpack");
+const ExtractTextPlugin = require("extract-text-webpack-plugin");
+const ManifestPlugin = require("webpack-manifest-plugin");
+const conf = require("./webpack.configuration");
+const isProduction = process.env.NODE_ENV === "production";
+
+const jsScripts = {
+ app: path.join(conf.SRC, "js/app.js"),
+};
+
+const _getAllFilesFromFolder = function(dir) {
+ let filesystem = require("fs");
+ let results = [];
+
+ filesystem.readdirSync(dir).forEach(function(file) {
+
+ file = dir + "/" + file;
+ let stat = filesystem.statSync(file);
+
+ if (stat && stat.isDirectory()) {
+ results = results.concat(_getAllFilesFromFolder(file))
+ } else results.push(file);
+
+ });
+
+ return results;
+};
+
+// add page specific scripts
+const pageScripts = _getAllFilesFromFolder(conf.PAGES);
+pageScripts.forEach((file) => {
+ jsScripts[path.basename(file, ".js")] = file;
+});
+
+module.exports = {
+ entry: jsScripts,
+ devtool: "source-map",
+ externals: {
+ "custom-select": "CustomSelect",
+ "ui-progress-button": "UIProgressButton"
+ },
+ module: {
+ rules: [{
+ test: /\.jsx?$/,
+ exclude: /node_modules/,
+ use: {
+ loader: "babel-loader",
+ options: {
+ presets: [
+ ["es2015", {
+ modules: false
+ }],
+ ["stage-2"]
+ ],
+ plugins: [
+ ["transform-react-jsx"],
+ ["react-hot-loader/babel"],
+ ]
+ },
+ }
+ }, {
+ test: /\.(png|jpg|gif|svg)$/,
+ loader: "file-loader",
+ options: {
+ name: "img/[name].[ext]",
+ }
+ }, {
+ test: /\.eot(\?v=\d+.\d+.\d+)?$/,
+ use: {
+ loader: "file-loader",
+ options: {
+ name: "fonts/[name].[ext]"
+ }
+ }
+ }, {
+ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
+ use: {
+ loader: "url-loader",
+ options: {
+ name: "fonts/[name].[ext]",
+ limit: 10000,
+ mimetype: "application/font-woff"
+ }
+ }
+ }, {
+ test: /\.[ot]tf(\?v=\d+.\d+.\d+)?$/,
+ use: {
+ loader: "url-loader",
+ options: {
+ name: "fonts/[name].[ext]",
+ limit: 10000,
+ mimetype: "application/octet-stream"
+ }
+ }
+ }, {
+ test: /\.worker\.js$/,
+ use: {
+ loader: "worker-loader"
+ }
+ }]
+ },
+ plugins: [
+ new webpack.ProvidePlugin({
+ $: "jquery",
+ jQuery: "jquery",
+ "window.jQuery": "jquery",
+ Popper: ["popper.js", "default"],
+ Util: "exports-loader?Util!bootstrap/js/dist/util",
+ Alert: "exports-loader?Alert!bootstrap/js/dist/alert",
+ Button: "exports-loader?Button!bootstrap/js/dist/button",
+ Carousel: "exports-loader?Carousel!bootstrap/js/dist/carousel",
+ Collapse: "exports-loader?Collapse!bootstrap/js/dist/collapse",
+ Dropdown: "exports-loader?Dropdown!bootstrap/js/dist/dropdown",
+ Modal: "exports-loader?Modal!bootstrap/js/dist/modal",
+ Tooltip: "exports-loader?Tooltip!bootstrap/js/dist/tooltip",
+ Popover: "exports-loader?Popover!bootstrap/js/dist/popover",
+ Scrollspy: "exports-loader?Scrollspy!bootstrap/js/dist/scrollspy",
+ Tab: "exports-loader?Tab!bootstrap/js/dist/tab",
+ })
+ ],
+};
\ No newline at end of file
diff --git a/webpack.config.dev.js b/webpack.config.dev.js
new file mode 100755
index 0000000..559a12d
--- /dev/null
+++ b/webpack.config.dev.js
@@ -0,0 +1,97 @@
+const autoprefixer = require('autoprefixer');
+const webpack = require('webpack');
+const merge = require('webpack-merge');
+const common = require('./webpack.config.common.js');
+const conf = require('./webpack.configuration');
+
+const IP = process.env.IP || conf.HOSTNAME;
+const PORT = process.env.PORT || conf.PORT;
+
+const config = merge.strategy({
+ entry: 'prepend'
+})(common, {
+
+ entry: {
+ app: [
+ 'react-hot-loader/patch',
+ 'webpack-dev-server/client?http://' + conf.HOSTNAME + ':' + conf.PORT + '/',
+ 'webpack/hot/only-dev-server',
+ ],
+ main: [
+ 'webpack-dev-server/client?http://' + conf.HOSTNAME + ':' + conf.PORT + '/',
+ 'webpack/hot/only-dev-server',
+ ],
+ },
+
+ output: {
+ path: conf.BUILD,
+ filename: '[name].js',
+ // necessary for HMR to know where to load the hot update chunks
+ publicPath: 'http://' + conf.HOSTNAME + ':' + conf.PORT + '/'
+ },
+
+ module: {
+ rules: [{
+ test: /\.scss$/,
+ use: [{
+ loader: 'style-loader',
+ options: {
+ sourceMap: true
+ }
+ }, {
+ loader: 'css-loader',
+ options: {
+ sourceMap: true
+ }
+ }, {
+ loader: 'postcss-loader',
+ options: {
+ sourceMap: true,
+ plugins: [
+ autoprefixer({
+ // If we want to use the same browser list for more tools
+ // this list should be moved to package.json
+ // https://evilmartians.com/chronicles/autoprefixer-7-browserslist-2-released
+ browsers: [
+ 'ie >= 11',
+ 'ie_mob >= 11',
+ 'Safari >= 10',
+ 'Android >= 4.4',
+ 'Chrome >= 44', // Retail
+ 'Samsung >= 4'
+ ]
+ }),
+ // http://lostgrid.org/docs.html
+ require('lost')
+ ]
+ }
+ }, {
+ loader: 'sass-loader',
+ options: {
+ sourceMap: true
+ }
+ }, ]
+ }, ]
+ },
+ plugins: [
+ new webpack.NamedModulesPlugin(),
+ new webpack.HotModuleReplacementPlugin(),
+ new webpack.NoEmitOnErrorsPlugin(),
+ ],
+
+ devServer: {
+ host: IP,
+ port: PORT,
+ historyApiFallback: true,
+ hot: true,
+ overlay: {
+ warnings: true,
+ errors: true
+ },
+ headers: {
+ 'Access-Control-Allow-Origin': '*'
+ }
+ },
+});
+
+module.exports = config;
\ No newline at end of file
diff --git a/webpack.config.prod.js b/webpack.config.prod.js
new file mode 100755
index 0000000..cedee69
--- /dev/null
+++ b/webpack.config.prod.js
@@ -0,0 +1,102 @@
+const path = require('path');
+const autoprefixer = require('autoprefixer');
+const webpack = require('webpack');
+const merge = require('webpack-merge');
+const common = require('./webpack.config.common.js');
+const OptimizeCSSAssets = require('optimize-css-assets-webpack-plugin');
+const conf = require('./webpack.configuration');
+const ExtractTextPlugin = require("extract-text-webpack-plugin");
+const FaviconsWebpackPlugin = require("favicons-webpack-plugin");
+const fs = require("fs");
+const yaml = require("js-yaml");
+const confYML = yaml.safeLoad(fs.readFileSync(path.join(__dirname, "site/_config/webpack.yml"), "utf8"));
+
+module.exports = merge(common, {
+
+ output: {
+ path: conf.BUILD,
+ filename: '[name].js',
+ publicPath: confYML.WebpackTemplateProvider.dist + '/',
+ },
+
+ module: {
+ rules: [{
+ test: /\.scss$/,
+ use: ExtractTextPlugin.extract({
+ fallback: "style-loader",
+ use: [{
+ loader: 'css-loader',
+ options: {
+ sourceMap: true
+ }
+ }, {
+ loader: 'postcss-loader',
+ options: {
+ sourceMap: true,
+ plugins: [
+ autoprefixer({
+ // If we want to use the same browser list for more tools
+ // this list should be moved to package.json
+ // https://evilmartians.com/chronicles/autoprefixer-7-browserslist-2-released
+ browsers: [
+ 'ie >= 11',
+ 'ie_mob >= 11',
+ 'Safari >= 10',
+ 'Android >= 4.4',
+ 'Chrome >= 44', // Retail
+ 'Samsung >= 4'
+ ]
+ }),
+ // http://lostgrid.org/docs.html
+ require('lost')
+ ]
+ }
+ }, {
+ loader: 'resolve-url-loader'
+ }, {
+ loader: 'sass-loader',
+ options: {
+ sourceMap: true
+ }
+ }, ]
+ })
+ }, ]
+ },
+
+ plugins: [
+
+ new webpack.DefinePlugin({
+ 'process.env': {
+ 'NODE_ENV': JSON.stringify('production')
+ }
+ }),
+
+ new webpack.optimize.ModuleConcatenationPlugin(),
+ new webpack.optimize.UglifyJsPlugin({
+ sourceMap: true,
+ comments: false
+ }),
+ new ExtractTextPlugin({
+ filename: '[name].css',
+ allChunks: true
+ }),
+ new OptimizeCSSAssets(),
+ new FaviconsWebpackPlugin({
+ logo: conf.SRC + '/favicon.png',
+ prefix: '/icons/',
+ statsFilename: confYML.WebpackTemplateProvider.dist + '/icons/iconstats.json',
+ icons: {
+ android: true,
+ appleIcon: true,
+ appleStartup: true,
+ coast: true,
+ favicons: true,
+ firefox: true,
+ opengraph: true,
+ twitter: true,
+ yandex: true,
+ windows: true
+ }
+ }),
+ ],
+});
\ No newline at end of file
diff --git a/webpack.configuration.js b/webpack.configuration.js
new file mode 100755
index 0000000..6921736
--- /dev/null
+++ b/webpack.configuration.js
@@ -0,0 +1,12 @@
+const path = require("path");
+const fs = require("fs");
+const yaml = require("js-yaml");
+const conf = yaml.safeLoad(fs.readFileSync(path.join(__dirname, "site/_config/webpack.yml"), "utf8"));
+
+module.exports = {
+ SRC: path.join(__dirname, conf.WebpackTemplateProvider.src),
+ BUILD: path.join(__dirname, conf.WebpackTemplateProvider.dist),
+ PAGES: path.join(__dirname, conf.WebpackTemplateProvider.pages),
+ HOSTNAME: conf.WebpackTemplateProvider.hostname,
+ PORT: conf.WebpackTemplateProvider.port
+};
\ No newline at end of file