TypeScript中的GulpFile,如何正确捆绑此简单示例场景?

时间:2019-11-29 03:12:35

标签: typescript gulp electron.net

编辑2(2019年12月1日)

好的,我对TypeScript知之甚少,但是我已经设法编写了如下代码:

gulpfile.ts

/// <binding AfterBuild='cleanCopyAndPack' Clean='clean' />

// ONLY imports from `node_modules`
import * as gulp from "gulp";
import * as fs from "fs";
import * as fs_extra from "fs-extra";
import * as path from "path";
import * as sourcemaps from "gulp-sourcemaps";
import * as gulpTs from "./node_modules/gulp-typescript/release/main";
import { promisify } from "util";

// `package.json` for config
var json = require(path.resolve(__dirname, "package.json"));

// definitions for await calls to avoid ugly callback syntax
const readFileAsync = (path: string) => new Promise((res: (data: string) => void) => fs.readFile(path, { encoding: "utf8" }, (err: NodeJS.ErrnoException, data: string) => res(data)));
const writeFileAsync = promisify(fs.writeFile);
const existsAsync = promisify(fs.exists);
const copyAsync = (src: string, dest: string) => fs_extra.copy(src, dest);
const removeAsync = fs_extra.remove;
const mkdirAsync = (path: string) => new Promise(res => fs.mkdir(path, { recursive: true }, res));
const readdirAsync = promisify(fs.readdir);

// Utility functions to keep `gulpfile` portable and independent of custom modules
var readdirRecursiveAsync = async (dir: string) => {
    var results: Array<string> = [];
    const list = await readdirAsync(dir);
    for (let file of list) {
        file = path.resolve(dir, file);
        const stat = fs.statSync(file);
        if (stat && stat.isDirectory()) {
            results = results.concat(await readdirRecursiveAsync(file));
        } else {
            results.push(file);
        }
    }
    return results;
}

var startsWithAny = (str: string, substrs: Array<string>) => {
    for (let substr of substrs) {
        if (str.startsWith(substr)) {
            return true;
        }
    }
    return false;
}

var endsWithAny = (str: string, substrs: Array<string>) => {
    for (let substr of substrs) {
        if (str.endsWith(substr)) {
            return true;
        }
    }
    return false;
}

var pathSeparator = (path: string) => {
    return path.includes("/") ? "/" : "\\";
}

// GULP TASK: `clean`
async function clean(cb: (err?: any) => void) {
    const srcTsConfigJsonShortPath = "tsconfig.json";
    const srcTsConfigJsonPath = path.resolve(__dirname, "wwwroot/_My/Scripts", srcTsConfigJsonShortPath);
    const destTsConfigJsonPath = path.resolve(__dirname, "../GulpDebug/GulpDebug", srcTsConfigJsonShortPath);
    const tsConfigJson = (await readFileAsync(srcTsConfigJsonPath)).replace(/\"module\":.*,/, "\"module\": \"commonjs\",");;
    await removeAsync(destTsConfigJsonPath);
    await writeFileAsync(destTsConfigJsonPath, tsConfigJson);

    await removeAsync(path.resolve(__dirname, "wwwroot/images"));
    await removeAsync(path.resolve(__dirname, "wwwroot/npm"));
    await removeAsync(path.resolve(__dirname, "wwwroot/_My/Scripts/dist"));

    const existingModulesPath = path.resolve(__dirname, "wwwroot/libs/npm");
    const nodePathKeys = Object.keys(json.dependencies).map(k => k.replace(pathSeparator(k), pathSeparator(existingModulesPath))); 
    const exisitngModules = (await readdirAsync(existingModulesPath)).filter(p => !p.startsWith("@types")).map(p => path.resolve(existingModulesPath, p));
    const existingModuleTypes = (await readdirAsync(path.resolve(existingModulesPath, "@types"))).map(p => path.resolve(existingModulesPath, "@types", p));
    const notReferencedAnymore = exisitngModules.concat(existingModuleTypes).filter(p => !endsWithAny(p, nodePathKeys));
    for (let nrm of notReferencedAnymore) {
        await removeAsync(nrm);
    }

    const jsFiles = (await readdirRecursiveAsync(path.resolve(__dirname, "wwwroot/_My/Scripts"))).filter(p => p.endsWith(".js") || p.endsWith(".js.map"));
    for (let jsFile of jsFiles) {
        await removeAsync(jsFile);
    }

    const electronPath = path.resolve(__dirname, "obj/Host/bin/wwwroot/_My/Scripts");
    if (await existsAsync(electronPath)) {
        await removeAsync(electronPath);
    }

    console.log("cleaning done");
    cb();
}

// GULP TASK: `copyNodeModules`
async function copyNodeModules(cb: (err?: any) => void) {
    const nodeModulesPath = path.resolve(__dirname, "node_modules");
    const newModulesPath = path.resolve(__dirname, "wwwroot/libs/npm");
    const nodePathKeys = Object.keys(json.dependencies).map(k => k.replace(pathSeparator(k), pathSeparator(nodeModulesPath)));
    const nodeExts = [".js", ".css", ".d.ts"];
    const modules = await readdirRecursiveAsync(nodeModulesPath + pathSeparator(nodeModulesPath));
    const modulesToCopy = modules.filter(p => startsWithAny(p.split(nodeModulesPath + pathSeparator(nodeModulesPath))[1], nodePathKeys.map(p => p + pathSeparator(p))) && endsWithAny(p, nodeExts));

    for (let p of modulesToCopy) {
        const newPath = path.resolve(newModulesPath, p.split(nodeModulesPath + pathSeparator(nodeModulesPath))[1]);
        if (!fs.existsSync(newPath)) {
            console.log(`  copying: node_modules\\${p.split(nodeModulesPath + pathSeparator(nodeModulesPath))[1]} ==> wwwroot\\libs\\npm\\${newPath.split(newModulesPath + pathSeparator(newModulesPath))[1]}`);
            await copyAsync(p, newPath);
        }
    }
    console.log("Modules copied properly");

    cb();
};

// custom modules resolver for GULP TASK `pack` - it removes references and replaces `requires` and `imports` 
// with valid paths provided as short or relative paths in the comments
async function resolveModulesAsync(jsDirPath: string) {
    const basePath = path.resolve(__dirname, "wwwroot/_My/Scripts");
    const jsFiles = (await readdirRecursiveAsync(jsDirPath)).filter(p => p.endsWith(".js"));
    for (let jsFile of jsFiles) {
        const lines = (await readFileAsync(jsFile)).toString().split("\n");
        for (let i = 0; i < lines.length; i++) {
            const l = lines[i];
            if ((l.includes("require(\"") || l.startsWith("import ")) && !l.trim().startsWith("//") && l.includes("//")) {

                const tsPath = l.match(/"([^"]+)"/)[1];
                if (!tsPath.endsWith(".js")) {
                    const jSPath = l.split("// ").pop().trim();
                    const shortPath = jSPath.split(`.${pathSeparator(jSPath)}`).slice(-1)[0];
                    console.log(`Resolving import for "${shortPath}" in "${jsFile.split(basePath + pathSeparator(basePath))[1]}"`);
                    console.log(`  from: "${tsPath}"`);
                    let relativePath = shortPath;
                    if (!jsFiles.map(js => js.toLowerCase()).some(js => js.endsWith(shortPath.toLowerCase()))) {
                        let depth = 0;
                        const jsFileDirPath = jsFile.split(pathSeparator(jsFile)).slice(0, -1).join(pathSeparator(jsFile));
                        while (!(await existsAsync(path.resolve(jsFileDirPath, relativePath))) && depth < 10) {
                            relativePath = `../${relativePath}`;
                            depth++;
                        }
                        if (depth > 10)
                            throw "module path not found";
                    }
                    const formattedRelativePath = startsWithAny(relativePath, [ ".", "/" ]) ? `"${relativePath}"` : `"./${relativePath}"`;
                    lines[i] = (l.split("// ")[0].replace(/".*?"/, formattedRelativePath)).replace(/(\r\n|\n|\r)/gm, " ");
                    console.log(`  to: ${formattedRelativePath}`);
                }
            } else if (l.startsWith("/// <reference")) {
                lines[i] = "";
            }
        }
        await writeFileAsync(jsFile, lines.join("\n"));
    }
}

// GULP TASK: `pack`
async function pack(cb: (err?: any) => void) {
    const myTsFiles = (await readdirRecursiveAsync(path.resolve(__dirname, "wwwroot/_My/Scripts"))).filter(p => p.endsWith(".ts") && !p.endsWith(".d.ts"));

    const tsConfigPath = path.resolve(__dirname, "wwwroot/_My/Scripts", "tsconfig.json");
    const tsProject = await gulpTs.createProject(tsConfigPath);
    const basePath = path.resolve(__dirname, "wwwroot/_My/Scripts");
    const electronPath = path.resolve(__dirname, "obj/Host/bin/wwwroot/_My/Scripts");

    await new Promise(res => gulp.src(myTsFiles, { base: basePath })
        .pipe(sourcemaps.init({ loadMaps: true }))
        .pipe(tsProject())
        .pipe(gulp.dest(basePath))        
        .pipe(sourcemaps.write()) // map must be inline for VS debugger to work
        .pipe(gulp.dest(basePath)).pipe(gulp.dest(electronPath)).on("end", res));

    await resolveModulesAsync(basePath);

    if (await existsAsync(electronPath)) { // overwrite default debugger copy to electron path
        await removeAsync(electronPath);
    }

    console.log("Copying files to electron app folders");
    const scripts = (await readdirRecursiveAsync(basePath)).filter(f => f.endsWith("js"));
    for (let script of scripts) {
        const shortPath = script.split(basePath + pathSeparator(basePath))[1];
        const name = script.split(pathSeparator(script)).slice(-1)[0];
        const dest = path.resolve(electronPath, shortPath);
        const dir = dest.split(pathSeparator(dest) + name)[0];
        console.log(`  --> ${shortPath}`);

        if (!(await existsAsync(dir))) {
            await mkdirAsync(dir);
        }

        await copyAsync(script, dest);
    }

    await removeAsync(path.resolve(__dirname, "wwwroot/_My/Scripts/dist/temp"));

    console.log("Files transpiled properly");

    cb(); // this is to keep the `Task Runner` happy because afaik there is no way to avoid callback syntax for the actual tasks
}

// GULP TASK: `copy` --> `pack`
function copyAndPack(cb: (err?: any) => void) {
    gulp.series(copyNodeModules, pack)(() => { });
    cb();
};

// GULP TASK: `clean` --> `copy`
function cleanAndCopy(cb: (err?: any) => void) {
    gulp.series(clean, copyNodeModules)(() => { });
    cb();
};

// GULP TASK: `clean` --> `copy` --> `pack`
function cleanCopyAndPack(cb: (err?: any) => void) {
    gulp.series(clean, copyNodeModules, pack)(() => { });
    cb();
};

export { clean, copyNodeModules, pack, copyAndPack, cleanAndCopy, cleanCopyAndPack };

上面的代码存在以下问题:

  1. 它不使用任何捆绑程序(webpack,browserify,汇总),因为我找不到适合它们的任何正确配置以将其捆绑到浏览器中,因此我依赖我的自定义解析器,这可能是一个过大的选择。 / li>
  2. 没有缩小器允许我在TS中使用gulpfile时将其添加到管道中。
  3. 我想稍微编译一下,但是我从管道中删除了gulp-babel,因为实际上没有选项或插件允许模块引用在浏览器中正常工作,所以我认为:babel --> webpack ,但如上所述,我也无法使webpack正常工作。
  4. 无论我在Gulp中做什么,因此在Task RunnerAfterBuild在为Electron.NET进行编译时都会被覆盖。基本上,obj/Host/bin/wwwroot会将我VS的所有结果写到目标目录后,pipe会自动将所有脚本自动写入,但是此后我所做的一切都会被忽略。为了克服这个问题,我在<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>文件中显式设置了.csproj,但这显然可以防止由于TS错误而导致编译被阻止,这是一个非常烦人的麻烦。

因此,请注意,我主要是在自言自语,我目前对你们的问题是:

如何正确地使用babel对其进行向下编译,最小化并与webpack或任何其他捆绑程序捆绑在一起?

.pipe(gulpbabel({
     presets: [path.resolve(__dirname, "node_modules", "@babel/preset-env")], 
     plugins: [path.resolve(__dirname, "node_modules", "@babel/plugin-transform-modules-amd")]
))
.pipe(gulpwebpack(config))

您还可以放心,我已经解决了以上列表中未提及的其他问题。

==================== OLD ===================

编辑1

基本上我需要这样(注意其TypeScript):

function pack(cb: (err?: any) => void) {
    const myTsFIles = readdirRecursiveSync(path.resolve(__dirname, "wwwroot/_My/Scripts")).filter(p => p.endsWith(".ts") && !p.endsWith(".d.ts"));

    // `tsconfig.json` NOT USED?
    const tsConfigPath = path.resolve(__dirname, "wwwroot/_My/Scripts/tsconfig.json");
    const destpath = path.resolve(__dirname, "wwwroot/_My/Scripts/dist");

    browserify({
        basedir: __dirname,
        debug: true,
        entries: myTsFIles,
        cache: {},
        packageCache: {},
        extensions: [".ts"]
    }).transform("babelify", {
        sourceType: "module",
        presets: ["@babel/preset-typescript"],
        extensions: [".ts"]
    })
        .plugin(tsify)
        .bundle()
        .pipe(vss("bundle.js"))
        .pipe(vb())
        .pipe(sourcemaps.init({ loadMaps: true }))
        .pipe(uglify())
        .pipe(sourcemaps.write("./"))
        .pipe(gulp.dest(destpath));

    console.log("_My Files transpiled properly");

    cb();
}

但是:

  1. 我不知道如何将tscnfig.json作为参数传递,也不知道如何正确地确保我可以用tsnext进行编写,但是将其编译为最合适的浏览器兼容性。 / li>
  2. 我不确定如何为uglifytsify编写适当的声明,因为它们没有提供任何声明。
  3. 在您的帮助下解决以上两个问题后,我们将着手解决babel解析器引发的签名不匹配和运行时错误。

或者我可以使用.pipe(tsProject()).js中的gulp-typescript而不是tsify,但是我不确定是否应该使用这些乙烯基电话。

===初始问题===

我正在学习TypeScript,并且整天都在努力寻找一种方法来正确地将事物与我创建的示例场景捆绑在一起。尽管事实上我现在非常了解browserifywebpackgulpbabel等工具应该如何工作,但{{1} }开发人员是否希望掌握全部情况。对于.NET尤其如此。很难遵循这些教程,因为其中大多数都是为TypeScript编写的,而且许多教程仍使用比当前语法更旧的语法,并且据我所知:JavaScript不会原谅已弃用的语法。许多工具(例如大量的TS部分或vinyl)根本根本不提供tsify,而我还不愿意编写这些定义。尤其是由于此类工具的许多功能部分或完全重叠,并且我不确定@types应用应使用哪种工具,以及使用什么顺序。每个教程对它的显示方式都不相同,没有一个与我的情况相似。我还不断收到Asp.NET Corebabel方法签名的怪异异常,它们与TS文章中指定的签名往往没有什么共同之处,但让我们留在那儿。我需要的是一个参考点,一个例子或几个:JS Gulp任务,可以处理我的情况,以便我可以看一下并了解以后如何使用这些工具。

假设我们已经将gulpfile.ts中的@typesnode_modules复制到了正确的文件夹中,并且我也默认禁用了package.json Visual Studio编译。我们还假设我具有以下目录结构:

enter image description here

我需要执行.ts任务:

  1. gulp中的所有示例.ts文件编译为与浏览器兼容的wwwroot/_My/Scripts

    所以:

    JavaScript

    Utils.ts

    import fs from "fs"; import path from "path"; class utils { static readdirRecursive = (dir: string, done: (err: NodeJS.ErrnoException, res?: Array<string>) => void) => { var results: Array<string> = []; fs.readdir(dir, (err, list) => { if (err) return done(err); var pending = list.length; if (!pending) return done(null, results); list.forEach(file => { file = path.resolve(dir, file); fs.stat(file, (_err, stat) => { if (stat && stat.isDirectory()) { utils.readdirRecursive(file, (_err, res) => { results = results.concat(res); if (!--pending) done(null, results); }); } else { results.push(file); if (!--pending) done(null, results); } }); }); }); }; static readdirRecursiveSync = (dir: string) => { var results: Array<string> = []; const list = fs.readdirSync(dir); list.forEach(file => { file = path.resolve(dir, file); const stat = fs.statSync(file); if (stat && stat.isDirectory()) { results = results.concat(utils.readdirRecursiveSync(file)); } else { results.push(file); } }); return results; } } export default utils;

    Extensions.ts

    // #region StringExtensions interface String { startsWithAny(substrs: Array<string>): boolean; } String.prototype.startsWithAny = function (this: string, substrs: Array<string>) { for (let substr of substrs) { if (this.startsWith(substr)) { return true; } } return false; } // #endregion

    Scripts.ts
  2. 使这些/// <reference path="customdeclarations.d.ts" /> import { createChart } from "../../libs/npm/lightweight-charts/dist/typings"; // ../../libs/npm/lightweight-charts/dist/lightweight-charts.esm.development.js import "./extensions"; // ./extensions.js import utils from "./utils"; // ./utils.js $(() => (document)).ready(() => { var sc = "pinacolada".startsWithAny(["pina", "foxes"]); $("body").overlayScrollbars({ className: "os-theme-dark" }); // Update Dropdowns $(() => (document)).on("click", ".dropdown-menu a", (e) => { const $a = $(e.target); const ddlText = $a.text(); const ddlValue = $a.attr("value"); const $divDropDown = $a.closest(".dropdown"); const $btnDropDownToggle = $divDropDown.find("button"); const $spanDescription = $btnDropDownToggle.find(".dropdown-description"); const $inputHidden = $btnDropDownToggle.find(".dropdown-hiddeninput"); $spanDescription.text(() => ddlText); $inputHidden.attr("value", ddlValue); }); }); 文件正确解析为.js:因此,这意味着imports应该正确解析为import { createChart } from "../../libs/npm/lightweight-charts/dist/typings";,而../../libs/npm/lightweight-charts/dist/lightweight-charts.esm.development.js则解析为{ {1}}。

  3. 缩小文件

  4. 将文件合并为一个捆绑包

  5. 生成正确的源地图

  6. 确保./utils断点适用于以这种方式生成的文件

  7. ./utils.js的内容复制到Visual Studio中,这样在wwwroot编译后就不会使用某些默认的obj/Host/bin/wwwroot配置({{1} }应该彼此匹配,并且转译的gulp代码不应由某些自动默认的东西重写)。

这是我正在进行的尝试,仅供参考,但是请不要基于此解决方案:

Electron.NET

直到现在,我一直在使用我的自定义解决方案,该解决方案从未真正触及过本文中介绍的大多数工具(对此也没有触及wwwroot)。我创建的解决方案并不完美,也无法解决js覆盖问题。但是,现在有了function pack(cb: (err?: any) => void) { const myTsFIles = readdirRecursiveSync(path.resolve(__dirname, "wwwroot/_My/Scripts")).filter(p => p.endsWith(".ts") && !p.endsWith(".d.ts")); //return browserify(o) // .plugin(tsify) // .bundle() // .pipe(vss("bundle.js")) // .pipe(gulp.dest('dist')); const tsConfigPath = path.resolve(__dirname, "wwwroot/_My/Scripts/tsconfig.json"); const srcPath = path.resolve(__dirname, "wwwroot/_My/Scripts/**/**.ts"); const destpath = path.resolve(__dirname, "wwwroot/_My/Scripts/dist"); const tsProject = gulptypescript.createProject(tsConfigPath); const tsResult = browserify({ basedir: __dirname, debug: true, entries: myTsFIles, cache: {}, packageCache: {}, extensions: [ ".ts" ] }).transform(babelify.configure({ sourceType: "module", presets: [ "@babel/preset-typescript" ], extensions: [ ".ts" ] })).bundle().pipe(tsProject()); //tsResult.dts.pipe(gulp.dest(destpath)); tsResult.js .pipe(sourcemaps.init({ loadMaps: true })) //.pipe(babel({ presets: ["@babel/env"] })) .pipe(concat("all.js")) .pipe(sourcemaps.write("./")) .pipe(gulp.dest(destpath)); //var browserified = vss(function(filename) { // var b = browserify({ entries: filename, debug: true }); // return b.bundle(); //}); //return gulp.src(paths.jsEntryPoint) // .pipe(browserified) // .pipe(sourcemaps.init({ loadMaps: true })) // .pipe(uglify()) // .pipe(sourcemaps.write('./')) // .pipe(gulp.dest(paths.dist)); console.log("_My Files transpiled properly"); cb(); } 并没有像传统JavaScript那样的开箱即用的功能,因此人们不得不学习,今天希望与您的帮助人员一起学习。

0 个答案:

没有答案