编辑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 };
上面的代码存在以下问题:
TS
中使用gulpfile
时将其添加到管道中。gulp-babel
,因为实际上没有选项或插件允许模块引用在浏览器中正常工作,所以我认为:babel --> webpack
,但如上所述,我也无法使webpack
正常工作。Gulp
中做什么,因此在Task Runner
中AfterBuild
在为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();
}
但是:
tscnfig.json
作为参数传递,也不知道如何正确地确保我可以用tsnext
进行编写,但是将其编译为最合适的浏览器兼容性。 / li>
uglify
和tsify
编写适当的声明,因为它们没有提供任何声明。或者我可以使用.pipe(tsProject()).js
中的gulp-typescript
而不是tsify
,但是我不确定是否应该使用这些乙烯基电话。
===初始问题===
我正在学习TypeScript
,并且整天都在努力寻找一种方法来正确地将事物与我创建的示例场景捆绑在一起。尽管事实上我现在非常了解browserify
,webpack
,gulp
,babel
等工具应该如何工作,但{{1} }开发人员是否希望掌握全部情况。对于.NET
尤其如此。很难遵循这些教程,因为其中大多数都是为TypeScript
编写的,而且许多教程仍使用比当前语法更旧的语法,并且据我所知:JavaScript
不会原谅已弃用的语法。许多工具(例如大量的TS
部分或vinyl
)根本根本不提供tsify
,而我还不愿意编写这些定义。尤其是由于此类工具的许多功能部分或完全重叠,并且我不确定@types
应用应使用哪种工具,以及使用什么顺序。每个教程对它的显示方式都不相同,没有一个与我的情况相似。我还不断收到Asp.NET Core
和babel
方法签名的怪异异常,它们与TS
文章中指定的签名往往没有什么共同之处,但让我们留在那儿。我需要的是一个参考点,一个例子或几个:JS Gulp
任务,可以处理我的情况,以便我可以看一下并了解以后如何使用这些工具。
假设我们已经将gulpfile.ts
中的@types
和node_modules
复制到了正确的文件夹中,并且我也默认禁用了package.json
Visual Studio
编译。我们还假设我具有以下目录结构:
我需要执行.ts
任务:
将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
使这些/// <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}}。
缩小文件
将文件合并为一个捆绑包
生成正确的源地图
确保./utils
断点适用于以这种方式生成的文件
将./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
那样的开箱即用的功能,因此人们不得不学习,今天希望与您的帮助人员一起学习。