diff --git a/frontend/encore/advanced-config.rst b/frontend/encore/advanced-config.rst index e66440ac43f..74b11d559dd 100644 --- a/frontend/encore/advanced-config.rst +++ b/frontend/encore/advanced-config.rst @@ -93,7 +93,7 @@ prefer to build configs separately, pass the ``--config-name`` option: .. code-block:: terminal - $ yarn run encore dev --config-name firstConfig + $ yarn encore dev --config-name firstConfig .. _`configuration options`: https://webpack.js.org/configuration/ .. _`Webpack's watchOptions`: https://webpack.js.org/configuration/watch/#watchoptions diff --git a/frontend/encore/installation-no-flex.rst b/frontend/encore/installation-no-flex.rst new file mode 100644 index 00000000000..c495ebdc968 --- /dev/null +++ b/frontend/encore/installation-no-flex.rst @@ -0,0 +1,100 @@ +Encore Installation (without Symfony Flex) +========================================== + +.. tip: + + If your project uses Symfony Flex, read :doc:`/frontend/encore/installation` + for easier instructions. + +Installing Encore +----------------- + +Install Encore into your project via Yarn: + +.. code-block:: terminal + + $ yarn add @symfony/webpack-encore --dev + +.. note:: + + If you prefer to use `npm`_, no problem! Run ``npm install @symfony/webpack-encore --save-dev``. + +This command creates (or modifies) a ``package.json`` file and downloads dependencies +into a ``node_modules/`` directory. Yarn also creates/updates a ``yarn.lock`` +(called ``package-lock.json`` if you use npm version 5+). + +.. tip:: + + You *should* commit ``package.json`` and ``yarn.lock`` (or ``package-lock.json`` + if using npm 5) to version control, but ignore ``node_modules/``. + +Creating the webpack.config.js File +----------------------------------- + +Next, create a new ``webpack.config.js`` file at the root of your project: + +.. code-block:: js + + var Encore = require('@symfony/webpack-encore'); + + 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/') + + /* + * ENTRY CONFIG + * + * Add 1 entry for each "page" of your app + * (including one that's included on every page - e.g. "app") + * + * Each entry will result in one JavaScript file (e.g. app.js) + * and one CSS file (e.g. app.css) if you JavaScript imports CSS. + */ + .addEntry('app', './assets/js/app.js') + //.addEntry('page1', './assets/js/page1.js') + //.addEntry('page2', './assets/js/page2.js') + + .cleanupOutputBeforeBuild() + .enableSourceMaps(!Encore.isProduction()) + // enables hashed filenames (e.g. app.abc123.css) + .enableVersioning(Encore.isProduction()) + + // uncomment if you use TypeScript + //.enableTypeScriptLoader() + + // uncomment if you use Sass/SCSS files + //.enableSassLoader() + + // uncomment if you're having problems with a jQuery plugin + //.autoProvidejQuery() + ; + + module.exports = Encore.getWebpackConfig(); + +Next, create a new ``assets/js/app.js`` file with some basic JavaScript *and* +import some JavaScript: + +.. code-block:: javascript + + // assets/js/app.js + + require('../css/app.css'); + + console.log('Hello Webpack Encore'); + +And the new ``assets/css/app.css`` file: + +.. code-block:: css + + // assets/css/app.css + body { + background-color: lightgray; + } + +You'll customize and learn more about these file in :doc:`/frontend/encore/simple-example`. + +.. _`npm`: https://www.npmjs.com/ diff --git a/frontend/encore/installation.rst b/frontend/encore/installation.rst index e0e13882d16..31bce801ea6 100644 --- a/frontend/encore/installation.rst +++ b/frontend/encore/installation.rst @@ -1,46 +1,22 @@ -Encore Installation -=================== +Installing Encore (with Symfony Flex) +===================================== -First, make sure you `install Node.js`_ and also the `Yarn package manager`_. +.. tip: -Then, install Encore into your project with Yarn: + If your project does **not** use Symfony Flex, read :doc:`/frontend/encore/installation-no-flex`. -.. code-block:: terminal - - $ yarn add @symfony/webpack-encore --dev - -.. note:: - - If you want to use `npm`_ instead of `yarn`_: - - .. code-block:: terminal - - $ npm install @symfony/webpack-encore --save-dev +First, make sure you `install Node.js`_ and also the `Yarn package manager`_. Then +run: -.. tip:: - - If you are using Flex for your project, you can initialize your project for Encore via: - - .. code-block:: terminal - - $ composer require symfony/webpack-encore-pack - $ yarn install - - This will create a ``webpack.config.js`` file, add the ``assets/`` directory, and add ``node_modules/`` to - ``.gitignore``. - -This command creates (or modifies) a ``package.json`` file and downloads dependencies -into a ``node_modules/`` directory. When using Yarn, a file called ``yarn.lock`` -is also created/updated. When using npm 5, a ``package-lock.json`` file is created/updated. +.. code-block:: terminal -.. tip:: + $ composer require webpack-encore + $ yarn install - You should commit ``package.json`` and ``yarn.lock`` (or ``package-lock.json`` - if using npm 5) to version control, but ignore ``node_modules/``. +This will create a ``webpack.config.js`` file, add the ``assets/`` directory, and +add ``node_modules/`` to ``.gitignore``. -Next, create your ``webpack.config.js`` in :doc:`/frontend/encore/simple-example`! +Nice work! Write your first JavaScript and CSS by reading :doc:`/frontend/encore/simple-example`! .. _`install Node.js`: https://nodejs.org/en/download/ .. _`Yarn package manager`: https://yarnpkg.com/lang/en/docs/install/ -.. _`npm`: https://www.npmjs.com/ -.. _`yarn`: https://yarnpkg.com/ diff --git a/frontend/encore/shared-entry.rst b/frontend/encore/shared-entry.rst index 1eacdb76261..6958a331f43 100644 --- a/frontend/encore/shared-entry.rst +++ b/frontend/encore/shared-entry.rst @@ -3,53 +3,55 @@ Creating a Shared Commons Entry Suppose you have multiple entry files and *each* requires ``jquery``. In this case, *each* output file will contain jQuery, slowing down your user's experience. -In this case, you can *extract* these common libraries to a "shared" entry file -that's included on every page: +To solve this, you can *extract* the common libraries to a "shared" entry file +that's included on every page. -.. code-block:: javascript +Suppose you already have an entry called ``app`` that's included on every page. +Update your code to use ``createSharedEntry()``: + +.. code-block:: diff Encore // ... - .addEntry('page1', 'assets/js/page1.js') - .addEntry('page2', 'assets/js/page2.js') - - // this creates a 'vendor.js' file with jquery and the bootstrap JS module - // these modules will *not* be included in page1.js or page2.js anymore - .createSharedEntry('vendor', [ - 'jquery', - 'bootstrap', - - // you can also extract CSS - this will create a 'vendor.css' file - // this CSS will *not* be included in page1.css or page2.css anymore - 'bootstrap/scss/bootstrap.scss' - ]) + - .addEntry('app', 'assets/js/app.js') + + .createSharedEntry('app', 'assets/js/app.js') + .addEntry('homepage', './assets/js/homepage.js') + .addEntry('blog', './assets/js/blog.js') + .addEntry('store', './assets/js/store.js') -As soon as you make this change, you need to include two extra JavaScript files -on your page before any other JavaScript file: +As soon as you make this change, you need to include *one* extra JavaScript file +in your layout, *before* ``app.js``: .. _encore-shared-entry-script: .. code-block:: twig - + {# templates/base.html.twig #} + - - - - - + + + + + + +Before making this change, if both ``app.js`` and ``store.js`` require ``jquery``, +then ``jquery`` would be packaged into *both* files, which is wasteful. By making +``app.js`` your "shard" entry, *any* code required by ``app.js`` (like jQuery) will +*no longer* be packaged into any other files. The same is true for any CSS. -The ``vendor.js`` file contains all the common code that has been extracted from -the other files, so it's obvious that it must be included. The other file (``manifest.js``) -is less obvious: it's needed so that Webpack knows how to load those shared modules. +Because ``app.js`` contains all the common code that other entry files depend on, +it's obvious that its script (and link) tag must be on every page. The other file +(``manifest.js``) is less obvious: it's needed so that Webpack knows how to load +these shared modules. .. tip:: - The ``vendor.js`` file works best when its contents are changed *rarely* + The ``app.js`` file works best when its contents are changed *rarely* and you're using :ref:`long-term caching `. Why? - If ``vendor.js`` contains application code that *frequently* changes, then + If ``app.js`` contains application code that *frequently* changes, then (when using versioning), its filename hash will frequently change. This means your users won't enjoy the benefits of long-term caching for this file (which is generally quite large). diff --git a/frontend/encore/simple-example.rst b/frontend/encore/simple-example.rst index 38a3a06cd9e..609e095cc75 100644 --- a/frontend/encore/simple-example.rst +++ b/frontend/encore/simple-example.rst @@ -1,30 +1,38 @@ -First Example -============= +Encore: Setting up your Project +=============================== -Imagine you have a simple project with one CSS and one JS file, organized into -an ``assets/`` directory: +After :doc:`installing Encore `, your app already has one +CSS and one JS file, organized into an ``assets/`` directory: * ``assets/js/app.js`` * ``assets/css/app.css`` -With Encore, you should think of CSS as a *dependency* of your JavaScript. This means, -you will *require* whatever CSS you need from inside JavaScript: +With Encore, think of your ``app.js`` file as a standalone JavaScript +application: it will *require* all of the dependencies it needs (e.g. jQuery), +*including* any CSS. Your ``app.js`` file is already doing this with a special +``require`` function: .. code-block:: javascript // assets/js/app.js + // ... + + // var $ = require('jquery'); + require('../css/app.css'); - // ...rest of JavaScript code here + // ... the rest of your JavaScript... -With Encore, we can easily minify these files, pre-process ``app.css`` -through PostCSS and a *lot* more. +Encore's job is simple: to read *all* of ``require`` statements and create one +final ``app.js`` (and ``app.css``) that contain *everything* your app needs. Of +course, Encore can do a lot more: minify files, pre-process Sass/LESS, support +React, Vue.js and a *lot* more. Configuring Encore/Webpack -------------------------- -Create a new file called ``webpack.config.js`` at the root of your project. -Inside, use Encore to help generate your Webpack configuration. +Everything in Encore is configured via a ``webpack.config.js`` file at the root +of your project. It already holds the basic config you need: .. code-block:: javascript @@ -32,71 +40,49 @@ Inside, use Encore to help generate your Webpack configuration. var Encore = require('@symfony/webpack-encore'); Encore - // the project directory where all compiled assets will be stored + // directory where compiled assets will be stored .setOutputPath('web/build/') - - // the public path used by the web server to access the previous directory + // public path used by the web server to access the output path .setPublicPath('/build') - // will create web/build/app.js and web/build/app.css .addEntry('app', './assets/js/app.js') - // allow legacy applications to use $/jQuery as a global variable - .autoProvidejQuery() - - // enable source maps during development - .enableSourceMaps(!Encore.isProduction()) - - // empty the outputPath dir before each build - .cleanupOutputBeforeBuild() - - // show OS notifications when builds finish/fail - .enableBuildNotifications() - - // create hashed filenames (e.g. app.abc123.css) - // .enableVersioning() - - // allow sass/scss files to be processed - // .enableSassLoader() + // ... ; - // export the final configuration - module.exports = Encore.getWebpackConfig(); + // ... -This is already a rich setup: it outputs 2 files and enables source maps -to help debugging. +They *key* part is ``addEntry()``: this tells Encore to load the ``assets/js/app.js`` +file and follow *all* of the ``require`` statements. It will then package everything +together and - thanks to the first ``app`` argument - output final ``app.js`` and +``app.css`` files into the ``public/build`` directory. .. _encore-build-assets: -To build the assets, use the ``encore`` executable: +To build the assets, run: .. code-block:: terminal # compile assets once - $ ./node_modules/.bin/encore dev + $ yarn encore dev - # recompile assets automatically when files change - $ ./node_modules/.bin/encore dev --watch + # or, recompile assets automatically when files change + $ yarn encore dev --watch - # compile assets, but also minify & optimize them - $ ./node_modules/.bin/encore production - - # shorter version of the above 3 commands - $ yarn run encore dev - $ yarn run encore dev --watch - $ yarn run encore production + # on deploy, create a production build + $ yarn encore production .. note:: - Re-run ``encore`` each time you update your ``webpack.config.js`` file. + Stop and restart ``encore`` each time you update your ``webpack.config.js`` file. -After running one of these commands, you can now add ``script`` and ``link`` tags -to the new, compiled assets (e.g. ``/build/app.css`` and ``/build/app.js``). -In Symfony, use the ``asset()`` helper: +Congrats! You now have two new files! Next, add a ``script`` and ``link`` tag +to the new, compiled assets (e.g. ``/build/app.css`` and ``/build/app.js``) to +your layout. In Symfony, use the ``asset()`` helper: .. code-block:: twig - {# base.html.twig #} + {# templates/base.html.twig #} @@ -109,34 +95,6 @@ In Symfony, use the ``asset()`` helper: -Using Sass ----------- - -Instead of using plain CSS you can also use Sass. In order to do so, change the -extension of the ``app.css`` file to ``.sass`` or ``.scss`` (based on the syntax -you want to use): - -.. code-block:: diff - - // assets/js/app.js - - require('../css/app.css'); - + require('../css/app.scss'); - -And enable the Sass pre-processor: - -.. code-block:: diff - - // webpack.config.js - Encore - // ... - - // allow sass/scss files to be processed - - // .enableSassLoader() - + .enableSassLoader() - -Using ``enableSassLoader()`` requires to install additional packages, but Encore -will tell you *exactly* which ones when running it. - Requiring JavaScript Modules ---------------------------- @@ -163,7 +121,7 @@ Great! Use ``require()`` to import ``jquery`` and ``greet.js``: // assets/js/app.js // loads the jquery package from node_modules - const $ = require('jquery'); + var $ = require('jquery'); // import the function from greet.js (the .js extension is optional) // ./ (or ../) means to look for a local file @@ -174,30 +132,116 @@ Great! Use ``require()`` to import ``jquery`` and ``greet.js``: }); That's it! When you build your assets, jQuery and ``greet.js`` will automatically -be added to the output file (``app.js``). For common libraries like jQuery, you -may want to :doc:`create a shared entry ` for better -performance. +be added to the output file (``app.js``). + +The import and export Statements +-------------------------------- -Multiple JavaScript Entries ---------------------------- +Instead of using ``require`` and ``module.exports`` like shown above, JavaScript +has an alternate syntax, which is a more accepted standard. Choose whichever you +want: they function identically: -The previous example is the best way to deal with SPA (Single Page Applications) -and very simple applications. However, as your app grows, you may want to have -page-specific JavaScript or CSS (e.g. homepage, blog, store, etc.). To handle this, -add a new "entry" for each page that needs custom JavaScript or CSS: +To export values, use ``exports``: + +.. code-block:: diff + + // assets/js/greet.js + - module.exports = function(name) { + + export default function(name) { + return `Yo yo ${name} - welcome to Encore!`; + }; + +To import values, use ``import``: + +.. code-block:: diff + + // assets/js/app.js + - var $ = require('jquery'); + + import $ from 'jquery'; + + - require('../css/app.css'); + + import '../css/app.css'; + +.. _multiple-javascript-entries: + +Page-Specific JavaScript or CSS (Multiple Entries) +-------------------------------------------------- + +So far, you only have one final JavaScript file: ``app.js``. For simple apps or +SPA's (Single Page Applications), that might be fine! However, as your app grows, +you may want to have page-specific JavaScript or CSS (e.g. homepage, blog, store, +etc.). To handle this, add a new "entry" for each page that needs custom JavaScript +or CSS: + +.. code-block:: diff + + Encore + // ... + .addEntry('app', './assets/js/app.js') + + .addEntry('homepage', './assets/js/homepage.js') + + .addEntry('blog', './assets/js/blog.js') + + .addEntry('store', './assets/js/store.js') + // ... + +Encore will now render new ``homepage.js``, ``blog.js`` and ``store.js`` files. +Add a ``script`` tag to each of these only on the page where they are needed. + +.. tip:: + + Remember to restart Encore each time you update your ``webpack.config.js`` file. + +If any entry requires CSS/Sass files (e.g. ``homepage.js`` requires +``assets/css/homepage.scss``), a CSS file will *also* be output (e.g. ``build/homepage.css``). +Add a ``link`` to the page where that CSS is needed. + +To avoid duplicating the same code in different entry files, see +:doc:`create a shared entry `. + +Using Sass +---------- + +Instead of using plain CSS you can also use Sass. To use Sass, rename +the ``app.css`` file to ``app.scss``. Update the ``require`` statement: + +.. code-block:: diff + + // assets/js/app.js + - require('../css/app.css'); + + require('../css/app.scss'); + +Then, tell Encore to enable the Sass pre-processor: + +.. code-block:: diff + + // webpack.config.js + Encore + // ... + + + .enableSassLoader() + ; + +Using ``enableSassLoader()`` requires to install additional packages, but Encore +will tell you *exactly* which ones when running it. Encore also supports +LESS and Stylus. See :doc:`/frontend/encore/css-preprocessors`. + +Compiling Only a CSS File +------------------------- + +To compile CSS together, you should generally follow the pattern above: use ``addEntry()`` +to point to a JavaScript file, then require the CSS needed from inside of that. +However, *if* you want to only compile a CSS file, that's also possible via +``addStyleEntry()``: .. code-block:: javascript + // webpack/config.js Encore // ... - .addEntry('homepage', './assets/js/homepage.js') - .addEntry('blog', './assets/js/blog.js') - .addEntry('store', './assets/js/store.js') + + .addStyleEntry('some_page', './assets/css/some_page.css') ; -If those entries include CSS/Sass files (e.g. ``homepage.js`` requires -``assets/css/homepage.scss``), two files will be generated for each: -(e.g. ``build/homepage.js`` and ``build/homepage.css``). +This will output a new ``some_page.css``. Keep Going! -----------