@@ -7,7 +7,7 @@ import { EventEmitter } from './events.ts'
7
7
import { createHtml } from './html.ts'
8
8
import log from './log.ts'
9
9
import { getPagePath , RouteModule , Routing } from './routing.ts'
10
- import { colors , ensureDir , path , ServerRequest , Sha1 , walk } from './std.ts'
10
+ import { colors , ensureDir , fromStreamReader , path , ServerRequest , Sha1 , walk } from './std.ts'
11
11
import { compile } from './tsc/compile.ts'
12
12
import type { AlephEnv , APIHandler , Config , RouterURL } from './types.ts'
13
13
import util , { existsDirSync , existsFileSync , hashShort , MB , reHashJs , reHttp , reLocaleID , reMDExt , reModuleExt , reStyleModuleExt } from './util.ts'
@@ -21,13 +21,20 @@ interface Module {
21
21
isRemote : boolean
22
22
sourceFilePath : string
23
23
sourceHash : string
24
- deps : { url : string , hash : string , async ?: boolean } [ ]
24
+ deps : ModuleDep [ ]
25
25
jsFile : string
26
26
jsContent : string
27
27
jsSourceMap : string
28
28
hash : string
29
29
}
30
30
31
+ interface ModuleDep {
32
+ url : string
33
+ hash : string
34
+ async ?: boolean
35
+ external ?: boolean
36
+ }
37
+
31
38
interface Renderer {
32
39
renderPage : Function
33
40
renderHead : Function
@@ -167,9 +174,11 @@ export class Project {
167
174
modId = id + '.js'
168
175
}
169
176
}
177
+ if ( ! this . #modules. has ( modId ) && modId . endsWith ( '.js' ) ) {
178
+ modId = util . trimSuffix ( modId , '.js' )
179
+ }
170
180
if ( ! this . #modules. has ( modId ) ) {
171
181
log . warn ( `can't get the module by path '${ pathname } (${ modId } )'` )
172
- console . log ( Array . from ( this . #modules. keys ( ) ) )
173
182
}
174
183
return this . getModule ( modId )
175
184
}
@@ -843,7 +852,7 @@ export class Project {
843
852
}
844
853
845
854
// todo: force recompile remote modules which URL don't specify version
846
- private async _compile ( url : string , options ?: { sourceCode ?: string , forceCompile ?: boolean , forceTarget ?: string } ) {
855
+ private async _compile ( url : string , options ?: { forceCompile ?: boolean , forceTarget ?: string } ) {
847
856
const mod = this . _moduleFromURL ( url )
848
857
if ( this . #modules. has ( mod . id ) && ! options ?. forceCompile ) {
849
858
return this . #modules. get ( mod . id ) !
@@ -870,18 +879,11 @@ export class Project {
870
879
}
871
880
}
872
881
873
- let sourceContent = ''
882
+ let sourceContent = new Uint8Array ( )
874
883
let shouldCompile = false
875
884
let fsync = false
876
885
877
- if ( options ?. sourceCode ) {
878
- const sourceHash = getHash ( options . sourceCode , true )
879
- if ( mod . sourceHash === '' || mod . sourceHash !== sourceHash ) {
880
- mod . sourceHash = sourceHash
881
- sourceContent = options . sourceCode
882
- shouldCompile = true
883
- }
884
- } else if ( mod . isRemote ) {
886
+ if ( mod . isRemote ) {
885
887
let dlUrl = url
886
888
const { imports } = this . importMap
887
889
for ( const importPath in imports ) {
@@ -920,8 +922,8 @@ export class Project {
920
922
if ( resp . status != 200 ) {
921
923
throw new Error ( `Download ${ url } : ${ resp . status } - ${ resp . statusText } ` )
922
924
}
925
+ sourceContent = await Deno . readAll ( fromStreamReader ( resp . body ! . getReader ( ) ) )
923
926
mod . sourceHash = getHash ( sourceContent )
924
- sourceContent = await resp . text ( )
925
927
shouldCompile = true
926
928
} catch ( err ) {
927
929
throw new Error ( `Download ${ url } : ${ err . message } ` )
@@ -932,11 +934,10 @@ export class Project {
932
934
if ( resp . status != 200 ) {
933
935
throw new Error ( `${ resp . status } - ${ resp . statusText } ` )
934
936
}
935
- const text = await resp . text ( )
936
- const sourceHash = getHash ( text , true )
937
- if ( mod . sourceHash !== sourceHash ) {
937
+ sourceContent = await Deno . readAll ( fromStreamReader ( resp . body ! . getReader ( ) ) )
938
+ const sourceHash = getHash ( sourceContent , true )
939
+ if ( mod . sourceHash === '' || mod . sourceHash !== sourceHash ) {
938
940
mod . sourceHash = sourceHash
939
- sourceContent = text
940
941
shouldCompile = true
941
942
}
942
943
} catch ( err ) {
@@ -945,35 +946,32 @@ export class Project {
945
946
}
946
947
} else {
947
948
const filepath = path . join ( this . srcDir , url )
948
- try {
949
- const fileinfo = await Deno . stat ( filepath )
950
- // 10mb limit
951
- if ( fileinfo . size > 10 * ( 1 << 20 ) ) {
952
- throw new Error ( `ignored module '${ url } ': too large(${ ( fileinfo . size / ( 1 << 20 ) ) . toFixed ( 2 ) } mb)` )
953
- }
954
- } catch ( err ) {
955
- if ( err instanceof Deno . errors . NotFound ) {
956
- throw new Error ( `module '${ url } ' not found` )
957
- }
958
- }
959
- const text = await Deno . readTextFile ( filepath )
960
- const sourceHash = getHash ( text , true )
949
+ sourceContent = await Deno . readFile ( filepath )
950
+ const sourceHash = getHash ( sourceContent , true )
961
951
if ( mod . sourceHash === '' || mod . sourceHash !== sourceHash ) {
962
952
mod . sourceHash = sourceHash
963
- sourceContent = text
964
953
shouldCompile = true
965
954
}
966
955
}
967
956
968
957
// compile source code
969
958
if ( shouldCompile ) {
970
959
const t = performance . now ( )
960
+ let sourceCode = ( new TextDecoder ) . decode ( sourceContent )
961
+ for ( const plugin of this . config . plugins ) {
962
+ if ( plugin . test . test ( url ) && plugin . transform ) {
963
+ const { code, loader = 'js' } = await plugin . transform ( sourceContent )
964
+ sourceCode = code
965
+ mod . loader = loader
966
+ break
967
+ }
968
+ }
971
969
mod . deps = [ ]
972
970
if ( mod . loader === 'css' ) {
973
- let css : string = sourceContent
971
+ let css : string = sourceCode
974
972
if ( mod . id . endsWith ( '.less' ) ) {
975
973
try {
976
- const output = await less . render ( sourceContent || '/* empty content */' )
974
+ const output = await less . render ( sourceCode || '/* empty content */' )
977
975
css = output . css
978
976
} catch ( error ) {
979
977
throw new Error ( `less: ${ error } ` ) ;
@@ -1004,7 +1002,7 @@ export class Project {
1004
1002
mod . jsSourceMap = '' // todo: sourceMap
1005
1003
mod . hash = getHash ( css )
1006
1004
} else if ( mod . loader === 'markdown' ) {
1007
- const { __content, ...props } = safeLoadFront ( sourceContent )
1005
+ const { __content, ...props } = safeLoadFront ( sourceCode )
1008
1006
const html = marked . parse ( __content )
1009
1007
mod . jsContent = [
1010
1008
this . isDev && `const _s = $RefreshSig$();` ,
@@ -1051,7 +1049,7 @@ export class Project {
1051
1049
return sig
1052
1050
}
1053
1051
}
1054
- const { diagnostics, outputText, sourceMapText } = compile ( mod . sourceFilePath , sourceContent , compileOptions )
1052
+ const { diagnostics, outputText, sourceMapText } = compile ( mod . sourceFilePath , sourceCode , compileOptions )
1055
1053
if ( diagnostics && diagnostics . length > 0 ) {
1056
1054
throw new Error ( `compile ${ url } : ${ diagnostics . map ( d => d . messageText ) . join ( '\n' ) } ` )
1057
1055
}
@@ -1097,7 +1095,7 @@ export class Project {
1097
1095
this . #modules. set ( mod . id , mod )
1098
1096
1099
1097
// compile deps
1100
- for ( const dep of mod . deps . filter ( ( { url } ) => ! url . startsWith ( '#useDeno.' ) ) ) {
1098
+ for ( const dep of mod . deps . filter ( ( { url, external } ) => ! url . startsWith ( '#useDeno.' ) && ! external ) ) {
1101
1099
const depMod = await this . _compile ( dep . url )
1102
1100
if ( dep . hash !== depMod . hash ) {
1103
1101
dep . hash = depMod . hash
@@ -1184,51 +1182,68 @@ export class Project {
1184
1182
} )
1185
1183
}
1186
1184
1187
- private _rewriteImportPath ( mod : Module , importPath : string , async ?: boolean ) : string {
1185
+ private _rewriteImportPath ( importer : Module , importPath : string , async ?: boolean ) : string {
1188
1186
let rewrittenPath : string
1189
- if ( importPath in this . importMap . imports ) {
1190
- importPath = this . importMap . imports [ importPath ]
1191
- }
1192
- if ( reHttp . test ( importPath ) ) {
1193
- if ( mod . isRemote ) {
1194
- rewrittenPath = relativePath (
1195
- path . dirname ( mod . url . replace ( reHttp , '/-/' ) . replace ( / : ( \d + ) / , '/$1' ) ) ,
1196
- renameImportUrl ( importPath )
1197
- )
1198
- } else {
1199
- rewrittenPath = relativePath (
1200
- path . dirname ( mod . url ) ,
1201
- renameImportUrl ( importPath )
1202
- )
1187
+ let resolveRet : { path : string , external ?: boolean } | null = null
1188
+ for ( const plugin of this . config . plugins ) {
1189
+ if ( plugin . test . test ( importPath ) && plugin . resolve ) {
1190
+ resolveRet = plugin . resolve ( importPath )
1191
+ break
1203
1192
}
1193
+ }
1194
+
1195
+ // when a plugin resolver returns an external path, do NOT rewrite the `importPath`
1196
+ if ( resolveRet && resolveRet . external ) {
1197
+ rewrittenPath = resolveRet . path
1204
1198
} else {
1205
- if ( mod . isRemote ) {
1206
- const modUrl = new URL ( mod . url )
1207
- let pathname = importPath
1208
- if ( ! pathname . startsWith ( '/' ) ) {
1209
- pathname = path . join ( path . dirname ( modUrl . pathname ) , importPath )
1199
+ if ( resolveRet ) {
1200
+ importPath = resolveRet . path
1201
+ }
1202
+ if ( importPath in this . importMap . imports ) {
1203
+ importPath = this . importMap . imports [ importPath ]
1204
+ }
1205
+ if ( reHttp . test ( importPath ) ) {
1206
+ if ( importer . isRemote ) {
1207
+ rewrittenPath = relativePath (
1208
+ path . dirname ( importer . url . replace ( reHttp , '/-/' ) . replace ( / : ( \d + ) / , '/$1' ) ) ,
1209
+ renameImportUrl ( importPath )
1210
+ )
1211
+ } else {
1212
+ rewrittenPath = relativePath (
1213
+ path . dirname ( importer . url ) ,
1214
+ renameImportUrl ( importPath )
1215
+ )
1210
1216
}
1211
- const importUrl = new URL ( modUrl . protocol + '//' + modUrl . host + pathname )
1212
- rewrittenPath = relativePath (
1213
- path . dirname ( mod . sourceFilePath ) ,
1214
- renameImportUrl ( importUrl . toString ( ) )
1215
- )
1216
1217
} else {
1217
- rewrittenPath = importPath . replace ( reModuleExt , '' ) + '.' + 'x' . repeat ( hashShort )
1218
+ if ( importer . isRemote ) {
1219
+ const modUrl = new URL ( importer . url )
1220
+ let pathname = importPath
1221
+ if ( ! pathname . startsWith ( '/' ) ) {
1222
+ pathname = path . join ( path . dirname ( modUrl . pathname ) , importPath )
1223
+ }
1224
+ const importUrl = new URL ( modUrl . protocol + '//' + modUrl . host + pathname )
1225
+ rewrittenPath = relativePath (
1226
+ path . dirname ( importer . sourceFilePath ) ,
1227
+ renameImportUrl ( importUrl . toString ( ) )
1228
+ )
1229
+ } else {
1230
+ rewrittenPath = importPath . replace ( reModuleExt , '' ) + '.' + 'x' . repeat ( hashShort )
1231
+ }
1218
1232
}
1219
1233
}
1234
+
1220
1235
if ( reHttp . test ( importPath ) ) {
1221
- mod . deps . push ( { url : importPath , hash : '' , async } )
1236
+ importer . deps . push ( { url : importPath , hash : '' , async, external : resolveRet ?. external } )
1222
1237
} else {
1223
- if ( mod . isRemote ) {
1224
- const sourceUrl = new URL ( mod . url )
1238
+ if ( importer . isRemote ) {
1239
+ const sourceUrl = new URL ( importer . url )
1225
1240
let pathname = importPath
1226
1241
if ( ! pathname . startsWith ( '/' ) ) {
1227
1242
pathname = path . join ( path . dirname ( sourceUrl . pathname ) , importPath )
1228
1243
}
1229
- mod . deps . push ( { url : sourceUrl . protocol + '//' + sourceUrl . host + pathname , hash : '' , async } )
1244
+ importer . deps . push ( { url : sourceUrl . protocol + '//' + sourceUrl . host + pathname , hash : '' , async, external : resolveRet ?. external } )
1230
1245
} else {
1231
- mod . deps . push ( { url : path . join ( path . dirname ( mod . url ) , importPath ) , hash : '' , async } )
1246
+ importer . deps . push ( { url : path . join ( path . dirname ( importer . url ) , importPath ) , hash : '' , async, external : resolveRet ?. external } )
1232
1247
}
1233
1248
}
1234
1249
@@ -1263,8 +1278,8 @@ export class Project {
1263
1278
host : 'localhost' ,
1264
1279
hostname : 'localhost' ,
1265
1280
port : '' ,
1266
- href : 'https://esm.sh ' + url . pathname + url . query . toString ( ) ,
1267
- origin : 'https://esm.sh ' ,
1281
+ href : 'https://localhost ' + url . pathname + url . query . toString ( ) ,
1282
+ origin : 'https://localhost ' ,
1268
1283
pathname : url . pathname ,
1269
1284
search : url . query . toString ( ) ,
1270
1285
hash : '' ,
@@ -1449,7 +1464,7 @@ function renameImportUrl(importUrl: string): string {
1449
1464
return pathname + ext
1450
1465
}
1451
1466
1452
- function getHash ( content : string , checkVersion = false ) {
1467
+ function getHash ( content : string | Uint8Array , checkVersion = false ) {
1453
1468
const sha1 = new Sha1 ( )
1454
1469
sha1 . update ( content )
1455
1470
if ( checkVersion ) {
0 commit comments