npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ember-cli-bundle-loader

v0.5.0

Published

The default blueprint for ember-cli addons.

Downloads

192

Readme

Ember-cli-bundle-loader Build Status Download count all time Ember Observer Score

Code Climate Test Coverage Dependency Status Dependency Status npm version

This project lets you build your app in different packages and load those packages lazily. The main goal of this is faster boot times for large applications, but it can also help with independentl build and deployments for different sub-products or sections of your app.

You can see a demo live on http://miguelmadero.com/ember-cli-bundle-loader/

How it works

We treat each package as an EmberApp, each of them will have its own JS and CSS output. For each package, it also changes certain things to avoid unnecessary processing of addons to get faster builds for packages.

Installation

ember install MiguelMadero/ember-cli-bundle-loader # follow the prompts and diff
ember generate package package-name

On a new app, simply override all the files when prompted during the ember install step. For existing apps, you can do a diff of each file. Most of the changes are simple and you can see an example diff.

Now you can add routes to the main app (app/router.js) and to each package (package-name/router.js). Additionally, you will need to edit config/bundles.js with information about your package and the routes it handles.

File Structure

Current Ember Apps

The typical output of an Ember App looks something like:

  • dist/
    • index.html
    • assets/ app.js app.css vendor.js vendor.css

Output of Packages with this addon

  • dist/
    • index.html
    • assets/ boot.js boot.css package1.js package1.css package2.js package2.css vedor.js* vendor.csss

* We could also break vendor into smaller assets and define them as a dependency. More on this later.

Source File Structure

  • app/
    • index.html, router.js, resolver.js, app.js
    • routes/, controllers/, helpers/, models/, styles/, templates/, etc/
    • pods/
  • config
    • environment.js
    • bundles.js (new)
    • package-names.js (new)
  • packages (new)
    • package-name/
      • index.html, router.js, resolver.js, app.js
      • routes/, controllers/, helpers/, models/, styles/, templates/, etc/
      • pods/
    • another-package/
      • index.html, router.js, resolver.js, app.js
      • routes/, controllers/, helpers/, models/, styles/, templates/, etc/
      • pods/

The app folder is identical to a normal EmberApp, it is, in fact just a normal EmberApp. This app is responsible for the booting everything. Only put here anything that is strictly required to get to the first page(s) or whatever is more important for a first time load. For example, your login page, the navbar, the default landing page. Each app will have different requirements, but the guidance is to keep it small. Initializers also have to be here since packages won't be available when we create the app. We could, in theory, lazy load initializers, but that doesn't make a lot of sense, so I have not tested it.

There is a new top-level folder called packages, each package is like a normal EmberApp, with the exception that we don't need an index.html and we can't initializers. In the future this packages will become engines.

Tests. Currently, all tests live under the traditional folders. It is suggested to create sub-folders like tests/unit/package-name/ to provide some separation. In the future, tests for a package will move under each package folder. (Future: packages/my-package/tests and packages/my-package/app). In-repo-addons and engine have the same problem today see issues#4461.

JS

All the JS modules are namespaced with the package name. For example, the route defined in package1/routes/package1.js is defined as define('package1/routes/package1' in the output code. This is because that's simply the name of the app when building. That gives us the advantage of being explicit about the way we consume and refer in code. However, this also requires a new resolver that can lookup for routes under the packages see app/resolvers/packages.js for more info. Please be aware of this namespace, it's especially important for utils or any other code that you import, for a hypothetical date-formatter utility under package1/utils/date-formatter will be imported as import dateFormatter from 'package1/utils/date-formatter'. Please be aware of this for tests.

Vendor assets

[ ] TODO: test We can define vendor assets as dependencies. Today, ember-cli supports specifying an outputFile when calling app.import. This works with static libraries and we'll add support to do it for addons. Once you have different vendor assets, you can simply define them as dependencies in config/bundles.js. For example:

// ember-cli-build.js
var EmberAppWithPackages = require('./lib/broccoli/ember-app-with-packages');
module.exports = function (defaults) {
 var app = new EmberAppWithPackages(defaults);
 app.import('bower_components/moment/moment.js', {outputFile: 'vendor2.js'});
 app.import('bower_components/lodash/lodash.js', {outputFile: 'vendor3.js'});
 return app.toTree();
};

// /config/bundles.js
module.exports = [{
    name: 'package1',
    routeNames: ['^package1']
  }, {
    name: 'package2',
    routeNames: ['^package2']
    dependsOn: ['package1']
}];

Based on the example above, when we go to /package2, we will load vendor2.js, package1.js and vendor3.js, only if they have not been loaded before. While the lbiraries are loaded in parallel, they will be executed in the order they were definied.

Vendor assets from components

Sometimes we can't depend on routes to load the packages, but instead, components are the ones that depend on a vendor lib. In this case they're responsible of initiating the load and providing a "loading" state. The following is an example using handsontable as a reference, assumine we have a handsontable wrapper:

// components/handson-table.js
export default Ember.Component.extend({
  lazyLoader: Ember.inject.service(),
  init () {
    this._super.apply(this, arguments);
    this.get('lazyLoader').loadBundle('handsontable').then(() =>  
      this.initHandsOnTableInstance());
  },
  didInsertElement () {
    this.get('isInDom', true);
    this.initHandsOnTableInstance();
  },
  initHandsOnTableInstance () {
    if (this.get('lazyLoader').isBundleLoaded('handsontable') && this.get('isInDom')) {
      // actual initialization of the wrapper
      // Often components need to wait for didInsert, in this case we need both, didInsert/
      // and loading the assets.
    }
  }
})

On the HBS for the wrapper:

{{#if lazyLoader.loadedBundles.handsontable}}
  {{!-- markup for handsontable --}}
{{else}}
  <div class="loading">Loading<div>
{{/if}}

In config/bundles.js

module.exports = [
  {
    // Other bundles here...
  // Vendor bundles
  }, {
    name: 'handsontable',
    urls: ['/assets/handsontable.js', '/assets/handsontable.css']
  }
];

In ember-cli-build.js

app.import('/bower_components/handsontable/handsontable.full.min.js', {outputFile: 'handsontable.js')
app.import('/bower_components/handsontable/handsontable.full.min.css', {outputFile: 'handsontable.css')

Potential issues

The config/environment lives under [modulePrefix]/config/environment, which means it has the same path for every package. This is fine in most cases except if you're planning to share your packages across applications.

CSS

This demo uses ember-cli-sass, but the same technique would work with vanilla CSS (but please use a pre-processor) or Less. Is not tested with postCSS, but it isn't tested.

Deploy

To deploy the dummy app, with examples for lazy-loading, you can use ember deploy development, this will publish it to http://miguelmadero.com/ember-cli-bundle-loader/

Disclaimer

Note: there is a ton of overlap with the work being done for * ember engines. While you might find this interesting, I strongly recommend you NOT to use these techniques and instead wait for ember-engines unless you're really desperate (like I am) to get lazy loading.

Running Tests

  • npm test (Runs ember try:testall to test your addon against multiple Ember versions)
  • ember test
  • ember test --server

References

  • Ember-engines. The "official" path going forward
  • Ember-cli-lazy-load. Another approach to lazy loading. Bundle-loader and lazy-load might merge in the future and both eventually deprecated in favor of engines.