UNPKG

18.7 kBMarkdownView Raw
1# ember-auto-import
2
3Just `import` from NPM, with zero configuration.
4
5## Installation
6
7```
8npm install --save-dev ember-auto-import webpack
9```
10
11If you're upgrading from 1.x to 2.x [see the upgrade guide](./docs/upgrade-guide-2.0.md).
12
13## Usage
14
15Add whatever dependency you want to your project using NPM or yarn like:
16
17```
18npm install --save-dev lodash-es
19```
20
21or
22
23```
24yarn add --dev lodash-es
25```
26
27Then just import it from your Ember app code:
28
29```js
30import { capitalize } from 'lodash-es';
31```
32
33There is no step two. Works from both app code and test code.
34
35## Dynamic Import
36
37In 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.
38
39Dynamic import is currently a Stage 3 ECMA feature, so to use it there are a few extra setup steps:
40
411. `npm install --save-dev babel-eslint`
422. In your `.eslintrc.js` file, add
43
44 parser: 'babel-eslint'
45
463. In your `ember-cli-build.js` file, enable the babel plugin provided by ember-auto-import:
47
48```js
49let app = new EmberApp(defaults, {
50 babel: {
51 plugins: [require.resolve('ember-auto-import/babel-plugin')],
52 },
53});
54```
55
56Once 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:
57
58```js
59export default Route.extend({
60 model({ id }) {
61 return Promise.all([
62 fetch(`/data-for-chart/${id}`).then(response => response.json()),
63 import('highcharts').then(module => module.default),
64 ]).then(([dataPoints, highcharts]) => {
65 return { dataPoints, highcharts };
66 });
67 },
68});
69```
70
71If 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`.
72
73## App imports
74
75`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:
76
77- use webpack plugins to influence the loading of `my-app-name/lib/massive`
78- dynamically import `my-app-name/lib/massive` in such a way that it wouldn't increase the size of your asset.
79- import assets from your app that would go through webpack loaders
80
81Fortunatly 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:
82
83```js
84let app = new EmberApp(defaults, {
85 autoImport: {
86 allowAppImports: [ 'lib/*' ],
87 }
88});
89```
90
91Then 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.
92
93## Customizing Build Behavior
94
95While 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.
96
97You can set options like this in your ember-cli-build.js:
98
99```js
100// In your ember-cli-build.js file
101let app = new EmberApp(defaults, {
102 autoImport: {
103 alias: {
104 // when the app tries to import from "plotly.js", use
105 // the real package "plotly.js-basic-dist" instead.
106 'plotly.js': 'plotly.js-basic-dist',
107
108 // you can also use aliases to pick a different entrypoint
109 // within the same package. This can come up when the default
110 // entrypoint only works in Node, but there is also a browser
111 // build available (and the author didn't provide a "browser"
112 // field in package.json that would let us detect it
113 // automatically).
114 handlebars: 'handlebars/dist/handlebars',
115
116 // We do a prefix match by default, so the above would also
117 // convert "handlebars/foo" to "handlebars/dist/handlesbars/foo".
118 // If instad you want an exact match only, you can use a trailing "$".
119 // For example, this will rewrite "some-package/alpha" to "customized"
120 // but leave "some-package/beta" alone.
121 'some-package/alpha$': 'customized',
122 },
123 allowAppImports: [
124 // minimatch patterns for app files that you want to be handled by ember-auto-import
125 ],
126 exclude: ['some-package'],
127 skipBabel: [
128 {
129 // when an already-babel-transpiled package like "mapbox-gl" is
130 // not skipped, it can produce errors in the production mode
131 // due to double transpilation
132 package: 'mapbox-gl',
133 semverRange: '*',
134 },
135 ],
136 watchDependencies: [
137 // trigger rebuilds if "some-lib" changes during development
138 'some-lib',
139 // trigger rebuilds if "some-lib"'s inner dependency "other-lib" changes
140 ['some-lib', 'other-lib'],
141 ],
142 webpack: {
143 // extra webpack configuration goes here
144 },
145 },
146});
147```
148
149Supported Options
150
151- `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.
152- `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.
153- `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).
154- `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.
155- `insertScriptsAt`: _string_, defaults to undefined. Optionally allows you to take manual control over where ember-auto-import's generated `<script>` tags will be inserted into your HTML and what attributes they will have. See "Customizing HTML Insertion" below.
156- `insertStylesAt`: _string_, defaults to undefined. Optionally allows you to take manual control over where ember-auto-import's generated `<link rel="stylesheet">` tags (if any) will be inserted into your HTML and what attributes they will have. See "Customizing HTML Insertion" below.
157- `publicAssetURL`: the public URL to your `/assets` directory on the web. Many apps won't need to set this because we try to detect it automatically, but you will need to set this explicitly if you're deploying your assets to a different origin than your app (for example, on a CDN) or if you are using `<script defer>` (which causes scripts to be unable to guess what origin they loaded from).
158- `skipBabel`: _list of objects, defaults to []_. The specified packages will be skipped from babel transpilation.
159- `watchDependencies`: _list of strings or string arrays, defaults to []_. Tells ember-auto-import that you'd like to trigger a rebuild if one of these auto-imported dependencies changes. Pass a package name that refers to one of your own dependencies, or pass an array of package names to address a deeper dependency.
160- `webpack`: _object_, An object that will get merged into the configuration we pass to webpack. This lets you work around quirks in underlying libraries and otherwise customize the way Webpack will assemble your dependencies.
161
162## Usage from Addons
163
164Using ember-auto-import inside an addon is almost exactly the same as inside an app.
165
166### Installing ember-auto-import in an addon
167
168To add ember-auto-import to your addon:
169
170- add ember-auto-import to your `dependencies`, not your `devDependencies`, so it will be present when your addon is used by apps
171- add webpack to your `devDependencies` (to support your test suite) but not your `dependencies` (the app's version will be used)
172- document for your users that their app must depend on ember-auto-import >= 2 in order to use your addon
173- configure ember-auto-import (if needed) in your `index.js` file (not your `ember-cli-build.js` file), like this:
174
175 ```js
176 // In your addon's index.js file
177 module.exports = {
178 name: 'sample-addon',
179 options: {
180 autoImport: {
181 exclude: ['some-package'],
182 },
183 },
184 };
185 ```
186
187- if your addon uses [Dynamic Import](#dynamic-import), it is [required](https://github.com/babel/ember-cli-babel#options) that you
188 register the babel plugin in your `index.js` instead of `ember-cli-build.js`:
189 ```js
190 // index.js
191 module.exports = {
192 options: {
193 babel: {
194 plugins: [require.resolve('ember-auto-import/babel-plugin')],
195 },
196 },
197 };
198 ```
199
200### Caveats in addons
201
202- ember-auto-import will refuse to import `devDependencies` of your addon into addon code (because that would fail in a consuming application). You _can_ import `devDependencies` into your test suite & dummy app.
203- ember-auto-import will not detect import statements inside your `app` folder. This is because the files inside `app` are conceptually not part of your addon's own package namespace at all, so they don't get access to your addon's dependencies. Do all your auto-importing from the `addon` folder, and reexport in `app` as needed.
204- while addons are allowed to pass the `autoImport.webpack` option to add things to the webpack config, this makes them less likely to be broadly compatible with apps using different webpack versions. If you need to rely on a specific webpack feature, you should document which versions of webpack you support.
205
206## Customizing HTML Insertion
207
208ember-auto-import uses webpack to generate one or more chunk files containing all your auto-imported dependencies, and then ember-auto-import inserts `<script>` tags to your HTML to make sure those chunks are included into your app (and tests, as appropriate). By default, the "app" webpack chunk(s) will be inserted after Ember's traditional "vendor.js" and the "tests" webpack chunk(s) will be inserted after "test-support.js".
209
210If you need more control over the HTML insertion, you can use the `insertScriptsAt` option (or the `insertStylesAt` option, which is exactly analogous but for standalone CSS instead of JS). To customize HTML insertion:
211
2121. Set `insertScriptsAt` to a custom element name. You get to pick the name so that it can't collide with any existing custom elements in your site, but a good default choice is "auto-import-script":
213
214 ```js
215 let app = new EmberApp(defaults, {
216 autoImport: {
217 insertScriptsAt: 'auto-import-script',
218 },
219 });
220 ```
221
2222. In your `index.html` and `tests/index.html`, use the custom element to designate exactly where you want the "app" and "tests" entrypoints to be inserted:
223
224 ```diff
225 <!-- in index.html -->
226 <body>
227 {{content-for "body"}}
228 <script src="{{rootURL}}assets/vendor.js"></script>
229 + <auto-import-script entrypoint="app"></auto-import-script>
230 <script src="{{rootURL}}assets/your-app.js"></script>
231 {{content-for "body-footer"}}
232 </body>
233 ```
234
235 ```diff
236 <!-- in tests/index.html -->
237 <body>
238 {{content-for "body"}}
239 {{content-for "test-body"}}
240
241 <div id="qunit"></div>
242 <div id="qunit-fixture">
243 <div id="ember-testing-container">
244 <div id="ember-testing"></div>
245 </div>
246 </div>
247
248 <script src="/testem.js" integrity=""></script>
249 <script src="{{rootURL}}assets/vendor.js"></script>
250 + <auto-import-script entrypoint="app"></auto-import-script>
251 <script src="{{rootURL}}assets/test-support.js"></script>
252 + <auto-import-script entrypoint="tests"></auto-import-script>
253 <script src="{{rootURL}}assets/your-app.js"></script>
254 <script src="{{rootURL}}assets/tests.js"></script>
255
256 {{content-for "body-footer"}}
257 {{content-for "test-body-footer"}}
258 </body>
259 ```
260
2613. Any attributes other than `entrypoint` will be copied onto the resulting `<script>` tags inserted by ember-auto-import. For example, if you want `<script defer></script>` you can say:
262
263 ```html
264 <auto-import-script defer entrypoint="app"> </auto-import-script>
265 ```
266
267 And this will result in output like:
268
269 ```html
270 <script defer src="/assets/chunk-12341234.js"></script>
271 ```
272
273Once 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:
274
275```js
276// ember-cli-build.js
277let app = new EmberApp(defaults, {
278 autoImport: {
279 insertScriptsAt: 'auto-import-script',
280 webpack: {
281 entry: {
282 polyfills: './lib/polyfills.js',
283 },
284 },
285 },
286});
287
288// lib/polyfills.js
289import 'core-js/stable';
290import 'intl';
291```
292
293```html
294<!-- index.html -->
295<auto-import-script nomodule entrypoint="polyfills"></auto-import-script>
296<script src="{{rootURL}}assets/vendor.js"></script>
297<auto-import-script entrypoint="app"></auto-import-script>
298<script src="{{rootURL}}assets/your-app.js"></script>
299```
300
301## Fastboot
302
303ember-auto-import works with [Fastboot](https://ember-fastboot.com) to support server-side rendering.
304
305When 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:
306
307```
308For the selected environment is no default script chunk format available:
309JSONP Array push can be chosen when 'document' or 'importScripts' is available.
310CommonJs exports can be chosen when 'require' or node builtins are available.
311Make 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.
312```
313
314You can fix this by setting the target to web explicitly:
315
316```js
317// ember-cli-build.js
318let app = new EmberApp(defaults, {
319 autoImport: {
320 webpack: {
321 target: 'web',
322 },
323 },
324});
325```
326
327## FAQ
328
329### `global is undefined` or `can't find module "path"` or `can't find module "fs"`
330
331You'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:
332
333```
334let app = new EmberApp(defaults, {
335 autoImport: {
336 webpack: {
337 node: {
338 global: true,
339 fs: 'empty'
340 }
341 }
342 }
343```
344
345See [webpack's docs on Node polyfills](https://v4.webpack.js.org/configuration/node/).
346
347### I use Content Security Policy (CSP) and it breaks ember-auto-import.
348
349See `forbidEval` above.
350
351### I'm trying to load a jQuery plugin, but it doesn't attach itself to the copy of jQuery that's already in my Ember app.
352
353Ember 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:
354
355```js
356// In your ember-cli-build.js file
357let app = new EmberApp(defaults, {
358 autoImport: {
359 webpack: {
360 externals: { jquery: 'jQuery' },
361 },
362 },
363});
364```
365
366Also, some jQuery plugins like masonry and flickity have [required manual steps to connect them to jQuery](https://github.com/ef4/ember-auto-import/issues/59#issuecomment-405391414).
367
368### I upgraded my `ember-auto-import` version and now things don't import. What changed?
369
370As of version `1.4.0`, by default, `ember-auto-import` does not include webpack's automatic polyfills for certain Node packages.
371Some 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."
372You can opt-in to [Webpack's polyfills](https://webpack.js.org/configuration/node/), or install your own.
373See [this issue](https://github.com/ef4/ember-auto-import/issues/224#issuecomment-503400386) for an example.
374
375### I get `Uncaught ReferenceError: a is not defined` [251](https://github.com/ef4/ember-auto-import/issues/251) with an already babel transpiled addon, e.g: `mapbox-gl`
376
377We should skip that specific addon from the ember-auto-import's babel transpilation as:
378
379```js
380// In your app's ember-cli-build.js file or check the `Usage from Addons` section for relevant usage of the following in addons
381let app = new EmberApp(defaults, {
382 autoImport: {
383 skipBabel: [
384 {
385 package: 'mapbox-gl',
386 semverRange: '*',
387 },
388 ],
389 },
390});
391```
392
393### I want to import a module for side effects only.
394
395Some 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.
396
397To import a module for side affects only, you can simply [import the module directly](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#import_a_module_for_its_side_effects_only).<br>
398Any side affects the module provides will take affect.
399
400Example: the `eventsource` package provides a ready to use [eventsource-polyfill.js](https://github.com/EventSource/eventsource/blob/master/example/eventsource-polyfill.js) module.
401
402This can be imported like:
403
404```js
405// In any js file, likely the file you need to access the polyfill, purely for organization.
406
407// Importing the polyfill adds a new global object EventSourcePolyfill.
408import 'eventsource/example/eventsource-polyfill.js';
409```
410
411## Debugging Tips
412
413Set the environment variable `DEBUG="ember-auto-import:*"` to see debug logging during the build.
414
415To see Webpack's console output, set the environment variable `AUTO_IMPORT_VERBOSE=true`.
416
417## Credit / History
418
419Takes 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.
420
421## Contributing
422
423[See CONTRIBUTING.md](CONTRIBUTING.md)
424
425## License
426
427This project is licensed under the [MIT License](LICENSE.md).