$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash
$ sudo apt-get install -y nodejs
See:
- https://symfony.com/doc/4.4/frontend/encore/installation.html
- https://symfony.com/doc/4.4/frontend/encore/versioning.html
- https://symfony.com/doc/4.4/frontend/encore/simple-example.html#requiring-javascript-modules
$ npm init
$ composer require symfony/webpack-encore-bundle
$ npm install @symfony/webpack-encore
$ npm install sass-loader@^8.0.0 node-sass
$ npm install popper.js
$ npm install jquery
$ npm install bootstrap
$ npm install @fortawesome/fontawesome-free
$ npm install webpack-notifier
(optional, doesn't seem to be a requirement anymore)
- /assets
- /js
- app.js (components and code used globally, like
bootstrap.js
) - /components (components used on multipe pages but not on the whole website)
- /complex-component-example
- complex-component-example-file-1.js
- complex-component-example-file-2.js
- simple-components.js
- /complex-component-example
- /views (js for specific views)
- /feature-1 (e.g. user-account, search-engines...)
- /feature-2
- page-1.js
- app.js (components and code used globally, like
- /scss (same logic than /js)
- app.scss
- /components
- base-style.scss (core style of the whole website, you could also put the content directly in
app.scss
) - bootstrap-mixins.scss
- /complex-component-example
- complex-component-example-file-1.scss
- complex-component-example-file-2.scss
- simple-component.scss
- base-style.scss (core style of the whole website, you could also put the content directly in
- /constants
- app.scss (your custom SCSS constants)
- bootstrap.scss (your custom overrides of Bootstrap SCSS constants)
- /overrides (your custom style overrides of third party modules like Bootstrap)
- bootstrap.scss
- random-npm-package.scss
- /views (style for specific views)
- /js
webpack.config.js
const Encore = require('@symfony/webpack-encore');
// Manually configure the runtime environment if not already configured yet by the "encore" command.
// It's useful when you use tools that rely on webpack.config.js file.
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
// Directory where compiled assets will be stored.
.setOutputPath('public/build/')
// Public path used by the web server to access the output path.
.setPublicPath('/build')
// Only needed for CDN's or sub-directory deploy.
// .setManifestKeyPrefix('build/')
// Main entry for JS required globally. File includes a reference to assets/css/app.scss for CSS required globally.
.addEntry('app', './assets/js/app.js')
/*
Entries for JS tied to a specific page/feature.
Each file can optionally include a reference to a CSS file tied to the same page/feature.
*/
// .addEntry('page', './assets/js/views/page.js')
// Splits entries into chunks to avoid code duplication (e.g. two page-tied JS files both importing jQuery).
.splitEntryChunks()
// Allows sass/scss files to be processed.
.enableSassLoader()
// Allows legacy applications to use $/jQuery as a global variable.
.autoProvidejQuery()
.enableSourceMaps(!Encore.isProduction())
// Enables hashed filenames (e.g. app.abc123.css). It forces browser to clear old assets from cache.
.enableVersioning()
// Purges the outputPath directory before each build (doesn't work on subsequent builds triggered by --watch).
.cleanupOutputBeforeBuild()
// Requires an extra script tag for runtime.js which must be loaded before any other script tag.
.enableSingleRuntimeChunk()
// Adds integrity="..." attributes to your script & link tags
.enableIntegrityHashes()
// Uncomment if you use TypeScript.
// .enableTypeScriptLoader()
// Shows OS notifications when builds finish/fail.
// .enableBuildNotifications()
// Enables @babel/preset-env polyfills.
.configureBabelPresetEnv(config => {
config.useBuiltIns = 'usage';
config.corejs = 3;
})
// uncomment if you use API Platform Admin (composer req api-admin)
//.enableReactPreset()
//.addEntry('admin', './assets/js/admin.js')
;
// Exports the final configuration.
module.exports = Encore.getWebpackConfig();
assets/js/app.js
import 'bootstrap';
import '../scss/app.scss';
import './components/helpers/jquery/functions';
// import './components/my-component-used-globally';
assets/scss/app.scss
// Put your fonts here.
@import '~@fortawesome/fontawesome-free/scss/fontawesome';
@import '~@fortawesome/fontawesome-free/scss/brands';
@import '~@fortawesome/fontawesome-free/scss/regular';
@import '~@fortawesome/fontawesome-free/scss/solid';
// Then your styles used globally.
@import './components/base-style';
assets/scss/components/base-style.scss (example)
@import '../constants/app';
@import '../overrides/bootstrap';
@import '../overrides/random-npm-package';
html {
font-size: 1rem;
}
a {
cursor: pointer;
}
a:active, a:focus {
outline: none;
}
button:active, button:focus {
outline: none;
}
.no-underline {
text-decoration: none !important;
}
// Preserves line breaks in content inputted by the user (e.g. through a textarea).
.text-pre-line {
white-space: pre-line;
}
.text-ucfirst::first-letter {
text-transform: uppercase;
}
.vh-100 {
height: 100vh;
}
.vh-90 {
height: 90vh;
}
.vh-10 {
height: 10vh;
}
.w-33 {
width: 33.33%;
}
assets/scss/constants/app.scss (example)
// Bootstrap colors.
$custom-color-primary: #007bff;
$custom-color-secondary: #6c757d;
$custom-color-input: #495057;
assets/scss/constants/bootstrap.scss (example)
@import './app';
$font-family-base: sans-serif;
$font-size-base: 1rem !default;
$theme-colors: (
'primary': $custom-color-primary,
'secondary': $custom-color-secondary,
'input': $custom-color-input,
);
$input-btn-focus-box-shadow: none;
$input-btn-focus-width: 0;
assets/scss/overrides/bootstrap.scss (example)
@import '../constants/bootstrap';
@import '~bootstrap/scss/bootstrap';
.btn {
border-radius: 0;
&.disabled, &:disabled {
opacity: 1 !important;
cursor: not-allowed;
}
}
.form-control {
border-radius: 0;
}
.table th, .table td {
vertical-align: middle;
}
@include media-breakpoint-up(xs) {
html {
font-size: 1rem;
}
}
@include media-breakpoint-up(sm) {
html {
font-size: 1rem;
}
}
@include media-breakpoint-up(md) {
html {
font-size: 1rem;
}
}
@include media-breakpoint-up(lg) {
html {
font-size: 0.9rem;
}
}
assets/scss/components/bootstrap-mixins.scss
// Import this file when you need to use Bootstrap mixins.
@import '~bootstrap/scss/_functions';
@import '~bootstrap/scss/_variables';
@import '~bootstrap/scss/mixins/_breakpoints';
assets/js/components/helpers/jquery/functions.js
import $ from 'jquery';
/*
Returns true if given element exists, false otherwise.
Example: $("#test").exists() will return false if there is no element with id="test" in DOM.
*/
$.fn.exists = function () {
return this.length !== 0;
};
assets/js/components/helpers/jquery/selectors.js
import $ from 'jquery';
export const body = $('body');
templates/_base.html.twig
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
<!--...-->
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
During dev:
$ node_modules/.bin/encore dev --watch
On production server:
$ node_modules/.bin/encore production
You could create scripts with these commands, like so in package.json:
"scripts": {
"encore:dev": "encore dev --watch",
"encore:prod": "encore production",
},
Then you can execute them by running npm run encore:dev
and npm run encore:prod
.
webpack.config.js
.addEntry('my-page', './assets/js/my-page.js')
assets/scss/views/my-page.scss (example)
@import '../../components/bootstrap-mixins';
@import '../../constants/app';
body {
background-color: $custom-color-primary;
}
@include media-breakpoint-down(md) {
body {
background-color: $custom-color-secondary;
}
}
assets/js/views/my-page.js
import '../scss/my-page.scss';
// Your JS
templates/my_page.html.twig
{% extends 'base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
{{ encore_entry_link_tags('my-page') }}
{% endblock %}
{% block body %}
<!--...-->
{% endblock %}
{% block javascripts %}
{{ parent() }}
{{ encore_entry_script_tags('my-page') }}
{% endblock %}