qodunpob
@qodunpob
Look at my code, my code is amazing!

Как написать gulp-task для сборки модульного js с помощью browserify?

Здравствуйте.
Подскажите пожалуйста, как написать gulp-таск для сборки *.js-файлов из typescript при помощи browserify со следующими условиями:
  • проект представляет из себя портал с разными по наполнению и функциональности разделами, а потому хочется генерировать не единый bundle.js для всего сайта, а отдельные скрипты для каждого раздела
  • общие модули хотелось бы вынести в отдельный файл common.js


Мои собственные изыскания привели меня к плагину factor-bundle для browserify, однако, как я понял, при его использовании требуется четкое указание входных и выходных файлов. Но это довольно неудобно, хочется чтобы скрипт был универсальным, и работал по шаблону. Еще один неясный для меня момент - это каким образом создать source map для каждого файла скрипта и положить его в определенную директорию.

Хотелось бы видеть следующий результат:
/<project_dir>
    /public
        /static
            /js
                /maps
                    ...
                /modules
                    ...
                common.js
    /static
        /typescript
            /compile
                /modules
                    ...
            /import
                ...

Файлы из папки static/typescript/compile должны после компиляция в обычный js и подключения всех зависимостей попадать в директорию public/static/js с сохранением своего имени и относительного пути (т.е. файлы из папки modules ложатся в modules), попутно генерируя common.js и source maps (в директории public/static/js/maps).

Я ни в коем случае не прошу предоставить мне готовое решение, лишь подскажите, в каком направлении копать. Я понимаю, что можно отдельно выполнить поиск файлов по шаблону, сгенерировать соотв. массивы входных и выходных файлов и создать все отсутствующие каталоги. Но разве нет более элегантного решения? Плюс, вопрос с source maps остается нерешенным...
  • Вопрос задан
  • 1320 просмотров
Решения вопроса 1
qodunpob
@qodunpob Автор вопроса
Look at my code, my code is amazing!
Сам спросил - сам отвечу. Как я понял, ни для browserify, ни для webpack нельзя указать wildcard в качестве точек входа. В своих поисках я пришел к двум решениям.

Если решать задачу с помощью browserify, необходимо самостоятельно позаботиться о формировании списка файлов и создании всех необходимых директорий. Получится нечто вроде этого:
gulp.task('compile-typescript', function () {
    let browserify = require('browserify'),
        factor = require('factor-bundle'),
        tsify = require('tsify'),
        path = require('path'),
        source = require('vinyl-source-stream'),
        glob = require('glob'),
        mkdirp = require('mkdirp'),

        srcDir = 'static/typescript/compile/',
        distDir = 'public/static/js/',

        entries = glob.sync(srcDir + '**/*.@(ts|tsx)'),
        outputs = [],

        mkdirProccess = [],

        typescriptOptions = {
            module: 'commonjs',
            target: 'es5',
            jsx: 'React'
        };

    entries.forEach(function (filename) {
        let relativePath = path.dirname(path.relative(srcDir, filename)),
            basename = path.basename(filename, path.extname(filename)) + '.js';

        if (relativePath === '.') {
            relativePath = '';
        } else {
            relativePath += '/';
            mkdirProccess.push(new Promise(function (resolve, reject) {
                mkdirp(distDir + relativePath, function (error) {
                    if (error) reject(error);
                    else resolve();
                });
            }));
        }

        outputs.push(distDir + relativePath + basename);
    });

    Promise.all(mkdirProccess)
        .then(function () {
            browserify({
                entries,
                debug: true
            })
                .plugin(tsify, typescriptOptions)
                .plugin(factor, {
                    outputs,
                    threshold: 2
                })
                .bundle()
                .pipe(source('common.js'))
                .pipe(gulp.dest(distDir));
        });
});

Однако, в таком случае source maps будут включены в итоговые файлы и содержать некорректные ссылки на исходники.

Но с использованием webpack-stream можно значительно упростить решение и получить желаемый результат:
gulpfile.js
gulp.task('compile-typescript', function () {
    let webpack = require('webpack-stream'),
        named = require('vinyl-named'),

        srcDir = 'static/typescript/compile/',
        distDir = 'public/static/js/';

    return gulp.src(srcDir + '**/*.@(ts|tsx)')
        .pipe(named(file => file.relative.replace(/\.tsx?$/, '')))
        .pipe(webpack(require('./webpack.config.js')))
        .pipe(gulp.dest(distDir));
});

webpack.config.js
'use strict';

const webpack = require('webpack');

module.exports = {
    resolve: {
        extensions: ['', '.webpack.js', '.web.js', '.js', '.ts', '.tsx']
    },
    output: {
        sourceMapFilename: 'maps/[file].map'
    },
    module: {
        loaders: [
            { test: /\.tsx?$/, loader: 'ts-loader' }
        ]
    },
    plugins: [
        new webpack.NoErrorsPlugin(),
        new webpack.optimize.CommonsChunkPlugin({
            name: 'common',
            minChunks: 3
        })
    ],
    devtool: 'source-map'
};

tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "jsx": "React",
    "sourceMap": true
  }
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы