我应该排除模块化 Typescript 项目的依赖项吗?
Should I exclude dependencies for modular Typescript project?
我目前正在尝试为 Typescript 项目创建一个 NPM 包(我正在使用 gulp 和 browserify 进行构建)。问题是包消费者目前没有使用模块,所以我正在尝试使用 Browserify 打包一个独立的包。
首先,捆绑所有依赖项会不会是个问题?据我所知,捆绑的 js 文件只是将我的依赖项(三个和 hammerjs)包装到全局命名空间中。我的包使用者有另一个包含 hammerjs(几乎相同版本)的组件,所以我怀疑最后包含的组件将定义我的应用程序可用的 hammerjs 包?其他以独立方式工作的 NPM 包如何处理这个问题?
我发现 Browserify 可以简单地通过将 bundleExternal 设置为 false 或通过一个一个地排除依赖项来排除它的依赖项,然后将这些库包含在浏览器中。这不起作用,我在控制台中收到“找不到模块 'hammerjs'”错误。我发现 以及如何分别浏览所有依赖项,这也有效,但据我所知,这与简单地将它们捆绑在一个地方是一样的,因为我不能简单地包含它们的 hammer.min.js 文件网站?
TL;DR
在不支持模块的应用程序中捆绑模块化 Typescript NPM 包和处理依赖项的正确方法是什么?
要构建一个有效的 npm 包,您需要在 package.json
中指定的主要路径上交付您的模块。
通常,这应该在 dist/my-main-class.js
.
内
所以在我的 src
里面有一个 class/module/namespace 代表我的项目:
class MyMainClass{
static myMethod = function(){
console.log('hello');
}
}
在文件末尾我有这个:export = MyMainClass;
.
如果我发布名为 MyMainClass 的包:$ npm publish MyMainClass
,我的用户只需使用:
即可导入它
let MyMainClass = require('MyMainClass');
MyMainClass.myMethod();
不要忘记在发布前构建。
这就是我确保永远不会忘记的方式:
"build": "tsc -p .",
"prepublish": "npm run build",
如果你想缩小你的代码或做其他事情,你可以在发布之前使用 gulp/webpack 并定位你的 dist 文件夹。
"minify": "gulp/grunt/webpack minify dist/**.js",
"build": "tsc -p .",
"prepublish": "npm run build && npm run minify",
我仍然不太确定处理依赖关系的最佳方法,但我选择的路线是创建一个 mylib.js 和一个 mylib.min.js 分布在 npm 包中并且不包含任何依赖项。除了分布式 js 文件外,我还包含了模块化形式的库,可以与 browserify 等一起使用。我在使用 browserify 时遇到的问题是,我尝试排除库的输出仍然依赖于某种形式的要求,当我尝试 webpack 时,它开箱即用。
我包含了完整的构建脚本以供参考。
文件结构
├───dist // Output folder for all my distributable standalone js files
│ ├───mylib.d.ts // Manually writtes declaration file
│ ├───myLib.js // Distributable without dependencies
│ └───myLib.min.js // Compressed distributable without dependencies
├───lib
│ ├───myLib.js // Compiled src
│ └───myLib.d.ts // Compiled d.ts
├───src // Folder containing my Typescript src
├───tests // Output folder for my tests
└───testSrc // Src folder for my test code
├───test.html
└───unittests
package.json
{
"name": "mylib",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "gulp compile && gulp webpack",
"prepublish": "gulp prepublish"
},
"main": "lib/mylib.js",
"typings": "lib/mylib",
"dependencies": {
"@types/es6-promise": "0.0.32",
"@types/hammerjs": "2.0.33",
"@types/three": "0.0.24",
"es6-promise": "4.0.5",
"hammerjs": "2.0.8",
"three": "0.82.1"
},
"devDependencies": {
"@types/jasmine": "2.5.37",
"gulp": "3.9.1",
"gulp-cli": "1.2.2",
"gulp-concat": "2.6.0",
"gulp-copy": "0.0.2",
"gulp-jasmine": "2.4.2",
"gulp-preprocess": "2.0.0",
"gulp-typescript": "3.1.2",
"jasmine": "2.5.2",
"ts-loader": "1.0.0",
"typescript": "2.0.6",
"webpack-stream": "3.2.0"
}
}
Gulpfile.js
var gulp = require('gulp');
var ts = require('gulp-typescript');
// Create projects from tsconfig.json
var tsProject = ts.createProject('tsconfig.json');
var mainTestTsProject = ts.createProject('testSrc/tsconfig.json');
var jasmineTsProject = ts.createProject('testSrc/unittests/tsconfig.json');
// External build libraries
var jasmine = require("gulp-jasmine");
var concat = require("gulp-concat");
var copy = require("gulp-copy");
var preprocess = require("gulp-preprocess");
var webpack = require("webpack-stream");
// Compile the modular library
gulp.task('compile', function () {
return tsProject.src()
.pipe(tsProject())
.pipe(gulp.dest("lib"));
});
// Pack and distribute
gulp.task('webpack', function (callback) {
var config = require("./webpack.config.js");
return tsProject.src()
.pipe(webpack(config))
.pipe(gulp.dest('./dist'))
});
// Pre-process the test.html
gulp.task('preprocessMainHtml', function () {
return gulp.src('./testSrc/*.html')
.pipe(preprocess({ context: { CURRENT_TIMESTAMP: Date.now() } }))
.pipe(gulp.dest('./tests/'));
});
// Copy output libraries for testing
gulp.task('copyLibs', function () {
return gulp.src(['./dist/*.js', './dist/*.map'])
.pipe(copy('./tests', { prefix: 1 }));
});
// Compile the test-html main javascript file
gulp.task('compileMainTest', ['copyLibs', 'preprocessMainHtml'], function (callback) {
return mainTestTsProject.src()
.pipe(mainTestTsProject())
.pipe(concat('mylib-test.js'))
.pipe(gulp.dest("tests"));
});
gulp.task('prepublish', ['compile', 'webpack']);
gulp.task('test', ['compile'], function () {
return jasmineTsProject.src()
.pipe(jasmineTsProject())
.pipe(gulp.dest("tests/unittests"))
.pipe(jasmine());
});
gulp.task("default", ['webpack', 'compileMainTest']);
webpack.config.js
module.exports = {
resolve: {
extensions: ['', '.js', '.ts', '.tsx']
},
module: {
loaders: [
{ test: /\.tsx?$/, loader: 'ts' },
]
},
externals: {
"hammerjs": "Hammer",
"three": "THREE"
},
entry: {
"MyLib": ['./src/mylib.ts'],
},
output: {
path: __dirname + '/dist',
filename: 'mylib.js',
libraryTarget: "umd",
library: 'MyLib'
},
devtool: 'source-map',
debug: true
}
手写声明文件
// Manually maintained declaration file
// External references
/// <reference types="hammerjs" />
/// <reference types="three" />
// This namespace should map to what is exported in the Gruntfile.js
export as namespace MyLib;
export declare class MyMainClass {
constructor(a: string);
}
test.html
中的 Js src
<!-- All mylib.js dependencies -->
<script src="../node_modules/three/build/three.js?_=<!-- @echo CURRENT_TIMESTAMP -->"></script>
<script src="../node_modules/hammerjs/hammer.js?_=<!-- @echo CURRENT_TIMESTAMP -->"></script>
<!-- mylib.js library -->
<script src="./mylib.js?_=<!-- @echo CURRENT_TIMESTAMP -->"></script>
<!-- mylib.js test source -->
<script src="./mylib-test.js?_=<!-- @echo CURRENT_TIMESTAMP -->"></script>
我目前正在尝试为 Typescript 项目创建一个 NPM 包(我正在使用 gulp 和 browserify 进行构建)。问题是包消费者目前没有使用模块,所以我正在尝试使用 Browserify 打包一个独立的包。
首先,捆绑所有依赖项会不会是个问题?据我所知,捆绑的 js 文件只是将我的依赖项(三个和 hammerjs)包装到全局命名空间中。我的包使用者有另一个包含 hammerjs(几乎相同版本)的组件,所以我怀疑最后包含的组件将定义我的应用程序可用的 hammerjs 包?其他以独立方式工作的 NPM 包如何处理这个问题?
我发现 Browserify 可以简单地通过将 bundleExternal 设置为 false 或通过一个一个地排除依赖项来排除它的依赖项,然后将这些库包含在浏览器中。这不起作用,我在控制台中收到“找不到模块 'hammerjs'”错误。我发现
TL;DR
在不支持模块的应用程序中捆绑模块化 Typescript NPM 包和处理依赖项的正确方法是什么?
要构建一个有效的 npm 包,您需要在 package.json
中指定的主要路径上交付您的模块。
通常,这应该在 dist/my-main-class.js
.
所以在我的 src
里面有一个 class/module/namespace 代表我的项目:
class MyMainClass{
static myMethod = function(){
console.log('hello');
}
}
在文件末尾我有这个:export = MyMainClass;
.
如果我发布名为 MyMainClass 的包:$ npm publish MyMainClass
,我的用户只需使用:
let MyMainClass = require('MyMainClass');
MyMainClass.myMethod();
不要忘记在发布前构建。 这就是我确保永远不会忘记的方式:
"build": "tsc -p .",
"prepublish": "npm run build",
如果你想缩小你的代码或做其他事情,你可以在发布之前使用 gulp/webpack 并定位你的 dist 文件夹。
"minify": "gulp/grunt/webpack minify dist/**.js",
"build": "tsc -p .",
"prepublish": "npm run build && npm run minify",
我仍然不太确定处理依赖关系的最佳方法,但我选择的路线是创建一个 mylib.js 和一个 mylib.min.js 分布在 npm 包中并且不包含任何依赖项。除了分布式 js 文件外,我还包含了模块化形式的库,可以与 browserify 等一起使用。我在使用 browserify 时遇到的问题是,我尝试排除库的输出仍然依赖于某种形式的要求,当我尝试 webpack 时,它开箱即用。
我包含了完整的构建脚本以供参考。
文件结构
├───dist // Output folder for all my distributable standalone js files
│ ├───mylib.d.ts // Manually writtes declaration file
│ ├───myLib.js // Distributable without dependencies
│ └───myLib.min.js // Compressed distributable without dependencies
├───lib
│ ├───myLib.js // Compiled src
│ └───myLib.d.ts // Compiled d.ts
├───src // Folder containing my Typescript src
├───tests // Output folder for my tests
└───testSrc // Src folder for my test code
├───test.html
└───unittests
package.json
{
"name": "mylib",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "gulp compile && gulp webpack",
"prepublish": "gulp prepublish"
},
"main": "lib/mylib.js",
"typings": "lib/mylib",
"dependencies": {
"@types/es6-promise": "0.0.32",
"@types/hammerjs": "2.0.33",
"@types/three": "0.0.24",
"es6-promise": "4.0.5",
"hammerjs": "2.0.8",
"three": "0.82.1"
},
"devDependencies": {
"@types/jasmine": "2.5.37",
"gulp": "3.9.1",
"gulp-cli": "1.2.2",
"gulp-concat": "2.6.0",
"gulp-copy": "0.0.2",
"gulp-jasmine": "2.4.2",
"gulp-preprocess": "2.0.0",
"gulp-typescript": "3.1.2",
"jasmine": "2.5.2",
"ts-loader": "1.0.0",
"typescript": "2.0.6",
"webpack-stream": "3.2.0"
}
}
Gulpfile.js
var gulp = require('gulp');
var ts = require('gulp-typescript');
// Create projects from tsconfig.json
var tsProject = ts.createProject('tsconfig.json');
var mainTestTsProject = ts.createProject('testSrc/tsconfig.json');
var jasmineTsProject = ts.createProject('testSrc/unittests/tsconfig.json');
// External build libraries
var jasmine = require("gulp-jasmine");
var concat = require("gulp-concat");
var copy = require("gulp-copy");
var preprocess = require("gulp-preprocess");
var webpack = require("webpack-stream");
// Compile the modular library
gulp.task('compile', function () {
return tsProject.src()
.pipe(tsProject())
.pipe(gulp.dest("lib"));
});
// Pack and distribute
gulp.task('webpack', function (callback) {
var config = require("./webpack.config.js");
return tsProject.src()
.pipe(webpack(config))
.pipe(gulp.dest('./dist'))
});
// Pre-process the test.html
gulp.task('preprocessMainHtml', function () {
return gulp.src('./testSrc/*.html')
.pipe(preprocess({ context: { CURRENT_TIMESTAMP: Date.now() } }))
.pipe(gulp.dest('./tests/'));
});
// Copy output libraries for testing
gulp.task('copyLibs', function () {
return gulp.src(['./dist/*.js', './dist/*.map'])
.pipe(copy('./tests', { prefix: 1 }));
});
// Compile the test-html main javascript file
gulp.task('compileMainTest', ['copyLibs', 'preprocessMainHtml'], function (callback) {
return mainTestTsProject.src()
.pipe(mainTestTsProject())
.pipe(concat('mylib-test.js'))
.pipe(gulp.dest("tests"));
});
gulp.task('prepublish', ['compile', 'webpack']);
gulp.task('test', ['compile'], function () {
return jasmineTsProject.src()
.pipe(jasmineTsProject())
.pipe(gulp.dest("tests/unittests"))
.pipe(jasmine());
});
gulp.task("default", ['webpack', 'compileMainTest']);
webpack.config.js
module.exports = {
resolve: {
extensions: ['', '.js', '.ts', '.tsx']
},
module: {
loaders: [
{ test: /\.tsx?$/, loader: 'ts' },
]
},
externals: {
"hammerjs": "Hammer",
"three": "THREE"
},
entry: {
"MyLib": ['./src/mylib.ts'],
},
output: {
path: __dirname + '/dist',
filename: 'mylib.js',
libraryTarget: "umd",
library: 'MyLib'
},
devtool: 'source-map',
debug: true
}
手写声明文件
// Manually maintained declaration file
// External references
/// <reference types="hammerjs" />
/// <reference types="three" />
// This namespace should map to what is exported in the Gruntfile.js
export as namespace MyLib;
export declare class MyMainClass {
constructor(a: string);
}
test.html
中的 Js src<!-- All mylib.js dependencies -->
<script src="../node_modules/three/build/three.js?_=<!-- @echo CURRENT_TIMESTAMP -->"></script>
<script src="../node_modules/hammerjs/hammer.js?_=<!-- @echo CURRENT_TIMESTAMP -->"></script>
<!-- mylib.js library -->
<script src="./mylib.js?_=<!-- @echo CURRENT_TIMESTAMP -->"></script>
<!-- mylib.js test source -->
<script src="./mylib-test.js?_=<!-- @echo CURRENT_TIMESTAMP -->"></script>