maishu-scripts 1.4.1 → 1.4.5
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/out/index.d.ts +2 -4
- package/out/index.js +4 -2
- package/out/modules/actions/babel-compile.d.ts +11 -1
- package/out/modules/actions/babel-compile.js +76 -55
- package/out/modules/actions/copy-file.js +12 -4
- package/out/modules/compile.d.ts +1 -0
- package/out/modules/compile.js +9 -5
- package/out/modules/configs.d.ts +3 -3
- package/out/modules/configs.js +7 -4
- package/out/modules/errors.d.ts +1 -0
- package/out/modules/errors.js +8 -0
- package/out/package.json +33 -0
- package/package.json +4 -3
- package/src/index.ts +2 -1
- package/src/modules/actions/babel-compile.test.ts +436 -0
- package/src/modules/actions/babel-compile.ts +80 -64
- package/src/modules/actions/copy-file.ts +12 -4
- package/src/modules/compile.test.ts +236 -189
- package/src/modules/compile.ts +14 -7
- package/src/modules/configs.ts +5 -6
- package/src/modules/errors.ts +8 -0
- package/src/modules/project-compiler.test.ts +245 -40
- package/out/modules/actions/biel-compile.d.ts +0 -7
- package/out/modules/actions/biel-compile.js +0 -153
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import { create, generateDeclarationFile } from './babel-compile';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
|
|
5
|
+
describe('compileFile (via create)', () => {
|
|
6
|
+
let testDir: string;
|
|
7
|
+
let fixturesDir: string;
|
|
8
|
+
let outDir: string;
|
|
9
|
+
let sourceFile: string;
|
|
10
|
+
|
|
11
|
+
const defaultBabelOptions: any = {
|
|
12
|
+
presets: ['@babel/preset-typescript'],
|
|
13
|
+
ast: true,
|
|
14
|
+
code: false,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
beforeAll(() => {
|
|
18
|
+
fixturesDir = path.join(__dirname, '..', '..', '..', 'test', 'fixtures', 'compile');
|
|
19
|
+
outDir = path.join(fixturesDir, 'out');
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(fixturesDir)) {
|
|
22
|
+
fs.mkdirSync(fixturesDir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
sourceFile = path.join(fixturesDir, 'simple.ts');
|
|
26
|
+
const sourceContent = `
|
|
27
|
+
export interface Config {
|
|
28
|
+
name: string;
|
|
29
|
+
value: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function greet(name: string): string {
|
|
33
|
+
return \`Hello, \${name}!\`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const VERSION = '1.0.0';
|
|
37
|
+
`;
|
|
38
|
+
fs.writeFileSync(sourceFile, sourceContent);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
afterEach(async () => {
|
|
42
|
+
if (fs.existsSync(outDir)) {
|
|
43
|
+
fs.rmSync(outDir, { recursive: true, force: true });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterAll(() => {
|
|
48
|
+
if (fs.existsSync(sourceFile)) {
|
|
49
|
+
fs.rmSync(sourceFile, { force: true });
|
|
50
|
+
}
|
|
51
|
+
if (fs.existsSync(fixturesDir)) {
|
|
52
|
+
fs.rmSync(fixturesDir, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('basic compilation', () => {
|
|
57
|
+
test('should compile .ts file and generate output', async () => {
|
|
58
|
+
expect(fs.existsSync(sourceFile)).toBeTruthy();
|
|
59
|
+
|
|
60
|
+
const compileFile = create({
|
|
61
|
+
defaultBabelOptions: {
|
|
62
|
+
...defaultBabelOptions,
|
|
63
|
+
sourceMaps: false,
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await compileFile(sourceFile, outDir, fixturesDir);
|
|
68
|
+
|
|
69
|
+
const expectedOutput = path.join(outDir, 'simple.js');
|
|
70
|
+
expect(fs.existsSync(expectedOutput)).toBeTruthy();
|
|
71
|
+
|
|
72
|
+
const content = fs.readFileSync(expectedOutput, 'utf-8');
|
|
73
|
+
expect(content.length).toBeGreaterThan(0);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('should compile .ts file with source maps', async () => {
|
|
77
|
+
const compileFile = create({
|
|
78
|
+
defaultBabelOptions: {
|
|
79
|
+
...defaultBabelOptions,
|
|
80
|
+
sourceMaps: true,
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await compileFile(sourceFile, outDir, fixturesDir);
|
|
85
|
+
|
|
86
|
+
const expectedOutput = path.join(outDir, 'simple.js');
|
|
87
|
+
expect(fs.existsSync(expectedOutput)).toBeTruthy();
|
|
88
|
+
|
|
89
|
+
const mapFile = path.join(outDir, 'simple.js.map');
|
|
90
|
+
expect(fs.existsSync(mapFile)).toBeTruthy();
|
|
91
|
+
|
|
92
|
+
const mapContent = JSON.parse(fs.readFileSync(mapFile, 'utf-8'));
|
|
93
|
+
expect(mapContent.file).toBe('simple.js');
|
|
94
|
+
expect(mapContent.sources).toBeDefined();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('should create output directory if not exists', async () => {
|
|
98
|
+
const nestedOutDir = path.join(fixturesDir, 'nested', 'deep', 'out');
|
|
99
|
+
|
|
100
|
+
const compileFile = create({
|
|
101
|
+
defaultBabelOptions
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
await compileFile(sourceFile, nestedOutDir, fixturesDir);
|
|
105
|
+
|
|
106
|
+
const expectedOutput = path.join(nestedOutDir, 'simple.js');
|
|
107
|
+
expect(fs.existsSync(expectedOutput)).toBeTruthy();
|
|
108
|
+
|
|
109
|
+
fs.rmSync(path.join(fixturesDir, 'nested'), { recursive: true, force: true });
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('error handling', () => {
|
|
114
|
+
test('should warn and return early when source file does not exist', async () => {
|
|
115
|
+
const nonExistentFile = path.join(fixturesDir, 'non-existent.ts');
|
|
116
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
117
|
+
|
|
118
|
+
const compileFile = create({
|
|
119
|
+
defaultBabelOptions
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await compileFile(nonExistentFile, outDir, fixturesDir);
|
|
123
|
+
|
|
124
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Path not exists'));
|
|
125
|
+
warnSpy.mockRestore();
|
|
126
|
+
|
|
127
|
+
const expectedOutput = path.join(outDir, 'non-existent.js');
|
|
128
|
+
expect(fs.existsSync(expectedOutput)).toBeFalsy();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('should throw error when babel transform fails', async () => {
|
|
132
|
+
const invalidFile = path.join(fixturesDir, 'invalid.ts');
|
|
133
|
+
fs.writeFileSync(invalidFile, 'const x: = invalid syntax;');
|
|
134
|
+
|
|
135
|
+
const compileFile = create({
|
|
136
|
+
defaultBabelOptions
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
await expect(compileFile(invalidFile, outDir, fixturesDir)).rejects.toThrow();
|
|
140
|
+
|
|
141
|
+
fs.rmSync(invalidFile, { force: true });
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('output extensions', () => {
|
|
146
|
+
test('should output .mjs file when specified', async () => {
|
|
147
|
+
const compileFile = create({
|
|
148
|
+
defaultBabelOptions,
|
|
149
|
+
outExt: '.mjs'
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await compileFile(sourceFile, outDir, fixturesDir);
|
|
153
|
+
|
|
154
|
+
const expectedOutput = path.join(outDir, 'simple.mjs');
|
|
155
|
+
expect(fs.existsSync(expectedOutput)).toBeTruthy();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('should output .cjs file when specified', async () => {
|
|
159
|
+
const compileFile = create({
|
|
160
|
+
defaultBabelOptions,
|
|
161
|
+
outExt: '.cjs'
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
await compileFile(sourceFile, outDir, fixturesDir);
|
|
165
|
+
|
|
166
|
+
const expectedOutput = path.join(outDir, 'simple.cjs');
|
|
167
|
+
expect(fs.existsSync(expectedOutput)).toBeTruthy();
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('declaration file generation', () => {
|
|
172
|
+
test('should generate declaration file when generateDeclaration is true', async () => {
|
|
173
|
+
const declSourceFile = path.join(fixturesDir, 'decl-test.ts');
|
|
174
|
+
const sourceContent = `
|
|
175
|
+
export interface User {
|
|
176
|
+
id: number;
|
|
177
|
+
name: string;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function greet(name: string): string {
|
|
181
|
+
return \`Hello, \${name}\`;
|
|
182
|
+
}
|
|
183
|
+
`;
|
|
184
|
+
fs.writeFileSync(declSourceFile, sourceContent);
|
|
185
|
+
|
|
186
|
+
const compileFile = create({
|
|
187
|
+
defaultBabelOptions,
|
|
188
|
+
generateDeclaration: true
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await compileFile(declSourceFile, outDir, fixturesDir);
|
|
192
|
+
|
|
193
|
+
const expectedJs = path.join(outDir, 'decl-test.js');
|
|
194
|
+
const expectedDecl = path.join(outDir, 'decl-test.d.ts');
|
|
195
|
+
|
|
196
|
+
expect(fs.existsSync(expectedJs)).toBeTruthy();
|
|
197
|
+
expect(fs.existsSync(expectedDecl)).toBeTruthy();
|
|
198
|
+
|
|
199
|
+
const declContent = fs.readFileSync(expectedDecl, 'utf-8');
|
|
200
|
+
expect(declContent).toContain('User');
|
|
201
|
+
expect(declContent).toContain('greet');
|
|
202
|
+
|
|
203
|
+
fs.rmSync(declSourceFile, { force: true });
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('should not generate declaration file when generateDeclaration is false', async () => {
|
|
207
|
+
const noDeclSourceFile = path.join(fixturesDir, 'no-decl-test.ts');
|
|
208
|
+
const sourceContent = `
|
|
209
|
+
export interface Config {
|
|
210
|
+
key: string;
|
|
211
|
+
}
|
|
212
|
+
`;
|
|
213
|
+
fs.writeFileSync(noDeclSourceFile, sourceContent);
|
|
214
|
+
|
|
215
|
+
const compileFile = create({
|
|
216
|
+
defaultBabelOptions,
|
|
217
|
+
generateDeclaration: false
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
await compileFile(noDeclSourceFile, outDir, fixturesDir);
|
|
221
|
+
|
|
222
|
+
const expectedJs = path.join(outDir, 'no-decl-test.js');
|
|
223
|
+
const expectedDecl = path.join(outDir, 'no-decl-test.d.ts');
|
|
224
|
+
|
|
225
|
+
expect(fs.existsSync(expectedJs)).toBeTruthy();
|
|
226
|
+
expect(fs.existsSync(expectedDecl)).toBeFalsy();
|
|
227
|
+
|
|
228
|
+
fs.rmSync(noDeclSourceFile, { force: true });
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('source map behavior', () => {
|
|
233
|
+
test('should generate map file when sourceMaps is enabled', async () => {
|
|
234
|
+
const compileWithMaps = create({
|
|
235
|
+
defaultBabelOptions: {
|
|
236
|
+
...defaultBabelOptions,
|
|
237
|
+
sourceMaps: true,
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
await compileWithMaps(sourceFile, outDir, fixturesDir);
|
|
241
|
+
|
|
242
|
+
const mapFile = path.join(outDir, 'simple.js.map');
|
|
243
|
+
expect(fs.existsSync(mapFile)).toBeTruthy();
|
|
244
|
+
|
|
245
|
+
const outputFile = path.join(outDir, 'simple.js');
|
|
246
|
+
const content = fs.readFileSync(outputFile, 'utf-8');
|
|
247
|
+
expect(content).toContain('sourceMappingURL=');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test('should not generate map file when sourceMaps is disabled', async () => {
|
|
251
|
+
const compileWithoutMaps = create({
|
|
252
|
+
defaultBabelOptions: {
|
|
253
|
+
...defaultBabelOptions,
|
|
254
|
+
sourceMaps: false,
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
await compileWithoutMaps(sourceFile, outDir, fixturesDir);
|
|
258
|
+
|
|
259
|
+
const outputFile = path.join(outDir, 'simple.js');
|
|
260
|
+
expect(fs.existsSync(outputFile)).toBeTruthy();
|
|
261
|
+
|
|
262
|
+
const content = fs.readFileSync(outputFile, 'utf-8');
|
|
263
|
+
expect(content).not.toContain('sourceMappingURL=');
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
describe('generateDeclarationFile', () => {
|
|
269
|
+
let testDir: string;
|
|
270
|
+
let fixturesDir: string;
|
|
271
|
+
|
|
272
|
+
beforeAll(() => {
|
|
273
|
+
testDir = path.join(__dirname, '..', '..', '..', 'test');
|
|
274
|
+
fixturesDir = path.join(testDir, 'fixtures', 'declaration');
|
|
275
|
+
|
|
276
|
+
if (!fs.existsSync(fixturesDir)) {
|
|
277
|
+
fs.mkdirSync(fixturesDir, { recursive: true });
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
afterEach(() => {
|
|
282
|
+
if (fs.existsSync(fixturesDir)) {
|
|
283
|
+
fs.readdirSync(fixturesDir).forEach(file => {
|
|
284
|
+
fs.rmSync(path.join(fixturesDir, file), { force: true });
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
afterAll(() => {
|
|
290
|
+
if (fs.existsSync(fixturesDir)) {
|
|
291
|
+
fs.rmSync(fixturesDir, { recursive: true, force: true });
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('should generate declaration file for .ts source', async () => {
|
|
296
|
+
const sourcePath = path.join(fixturesDir, 'sample.ts');
|
|
297
|
+
fs.writeFileSync(sourcePath, 'export const x: number = 1;');
|
|
298
|
+
|
|
299
|
+
const targetPath = path.join(fixturesDir, 'sample.js');
|
|
300
|
+
const outExt: '.js' = '.js';
|
|
301
|
+
const expectedDeclPath = path.join(fixturesDir, 'sample.d.ts');
|
|
302
|
+
|
|
303
|
+
await generateDeclarationFile(path.resolve(), sourcePath, targetPath, outExt);
|
|
304
|
+
|
|
305
|
+
expect(fs.existsSync(expectedDeclPath)).toBeTruthy();
|
|
306
|
+
const content = fs.readFileSync(expectedDeclPath, 'utf-8');
|
|
307
|
+
expect(content.length).toBeGreaterThan(0);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test('should generate declaration file for .tsx source', async () => {
|
|
311
|
+
const tsxSource = path.join(fixturesDir, 'component.tsx');
|
|
312
|
+
const tsxContent = `
|
|
313
|
+
export interface ComponentProps {
|
|
314
|
+
name: string;
|
|
315
|
+
value?: number;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function Component(props: ComponentProps): string {
|
|
319
|
+
return props.name;
|
|
320
|
+
}
|
|
321
|
+
`;
|
|
322
|
+
fs.writeFileSync(tsxSource, tsxContent);
|
|
323
|
+
|
|
324
|
+
const targetPath = path.join(fixturesDir, 'component.js');
|
|
325
|
+
const outExt: '.js' = '.js';
|
|
326
|
+
const expectedDeclPath = path.join(fixturesDir, 'component.d.ts');
|
|
327
|
+
|
|
328
|
+
await generateDeclarationFile(path.resolve(), tsxSource, targetPath, outExt);
|
|
329
|
+
|
|
330
|
+
expect(fs.existsSync(expectedDeclPath)).toBeTruthy();
|
|
331
|
+
const content = fs.readFileSync(expectedDeclPath, 'utf-8');
|
|
332
|
+
expect(content).toContain('ComponentProps');
|
|
333
|
+
expect(content).toContain('Component');
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test('should skip for .js source file', async () => {
|
|
337
|
+
const sourcePath = path.join(fixturesDir, 'sample.js');
|
|
338
|
+
fs.writeFileSync(sourcePath, 'const x = 1;');
|
|
339
|
+
|
|
340
|
+
const targetPath = path.join(fixturesDir, 'sample.js');
|
|
341
|
+
const outExt: '.js' = '.js';
|
|
342
|
+
const expectedDeclPath = path.join(fixturesDir, 'sample.d.ts');
|
|
343
|
+
|
|
344
|
+
await generateDeclarationFile(path.resolve(), sourcePath, targetPath, outExt);
|
|
345
|
+
|
|
346
|
+
expect(fs.existsSync(expectedDeclPath)).toBeFalsy();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test('should skip for .mjs source file', async () => {
|
|
350
|
+
const sourcePath = path.join(fixturesDir, 'sample.mjs');
|
|
351
|
+
fs.writeFileSync(sourcePath, 'export const x = 1;');
|
|
352
|
+
|
|
353
|
+
const targetPath = path.join(fixturesDir, 'sample.mjs');
|
|
354
|
+
const outExt: '.mjs' = '.mjs';
|
|
355
|
+
const expectedDeclPath = path.join(fixturesDir, 'sample.d.ts');
|
|
356
|
+
|
|
357
|
+
await generateDeclarationFile(path.resolve(), sourcePath, targetPath, outExt);
|
|
358
|
+
|
|
359
|
+
expect(fs.existsSync(expectedDeclPath)).toBeFalsy();
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test('should handle .cjs extension correctly', async () => {
|
|
363
|
+
const sourcePath = path.join(fixturesDir, 'sample.cjs');
|
|
364
|
+
fs.writeFileSync(sourcePath, 'module.exports = {};');
|
|
365
|
+
|
|
366
|
+
const targetPath = path.join(fixturesDir, 'sample.cjs');
|
|
367
|
+
const outExt: '.cjs' = '.cjs';
|
|
368
|
+
const expectedDeclPath = path.join(fixturesDir, 'sample.d.ts');
|
|
369
|
+
|
|
370
|
+
await generateDeclarationFile(path.resolve(), sourcePath, targetPath, outExt);
|
|
371
|
+
|
|
372
|
+
expect(fs.existsSync(expectedDeclPath)).toBeFalsy();
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
test('should generate declaration for module with exports', async () => {
|
|
376
|
+
const sourcePath = path.join(fixturesDir, 'exports.ts');
|
|
377
|
+
const exportsContent = `
|
|
378
|
+
export interface User {
|
|
379
|
+
id: number;
|
|
380
|
+
name: string;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export type Status = 'active' | 'inactive';
|
|
384
|
+
|
|
385
|
+
export function greet(name: string): string {
|
|
386
|
+
return \`Hello, \${name}!\`;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export const VERSION = '1.0.0';
|
|
390
|
+
`;
|
|
391
|
+
fs.writeFileSync(sourcePath, exportsContent);
|
|
392
|
+
|
|
393
|
+
const targetPath = path.join(fixturesDir, 'exports.js');
|
|
394
|
+
const outExt: '.js' = '.js';
|
|
395
|
+
const expectedDeclPath = path.join(fixturesDir, 'exports.d.ts');
|
|
396
|
+
|
|
397
|
+
await generateDeclarationFile(path.resolve(), sourcePath, targetPath, outExt);
|
|
398
|
+
|
|
399
|
+
expect(fs.existsSync(expectedDeclPath)).toBeTruthy();
|
|
400
|
+
const content = fs.readFileSync(expectedDeclPath, 'utf-8');
|
|
401
|
+
expect(content).toContain('User');
|
|
402
|
+
expect(content).toContain('Status');
|
|
403
|
+
expect(content).toContain('greet');
|
|
404
|
+
expect(content).toContain('VERSION');
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test('should handle file with type errors and still generate declaration', async () => {
|
|
408
|
+
const sourcePath = path.join(fixturesDir, 'with-error.ts');
|
|
409
|
+
const errorContent = `
|
|
410
|
+
export function add(a: number, b: string): number {
|
|
411
|
+
return a + b;
|
|
412
|
+
}
|
|
413
|
+
`;
|
|
414
|
+
fs.writeFileSync(sourcePath, errorContent);
|
|
415
|
+
|
|
416
|
+
const targetPath = path.join(fixturesDir, 'with-error.js');
|
|
417
|
+
const outExt: '.js' = '.js';
|
|
418
|
+
const expectedDeclPath = path.join(fixturesDir, 'with-error.d.ts');
|
|
419
|
+
|
|
420
|
+
await expect(generateDeclarationFile(path.resolve(), sourcePath, targetPath, outExt)).resolves.not.toThrow();
|
|
421
|
+
expect(fs.existsSync(expectedDeclPath)).toBeTruthy();
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test('should correctly replace output extension with .d.ts', async () => {
|
|
425
|
+
const sourcePath = path.join(fixturesDir, 'test.ts');
|
|
426
|
+
fs.writeFileSync(sourcePath, 'export const x = 1;');
|
|
427
|
+
|
|
428
|
+
const targetPath = path.join(fixturesDir, 'output.mjs');
|
|
429
|
+
const outExt: '.mjs' = '.mjs';
|
|
430
|
+
const expectedDeclPath = path.join(fixturesDir, 'output.d.ts');
|
|
431
|
+
|
|
432
|
+
await generateDeclarationFile(path.resolve(), sourcePath, targetPath, outExt);
|
|
433
|
+
|
|
434
|
+
expect(fs.existsSync(expectedDeclPath)).toBeTruthy();
|
|
435
|
+
});
|
|
436
|
+
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import * as babel from "@babel/core";
|
|
3
3
|
import * as fs from "fs";
|
|
4
4
|
import * as path from "path";
|
|
5
|
+
import * as ts from "typescript";
|
|
5
6
|
import { errors } from "../errors";
|
|
6
7
|
import { ProjectCompiler } from "../project-compiler";
|
|
7
8
|
import { FileAction, JavaScriptExtension } from "../../types";
|
|
@@ -9,32 +10,41 @@ import { FileAction, JavaScriptExtension } from "../../types";
|
|
|
9
10
|
// const outExt = ProjectCompiler.tsOutExt;
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
export function create(defaultBabelOptions?: babel.TransformOptions,
|
|
13
|
+
export function create(defaultBabelOptions?: babel.TransformOptions & { declaration?: boolean }, outExt?: JavaScriptExtension): FileAction {
|
|
13
14
|
const action: FileAction = (sourcePath: string, outputPath: string, projectPath: string) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
outExt = outExt || ".js";
|
|
16
|
+
|
|
17
|
+
let babelOptions = defaultBabelOptions;
|
|
18
|
+
if (!babelOptions) {
|
|
19
|
+
// 如果没有 babelOptions,则尝试从项目目录查找 babel 配置文件,如果没有,则使用默认配置。
|
|
20
|
+
// let babelOptions: babel.TransformOptions;
|
|
21
|
+
let bablePath: string;
|
|
22
|
+
let sourceDir = path.dirname(sourcePath);
|
|
23
|
+
if (projectPath) {
|
|
24
|
+
let c = ProjectCompiler.getBabelConfig(projectPath, sourceDir);
|
|
25
|
+
bablePath = c.path;
|
|
26
|
+
babelOptions = c.options;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
babelOptions = ProjectCompiler.getDefaultBabelConfig();
|
|
30
|
+
bablePath = '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
babelOptions.filename = sourcePath;
|
|
34
|
+
babelOptions.code = false;
|
|
35
|
+
babelOptions.ast = true;
|
|
17
36
|
}
|
|
18
37
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
let
|
|
25
|
-
|
|
26
|
-
babelOptions = c.options;
|
|
38
|
+
const declaration = babelOptions.declaration || false;
|
|
39
|
+
delete babelOptions.declaration;
|
|
40
|
+
if (declaration) {
|
|
41
|
+
// 如果需要生成声明文件,则调用 ts-compile 模块的生成声明文件方法
|
|
42
|
+
const ext = path.extname(sourcePath);
|
|
43
|
+
let targetPath = path.join(outputPath, path.basename(sourcePath).replace(ext, outExt));
|
|
44
|
+
generateDeclarationFile(projectPath, sourcePath, targetPath, outExt);
|
|
27
45
|
}
|
|
28
|
-
else {
|
|
29
|
-
babelOptions = ProjectCompiler.getDefaultBabelConfig();
|
|
30
|
-
bablePath = '';
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
babelOptions.filename = sourcePath;
|
|
34
|
-
babelOptions.code = false;
|
|
35
|
-
babelOptions.ast = true;
|
|
36
46
|
|
|
37
|
-
return compileFile(sourcePath, outputPath, projectPath, babelOptions,
|
|
47
|
+
return compileFile(sourcePath, outputPath, projectPath, babelOptions, outExt);
|
|
38
48
|
}
|
|
39
49
|
return action;
|
|
40
50
|
}
|
|
@@ -45,41 +55,16 @@ export function create(defaultBabelOptions?: babel.TransformOptions, outExit?: J
|
|
|
45
55
|
* @param outputPath 输出目录
|
|
46
56
|
* @param projectPath 项目路径
|
|
47
57
|
* */
|
|
48
|
-
async function compileFile(sourcePath: string, outputPath: string, projectPath: string, babelOptions: babel.TransformOptions, outExt: JavaScriptExtension) {
|
|
58
|
+
async function compileFile(sourcePath: string, outputPath: string, projectPath: string, babelOptions: babel.TransformOptions, outExt: JavaScriptExtension, generateDeclaration = false): Promise<void> {
|
|
49
59
|
if (!sourcePath) throw errors.argumentNull("sourcePath");
|
|
50
60
|
if (!outputPath) throw errors.argumentNull("outputPath");
|
|
51
61
|
if (!projectPath) throw errors.argumentNull("projectPath");
|
|
52
62
|
|
|
53
|
-
// if (!fs.existsSync(sourcePath)) throw errors.pathNotExists(sourcePath);
|
|
54
63
|
if (!fs.existsSync(sourcePath)) {
|
|
55
64
|
console.warn(`Path not exists: ${sourcePath}`);
|
|
56
65
|
return;
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
let sourceDir = path.dirname(sourcePath);
|
|
60
|
-
// let babelOptions: babel.TransformOptions;
|
|
61
|
-
let bablePath: string;
|
|
62
|
-
//= projectPath ?
|
|
63
|
-
// ProjectCompiler.getBabelConfig(projectPath, sourceDir) : ProjectCompiler.getDefaultBabelConfig();
|
|
64
|
-
if (projectPath) {
|
|
65
|
-
// let c = ProjectCompiler.getBabelConfig(projectPath, sourceDir);
|
|
66
|
-
// bablePath = c.path;
|
|
67
|
-
// babelOptions = c.options;
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
// babelOptions = ProjectCompiler.getDefaultBabelConfig();
|
|
71
|
-
// bablePath = '';
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// babelOptions.filename = sourcePath;
|
|
75
|
-
// babelOptions.code = false;
|
|
76
|
-
// babelOptions.ast = true;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// let fileResult = babel.transformFileSync(sourcePath, {
|
|
80
|
-
// filename: sourcePath, code: false, ast: true, plugins,
|
|
81
|
-
// presets
|
|
82
|
-
// });
|
|
83
68
|
let fileResult = babel.transformFileSync(sourcePath, babelOptions);
|
|
84
69
|
if (!fileResult)
|
|
85
70
|
throw errors.compileError(sourcePath);
|
|
@@ -122,6 +107,10 @@ async function compileFile(sourcePath: string, outputPath: string, projectPath:
|
|
|
122
107
|
|
|
123
108
|
fs.writeFileSync(targetPath, r.code);
|
|
124
109
|
|
|
110
|
+
if (generateDeclaration) {
|
|
111
|
+
await generateDeclarationFile(projectPath, sourcePath, targetPath, outExt);
|
|
112
|
+
}
|
|
113
|
+
|
|
125
114
|
}
|
|
126
115
|
|
|
127
116
|
function extname(file: string) {
|
|
@@ -130,22 +119,6 @@ function extname(file: string) {
|
|
|
130
119
|
return ext;
|
|
131
120
|
}
|
|
132
121
|
|
|
133
|
-
/**
|
|
134
|
-
* 获取源文件所对应生成文件的扩展名
|
|
135
|
-
* @param file 源文件名
|
|
136
|
-
* */
|
|
137
|
-
// function fileOutExt(file: string) {
|
|
138
|
-
// let ext = extname(file);
|
|
139
|
-
// if (ext === ".ts")
|
|
140
|
-
// return outExt;
|
|
141
|
-
|
|
142
|
-
// if (ext === ".tsx")
|
|
143
|
-
// return outExt;
|
|
144
|
-
|
|
145
|
-
// return ext;
|
|
146
|
-
// }
|
|
147
|
-
|
|
148
|
-
|
|
149
122
|
class ImportPathRewrite {
|
|
150
123
|
constructor(private filePath: string, node: babel.types.File, private outExt: JavaScriptExtension) {
|
|
151
124
|
this.traverse(node);
|
|
@@ -176,7 +149,50 @@ class ImportPathRewrite {
|
|
|
176
149
|
|
|
177
150
|
}
|
|
178
151
|
}
|
|
179
|
-
|
|
180
152
|
}
|
|
181
153
|
|
|
182
154
|
|
|
155
|
+
/**
|
|
156
|
+
* 生成声明文件
|
|
157
|
+
* @param projectPath 项目路径
|
|
158
|
+
* @param sourcePath 源文件
|
|
159
|
+
* @param targetPath 目标文件
|
|
160
|
+
* @param outExt 输出扩展名
|
|
161
|
+
*/
|
|
162
|
+
export function generateDeclarationFile(projectPath: string, sourcePath: string, targetPath: string, outExt: JavaScriptExtension): Promise<void> {
|
|
163
|
+
if (!projectPath) throw errors.argumentNull("projectPath");
|
|
164
|
+
if (!sourcePath) throw errors.argumentNull("sourcePath");
|
|
165
|
+
if (!targetPath) throw errors.argumentNull("targetPath");
|
|
166
|
+
|
|
167
|
+
const sourceExt = path.extname(sourcePath);
|
|
168
|
+
if (!sourceExt || (sourceExt !== ".ts" && sourceExt !== ".tsx")) {
|
|
169
|
+
return Promise.resolve();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const declarationPath = targetPath.endsWith(outExt)
|
|
173
|
+
? targetPath.slice(0, targetPath.length - outExt.length) + ".d.ts"
|
|
174
|
+
: targetPath.replace(path.extname(targetPath), ".d.ts");
|
|
175
|
+
|
|
176
|
+
const content = fs.readFileSync(sourcePath, "utf-8");
|
|
177
|
+
const result = ts.transpileDeclaration(content, {
|
|
178
|
+
fileName: path.basename(sourcePath),
|
|
179
|
+
reportDiagnostics: false,
|
|
180
|
+
compilerOptions: {
|
|
181
|
+
declaration: true,
|
|
182
|
+
emitDeclarationOnly: true,
|
|
183
|
+
skipLibCheck: true,
|
|
184
|
+
esModuleInterop: true,
|
|
185
|
+
jsx: ts.JsxEmit.React,
|
|
186
|
+
target: ts.ScriptTarget.ESNext,
|
|
187
|
+
module: ts.ModuleKind.NodeNext,
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const outDir = path.dirname(declarationPath);
|
|
192
|
+
if (!fs.existsSync(outDir)) {
|
|
193
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
fs.writeFileSync(declarationPath, result.outputText || "");
|
|
197
|
+
return Promise.resolve();
|
|
198
|
+
}
|
|
@@ -8,15 +8,23 @@ let copyFile: FileAction = (filePath: string, outPath: string) => {
|
|
|
8
8
|
if (!outPath) throw errors.argumentNull("outPath");
|
|
9
9
|
|
|
10
10
|
if (!fs.existsSync(filePath)) {
|
|
11
|
+
throw errors.pathNotExists(filePath);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!fs.statSync(filePath).isFile()) {
|
|
11
15
|
return;
|
|
12
16
|
}
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
const destFilePath = path.join(outPath, path.basename(filePath));
|
|
19
|
+
const destDirPath = path.dirname(destFilePath);
|
|
16
20
|
|
|
17
|
-
fs.mkdirSync(
|
|
21
|
+
fs.mkdirSync(destDirPath, { recursive: true });
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
try {
|
|
24
|
+
fs.copyFileSync(filePath, destFilePath);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
throw errors.copyFileError(filePath, destFilePath, err as Error);
|
|
27
|
+
}
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
export default copyFile;
|