Where do I import the thing?
Ember has historically allowed access to everything in a global scope, like web-components. This proved confusing for developers, and Ember has moved to a more explicit, import-what-you-need approach, called "template-tag" -- more information on that here, in the official guides.
A common thing I hear from folks is that they don't know where to import a component/modifier/helper from a particular addon, or maybe assume that because something isn't documented, they can't import the component/modifier/helper.
Good news: if it works in loose mode (the globals way), the import is public API.
Here is how you find them:
open
node_modules/${libraryName}
- if it doesn't exist, install
libraryName
/ add to yourpackage.json
- if it doesn't exist, install
open the
package.json
oflibraryName
does it have
exports
?This could look like this for types-providing projects:
"exports": { ".": { "types": "./declarations/index.d.ts", "default": "./dist/index.js" }, "./*": { "types": "./declarations/*.d.ts", "default": "./dist/*.js" }, "./addon-main.js": "./addon-main.cjs" },
or it could look like this for non-types-providing projects
"exports": { ".": "./dist/index.js", "./*": "./dist/*.js", "./addon-main.js": "./addon-main.cjs" },
Exports say if you import x from
library-name/${sub path specifier}
, theexports
map will match to a file on disk. So you can look through the dist directory if./*
is specified and see what all you can import. See the docs for details.
For example, if you see a file in dist/components/foo.js (and given the above exports), you can import it atlibrary-name/components/foo
.
There is an intermediary step which may or may not ever be relevant, due to how strongly folks follow conventions in with the library blueprints, but if there is an
ember-addon.app-js
key in the package.json -- this is the mapping of what is dumped in to the globals resolver, and is what is exposed to you pre-gjs/gts/template-tag. This config will looks something like this:"ember-addon": { "version": 2, "type": "addon", "main": "addon-main.cjs", "app-js": { "./components/foo.js": "./dist/some-other-folder/foo.js" } },
In this scenario, instead of importing from
libraryName/components/foo
, you'd import fromlibraryName/some-other-folder/foo
.Once you find the file in
dist
, you're done.
If the package.json you're looking at does not have an
exports
config, and the library is an ember-addon (hasember-addon
listed inkeywords
), this likely the older "v1 addon" format, which was convention-based, and didn't follow broader standards (as they didn't exist yet). By convention, you'll need to check theapp
folder for your components/modifiers/helpers, and see what those files define or re-export. You can then import what those files use.For example, if you find
app/components/foo.js
containsexport { default } from 'libraryName/some-other-folder/foo'
you can import from that same location. e.g.:
import theDefaultExport from 'libraryName/some-other-folder/foo';
This information is also available on the ember-template-imports README.
Don't know which library to start with?
you can find anything by searching in node_modules
.
I like using thesilversearcher, but any search tool will work.
❯ ag --unrestricted "<ResponsiveImage" --file-search-regex '.js$'
I use --unrestricted
to search ignored files (node_modules
), and --file-search-regex
with .js$
, because I want to exclude source-map files, .js.map$
(I haven't learned how to read those).
So if I want to search for "ResponsiveImage" (a component that's used on this site), I don't get results in node_modules, but in my dist
folder (my app's output) -- this means I need to try one of the other forms of which components can be referenced.
These are the possibilities for the direct name reference:
<PascalCase
, hoping that the component's JSDoc exists❯ ag --unrestricted "<ResponsiveImage" --file-search-regex '.js$'
class PascalCase
to find the file in JS❯ ag --unrestricted "class ResponsiveImage" --file-search-regex '.js$'
But we can also search via file path:
kebab-case
❯ ag --unrestricted --filename-pattern 'responsive-image'
using
--filename-pattern
allows us to search for file paths, and not the contents of the file. We might not know what is in a particular file.or, if too many results, you can search with the extensions:
kebab-case.js
kebab-case.ts
kebab-case.gjs
kebab-case.gts
kebab-case.hbs
kebab-case/index.js
kebab-case/index.ts
kebab-case/index.gjs
kebab-case/index.gts
kebab-case/index.hbs
All can be searched for all at once with this (which can still be far fewer results than with no extension):
❯ ag --unrestricted --filename-pattern 'responsive-image(\/index)?\.(js|ts|hbs|gjs|gts)'
Feel like a lot of work to find which library something comes from? I think so, too.
This is why gjs/gts/template-tag is so nice, because you just know exactly where something is coming from.
To try out gjs / <template>
in your browser, check out this interactive tutorial.