@@ -11,8 +11,8 @@ import util from '../shared/util.ts'
11
11
import type { Config , LoaderPlugin , LoaderTransformResult , ModuleOptions , RouterURL , ServerApplication , TransformFn } from '../types.ts'
12
12
import { VERSION } from '../version.ts'
13
13
import { Bundler } from './bundler.ts'
14
- import { defaultConfig , loadConfig } from './config.ts'
15
- import { clearCompilation , computeHash , createHtml , formatBytesWithColor , getAlephPkgUri , getRelativePath , isLoaderPlugin , reFullVersion , reHashJs , reHashResolve , toLocalUrl , trimModuleExt } from './helper.ts'
14
+ import { defaultConfig , loadConfig , loadImportMap } from './config.ts'
15
+ import { clearCompilation , computeHash , createHtml , formatBytesWithColor , getAlephPkgUri , getRelativePath , getDenoDir , isLoaderPlugin , reFullVersion , reHashJs , reHashResolve , toLocalUrl , trimModuleExt } from './helper.ts'
16
16
17
17
/** A module includes the compilation details. */
18
18
export type Module = {
@@ -75,6 +75,7 @@ export class Application implements ServerApplication {
75
75
const walkOptions = { includeDirs : false , skip : [ / ( ^ | \/ | \\ ) \. / , / \. d \. t s $ / i, / ( \. | _ ) ( t e s t | s p e c | e 2 e ) \. ( t s x ? | j s x ? | m j s ) ? $ / i] }
76
76
const apiDir = path . join ( this . srcDir , 'api' )
77
77
const pagesDir = path . join ( this . srcDir , 'pages' )
78
+ const buildManifestFile = path . join ( this . buildDir , 'build.manifest.json' )
78
79
79
80
if ( ! ( existsDirSync ( pagesDir ) ) ) {
80
81
log . fatal ( `'pages' directory not found.` )
@@ -84,21 +85,21 @@ export class Application implements ServerApplication {
84
85
log . fatal ( `need Deno ${ minDenoVersion } +, but got ${ Deno . version . deno } ` )
85
86
}
86
87
87
- const p = Deno . run ( {
88
- cmd : [ Deno . execPath ( ) , 'info' , '--unstable' , '--json' ] ,
89
- stdout : 'piped' ,
90
- stderr : 'null'
91
- } )
92
- const output = ( new TextDecoder ) . decode ( await p . output ( ) )
93
- const { denoDir } = JSON . parse ( output )
94
- p . close ( )
95
- if ( ! existsDirSync ( denoDir ) ) {
96
- log . fatal ( `can't find the deno dir` )
88
+ let shouldRebuild = ! existsFileSync ( buildManifestFile )
89
+ if ( ! shouldRebuild ) {
90
+ try {
91
+ const bm = JSON . parse ( await Deno . readTextFile ( buildManifestFile ) )
92
+ shouldRebuild = bm . compiler !== buildChecksum
93
+ if ( shouldRebuild ) {
94
+ log . debug ( 'rebuild...' )
95
+ }
96
+ } catch ( e ) { }
97
97
}
98
- this . #dirs. set ( 'denoDir' , denoDir )
99
98
100
- if ( reload ) {
101
- this . #reloading = true
99
+ if ( reload || shouldRebuild ) {
100
+ if ( reload && ! shouldRebuild ) {
101
+ this . #reloading = true
102
+ }
102
103
if ( existsDirSync ( this . buildDir ) ) {
103
104
await Deno . remove ( this . buildDir , { recursive : true } )
104
105
}
@@ -113,9 +114,14 @@ export class Application implements ServerApplication {
113
114
Deno . env . set ( 'ALEPH_VERSION' , VERSION )
114
115
Deno . env . set ( 'BUILD_MODE' , this . mode )
115
116
116
- const [ config , importMap ] = await loadConfig ( this . workingDir )
117
+ const [ config , importMap , denoDir ] = await Promise . all ( [
118
+ loadConfig ( this . workingDir ) ,
119
+ loadImportMap ( this . workingDir ) ,
120
+ getDenoDir ( )
121
+ ] )
117
122
Object . assign ( this . config , config )
118
123
Object . assign ( this . importMap , importMap )
124
+ this . #dirs. set ( 'denoDir' , denoDir )
119
125
120
126
// apply server plugins
121
127
for ( const plugin of plugins ) {
@@ -195,6 +201,12 @@ export class Application implements ServerApplication {
195
201
this . #reloading = false
196
202
}
197
203
204
+ ensureTextFile ( buildManifestFile , JSON . stringify ( {
205
+ aleph : VERSION ,
206
+ compiler : buildChecksum ,
207
+ deno : Deno . version . deno ,
208
+ } ) )
209
+
198
210
log . debug ( `init project in ${ Math . round ( performance . now ( ) - t ) } ms` )
199
211
200
212
if ( this . isDev ) {
@@ -612,8 +624,8 @@ export class Application implements ServerApplication {
612
624
}
613
625
try {
614
626
if ( existsFileSync ( metaFile ) ) {
615
- const { url , sourceHash, deps } = JSON . parse ( await Deno . readTextFile ( metaFile ) )
616
- if ( url === url && util . isNEString ( sourceHash ) && util . isArray ( deps ) ) {
627
+ const { sourceHash, deps } = JSON . parse ( await Deno . readTextFile ( metaFile ) )
628
+ if ( util . isNEString ( sourceHash ) && util . isArray ( deps ) ) {
617
629
mod . sourceHash = sourceHash
618
630
mod . deps = deps
619
631
} else {
@@ -625,9 +637,9 @@ export class Application implements ServerApplication {
625
637
}
626
638
627
639
let sourceContent = new Uint8Array ( )
628
- let contentType : string | null = null
640
+ let contentType : null | string = null
629
641
let jsContent = ''
630
- let jsSourceMap : string | null = null
642
+ let jsSourceMap : null | string = null
631
643
let shouldCompile = false
632
644
let fsync = false
633
645
@@ -639,7 +651,14 @@ export class Application implements ServerApplication {
639
651
shouldCompile = true
640
652
}
641
653
} else if ( isRemote ) {
642
- try {
654
+ let shouldFetch = true
655
+ if ( mod . sourceHash !== '' && ! url . startsWith ( 'http://localhost:' ) ) {
656
+ const jsFile = util . cleanPath ( saveDir + '/' + name + '.js' )
657
+ if ( existsFileSync ( jsFile ) ) {
658
+ shouldFetch = false
659
+ }
660
+ }
661
+ if ( shouldFetch ) {
643
662
const [ content , headers ] = await this . fetchModule ( url )
644
663
const sourceHash = computeHash ( content )
645
664
sourceContent = content
@@ -648,9 +667,6 @@ export class Application implements ServerApplication {
648
667
mod . sourceHash = sourceHash
649
668
shouldCompile = true
650
669
}
651
- } catch ( err ) {
652
- log . error ( `Download ${ url } :` , err )
653
- return mod
654
670
}
655
671
} else {
656
672
const filepath = path . join ( this . srcDir , url )
@@ -671,7 +687,7 @@ export class Application implements ServerApplication {
671
687
}
672
688
673
689
// compute hash
674
- mod . hash = computeHash ( mod . sourceHash + buildChecksum + JSON . stringify ( this . defaultCompileOptions ) )
690
+ mod . hash = isRemote ? mod . sourceHash : computeHash ( mod . sourceHash + JSON . stringify ( this . defaultCompileOptions ) )
675
691
mod . jsFile = util . cleanPath ( saveDir + '/' + name + ( isRemote ? '' : `.${ mod . hash . slice ( 0 , hashShortLength ) } ` ) + '.js' )
676
692
677
693
// check previous compilation output if the source content doesn't changed.
@@ -711,7 +727,10 @@ export class Application implements ServerApplication {
711
727
712
728
for ( const plugin of this . config . plugins ) {
713
729
if ( plugin . type === 'loader' && plugin . test . test ( url ) ) {
714
- const { code, type = 'js' } = await this . applyLoader ( plugin , { url, content : sourceContent } )
730
+ const { code, type = 'js' } = await this . applyLoader (
731
+ plugin ,
732
+ { url, content : sourceContent }
733
+ )
715
734
sourceCode = code
716
735
sourceType = type
717
736
break
@@ -778,13 +797,16 @@ export class Application implements ServerApplication {
778
797
if ( jsContent === '' ) {
779
798
jsContent = await Deno . readTextFile ( mod . jsFile )
780
799
}
781
- const newContent = jsContent . replace ( reHashResolve , ( s , key , spaces , ql , importPath , qr ) => {
782
- const importPathname = importPath . replace ( reHashJs , '' )
783
- if ( importPathname == dep . url || importPathname === relativePathname ) {
784
- return `${ key } ${ spaces } ${ ql } ${ importPathname } .${ dep . hash . slice ( 0 , hashShortLength ) } .js${ qr } `
800
+ const newContent = jsContent . replace (
801
+ reHashResolve ,
802
+ ( s , key , spaces , ql , importPath , qr ) => {
803
+ const importPathname = importPath . replace ( reHashJs , '' )
804
+ if ( importPathname == dep . url || importPathname === relativePathname ) {
805
+ return `${ key } ${ spaces } ${ ql } ${ importPathname } .${ dep . hash . slice ( 0 , hashShortLength ) } .js${ qr } `
806
+ }
807
+ return s
785
808
}
786
- return s
787
- } )
809
+ )
788
810
if ( newContent !== jsContent ) {
789
811
jsContent = newContent
790
812
if ( ! fsync ) {
@@ -841,11 +863,16 @@ export class Application implements ServerApplication {
841
863
}
842
864
} )
843
865
} )
844
- this . #pageRouting. lookup ( routes => routes . forEach ( ( { module : { url } } ) => entryMods . add ( url ) ) )
866
+ this . #pageRouting. lookup ( routes => {
867
+ routes . forEach ( ( { module : { url } } ) => entryMods . add ( url ) )
868
+ } )
845
869
refCounter . forEach ( ( refers , url ) => {
846
870
if ( refers . size > 1 ) {
847
- const localDepEntryMods = Array . from ( depMods . entries ( ) ) . filter ( ( [ url , isEntry ] ) => ! util . isLikelyHttpURL ( url ) && isEntry ) . map ( ( [ url ] ) => url )
848
- const exported = Array . from ( refers ) . filter ( url => entryMods . has ( url ) || localDepEntryMods . includes ( url ) ) . length > 1
871
+ const localDepEntryMods = Array . from ( depMods . entries ( ) )
872
+ . filter ( ( [ url , isEntry ] ) => ! util . isLikelyHttpURL ( url ) && isEntry )
873
+ . map ( ( [ url ] ) => url )
874
+ const exported = Array . from ( refers )
875
+ . some ( url => entryMods . has ( url ) || localDepEntryMods . includes ( url ) )
849
876
if ( exported ) {
850
877
addDepEntry ( url )
851
878
} else if ( ! depMods . has ( url ) ) {
@@ -885,7 +912,11 @@ export class Application implements ServerApplication {
885
912
const htmlFile = path . join ( outputDir , pathname , 'index.html' )
886
913
await ensureTextFile ( htmlFile , html )
887
914
if ( data ) {
888
- const dataFile = path . join ( outputDir , '_aleph/data' , ( pathname === '/' ? 'index' : pathname ) + '.json' )
915
+ const dataFile = path . join (
916
+ outputDir ,
917
+ '_aleph/data' ,
918
+ ( pathname === '/' ? 'index' : pathname ) + '.json'
919
+ )
889
920
await ensureTextFile ( dataFile , JSON . stringify ( data ) )
890
921
}
891
922
log . info ( ' ○' , pathname , colors . dim ( '• ' + util . formatBytes ( html . length ) ) )
@@ -922,7 +953,7 @@ export class Application implements ServerApplication {
922
953
}
923
954
924
955
/** fetch remote module content */
925
- private async fetchModule ( url : string ) : Promise < [ Uint8Array , Headers ] > {
956
+ async fetchModule ( url : string ) : Promise < [ Uint8Array , Headers ] > {
926
957
const u = new URL ( url )
927
958
if ( url . startsWith ( 'https://esm.sh/' ) ) {
928
959
if ( this . isDev && ! u . searchParams . has ( 'dev' ) ) {
@@ -934,7 +965,7 @@ export class Application implements ServerApplication {
934
965
const { protocol, hostname, port, pathname, search } = u
935
966
const versioned = reFullVersion . test ( pathname )
936
967
const reload = this . #reloading || ! versioned
937
- const isLocalhost = / ^ h t t p s ? : \/ \ /l o c a l h o s t ( : \d + ) ? \/ / . test ( url )
968
+ const isLocalhost = url . startsWith ( 'http:/ /localhost:' )
938
969
const cacheDir = path . join (
939
970
this . #dirs. get ( 'denoDir' ) ! ,
940
971
'deps' ,
0 commit comments