Skip to content

Allow to define multiple entrypoints and load assets from each of them. #17

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 6 commits into from
Dec 20, 2018

Conversation

shulard
Copy link
Contributor

@shulard shulard commented Nov 15, 2018

This PR is added to fix the issue #13. With that new code, you can configure your project like this :

webpack_encore:
    # the "default" build
    output_path: '%kernel.project_dir%/public/build'
    builds:
        frontend: '%kernel.project_dir%/public/frontend/build'

Then in your Twig template :

{{ encore_entry_script_tags('entry1', 'frontend') }}

Copy link
Member

@weaverryan weaverryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @shulard!

Wow! Great job with this big feature! I've left a series of comments - it's all just architecture related - avoiding BC breaks, making things lazy for performance, etc. Let's get this guy finished - I think a lot of people are excited for it :).

Cheers!

public function __construct(
EntrypointLookupInterface $entrypointLookup,
EntrypointLookupCollection $entrypointLookupCollection = null,
Packages $packages
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reordering of the args breaks BC. What if we did this:

  1. Replace the first arg entirely with EntrypointLookupCollection
  2. But, don't add the type-hint for EntrypointLookupCollection (as that would be a BC break). Instead, we would do an instanceof check in the constructor: if the user is passing an EntrypointLookupInterface, we would trigger a BC warning (example: https://github.com/symfony/symfony/blob/63d34515a51819a6a6b3a94d598e8e37068e85ce/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php#L36). In this situation, we would instantiate a new EntrypountLookupCollection() and pass in just the one EntrypointLookupInterface object. If they pass something that is neither of these 2 types, we would throw a TypeError

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I understand the point about BC breaks 😄

Only one question about the exception message. For the TypeError, I think:

The "$entrypointLookupCollection" argument must be an instance of EntrypointLookupCollection.

For the BC warning :

The "$entrypointLookupCollection" argument in method "__construct()" must be an instance of EntrypointLookupCollection.

We do you think ?

$container->getDefinition('webpack_encore.entrypoint_lookup_collection')
->replaceArgument(0, array_map(function ($item) {
return $item.'/entrypoints.json';
}, $config['builds'] ?? []));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's pass in the "default" path here as well - we could give it a key like default (and add some validation in the Configuration class to make sure there is no key called default set under builds

Also, is it possible for $config['builds'] to not be set? Shouldn't it always be set thanks to the Configuration class (and so the $config['builds'] ?? [] is unnecessary?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated that part to check for default build name and I removed the ?? operator.

public function getEntrypointLookup(string $key): EntrypointLookupInterface
{
if (!isset($this->buildEntryPoints[$key])) {
throw new UndefinedEntrypointException(sprintf('Given entry point "%s" is not configured', $key));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're mixing up "entrypoint" and "builds" in a few places. This should be something more like UndefinedBuildException, right? And, in TagRenderer, the argument to private function getEntrypointLookup(string $entrypointName = null) should really be $buildName, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you're right, I update.

public function renderWebpackScriptTags(
string $entryName,
string $entrypointName = null,
string $packageName = null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, args 2 and 3 need to be reversed, otherwise it's a BC break.

And put the method back on one line (I sometimes do this in my code, but we don't do this in Symfony)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reordered the arguments.

string $entryName,
string $entrypointName = null,
string $packageName = null
): string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: the 2nd and 3rd args need to be rearranged, unfortunately. I don't think there's a good way to "reorder" these args without breaking BC, except adding an entirely new Twig function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reordered the arguments.

}

private function getEntrypointLookup(): EntrypointLookupInterface
private function getEntrypointLookup(string $entrypointName = null): EntrypointLookupInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?string $entrypointName

Let's allow the value to be null, but force it to be passed to this function. Same in TagRenderer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since entrypoint name is now always a string (default value is _default), I think allowing null is not useful anymore.

return $this->container->get('webpack_encore.entrypoint_lookup');
}
return $this->container->get('webpack_encore.entrypoint_lookup_collection')
->getEntrypointLookup($entrypointName);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we make the EntrypointCollection also able to handle the "default" build, this should simplify

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I've removed the lookup / collection usage to rely only on collection.

{
$this->buildEntryPoints = array_map(function ($path) {
return new EntrypointLookup($path);
}, $buildEntryPoints);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Let's tweak this slightly:

  1. Make this class receive a ContainerInterface, which will be a service locator (like we use in the Twig extension) containing all of the EntrypointLookup objects.

  2. In WebpackEncoreExtension, for each "build", register a new service dynamically. Then, create a container/service locator for all of the builds, and pass that here.

That will allow all EntrypointLookup objects to be managed by the container, but also passed lazily to this class, so that we're not instantiating ALL of the EntrypointLookup objects just to use one of them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just pushed the changes.

@weaverryan
Copy link
Member

Ping @shulard! Are you able to make the changes I suggested?

@shulard
Copy link
Contributor Author

shulard commented Nov 26, 2018

Hello @weaverryan, sorry about the delay but the last week was really busy. I'm able to perform requested changes I hope this will be done today.

@shulard shulard force-pushed the feature/multiple-entry-points branch 2 times, most recently from 161a5e8 to 0ddfb18 Compare November 26, 2018 18:27
@shulard
Copy link
Contributor Author

shulard commented Nov 26, 2018

Hello @weaverryan I've updated all the requested changes (I think 😄).

For me the latest point is the exception messages that maybe need to be refined.

@frederickhamla
Copy link

Hi !

@weaverryan : Thanks for your work, happy for this feature. Did you know when you merge this request ?

@shulard : nice job !

Copy link
Member

@weaverryan weaverryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome! Sorry for the delay - only minor comments!

->validate()
->always(function ($values) {
if (isset($values['_default'])) {
throw new \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException("Key '_default' can't be used as build name.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor - add a use statement for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, updated.

$this->assertNotContains(
'<link rel="stylesheet" href="/build/styles4.css">',
$html2
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, could we split this into a separate test instead of adding to this test? It makes it a bit harder to understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've created two test cases: one with contains check and one with "not contains check".

@@ -75,6 +103,9 @@ public function registerContainerConfiguration(LoaderInterface $loader)

$container->loadFromExtension('webpack_encore', [
'output_path' => __DIR__.'/fixtures/build',
'builds' => [
'other' => __DIR__.'/fixtures/other-build'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of other - how about different_build. The problem is that there is an other_entry in the main build, so it's confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've renamed it.

This collection will be used to store all the different builds EntrypointLookup instances.
Add a new parameter to define in which entrypoint the assets must be found.

Also implement a fallaback system: If no named entrypoint is given, use the default one.
@shulard shulard force-pushed the feature/multiple-entry-points branch from 0ddfb18 to 0f3134b Compare December 19, 2018 10:23
@weaverryan
Copy link
Member

Thank you for your great work Stéphane!

@weaverryan weaverryan merged commit 0f3134b into symfony:master Dec 20, 2018
weaverryan added a commit that referenced this pull request Dec 20, 2018
… each of them. (shulard)

This PR was squashed before being merged into the master branch (closes #17).

Discussion
----------

Allow to define multiple entrypoints and load assets from each of them.

This PR is added to fix the issue #13. With that new code, you can configure your project like this :

```yaml
webpack_encore:
    # the "default" build
    output_path: '%kernel.public_dir%/build'
    builds:
        frontend: '%kernel.public_dir%/frontend/build'
```

Then in your Twig template :

```twig
{{ encore_entry_script_tags('entry1', 'frontend') }}
```

Commits
-------

0f3134b Add test regarding EntrypointLookupCollection.
a238fe7 Handle the new Entrypoint collection to render paths.
2c38514 Create a new exception when the Entrypoint is not defined.
59bbd29 Update extension to load entrypoint build path.
0f69516 Add an EntrypointLookupCollection.
ebe61d9 Allow to define a list of entrypoints to be used in the project.
@seyfer
Copy link

seyfer commented Jan 6, 2019

Great PR. 👍
Docs need to be updated too and show how multiple entry points can be used.

Also would be great to have v1.0.1 or v1.1.0 release tag with this feature.

@seyfer
Copy link

seyfer commented Jan 14, 2019

@weaverryan Does it break dev-server --hot?
See my comment here symfony/webpack-encore#478 (comment)

@nyroDev
Copy link

nyroDev commented Jan 15, 2019

I know this have been merged but not yet published, but I found an inconsistency between entrypoints and manifest configuration.

In assets, the configuration might looks like:

framework:
    assets:
        json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'
        packages:
            mobile:
                json_manifest_path: "%kernel.project_dir%/public/buildMobile/manifest.json"

And in webpack_encore, with this PR, this might look like :

webpack_encore:
    # The path where Encore is building the assets.
    # This should match Encore.setOutputPath() in webpack.config.js.
    output_path: '%kernel.project_dir%/public/build'
    builds:
        mobile: '%kernel.project_dir%/public/buildMobile'

My first suggestion would be to simply rename builds into packages directly. (I could PR it if needed)

And then, maybe we can have a more global thinking later in order to merge both configuration and to have only 1 place to configure the output folder.
One solution might be that webpack_encore bundle read the json_manifest_path and deduce the entrypoints path from it.

@shulard
Copy link
Contributor Author

shulard commented Jan 15, 2019

@nyroDev, for me this is two different things. Your first code sample is related to the Asset component configuration. package mean asset package here so you can define multiple asset stores to be used in your application.

Your second sample is the webpack_encore bundle configuration. In that context, the build term mean a specific webpack build.

For me those two terms are relevant in their specific context. Event with that PR merged, you can use different asset packages with different webpack builds. Each of these features allow to control specific part of the asset management in your app.

@ureimers
Copy link

Hi @shulard,
you're using the parameter %kernel.public_dir% but that one's not defined in Symfony. At least not in Symfony 4.2:
You have requested a non-existent parameter "kernel.public_dir". It's not hard, to change that to '%kernel.project_dir%/public' but I'm just curious.

Am I missing something?

@shulard
Copy link
Contributor Author

shulard commented Jan 28, 2019

Hi @ureimers,

When are you getting that error ? I don't find any usage of public_dir in the code, only in the README which maybe contains a bad example...

@ureimers
Copy link

@shulard, no you're right, I just took it from the README (and your initial post from this PR) and thought that maybe my SF 4.2 was incorrectly setup. It was just out of curiosity.

Cheers

@shulard
Copy link
Contributor Author

shulard commented Jan 29, 2019

Ok 😄

It's an important feedback if the variable doesn't exists, I'll make a tiny PR to update the README.

@shulard
Copy link
Contributor Author

shulard commented Jan 29, 2019

@ureimers, I've created a tiny PR to update the README and I've updated that PR description to avoid confusion 😉. Thank you for the feedback !

@ureimers
Copy link

@shulard, you're welcome and thank you for the quick response!

weaverryan added a commit that referenced this pull request Mar 1, 2019
…d. (shulard)

This PR was merged into the master branch.

Discussion
----------

Remove unknown variable `%kernel.public_dir%` from README.md.

As said in #17 (comment), this variable does not exists in Symfony 4.2 and must not be used in the README as a valid example.

Commits
-------

9996437 Remove unknown variable `%kernel.public_dir%`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants