我正在尝试创建一个既可以在浏览器中又可以在节点中工作的库。
我有三个 json 配置文件,后两个扩展了tsconfig.json
- tsconfig.json(只包含构建的文件)
- tsconfig.browser.json
- tsconfig.node.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"
]
}
我有这个index.ts文件(仅包含在节点构建中):
export { collect } from './components/collections'
export { query } from './components/query'
然后我在collections.ts文件中有这个:
export namespace libjs {
export function collect<T>(data: T[]) {
// Do some stuff
}
export class collection(){}
}
而这个查询.ts文件:
export namespace libjs {
export class query<T> {
private _results: collection<T>
}
}
我遇到的问题是,当我尝试构建到节点时,索引文件找不到collect
函数,当我构建到浏览器时,query
类找不到collection
类。编写代码以便我可以同时构建节点和浏览器的最佳方法是什么?如果我删除namespace
上的export
,我可以很好地构建到浏览器,但我不能构建到节点。
我想使用这些方法如下:
Nodejs
const libjs = require('libjs')
let c = libjs.collect([1, 123, 123, 1231, 32, 4])
浏览器
<script src="/js/libjs.js"></script>
<script>
let c = libjs.collect([1, 123, 123, 1231, 32, 4])
</script>
编译具有顶层export ...
的文件时,每个文件都被视为具有自己作用域的模块,并且每个文件中的namespace libjs
是不同的,并且与每个其他文件中的libjs
分开。
如果要生成一个可以在没有模块加载器的浏览器中使用的单个脚本(将libjs
定义为全局),则必须删除所有顶级导出,并且根本不tsconfig
中设置module
:
组件/集合.ts
namespace libjs {
export function collect<T>(data: T[]) {
// Do some stuff
}
export class collection<T>{}
}
组件/查询.ts
namespace libjs {
export class query<T> {
private _results: collection<T>
}
}
现在,您也可以在节点中使用相同的生成脚本,如果您添加在运行时检测节点环境并将libjs
分配给module.exports
的代码:
索引.ts
namespace libjs {
declare var module: any;
if (typeof module !== "undefined" && module.exports) {
module.exports = libjs;
}
}
浏览器和节点的单个tsconfig.json(请注意,我将输出从../dist
更改为dist
)
{
"compilerOptions": {
"outFile": "./dist/libjs.js",
"removeComments": true,
"declaration": true
},
"files": [
"components/collections.ts",
"components/query.ts",
"index.ts"
]
}
您可以在javascript的node中立即使用生成的脚本:
测试-js.js
const lib = require('./dist/libjs')
console.log(typeof lib.collect);
let c = lib.collect([1, 123, 123, 1231, 32, 4])
不幸的是,您不能在带有生成libjs.d.ts
的打字稿的节点中使用它,因为它将libjs
声明为全局。对于 node,您需要单独的libjs.d.ts
,其中包含一个附加的export = libjs
语句,您必须手动添加该语句或作为构建过程的一部分添加该语句:
Completedist/libjs.d.tsfor 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;
测试-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"
]
}
您可以选择走完全不同的路线并构建一个由模块组成的库,但为此您必须在浏览器中使用模块加载器(或使用 webpack 构建),并且您可能需要阅读此答案解释为什么模块完全不需要namespace libjs
。