diff --git a/frontend.rst b/frontend.rst index 235702beada..053fd8aec33 100644 --- a/frontend.rst +++ b/frontend.rst @@ -8,34 +8,45 @@ more advanced - like scaffolding your front-end with a tool like Next.js. However, Symfony *does* come with two powerful options to help you build a modern, fast frontend, *and* enjoy the process: -* :ref:`Webpack Encore ` is a powerful tool built with `Node.js`_ - on top of `Webpack`_ that allows you to write modern CSS & JavaScript and handle - things like JSX (React), Vue or TypeScript. - -* :ref:`AssetMapper `, is a production-ready simpler alternative - to Webpack Encore that runs entirely in PHP. - -================================ ================= ====================================================== - Encore AssetMapper -================================ ================= ====================================================== -Production Ready? yes yes -Stable? yes yes -Requirements Node.js none: pure PHP -Requires a build step? yes no -Works in all browsers? yes yes -Supports `Stimulus/UX`_ yes yes -Supports Sass/Tailwind yes :ref:`yes ` -Supports React, Vue, Svelte? yes yes :ref:`[1] ` -Supports TypeScript yes no :ref:`[1] ` -================================ ================= ====================================================== +* :ref:`AssetMapper ` (recommended for new projects) runs + entirely in PHP, doesn't require any build step and leverages modern web standards. + +* :ref:`Webpack Encore ` is built with `Node.js`_ + on top of `Webpack`_. + +================================ ================================== ========== + AssetMapper Encore +================================ ================================== ========== +Production Ready? yes yes +Stable? yes yes +Requirements none Node.js +Requires a build step? no yes +Works in all browsers? yes yes +Supports `Stimulus/UX`_ yes yes +Supports Sass/Tailwind :ref:`yes ` yes +Supports React, Vue, Svelte? yes :ref:`[1] ` yes +Supports TypeScript :ref:`yes ` yes +================================ ================================== ========== .. _ux-note-1: -**[1]** Using JSX (React), Vue or TypeScript with AssetMapper is possible, but you'll +**[1]** Using JSX (React), Vue, etc with AssetMapper is possible, but you'll need to use their native tools for pre-compilation. Also, some features (like Vue single-file components) cannot be compiled down to pure JavaScript that can be executed by a browser. +.. _frontend-asset-mapper: + +AssetMapper (Recommended) +------------------------- + +AssetMapper is the recommended system for handling your assets. It runs entirely +in PHP with *no* complex build step or dependencies. It does this by leveraging +the ``importmap`` feature of your browser, which is available in all browsers thanks +to a polyfill. + +:doc:`Read the AssetMapper Documentation ` + .. _frontend-webpack-encore: Webpack Encore @@ -50,83 +61,15 @@ It *wraps* Webpack, giving you a clean & powerful API for bundling JavaScript mo pre-processing CSS & JS and compiling and minifying assets. Encore gives you a professional asset system that's a *delight* to use. -Encore is inspired by `Webpacker`_ and `Mix`_, but stays in the spirit of Webpack: -using its features, concepts and naming conventions for a familiar feel. It aims -to solve the most common Webpack use cases. - -.. tip:: - - Encore is made by `Symfony`_ and works *beautifully* in Symfony applications. - But it can be used in any PHP application and even with other server-side - programming languages! - -.. _encore-toc: - -Encore Documentation --------------------- - -Getting Started -............... - -* :doc:`Installation ` -* :doc:`Using Webpack Encore ` - -Adding more Features -.................... - -* :doc:`CSS Preprocessors: Sass, LESS, etc. ` -* :doc:`PostCSS and autoprefixing ` -* :doc:`Enabling React.js ` -* :doc:`Enabling Vue.js (vue-loader) ` -* :doc:`/frontend/encore/copy-files` -* :doc:`Configuring Babel ` -* :doc:`Source maps ` -* :doc:`Enabling TypeScript (ts-loader) ` - -Optimizing -.......... - -* :doc:`Versioning (and the entrypoints.json/manifest.json files) ` -* :doc:`Using a CDN ` -* :doc:`/frontend/encore/code-splitting` -* :doc:`/frontend/encore/split-chunks` -* :doc:`/frontend/encore/url-loader` - -Guides -...... - -* :doc:`Using Bootstrap CSS & JS ` -* :doc:`jQuery and Legacy Applications ` -* :doc:`Passing Information from Twig to JavaScript ` -* :doc:`webpack-dev-server and Hot Module Replacement (HMR) ` -* :doc:`Adding custom loaders & plugins ` -* :doc:`Advanced Webpack Configuration ` -* :doc:`Using Encore in a Virtual Machine ` - -Issues & Questions -.................. - -* :doc:`FAQ & Common Issues ` - -Full API -........ - -* `Full API`_ - -.. _frontend-asset-mapper: - -AssetMapper ------------ - -AssetMapper is an alternative to Webpack Encore that runs entirely in PHP -without any complex build steps. It leverages the ``importmap`` feature of -your browser, which is available in all browsers thanks to a polyfill. - -:doc:`Read the AssetMapper Documentation ` +:doc:`Read the Encore Documentation ` Stimulus & Symfony UX Components -------------------------------- +Once you've installed AssetMapper or Encore, it's time to start building your +front-end. You can write your JavaScript however you want, but we recommend +using `Stimulus`_, `Turbo`_ and a set of tools called `Symfony UX`_. + To learn about Stimulus & the UX Components, see: the `StimulusBundle Documentation`_ @@ -139,10 +82,9 @@ Other Front-End Articles .. _`Webpack Encore`: https://www.npmjs.com/package/@symfony/webpack-encore .. _`Webpack`: https://webpack.js.org/ .. _`Node.js`: https://nodejs.org/ -.. _`Webpacker`: https://github.com/rails/webpacker -.. _`Mix`: https://laravel.com/docs/mix -.. _`Symfony`: https://symfony.com/ -.. _`Full API`: https://github.com/symfony/webpack-encore/blob/master/index.js .. _`Webpack Encore screencast series`: https://symfonycasts.com/screencast/webpack-encore .. _StimulusBundle Documentation: https://symfony.com/bundles/StimulusBundle/current/index.html .. _Stimulus/UX: https://symfony.com/bundles/StimulusBundle/current/index.html +.. _Stimulus: https://stimulus.hotwired.dev/ +.. _Turbo: https://turbo.hotwired.dev/ +.. _Symfony UX: https://ux.symfony.com diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index f65aff65c17..a3b51f5e267 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -11,17 +11,17 @@ like the ``import`` statement and ES6 classes. And the HTTP/2 protocol means tha combining your assets to reduce HTTP connections is no longer urgent. This component is a light layer that helps serve your files directly to the browser. -The AssetMapper component has two main features: +The component has two main features: * :ref:`Mapping & Versioning Assets `: All files inside of ``assets/`` - are made available publicly and **versioned**. For example, you can reference + are made available publicly and **versioned**. You can reference ``assets/styles/app.css`` in a template with ``{{ asset('styles/app.css') }}``. The final URL will include a version hash, like ``/assets/styles/app-3c16d9220694c0e56d8648f25e6035e9.css``. * :ref:`Importmaps `: A native browser feature that makes it easier to use the JavaScript ``import`` statement (e.g. ``import { Modal } from 'bootstrap'``) without a build system. It's supported in all browsers (thanks to a shim) - and is part of the `HTML5 standard `_. + and is part of the `HTML standard `_. Installation ------------ @@ -47,12 +47,8 @@ It also *updated* the ``templates/base.html.twig`` file: .. code-block:: diff - {% block stylesheets %} - + - {% endblock %} - {% block javascripts %} - + {{ importmap() }} + + {{ importmap('app') }} {% endblock %} If you're not using Flex, you'll need to create & update these files manually. See @@ -97,30 +93,6 @@ This will physically copy all the files from your mapped directories to ``public/assets/`` so that they're served directly by your web server. See :ref:`Deployment ` for more details. -Paths Inside of CSS Files -~~~~~~~~~~~~~~~~~~~~~~~~~ - -From inside CSS, you can reference other files using the normal CSS ``url()`` -function and a relative path to the target file: - -.. code-block:: css - - /* assets/styles/app.css */ - .quack { - /* file lives at assets/images/duck.png */ - background-image: url('../images/duck.png'); - } - -The path in the final ``app.css`` file will automatically include the versioned URL -for ``duck.png``: - -.. code-block:: css - - /* public/assets/styles/app-3c16d9220694c0e56d8648f25e6035e9.css */ - .quack { - background-image: url('../images/duck-3c16d9220694c0e56d8648f25e6035e9.png'); - } - Debugging: Seeing All Mapped Assets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -183,7 +155,7 @@ All modern browsers support the JavaScript `import statement`_ and modern } } -Thanks to the ``{{ importmap() }}`` Twig function, which you'll learn all about in +Thanks to the ``{{ importmap('app') }}`` Twig function call, which you'll learn about in this section, the ``assets/app.js`` file is loaded & executed by the browser. .. tip:: @@ -213,8 +185,10 @@ This adds the ``bootstrap`` package to your ``importmap.php`` file:: // importmap.php return [ - // ... - + 'app' => [ + 'path' => './assets/app.js', + 'entrypoint' => true, + ], 'bootstrap' => [ 'version' => '5.3.0', ], @@ -224,10 +198,8 @@ This adds the ``bootstrap`` package to your ``importmap.php`` file:: Sometimes, a package - like ``bootstrap`` - will have one or more dependencies, such as ``@popperjs/core``. The ``importmap:require`` command will add both the - main package *and* its dependencies. - -After adding/updating the package in your ``importmap.php`` file, all new packages -will be downloaded into an ``assets/vendor/`` directory. + main package *and* its dependencies. If a package includes a main CSS file, + that will also be added (see :ref:`Handling 3rd-Party CSS `). Now you can import the ``bootstrap`` package like usual: @@ -236,9 +208,10 @@ Now you can import the ``bootstrap`` package like usual: import { Alert } from 'bootstrap'; // ... -It's recommended to ignore the ``assets/vendor/`` directory and not commit it to -your repository. Therefore, you'll need to run the ``php bin/console importmap:install`` -command to download the files on other computers if some files are missing: +All packages in ``importmap.php`` are downloaded into an ``assets/vendor/`` directory, +which should be ignored by git (the Flex recipe adds it to ``.gitignore`` for you). +You'll need to run the ``php bin/console importmap:install`` +command to download the files on other computers if some are missing: .. code-block:: terminal @@ -285,58 +258,68 @@ outputs an `importmap`_: "imports": { "app": "/assets/app-4e986c1a2318dd050b1d47db8d856278.js", "/assets/duck.js": "/assets/duck-1b7a64b3b3d31219c262cf72521a5267.js", - "bootstrap": "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/+esm" + "bootstrap": "/assets/vendor/bootstrap/bootstrap.index-f0935445d9c6022100863214b519a1f2.js" } } -Import maps are a native browser feature. They work in all browsers thanks to -a "shim" file that's included automatically by the AssetMapper component -(all *modern* browsers `support them natively `_). - -When you import ``bootstrap`` from your JavaScript, the browser will look at -the ``importmap`` and see that it should fetch the package from the URL. +Import maps are a native browser feature. When you import ``bootstrap`` from +JavaScript, the browser will look at the ``importmap`` and see that it should +fetch the package from the associated path. .. _automatic-import-mapping: -But where did the ``/assets/duck.js`` import entry come from? Great question! +But where did the ``/assets/duck.js`` import entry come from? That doesn't live +in ``importmap.php``. Great question! The ``assets/app.js`` file above imports ``./duck.js``. When you import a file using a relative path, your browser looks for that file relative to the one importing it. So, it would look for ``/assets/duck.js``. That URL *would* be correct, except that the ``duck.js`` file is versioned. Fortunately, the AssetMapper component -sees that import and adds a mapping from ``/assets/duck.js`` to the correct, versioned +sees the import and adds a mapping from ``/assets/duck.js`` to the correct, versioned filename. The result: importing ``./duck.js`` just works! -Preloading and Initializing "app.js" -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``importmap()`` function also outputs an `ES module shim`_ so that +`older browsers `_ understand importmaps +(see the :ref:`polyfill config `). -In addition to the importmap, the ``{{ importmap() }}`` Twig function also renders -an `ES module shim`_ (see the :ref:`polyfill config `) and -a few other things, like a set of "preloads": +.. _app-entrypoint: -.. code-block:: html +The "app" Entrypoint & Preloading +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - +An "entrypoint" is the main JavaScript file that the browser loads, +and your app starts with one by default:: -In ``importmap.php``, each entry can have a ``preload`` option. If set to ``true``, -a ```` tag is rendered for that entry as well as for -any JavaScript files it imports (this happens for "relative" - ``./`` or ``../`` - -imports only). This is a performance optimization and you can learn more about below -in :ref:`Performance: Add Preloading `. + // importmap.php + return [ + 'app' => [ + 'path' => './assets/app.js', + 'entrypoint' => true, + ], + // ... + ]; .. _importmap-app-entry: -The ``importmap()`` function also renders one more line: +In addition to the importmap, the ``{{ importmap('app') }}`` in +``base.html.twig`` outputs a few other things, including: .. code-block:: html -So far, the snippets shown export an ``importmap`` and even hinted to the -browser that it should preload some files. But the browser hasn't yet been told to -actually parse and execute any JavaScript. This line does that: it imports the -``app`` entry, which causes the code in ``assets/app.js`` to be executed. +This line tells the browser to load the ``app`` importmap entry, which causes the +code in ``assets/app.js`` to be executed. + +The ``importmap()`` function also outputs a set of "preloads": + +.. code-block:: html + + + + +This is a performance optimization and you can learn more about below +in :ref:`Performance: Add Preloading `. Importing Specific Files From a 3rd Party Package ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -354,7 +337,7 @@ and a specific language: hljs.highlightAll(); In this case, adding the ``highlight.js`` package to your ``importmap.php`` file -won't work: whatever your importing - e.g. ``highlight.js/lib/core`` - needs to +won't work: whatever you import - e.g. ``highlight.js/lib/core`` - needs to *exactly* match an entry in the ``importmap.php`` file. Instead, use ``importmap:require`` and pass it the exact paths you need. This @@ -405,65 +388,121 @@ from inside ``app.js``: // things on "window" become global variables window.$ = $; -Handling 3rd-Party CSS ----------------------- +Handling CSS +------------ -With the ``importmap:require`` command, you can quickly use any JavaScript -package. But what about CSS? For example, the ``bootstrap`` package also contains -a CSS file. +.. versionadded:: 6.4 -Including CSS is a bit more manual, but still easy enough. To find the CSS, -we recommend using `jsdelivr.com`_: + The ability to import CSS files was introduced in Symfony 6.4. -#. Search for the package on `jsdelivr.com`_. -#. Once on the package page (e.g. https://www.jsdelivr.com/package/npm/bootstrap), - sometimes the ``link`` tag to the CSS file will already be shown in the "Install" box. -#. If not, click the "Files" tab and find the CSS file you need. For example, - the ``bootstrap`` package has a ``dist/css/bootstrap.min.css`` file. If you're - not sure which file to use, check the ``package.json`` file. Often - this will have a ``main`` or ``style`` key that points to the CSS file. +CSS can be added to your page by importing it from a JavaScript file. The default +``assets/app.js`` already imports ``assets/styles/app.css``: -Once you have the URL, include it in ``base.html.twig``: +.. code-block:: javascript -.. code-block:: diff + // assets/app.js + import '../styles/app.css'; - {% block stylesheets %} - + - - {% endblock %} + // ... -If you'd rather download the CSS file and include it locally, you can do that. -For example, you could manually download, save it to ``assets/vendor/bootstrap.min.css`` -and then include it with: +When you call ``importmap('app')`` in ``base.html.twig``, AssetMapper parses +``assets/app.js`` (and any JavaScript files that *it* imports) looking for ``import`` +statements for CSS files. The final collection of CSS files is rendered onto +the page as ``link`` tags in the order they were imported. -.. code-block:: html+twig +.. note:: - + Importing a CSS file is *not* something that is natively supported by + JavaScript modules and normally causes an error. AssetMapper makes this + work by adding an importmap entry for each CSS file that is valid, but + does nothing. AssetMapper adds a ``link`` tag for each CSS file, but when + the JavaScript executes the ``import`` statement, nothing additional happens. -Lazily Importing CSS from a JavaScript File -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _asset-mapper-3rd-party-css: + +Handling 3rd-Party CSS +~~~~~~~~~~~~~~~~~~~~~~ -When using a bundler like :ref:`Encore `, you can -import CSS from a JavaScript file: +Sometimes a JavaScript package will contain one or more CSS files. For example, +the ``bootstrap`` package has a `dist/css/bootstrap.min.css file`_. + +You can require CSS files in the same way as JavaScript files: + +.. code-block:: terminal + + $ php bin/console importmap:require bootstrap/dist/css/bootstrap.min.css + +To include it on the page, import it from a JavaScript file: .. code-block:: javascript - // this CAN work (keep reading), but will be loaded lazily - import 'swiper/swiper-bundle.min.css'; + // assets/app.js + import 'bootstrap/dist/css/bootstrap.min.css'; -This *can* work with importmaps, but it should *not* be used for critical CSS -that needs to be loaded before the page is rendered because the browser -won't download the CSS until the JavaScript file executed. + // ... -However, if you *do* want to lazily-load a CSS file, you can make this work -by using the ``importmap:require`` command and pointing it at a CSS file. +.. tip:: -.. code-block:: terminal + Some packages - like ``bootstrap`` - advertise that they contain a CSS + file. In those cases, when you ``importmap:require bootstrap``, the + CSS file is also added to ``importmap.php`` for convenience. + +Paths Inside of CSS Files +~~~~~~~~~~~~~~~~~~~~~~~~~ + +From inside CSS, you can reference other files using the normal CSS ``url()`` +function and a relative path to the target file: + +.. code-block:: css + + /* assets/styles/app.css */ + .quack { + /* file lives at assets/images/duck.png */ + background-image: url('../images/duck.png'); + } + +The path in the final ``app.css`` file will automatically include the versioned URL +for ``duck.png``: + +.. code-block:: css + + /* public/assets/styles/app-3c16d9220694c0e56d8648f25e6035e9.css */ + .quack { + background-image: url('../images/duck-3c16d9220694c0e56d8648f25e6035e9.png'); + } + +.. _asset-mapper-tailwind: + +Using Tailwind CSS +~~~~~~~~~~~~~~~~~~ + +To use the `Tailwind`_ CSS framework with the AssetMapper component, check out +`symfonycasts/tailwind-bundle`_. + +.. _asset-mapper-sass: + +Using Sass +~~~~~~~~~~ + +To use Sass with AssetMapper component, check out `symfonycasts/sass-bundle`_. + +Lazily Importing CSS from a JavaScript File +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you have some CSS that you want to load lazily, you can do that via +the normal, "dynamic" import syntax: - $ php bin/console importmap:require swiper/swiper-bundle.min.css +.. code-block:: javascript + + // assets/any-file.js + import('./lazy.css'); -This works because ``jsdelivr`` returns a URL to a JavaScript file that, -when executed, adds the CSS to your page. + // ... + +In this case, ``lazy.css`` will be downloaded asynchronously and then added to +the page. If you use a dynamic import to lazily-load a JavaScript file and that +file imports a CSS file (using the non-dynamic ``import`` syntax), that CSS file +will also be downloaded asynchronously. Issues and Debugging -------------------- @@ -609,75 +648,43 @@ validate the performance of your site! .. _performance-preloading: -Performance: Add Preloading -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Performance: Understanding Preloading +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 6.4 + + Automatic preloading of JavaScript files was introduced in Symfony 6.4. -One common issue that LightHouse may report is: +One issue that LightHouse may report is: Avoid Chaining Critical Requests -Some items in this list are fine. But if this list is long or some items are -multiple-levels deep, that *is* something you should fix with "preloading". -To understand the problem, imagine that you have this setup: +To understand the problem, imagine this theoretical setup: - ``assets/app.js`` imports ``./duck.js`` - ``assets/duck.js`` imports ``bootstrap`` -When the browser downloads the page, this happens: +Without preloading, when the browser downloads the page, the following would happen: 1. The browser downloads ``assets/app.js``; 2. It *then* sees the ``./duck.js`` import and downloads ``assets/duck.js``; 3. It *then* sees the ``bootstrap`` import and downloads ``assets/bootstrap.js``. -Instead of downloading all 3 files in parallel, the browser is forced to -download them one-by-one as it discovers them. This hurts performance. To fix -this, in ``importmap.php``, add a ``preload`` key to the ``app`` entry, which -points to the ``assets/app.js`` file. Actually, this should already be -done for you:: - - // importmap.php - return [ - 'app' => [ - 'path' => 'app.js', - 'preload' => true, - ], - // ... - ]; +Instead of downloading all 3 files in parallel, the browser would be forced to +download them one-by-one as it discovers them. That would hurt performance. -Thanks to this, the AssetMapper component will render a "preload" tag onto your page -for ``assets/app.js`` *and* any other JavaScripts files that it imports using -a relative path (i.e. starting with ``./`` or ``../``): - -.. code-block:: html - - - - -This tells the browser to start downloading both of these files immediately, -even though it hasn't yet seen the ``import`` statement for ``assets/duck.js`` - -You'll also want to preload ``bootstrap`` as well, which you can do in the -same way:: - - // importmap.php - return [ - // ... - 'bootstrap' => [ - 'path' => '...', - 'preload' => true, - ], - ]; +AssetMapper avoids this problem by outputting "preload" ``link`` tags. +The logic works like this: -.. note:: +**A) When you call ``importmap('app')`` in your template**, the AssetMapper component +looks at the ``assets/app.js`` file and finds all of the JavaScript files +that it imports or files that those files import, etc. - As described above, when you preload ``assets/app.js``, the AssetMapper component - find all of the JavaScript files that it imports using a **relative** path - and preloads those as well. However, it does not currently do this when - you import "packages" (e.g. ``bootstrap``). These packages will already - live in your ``importmap.php`` file, so their preload setting is handled - explicitly in that file. +**B) It then outputs a ``link`` tag** for each of those files with a ``rel="preload"`` +attribute. This tells the browser to start downloading those files immediately, +even though it hasn't yet seen the ``import`` statement for them. -If the :doc:`WebLink Component ` is available in your application, +Additionally, if the :doc:`WebLink Component ` is available in your application, Symfony will add a ``Link`` header in the response to preload the CSS files. .. versionadded:: 6.4 @@ -723,7 +730,7 @@ Google Lighthouse score. Does the AssetMapper Component work in All Browsers? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Yup! Features like importmaps and the ``import`` statement are supported +Yes! Features like importmaps and the ``import`` statement are supported in all modern browsers, but the AssetMapper component ships with an `ES module shim`_ to support ``importmap`` in old browsers. So, it works everywhere (see note below). @@ -762,12 +769,16 @@ Can I Use with Sass or Tailwind? Sure! See :ref:`Using Tailwind CSS ` or :ref:`Using Sass `. -Can I use with TypeScript, JSX or Vue? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Can I use with TypeScript? +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sure! See :ref:`Using TypeScript `. -Probably not. +Can I use with JSX or Vue? +~~~~~~~~~~~~~~~~~~~~~~~~~~ -TypeScript, by its very nature, requires a build step. +Probably not. And if you're writing an application in React, Svelte or another +frontend framework, you'll probably be better off using *their* tools directly. JSX *can* be compiled directly to a native JavaScript file but if you're using a lot of JSX, you'll probably want to use a tool like :ref:`Encore `. @@ -780,20 +791,12 @@ files) with component, as those must be used in a build system. See the `UX Vue.js Documentation`_ for more details about using with the AssetMapper component. -.. _asset-mapper-tailwind: - -Using Tailwind CSS ------------------- - -To use the `Tailwind`_ CSS framework with the AssetMapper component, check out -`symfonycasts/tailwind-bundle`_. - -.. _asset-mapper-sass: +.. _asset-mapper-ts: -Using Sass ----------- +Using TypeScript +---------------- -To use Sass with AssetMapper component, check out `symfonycasts/sass-bundle`_. +To use TypeScript with AssetMapper component, check out `sensiolabs/typescript-bundle`_. Third-Party Bundles & Custom Asset Paths ---------------------------------------- @@ -839,48 +842,19 @@ used instead of the original file. Importing Assets Outside of the ``assets/`` Directory ----------------------------------------------------- -You cannot currently import assets that live outside of your asset path -(i.e. the ``assets/`` directory). For example, this won't work: +You *can* import assets that live outside of your asset path +(i.e. the ``assets/`` directory). For example: .. code-block:: css /* assets/styles/app.css */ - /* you cannot reach above assets/ */ + /* you can reach above assets/ */ @import url('../../vendor/babdev/pagerfanta-bundle/Resources/public/css/pagerfanta.css'); - /* using a logical path won't work either */ - @import url('bundles/babdevpagerfanta/css/pagerfanta.css'); - -This wouldn't work either: - -.. code-block:: javascript - - // assets/app.js - - // you cannot reach above assets/ - import '../vendor/symfony/ux-live-component/assets/dist/live_controller.js'; - // using a logical path won't work either (the "@symfony/ux-live-component" path is added by the LiveComponent library) - import '@symfony/ux-live-component/live_controller.js'; - // importing like a JavaScript "package" won't work - import '@symfony/ux-live-component'; - -For CSS files, you can solve this by adding a ``link`` tag to your template -instead of using the ``@import`` statement. - -For JavaScript files, you can add an entry to your ``importmap`` file: - -.. code-block:: terminal - - $ php bin/console importmap:require @symfony/ux-live-component --path=vendor/symfony/ux-live-component/assets/dist/live_controller.js -Then you can ``import '@symfony/ux-live-component'`` like normal. The ``--path`` -option tells the command to point to a local file instead of a package. -In this case, the ``@symfony/ux-live-component`` argument could be anything: -whatever you use here will be the string that you can use in your ``import``. +However, if you get an error like this: -If you get an error like this: - - The "some/package" importmap entry contains the path "vendor/some/package/assets/foo.js" + The "app" importmap entry contains the path "vendor/some/package/assets/foo.js" but it does not appear to be in any of your asset paths. It means that you're pointing to a valid file, but that file isn't in any of @@ -1000,61 +974,66 @@ rendered by the ``{{ importmap() }}`` Twig function: Page-Specific CSS & JavaScript ------------------------------ +---> TODO HEre +---> need to add the entrypoint in the importmap.php file +----> and should NOT call parent() in the javascript block + Sometimes you may choose to include CSS or JavaScript files only on certain -pages. To add a CSS file to a specific page, create the file, then add a -``link`` tag to it like normal: +pages. For JavaScript, an easy way is to load the file with a `dynamic import`_: -.. code-block:: html+twig +.. code-block:: javascript - {# templates/products/checkout.html.twig #} - {% block stylesheets %} - {{ parent() }} + const someCondition = '...'; + if (someCondition) { + import('./some-file.js'); - - {% endblock %} + // or use async/await + // const something = await import('./some-file.js'); + } -For JavaScript, first create the new file (e.g. ``assets/checkout.js``). Then, -add a ``script`` tag that imports it: +Another option is to create a separate :ref:`entrypoint `. For +example, create a ``checkout.js`` file that contains whatever JavaScript and +CSS you need: -.. code-block:: html+twig +.. code-block:: javascript - {# templates/products/checkout.html.twig #} - {% block javascripts %} - {{ parent() }} + // assets/checkout.js + import './checkout.css'; - - {% endblock %} + // ... -This instructs your browser to download and execute the file. +Next, add this to ``importmap.php`` and mark it as an entrypoint:: -In this setup, the normal ``app.js`` file will be executed first and *then* -``checkout.js``. If, for some reason, you want to execute *only* ``checkout.js`` -and *not* ``app.js``, override the ``javascript`` block entirely and render -``checkout.js`` through the ``importmap()`` function: + // importmap.php + return [ + // the 'app' entrypoint ... -.. code-block:: html+twig + 'checkout' => [ + 'path' => './assets/checkout.js', + 'entrypoint' => true, + ], + ]; + +Finally, on the page that needs this JavaScript, call ``importmap()`` and pass +both ``app`` and ``checkout``: + +.. code-block:: twig {# templates/products/checkout.html.twig #} {% block javascripts %} - - {% endblock %} + {# do NOT call parent() #} -The important thing is that the ``importmap()`` function must be called exactly -*one* time on each page. It outputs the ``importmap`` and also adds a -``