yiisoft / yii2

Yii 2: The Fast, Secure and Professional PHP Framework
http://www.yiiframework.com
BSD 3-Clause "New" or "Revised" License
14.24k stars 6.91k forks source link

Asset bundles approach is wrong and lead to Yii 2 extensions mess and lot of troubles #4391

Closed creocoder closed 10 years ago

creocoder commented 10 years ago

For example we have foo\yii2-gallery-widget. It require js libs:

We have 2 Asset Bundles: foo\gallery\JqueryGalleryAsset and foo\gallery\DotAsset.

Second extension we have bar\yii2-file-uploader-widget. It requre js libs:

We have 2 Asset Bundles: bar\uploader\JqueryFileUploaderAsset and bar\uploader\DotAsset.

Using this widgets in one page will lead to critical js error, because doT will be linked 2 times. Trying to use asset converter will lead to critical js error, because it will use doT 2 times. Trying to override doT bundle absolutte FAIL, because we can override 1 bundle, while there we have 2.

Because all of it troubles current Yii 2 situation is: NOT use Yii 2 extensions at all, because it just DUNGEROUS. In middle/big application you will be faced with all described troubles.

Also as you see this is simple example where trouble come. In real world extensions which requre 2 - 3 js libraries possibility of such bundle collisions is 99%.

@qiangxue It is not yet too late (there is no release yet) to totally rethink asset bundles approach. Because if it will go to release prepare to situation when yii 2 extensions will not work together and no one will use them.

samdark commented 10 years ago

@SDKiller that's all fine if all these bundles are yours but if these are all from different vendors you can't really force them to use same keys for same libraries.

SDKiller commented 10 years ago

Why not if we can override any bundle configuration in config? We don't force vendors - we force bundle config.

https://github.com/yiisoft/yii2/blob/master/docs/guide/structure-assets.md#overriding-asset-bundles

samdark commented 10 years ago

Well, that's manual editing that we're trying to avoid as much as possible.

mithun12000 commented 10 years ago

one suggestion.

you can compare hash for that asset file to check duplicate. In that case you might need to process extra code for it which can be time taking too.

qiangxue commented 10 years ago

@samdark I'm not clear how you claim the issue about asset distribution is solved. If both widget A and widget B depend on the same 3rd party lib C. How do you propose to solve the problem?

There's certain overhead of declaring asset bundles.

I don't think this is an issue. It's more like the effort you have to take.

There's no central place one can review/edit all asset bundles.

This can be solved with an asset command.

samdark commented 10 years ago

@qiangxue

  1. composer-bower will make it possible to reference bower packages from extensions.
  2. Assets from these packages will be placed at the same path no matter which bundle uses it.
  3. It doesn't matter how many asset bundles will reference the same file, it will be published to the same place, won't be included twice and will be OK priritywise.
creocoder commented 10 years ago

@samdark How to override asset bundle for doT for example? Realize we have 3 different asset bundles for it. Which one override?

SDKiller commented 10 years ago

that's manual editing that we're trying to avoid as much as possible

@samdark

Anyway you cannot awoid it 100%. Even with composer-bower packages the problem of double inclusion will not be solved if one extension wants js on POS_READY and another - on POS_LOAD

samdark commented 10 years ago

@creocoder you don't really need to override it if it's downloaded as bower package. You can adjust package config an it will get different version for all three bundles.

creocoder commented 10 years ago

@samdark Sorry, but we have feature, by using which we can override any asset bundles. For example i can override JqueryAsset and get jquery from CDN. And i'll try ask again ;) Realize we have 2+ asset bundles of same library from different extensions and i want to use regular overriding feature to get it from CDN for example.

samdark commented 10 years ago

You have to override both then.

creocoder commented 10 years ago

@samdark Ok, both. Now realize JqueryAsset not part of backend framework. Yii 2 in our case. 90% of libraries requre jquery. For example you use 10 extensions. To override JqueryAsset you will need override 10 bundles. Do not you think that something wrong here?

Another example. What if one vendor will use lib.js in his asset bundle and other lib.min.js. We will be faced with double include, etc. We need reliable solution. Or are you willing to hope maybe? How users can trust Yii knowing that it use not reliable solutions? Please, think of it in first.

samdark commented 10 years ago

@creocoder jquery bundle is part of Yii and there's no other library that is that popular.

samdark commented 10 years ago

The issue with minified script is indeed valid.

qiangxue commented 10 years ago

I don't think the same 3rd party lib should have different asset bundle classes in different widgets.

Perhaps we can introduce some special dependency declaration. For example,

class MyAssetBundle extends AssetBundle
{
    public $depends = [
        // depends on a bower package where package1 is the unique package name
        'bower:package1',
        // depends on my own asset bundle
        'path\to\MyOwnBundle',
    ];
}

Because the name bower:package1 is globally unique, it can be used in different widgets by different vendors.

This is just some wild thinking. There are still technical details to be worked out.

creocoder commented 10 years ago

@qiangxue But bower package itself does not contain any asset bundles. How it help? Also there is no possibility to autodetect which files should be included in case of bower package. For example if you'll look into selectize.js, you will see that there is js/selectize.js which requre sifter and microplugin and js/standalone/selectize.js. After installing this package via bower you'll have both. Also this is new case similar to minified VS not minified files. How you can garantee that vendor A will use standalone selectize.js verison in his SelectizeAsset and vendor B will not use selectize.js with sifter and microplugin. When i open this issue mine investigations show that there is no reliable solutions. This is why i suggest simple, non-automated solution. And only this can be 100% reliable.

qiangxue commented 10 years ago

Some further ideas. Unlike regular asset bundles which use their class names as global IDs, the bundle bower:package1 uses the bower package name as the global ID. Like regular asset bundles, you can specify js, css for such bundles. You can also configure it via AssetManager::bundles. For example,

class MyAssetBundle extends AssetBundle
{
    public $depends = [
        // depends on a bower package where package1 is the unique package name
        'bower:package1' => [
             'js' => [...],
             'css' => [...],
        ],
        // depends on my own asset bundle
        'path\to\MyOwnBundle',
    ];
}

So you can pick up the needed js/css files as you want for your widget. If two widgets are using the same bundle, Yii can automatically merge them or you can configure it via AssetManager::bundles (this is similar to what you would do without using asset bundles).

schmunk42 commented 10 years ago

@creocoder I had exactly the same problem as you described. In fact we may think about distinguishing between standard, minified and bower as a AssetBundle "state", but let's focus on standard vs. bower first.

@qiangxue What do you think about the following approach, it's only two more properties for the AssetBundle class. IMHO the only thing we need to know is, is where is the bower.json for this AssetBundle and which files are NOT available from bower.

So $js are the script files which are generated from the bower packages specified in $bowerJson - they are exchangeable! Files from $jsCustom are customizations for the current package.

Now I can stil use the standard way of including assets with Yii 2. But it'll be also possible to collect the bower information from all required AssetBundles (including dependencies) and then install these with bower, bowerphp and throw them into grunt or whatever you like.

Specifying just the path to bower.json would make sure you have only one place where you have to edit your client-script dependencies.

class SirTrevorAsset extends AssetBundle
{
    public $language;
    public $sourcePath = '@vendor/drmabuse/yii2-sir-trevor-js/assets';

    // files generated by the developer
    public $css = [
        "grunt/sir-trevor-js.css",
    ];

    // files generated by the developer
    public $js = [
        "grunt/sir-trevor.js",
        "grunt/underscore.js",
        "grunt/eventable.js",
        "grunt/locales/de.js",
        "grunt/locales/es.js",
    ];

    // specify path to bower.json, so we can grab the above files from there
    public $bowerJson = "bower.json";

    // always to be included, not available via bower
    public $jsCustom = [
        "blocks/CodeBlock.js",
        "blocks/ColumnsBlock.js",
        "blocks/Gallery.js",
        "blocks/ImageCaption.js"
    ];

    // these assets should also define their bower.json location
    public $depends = [
        'yii\web\JqueryAsset',
        'yii\web\YiiAsset'
    ];
}
SDKiller commented 10 years ago

This discussion may last forever.

2 major points to keep in mind:

1) 100% automation is impossible 2) where it is possible - automation is useful

Summary: 1) nevertheless of modern techniques there should be enough 'manual' tools to adjust everything. IMO current assets bundles implementation is lacking flexibility; 2) in simple cases current assets bundles implementation is enough.

Some options could be passed to assets in runtime - currently it is not available.

If we register assets 'per-view' - why we can configure them only application-wide in config file?

Why not to pass some options - for example to load only particular files in this view - anyway the whole folder with js files have been published already.

Etc...

samdark commented 10 years ago

4269

schmunk42 commented 10 years ago

Just found this project: https://github.com/francoispluchino/composer-asset-plugin

From the website:

The Composer Asset Plugin allows you to manage project assets (css, js, etc.) in your composer.json without installing NPM or Bower.

This plugin works by transposing package information from NPM or Bower to a compatible version for Composer. This allows you to manage asset dependencies in a PHP based project very easily.

cebe commented 10 years ago

@schmunk42 thanks, we are already aware of it :) I am currently waiting for composer to merge a pull request it depends on.

schmunk42 commented 10 years ago

Must have overlooked it :)

schmunk42 commented 10 years ago

btw: Did you talk to @Seldaek about this?

The PR from https://github.com/composer/composer/pull/3082 looks neat ... and ... maybe he just doesn't know that here are so many people waiting for this and would revere him for merging it. :bow: :pray:

cebe commented 10 years ago

@schmunk42 yes, see this comment: https://github.com/composer/composer/pull/3082#issuecomment-49978235

qiangxue commented 10 years ago

Closed to move discussion to #4454 and #4855.