Zero-config import from NPM packages
npm install ember-auto-importJust import from NPM, with zero configuration.
```
npm install --save-dev ember-auto-import webpack
If you're upgrading from 1.x to 2.x see the upgrade guide.
Add whatever dependency you want to your project using NPM or yarn like:
``
npm install --save-dev lodash-es
or
``
yarn add --dev lodash-es
Then just import it from your Ember app code:
`js`
import { capitalize } from 'lodash-es';
There is no step two. Works from both app code and test code.
In addition to static top-level import statements, you can use dynamic import() to lazily load your dependencies. This can be great for reducing your initial bundle size.
Dynamic import is currently a Stage 3 ECMA feature, so to use it there are a few extra setup steps:
1. npm install --save-dev babel-eslint.eslintrc.js
2. In your file, add
parser: 'babel-eslint'
3. In your ember-cli-build.js file, enable the babel plugin provided by ember-auto-import:
`js`
let app = new EmberApp(defaults, {
babel: {
plugins: [require.resolve('ember-auto-import/babel-plugin')],
},
});
Once you're setup, you can use dynamic import() and it will result in loading that particular dependency (and all its recursive dependencies) via a separate Javascript file at runtime. Here's an example of using dynamic import from within a Route, so that the extra library needed for the route is loaded at the same time the data is loaded:
`js/data-for-chart/${id}
export default Route.extend({
model({ id }) {
return Promise.all([
fetch().then(response => response.json()),`
import('highcharts').then(module => module.default),
]).then(([dataPoints, highcharts]) => {
return { dataPoints, highcharts };
});
},
});
If you're using custom deployment code, make sure it will include all the Javascript files in dist/assets, not just the default app.js and vendor.js.
ember-auto-import was originally designed to allow Ember apps to import from npm packages easily, and would have no influence on your app's files (i.e. files that exist in your app folder). This meant that every time you had an import like import someBigLib from 'my-app-name/lib/massive' there was no way for you to:
- use webpack plugins to influence the loading of my-app-name/lib/massivemy-app-name/lib/massive
- dynamically import in such a way that it wouldn't increase the size of your asset.
- import assets from your app that would go through webpack loaders
Fortunatly there is a way to configure ember-auto-import to work on certain parts of your app using the allowAppImports configuration option. If you set the option to:
`js`
let app = new EmberApp(defaults, {
autoImport: {
allowAppImports: [ 'lib/*' ],
}
});
Then the my-app-name/lib/massive file (and all other files in lib) would now be handled by ember-auto-import. This would then allow you to dynamically import('my-app-name/lib/massive') which means that you can dynamically load parts of your app on demand without first splitting them into an addon or an npm package.
While most NPM packages authored in CommonJS or ES Modules will Just Work, for others you may need to give ember-auto-import a hint about what to do.
You can set options like this in your ember-cli-build.js:
`js
// In your ember-cli-build.js file
let app = new EmberApp(defaults, {
autoImport: {
alias: {
// when the app tries to import from "plotly.js", use
// the real package "plotly.js-basic-dist" instead.
'plotly.js': 'plotly.js-basic-dist',
// you can also use aliases to pick a different entrypoint
// within the same package. This can come up when the default
// entrypoint only works in Node, but there is also a browser
// build available (and the author didn't provide a "browser"
// field in package.json that would let us detect it
// automatically).
handlebars: 'handlebars/dist/handlebars',
// We do a prefix match by default, so the above would also
// convert "handlebars/foo" to "handlebars/dist/handlesbars/foo".
// If instad you want an exact match only, you can use a trailing "$".
// For example, this will rewrite "some-package/alpha" to "customized"
// but leave "some-package/beta" alone.
'some-package/alpha$': 'customized',
},
allowAppImports: [
// minimatch patterns for app files that you want to be handled by ember-auto-import
],
exclude: ['some-package'],
skipBabel: [
{
// when an already-babel-transpiled package like "mapbox-gl" is
// not skipped, it can produce errors in the production mode
// due to double transpilation
package: 'mapbox-gl',
semverRange: '*',
},
],
watchDependencies: [
// trigger rebuilds if "some-lib" changes during development
'some-lib',
// trigger rebuilds if "some-lib"'s inner dependency "other-lib" changes
['some-lib', 'other-lib'],
],
webpack: {
// extra webpack configuration goes here
},
},
});
`
Supported Options
- alias: _object_, Map from imported names to substitute names that will be imported instead. This is a prefix match by default. To opt out of prefix-matching and only match exactly, add a $ suffix to the pattern.allowAppImports
- : _list of strings, defaults to []_. Files in your app folder that match these minimatch patterns will be handled by ember-auto-import (and thus Webpack) and no longer be part of the regular ember-cli pipeline.exclude
- : _list of strings, defaults to []_. Packages in this list will be ignored by ember-auto-import. Can be helpful if the package is already included another way (like a shim from some other Ember addon).forbidEval
- : _boolean_, defaults to false. We use eval in development by default (because that is the fastest way to provide sourcemaps). If you need to comply with a strict Content Security Policy (CSP), you can set forbidEval: true. You will still get sourcemaps, they will just use a slower implementation.insertScriptsAt
- : _string_, defaults to undefined. Optionally allows you to take manual control over where ember-auto-import's generated
+
{{content-for "body-footer"}}
`
` {{content-for "body-footer"}}diff`
{{content-for "body"}}
{{content-for "test-body"}}
+
+
{{content-for "test-body-footer"}}
3. Any attributes other than entrypoint will be copied onto the resulting you can say:
`html`
And this will result in output like:
`html`
Once you enable insertScriptsAt you _must_ designate places for the "app" and "tests" entrypoints if you want ember-auto-import to work correctly. You may also optionally designate additional entrypoints and manually add them to the webpack config. For example, you might want to build a polyfills bundle that needs to run before vendor.js on pre-ES-module browsers:
`js
// ember-cli-build.js
let app = new EmberApp(defaults, {
autoImport: {
insertScriptsAt: 'auto-import-script',
webpack: {
entry: {
polyfills: './lib/polyfills.js',
},
},
},
});
// lib/polyfills.js
import 'core-js/stable';
import 'intl';
`
`html`
ember-auto-import works with Fastboot to support server-side rendering.
When using Fastboot, you may need to add your Node version to config/targets.js in order to only use Javascript features that work in that Node version. When you do this, it may prevent webpack from being able to infer that it should still be doing a build that targets the web. This may result in an error message like:
``
For the selected environment is no default script chunk format available:
JSONP Array push can be chosen when 'document' or 'importScripts' is available.
CommonJs exports can be chosen when 'require' or node builtins are available.
Make sure that your 'browserslist' includes only platforms that support these features or select an appropriate 'target' to allow selecting a chunk format by default. Alternatively specify the 'output.chunkFormat' directly.
You can fix this by setting the target to web explicitly:
`js`
// ember-cli-build.js
let app = new EmberApp(defaults, {
autoImport: {
webpack: {
target: 'web',
},
},
});
You're trying to use a library that is written to work in NodeJS and not in the browser. You can choose to polyfill the Node feature you need by passing settings to webpack. For example:
``
let app = new EmberApp(defaults, {
autoImport: {
webpack: {
node: {
global: true,
fs: 'empty'
}
}
}
See webpack's docs on Node polyfills.
See forbidEval above.
Ember apps typically get jQuery from the ember-source or @ember/jquery packages. Neither of these is the real jquery NPM package, so ember-auto-import cannot "see" it statically at build time. You will need to give webpack a hint to treat jQuery as external:
`js`
// In your ember-cli-build.js file
let app = new EmberApp(defaults, {
autoImport: {
webpack: {
externals: { jquery: 'jQuery' },
},
},
});
Also, some jQuery plugins like masonry and flickity have required manual steps to connect them to jQuery.
As of version 1.4.0, by default, ember-auto-import does not include webpack's automatic polyfills for certain Node packages.
Some signs that your app was depending on these polyfills by accident are things like "global is not defined," "can't resolve path," or "default is not a function."
You can opt-in to Webpack's polyfills, or install your own.
See this issue for an example.
We should skip that specific addon from the ember-auto-import's babel transpilation as:
`jsUsage from Addons
// In your app's ember-cli-build.js file or check the section for relevant usage of the following in addons`
let app = new EmberApp(defaults, {
autoImport: {
skipBabel: [
{
package: 'mapbox-gl',
semverRange: '*',
},
],
},
});
Some modules, often times polyfills, don't provide values meant for direct import. Instead, the module is meant to provide certain side affects, such as mutating global variables.
To import a module for side affects only, you can simply import the module directly.
Any side affects the module provides will take affect.
Example: the eventsource package provides a ready to use eventsource-polyfill.js module.
This can be imported like:
`js
// In any js file, likely the file you need to access the polyfill, purely for organization.
// Importing the polyfill adds a new global object EventSourcePolyfill.
import 'eventsource/example/eventsource-polyfill.js';
`
Check the package.json "exports" config for the library you're trying to import. ember-auto-import only automatically includes files with the "browser" condition.
`js`
// Example package.json from @googleworkspace/drive-picker-element
"type": "module",
"exports": {
".": {
"browser": "./dist/index.iife.min.js",
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./drive-picker": {
"types": "./dist/drive-picker/index.d.ts",
"import": "./dist/drive-picker/index.js"
},
"./package.json": "./package.json"
},
If the file you want to import is specified under some other condition, such as "import" for ESM, you'll have to provide additional webpack config like this:
`jsUsage from Addons
// In your app's ember-cli-build.js file or check the section for relevant usage of the following in addons`
let app = new EmberApp(defaults, {
autoImport: {
webpack: {
resolve: {
conditionNames: ['import'],
},
},
},
});
Set the environment variable DEBUG="ember-auto-import:*" to see debug logging during the build.
To see Webpack's console output, set the environment variable AUTO_IMPORT_VERBOSE=true`.
Takes inspiration and some code from ember-browserify and ember-cli-cjs-transform. This package is basically what you get when you combine the ideas from those two addons.
This project is licensed under the MIT License.