Skip to content

[WCM] Encore updates for Webpack 4 #10322

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion frontend.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,18 @@ Adding more Features
* :doc:`PostCSS and autoprefixing </frontend/encore/postcss>`
* :doc:`Enabling React.js </frontend/encore/reactjs>`
* :doc:`Enabling Vue.js (vue-loader) </frontend/encore/vuejs>`
* :doc:`/frontend/encore/copy-files`
* :doc:`Configuring Babel </frontend/encore/babel>`
* :doc:`Source maps </frontend/encore/sourcemaps>`
* :doc:`Enabling TypeScript (ts-loader) </frontend/encore/typescript>`

Optimizing
..........

* :doc:`Versioning (and the manifest.json file) </frontend/encore/versioning>`
* :doc:`Versioning (and the entrypoints.json/manifest.json files) </frontend/encore/versioning>`
* :doc:`Using a CDN </frontend/encore/cdn>`
* :doc:`/frontend/encore/code-splitting`
* :doc:`/frontend/encore/split-chunks`
* :doc:`Creating a "Shared" entry for re-used modules </frontend/encore/shared-entry>`
* :doc:`/frontend/encore/url-loader`

Expand Down
48 changes: 27 additions & 21 deletions frontend/encore/babel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ Configuring Babel
=================

`Babel`_ is automatically configured for all ``.js`` and ``.jsx`` files via the
``babel-loader`` with sensible defaults (e.g. with the ``env`` preset and
``react`` if requested).
``babel-loader`` with sensible defaults (e.g. with the ``@babel/preset-env`` and
``@babel/preset-react`` if requested).

Need to extend the Babel configuration further? The easiest way is via
``configureBabel()``:
Expand All @@ -16,17 +16,37 @@ Need to extend the Babel configuration further? The easiest way is via
Encore
// ...

// first, install any presets you want to use (e.g. yarn add babel-preset-es2017)
// then, modify the default Babel configuration
.configureBabel(function(babelConfig) {
// add additional presets
babelConfig.presets.push('es2017');
// babelConfig.presets.push('@babel/preset-flow');

// no plugins are added by default, but you can add some
// babelConfig.plugins.push('styled-jsx/babel');
}, {
// node_modules is not processed through Babel by default
// but you can whitelist specific modules to process
// include_node_modules: ['foundation-sites']

// or completely control the exclude
// exclude: /bower_components/
})
;

Configuring Browser Targets
---------------------------

The ``@babel/preset-env`` preset rewrites your JavaScript so that the final syntax
will work in whatever browsers you want. To configure the browsers that you need
to support, see :ref:`browserslist_package_config`.

After change our "browerslist" config, you will need to manually remove the babel
cache directory:

.. code-block:: terminal

$ On Unix run this command. On Windows, clear this directory manually
$ rm -rf node_modules/.cache/babel-loader/

Creating a .babelrc File
------------------------

Expand All @@ -37,21 +57,7 @@ Babel, but it has a downside: as soon as a ``.babelrc`` file is present,
if you call ``Encore.enableReactPreset()``, the ``react`` preset will *not*
automatically be added to Babel: you must add it yourself in ``.babelrc``.

An example ``.babelrc`` file might look like this:

.. code-block:: json

{
presets: [
['env', {
modules: false,
targets: {
browsers: '> 1%',
uglify: true
},
useBuiltIns: true
}]
]
}
As soon as a ``.babelrc`` file is present, it will take priority over the Babel
configuration added by Encore.

.. _`Babel`: http://babeljs.io/
29 changes: 11 additions & 18 deletions frontend/encore/bootstrap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,29 +41,17 @@ Bootstrap JavaScript requires jQuery and Popper.js, so make sure you have this i

.. code-block:: terminal

$ yarn add jquery --dev
$ yarn add popper.js --dev
$ yarn add jquery popper.js --dev

Next, make sure to call ``.autoProvidejQuery()`` in your ``webpack.config.js`` file:

.. code-block:: diff

// webpack.config.js
Encore
// ...
+ .autoProvidejQuery()
;

This is needed because Bootstrap expects jQuery to be available as a global
variable. Now, require bootstrap from any of your JavaScript files:
Now, require bootstrap from any of your JavaScript files:

.. code-block:: javascript

// app.js

const $ = require('jquery');
// JS is equivalent to the normal "bootstrap" package
// no need to set this to a variable, just require it
// this "modifies" the jquery module: adding behavior to it
// the bootstrap module doesn't export/return anything
require('bootstrap');

// or you can include specific pieces
Expand All @@ -74,8 +62,13 @@ variable. Now, require bootstrap from any of your JavaScript files:
$('[data-toggle="popover"]').popover();
});

Thanks to ``autoProvidejQuery()``, you can require any other jQuery
plugins in a similar way:
Using other Bootstrap / jQuery Plugins
--------------------------------------

If you need to use jQuery plugins that work well with jQuery, you may need to use
Encore's :ref:`autoProvidejQuery() <encore-autoprovide-jquery>` method so that
these plugins know where to find jQuery. Then, you can include the needed JavaScript
and CSS like normal:

.. code-block:: javascript

Expand Down
12 changes: 3 additions & 9 deletions frontend/encore/cdn.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@ e.g. ``https://my-cool-app.com.global.prod.fastly.net/dashboard.js``.
directly from your web server.

You *do* need to make sure that the ``script`` and ``link`` tags you include on your
pages also use the CDN. Fortunately, the ``manifest.json`` paths are updated to
point to the CDN. In Symfony, as long as you've configured
:doc:`Asset Versioning </frontend/encore/versioning>`, you're done! The ``manifest.json``
file includes the full CDN URL:

.. code-block:: twig

{# Your script/link tags don't need to change at all to support the CDN #}
<script src="{{ asset('build/dashboard.js') }}"></script>
pages also use the CDN. Fortunately, the
:ref:`entrypoints.json <encore-entrypointsjson-simple-description>` paths are updated
to include the full URL to the CDN.
64 changes: 64 additions & 0 deletions frontend/encore/code-splitting.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
Async Code Splitting
====================

When you require/import a JavaScript or CSS module, Webpack compiles that code into
the final JavaScript or CSS file. Usually, that's exactly what you want. But what
if you only need to use a piece of code under certain conditions? For example,
what if you want to use `video.js`_ to play a video, but only once a user has
clicked a link:

.. code-block:: javascript

// assets/js/app.js

import $ from 'jquery';
// a fictional "large" module (e.g. it imports video.js internally)
import VideoPlayer from './components/VideoPlayer';

$('.js-open-video').on('click', function() {
// use the larger VideoPlayer module
const player = new VideoPlayer('some-element');
});

In this example, the VidePlayer module and everything it imports will be packaged
into the final, built JavaScript file, even though it may not be very common for
someone to actually need it. A better solution is to use `dynamic imports`_: load
the code via AJAX when it's needed:

.. code-block:: javascript

// assets/js/app.js

import $ from 'jquery';

$('.js-open-video').on('click', function() {
// you could start a loading animation here

// use import() as a function - it returns a Promise
import('./components/VideoPlayer').then(({ default: VideoPlayer }) => {
// you could stop a loading animation here

// use the larger VideoPlayer module
const player = new VideoPlayer('some-element');

}).catch(error => 'An error occurred while loading the component');
});

By using ``import()`` like a function, the module will be downloaded async and
the ``.then()`` callback will be executed when it's finished. The ``VideoPlayer``
argument to the callback will be the loaded module. In other words, it works like
normal AJAX calls! Behind the scenes, Webpack will package the ``VideoPlayer`` module
into a separate file (e.g. ``0.js``) so it can be downloaded. All the details are
handled for you.

The ``{ default: VideoPlayer }`` part may look strange. When using the async
import, your ``.then()`` callback is passed an object, where the *actual* module
is on a ``.default`` key. There are reasons why this is done, but it does look
quirky. The ``{ default: VideoPlayer }`` code makes sure that the ``VideoPlayer``
module we want is read from this ``.default`` property.

For more details and configuration options, see `dynamic imports`_ on Webpack's
documentation.

.. _`video.js`: https://videojs.com/
.. _`dynamic imports`: https://webpack.js.org/guides/code-splitting/#dynamic-imports
66 changes: 66 additions & 0 deletions frontend/encore/copy-files.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
Copying & Referencing Images
============================

Need to reference a static file - like the path to an image for an ``img`` tag?
That can be tricky if you store your assets outside of the public document root.
Fortunately, depending on your situation, there is a solution!

Referencing Images from Inside a Webpacked JavaScript File
----------------------------------------------------------

To reference an image tag from inside a JavaScript file, *require* the file:

.. code-block:: javascript

// assets/js/app.js

// returns the final, public path to this file
// path is relative to this file - e.g. assets/images/logo.png
const logoPath = require('../images/logo.png');

var html = `<img src="${logoPath}">`;

When you ``require`` (or ``import``) an image file, Webpack copies it into your
output directory and returns the final, *public* path to that file.

Referencing Image files from a Template
---------------------------------------

To reference an image file from outside of a JavaScript file that's processed by
Webpack - like a template - you can use the ``copyFiles()`` method to copy those
files into your final output directory.

.. code-block:: diff

// webpack.config.js

Encore
// ...
.setOutputPath('web/build/')

+ .copyFiles({
+ from: './assets/images',
+
+ // optional target path, relative to the output dir
+ //to: 'images/[path][name].[ext]',
+
+ // only copy files matching this pattern
+ //pattern: /\.(png|jpg|jpeg)$/
+ })

This will copy all files from ``assets/images`` into ``web/build`` (the output
path). If you have :doc:`versioning enabled <versioning>`, the copied files will
include a hash based on their content.

To render inside Twig, use the ``asset()`` function:

{# assets/images/logo.png was copied to web/build/logo.png #}
<img src="{{ asset('build/logo.png') }}"

{# assets/images/subdir/logo.png was copied to web/build/subdir/logo.png #}
<img src="{{ asset('build/subdir/logo.png') }}"

Make sure you've enabled the :ref:`json_manifest_path <load-manifest-files>` option,
which tells the ``asset()`` function to read the final paths from the ``manifest.json``
file. If you're not sure what path argument to pass to the ``asset()`` function,
find the file in ``manifest.json`` and use the *key* as the argument.
76 changes: 16 additions & 60 deletions frontend/encore/css-preprocessors.rst
Original file line number Diff line number Diff line change
@@ -1,29 +1,7 @@
CSS Preprocessors: Sass, LESS, etc.
===================================
CSS Preprocessors: Sass, LESS, Stylus, etc.
===========================================

Using Sass
----------

To use the Sass pre-processor, install the dependencies:

.. code-block:: terminal

$ yarn add --dev sass-loader node-sass

And enable it in ``webpack.config.js``:

.. code-block:: javascript

// webpack.config.js
// ...

Encore
// ...
.enableSassLoader()
;

That's it! All files ending in ``.sass`` or ``.scss`` will be pre-processed. You
can also pass options to ``sass-loader``:
To use the Sass, LESS or Stylus pre-processors, enable the one you want in ``webpack.config.js``:

.. code-block:: javascript

Expand All @@ -32,46 +10,24 @@ can also pass options to ``sass-loader``:

Encore
// ...
.enableSassLoader(function(options) {
// https://github.com/sass/node-sass#options
// options.includePaths = [...]
});
;

Using LESS
----------

To use the LESS pre-processor, install the dependencies:

.. code-block:: terminal
// enable just the one you want

$ yarn add --dev less-loader less

And enable it in ``webpack.config.js``:

.. code-block:: javascript

// webpack.config.js
// ...
// processes files ending in .scss or .sass
.enableSassLoader()

Encore
// ...
// processes files ending in .less
.enableLessLoader()
;

That's it! All files ending in ``.less`` will be pre-processed. You can also pass
options to ``less-loader``:
// processes files ending in .styl
.enableStylusLoader()
;

.. code-block:: javascript
Then restart Encore. When you do, it will give you a command you can run to
install any missing dependencies. After running that command and restarting
Encore, you're done!

// webpack.config.js
// ...
You can also pass configuration options to each of the loaders. See the
`Encore's index.js file`_ for detailed documentation.

Encore
// ...
.enableLessLoader(function(options) {
// https://github.com/webpack-contrib/less-loader#examples
// http://lesscss.org/usage/#command-line-usage-options
// options.relativeUrls = false;
});
;
.. _`Encore's index.js file`: https://github.com/symfony/webpack-encore/blob/master/index.js
Loading