00:00
[MUSIC]
00:05
So far, we have been making use of
00:07
Visual Studio Code's auto-import feature to import
00:09
the various files and packages that we've been
00:11
using throughout our application so far.
00:13
If we take a brief moment here to actually
00:14
inspect some of the imports that we have,
00:16
for example, I'm within our movie model right now,
00:18
we'll see something that we might not be familiar with,
00:21
this hash services/cache service.
00:23
Particularly, it's the hash services
00:25
portion that you may not be familiar with.
00:27
However, as we hover over this particular import,
00:29
we see that Visual Studio Code is successfully able to map
00:32
this back to a module at our code,
00:35
let's learn AdonisJS 6 at services/cache service file,
00:39
which is absolutely correct.
00:41
We go at services/cache service,
00:43
and that is indeed the file that we're importing here.
00:45
So how exactly is that mapping happening?
00:47
That's what we want to discuss here today.
00:49
So this is using a feature called Node.js subpath imports.
00:53
If we dive into the documentation here,
00:54
so let's open up our browser and head to
00:56
nodejs.org/api/packages.html/subpathimports.
01:01
There's a whole section here talking about what these are.
01:04
If we go ahead and scroll down here a little bit further,
01:06
we'll see subpath patterns.
01:08
This is the particular feature that AdonisJS
01:10
is making use of to allow us to specify that hash services
01:14
to then import from our app services directory.
01:17
Within the example here,
01:18
we see that we have an imports object,
01:20
which has a mapping of hash internal/star.js.
01:24
The hash internal is defining a subpath import namespace.
01:27
The star is allowing us to specify any file
01:30
within the internal directory
01:32
that we're in turn pointing to right here.
01:34
And then the .js is required
01:36
because we're working with ECMAScript modules,
01:38
so we need to specify the file extension.
01:40
The right-hand side of that is then pointing
01:42
to the underlying directory and the files therein
01:45
that our import namespace should resolve to.
01:48
So if we were to specify an import for hash internals/test.js,
01:53
the underlying file would wanna be within
01:54
source internal/test.js.
01:57
If we scroll down just slightly further,
01:59
Node.js will describe this within their documentation.
02:01
So you have an import for various features
02:03
from es-module-package/features/x.js,
02:08
and then they'll have the underlying resolved package
02:10
from that import right here.
02:11
If we scroll up a little bit further,
02:12
they also describe why the hash is there.
02:15
So right here, entries in the imports field
02:17
must always start with a hash
02:18
to ensure that they are disambiguated
02:20
from external package specifiers.
02:22
Essentially meaning it's a special character
02:24
that allows them to know specifically
02:25
that you wanna use a subpath import
02:27
to import the particular file that you're working with,
02:29
as opposed to like the at or ./ designation
02:32
that you could be doing to import from Node.js
02:35
or a relative path file.
02:36
Cool, so if you wanna dig into this further,
02:39
there's the documentation path right there.
02:41
Let's go and switch over to our application now
02:43
and hide our browser back away.
02:44
So we know that these are defined within our package.json,
02:47
so let's scroll down and take a look at our package.json.
02:49
You wanna scroll down just a little bit
02:50
until you see this imports object right here,
02:52
where we see a number of different
02:54
subpath namespaces defined.
02:56
We have one for our controllers, exceptions, models,
02:58
mails, services, listeners, et cetera.
03:00
With every fresh AdonisJS 6 project,
03:02
Adonis has set these up for us automatically
03:04
so that we don't need to go in and manually define them
03:06
whenever we get to the point where we're working with them.
03:08
They know that these are most likely
03:09
going to be directories that we need to work with
03:11
at some point through our applications development.
03:13
So they've gone ahead and defined them for us here
03:16
so that we can make easy use of them.
03:17
However, there may come a time
03:18
where you need to define your own
03:19
because you're using something to your own taste
03:21
or specifications.
03:22
For example, a lot of people like to use DTOs or ViewModels,
03:26
so you could add those into your project
03:28
and then define the subpath alias
03:29
for those various import paths.
03:31
So we're gonna end up getting rid of the work
03:32
that we do within this lesson here.
03:34
I just want to highlight how you can add
03:35
your own subpath imports in should you need to.
03:38
So let's roll with the ViewModel example.
03:41
So within our app directory, let's create a new file
03:43
and let's create a new directory called view_models.
03:47
And within here, we'll create a new file called movie.ts.
03:50
So now we have both a movie model and a movie view model.
03:53
Now, as the name suggests, the movie model
03:56
defines what a movie is per our database.
03:59
The movie view model then defines
04:01
what a movie is to our views.
04:03
So they're just models with two different purposes
04:05
within this particular example.
04:07
So that we don't spend too much time on this example,
04:09
I'm just going to go ahead and export default class,
04:12
movie VM, and I'm just going to extend our movie model
04:16
just like so.
04:16
If we wanted to make it obvious
04:17
which one we were working with,
04:19
we can go ahead and add an additional property on here.
04:20
So public type equals ViewModel, something like that.
04:23
So now this type property only exists on our ViewModel
04:26
and not on our movie model.
04:27
Okay, next, let's go ahead and jump
04:29
into our movies controller and let's make use of it.
04:31
So instead of using our movie
04:32
to query all of our different movies,
04:33
let's go ahead and switch this over to our movie model.
04:35
So we'll go ahead and just use automatic imports here
04:37
and we'll scroll down to our movie VM right here
04:40
and go and give that an import.
04:41
And the first thing you'll notice
04:42
is that this is a relative path import that it has added.
04:45
And that's because we haven't defined
04:47
a sub path import quite yet for our ViewModels directory.
04:50
So it's telling Node.js to look for this file at ./
04:53
which goes back a directory from where we're at.
04:55
So out of controllers and into app, into ViewModels
04:58
and then into movie.js.
05:00
Now the file extension is here as we described earlier
05:03
because we're working with ECMAScript modules
05:05
and the file extension is needed
05:06
for relative imports for those.
05:08
Now you might notice that this is a .js file
05:10
despite the actual file being movie.ts.
05:13
The reason behind that to my understanding
05:15
is that it was a TypeScript decision
05:16
because at the end of the day TypeScript
05:18
compiles down to JavaScript
05:19
and they didn't want to mix and match the file extensions.
05:22
So that's why that is there to my understanding.
05:24
Okay, so before we switch this over to a sub path import,
05:27
let's go and give it a test
05:28
just to make sure everything's still working A-okay.
05:30
So let's dive back into our browser
05:32
and give our homepage a refresh where we swap this in.
05:34
Okay, good, everything's still working.
05:35
We can hide that back away.
05:36
And now we know that we need to define our sub path import
05:39
within our package.json file.
05:40
So let's dive down into there.
05:42
And just after our models,
05:43
let's go ahead and add in our view models.
05:45
So we'll do a string hash view_models/*
05:49
designating that the view models namespace
05:51
will work for any file within here, colon.
05:54
And now we can add in the value for this,
05:55
which should be a relative path to the files.
05:57
So add in a string ./,
05:59
which is the start of a relative path
06:01
coming out of our applications root.
06:03
So if we scroll up a little bit,
06:04
we'll want to go into app view_models/*.js
06:08
to support any file within our view models.
06:10
So we'll go app view_models/*.js.
06:15
Give this a save.
06:16
And now we can jump back into our movies controller
06:18
and let's switch this relative import
06:21
for our sub path import.
06:22
So we can get rid of the ./ and replace it with a hash.
06:25
And now we can just get rid of the file extension
06:28
and voila, everything is happy so far.
06:30
However, there is one more step that we need to take.
06:33
Sub path imports are one area where JavaScript
06:35
and TypeScript don't communicate too well with one another
06:38
and TypeScript isn't actually going to read these imports
06:41
from our package.json file.
06:42
So we need to inform TypeScript about them separately.
06:45
So we'll have kind of a one-to-one mapping
06:47
in two different places
06:48
within both of our nodejs package.json configurations
06:52
and our tsconfig.json file.
06:54
So if we scroll down to our tsconfig.json file,
06:57
we will see a paths object like so
07:00
with all of the same mappings that we have
07:01
within our package.json file,
07:03
except they're here now for TypeScript.
07:05
So in order to make use of the sub path alias,
07:07
we'll also want to define our view model sub path alias
07:10
within our TypeScript paths as well.
07:13
Everything here will look pretty much the exact same.
07:15
The only difference is the value will be an array
07:17
rather than just a string.
07:19
And now we should be able to jump back into our browser,
07:21
give our homepage a refresh and everything still works A-OK
07:25
despite us now importing using sub path aliases
07:28
for our movie view model.
07:30
Okay, so within this series,
07:31
we're not actually going to be working with view models.
07:32
So we'll go ahead and get rid of what we've just done.
07:34
But before we do,
07:35
let's go ahead and summarize
07:37
what exactly we covered in this lesson.
07:38
So first, hashes within your imports
07:40
designate sub path imports,
07:42
and you can define your own,
07:43
but you need to do so within both the tsconfig.json file
07:46
and the package.json file.
07:48
Within the tsconfig.json, it will be within paths.
07:50
I'm gonna go ahead and get rid of our view model import here.
07:52
And this paths property is specifically
07:54
within the compiler options.
07:55
Within our package.json file,
07:57
it's gonna be within imports.
07:58
So we'll go ahead and get rid of the view models there
08:00
as well.
08:01
If we scroll up,
08:01
imports is just a baseline property
08:03
within our package.json file.
08:05
The package.json import is needed for node.js,
08:07
whereas the tsconfig.json import is needed for TypeScript.
08:11
You can do relative path imports.
08:13
So if we go back a couple of steps here,
08:14
this works A-okay,
08:16
but you will need to add in .js
08:18
because we're working with ECMAScript modules.
08:20
And you'll need to use .js
Join The Discussion! (4 Comments)
Please sign in or sign up for free to join in on the dicussion.
news-zanndo
Not present by default in tsconfig.json
Please sign in or sign up for free to reply
tomgobich
Hi news-zanndo! Yes, as of TypeScript 5.4, TypeScript will now automatically use the subpath imports defined within your
package.json
, meaning we no longer need to redundantly re-define them inside thetsconfig.json
.Apologies, I thought I had updated the body text for this lesson noting this, but evidently, I did not. Will do that now.
Please sign in or sign up for free to reply
noctisy
Thank you for the explanation!
Please sign in or sign up for free to reply
tomgobich
Anytime, noctisy!!
Please sign in or sign up for free to reply