diff --git a/README.md b/README.md index ad1cd98..b3258ca 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,22 @@ Checkout files at /site/ folder for details + WebpackTemplateProvider::WebpackJS('file-name') and WebpackTemplateProvider::WebpackCSS('file-name') can be used at php area + All images will be optimised at /site/src/img and will be written to /site/dist/img (by default) + Favicons will be generated at /site/dist/icons using /site/src/favicon.png -+ Folder /site/src/js/types is used to create page specific JS (just create a JS file there and it will be compiled) ++ Folder /site/src/js/types is used to create page specific JS (just create JS file there and it will be compiled) ++ Folder /site/src/scss/types is used to create page specific CSS (just create SCSS file there and it will be compiled) ++ Automatic linting (JS+SCSS) + Bootstrap 4 included by default ++ Font-Awesome included by default ++ Deferred requirements loading ++ Requirements auto-loading ### Folder structure: + + /site/_config/webpack.yml (Webpack configurtion) + /site/code/WebpackTemplateProvider.php (WebpackJS and WebpackCSS functionality) ++ /site/code/DeferedRequirements.php (Deferred Requirements + Requirements auto-loader) + /site/templates/Page.ss (An example Page.ss) + /site/src (Your sources) ++ /site/dist (Your production assets) @@ -28,18 +36,16 @@ Checkout files at /site/ folder for details + /site/src/js/main.js (Your custom site-wide functionality) + /site/src/js/_events.js (Your custom site-wide events) + /site/src/js/_pageType_and_component_template.js (A template which can be used to create new modules) -+ /site/src/types/*.js (Extra page-specific modules to be autocompiled. My suggestion is to use *ClassName*.js and then require it at SilverStripe custom controller area) ++ /site/src/types/*.js (Extra page-specific modules to be auto-compiled. My suggestion is to use *ClassName*.js and then require it at SilverStripe custom controller area) + /site/src/scss (Your styling to be compiled) + /site/src/scss/_components (Your custom SCSS components) -+ /site/src/scss/app.scss (main application file to include sie-wide components) ++ /site/src/scss/app.scss (main application file to include site-wide components) + /site/src/scss/_variables.sccs (your custom variables, ex. bootstrap) + /site/src/scss/_layout.sccs (Your site-wide styling) -##### P.S to compile page specific styling add following line to /site/src/types/*PageClassName*.js -###### import "../scss/types/*PageClassName*.scss"; ### Requirements: @@ -51,14 +57,16 @@ Checkout files at /site/ folder for details + git clone https://github.com/a2nt/silverstripe-webpack.git + cd silverstripe-webpack + composer install -+ yarn install ++ npm install + edit robots.txt, humans.txt, cache.appcache, manifest.json and package.json to setup your own project ### Commands: + yarn - to update packages -+ yarn start - to start webpack webserver -+ yarn build - to build assets# silverstripe-webpack ++ yarn start - to start webpack development webserver ++ yarn build - to build production assets ++ yarn lint:check - to check SCSS and JS linting ++ yarn lint:fix - to fix SCSS and JS linting automatically ### TODO: diff --git a/site/_config/webpack.yml b/site/_config/webpack.yml index f2b9109..be96c54 100644 --- a/site/_config/webpack.yml +++ b/site/_config/webpack.yml @@ -2,10 +2,10 @@ # 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 - pagesscss: site/src/scss/types - src: site/src +WebpackTemplateProvider: + SRC: site/src + DIST: site/dist + HOSTNAME: localhost + PORT: "3000" + TYPESJS: site/src/js/types + TYPESSCSS: site/src/scss/types diff --git a/site/code/DeferedRequirements.php b/site/code/DeferedRequirements.php new file mode 100755 index 0000000..29b3d05 --- /dev/null +++ b/site/code/DeferedRequirements.php @@ -0,0 +1,122 @@ + 'Auto', + 'DeferedCSS' => 'loadCSS', + 'DeferedJS' => 'loadJS', + ]; + } + + public static function Auto($class = false) + { + // Initialization + Requirements::block(THIRDPARTY_DIR.'/jquery/jquery.js'); + if (defined('FONT_AWESOME_DIR')) { + Requirements::block(FONT_AWESOME_DIR.'/css/lib/font-awesome.min.css'); + } + Requirements::set_force_js_to_bottom(true); + + // Main libs + DeferedRequirements::loadJS('//ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'); + + // App libs + DeferedRequirements::loadJS(project().'/dist/js/app.js'); + DeferedRequirements::loadCSS(project().'/dist/css/app.css'); + + // Class libs + if($class) { + DeferedRequirements::loadJS(project() . '/dist/js/' . $class . '.js'); + DeferedRequirements::loadCSS(project() . '/dist/css/' . $class . '.css'); + } + + return self::forTemplate(); + } + + public static function loadCSS($css) + { + if (self::$defered && !self::_webpackActive()) { + self::$css[] = $css; + } else { + WebpackTemplateProvider::loadCSS($css); + } + } + + public static function loadJS($js) + { + if (self::$defered && !self::_webpackActive()) { + self::$js[] = $js; + } else { + WebpackTemplateProvider::loadJS($js); + } + } + + protected static function _webpackActive() + { + return class_exists('WebpackTemplateProvider') && WebpackTemplateProvider::isActive(); + } + + public static function setDefered($bool) + { + self::$defered = $bool; + } + + public static function forTemplate() + { + if (!self::$defered || self::_webpackActive()) { + return false; + } + + $result = ''; + foreach (self::$css as $css) { + $result .= ''; + } + foreach (self::$js as $js) { + $result .= ''; + } + + $result .= + ''; + + return $result; + } + + private static function get_url($url) + { + // external URL + if (strpos($url, '//') !== false) { + return $url; + } + + $version = Config::inst()->get('DeferedRequirements', 'version'); + $version = $version + ? strpos($url, '?') // inner URL + ? '&'.$version // add param + : '?'.$version // new param + : ''; // no version defined + + $static_domain = Config::inst()->get('DeferedRequirements', 'static_domain'); + $static_domain = $static_domain ? $static_domain : ''; + + return $url.$version; + } +} diff --git a/site/code/WebpackTemplateProvider.php b/site/code/WebpackTemplateProvider.php index e1125a8..24defa3 100644 --- a/site/code/WebpackTemplateProvider.php +++ b/site/code/WebpackTemplateProvider.php @@ -6,18 +6,6 @@ class WebpackTemplateProvider extends Object implements TemplateGlobalProvider { - /** - * @return array - */ - public static function get_template_global_variables() - { - return [ - 'WebpackDevServer' => 'isActive', - 'WebpackCSS' => 'loadCSS', - 'WebpackJS' => 'loadJS', - ]; - } - /** * @var int port number */ @@ -31,7 +19,19 @@ class WebpackTemplateProvider extends Object implements TemplateGlobalProvider /** * @var string assets static files directory */ - private static $distDir = 'site/dist'; + private static $dist = 'site/dist'; + + /** + * @return array + */ + public static function get_template_global_variables() + { + return [ + 'WebpackDevServer' => 'isActive', + 'WebpackCSS' => 'loadCSS', + 'WebpackJS' => 'loadJS', + ]; + } /** * Load CSS file @@ -39,9 +39,7 @@ class WebpackTemplateProvider extends Object implements TemplateGlobalProvider */ public static function loadCSS($path) { - if (!self::isActive()) { - Requirements::css(self::_toPublicPath($path)); - } + Requirements::css(self::_getPath($path)); } /** @@ -50,11 +48,7 @@ class WebpackTemplateProvider extends Object implements TemplateGlobalProvider */ public static function loadJS($path) { - $path = self::isActive() ? - self::_toDevServerPath($path) : - self::_toPublicPath($path); - - Requirements::javascript($path); + Requirements::javascript(self::_getPath($path)); } @@ -66,9 +60,16 @@ class WebpackTemplateProvider extends Object implements TemplateGlobalProvider { $class = __CLASS__; return Director::isDev() && !!@fsockopen( - $class::config()->get('hostname'), - $class::config()->get('port') - ); + $class::config()->get('HOSTNAME'), + $class::config()->get('PORT') + ); + } + + protected static function _getPath($path) + { + return self::isActive() && strpos($path,'//') === false ? + self::_toDevServerPath($path) : + self::_toPublicPath($path); } protected static function _toDevServerPath($path) @@ -77,15 +78,21 @@ class WebpackTemplateProvider extends Object implements TemplateGlobalProvider return sprintf( '%s%s:%s/%s', Director::protocol(), - $class::config()->get('hostname'), - $class::config()->get('port'), - $path + $class::config()->get('HOSTNAME'), + $class::config()->get('PORT'), + basename($path) ); } protected static function _toPublicPath($path) { $class = __CLASS__; - return $class::config()->get('distDir') . '/' . $path; + return strpos($path,'//') === false ? + Controller::join_links( + $class::config()->get('DIST'), + (strpos($path,'.css') ? 'css' : 'js' ), + $path + ) + : $path; } } diff --git a/site/src/js/app.js b/site/src/js/app.js index 372e7ef..950bdca 100644 --- a/site/src/js/app.js +++ b/site/src/js/app.js @@ -18,9 +18,6 @@ import 'bootstrap/js/dist/tab'; // import your custom UI components import './main'; -// bootstrap fix -window.Popper = Popper; - // import images function importAll(r) { return r.keys().map(r); diff --git a/site/templates/Includes/Prestyling.ss b/site/templates/Includes/Prestyling.ss new file mode 100644 index 0000000..883b546 --- /dev/null +++ b/site/templates/Includes/Prestyling.ss @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/templates/Page.ss b/site/templates/Page.ss index e2cf0f0..8422260 100644 --- a/site/templates/Page.ss +++ b/site/templates/Page.ss @@ -40,8 +40,19 @@ + + <% include Prestyling %> + <%-- Upgrade your Browser notice --%> + + + <%-- No JS enabled notice --%> + + + <%-- Loading Spinner --%> +

<%t Page.LOADINGTEXT 'LOADING ..' %>
+
@@ -60,8 +71,7 @@ - $WebpackJS('app.js') - $WebpackCSS('app.css') - + <%-- Require CSS+JS from /site/dist/[js,css]/[ClassName].[js,css] --%> + $AutoRequirements($ClassName).RAW \ No newline at end of file diff --git a/webpack.config.common.js b/webpack.config.common.js index a13fc58..ab32204 100755 --- a/webpack.config.common.js +++ b/webpack.config.common.js @@ -1,15 +1,19 @@ -const path = require("path"); +/* + * Common Environment + */ + 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 path = require("path"); const includes = { - app: path.join(conf.SRC, "js/app.js"), + app: path.join(__dirname, conf.SRC, "js/app.js"), }; const _getAllFilesFromFolder = function(dir) { + dir = path.join(__dirname, dir); + let filesystem = require("fs"); let results = []; @@ -28,13 +32,13 @@ const _getAllFilesFromFolder = function(dir) { }; // add page specific scripts -const pageScripts = _getAllFilesFromFolder(conf.PAGES); +const pageScripts = _getAllFilesFromFolder(conf.TYPESJS); pageScripts.forEach((file) => { includes[path.basename(file, ".js")] = file; }); // add page specific scss -const scssIncludes = _getAllFilesFromFolder(conf.PAGESSCSS); +const scssIncludes = _getAllFilesFromFolder(conf.TYPESSCSS); scssIncludes.forEach((file) => { includes[path.basename(file, ".scss")] = file; }); @@ -75,7 +79,9 @@ module.exports = { test: /\.(png|jpg|gif|svg)$/, loader: "file-loader", options: { - name: "img/[name].[ext]", + name: "[name].[ext]", + outputPath: 'img/', + publicPath: '/site/dist/img/' } }, { test: /\.worker\.js$/, diff --git a/webpack.config.dev.js b/webpack.config.dev.js index 8cca152..7dfb111 100755 --- a/webpack.config.dev.js +++ b/webpack.config.dev.js @@ -1,3 +1,8 @@ +/* + * Development assets generation + */ + +const path = require("path"); const autoprefixer = require('autoprefixer'); const webpack = require('webpack'); const merge = require('webpack-merge'); @@ -16,14 +21,14 @@ const config = merge.strategy({ 'react-hot-loader/patch', 'webpack-dev-server/client?https://' + conf.HOSTNAME + ':' + conf.PORT + '/', 'webpack/hot/only-dev-server', - ], + ] }, output: { - path: conf.BUILD, + path: path.join(__dirname, conf.DIST), filename: '[name].js', // necessary for HMR to know where to load the hot update chunks - publicPath: 'http://' + conf.HOSTNAME + ':' + conf.PORT + '/' + publicPath: 'https://' + conf.HOSTNAME + ':' + conf.PORT + '/' }, module: { @@ -56,9 +61,7 @@ const config = merge.strategy({ 'Chrome >= 44', // Retail 'Samsung >= 4' ] - }), - // http://lostgrid.org/docs.html - require('lost') + }) ] } }, { @@ -72,7 +75,7 @@ const config = merge.strategy({ use: [{ loader: 'url-loader' }] - }, ] + }] }, plugins: [ new webpack.NamedModulesPlugin(), @@ -84,7 +87,9 @@ const config = merge.strategy({ host: IP, port: PORT, historyApiFallback: true, - hot: true, + hot: false, + clientLogLevel: "info", + //watchContentBase: true, overlay: { warnings: true, errors: true diff --git a/webpack.config.prod.js b/webpack.config.prod.js index 4d2302b..8ebf199 100755 --- a/webpack.config.prod.js +++ b/webpack.config.prod.js @@ -1,22 +1,25 @@ -const path = require('path'); -const autoprefixer = require('autoprefixer'); +/* + * Production assets generation + */ + const webpack = require('webpack'); +const conf = require('./webpack.configuration'); 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 path = require('path'); +const autoprefixer = require('autoprefixer'); const ExtractTextPlugin = require("extract-text-webpack-plugin"); + +const OptimizeCSSAssets = require('optimize-css-assets-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 + '/', + path: path.join(__dirname, conf.DIST), + filename: 'js/[name].js', + publicPath: conf.DIST + '/', }, module: { @@ -46,9 +49,7 @@ module.exports = merge(common, { 'Chrome >= 44', // Retail 'Samsung >= 4' ] - }), - // http://lostgrid.org/docs.html - require('lost') + }) ] } }, { @@ -66,35 +67,33 @@ module.exports = merge(common, { loader: 'file-loader', options: { name: '[name].[ext]', - outputPath: 'fonts/', // where the fonts will go - publicPath: './' // override the default path + outputPath: 'fonts/', + publicPath: '../fonts/' } }] } ] }, 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', + filename: 'css/[name].css', allChunks: true }), new OptimizeCSSAssets(), new FaviconsWebpackPlugin({ - logo: conf.SRC + '/favicon.png', + logo: path.join(__dirname, conf.SRC) + '/favicon.png', prefix: '/icons/', - statsFilename: confYML.WebpackTemplateProvider.dist + '/icons/iconstats.json', + statsFilename: conf.DIST + '/icons/iconstats.json', icons: { android: true, appleIcon: true, diff --git a/webpack.configuration.js b/webpack.configuration.js index 74b03ff..fc526f4 100755 --- a/webpack.configuration.js +++ b/webpack.configuration.js @@ -1,13 +1,10 @@ +/* + * Load webpack configuration from site/_config/webpack.yml + */ + 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), - PAGESSCSS: path.join(__dirname, conf.WebpackTemplateProvider.pagesscss), - HOSTNAME: conf.WebpackTemplateProvider.hostname, - PORT: conf.WebpackTemplateProvider.port -}; \ No newline at end of file +module.exports = conf.WebpackTemplateProvider; \ No newline at end of file