Silverstripe Gulp-plate
Boilerplate / starting-point to create a SilverStripe production theme using a gulp.js workflow.
Includes the following tools, tasks, and work-flows:
- Webpack as JavaScript module bundler
- webpack-dev-middleware for live reloading (in memory compilation for faster rebuilds while developing)
- ES2015 syntax transpiled with Babel
- ESLint for JavaScript linting
- Prettier for JavaScript code formatting
- SASS compiled with libsass, source maps, autoprefixer and Sass linting
- BrowserSync for live reloading and static server
- svgo for SVG compression.
- gulp-svg-symbols to generate a SVG icon sprite with
<symbol>
&<use>
tags
Looking for the static version? Look here.
Dependencies / Installation
Gulp-plate depends on the following technologies:
- node.js as local host environment for gulp (v. 7.5.0 or higher) [1]
- gulp as task-runner
- yarn as dependency manager
[1] It is recommended to install node trough nvm (Node Version Manager).
To get started:
# In myProject `themes` folder
$ git clone https://github.com/arillo/gulp-plate source_myTheme
$ cd source_myTheme
$ rm -r .git # Remove the link to the git repo
$ yarn # Install dependencies
Important:
Prefix your theme name with source_
. For a source folder named source_myTheme
a production theme with the name myTheme
will be created. After compiling your folder structure should look like this:
myProject
#...
themes/
myTheme/ # Put this on the server after running the prod task. Should be ignored in `.gitignore`
source_myTheme/ # Holds your source files and gulp tasks
Commands
$ yarn run build
Run the default task and generate a dev version of the site in the dist
folder.
# Equivalent
$ yarn start
$ yarn run watch
Run the default task once, start a server and watch for file changes. See below to learn how to customize the proxy.
$ yarn run prod
Set NODE_ENV='production'
and generate a production version of the site by compressing js, css & html. This is the folder that should go on the server.
If you want to run any other gulp task just append the task name to the build /gulp command:
# Equivalent
$ yarn run build sprite
$ yarn run b sprite
$ yarn run gulp sprite
$ yarn run g sprite
Important:
Every time you run build / watch / prod the generated theme directory will be deleted. Don't make any changes in that directory.
Folder structure
source_myTheme/
gulpfile.js/ # gulp tasks
src/
icons/ # SVG files to be included in he the sprite
images/ # other images
js/ # js code
sass/ # Sass code, SCSS and Sass indented syntax possible
templates/ # Silverstripe templates
Configuration
All paths and plugin settings have been abstracted into a centralized file: ./gulpfile.js/config.js
. Adapt the paths and settings to the structure and needs of your project.
Setting the proxy domain for browserSync
By default browserSync will use the proxy domain php7.test
to modify the domain you can create a file named config-local.js
. The file should export a object containing a proxy
key with your local domain as a value:
// ./gulpfile.js/config-local.js
module.exports = {
proxy: 'local-domain.dev',
};
Symlink theme folders (SilverStripe 4)
Theme folders need to be symlinked in the public/resources/themes/<your-theme-name>/
folder for the theme to work, run:
cd public/resources/themes/<your-theme-name>/
and link each folder in the theme (replace css
with the folder name you want to link):
ln -s ../../../../themes/<your-theme-name>/css css
SVG Sprite configuration
The sprite creates an image with the name sprite.svg
in ./dist/images/
. It also creates a Sass file named: _sprite.scss
in ./src/sass/base/
.
The generated Sass files contains useful information about the sprite icons like the dimensions of each icon. The file will change every time an icon is added, removed or changed, do not edit it manually. You can change the file by changing the template in ./gulpfile.js/tpl/_sprite.scss
.
Static assets
To move static assets from the source directory without transformations, e.g. font files. Add the src
and dest
paths to the static
array in the config.js
Sass
Sass indented syntax is used by default. The main Sass files need to have a .sass
extension, otherwise the compiler fails. Partials can be both .sass
and .scss
.
Include external vendor css files
To include third-party styles in your css use include them in the main.sass
file:
// main.sass
@import url('../../node_modules/normalize.css/normalize.css');
A postcss plugin will then inline the files preserving the source-maps. After the Sass compilation.
Beware that Sass will move @import url(...)
statements to the top of the generated CSS file, so independently of the place of inclusion these styles will always be included at the top of the file.
Sass-lint errors
At the time of writing sass-lint
fails when it encounters empty selectors. This is a bug, it can be prevented by adding a indented comment //
after the empty selector:
.mySelector
//
.mySelector_child
text-align: center
JavaScript
The ./gulpfile.js/config.js
file contains the full webpack configuration (see the js
variable). Feel free to alter is as needed. Keep in mind that the babel-loader
should always be present as eslint
will rely on it.
There configuration will be slightly altered depending on the task you are running. When using the watch task, Javascript compilation will happen in memory, so no files are written to disk (./dist/js/
will be empty) and webpack-hot-middleware/client
will be injected in all bundles for live reloading to work. When building for production webpack.optimize.UglifyJsPlugin
is used for minification. Take a look at ./gulpfile.js/util/getWebpackConfig.js
to see exactly what is happening and change it as needed.
Here are some useful recipes to get you up and running:
Declare aliases for frequently required files
// gulpfile.js/config.js
const js = {
resolve: {
extensions: ['.js'],
alias: {
// Path relative to `context`
myModule: './myModule/myModule.js',
},
},
};
// src/js/some-file.js
import myModule from 'myModule';
myModule();
Docs: https://webpack.js.org/configuration/resolve/#resolve-alias
Shimming non CommonJs modules
jQuery plugin
// gulpfile.js/config.js
const webpack = require('webpack');
//...
const js = {
plugins: [
// Make jQuery global, expected by the plugin.
new webpack.ProvidePlugin({
'window.jQuery': 'jquery',
}),
],
//...
resolve: {
// Add extensions to prevent linting errors.
extensions: ['.js', '.json'],
// Path from `node_modules`, where `myModule` is the module name.
alias: {
myModule: 'myModule/dist/myModule.js',
},
},
};
// src/js/main.js
import $ from 'jquery';
import 'myModule';
$('.js-selector').myModule();
Regular JavaScript module
// gulpfile.js/config.js
const js = {
//...
resolve: {
// Add extensions to prevent linting errors.
extensions: ['.js', '.json'],
// Path from `node_modules`, where `myModule` is the module name.
alias: {
myModule: 'myModule/dist/myModule.js',
},
},
module: {
rules: [
// ...
{
include: require.resolve('myModule/dist/myModule.js'),
loader: 'exports-loader?MyModule',
},
],
},
};
// src/js/main.js
import $ from 'jquery';
import MyModule from 'myModule';
const myInstance = new MyModule();
Docs: https://webpack.js.org/guides/shimming/
Multiple JavaScript bundles & vendor code sharing
To create multiple bundles add entires to entry
// gulpfile.js/config.js
const js = {
// ...
entry: {
main: ['./main.js'],
other: ['./someFile.js', './sotherOtherFile.js'],
},
// ...
};
This will generate two bundles: main.js
& other.js
.
If you do this it is probably a good idea to generate another bundle that contains all shared vendor code:
// gulpfile.js/config.js
const webpack = require('webpack');
//...
const js = {
// ...
entry: {
main: ['./main.js'],
other: ['./someFile.js', './sotherOtherFile.js'],
// List vendor modules here:
vendor: ['jquery', 'svg4everybody'],
},
// ...
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor', // Specify the common bundle's name
}),
],
// ...
};
Docs: https://webpack.js.org/guides/code-splitting-libraries/
Roadmap
- Research Tree-shaking with webpack and implement if possible
Credits
Gulp-plate is based on https://github.com/greypants/gulp-starter