-
Notifications
You must be signed in to change notification settings - Fork 12k
/
Copy pathesbuild-executor.ts
124 lines (106 loc) · 3.82 KB
/
esbuild-executor.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import type {
FormatMessagesOptions,
PartialMessage,
TransformOptions,
TransformResult,
} from 'esbuild';
/**
* Provides the ability to execute esbuild regardless of the current platform's support
* for using the native variant of esbuild. The native variant will be preferred (assuming
* the `alwaysUseWasm` constructor option is `false) due to its inherent performance advantages.
* At first use of esbuild, a supportability test will be automatically performed and the
* WASM-variant will be used if needed by the platform.
*/
export class EsbuildExecutor
implements Pick<typeof import('esbuild'), 'transform' | 'formatMessages'>
{
private esbuildTransform: this['transform'];
private esbuildFormatMessages: this['formatMessages'];
private initialized = false;
/**
* Constructs an instance of the `EsbuildExecutor` class.
*
* @param alwaysUseWasm If true, the WASM-variant will be preferred and no support test will be
* performed; if false (default), the native variant will be preferred.
*/
constructor(private alwaysUseWasm = false) {
this.esbuildTransform = this.esbuildFormatMessages = () => {
throw new Error('esbuild implementation missing');
};
}
/**
* Determines whether the native variant of esbuild can be used on the current platform.
*
* @returns A promise which resolves to `true`, if the native variant of esbuild is support or `false`, if the WASM variant is required.
*/
static async hasNativeSupport(): Promise<boolean> {
// Try to use native variant to ensure it is functional for the platform.
try {
const { formatMessages } = await import('esbuild');
await formatMessages([], { kind: 'error' });
return true;
} catch {
return false;
}
}
/**
* Initializes the esbuild transform and format messages functions.
*
* @returns A promise that fulfills when esbuild has been loaded and available for use.
*/
private async ensureEsbuild(): Promise<void> {
if (this.initialized) {
return;
}
// If the WASM variant was preferred at class construction or native is not supported, use WASM
if (this.alwaysUseWasm || !(await EsbuildExecutor.hasNativeSupport())) {
await this.useWasm();
this.initialized = true;
return;
}
try {
// Use the faster native variant if available.
const { transform, formatMessages } = await import('esbuild');
this.esbuildTransform = transform;
this.esbuildFormatMessages = formatMessages;
} catch {
// If the native variant is not installed then use the WASM-based variant
await this.useWasm();
}
this.initialized = true;
}
/**
* Transitions an executor instance to use the WASM-variant of esbuild.
*/
private async useWasm(): Promise<void> {
const { transform, formatMessages } = await import('esbuild-wasm');
this.esbuildTransform = transform;
this.esbuildFormatMessages = formatMessages;
// The ESBUILD_BINARY_PATH environment variable cannot exist when attempting to use the
// WASM variant. If it is then the binary located at the specified path will be used instead
// of the WASM variant.
delete process.env.ESBUILD_BINARY_PATH;
this.alwaysUseWasm = true;
}
async transform(
input: string | Uint8Array,
options?: TransformOptions,
): Promise<TransformResult> {
await this.ensureEsbuild();
return this.esbuildTransform(input, options);
}
async formatMessages(
messages: PartialMessage[],
options: FormatMessagesOptions,
): Promise<string[]> {
await this.ensureEsbuild();
return this.esbuildFormatMessages(messages, options);
}
}