1
- import { join } from 'https://deno.land/[email protected] /path/mod.ts'
1
+ import { dirname , join } from 'https://deno.land/[email protected] /path/mod.ts'
2
2
import { ensureDir } from 'https://deno.land/[email protected] /fs/ensure_dir.ts'
3
+ import { esbuild } from '../bundler/esbuild.ts'
4
+ import { trimBuiltinModuleExts } from '../framework/core/module.ts'
3
5
import { existsFile } from '../shared/fs.ts'
4
6
import { Measure } from '../shared/log.ts'
7
+ import util from '../shared/util.ts'
8
+ import { decoder , getDenoDir , toLocalPath , toRelativePath } from '../server/helper.ts'
5
9
import type { ImportMap } from '../types.d.ts'
6
- import { getDenoDir } from '../server/helper.ts'
7
10
import { checksum } from './dist/checksum.js'
8
11
import init , { parseExportNamesSync , stripSsrCodeSync , transformSync } from './dist/compiler.js'
9
12
@@ -16,6 +19,12 @@ export enum SourceType {
16
19
Unknown = '??' ,
17
20
}
18
21
22
+ export type ModuleSource = {
23
+ code : string
24
+ type : SourceType
25
+ map ?: string
26
+ }
27
+
19
28
export type SWCOptions = {
20
29
sourceType ?: SourceType
21
30
jsxFactory ?: string
@@ -28,10 +37,10 @@ export type ReactOptions = {
28
37
}
29
38
30
39
export type TransformOptions = {
31
- swcOptions ?: SWCOptions
32
- workingDir ?: string
33
40
alephPkgUri ?: string
41
+ workingDir ?: string
34
42
importMap ?: ImportMap
43
+ swcOptions ?: SWCOptions
35
44
react ?: ReactOptions
36
45
sourceMap ?: boolean
37
46
isDev ?: boolean
@@ -43,7 +52,7 @@ export type TransformOptions = {
43
52
44
53
export type TransformResult = {
45
54
code : string
46
- deps ?: DependencyDescriptor [ ]
55
+ deps ?: RawDependencyDescriptor [ ]
47
56
ssrPropsFn ?: string
48
57
ssgPathsFn ?: boolean
49
58
denoHooks ?: string [ ]
@@ -52,7 +61,7 @@ export type TransformResult = {
52
61
map ?: string
53
62
}
54
63
55
- type DependencyDescriptor = {
64
+ type RawDependencyDescriptor = {
56
65
specifier : string
57
66
resolved : string
58
67
isDynamic : boolean
@@ -99,15 +108,17 @@ async function checkWasmReady() {
99
108
}
100
109
101
110
/**
102
- * transform module by swc.
111
+ * Transforms the module with esbuild/ swc.
103
112
*
104
113
* ```tsx
105
114
* transform(
106
115
* '/app.tsx',
107
116
* `
108
- * export default App() {
109
- * return <h1>Hello World</h1>
110
- * }
117
+ * import React from 'https://esm.sh/react';
118
+ *
119
+ * export default App() {
120
+ * return <h1>Hello World</h1>
121
+ * }
111
122
* `
112
123
* )
113
124
* ```
@@ -116,17 +127,7 @@ export async function transform(specifier: string, code: string, options: Transf
116
127
await checkWasmReady ( )
117
128
118
129
const { inlineStylePreprocess, ...transformOptions } = options
119
- let {
120
- code : jsContent ,
121
- deps,
122
- ssrPropsFn,
123
- ssgPathsFn,
124
- inlineStyles,
125
- denoHooks,
126
- starExports,
127
- jsxStaticClassNames,
128
- map,
129
- } = transformSync ( specifier , code , transformOptions )
130
+ let { code : jsContent , inlineStyles, ...rest } = transformSync ( specifier , code , transformOptions )
130
131
131
132
// resolve inline-style
132
133
if ( inlineStyles ) {
@@ -154,15 +155,77 @@ export async function transform(specifier: string, code: string, options: Transf
154
155
} ) )
155
156
}
156
157
158
+ return { code : jsContent , ...rest }
159
+ }
160
+
161
+ export async function fastTransform ( specifier : string , source : ModuleSource , { react } : TransformOptions = { } ) : Promise < TransformResult > {
162
+ if ( ! util . isLikelyHttpURL ( specifier ) ) {
163
+ throw new Error ( 'Expect a remote(http) module' )
164
+ }
165
+ if ( source . type === SourceType . Unknown ) {
166
+ throw new Error ( 'Unknown source type' )
167
+ }
168
+ if ( source . type === SourceType . JSX || source . type === SourceType . TSX || source . type === SourceType . CSS ) {
169
+ throw new Error ( 'Expect non-jsx/css module' )
170
+ }
171
+ const deps : RawDependencyDescriptor [ ] = [ ]
172
+ const r = await esbuild ( {
173
+ stdin : {
174
+ loader : source . type ,
175
+ contents : source . code ,
176
+ sourcefile : specifier ,
177
+ } ,
178
+ format : 'esm' ,
179
+ write : false ,
180
+ bundle : true ,
181
+ plugins : [ {
182
+ name : 'module-resolver' ,
183
+ setup ( build ) {
184
+ build . onResolve ( { filter : / .* / } , args => {
185
+ if ( args . kind === 'entry-point' ) {
186
+ return { path : args . path }
187
+ }
188
+
189
+ const isRemote = util . isLikelyHttpURL ( args . path )
190
+ const url = new URL ( args . path , ! isRemote ? specifier : undefined )
191
+
192
+ if ( react ) {
193
+ if ( url . hostname === 'esm.sh' || url . hostname === 'cdn.esm.sh' || url . hostname === 'esm.x-static.io' ) {
194
+ const a = url . pathname . split ( '/' ) . filter ( Boolean )
195
+ const v = Boolean ( a [ 0 ] ) && a [ 0 ] . startsWith ( 'v' )
196
+ const n = v ? a [ 1 ] : a [ 0 ]
197
+ if ( n ) {
198
+ const prefix = v ? '/v' + react . esmShBuildVersion + '/' : '/'
199
+ const subPath = '@' + react . version + '/' + a . slice ( v ? 2 : 1 ) . join ( '/' )
200
+ if ( n === 'react' || n === 'react-dom' ) {
201
+ url . pathname = prefix + n + subPath
202
+ }
203
+ if ( n . startsWith ( 'react@' ) || n . startsWith ( 'react-dom@' ) ) {
204
+ url . pathname = prefix + n . split ( '@' ) [ 0 ] + subPath
205
+ }
206
+ }
207
+ }
208
+ }
209
+
210
+ const path = util . trimSuffix ( url . toString ( ) , '/' )
211
+ const resolved = toRelativePath (
212
+ dirname ( toLocalPath ( specifier ) ) ,
213
+ toLocalPath ( trimBuiltinModuleExts ( path ) + '.js' )
214
+ )
215
+ deps . push ( {
216
+ specifier : path ,
217
+ resolved,
218
+ isDynamic : args . kind === 'dynamic-import' ,
219
+ } )
220
+ return { path : resolved , external : true }
221
+ } )
222
+ }
223
+ } ] ,
224
+ } )
225
+
157
226
return {
158
- code : jsContent ,
159
- deps,
160
- ssrPropsFn,
161
- ssgPathsFn,
162
- denoHooks,
163
- starExports,
164
- jsxStaticClassNames,
165
- map
227
+ code : decoder . decode ( r . outputFiles [ 0 ] . contents ) ,
228
+ deps
166
229
}
167
230
}
168
231
0 commit comments