-
-
Notifications
You must be signed in to change notification settings - Fork 198
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
What's the solution proposed for assets stored in bundles? #5
Comments
The way we do things ATM is that we have a few common bundles e.g customer, account, messaging, which come with the default set of the assets js/less. |
@javiereguiluz Webpack has an option to hook into the resolving, by allowing to define aliases for the module resolution. We could expose a way to use them, to allow defining an alias pointing to a custom location (which could be |
Hi guys! You can load assets from anywhere. Either in .addEntry('main', './src/SunShine/FooBundle/Resources/public/js/foo_sunshine.js') Or also by requiring it in a .js file: // src/SunShine/BarBundle/Resources/public/js/app.js
// you need to go up a few directories, but it's a straightforward path
require ('../../../../FooBundle/public/js/foo_sunshine.js'); So technically, I don't think there's an issue. Stof is totally right about being able alias paths for modules: https://webpack.js.org/configuration/resolve/. We can add a hook to allow these to be added (actually, you can already do it by just adding this key manually to the bottom of your Btw, about FOSJsRoutingBundle, iirc, due to how that JS file is built, I think requiring it as a module is still a bit of a pain. |
@weaverryan to use FOSJsRoutingBundle in a CommonJS environment, the gotcha is that you need to configure the router before using it (and that it defines a global variable). This is solved easily by having a module in your project responsible for that, and having all your other code requiring your module instead of requiring the router directly: // backend_router.js
// Use the webpack feature allowing to read some variable: https://webpack.github.io/docs/shimming-modules.html
const { router, setRoutingData } = require('imports-loader?window=>{}!exports-loader?router=window.Routing,setData=fos.Router.setRoutingData!../vendor/friensofsymfony/js-routing-bundle/Resources/public/js/router.js');
// dumped_routes.json is the output file for the fos:js-routing:dump command
const routerConfig = require('../dumped_routes.json');
setRoutingData(routerConfig);
modules.exports = router; and then in your code: const router = require('./backend_router.js');
const url = router.generate('welcome', {}, true); however, I agree that this will be easier once we have CommonJS support in FOSJsRoutingBundle, to simplify the loading of the router. |
Shouldn't Encore generate the loader paths for you from your installed bundles? |
@dkarlovi can you say more about what you mean by that? Perhaps show a "fake code" example of how you'd expect it to work? There seem to be a lot of questions about assets in bundles... but these are just assets in different directories, which you can of course import. I feel I may not be understanding the requirement that some people have :) |
@weaverryan basically, if I install a bunch of bundles, some of them might need their assets to be installed. If used, I'd expect Encore to basically just work with them, without me reading Webpack docs and figuring out what goes where (for 3rd party bundles, mind you). So, use case is:
|
I strongly disagree with @dkarlovi's idea. Sometimes I install bundles to use only their backend stuff or extend them and I 100% don't want their JS/CSS files to make mine bigger by being "auto required". If I want them, I can |
I think what a lot of people are after is automation with bundles. This probably stems from assetic and having it "compile" everything from templates. A bundle may have twig templates that have a parent () override for a block named javascripts. When you install the bundle all the js for those templates work out of the box. This is great for utility bundles that many organisations may have for FormTypes ( think date pickers, recaptcha etc) |
I'm not sure that's what the OP had in mind, and automation can mean many different things. |
Apologies for the long comment. I'd like to highlight a common-ish use-case with "assets stored in bundles", the current solution as far as I can tell, and how it might be improved a bit. With Assetic we tend to use: {% stylesheets
'@SomeBundle/Resources/public/css/something.css'
'@OtherBundle/Resources/public/css/other-thing.css'
%}
<link rel="stylesheet" src="{{ asset_url }}">
{% endstylesheets %} (On different platforms there are similar tools, using config rather than template tags most of the time, but the rationale is that you tend to give it the path to some files to concatenate and perhaps minify.) Looking at the Encore docs, if you have a specific script from a bundle as a dependency, you could have: // my/entry/point.js
require('../../vendor/some-vendor/some-bundle/src/Resources/public/css/something.css');
require('../../vendor/other-vendor/other-bundle/Resources/public/css/other-thing.css'); And if your main entry point uses the name The one issue with this approach is that referencing files from bundles can be a pain, what with the sea of One way to make it better would be to have some webpack aliases:
The first two have some complexity (what if your The third option would require some PHP tool to publish a map of sorts. Encore itself could have a method for defining webpack Encore
.addAlias({'@': './'})
.addAlias(require('./var/whatever/bundle-map.json')) Or that can be done manually I think: const config = Encore
/* ... */
.getWebpackConfig();
config.resolve.alias = Object.assign({'@': './'},
require('./var/whatever/bundle-map.json'));
module.exports = config; |
Auto-configuration of assets with encore is definitely the thing that I would prefer as well. Kinda expected that encore would be somehow coupled with SF Flex, which would, with bundle installation, add assets configuration as well. I do agree with @Growiel - we have to have sufficient amount of control, that is, to opt-in or opt-out from certain assets inclusions. Since Flex is "easy install and configuration" system - somehow, bridging/integrating encore with flex seams reasonable. Example: I am a vendor of DateTimePickerBundle, it has PHP code, Twig code, JS and CSS. When installed with Flex, it should be auto-configured with reasonable defaults and assets included - since, naturally, in order bundle to work, all those are required. However, you maybe don't like my CSS, and you want to exclude it from configuration - and that seams like common use case scenario. So, both @dkarlovi and @Growiel are right, it should be auto-configured with bundle installation with reasonable defaults, it should be 100% configurable. How awesome that would be! |
@TheCelavi totally agree. In my mind, bundles should be able to ship their Encore setup (similar to composer.json autoload part which explains to Composer how to auto-load your specific code) which would fit into your application out of the box by default, but you the developer might want to modify (ie. blacklist) certain bundles (or certain aspects of a bundle) to do so. This would allow Flex to install the bundle, bundle being installed would hook into existing Encore environment and you're done. |
Per request for examples, I've a SF3.3 project. Layout includes
sources, for this example, include
where
In my prior gulp environment, specifying includePaths -- additional to defaults -- worked for multiple dirs,
On gulp task exec aggregation/minification of @import'd files was OK with with NO full/relatives required in the .scss parents But, in webpack/encore, exec fails, not finding the PATH-less @import-s
editing
cures the problem -- for those specific @import-s Ideally, rather than editing the .scss, pointing at file-paths, a webpack/encore equivalent of directory search paths,
consolidated in the webpack.config.js would be helpful. Reading above, I don't see a finalized/agreed upon approach. Whether that's using Aliases, or some other approach, I'm open. |
I empathize with the desire to get autoloading of front-end code, but as a front-end developer I clearly don’t want that happening, even with opt-out, for three reasons:
You need to manage loading your front-end dependencies yourself, either from bundles or from As an aside: as far as I know, Symfony doesn’t a standard way of declaring assets in configuration, like e.g. Drupal 8 does (with its “libraries” config). If it had one, and your app and required bundles used it, you could then output a list of known assets (e.g. as JSON): {
"somecomponent": {
"javascript": {
"main": "vendor/my-vendor/my-bundle/src/Resources/public/js/somecomponent-main.js",
"lang_fr": "vendor/my-vendor/my-bundle/src/Resources/public/js/somecomponent-lang-fr.js",
"lang_de": "vendor/my-vendor/my-bundle/src/Resources/public/js/somecomponent-lang-de.js"
}
},
"othercomponent": {
…
}
} (or some other format) and then consume it in Encore or other tools (standard webpack config, gulpfile, etc.). Then you could choose to e.g. concatenate all the JS to one big file and serve it to browsers, though that would probably break horribly for reasons I described before. Or you could use it to create webpack aliases, or to require specific assets (e.g. That would be fun, but does not exist in the Symfony world unless I’ve missed it. |
@fvsch So you don't want it - but because of the fact you don't want it, it should not be available and optional either?
Purpose of encore and flex is to enable full RAD support, with possibility to reconfigure everything/opt out from everything. Sorry, but you are clearly missing the point of stated tools. And you are clearly missing the fact that whatever decision is made here, it does not affect you to use whatever tool you want and use - since you can opt out from every tool discussed here. We want RAD tool, for front end development world as well. In use cases in which tool is failing, we want to be able to reconfigure and fix manually. In other cases - to opt out easily. None of what you have stated is a deal breaker here not to get that tool. |
I stand by what I wrote, but it seems we’re getting sidetracked from the original issue here. The original question was about being able to get assets from bundles. Quoting the OP:
The basic answer is that if you put your webpack config at the root of your project, you can require JS, CSS etc. from // assets/app.js
// (defined as my main entry point in webpack.config.js)
require('src/Something/MyBundle/Resources/public/lib/whatever/whatever.js');
require('src/Something/MyBundle/Resources/public/lib/whatever/whatever.css');
require('vendor/pinano/select2-bundle/Resources/public/js/select2.min.js');
require('vendor/pinano/select2-bundle/Resources/public/js/i18n/es.js'); The expanded answer is that it might be useful to offer a ways to treat bundle names as aliases, so that you don’t have to provide the full path to a bundle, but that offering this convenience on the Node.js side would require some work on the PHP side as well, to provide a map of installed bundles with their names and paths. For instance, Symfony 3.3/4 could write a {
"FrameworkBundle": "vendor/symfony/framework-bundle"
} And that could be loaded from your In any case, this would require a PHP script, either as an addition to the Asset component or as a standalone dependency. From my understanding, this is the scope of this issue (with the whole aliases thing being just an idea that the Encore team may not want at all). “How to enable Rapid Application Development for the frontend in Symfony” is a whole other topic, and is imo far beyond the scope of |
You are right, lets correct that: symfony/flex#102 It's a very young package, we are pitching ideas. In regards solely to this topic, bundle locator syntax (@BundleName) seams quite logical approach, it is used by Twig, SF configuration (routing,services...etc..), etc... |
Fair point re: right comment(s) in right thread. That said, 'my' comment, above, suggests the option to specify additional include/search paths that'll be followed during aggregation. So as to find 'pathless' @import-s, for example. Again, easily available in gulp. Does that appropriately belong as part of this^ discussion? |
My original sentence on the slack was something along the lines that I expected the encore to be something like |
@fvsch point is: you always have the option to NOT use the auto-enabler in your project and basically ignore Encore completely. This way, you install "dozens of bundles with frontend assets", they ship their Encore config, but your app doesn't care as it doesn't use it. The way I see it, Encore should solve most common use cases for most people by default, allow for some flexibility and more advanced usage (whitelisting / blacklisting, opt out, etc) and for people to not use it. But, if you're always required to mess around with Webpack config anyway, Encore might not be worth the effort at all, you drop a lot of opportunities for RAD. |
Comming back to the idea of autoproviding assets from bundles... I have just started with webpack a few days ago. Is there a way allready to autoprovide assets? I could help myself with a composer postinstall script which parses the bundle for scripts/css inside Resources/public/js and css folder and adds
to webpack.config.js lets say after the last addEntry(... and for css the last addStyleEntry(... line - if there is one I simply don't want to do ANYTHING after composer require(vendor/bundle). Actually i want to create an installer for Packages to be used inside of my application based on composer... |
Loading assets from bundler should not depend on where the bundle is installed. Today it can be installed by Composer in |
What about bundles that provide stand-alone functionality? (Just for example, SonataAdminBundle) |
@harentius I'm not sure that handling assets for that kind of vendor should be done by the project using it. It would make more sense for the vendor to directly include a pre-compiled version of them. |
@Lyrkan thanks, this is a possible solution.
|
@harentius I agree that vendors should include a pre-compiled version of theirs assets. But you still need to add them to your layout so they’re loaded, and you need to concatenate them with the rest of your assets so they don’t generate a gazillion HTTP requests. |
I do not agree with that the vendors should provide minimized versions! |
So if I use a vendor, I have configure my frontend toolchain with Babel, CoffeeScript, TypeScript, WhateverEsotericTranspilerSomeoneDecidedToPlayWith even though I don’t use them? Do you realise what you’re saying? |
Folks, we’re never going to get consensus on this. Even in the Node.js ecosystem, which has a much stronger focus on front-end JS (downloads from npmjs.com skew hugely towards frontend packages), there is no agreed-upon answer to these questions:
The only convention is for what file gets required when you do So, on the PHP/Composer/Symfony side, it would be useful if Symfony provided guidelines for bundles that want to provide (optional or mandatory) frontend code, but don’t expect them to be followed all the time. As a result, don’t expect Symfony Encore to automagically be able to load this frontend code, because even if conventions exist they will be broken all the time. Everyone is going to have to do this a bit manually.
No, and vendors should probably include ES5 builds (minified and unminified). But if they rely on ES6 features and libs like React, VueJS etc, and polyfills for ES6 and other browser features, then this ES5 build will include a lot of runtime stuff that will make it huge. If 3 vendors do that, you end up paying the price of the polyfills and libs 3 times, possibly with JS conflicts breaking your pages too. But that’s already a problem with vendors that require and include jQuery, Bootstrap, Modernizr and a bazillion dependencies. It’s cool if you only use this vendor in your pages and not 2-5 others plus your own Bootstrap build etc. In practice it’s often a mess, and vendors which come with many frontend dependencies should be avoided like the plague if they’re only responsible for a widget that you want to use in your pages (rather than for full views). So it’s not exactly a new problem. And there is no one-size-fits-all solution. IMO, a vendor which provides a big frontend feature should include several scripts:
And they should document how to require all those scripts with different build tools; a section on adding stuff directly in HTML (with |
Thanks a lot all for clarification, but approach with storing pre-compiled assets in vendor bundles still not fully clear.
|
I see this is quite an old discussion, but still an open issue... I came here while searching for a solution to provide some javascript in a bundle that should be included in the Webpack-Encore pipeline (it's the use case others mentioned here: I have a private bundle providing special form types that is shared between my projects). My proposed favorite solution would be the following:
This way, no Javascript from bundles will be automatically injected into the project. But there would be a well-known convention for bundle authors to provide javascript moduls and other assets on the one hand, and for project builders to include those assets into their webpack-encore build on the other hand. |
I just published a bundle providing the commands proposed in my previous comment: https://packagist.org/packages/wasinger/bundle-asset-provider-bundle Provided commands:
No npm packages are installed and no assets are going to be included automatically; it's the responsibility of the project's developer to install the missing dependencies via npm or yarn and to include the provided assets from the I would like to see those commands included in Webpack Encore... |
Since handling assets in symfony is moved to webpack (and therefor to javascript) for valuable reasons, in my opionen, provider of third party bundles should following this way consistently. In this issue, there are some approches that depend on PHP code, like JS looking up for bundle paths or PHP looking up for package.json files, etc. I would prefer a solution:
A solution could be on the bundle project:
// entry.js
// This code should keep as simple as possible (like config files)
// The path will be resolved correctly without configuration, because node
// will go up on the directory path to search for node_modules/my_bundle_package
const my_package_loader = require('my_bundle_package');
my_package_loader.load() // webpack.config.js
Encore.addEntry('./assets/entry.js', 'entry') The my_bundle_package should be pushed to NPM Then the user code would look like:
// entry.js
// same code as the providers bundle
const my_package_loader = require('my_bundle_package');
my_package_loader.load() // webpack.config.js
Encore.addEntry('./assets/entry.js', 'entry') So the bundle asset code is loaded by webpack over node_modules and all dependencies of my_bundle_package are loaded correctly as well, because it's handled by the package manager. It's also not to difficult to install and flex could handle the package.json and the copy of entry.js in future. The user is also able to modify the entry.js in order to add some plugin code, load an adapter you may provide or merge it with his entry point And there is no dependency to PHP The disadvantages i see so far are:
|
my_bundle package cannot be pushed to NPM! Please note, that most projects that use symfony are closed source. |
Hello, any solution for third-party bundles's assets? |
Here's my solution that I'm using to making bundle assets available: package.json
{
...
"scripts": {
...
"prewatch": "bin/console assets:install --symlink --relative ./assets",
"watch": "encore dev --watch"
}
} webpack.config.js
Encore
.addEntry('app', path.resolve(__dirname, './assets/js/main.js'))
.addAliases({app: path.resolve(__dirname, './assets/')})
...
;
const bundlesPath = path.resolve(__dirname, 'assets/bundles/');
const bundles = fs.readdirSync(bundlesPath);
for (let i = 0; i < bundles.length; ++i) {
const bundle = bundles[i];
Encore
.addEntry(bundle, path.resolve(bundlesPath, bundle, 'js/main.js'))
.addAliases({[bundle]: path.resolve(bundlesPath, bundle)});
}
...
module.exports = Encore.getWebpackConfig();
module.exports.resolve.symlinks = false; index.html.twig (or somewhere reasonably similar)
<html>
<head>
{{ encore_entry_link_tags('app') }}
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{{ encore_entry_script_tags('app') }}
{% block javascripts %}{% endblock %}
</body>
</html> bundles/my-bundle/Resources/views/index.html.twig
{% extends 'index.html.twig' %}
{% block stylesheets %}
{{ parent() }}
{{ encore_entry_link_tags('mybundle') }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
{{ encore_entry_script_tags('mybundle') }}
{% endblock %}
{% block body %}
...
{% endblock %} |
problem still not resolved |
@9ae8sdf76 Your approach is interesting, but how do manage to override the I presume you registered the Bundle paths in twig / path config, otherwise they get ignored for me entirely. |
Hi guys! I have a running bundle in its own directory. The assets are generated by webpack-encore in the parent symfony project:
For now my new bundle accepts all entrypoints:
Is it just that easy? Or is my approach wrong? |
The problem comes up when working on a team, and doing it this way means that not only does your test/production sites have to have this file structure enforced, but so does your team. And if there's one thing I've learned in all my years: people guard their coding preferences more closely than they do their command-line text editor (vim4life!). Now if there were some way to maybe bridge the gap between We could even go so far as to add something to the |
@taleteller
I'm not entirely sure I understand what you're doing, but I'll try my best. My base I also treat each bundle as a separate entity that only has knowledge about itself; in the rare circumstance there needs to be a bridge between two bundles, I keep it minimal and out of my templates. For example, I create an endpoint that simply forwards the request to the appropriate bundle. This allows me to minimize changing my templates and forcing changes in the controller only - MVC style.
I didn't register bundle paths with Twig. I use the |
When I (composer) require webpack encore - I could give a webpack.config.js into the bundle to generate the assets inside of the bundle. That should not affect the coding preferences of others much, isn't it? |
Let's say you have this kind of setup:
Now I want to code my-bundle assets while testing in demo-app, but I don't want to install all bundle's node dependencies into my demo-app. Solution, in .setOutputPath('src/Resources/public/build/')
.setPublicPath('/bundles/my-bundle/build')
.setManifestKeyPrefix('/bundles/my-bundle/build/') Don't forget to install your assets with symlink in demo-app:
Works with Problem solved. |
Let's close this issue. We haven't resolved it in almost 6 years ... and nothing serious has happened. We've built lots of apps using Webpack Encore during this time. So, things are fine. Thanks. |
On Symfony Slack some people are concerned about Encore and bundles' assets. The issue is that Encore expects all assets to be stored in the same location. What about in-app bundles then? And what about third-party bundles?
In-app bundles is a minor issue because Symfony 4 (released on November 2017) will remove them ... but third-party bundles will still exist.
The text was updated successfully, but these errors were encountered: