Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save thibaut-decherit/73c329f50bc118981f1623d3fc7d97fa to your computer and use it in GitHub Desktop.
Save thibaut-decherit/73c329f50bc118981f1623d3fc7d97fa to your computer and use it in GitHub Desktop.
Symfony - NPM, Webpack Encore and SASS Install (for Bootstrap 4)

Symfony - NPM, Webpack Encore and SASS Install (for Bootstrap 4)

If new computer start with these two:

$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash

$ sudo apt-get install -y nodejs

Then:

See:

$ 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 structure

  • /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
      • /views (js for specific views)
        • /feature-1 (e.g. user-account, search-engines...)
        • /feature-2
        • page-1.js
    • /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
      • /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)

At project root create this file:

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 %}

Compilation commands:

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.

Example of page-tied assets

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 %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment