Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Importing Specific Package Files

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Sometimes, instead of importing a package itself, you may want to import only part of it: like a specific file. Lodash is a good example of this.

No Tree Shaking in the Browser

But before we get there, instead of importing everything from lodash, you should be able to say import { camelCase } from 'lodash'. Then, down here, you would use camelCase directly.

6 lines assets/app.js
... line 1
import { camelCase } from 'lodash';
... lines 3 - 4
console.log(camelCase(mix.describe()));

However, when we move over and try this... error!

The requested module lodash does not provide an export named camelCase.

This should work... and the reason it doesn't is complicated. Basically, due to the way that this specific library packages their module, you can't import specific functions like this. It will work with most other packages, however.

For example, if you say import { Modal } from 'bootstrap' (if you're using Bootstrap), that will work. Bootstrap packages their files correctly.

However, using this syntax may not always be ideal with AssetMapper.

Here's the problem. If we ran this code through Encore, Encore would do something called "tree shaking". This is where it would see that we're only importing camelCase from lodash. And so, in the final JavaScript, it would only give us the code for camelCase, not the entire lodash package.

In a browser environment, if you import from lodash, you're going to get all of lodash... even if you're only importing one part of it. Now, that might not be that big of a deal. The full build of lodash is still only 24 kilobytes. But what if we are using a big package... but only need to import one specific thing?

Importing a Specific File

A lot of times, there's a specific file that we can import, like /camelCase. You'll usually find details about these files in the docs... though you can also go look for them. Head back to JSDelivr... and down here, search for "lodash". Below, click "Files" to see all the files that are part of this package.

For lodash, it's a huge list... because this is a huge library. One of these is camelCase.js.

Ok! So let's try importing lodash/camelCase.

6 lines assets/app.js
... line 1
import camelCase from 'lodash/camelCase';
... lines 3 - 6

I'm not including the .js... but it's not going to work anyway. Watch: when we refresh... error!

Failed to resolve module specifier lodash/camelCase. Relative references must start with either "/", "./", or "../"

This error means that we're importing something using a "bare" import, and it was not found in the importmap. If we "View Page Source", we do have an importmap for lodash, but not lodash/camelCase. Yup, that matching is done exactly. Ok, there is a way to do a, sort of, "fuzzy" matching - lodash/* - but I don't use that.

The point is: if you want to use lodash/camelCase, you should add that to your importmap, not lodash.

Watch: find your terminal and run:

php bin/console importmap:remove lodash

That will remove lodash from importmap.php and delete the file from assets/vendor/.

22 lines importmap.php
... lines 1 - 15
return [
'app' => [
'path' => 'app.js',
'preload' => true,
],
];

Nice! Now run ./bin/console importmap:require with the package name / the path that you want: lodash/camelCase.js.

php bin/console importmap:require lodash/camelCase.js

camelCase.js is the name of the file over on the CDN. But you'll notice that, a lot of times in the docs, they'll reference lodash/camelCase without the .js. And in this case, you can leave the .js off: it's up to you. That works because jsDelivr is friendly and makes both versions of the URL work.

The result of the command? The same as before! We get a new entry in importmap.php matching what we want to import and set to a URL.

25 lines importmap.php
... lines 1 - 15
return [
... lines 17 - 20
'lodash/camelCase' => [
'url' => 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/camelCase/+esm',
],
];

Copy that URL so we can see it. There we go! It's the code from just camelCase.js.

And when we try the page... it works!

Here's the takeaway: if you need to import a specific file from a package, you can do that: just pass the package name + file path to importmap:require.

Next, let's add Stimulus to our app!

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^4.0", // v4.2.0
        "doctrine/doctrine-bundle": "^2.7", // 2.10.0
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.4
        "doctrine/orm": "^2.12", // 2.15.2
        "knplabs/knp-time-bundle": "^1.18", // v1.20.0
        "pagerfanta/doctrine-orm-adapter": "^4.0", // v4.1.0
        "pagerfanta/twig": "^4.0", // v4.1.0
        "stof/doctrine-extensions-bundle": "^1.7", // v1.7.1
        "symfony/asset": "6.3.*", // v6.3.0
        "symfony/asset-mapper": "6.3.*", // v6.3.0
        "symfony/console": "6.3.*", // v6.3.0
        "symfony/dotenv": "6.3.*", // v6.3.0
        "symfony/flex": "^2", // v2.3.1
        "symfony/framework-bundle": "6.3.*", // v6.3.0
        "symfony/http-client": "6.3.*", // v6.3.0
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/proxy-manager-bridge": "6.3.*", // v6.3.0
        "symfony/runtime": "6.3.*", // v6.3.0
        "symfony/stimulus-bundle": "^2.9", // v2.9.1
        "symfony/twig-bundle": "6.3.*", // v6.3.0
        "symfony/ux-turbo": "^2.9", // v2.9.1
        "symfony/web-link": "6.3.*", // v6.3.0
        "symfony/yaml": "6.3.*", // v6.3.0
        "twig/extra-bundle": "^2.12|^3.0", // v3.6.1
        "twig/twig": "^2.12|^3.0" // v3.6.1
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.4
        "symfony/debug-bundle": "6.3.*", // v6.3.0
        "symfony/maker-bundle": "^1.41", // v1.49.0
        "symfony/stopwatch": "6.3.*", // v6.3.0
        "symfony/web-profiler-bundle": "6.3.*", // v6.3.0
        "zenstruck/foundry": "^1.21" // v1.33.0
    }
}
userVoice