How to convert my CommonJS module to UMD? - javascript

I'm new to UMD and AMD. I've written a JS library in Browserify and I've just came across UMD. My understanding is if I include a piece of code for every module, my module should be able to be used in CommonJs and AMD.
Here's my sample Module.
./src/main.js
import Cookie from 'js-cookie'; // from npm install js-cookie
import lib1 from './lib/lib1';
window.MyModule = function MyModule(options) {
let lib1;
function methodA() {
}
return {
methodA: methodA
};
(function init() {
lib1 = lib1();
// Some initialization code.
})();
};
module.exports = window.MyModule;
./lib/lib1.js
module.exports = (options) => {
function func1() {
}
return {
func1: func1
};
}
And this is how I pack everything using browserify
browserify src/main.js --outfile dist/main.js --debug
And when I want to use this module I just do.
<script src="//main.js"></script>
My question is, how do I convert my module to be UMD so it can be included in both CommonJS and AMD.

You can use deamdify, es6ify and deglobalify to do what you're after.
npm install deamdify es6ify deglobalify
browserify -t deamdify -t es6ify -t deglobalify main.js -o bundle.js
Source: http://dontkry.com/posts/code/browserify-and-the-universal-module-definition.html

Related

Syntax in crac-webpack-rewired to solve webpack polyfills error

I created my project in React using create-react-app, but after sometime I got to this error in the console
BREAKING CHANGE: webpack \< 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
\- add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
\- install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "path": false }
# ./node_modules/loader-utils/lib/index.js 7:25-54
# ./node_modules/file-loader/dist/index.js 11:19-42
# ./node_modules/file-loader/dist/cjs.js 3:15-33
# ./src/App.js 13:0-35
# ./src/index.js 7:0-24 11:33-36
After reading this post, I reached the conclusion I had to modify the webpack.config.js that I did not have because I created the project with create-react-app. Instead of ejecting (it is not very recommended), I used the npm package advised at the end of this thread. The problem is that there are no examples on how to use it. My question is which syntaxis do I have to follow to modify config/webpack.extend.js? This is the code at the moment:
module.exports = {
dev: (config) => {
//override webpack configuration
config.externals =[..];
return config;
},
prod: (config) => {
//override webpack configuration
config.externals =[..];
return config;
}
};
I have tried using console.log(config) but it never gets to print as errors are printed back.
You cannot use console.log() because that code is not executed from the browser, but in the Webpack "compilation" phase.
This could be a possible solution for your case.
module.exports = {
dev: (config) => {
//override webpack configuration
config.resolve.fallback = {"path": require.resolve("path-browserify")};
return config;
},
prod: (config) => {
//override webpack configuration
config.resolve.fallback = {"path": require.resolve("path-browserify")};
return config;
}
}
Notice that you have to install the package path-browserfy.
You can also set the path property to false.

node programmatically set process environment variables not available to imported modules

I'm new, hopefully this question is properly formatted and formulated. Can't wait to see your answers on this question. Let's get to it..
Context
Past weekend I was trying to implement es2015 syntax support in my create-react-app configuration files, which was straight forward. All I had to do was use babel-register and babel-preset-env to get it working. So far so good you could say, however it wasn't all good. After some hours of searching I found that process.env variables are not passed through to imported modules. The code below will demonstrate my issue.
The code
package.json
{
...
"scripts": [
"good": "NODE_ENV=development BABEL_ENV=development node -r babel-register scripts/start.js",
"bad": "node -r babel-register scripts/start.js"
],
"devDependencies": {
"babel-core": "^6.26.3",
"babel-preset-env": "^1.7.0",
"babel-register": "^6.26.0"
}
...
}
.babelrc
{
"presets": [ "env" ]
}
scripts/start.js
'use strict'
process.env.NODE_ENV = 'development';
process.env.BABEL_ENV = 'development';
// Always works
const a = require('../src/a');
// Only when environment variables are passed in via the CLI
import b from '../src/b';
console.log('Bye bye..');
src/a.js
'use strict'
console.log('Module A:', process.env.NODE_ENV);
const a = { name: "Module A" };
export default a;
src/b.js
'use strict'
console.log('Module B:', process.env.NODE_ENV);
const b = { name: "Module B" };
export default b;
Running the code
Below you will see the output of both npm scripts:
npm run good
# Outputs:
Module B: development
Module A: development
Bye bye..
npm run bad
# Outputs:
Module B: undefined
Module A: development
Bye bye..
My question(s)
Why aren't programmatically set environment variables passed through to imported modules?
Can this be fixed while keeping es2015 syntax? (e.g. using a babel plugin?)
More info
Just moving my process.env.NODE_PATH over to the CLI won't work, create-react-app programmatically sets environment variables at multiple places in their configuration/script files. I have listed a few links below, pointing to the create-react-app repo and some of the files that are giving me troubles.
create-react-app A link to the create-react-app repo.
scripts/start.js This script sets both process.env.NODE_ENV and process.env.BABEL_ENV.
config/env.js This config file sets process.env.NODE_PATH.
Note(s)
From my current understanding, create-react-app has little to none to do with the problem I'm having. I'm interested in why programmatically set environment variables are not passed through to imported modules.
My setup
OS: Ubuntu 16.04
Node: v8.11.2
Npm: 6.3
ES6 imports are hoisted. This means they will run before the rest of the code regardless of where they are in the source. The result is that b.js will run before you have set process.env.NODE_ENV = 'development'.
Babel's output will be consistent with this and will simulate hoisted imports by moving b's require statement to the top of the file. Babel will create a start file that looks like:
'use strict';
var _b = require('../src/b');
var _b2 = _interopRequireDefault(_b);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
process.env.NODE_ENV = 'development';
process.env.BABEL_ENV = 'development';
// Always works
var a = require('../src/a');
// Only when environment variables are passed in via the CLI
It should be clear looking at that why this isn't working.
[ As a side note, many people strongly recommend that you don't set NODE_ENV at runtime ]
Answer to my second question
Thanks to the insights provided by #Mark Meyer I was able to get it to work.
scripts/start.js
'use strict'
import '../config/devEnv';
config/devEnv.js
'use strict'
process.env.NODE_ENV = 'development';
process.env.BABEL_ENV = 'development';
Now the environment variable setters are hoisted as well, making them available to all imported modules.

JavaScript export / import doesn't work

it might be a silly question but I can't fix it anyway. I have a JavaScript file with various functions I'd like to export.
export function AddNumbers(...numbers)
{
let value = 0;
for(var i = 0;i < numbers.length;i++)
{
value += numbers[i];
}
return value;
}
When I call this method (using mocha) I get an error message "export function AddNumbers(...numbers) Unexpected token export". The project is build as ES6. Does anybody know what I'm doing wrong?
Best regards,
Torsten
You need to use module.exports as NodeJS uses CommonJS Module syntax which requires to use module.exports and not just export which is defined by ES6 module syntax. So, make sure CommonJS is also configured properly in your project.
Another solution is to use Babel. Install it with
npm install babel-core --save-dev
npm install babel-preset-es2015 --save-dev
Create in root directory a file .babelrc with following content
{
"preset" : ["es2015"]
}
and finally change the script in package.json to run into:
"scripts": {
"test": "mocha Tests --require babel-core/register"
}
and now export / import works.

Gulp and Babel: Error: Cannot find module

I have a project that I've set up using gulp and babel. Everything is working fine, except when I create a module and import it once it's converted from ES6 to ES6 it doesn't work. I get an error:
Error: Cannot find module 'hello.js'
at Function.Module._resolveFilename (module.js:440:15)
at Function.Module._load (module.js:388:25)
at Module.require (module.js:468:17)
Here's my gulpfile.babel.js:
import gulp from "gulp";
import babel from "gulp-babel"
import concat from "gulp-concat"
const dirs = {
src: "src",
dest: "build"
}
gulp.task("build", () => {
return gulp.src(dirs.src + "/**/*.js")
.pipe(babel())
.pipe(concat("build.js"))
.pipe(gulp.dest(dirs.dest))
});
gulp.task("default", ["build"]);
During build everything is concatenated into one file. Under src/ I have:
app.js
hellojs
app.js
import hello from './hello.js'
console.log(hello());
hello.js
export default () => {
return 'Hey from hello.js';
};
And I run like so:
npm start
Which basically calls node ./build/build.js.
I think it's because it's concatenating the ES6 into ES5 and the bundle.js still contains the require for hello.js. It wont find it though because its concatenated. Is that possible?
It is incorrect to concatenate two module files and expect the program to work properly, even when transpiled to ES5. Bundling involves more than concatenating the scripts: each module needs a closure for registering exports and resolving the contents of other modules.
You must instead use a bundling tool such as Browserify, Webpack or Rollup. Here's how one would bundle with Browserify (which in this case, it is easier to rely on the Babelify transform rather than gulp-babel):
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var babelify = require('babelify');
gulp.task('browserify', function() {
return browserify({
entries: './src/app.js'
})
.transform(babelify)
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest('./build/'));
});

Unit testing using Jasmine and TypeScript

I am trying to get a unit test written in Typescript using Jasmine to compile. With the following in my unit-test file, Resharper prompts me with a link to import types from jasmine.d.ts.
/// <reference path="sut.ts" />
/// <reference path="../../../scripts/typings/jasmine/jasmine.d.ts" />
describe("Person FullName", function () {
var person;
BeforeEach(function () {
person = new Person();
person.setFirstName("Joe");
person.setLastName("Smith");
});
It("should concatenate first and last names", function () {
Expect(person.getFullName()).toBe("Joe, Smith");
});
});
So I click on the link and end up with the following (actually resharper only prefixed the describe function with "Jasmine.", so I manually prefixed the other Jasmine calls):
/// <reference path="sut.ts" />
/// <reference path="../../../scripts/typings/jasmine/jasmine.d.ts" />
import Jasmine = require("../../../Scripts/typings/jasmine/jasmine");
Jasmine.describe("Person FullName", function () {
var person;
Jasmine.BeforeEach(function () {
person = new Person();
person.setFirstName("Joe");
person.setLastName("Smith");
});
Jasmine.It("should concatenate first and last names", function () {
Jasmine.Expect(person.getFullName()).toBe("Joe, Smith");
});
});
However the import statement has a red squiggly line with error message "Unable to resolve external module ../../../scripts/typings/jasmine/jasmine. Module cannot be aliased to a non-module type"
Any idea what is causing this error? I've checked that the "Module System" option is set to AMD in my project build settings. I've also checked that the jasmine module is defined in jasmine.d.ts. I downloaded this file from DefinitelyTyped site.
declare module jasmine {
...
}
Here's (in my opinion) the best way to test a ts-node app as of 2018:
npm install --save-dev typescript jasmine #types/jasmine ts-node
In package.json:
{
"scripts": {
"test": "ts-node node_modules/jasmine/bin/jasmine"
}
}
In jasmine.json change file pattern to *.ts
"spec_files": ["**/*[sS]pec.ts"],
In your spec files:
import "jasmine";
import something from "../src/something";
describe("something", () => {
it("should work", () => {
expect(something.works()).toBe(true);
});
});
To run the tests:
npm test
This will use the locally installed versions of ts-node and jasmine. This is better than using globally installed versions, because with local versions, you can be sure that everyone is using the same version.
Note: if you have a web app instead of a node app, you should probably run your tests using Karma instead of the Jasmine CLI.
Put this at the top of your typescript spec file:
/// <reference path="../../node_modules/#types/jasmine/index.d.ts" />
let Jasmine = require('jasmine');
You must install the following Jasmine modules for that to work:
$ npm install jasmine-core jasmine #types/jasmine #ert78gb/jasmine-ts --save-dev
Once you do that, the IDE (such as WebStorm) will recognize Jasmine and its functions such as describe(), it(), and expect().. So you don't need to prefix them with "Jasmine." Also, you can run your spec files from the command line using the jasmine-ts module. Install these command line tools globally:
$ npm install -g jasmine #ert78gb/jasmine-ts
Then configure the "jasmine" command line module so that Jasmine can find its configuration file. Then you should be able to run jasmine-ts and your spec file should run fine from the command line:
./node_modules/.bin/jasmine-ts src/something.spec.ts
.. and, you can configure your IDE to run it like that as well, and debug runs that way should also work (works for me).
Writing your tests this way, you can run a Jasmine test spec on the server side without Karma, or run it in a web browser using Karma. Same typescript code.
If you have issues with imports, use tsconfig-paths
npm i ts-node tsconfig-paths types/jasmine jasmine --save-dev
Run typescript-enabled jasmine:
ts-node -r tsconfig-paths/register node_modules/jasmine/bin/jasmine.js
Ensure that your jasmine will search .ts files:
"spec_files": [
"**/*[sS]pec.ts"
],
"helpers": [
"helpers/**/*.ts"
],
To test your scripts you may also need polyfills if you use them in your project. Create a helper file with required imports, like helpers/global/polifill.ts
import 'core-js';
For me I did the following:
Install Typings
npm install typings --global
Then add the typings in for jasmine
typings install dt~jasmine --save --global
You could try a side-effect only import which brings in the #types/jasmine declaration and places the jasmine functions into the global scope so you don't need to prefix each call with jasmine. allowing a quick port from existing unit tests and still plays nice with webpack.
// tslint:disable-next-line:no-import-side-effect
import "jasmine";
describe("My Unit Test", () => { /* ... */ } );
Of course you still need to install jasmine and the typings:
$ npm i jasmine #types/jasmine --save-dev
But no need for specialized jasmine loaders for ts or node. Just run jasmine against the compiled js files:
$ node ./node_modules/jasmine/bin/jasmine.js --config=test/support/jasmine.json
Assuming your typescript files are within a "test" subdirectory compiling to bin/test and you have a test/support/jasmine.json with something like this:
{
"spec_dir": "bin/test",
"spec_files": [
"**/*[sS]pec.js"
],
"stopSpecOnExpectationFailure": false,
"random": false
}
P.S. all of the above works on Windows too
Include this to your jasmine html file,...
<script type="text/javascript" src="jasmine/lib/jasmine-2.0.0/jasmine.js"></script>
...or install the npm jasmine package:
npm install --save-dev jasmine
when you are using the second way (jasmine as module) you have to import it:
var jasmine = require('jasmine');
or
import jasmine from 'jasmine';
then change the other code:
jasmine.describe("Person FullName", function () {
var person;
jasmine.beforeEach(function () {
person = new Person();
person.setFirstName("Joe");
person.setLastName("Smith");
});
jasmine.it("should concatenate first and last names", function () {
jasmine.expect(person.getFullName()).toBe("Joe, Smith");
});
});
Personally i would prefer the first way without using the jasmine npm module. (I didn't test the module yet)
You didn't ask for this, but for bonus points: once you get AJ's answer up and running (using ts-node to invoke the Jasmine startup script), you can add a new task:
"scripts": {
"watch": "ts-node-dev --respawn -- ./node_modules/jasmine/bin/jasmine src/**.spec.ts"
}
Of course, you can pass your specs or any other arguments using Jasmine's config file instead, if you like. Now, Jasmine will run all your specs once, then ts-node-dev will sit in the background waiting for your tests or anything they might have require'd to change, at which point jasmine will be run again. I haven't worked out a way to only run the tests that have changed (or tests whose imports have changed) yet -- as far as I can tell, that's not supported anyway;
My folder structure
Spec folder is on the root of project
spec
\- dist // compiled tests
\- helpers // files modified testing env
\- ts-console.ts // pretty prints of results
\- support
\- jasmine.json
\- YourTestHere.spec.ts
\- tsconfig.json // tsconfig for your tests
Files content
ts-console.ts
const TSConsoleReporter = require("jasmine-console-reporter");
jasmine.getEnv().clearReporters();
jasmine.getEnv().addReporter(new TSConsoleReporter());
jasmine.json
{
"spec_dir": "spec/dist",
"spec_files": [
"**/*[sS]pec.js"
],
"helpers": [
"spec/helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}
With extra script in package.json
"scripts": {
"test": "rm -rf ./spec/dist && tsc -p ./spec && jasmine"
}
and add line "/spec/dist" to .gitignore
Run your tests!
Run your tests with npm test.
How does it work?
Directory for tests is cleaned.
Tests are compiled to spec/dist folder to JS.
Tests are runned from this location.
I hope it will help you. Good coding.

Categories