If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeSometimes, 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.
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.
... 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 namedcamelCase
.
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?
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
.
... 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/
.
... 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.
... 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!
"Houston: no signs of life"
Start the conversation!
// 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
}
}