Skip to content

[StimulusBundle] Error with AssetMapper 6.4 & StimulusBundle #1210

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

Closed
weaverryan opened this issue Oct 20, 2023 · 6 comments · May be fixed by #1213
Closed

[StimulusBundle] Error with AssetMapper 6.4 & StimulusBundle #1210

weaverryan opened this issue Oct 20, 2023 · 6 comments · May be fixed by #1213
Labels
Bug Bug Fix

Comments

@weaverryan
Copy link
Member

Originally from symfony/symfony#52106 (comment) from @tacman

The above require now works, thanks!

When I try to install asset-mapper:^6.4 in an existing project that's been upgraded to 6.4-DEV, I'm getting an error:

bash
composer req symfony/asset-mapper:^6.4

Symfony operations: 1 recipe (f9876b0bb3302355035a27b85d2368f3)
  - Configuring symfony/asset-mapper (>=6.4): From github.com/symfony/recipes:main
Executing script importmap:require [OK]
Executing script importmap:require [KO]
 [KO]
Script importmap:require returned with error code 1
!!  
!!  In ControllersMapGenerator.php line 75:
!!                                                         
!!    Warning: Attempt to read property "content" on null  
!!                                                         
!!  
!!  importmap:require [--entrypoint] [--path PATH] [--] <packages>...
!!  
!!  

The lines in question are

        foreach ($finder as $file) {
            $name = $file->getRelativePathname();
            $name = str_replace(['_controller.js', '-controller.js'], '', $name);
            $name = str_replace(['_', '/'], ['-', '--'], $name);

            $asset = $this->assetMapper->getAssetFromSourcePath($file->getRealPath());
            $isLazy = preg_match('/\/\*\s*stimulusFetch:\s*\'lazy\'\s*\*\//i', $asset->content);

            $controllersMap[$name] = new MappedControllerAsset($asset, $isLazy);
        }

so I suspect this has something to do with this project originally being in 6.3 and now being updated. Might it be possible to get a better message? $asset is null, and so maybe check for that and throw and error that includes the file->getRealPath().

The component installs okay, it's failing during the recipe, but I'm not sure what the full command is that the recipe is trying to run.

Executing script importmap:require [OK]

It would be helpful if the arguments were in this debug line, so I could isolate the problem.

@weaverryan weaverryan added the Bug Bug Fix label Oct 20, 2023
@weaverryan
Copy link
Member Author

This could be a legit problem. We're still working on updating StimulusBundle for AssetMapper, but I'm not aware of this issue.

Since, I think StimulusBundle is already installed in your app, can you sneak in a dump($file) before the error to see what file it's looking for?

@tacman
Copy link
Contributor

tacman commented Oct 20, 2023

OK, I figured it out, and it definitely deserves a better error message.

TL;DR: There was an invalid symlink in my assets directory

image

How could I get to this crazy, edge-case situation, you might wonder. Debugging javascript in a local bundle is a pain because I found myself constantly re-installing the bundle to get the assets to the right spot then running yarn dev, and maybe a yarn install --force for good measure. To simplify this a bit, I created a symlink to the controller I was testing, and invoked it when a special flag was set. This saved a step, but was still very error prone. Then I reorganized my bundles, but since the invalid stimulus controller was never used, it didn't cause a problem before, but now that asset-mapper checks the assets to create the map, all the assets have to be valid, even if they're never used.

Deleting the invalid symlink got rid of my problem, but as improbably as it seems, it's possible for $asset to be null there.

@weaverryan
Copy link
Member Author

Agreed - we should code defensively there and throw an exception. So, right here - https://github.com/symfony/ux/blob/2.x/src/StimulusBundle/src/AssetMapper/ControllersMapGenerator.php#L74

if (!$asset) {
    // throw some awesome exception
}

PR welcome :)

@smnandre
Copy link
Member

this has something to do with this project originally being in 6.3

Similar, this bloc seems a bit buggy in edge cases...

            /*
             * The AssetDependency will already be added by AssetMapper itself when
             * it processes this file. However, due to the "stimulusFetch: 'lazy'"
             * that may appear inside the controllers, this file is dependent on
             * the "contents" of each controller. So, we add the dependency here
             * and mark it as a "content" dependency so that this file's contents
             * will be recalculated when the contents of any controller changes.
             */
            if (class_exists(AssetDependency::class)) {
                // Backwards compatibility with Symfony 6.3
                $asset->addDependency(new AssetDependency(
                    $mappedControllerAsset->asset,
                    $mappedControllerAsset->isLazy,
                    true,
                ));
            } else {
                $asset->addDependency($mappedControllerAsset->asset);
            }

Not 100% but i feel that in local, if AssetDependency exists (in 6.3) and then you run composer update...
... it's still loaded and so the code make as it still is in the code

Not sure, really, but multiple times i felt this was what happened

@phasdev
Copy link

phasdev commented Apr 24, 2025

I have a build step where TypeScript controllers that are transpiled to JavaScript files which are saved in the same directory as the source TypeScript files. My asset mapper config excludes TypeScript files since I'm only interested in serving JavaScript. When loadCustomControllers is called, it finds all .ts and .js files in my controllers path. As it iterates over these files, it will try and look up asset objects corresponding to the .ts files:

$asset = $this->assetMapper->getAssetFromSourcePath($file->getRealPath());

However since .ts files are excluded assets, the corresponding asset objects cannot be found and the function throws an exception when it tries to read the sourcePath property on the null asset object.

$content = file_get_contents($asset->sourcePath);

Instead of throwing an exception when a null asset is detected, perhaps we could skip the unmapped asset:

if (!$asset) {
    continue;
}

That way the code defers to the asset mapper config when determining which controller files are mapped assets, instead of assuming it's all .js and .ts files in the controllers path. I'll submit a PR shortly.

@smnandre
Copy link
Member

I'm closing this issue here.. as the expected /decided behaviour at that time was on the contrary to throw an exception / alert user if an asset is missing.

Let's discuss your case and implementation idea in the PR you'll open !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Bug Fix
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants