Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit b167cda

Browse files
committed
Add fastTransform function
1 parent 546b7a8 commit b167cda

File tree

2 files changed

+123
-47
lines changed

2 files changed

+123
-47
lines changed

compiler/mod.ts

Lines changed: 92 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
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'
22
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'
35
import { existsFile } from '../shared/fs.ts'
46
import { Measure } from '../shared/log.ts'
7+
import util from '../shared/util.ts'
8+
import { decoder, getDenoDir, toLocalPath, toRelativePath } from '../server/helper.ts'
59
import type { ImportMap } from '../types.d.ts'
6-
import { getDenoDir } from '../server/helper.ts'
710
import { checksum } from './dist/checksum.js'
811
import init, { parseExportNamesSync, stripSsrCodeSync, transformSync } from './dist/compiler.js'
912

@@ -16,6 +19,12 @@ export enum SourceType {
1619
Unknown = '??',
1720
}
1821

22+
export type ModuleSource = {
23+
code: string
24+
type: SourceType
25+
map?: string
26+
}
27+
1928
export type SWCOptions = {
2029
sourceType?: SourceType
2130
jsxFactory?: string
@@ -28,10 +37,10 @@ export type ReactOptions = {
2837
}
2938

3039
export type TransformOptions = {
31-
swcOptions?: SWCOptions
32-
workingDir?: string
3340
alephPkgUri?: string
41+
workingDir?: string
3442
importMap?: ImportMap
43+
swcOptions?: SWCOptions
3544
react?: ReactOptions
3645
sourceMap?: boolean
3746
isDev?: boolean
@@ -43,7 +52,7 @@ export type TransformOptions = {
4352

4453
export type TransformResult = {
4554
code: string
46-
deps?: DependencyDescriptor[]
55+
deps?: RawDependencyDescriptor[]
4756
ssrPropsFn?: string
4857
ssgPathsFn?: boolean
4958
denoHooks?: string[]
@@ -52,7 +61,7 @@ export type TransformResult = {
5261
map?: string
5362
}
5463

55-
type DependencyDescriptor = {
64+
type RawDependencyDescriptor = {
5665
specifier: string
5766
resolved: string
5867
isDynamic: boolean
@@ -99,15 +108,17 @@ async function checkWasmReady() {
99108
}
100109

101110
/**
102-
* transform module by swc.
111+
* Transforms the module with esbuild/swc.
103112
*
104113
* ```tsx
105114
* transform(
106115
* '/app.tsx',
107116
* `
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+
* }
111122
* `
112123
* )
113124
* ```
@@ -116,17 +127,7 @@ export async function transform(specifier: string, code: string, options: Transf
116127
await checkWasmReady()
117128

118129
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)
130131

131132
// resolve inline-style
132133
if (inlineStyles) {
@@ -154,15 +155,77 @@ export async function transform(specifier: string, code: string, options: Transf
154155
}))
155156
}
156157

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+
157226
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
166229
}
167230
}
168231

server/aleph.ts

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { walk } from 'https://deno.land/[email protected]/fs/walk.ts'
55
import { createHash } from 'https://deno.land/[email protected]/hash/mod.ts'
66
import { basename, dirname, extname, join, resolve } from 'https://deno.land/[email protected]/path/mod.ts'
77
import { Bundler, bundlerRuntimeCode, simpleJSMinify } from '../bundler/mod.ts'
8-
import type { TransformOptions } from '../compiler/mod.ts'
9-
import { wasmChecksum, parseExportNames, SourceType, transform, stripSsrCode } from '../compiler/mod.ts'
8+
import type { TransformOptions, TransformResult, ModuleSource } from '../compiler/mod.ts'
9+
import { wasmChecksum, parseExportNames, SourceType, fastTransform, transform, stripSsrCode } from '../compiler/mod.ts'
1010
import { EventEmitter } from '../framework/core/events.ts'
1111
import { builtinModuleExts, toPagePath, trimBuiltinModuleExts } from '../framework/core/module.ts'
1212
import { Routing } from '../framework/core/routing.ts'
@@ -27,12 +27,6 @@ import {
2727
import { getContentType } from './mime.ts'
2828
import { buildHtml, Renderer } from './renderer.ts'
2929

30-
type ModuleSource = {
31-
code: string
32-
type: SourceType
33-
map?: string
34-
}
35-
3630
type CompileOptions = {
3731
source?: ModuleSource,
3832
forceRefresh?: boolean,
@@ -820,8 +814,8 @@ export class Aleph implements IAleph {
820814
/** common compiler options */
821815
get commonCompilerOptions(): TransformOptions {
822816
return {
823-
workingDir: this.#workingDir,
824817
alephPkgUri: getAlephPkgUri(),
818+
workingDir: this.#workingDir,
825819
importMap: this.#importMap,
826820
inlineStylePreprocess: async (key: string, type: string, tpl: string) => {
827821
if (type !== 'css') {
@@ -1225,6 +1219,8 @@ export class Aleph implements IAleph {
12251219
return
12261220
}
12271221

1222+
const ms = new Measure()
1223+
12281224
if (source.type === SourceType.CSS) {
12291225
const { code, map } = await cssLoader({ specifier, data: source.code }, this)
12301226
source.code = code
@@ -1233,15 +1229,31 @@ export class Aleph implements IAleph {
12331229
module.isStyle = true
12341230
}
12351231

1236-
const ms = new Measure()
1237-
const { code, deps = [], denoHooks, ssrPropsFn, ssgPathsFn, starExports, jsxStaticClassNames, map } = await transform(specifier, source.code, {
1238-
...this.commonCompilerOptions,
1239-
sourceMap: this.isDev,
1240-
swcOptions: {
1241-
sourceType: source.type
1242-
},
1243-
httpExternal
1244-
})
1232+
let ret: TransformResult
1233+
// use `fastTransform` when the module is remote non-jsx module in `development` mode
1234+
if (this.isDev && util.isLikelyHttpURL(specifier) && source.type !== SourceType.JSX && source.type !== SourceType.TSX) {
1235+
ret = await fastTransform(specifier, source, { react: this.#config.react })
1236+
} else {
1237+
ret = await transform(specifier, source.code, {
1238+
...this.commonCompilerOptions,
1239+
sourceMap: this.isDev,
1240+
swcOptions: {
1241+
sourceType: source.type
1242+
},
1243+
httpExternal
1244+
})
1245+
}
1246+
1247+
const {
1248+
code,
1249+
deps = [],
1250+
denoHooks,
1251+
ssrPropsFn,
1252+
ssgPathsFn,
1253+
starExports,
1254+
jsxStaticClassNames,
1255+
map
1256+
} = ret
12451257

12461258
let jsCode = code
12471259
let sourceMap = map
@@ -1440,6 +1452,7 @@ export class Aleph implements IAleph {
14401452
}
14411453
}
14421454

1455+
14431456
/** create bundled chunks for production. */
14441457
private async bundle() {
14451458
const entries = this.analyze()

0 commit comments

Comments
 (0)