This is a collection of tools, configurations and recommendations for setting up a project. It provides build tooling setup for SilverStripe based websites, which includes Vue (or optionally React) and recommended extensions for a productive development experience within the IDE developers use at DNA – Visual Studio Code (VS Code).
Note that this boilerplate uses yarn v3 (berry) which handles node modules quite differently than it previously did, please consult the yarn docs for an overview and explanation of these changes.
Although this provides a lot out of the box, feel free to customise it to fit the project.
- Make sure you have nvm and yarn installed
- Install required development extensions (see the Development Extensions section)
- By default this is configured for Vue, if you are using React then move and replace the files from
themes/base/react
otherwise this folder can be deleted - Make appropriate configuration changes to
webpack.env.js
- Run
yarn install
in the folder containingpackage.json
Near the top of webpack.env.js
are a set of constants that should be configured to match your project setup.
theme
is the name of your theme directory (default is'base'
)defaultLocalDomain
is the domain name for local development (default isdna.test
)
If you prefer to have a different domain for your local environment to the one defined in webpack.env.js
you can define WEBPACK_DOMAIN
in your .env
file.
The following variables may be included in your .env
file:
WEBPACK_DOMAIN
: Domain to be used by webpackDevServerWEBPACK_SSL_ENABLE
: Whether your local server is HTTPS, if so provide the following variablesWEBPACK_SSL_KEY
: SSL Key to be used by webpackDevServerWEBPACK_SSL_CERT
: SSL Cert to be used by webpackDevServerWEBPACK_SSL_CA
: SSL CA to be used by webpackDevServer
Developers at DNA use VS Code, there is workspace specific configuration for these extensions in vscode-settings.json
, you should copy these to .vscode/settings.json
(we don't commit this dir so you can make your own workspace customisations).
You can get all the required/recommended extensions by:
- Copy
vscode-extensions.json
to.vscode/extensions.json
- Navigate to the 'Extensions' panel in VS Code
- Filter by 'recommended'
- Our recommendations are under 'workspace recommendations'
If you do not use VS Code it is recommended that you use equivalent extensions/configuration for your IDE.
These extensions are to provide code linting and formatting, this is to make sure that we are avoiding common pitfalls and writing code the same style. These extensions help us to fix errors before triggering the build task, as you will be warned about errors while typing and files will be fixed on save.
Extension | Description |
---|---|
dbaeumer.vscode-eslint |
JS linting and fixing |
editorconfig.editorconfig |
Consistent spacing in files |
esbenp.prettier-vscode |
File formatting |
octref.vetur |
.vue file support |
stylelint.vscode-stylelint |
SCSS linting and fixing |
These extensions are likely to improve your development experience for js and scss
Extension | Description |
---|---|
arcanis.vscode-zipfs |
Handle yarn 2 zips |
christian-kohler.npm-intellisense |
JS import suggestions |
christian-kohler.path-intellisense |
File path suggestions |
mrmlnc.vscode-scss |
SCSS Intellisense |
oouo-diogo-perdigao.docthis |
Generate JSDoc |
orta.vscode-jest |
Jest Integration |
streetsidesoftware.code-spell-checker |
Spellchecker |
drknoxy.eslint-disable-snippets |
Snippets for eslint-disable |
It is likely that you will be using this build chain in conjunction with Silverstripe/PHP development, these extensions will greatly enhance your Silverstripe development experience. Note phpcs and phpcbf tools must be installed to take advantage of use the linting and formatting, this can be done by running composer global require squizlabs/php_codesniffer
Extension | Description |
---|---|
adrianhumphreys.silverstripe |
Silverstripe .ss syntax support |
bmewburn.vscode-intelephense-client |
PHP Intellisense |
felixfbecker.php-debug |
Debug support for PHP with XDebug |
mehedidracula.php-namespace-resolver |
Import and expand PHP namespaces |
neilbrayfield.php-docblocker |
PHP Docblock support |
phproberto.vscode-php-getters-setters |
Create PHP getters and setters from class properties |
valeryanm.vscode-phpsab |
PHP linting and fixing |
We use webpack (see webpack.config.js
) to generate assets
To run a task run yarn <task-name>
dev
: compiles code fromsrc
todist
with source-mapswatch
: complies code fromsrc
todist
with source-maps, watches for code changes and compiles accordinglyserve
: serves files directly via webpack-dev-server (port:8080
), uses HMR to update page with code changes without reloading (if configured in your app), otherwise falls back to reloading on changeprod
: compiles code fromsrc
todist
without source-maps, fixes files, performs vendor chunkingtest
: runs jest which runs anytest.js(x)
- babel transpiles the js (see
.babelrc
) and allows us to use newer syntax@babel/plugin-proposal-class-properties
and@babel/plugin-proposal-decorators
allow us to use js decorators- @babel/plugin-proposal-optional-chaining
- @babel/plugin-proposal-nullish-coalescing-operator
- eslint lints and fixes the js (see
.eslintrc
)- airbnb as our style guide
- simple-import-sort for automatic import sorting
- prettier for overall formatting (see
.prettierrc
)
- stylelint lints and fixes scss (see
.stylelintrc
)- sass-guidelines as our style guide
- stylelint-concentric-order for automatic property sorting
- prettier for overall formatting (see
.prettierrc
) - postcss for CSS Processing (see
postcss.config.js
)- autoprefixer for adding appropriate prefixes for your supported browsers
- inline-svg for inlining svg within css files
- normalize for normalising default styles across browsers
Many of the build tools we use support browserslist, you should define browserslist
in your package.json
. We have chosen a list which roughly equates to supporting the last 2 versions of major browsers and specifically excludes IE, since we have stopped officially supporting it.
This list affects transformations performed by babel, normalize and autoprefixer.
We use Jest to test javascript components. Note there is a configuration file called jest.config.js
where custom configuration and mocks can be added. Additionally there is jest.setup.js
which is run before each test.
Also included is enzyme which makes it easier to test React components.
If intending to use React follow these steps:
-
Run these commands in the theme folder
yarn remove eslint-config-airbnb-base;
yarn add prop-types react react-bem-helper react-dom react-hot-loader react-use;
yarn add --dev @types/enzyme @types/react enzyme enzyme-adapter-react-16 enzyme-to-json eslint-config-airbnb eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-hooks;
-
Update these blocks in
.eslintrc
"extends": [ "airbnb", "plugin:prettier/recommended", "plugin:jest/recommended" ], "overrides": [ { "files": ["*.test.js", "*.test.jsx"], "globals": { "React": true } } ]
-
Update these jest files
- Add this line to
jest.config.js
snapshotSerializers: ['enzyme-to-json/serializer']
- Replace the contents of
jest.setup.js
// Uncomment if using mobx // import 'mobx-react/batchingForReactDom'; import Enzyme from "enzyme"; import Adapter from "enzyme-adapter-react-16"; // Uncomment if using mobx // import { configure as MobxConfigure } from 'mobx'; import React from "react"; MobxConfigure({ enforceActions: "observed" }); Enzyme.configure({ adapter: new Adapter() }); global.React = React;
- Add this line to
Below are some PHP functions that automatically includes generated files by parsing the webpack-assets.json
that webpack outputs (it contains has a list of .js
and .css
files generated). This can be helpful because dev
produces less files than prod
as it does not perform vendor chunking.
In PageController.php
public function init()
{
parent::init();
Requirements::set_force_js_to_bottom(true);
$this->loadBuiltReqs();
}
/**
* Get the webpack-assets.json, returns false if not present
*
* @return array|false
*/
public function getWebpackAssets()
{
return glob(THEMES_PATH . '/base/dist/webpack-assets.json');
}
/**
* Loads a requirement according to it's type
*
* @return void
*/
private function loadRequirement($type, $fileName)
{
if (gettype($fileName) === 'array') {
$bundle = array_filter($fileName, function ($file) {
return strpos($file, '.bundle');
});
$fileName = array_pop($bundle);
}
$fileName = ltrim($fileName, '/');
$fileName = str_replace('_resources/', '', $fileName);
if ($type === 'js') {
Requirements::javascript($fileName);
} elseif ($type === 'css') {
Requirements::css($fileName);
}
}
/**
* Loads the list of built requirements from webpack 'webpack-assets.json'
*
* @return void
*/
public function loadBuiltReqs()
{
$assetsFile = $this->getWebpackAssets();
if (!$assetsFile) {
return;
}
$assets = json_decode(file_get_contents($assetsFile[0]), true);
// Skip loading these assets since they're loaded through other means
$skipAssets = [
'', 'editor', 'polyfills', 'components'
];
foreach ($skipAssets as $asset) {
if (array_key_exists($asset, $assets)) {
unset($assets[$asset]);
}
}
// Load vendor first
if (array_key_exists('vendor', $assets)) {
foreach ($assets['vendor'] as $type => $fileName) {
$this->loadRequirement($type, $fileName);
}
unset($assets['vendor']);
}
foreach ($assets as $reqs) {
foreach ($reqs as $type => $fileName) {
$this->loadRequirement($type, $fileName);
}
}
}
/**
* Returns the favicon.ico url if webpack assets are built
*
* @return string|false
*/
public function getFavicon($type = 'ico')
{
if (!$this->getWebpackAssets()) {
return false;
}
return ModuleResourceLoader::singleton()->resolveResource("/_resources/themes/base/dist/static/favicons/favicon.$type");
}