lundi 23 avril 2018

How to import a custom library to Ember via npm

Despite what seems like a plethora of blog posts about it, I am still struggling to perfect using a custom library as a dependency for my ember application through npm.

I have written a WebGL library and currently have it importing into my Ember app by installing it via npm from a private repository. This currently works and is in production but the workflow is somewhat clunky. The library is written using NodeJS modules (require -> export.modules). Currently I am just using babel on my src files which results in a build folder with the ES5 versions of the files still separated.

I then have an index file that looks like this:

var Helper = require('./com/XXXX/utils/Helper');
module.exports = {
  Module1: require('./com/XXXX/media/Module1'),
  Module2: require('./com/XXXX/media/Module2'),
  Module3: require("./com/XXXX/media/Module3"),
  Module4: require('./Module4'),
  Module5: require('./com/XXXX/media/Module5'),
  Module6: Helper.Module6,
  Module7: Helper.Module7
};

Using npm I can install this build directory into my Ember app and import the modules I need using the following syntax:

import webglRenderLibrary from 'npm:webglRenderLibrary';
const { Module5 } = webglRenderLibrary;

Where Module5 is a class in the library exported like this:

class Module5 {
  //Magic rendering code
}
module.exports = Module5;

I didn't have to install any other plugins or import the library-files to the ember vendor file as so many blog posts say you have to in order to get this to work. I don't really understand why this method works but it does.

¯\_(ツ)_/¯

I've never really liked this setup/workflow though (and not knowing why it works) so I'm trying to improve it but the many knowledge gaps I have in Ember, JS modules and so on are making it difficult.

The first thing I wanted to do was move the library over to ES6 modules (import -> export). From what I understand ES6 modules are leaner and the future so I would rather use them for my library. Changing all the source code was a little labour intensive but it works nicely and allowed me to create a nice workflow for my library development.

Module5 now looks like this:

export default class Module5 {
  //Magic rendering code
}

In the package.json of the library I have a number of npm scripts now calling watchify so I can test my modules in demo files individually.

{
  "name": "webglRenderLibrary",
  "main": "dist/js/app.js",
  "version": "2.0.5",
  "author": "JibJab Media",
  "description": "WebGL Render Library",
  "repository": "private.git",
  "scripts": {
    "watch-sass": "sass --watch src/scss/app.scss:demo/css/app.css",
    "watch-js": "watchify src/js/index.js -t babelify -o dist/js/app.js -dv",
    "watch-module1": "watchify src/js/demos/Module1Demo.js -t babelify -o demo/js/Module1Demo.js -dv",
    "watch-module2": "watchify src/js/demos/Module2Demo.js -t babelify -o demo/js/Module2Demo.js -dv",
    "watch-module3": "watchify src/js/demos/Module3Demo.js -t babelify -o demo/js/Module3Demo.js -dv",
    "watch-module4": "watchify src/js/demos/Module4Demo.js -t babelify -o demo/js/Module4Demo.js -dv",
    "watch-module5": "watchify src/js/demos/Module5Demo.js -t babelify -o demo/js/Module5Demo.js -dv",
    "watch-module6": "watchify src/js/demos/Module6Demo.js -t babelify -o demo/js/Module6Demo.js -dv",
    "watch": "npm run watch-sass & npm run watch-module1 & npm run watch-module2 & npm run watch-module3 & npm run watch-module5 & npm run watch-module5 & npm run watch-module6",
    "build-sass": "sass src/scss/app.scss:dist/css/app.css --style compressed",
    "build-js": "browserify src/js/index.js -t [ babelify --presets [ \"env\" ] ] | uglifyjs -mc > dist/js/app.js",
    "build": "npm run build-js & npm run build-sass",
    "test": "mocha --require babel-core/register",
    "test-coverage": "nyc mocha --require babel-core/register"
  },
  "browserify": {
    "transform": [
      "babelify"
    ]
  },
  "dependencies": {
    "babel-preset-env": "1.6.1",
    "babelify": "^7.2.0",
    "opentype.js": "0.8.0"
  },
  "devDependencies": {
    "babel-cli": "*",
    "mocha": "5.0.5",
    "nyc": "11.6.0",
    "watchify": "3.11.0"
  },
  "bugs": {
    "url": "private/issues"
  },
  "homepage": "private#readme",
  "private": true
}

I bring this all up to point out that my conversion to ES6 modules has gone smoothly. My test Js files for the individual modules compile and babelify nicely; I can run them in test HTML files and the WebGL renders correctly.

Now, here is where my knowledge gets kind of fuzzy.

The library contains 7 modules I want to expose so that the Ember App can use them. So, I have an index.js file in the library which simply imports each of the modules then exports them. (Please let me know if this is not the way to do this)

import Module4              from './Module4';
import Module1              from './com/XXXX/media/Module1';
import Module2              from './com/XXXX/media/Module2';
import Module3              from './com/XXXX/media/Module3';
import Module5              from './com/XXXX/media/Module5';
import { Module6, Module7 } from "./com/XXXX/utils/Helper";

export { Module1, Module2, Module3, Module4, Module5, Module6, Module7 };

From my understanding, this allows me to have browserify/babelify transpile my ES6 module library into something that Ember can consume. In the libraries package.json I have a 'build' script which runs browserify, babel and uglify to mash my whole library into one minified file in dist/js/app.js which is the entry point under main in the libraries package.json.

I was thinking that this should really be the same code which is currently working in my Ember app. The only difference should be that it has been put into a single file by browseridy and minified with Uglify (Please correct me if I'm wrong, which I think I am). I thought I wouldn't have to make any changes to my Ember app but now I'm getting:

Uncaught TypeError: Module5 is not a constructor

while importing it the way I did before:

import webglRenderLibrary from 'npm:webglRenderLibrary';
const { Module5 } = webglRenderLibrary;

All of this being said leads me to several questions:

  1. Why does my original strategy of simply babeling my src code and importing it as described work while so many blog posts talk about using brocolli and importing it to the vendor file. Example
  2. If the original strategy worked, why didn't my change to the new library structure work as well. I'm guessing it's because browserify changes something about the NodeJS module exports but my knowledge here is fuzzy.
  3. For creating a library with multiple modules to export, is it correct/necessary to export them from a single index file as I have done? If not then how do other libraries expose their modules to Ember (example lodash)
  4. I've seen a number of plugins/packages which claim to allow the import of ES6 modules into ember. For example ember-cli-es6-transform. I could not get this to work with my library. Has anyone had success with something like this for a setup similar to mine? How did you make it work?
  5. If you were creating a custom library to import to an ember app, how would you do it?



Aucun commentaire:

Enregistrer un commentaire