I'm trying to use d3-arrays in a project. The module itself includes both original ES6 modules and a UMD build. I would expect to be able to add this directly as a dependency to my ember-cli project and have it available, but that is not the case.
I've seen suggestions that are over a year old saying to use ember-browserify, and others suggesting making a shim, but AFAIK this would really only be ideal if it were a bower dependency, and bower seems to be on the way out.
For the sake of correctness, how can I just import this module as though it were part of my project and use it as import {mean} from 'd3-arrays without needing to convert it to another package format first?
I've tried making a shim which just exports the imported UMD code:
// index.js
var d3ArraysExports = require('d3-arrays');
d3ArraysExports.name = 'd3-arrays-shim';
module.exports = d3ArraysExports;
Ember finds this module just fine, but it never gets added to the require entries list.
If there is some design decision in Ember CLI as to why this doesn't work, please point me to it.
I figured out an elegant solution to this:
I created a shim library which imports the ES6 sources from d3-arrays and makes them available to broccoli. Here's an example:
var path = require('path');
module.exports = {
name: 'd3-arrays',
treeForAddon: function() {
var packagePath = path.join(this.project.addonPackages['ember-d3-arrays-shim'].path, 'node_modules', 'd3-arrays');
var d3ArraysTree = this.treeGenerator(packagePath);
return this._super.treeForAddon.call(this, d3ArraysTree);
}
};
Related
This is just something I thought today and I didn't see a lot of information so I'm going to share this weird cases and how I personally solved them (if there's a better way please comment, but meanwhile this might help others ^^)
In a regular module, you would do something like this to export your function/library/object/data:
// regular NodeJS way:
module.exports = data;
// ES6 way
// (will get transpiled to the regular way using the module variable by webpack)
export data;
default export data;
When compiling the library usually babel or tsc are used, but if for any reason you want not only to compile (transpile) your library but also pack it using webpack, you will encounter this case.
As you know, in a webpack bundle the module variable is local to the bundle (every module/file gets wrapped with a function where module is a parameter = local variable), so nothing really gets exported outside the bundle, is just nicely managed by webpack.
That means that you can't also access the contents using the regular require/import methods.
In some case you might find necessary to export outside webpack. (i.e. you are trying to build a library using webpack and you want it to be accessible by other people). This basically means you need to access the original module variable, but webpack doesn't expose it like it happened with __non_webpack_require__.
See also: Importing runtime modules from outside webpack bundle
The solution is to create our own __non_webpack_module__ (as webpack does with __non_webpack_require__.
How I did it is using webpack.BannerPlugin to inject some code outside the bundle. This code is prepended to the build after the minification is done, so it's preserved safely.
In your webpack.config.js:
plugins: [
new BannerPlugin({
raw: true,
banner: `const __non_webpack_module__ = module;`,
}),
]
And again, if you are using TypeScript, in global.d.ts:
declare const __non_webpack_module__: NodeModule;
And now, you can do something like this in your code:
__non_webpack_module__.exports = /* your class/function/data/whatever */
This will allow to import it as usual from other files
Tip: You might want to look at BannerPlugin to check other options, like include or exclude so this variable is only generated on the desired files, etc.
I pulled down a library that contains Typescript and it's javascript version.
When declaring import Library from "#scope/library", my app can only access the typescript, even when I add the js extension.
How can I import the js file instead? I get errors on types.
You can override the package's default entry point:
const myObj = require("../node_modules/library/path/to/dist/file.js");
like you would reference any other file in your project structure.
Another way to get Typescript to shut up about typings is to require instead of import:
const myObj = require("#scope/library");
This way you don't have to worry about resolving exactly the right js file, and can still benefit from the Node module resolution mechanism.
If you're getting issues with the types, you either need to install the types for the package
npm install -D #types/<package>
yarn add -D #types/<package>
Or you can ignore types by requiring instead of importing:
const package = require('package')
I'm trying to include bitcore-lib partially into my webpage using tree-shaking that rollup provides out of the box and rollup-plugin-commonjs to load Node.js module.
To better illustrate the problem I make a demo project that available on the github
You can have a look at bundle.js. If I define a module in the following way:
const useful = "3";
const useless = "4";
export {usefull, useless}
Tree shaking works correctly - the final bundle includes only useful dependency.
But if I define a module in the way it defined in bitcore-lib (node-lib.js) in demo project:
module.exports = {
useful: "1",
useless: "2"
};
In that case, the final bundle includes the whole module.
I've expected that useless: 2 dependency shouldn't be included because of tree-shaking. My index.js is here:
import {usefull as usefull1} from "./my-node-lib"
import {usefull as usefull2} from "./my-es-lib"
console.log(`hi! ${usefull1} ${usefull2}`);
My rollup.config.js is available here
Is it a problem of module definition or rollup config?
Tree shaking works only for ES6 modules. At least it's true for Webpack and I suppose for rollup as well. Your first definition is ES6, second is commonjs.
Therefore if a library is not compiled/transpiled to ES6 modules tree shaking will not work.
Another feature which will not work is module concatenation.
Depending on the library you can try to recompile it.
Im working with project that is written in Node CommonJS modules. My point was to make this project accessible from a browser. I decided to use Rollup.js, so when the bundle is created you can include script in browser and use functions from library (thanks to iife format). I needed to install plugins for Rollup to convert CommonJS modules into ES6 modules, so browser can understand it.
Everything is fine, except that this project uses bson library from npm. This bson library is required in one of my modules which uses some of it's functions. After I create a bundle and include it into index.html an error appears in console which says: "require is not defined". When I look inside the created bundle there are some requires.
var Map = require('./map'),
Long = require('./long'),
Double = require('./double'),
Timestamp = require('./timestamp'),
ObjectID = require('./objectid'),
BSONRegExp = require('./regexp'),
Symbol$1 = require('./symbol'),
Int32 = require('./int_32'),
Code = require('./code'),
Decimal128 = require('./decimal128'),
MinKey = require('./min_key'),
MaxKey = require('./max_key'),
DBRef = require('./db_ref'),
Binary = require('./binary');
I have created simple code in Plunker to illustrate you my config and simplified structure.
https://plnkr.co/edit/YuiVJxhwhjUQ0Flw0Mg3?p=preview
In this plunker there are two simple modules, which one requires bson library, and second requires this first module. There is also Rollup config file, where I use plugins (if there is no globals plugin there is an error: Uncaught ReferenceError: Buffer is not defined).
I'm really confused. Am I misunderstanding something? Why isn't it converted into ES6 modules just like other of my code?
Here is link to bson library: https://www.npmjs.com/package/bson
Let's see:
In order for rollup to bundle your application you need to transform commonjs modules into es modules first, this is done with rollup-plugin-commonjs (https://github.com/rollup/rollup-plugin-commonjs) which you are already using, maybe you could explicitly set in the options to include the bson package. I usually use it like this, just in case:
commonjs({
include: ['node_modules/**']
})
If some library uses node global modules you will need to include them in the browser, thats why you need rollup-plugin-node-globals.
Finally, if you take a look at bson github repository there is a folder called browser_build, which contains the UMD definition of the library, so if you require 'bson/browser_build' instead of 'bson' it should work, and you may not need to use globals plugin.
Take a look at js module formats (cjs, umd, iife, es, amd) it is worth it.
Does an NPM package need to be modified to be compatible with Angular 2 (eg. add in typings, make directives for them) or will any existing package work? If they're not all compatible, how do I know what is and what is not compatible?
For example, say I want to import this package (https://github.com/pvorb/node-md5). I'm aware there is a ts-md5 package for angular 2 to do md5 - I'm just using this package as an example.
How would I get this to work?
I've installed it using
npm install md5 --save
npm install #types/md5 --save
but I can't seem to be import it
import {md5} from 'md5';
I get this error message after I try to run
Module
'"/Users/xxx/Source/tempProjects/ngUnderscore/node_modules/#types/md5/index"'
resolves to a non-module entity and cannot be imported using this
construct.
I'm not sure what this message means. Does it mean that in its current state, the package is not compatible or am I trying to use the package incorrectly?
I managed to make it work using declare and require instead of import (import won't work for this non-module lib)
declare const require: any;
const md5 = require('md5');
If you don't want to workaround import like this, you can try using Typescript MD5 implementation called ts-md5. Then import like the one below should work. (referenced from this question)
import { Md5 } from 'ts-md5/dist/md5'
If there is no TS implementation, you can search for the types in DefinitelyTyped and then simply install package by npm i --save-dev #types/{package-name}
If the library works on your project depends on many factors: your Angular version, your TypeScript version, etc.
This said, is obvious that we should check the library's documentation and see which dependencies has and its versions, and of course the library should be the Angular 2 version of itself. Following your example, there are several md5 libraries but if you use TypeScript you should maybe consider this one: https://www.npmjs.com/package/ts-md5
If we have all that covered but still there is something not working because of some type of incompatibility, like for example:
My version of angular is 2, the library I just installed works with Angular 4. I have code full of <template>, library uses <ng-template>... What can I do?
You can fork the library in github and modify whatever you need to asure it is compatible with your project. Steps:
Fork library repository and modify what you need
Subscribe to main library repository in order to be updated with changes
In packages.json use your forked version of the library, for example:
"ng2-datetime": "https://github.com/my_user/ng2-datetime/tarball/my_forked_version_name",
If you think that your modifications could suit other users... Make a Pull request! :D
This is more of a TypeScript question since md5 is not an Angular package.
The key is to get the import correct to be equivalent to a require() function.
import * as md5 from "md5";
Use directly in TypeScript file:
console.log(md5('message'));
To use this on the template, preferably it should be used in method implementation, but can also be exposed directly. Add it as a property on the Component:
md5: any = md5;
Then on the template:
{{md5('message')}}
They usually say which Angular it is meant for, sometimes you have one package for both or for each.
If you are using an Angular 1x package and there is no Angular2 compatibility, then you can just use ngUpgrade.
But If you are using a common plugin then there must be an angular 2 solution.
If you want the other way around then you're probably going the wrong way.
The issue you experienced is not related to Angular. It is an existing issue on TypeScript when importing CommonJS packages.
The rule of thumb (my recommendation) is to stay away from using the interop feature (i.e. import * as X from 'x') when importing CommonJS and use the "old" syntax instead (i.e. import X = require('x')).
When CommonJS is exporting a function in the form of module.exports = function() {...}, import * as X from 'x' does not work.
This includes the case when the package is exporting an ES6 class but transpiling to ES5 with Babel.
You may see some packages do work with this syntax, but that is because the typings have a hack in it:
declare module 'x' {
function foo(): void
namespace foo {} // <-- the hack
exports = foo
}
There are other reasons the interop is not a good idea, including the syntax between TypeScript (import * X from 'x') and Babel (import X from 'x') does not agree.
You can learn more about it here and follow the links:
https://github.com/unional/typescript-guidelines/blob/master/pages/default/modules.md#import-keyword
UPDATE: with TypeScript#2.7 released, you can now do import EditableElement from 'Transformer' directly.
Turn on esModuleInterop in your tsconfig.json