【问题标题】:Should I exclude dependencies for modular Typescript project?我应该排除模块化 Typescript 项目的依赖项吗?
【发布时间】:2017-03-17 05:41:15
【问题描述】:

我目前正在尝试为 Typescript 项目创建一个 NPM 包(我正在使用 gulp 和 browserify 构建)。问题是包消费者当前没有使用模块,所以我正在尝试使用 Browserify 打包一个独立的包。

首先,捆绑所有依赖项会不会是个问题?据我所知,捆绑的 js 文件只是将我的依赖项(三个和hammerjs)包装到全局命名空间中。我的包消费者有另一个包含hammerjs的组件(几乎相同的版本)所以我怀疑最后包含的组件将定义哪些hammerjs包可用于我的应用程序?其他以独立方式工作的 NPM 包如何处理这个问题?

我发现 Browserify 可以简单地通过将 bundleExternal 设置为 false 或通过逐个排除依赖项来排除其依赖项,然后将这些库包含在浏览器中。这不起作用,我在控制台中收到“找不到模块'hammerjs'”错误。我找到了how to use the exclude in browserify? 以及如何单独浏览所有依赖项,这也有效,但据我所知,这与简单地将它们捆绑在一个地方是一样的,因为我不能简单地从他们的网站中包含hammer.min.js 文件?

TL;DR

捆绑模块化 Typescript NPM 包并处理依赖项以在不支持模块的应用程序中使用的正确方法是什么?

【问题讨论】:

    标签: typescript npm browserify


    【解决方案1】:

    我仍然不太确定处理依赖项的最佳方法,但我选择采用的方法是创建一个 mylib.js 和一个 mylib.min.js,它们分布在 npm 包中并且不包含任何依赖项。除了分布式 js 文件之外,我还以模块化形式包含了该库,例如 browserify。我在 browserify 中遇到的问题是,我尝试排除库的输出仍然依赖于某种形式的 require,当我尝试使用 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>
    

    【讨论】:

    【解决方案2】:

    要构建一个有效的 npm 包,您需要在您的 package.json 中指定的主要路径上交付您的模块。 通常,这应该在dist/my-main-class.js 内。

    所以在我的src 中,我有一个代表我的项目的类/模块/命名空间:

    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",
    

    【讨论】:

    • 感谢您的回复!我必须使用 es5,所以我假设您的意思是正确的方法是将 requirejs 包含在我的消费应用程序中?在您的示例中,您没有缩小任何东西,我认为这不是需要句柄的东西,我应该在导出到 dist/my-main-class.js 之前进行一些缩小?但是当我像现在一样尝试捆绑和缩小所有内容时,我不会遇到同样的问题吗?
    • 除非要在客户端使用,否则不需要缩小。如果需要,请确保同时提供:MyMainClass.js 和 MyMainClass.min.js
    • 由于我的主应用程序当前的结构我不想使用 require 所以我只想包含这个组件,因为我已经将所有其他组件作为独立的 js 文件包含在内。尽管我认为您的解决方案更符合我将来想要做的事情,但我确保我可以使用 browserify 或类似工具来使用这个组件。我用完整的解决方案回答了我自己的问题。我仍在研究如何进行缩小,但我觉得这超出了我最初的问题的范围。
    猜你喜欢
    • 1970-01-01
    • 2021-02-27
    • 2019-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多