maishu-scripts 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/babel.config.js +6 -0
- package/index.ts +5 -0
- package/modules/compile.test.ts +46 -0
- package/modules/compile.ts +221 -0
- package/modules/errors.ts +26 -0
- package/modules/import-path-rewrite.ts +44 -0
- package/modules/project-compiler.test.ts +36 -0
- package/modules/project-compiler.ts +266 -0
- package/modules/run.ts +8 -0
- package/package.json +24 -0
- package/start.ts +8 -0
- package/test/package.json +7 -0
- package/test/src/app.controller.ts +14 -0
- package/test/src/app.module.ts +17 -0
- package/test/src/app.service.ts +8 -0
- package/test/src/config.ts +16 -0
- package/test/src/controllers/proxy.ts +13 -0
- package/test/src/main.ts +8 -0
- package/test/src/middleware/proxy.ts +36 -0
- package/test/src/static/babel.config.json +6 -0
- package/test/src/static/design/babel.config.js +6 -0
- package/test/tsconfig.json +8 -0
package/babel.config.js
ADDED
package/index.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { copyFile, generateCode, isIgnoredFile } from "./compile";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import ts from "typescript";
|
|
5
|
+
|
|
6
|
+
describe("Compile Test", function () {
|
|
7
|
+
|
|
8
|
+
test("copyFile test", function () {
|
|
9
|
+
|
|
10
|
+
let srcPath = "test/src";
|
|
11
|
+
let srcExists = fs.existsSync(srcPath);
|
|
12
|
+
expect(srcExists).toBe(true);
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test("generateCode test", function () {
|
|
16
|
+
|
|
17
|
+
let srcPath = "test/src";
|
|
18
|
+
let destPath = "test/out";
|
|
19
|
+
if (fs.existsSync(destPath)) {
|
|
20
|
+
fs.rmdirSync(destPath, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
generateCode(srcPath, destPath);
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test("isIgnored test", function () {
|
|
26
|
+
let filePath = "D:\\startdard-process\\plm-projects\\tc-api-proxy\\src\\app.controller.spec.ts";
|
|
27
|
+
let ignored = isIgnoredFile(filePath);
|
|
28
|
+
expect(ignored).toBe(true);
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test("temp", function () {
|
|
32
|
+
let sourceFile = "D:\\startdard-process\\plm-projects\\tc-api-proxy\\src\\app.controller.ts";
|
|
33
|
+
let sourceCode = fs.readFileSync(sourceFile, "utf8");
|
|
34
|
+
let r = ts.transpileModule(sourceCode, {
|
|
35
|
+
compilerOptions: {
|
|
36
|
+
module: ts.ModuleKind.CommonJS,
|
|
37
|
+
target: ts.ScriptTarget.ES2015,
|
|
38
|
+
sourceMap: true
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
console.log(r);
|
|
43
|
+
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
})
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import * as babel from "@babel/core";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import watch from "node-watch";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { errors } from "./errors";
|
|
6
|
+
import { ProjectCompiler } from "./project-compiler";
|
|
7
|
+
|
|
8
|
+
const skipFiles = ["\\S+\\.d\\.tsx?$", "\\S+\\.test\\.tsx?$", "\\S+\\.spec\\.tsx?$"]
|
|
9
|
+
const outExt = ".js";
|
|
10
|
+
|
|
11
|
+
type FileAction = (filePath: string, outDir: string, projectPath?: string) => void;
|
|
12
|
+
let fileActions: { [ext: string]: FileAction } = {
|
|
13
|
+
".ts": compileFile,
|
|
14
|
+
".tsx": compileFile,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** 将 sourceDir 目录下所有文件生成到 outDir */
|
|
18
|
+
export function generateCode(sourceDir: string, outDir: string, projectPath?: string) {
|
|
19
|
+
|
|
20
|
+
if (!sourceDir) throw errors.argumentNull("sourceDir");
|
|
21
|
+
if (!outDir) throw errors.argumentNull("outDir");
|
|
22
|
+
|
|
23
|
+
if (!fs.existsSync(sourceDir))
|
|
24
|
+
throw errors.pathNotExists(sourceDir);
|
|
25
|
+
|
|
26
|
+
let files = fs.readdirSync(sourceDir);
|
|
27
|
+
for (let file of files) {
|
|
28
|
+
let filePath = path.join(sourceDir, file);
|
|
29
|
+
if (!fs.statSync(filePath).isFile()) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let isSkip = isIgnoredFile(filePath);//skipFiles.some(pattern => new RegExp(pattern).test(filePath));
|
|
34
|
+
if (isSkip) {
|
|
35
|
+
console.log(`Skip ${filePath}`);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let ext = extname(file);
|
|
40
|
+
let action = fileActions[ext];
|
|
41
|
+
if (action) {
|
|
42
|
+
action(filePath, outDir, projectPath);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let dirs = fs.readdirSync(sourceDir);
|
|
47
|
+
for (let dir of dirs) {
|
|
48
|
+
let fullPath = path.join(sourceDir, dir);
|
|
49
|
+
let outDirPath = path.join(outDir, dir);
|
|
50
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
51
|
+
generateCode(fullPath, outDirPath, projectPath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** 监听 sourceRoot 目录下所有文件变化,并生成到 outRoot */
|
|
57
|
+
export function watchDirectory(sourceRoot: string, outRoot: string, projectPath?: string) {
|
|
58
|
+
watch(sourceRoot, { recursive: true }, async (evt, name) => {
|
|
59
|
+
let action = fileActions[extname(name)];
|
|
60
|
+
let outPath = path.dirname(name).replace(sourceRoot, outRoot);
|
|
61
|
+
if (action) {
|
|
62
|
+
action(name, outPath, projectPath);
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 编译特定文件,并生成到制定目录
|
|
69
|
+
* @param sourcePath 源文件路径
|
|
70
|
+
* @param outDir 输出目录
|
|
71
|
+
* */
|
|
72
|
+
export async function compileFile(sourcePath: string, outDir: string, projectPath?: string) {
|
|
73
|
+
if (!sourcePath) throw errors.argumentNull("sourcePath");
|
|
74
|
+
if (!outDir) throw errors.argumentNull("outDir");
|
|
75
|
+
// if (!fs.existsSync(sourcePath)) throw errors.pathNotExists(sourcePath);
|
|
76
|
+
if (!fs.existsSync(sourcePath)) {
|
|
77
|
+
console.warn(`Path not exists: ${sourcePath}`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let sourceDir = path.dirname(sourcePath);
|
|
82
|
+
let babelOptions: babel.TransformOptions;
|
|
83
|
+
let bablePath: string;
|
|
84
|
+
//= projectPath ?
|
|
85
|
+
// ProjectCompiler.getBabelConfig(projectPath, sourceDir) : ProjectCompiler.getDefaultBabelConfig();
|
|
86
|
+
if (projectPath) {
|
|
87
|
+
let c = ProjectCompiler.getBabelConfig(projectPath, sourceDir);
|
|
88
|
+
bablePath = c.path;
|
|
89
|
+
babelOptions = c.options;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
babelOptions = ProjectCompiler.getDefaultBabelConfig();
|
|
93
|
+
bablePath = '';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
babelOptions.filename = sourcePath;
|
|
97
|
+
babelOptions.code = false;
|
|
98
|
+
babelOptions.ast = true;
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
// let fileResult = babel.transformFileSync(sourcePath, {
|
|
102
|
+
// filename: sourcePath, code: false, ast: true, plugins,
|
|
103
|
+
// presets
|
|
104
|
+
// });
|
|
105
|
+
let fileResult = babel.transformFileSync(sourcePath, babelOptions);
|
|
106
|
+
if (!fileResult)
|
|
107
|
+
throw errors.compileError(sourcePath);
|
|
108
|
+
|
|
109
|
+
let ast = fileResult.ast;
|
|
110
|
+
if (!ast)
|
|
111
|
+
throw errors.compileError(sourcePath);
|
|
112
|
+
|
|
113
|
+
new ImportPathRewrite(sourcePath, ast);
|
|
114
|
+
let r = babel.transformFromAstSync(ast, undefined, {
|
|
115
|
+
filename: sourcePath, plugins: babelOptions.plugins,
|
|
116
|
+
presets: babelOptions.presets, sourceMaps: true
|
|
117
|
+
});
|
|
118
|
+
if (!r || r.code == null)
|
|
119
|
+
throw errors.compileError(sourcePath);
|
|
120
|
+
|
|
121
|
+
let ext = extname(sourcePath);
|
|
122
|
+
let outExt = fileOutExt(sourcePath);
|
|
123
|
+
let targetPath = path.join(outDir, path.basename(sourcePath).replace(ext, outExt));//sourcePath.replace(new RegExp(ext + "$"), outExt).replace(path.dirname(sourcePath), outDir);
|
|
124
|
+
let outDirPath = path.resolve(targetPath, "..");
|
|
125
|
+
|
|
126
|
+
if (r.map) {
|
|
127
|
+
r.map.file = path.basename(targetPath);
|
|
128
|
+
let sources = r.map.sources || [];
|
|
129
|
+
let sourceDir = path.dirname(sourcePath);
|
|
130
|
+
sources.forEach((s, i) => {
|
|
131
|
+
sources[i] = path.relative(outDirPath, path.join(sourceDir, s));
|
|
132
|
+
});
|
|
133
|
+
r.map.sources = sources;
|
|
134
|
+
let mapPath = targetPath + ".map";
|
|
135
|
+
if (!fs.existsSync(outDirPath))
|
|
136
|
+
fs.mkdirSync(outDirPath, { recursive: true });
|
|
137
|
+
|
|
138
|
+
fs.writeFileSync(mapPath, JSON.stringify(r.map));
|
|
139
|
+
r.code += `\n//babel file path = ${bablePath}`;
|
|
140
|
+
r.code += "\n//# sourceMappingURL=" + path.basename(mapPath);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!fs.existsSync(outDirPath))
|
|
144
|
+
fs.mkdirSync(outDirPath, { recursive: true });
|
|
145
|
+
|
|
146
|
+
fs.writeFileSync(targetPath, r.code);
|
|
147
|
+
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function copyFile(filePath: string, outDir: string) {
|
|
151
|
+
if (!filePath) throw errors.argumentNull("filePath");
|
|
152
|
+
if (!outDir) throw errors.argumentNull("outDir");
|
|
153
|
+
|
|
154
|
+
let out = filePath.replace(path.dirname(filePath), outDir);
|
|
155
|
+
let outDirPath = path.resolve(out, "..");
|
|
156
|
+
|
|
157
|
+
fs.mkdirSync(outDirPath, { recursive: true });
|
|
158
|
+
|
|
159
|
+
fs.copyFileSync(filePath, out);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function isIgnoredFile(filePath: string) {
|
|
163
|
+
if (!filePath) throw errors.argumentNull("filePath");
|
|
164
|
+
|
|
165
|
+
let isSkip = skipFiles.some(pattern => new RegExp(pattern).test(filePath));
|
|
166
|
+
return isSkip;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function extname(file: string) {
|
|
170
|
+
// let ext = /\.[a-zA-Z]+/.exec(file)?.[0] || '';
|
|
171
|
+
let ext = path.extname(file);
|
|
172
|
+
return ext;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 获取源文件所对应生成文件的扩展名
|
|
177
|
+
* @param file 源文件名
|
|
178
|
+
* */
|
|
179
|
+
function fileOutExt(file: string) {
|
|
180
|
+
let ext = extname(file);
|
|
181
|
+
if (ext === ".ts")
|
|
182
|
+
return outExt;
|
|
183
|
+
|
|
184
|
+
if (ext === ".tsx")
|
|
185
|
+
return outExt;
|
|
186
|
+
|
|
187
|
+
return ext;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
class ImportPathRewrite {
|
|
191
|
+
constructor(private filePath: string, node: babel.types.File) {
|
|
192
|
+
this.traverse(node);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private traverse(node: babel.types.Node) {
|
|
196
|
+
switch (node.type) {
|
|
197
|
+
case "Program":
|
|
198
|
+
node.body.forEach(c => this.traverse(c));
|
|
199
|
+
break;
|
|
200
|
+
case "File":
|
|
201
|
+
this.traverse(node.program);
|
|
202
|
+
break;
|
|
203
|
+
case "ImportDeclaration":
|
|
204
|
+
if (node.source.type === "StringLiteral" && node.source.value.startsWith(".")) {
|
|
205
|
+
let ext = extname(node.source.value);
|
|
206
|
+
if (ext != outExt) {
|
|
207
|
+
let dir = path.dirname(this.filePath);
|
|
208
|
+
let fullPath = path.join(dir, node.source.value);
|
|
209
|
+
console.log(`ImportDeclaration: ${fullPath} -> ${ext}`)
|
|
210
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
|
|
211
|
+
node.source.value = node.source.value + "/index";
|
|
212
|
+
}
|
|
213
|
+
node.source.value += outExt;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Errors as BaseErrors } from "maishu-toolkit/out/errors";
|
|
2
|
+
|
|
3
|
+
class Errors extends BaseErrors {
|
|
4
|
+
compileError(filePath: string) {
|
|
5
|
+
let error = new Error(`Compile error in file: ${filePath}`);
|
|
6
|
+
error.name = Errors.prototype.compileError.name;
|
|
7
|
+
return error;
|
|
8
|
+
}
|
|
9
|
+
pathNotExists(path: string) {
|
|
10
|
+
let error = new Error(`Path not exists: ${path}`);
|
|
11
|
+
error.name = Errors.prototype.pathNotExists.name;
|
|
12
|
+
return error;
|
|
13
|
+
}
|
|
14
|
+
notAbsolutePath(path: string) {
|
|
15
|
+
let error = new Error(`Path is not absolute: ${path}`);
|
|
16
|
+
error.name = Errors.prototype.notAbsolutePath.name;
|
|
17
|
+
return error;
|
|
18
|
+
}
|
|
19
|
+
invalidBabelConfigExtension(extension: string, configPath: string) {
|
|
20
|
+
let error = new Error(`Invalid babel config file extension: ${extension}, babel config file path: ${configPath}.`);
|
|
21
|
+
error.name = Errors.prototype.invalidBabelConfigExtension.name;
|
|
22
|
+
return error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const errors = new Errors();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
|
|
4
|
+
// const outExt = ".js";
|
|
5
|
+
|
|
6
|
+
export class ImportPathRewrite {
|
|
7
|
+
constructor(private filePath: string, node: babel.types.File, private outExt: string) {
|
|
8
|
+
this.traverse(node);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
private traverse(node: babel.types.Node) {
|
|
12
|
+
switch (node.type) {
|
|
13
|
+
case "Program":
|
|
14
|
+
node.body.forEach(c => this.traverse(c));
|
|
15
|
+
break;
|
|
16
|
+
case "File":
|
|
17
|
+
this.traverse(node.program);
|
|
18
|
+
break;
|
|
19
|
+
case "ImportDeclaration":
|
|
20
|
+
if (node.source.type === "StringLiteral" && node.source.value.startsWith(".")) {
|
|
21
|
+
let ext = this.extname(node.source.value);
|
|
22
|
+
if (ext != this.outExt) {
|
|
23
|
+
let dir = path.dirname(this.filePath);
|
|
24
|
+
let fullPath = path.join(dir, node.source.value);
|
|
25
|
+
console.log(`ImportDeclaration: ${fullPath} -> ${ext}`)
|
|
26
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
|
|
27
|
+
node.source.value = node.source.value + "/index";
|
|
28
|
+
}
|
|
29
|
+
node.source.value += this.outExt;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private extname(file: string) {
|
|
38
|
+
// let ext = /\.[a-zA-Z]+/.exec(file)?.[0] || '';
|
|
39
|
+
let ext = path.extname(file);
|
|
40
|
+
return ext;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { ProjectCompiler } from "./project-compiler";
|
|
4
|
+
describe('project-compiler test', function () {
|
|
5
|
+
|
|
6
|
+
let projectPath = path.join(__dirname, "..", "test");
|
|
7
|
+
let projectCompiler = new ProjectCompiler(projectPath, "src", "out");
|
|
8
|
+
|
|
9
|
+
test("findBabelConfigPath test", function () {
|
|
10
|
+
let staticPath = path.join(projectPath, "src", "static");
|
|
11
|
+
let babelConfigPath = ProjectCompiler.findBabelConfigPath(projectPath, staticPath);
|
|
12
|
+
expect(babelConfigPath).toBe(path.join(staticPath, "babel.config.json"));
|
|
13
|
+
|
|
14
|
+
let componentsPath = path.join(staticPath, "components");
|
|
15
|
+
expect(fs.existsSync(componentsPath)).toBeTruthy();
|
|
16
|
+
babelConfigPath = ProjectCompiler.findBabelConfigPath(projectPath, componentsPath);
|
|
17
|
+
expect(babelConfigPath).toBe(path.join(staticPath, "babel.config.json"));
|
|
18
|
+
|
|
19
|
+
let designPath = path.join(staticPath, "design");
|
|
20
|
+
babelConfigPath = ProjectCompiler.findBabelConfigPath(projectPath, designPath);
|
|
21
|
+
expect(babelConfigPath).toBe(path.join(designPath, "babel.config.js"));
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test("loadBabelConfig test", function () {
|
|
25
|
+
|
|
26
|
+
let staticPath = path.join(projectPath, "src", "static");
|
|
27
|
+
let designPath = path.join(staticPath, "design");
|
|
28
|
+
let babelConfigPath = ProjectCompiler.findBabelConfigPath(projectPath, designPath);
|
|
29
|
+
expect(babelConfigPath).not.toBe(null);
|
|
30
|
+
let babelConfig = ProjectCompiler.loadBabelConfig(babelConfigPath as string);
|
|
31
|
+
expect(babelConfig).not.toBe(undefined);
|
|
32
|
+
expect(babelConfig.presets).not.toBe(undefined);
|
|
33
|
+
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
});
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import * as nodemon from "nodemon";
|
|
2
|
+
import { errors } from "./errors";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import * as babel from "@babel/core";
|
|
6
|
+
import { ImportPathRewrite } from "./import-path-rewrite";
|
|
7
|
+
import watch from "node-watch";
|
|
8
|
+
|
|
9
|
+
const tsOutExt = ".js";
|
|
10
|
+
|
|
11
|
+
type FileAction = (filePath: string, outDir: string) => void;
|
|
12
|
+
|
|
13
|
+
export class ProjectCompiler {
|
|
14
|
+
|
|
15
|
+
private fileActions: { [ext: string]: FileAction };
|
|
16
|
+
private skipFiles = ["\\S+\\.d\\.tsx?$", "\\S+\\.test\\.tsx?$", "\\S+\\.spec\\.tsx?$"];
|
|
17
|
+
|
|
18
|
+
constructor(private projectPath: string, private sourceDirectoryNanme: string, private outputDirectoryName: string) {
|
|
19
|
+
if (!projectPath) throw errors.argumentNull("projectPath");
|
|
20
|
+
if (!sourceDirectoryNanme) throw errors.argumentNull("sourceDirectoryNanme");
|
|
21
|
+
if (!outputDirectoryName) throw errors.argumentNull("outputDirectoryName");
|
|
22
|
+
|
|
23
|
+
if (!path.isAbsolute(projectPath))
|
|
24
|
+
throw errors.notAbsolutePath(projectPath);
|
|
25
|
+
|
|
26
|
+
let sourcePath = path.join(projectPath, sourceDirectoryNanme);
|
|
27
|
+
if (!fs.existsSync(sourcePath))
|
|
28
|
+
throw errors.pathNotExists(sourcePath);
|
|
29
|
+
|
|
30
|
+
this.fileActions = {
|
|
31
|
+
".ts": this.compileFile.bind(this),
|
|
32
|
+
".tsx": this.compileFile.bind(this),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 编译特定文件,并生成到制定目录
|
|
39
|
+
* @param sourcePath 源文件路径
|
|
40
|
+
* @param outDir 输出目录
|
|
41
|
+
* */
|
|
42
|
+
async compileFile(filePath: string, outDir: string) {
|
|
43
|
+
this.compileTypeScriptFileByBabel(filePath, outDir);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private async compileTypeScriptFileByBabel(sourcePath: string, outDir: string) {
|
|
47
|
+
if (!sourcePath) throw errors.argumentNull("sourcePath");
|
|
48
|
+
if (!outDir) throw errors.argumentNull("outDir");
|
|
49
|
+
if (!fs.existsSync(sourcePath)) throw errors.pathNotExists(sourcePath);
|
|
50
|
+
|
|
51
|
+
let sourceDir = path.dirname(sourcePath);
|
|
52
|
+
let babelConfig = ProjectCompiler.getBabelConfig(this.projectPath, sourceDir);
|
|
53
|
+
let babelOptions = babelConfig.options;
|
|
54
|
+
let babelPath = babelConfig.path;
|
|
55
|
+
babelOptions.filename = sourcePath;
|
|
56
|
+
babelOptions.code = false;
|
|
57
|
+
babelOptions.ast = true;
|
|
58
|
+
|
|
59
|
+
let fileResult = babel.transformFileSync(sourcePath, babelOptions);
|
|
60
|
+
if (!fileResult)
|
|
61
|
+
throw errors.compileError(sourcePath);
|
|
62
|
+
|
|
63
|
+
let ast = fileResult.ast;
|
|
64
|
+
if (!ast)
|
|
65
|
+
throw errors.compileError(sourcePath);
|
|
66
|
+
|
|
67
|
+
new ImportPathRewrite(sourcePath, ast, tsOutExt);
|
|
68
|
+
let r = babel.transformFromAstSync(ast, undefined, {
|
|
69
|
+
filename: sourcePath, plugins: babelOptions.plugins,
|
|
70
|
+
presets: babelOptions.presets, sourceMaps: true
|
|
71
|
+
});
|
|
72
|
+
if (!r || r.code == null)
|
|
73
|
+
throw errors.compileError(sourcePath);
|
|
74
|
+
|
|
75
|
+
let ext = path.extname(sourcePath);
|
|
76
|
+
let outExt = this.fileOutExt(sourcePath);
|
|
77
|
+
let targetPath = path.join(outDir, path.basename(sourcePath).replace(ext, outExt));
|
|
78
|
+
let outDirPath = path.resolve(targetPath, "..");
|
|
79
|
+
|
|
80
|
+
if (r.map) {
|
|
81
|
+
r.map.file = path.basename(targetPath);
|
|
82
|
+
let sources = r.map.sources || [];
|
|
83
|
+
let sourceDir = path.dirname(sourcePath);
|
|
84
|
+
sources.forEach((s, i) => {
|
|
85
|
+
sources[i] = path.relative(outDirPath, path.join(sourceDir, s));
|
|
86
|
+
});
|
|
87
|
+
r.map.sources = sources;
|
|
88
|
+
let mapPath = targetPath + ".map";
|
|
89
|
+
if (!fs.existsSync(outDirPath))
|
|
90
|
+
fs.mkdirSync(outDirPath, { recursive: true });
|
|
91
|
+
|
|
92
|
+
fs.writeFileSync(mapPath, JSON.stringify(r.map));
|
|
93
|
+
r.code += `\n//babelPath:${babelPath}`;
|
|
94
|
+
r.code += "\n//# sourceMappingURL=" + path.basename(mapPath);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!fs.existsSync(outDirPath))
|
|
98
|
+
fs.mkdirSync(outDirPath, { recursive: true });
|
|
99
|
+
|
|
100
|
+
fs.writeFileSync(targetPath, r.code);
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static loadBabelConfig(configPath: string): babel.TransformOptions {
|
|
105
|
+
if (!configPath) throw errors.argumentNull("configPath");
|
|
106
|
+
if (!fs.existsSync(configPath)) throw errors.pathNotExists(configPath);
|
|
107
|
+
|
|
108
|
+
let ext = path.extname(configPath);
|
|
109
|
+
if (ext === ".json") {
|
|
110
|
+
let content = fs.readFileSync(configPath, "utf-8");
|
|
111
|
+
return JSON.parse(content);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (ext === ".js") {
|
|
115
|
+
let module = require(configPath);
|
|
116
|
+
let config = Object.assign({}, module.default || module);
|
|
117
|
+
return config;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
throw errors.invalidBabelConfigExtension(ext, configPath);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static findBabelConfigPath(projectPath: string, directoryPath: string): string | null {
|
|
124
|
+
if (!path.isAbsolute(directoryPath)) {
|
|
125
|
+
directoryPath = path.join(projectPath, directoryPath);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.assert(path.isAbsolute(projectPath), `projectPath ${projectPath} is not absolute`);
|
|
129
|
+
console.assert(path.isAbsolute(directoryPath), `directoryPath ${directoryPath} is not absolute`);
|
|
130
|
+
let jsonConfigPath = path.join(directoryPath, "babel.config.json");
|
|
131
|
+
let jsConfigPath = path.join(directoryPath, "babel.config.js");
|
|
132
|
+
if (fs.existsSync(jsConfigPath)) {
|
|
133
|
+
return jsConfigPath;
|
|
134
|
+
}
|
|
135
|
+
if (fs.existsSync(jsonConfigPath)) {
|
|
136
|
+
return jsonConfigPath;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (projectPath == directoryPath)
|
|
140
|
+
return null;
|
|
141
|
+
|
|
142
|
+
let parentPath = path.resolve(directoryPath, "..");
|
|
143
|
+
|
|
144
|
+
return this.findBabelConfigPath(projectPath, parentPath);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static getBabelConfig(projectPath: string, directoryPath: string): { options: babel.TransformOptions, path: string } {
|
|
148
|
+
let configPath = this.findBabelConfigPath(projectPath, directoryPath);
|
|
149
|
+
let babelOptions: babel.TransformOptions;
|
|
150
|
+
if (configPath) {
|
|
151
|
+
babelOptions = this.loadBabelConfig(configPath);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
|
|
155
|
+
babelOptions = this.getDefaultBabelConfig();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
babelOptions.ast = true;
|
|
159
|
+
babelOptions.code = false;
|
|
160
|
+
|
|
161
|
+
return { options: babelOptions, path: configPath || '' };
|
|
162
|
+
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
static getDefaultBabelConfig(): babel.TransformOptions {
|
|
166
|
+
let plugins = [
|
|
167
|
+
"babel-plugin-transform-typescript-metadata",
|
|
168
|
+
["@babel/plugin-proposal-decorators", { legacy: true }],
|
|
169
|
+
["@babel/plugin-transform-typescript", { isTSX: true }],
|
|
170
|
+
["@babel/plugin-transform-react-jsx", { "pragma": "React.createElement", "pragmaFrag": "React.Fragment" }],
|
|
171
|
+
];
|
|
172
|
+
let presets = [
|
|
173
|
+
['@babel/preset-env', { targets: { node: 'current' } }],
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
let babelOptions = { plugins, presets };
|
|
177
|
+
return babelOptions;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 获取源文件所对应生成文件的扩展名
|
|
182
|
+
* @param file 源文件名
|
|
183
|
+
* */
|
|
184
|
+
private fileOutExt(file: string) {
|
|
185
|
+
let ext = path.extname(file);
|
|
186
|
+
if (ext === ".ts")
|
|
187
|
+
return tsOutExt;
|
|
188
|
+
|
|
189
|
+
if (ext === ".tsx")
|
|
190
|
+
return tsOutExt;
|
|
191
|
+
|
|
192
|
+
return ext;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private isIgnoredFile(filePath: string) {
|
|
196
|
+
if (!filePath) throw errors.argumentNull("filePath");
|
|
197
|
+
|
|
198
|
+
let isSkip = this.skipFiles.some(pattern => new RegExp(pattern).test(filePath));
|
|
199
|
+
return isSkip;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
generateCode(): void;
|
|
203
|
+
generateCode(sourceDir: string, outDir: string): void;
|
|
204
|
+
generateCode(sourceDir?: string, outDir?: string): void {
|
|
205
|
+
|
|
206
|
+
// if (!sourceDir) throw errors.argumentNull("sourceDir");
|
|
207
|
+
// if (!outDir) throw errors.argumentNull("outDir");
|
|
208
|
+
if (!sourceDir)
|
|
209
|
+
sourceDir = path.join(this.projectPath, this.sourceDirectoryNanme);
|
|
210
|
+
|
|
211
|
+
if (!outDir)
|
|
212
|
+
outDir = path.join(this.projectPath, this.outputDirectoryName);
|
|
213
|
+
|
|
214
|
+
let fileActions = this.fileActions;
|
|
215
|
+
|
|
216
|
+
if (!fs.existsSync(sourceDir))
|
|
217
|
+
throw errors.pathNotExists(sourceDir);
|
|
218
|
+
|
|
219
|
+
let files = fs.readdirSync(sourceDir);
|
|
220
|
+
for (let file of files) {
|
|
221
|
+
let filePath = path.join(sourceDir, file);
|
|
222
|
+
if (!fs.statSync(filePath).isFile()) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let isSkip = this.isIgnoredFile(filePath);//skipFiles.some(pattern => new RegExp(pattern).test(filePath));
|
|
227
|
+
if (isSkip) {
|
|
228
|
+
console.log(`Skip ${filePath}`);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let ext = path.extname(file);
|
|
233
|
+
let action = fileActions[ext];
|
|
234
|
+
if (action) {
|
|
235
|
+
action(filePath, outDir);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let dirs = fs.readdirSync(sourceDir);
|
|
240
|
+
for (let dir of dirs) {
|
|
241
|
+
let fullPath = path.join(sourceDir, dir);
|
|
242
|
+
let outDirPath = path.join(outDir, dir);
|
|
243
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
244
|
+
this.generateCode(fullPath, outDirPath);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
watchDirectory() {//sourceRoot: string, outRoot: string
|
|
250
|
+
watch(this.sourceDirectoryNanme, { recursive: true }, async (evt, name) => {
|
|
251
|
+
let action = this.fileActions[path.extname(name)];
|
|
252
|
+
let outPath = path.dirname(name).replace(this.sourceDirectoryNanme, this.outputDirectoryName);
|
|
253
|
+
if (action) {
|
|
254
|
+
action(name, outPath);
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
run() {
|
|
261
|
+
nodemon({
|
|
262
|
+
script: `./${this.outputDirectoryName}/main.js`,
|
|
263
|
+
watch: [`./${this.outputDirectoryName}/`],
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
}
|
package/modules/run.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "maishu-scripts",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "用于对 node 项目进行代码生成,打包,运行",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@babel/core": "^7.24.3",
|
|
7
|
+
"@babel/plugin-proposal-decorators": "^7.24.1",
|
|
8
|
+
"@babel/plugin-transform-react-jsx": "^7.23.4",
|
|
9
|
+
"@babel/plugin-transform-typescript": "^7.24.1",
|
|
10
|
+
"@types/node": "^20.12.2",
|
|
11
|
+
"babel-plugin-transform-typescript-metadata": "^0.3.2",
|
|
12
|
+
"maishu-toolkit": "^1.12.6",
|
|
13
|
+
"node-watch": "^0.7.4",
|
|
14
|
+
"nodemon": "^3.1.0",
|
|
15
|
+
"typescript": "^5.4.5"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@babel/preset-env": "^7.24.3",
|
|
19
|
+
"@babel/preset-typescript": "^7.24.1",
|
|
20
|
+
"@types/babel__core": "^7.20.5",
|
|
21
|
+
"@types/jest": "^29.5.12",
|
|
22
|
+
"jest": "^29.7.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/start.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Controller, Get } from '@nestjs/common';
|
|
2
|
+
import { AppService } from './app.service';
|
|
3
|
+
|
|
4
|
+
@Controller()
|
|
5
|
+
export class AppController {
|
|
6
|
+
constructor(private readonly appService: AppService) {}
|
|
7
|
+
|
|
8
|
+
@Get()
|
|
9
|
+
getHello(): string {
|
|
10
|
+
return this.appService.getHello();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common';
|
|
2
|
+
import { AppController } from './app.controller';
|
|
3
|
+
import { AppService } from './app.service';
|
|
4
|
+
import { ProxyMiddleware } from './middleware/proxy';
|
|
5
|
+
import { ProxyController } from './controllers/proxy';
|
|
6
|
+
|
|
7
|
+
@Module({
|
|
8
|
+
imports: [],
|
|
9
|
+
controllers: [AppController],
|
|
10
|
+
providers: [AppService],
|
|
11
|
+
})
|
|
12
|
+
export class AppModule implements NestModule {
|
|
13
|
+
configure(consumer: MiddlewareConsumer) {
|
|
14
|
+
consumer.apply(ProxyMiddleware)
|
|
15
|
+
.forRoutes({ path: "proxy/*", method: RequestMethod.GET });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//const config = require("../config.js").default as Config;
|
|
2
|
+
import config from "../config.mjs";
|
|
3
|
+
|
|
4
|
+
export interface Config {
|
|
5
|
+
plmUrl: string,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// TODO: 校验配置项是否正确
|
|
9
|
+
if (!config || !config.plmUrl)
|
|
10
|
+
throw new Error("plmUrl is required in config.js");
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export function getConfig(): Config {
|
|
15
|
+
return config;
|
|
16
|
+
}
|
package/test/src/main.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { get, request } from 'http';
|
|
3
|
+
import { getConfig } from '../config';
|
|
4
|
+
|
|
5
|
+
let config = getConfig();
|
|
6
|
+
export class ProxyMiddleware {
|
|
7
|
+
constructor() { }
|
|
8
|
+
|
|
9
|
+
static rootPath = '/proxy/';
|
|
10
|
+
|
|
11
|
+
async use(req: Request, res: Response, next: NextFunction) {
|
|
12
|
+
const { url, headers, method, body } = req;
|
|
13
|
+
|
|
14
|
+
let targetUrl = url.replace(ProxyMiddleware.rootPath, '/');
|
|
15
|
+
targetUrl = config.plmUrl + targetUrl;
|
|
16
|
+
console.log('targetUrl', targetUrl);
|
|
17
|
+
|
|
18
|
+
const options = {
|
|
19
|
+
url: targetUrl,
|
|
20
|
+
headers: headers,
|
|
21
|
+
method: method,
|
|
22
|
+
body: body
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// url = url.replace(ProxyMiddleware.rootPath, '');
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
const proxyReq = request(options, (proxyRes) => {
|
|
29
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
30
|
+
proxyRes.pipe(res);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
req.pipe(proxyReq);
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
}
|