I am trying to make a library that works both in the browser as well as in node.
I have three json config files where the latter two extend tsconfig.json
tsconfig.browser.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"target": "es6",
"module": "system",
"outFile": "../dist/browser/libjs.js",
"removeComments": true,
"declaration": true
}
}
tsconfig.node.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"outDir": "../dist/node",
"removeComments": true,
"declaration": true,
"declarationDir": "../dist/node/typings"
},
"files": [
"./index"
]
}
I have this index.ts file (only included on the node build):
export { collect } from './components/collections'
export { query } from './components/query'
Then I have this in the collections.ts file:
export namespace libjs {
export function collect<T>(data: T[]) {
// Do some stuff
}
export class collection(){}
}
And this query.ts file:
export namespace libjs {
export class query<T> {
private _results: collection<T>
}
}
The issue I am having, is when I try to build to node, the index file cannot find the collect
function, and when I build to the browser the query
class cannot find the collection
class. What is the best way to code this so I can build to both node and the browser? If I remove the export
on the namespace
I can build to the browser fine, but I cannot build to node.
The way I would like to use these are as follows:
Nodejs
const libjs = require('libjs')
let c = libjs.collect([1, 123, 123, 1231, 32, 4])
Browser
<script src="/js/libjs.js"></script>
<script>
let c = libjs.collect([1, 123, 123, 1231, 32, 4])
</script>
When you compile files that have export ...
at the top level, each file is treated as a module with its own scope, and namespace libjs
in each file is distinct and separate from libjs
in every other file.
If you want to generate a single script that can be used in a browser without module loader (defining libjs
as global), you have to remove all toplevel exports, and don't set module
at all in tsconfig
:
components/collections.ts
namespace libjs {
export function collect<T>(data: T[]) {
// Do some stuff
}
export class collection<T>{}
}
components/query.ts
namespace libjs {
export class query<T> {
private _results: collection<T>
}
}
Now, you can use the same generated script in node too, if you add code that detects node environment at runtime and assigns libjs
to module.exports
:
index.ts
namespace libjs {
declare var module: any;
if (typeof module !== "undefined" && module.exports) {
module.exports = libjs;
}
}
single tsconfig.json for browser and node (note that I changed output to dist
from ../dist
)
{
"compilerOptions": {
"outFile": "./dist/libjs.js",
"removeComments": true,
"declaration": true
},
"files": [
"components/collections.ts",
"components/query.ts",
"index.ts"
]
}
You can use generated script right away in node in javascript:
test-js.js
const lib = require('./dist/libjs')
console.log(typeof lib.collect);
let c = lib.collect([1, 123, 123, 1231, 32, 4])
Unfortunately, you can't use it in node in typescript with generated libjs.d.ts
because it declares libjs
as global. For node, you need separate libjs.d.ts
that contains one additional export = libjs
statement that you have to add manually or as part of build process:
complete dist/libjs.d.ts for node
declare namespace libjs {
function collect<T>(data: T[]): void;
class collection<T> {
}
}
declare namespace libjs {
class query<T> {
private _results;
}
}
declare namespace libjs {
}
// this line needs to be added manually after compilation
export = libjs;
test-ts.ts
import libjs = require('./dist/libjs');
console.log(typeof libjs.collect);
let c = libjs.collect([1, 123, 123, 1231, 32, 4])
tsconfig.test.json
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"removeComments": true
},
"files": [
"./test-ts.ts",
"./dist/libjs.d.ts"
]
}
You can choose to go a completely different route and build a library composed of modules, but for that you have to use module loader in the browser (or build with webpack), and you probably need to read this answer explaining why namespace libjs
is totally unnecessary with modules.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With