I created a TypeScript module which requires a third-party library:
import Dexie from "dexie";
namespace storage {
...
}
When I compile my TypeScript file, I get the following JavaScript output:
"use strict";
var dexie_1 = require("dexie");
var storage;
(function (storage) {
...
})(storage || (storage = {}));
I am okay with this when using the output in a Node.js environment. But for the usage in a browser I want to replace the var dexie_1 = require("dexie");
with an object from window
like: var dexie_1 = window.Dexie;
.
Can I replace the require
statement in my compiled JS with an object from window
(global namespace)? Is there a Gulp plugin or sth. similar around?
My tsconfig.json is this:
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"target": "es5"
},
"exclude": [
"node_modules",
"typings/browser",
"typings/browser.d.ts"
]
}
Webpack can map require("dexie");
to window.Dexie
.
All you have to do is to declare the following in your webpack.config.js:
module.exports = {
externals: {
'dexie': 'Dexie'
}
};
Here is a Minimal Working Example for the sake of completeness:
Directory Layout:
bower install
)gulp default
)npm install
)typings install
)src/storage.ts
/// <reference path="../typings/index.d.ts" />
import Dexie from "dexie";
namespace storage {
export function setupDatabase():void {
let db = new Dexie('MyDatabase');
db.version(1).stores({
friends: 'name, age'
});
db.open().then(function () {
console.log('Initialized database: ' + db.name);
});
}
}
module.exports = storage;
bower.json
{
"name": "storage",
"main": "dist/webpacked.js",
"private": true,
"dependencies": {
"dexie": "^1.4.1"
}
}
gulpfile.js
var gulp = require('gulp');
var rename = require('gulp-rename');
var runSequence = require('run-sequence');
var ts = require('gulp-typescript');
var tsProject = ts.createProject('tsconfig.json');
var webpack = require('webpack-stream');
gulp.task('build', function () {
return gulp.src('src/**/*.ts')
.pipe(ts(tsProject))
.pipe(gulp.dest('dist'));
});
gulp.task('webpack', function () {
return gulp.src('dist/index.js')
.pipe(webpack(require('./webpack.config.js')))
.pipe(rename('webpacked.js'))
.pipe(gulp.dest('dist'));
});
gulp.task('default', function (done) {
runSequence('build', 'webpack', done);
});
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Demo</title>
<script src="bower_components/dexie/dist/dexie.js"></script>
<script src="dist/webpacked.js"></script>
</head>
<body>
<script>
document.addEventListener("DOMContentLoaded", function() {
storage.setupDatabase();
}, false);
</script>
</body>
</html>
index.js
window.storage = require('./dist/storage');
package.json
{
"name": "storage",
"private": true,
"devDependencies": {
"dexie": "^1.4.1",
"gulp": "^3.9.1",
"gulp-rename": "^1.2.2",
"gulp-typescript": "^2.13.6",
"run-sequence": "^1.2.2",
"webpack-stream": "^3.2.0"
}
}
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"target": "es5"
},
"exclude": [
"node_modules",
"typings/browser",
"typings/browser.d.ts"
]
}
typings.json
{
"globalDependencies": {
"node": "registry:dt/node#6.0.0+20160709114037"
}
}
Note: The node
entry comes from typings install dt~node --global --save
and is needed for TypeScript to resolve module
in the module.exports
statement.
webpack.config.js
module.exports = {
externals: {
'dexie': 'Dexie'
}
};
Approach:
The TypeScript code imports Dexie
and declares itself using the namespace storage
. To follow the commonjs
way of dependency management (which is declared in tsconfig.json
), the TypeScript code needs to export the storage
namespace as a module with: module.exports = storage
.
Because TypeScript doesn't know the module
object, we need to get it's definition. The module
definition is part of the type definition for node
, which we get from the DefinitelyTyped repository with the typings tool using typings install dt~node --global --save
. To link node's retrieved type definition in TypeScript, we need to declare /// <reference path="../typings/index.d.ts" />
.
After we compile our TypeScript code (using gulp build
), we need to declare an entry point to make our code accessible. This is done in index.js
. A successful build outputs our dist/storage.js
file, which is referenced in index.js
.
When having the build in place, we can webpack our code (to bundle it for a HTML5 browser). Our webpack.config.js
maps the "require" name of our dependency (dexie
) to an object from the global namespace (window.Dexie
). This guarantees us, that Dexie is not part of our compiled code (dist/webpacked.js
).
Once we have the webpacked.js
, we can use it in a browser. But we have to make sure that all external dependencies are referenced in our HTML page (that's why Dexie is also declared as a frontend dependency using Bower).