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

Commit bad514f

Browse files
committed
Replace dep hash if it changes (fix #320)
1 parent 16483f9 commit bad514f

File tree

2 files changed

+58
-40
lines changed

2 files changed

+58
-40
lines changed

server/aleph.ts

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,9 @@ export class Aleph implements IAleph {
345345
e.emit('modify-' + module.specifier, { refreshPage: refreshPage || undefined })
346346
})
347347
}
348-
this.applyCompilationSideEffect(module, ({ specifier }) => {
348+
this.applyCompilationSideEffect(module, ({ specifier, hash }) => {
349349
if (!hmrable && this.isHMRable(specifier)) {
350-
log.debug('compilation side-effect:', specifier, dim('<-'), module.specifier)
350+
log.debug(`compilation side-effect: ${specifier}(${hash?.substr(0, 6)}) ${dim('<-')} ${module.specifier}(${module.hash?.substr(0, 6)})`)
351351
this.#fsWatchListeners.forEach(e => {
352352
e.emit('modify-' + specifier, { refreshPage: refreshPage || undefined })
353353
})
@@ -895,7 +895,7 @@ export class Aleph implements IAleph {
895895
return await import(`file://${join(this.buildDir, jsFile)}#${(hash || sourceHash).slice(0, 6)}`)
896896
}
897897

898-
async readModuleJS(module: Module): Promise<Uint8Array | null> {
898+
async readModuleJS(module: Module, injectHMRCode = false): Promise<Uint8Array | null> {
899899
const { specifier, jsFile, jsBuffer } = module
900900
if (!jsBuffer) {
901901
const cacheFp = join(this.buildDir, jsFile)
@@ -905,8 +905,12 @@ export class Aleph implements IAleph {
905905
}
906906
}
907907

908-
if (!this.isDev || !this.isHMRable(specifier) || !module.jsBuffer) {
909-
return module.jsBuffer || null
908+
if (!module.jsBuffer) {
909+
return null
910+
}
911+
912+
if (!injectHMRCode || !this.isHMRable(specifier)) {
913+
return module.jsBuffer
910914
}
911915

912916
let code = new TextDecoder().decode(module.jsBuffer)
@@ -915,16 +919,17 @@ export class Aleph implements IAleph {
915919
code = (module as any).csrCode
916920
} else {
917921
const { code: csrCode } = await stripSsrCode(specifier, code, { sourceMap: true, swcOptions: { sourceType: SourceType.JS } })
922+
// cache csr code
918923
Object.assign(module, { csrCode })
919-
// todo: merge source map
920924
code = csrCode
925+
// todo: merge source map
921926
}
922927
}
923928
this.#transformListeners.forEach(({ test, transform }) => {
924929
if (test === 'hmr') {
925930
const ret = transform({ specifier, code })
926-
// todo: merge source map
927931
code = ret.code
932+
// todo: merge source map
928933
}
929934
})
930935
return new TextEncoder().encode([
@@ -1239,10 +1244,6 @@ export class Aleph implements IAleph {
12391244
])
12401245
}
12411246

1242-
if (ignoreDeps) {
1243-
return
1244-
}
1245-
12461247
if (module.deps.length > 0) {
12471248
let fsync = false
12481249
const encoder = new TextEncoder()
@@ -1252,49 +1253,54 @@ export class Aleph implements IAleph {
12521253
await this.readModuleJS(module)
12531254
}
12541255
await Promise.all(module.deps.map(async ({ specifier, hashLoc }) => {
1255-
const [depModule, depSource] = await this.initModule(specifier, { externalRemoteDeps })
1256-
if (!depModule.external) {
1257-
await this.transpileModule(depModule, depSource, false, __tracing)
1256+
let depModule: Module | null
1257+
if (ignoreDeps) {
1258+
depModule = this.getModule(specifier)
1259+
} else {
1260+
const [mod, src] = await this.initModule(specifier, { externalRemoteDeps })
1261+
if (!mod.external) {
1262+
await this.transpileModule(mod, src, false, __tracing)
1263+
}
1264+
depModule = mod
12581265
}
1259-
const hash = depModule.hash || depModule.sourceHash
1260-
if (hashLoc !== undefined) {
1261-
const { jsBuffer } = module
1262-
const hashData = encoder.encode((hash).substr(0, 6))
1263-
if (jsBuffer && !equals(hashData, jsBuffer.slice(hashLoc, hashLoc + 6))) {
1264-
copy(hashData, jsBuffer, hashLoc)
1265-
if (!fsync) {
1266+
if (depModule) {
1267+
const hash = depModule.hash || depModule.sourceHash
1268+
if (hashLoc !== undefined) {
1269+
if (await this.replaceDepHash(module, hashLoc, hash)) {
12661270
fsync = true
12671271
}
12681272
}
1273+
hasher.update(hash)
1274+
} else {
1275+
log.error(`transpile '${module.specifier}': missing dependency module '${specifier}'`)
12691276
}
1270-
hasher.update(hash)
12711277
}))
12721278
module.hash = hasher.toString()
12731279
if (fsync) {
1274-
await this.cacheModule(module)
1280+
await this.writeModule(module)
12751281
}
12761282
} else {
12771283
module.hash = module.sourceHash
12781284
}
12791285
}
12801286

12811287
/** apply compilation side-effect caused by updating dependency graph. */
1282-
private async applyCompilationSideEffect(by: Module, callback: (mod: Module) => void) {
1288+
private async applyCompilationSideEffect(by: Module, callback: (mod: Module) => void, __tracing = new Set<string>()) {
1289+
if (__tracing.has(by.specifier)) {
1290+
return
1291+
}
1292+
__tracing.add(by.specifier)
1293+
12831294
const hash = by.hash || by.sourceHash
1284-
const hashData = (new TextEncoder()).encode(hash.substr(0, 6))
12851295
for (const mod of this.#modules.values()) {
12861296
const { deps } = mod
12871297
if (deps.length > 0) {
12881298
let fsync = false
12891299
for (const dep of deps) {
12901300
const { specifier, hashLoc } = dep
12911301
if (specifier === by.specifier && hashLoc !== undefined) {
1292-
const jsCode = await this.readModuleJS(mod)
1293-
if (jsCode && !equals(hashData, jsCode.slice(hashLoc, hashLoc + 6))) {
1294-
copy(hashData, jsCode, hashLoc)
1295-
if (!fsync) {
1296-
fsync = true
1297-
}
1302+
if (await this.replaceDepHash(mod, hashLoc, hash)) {
1303+
fsync = true
12981304
}
12991305
}
13001306
}
@@ -1307,14 +1313,28 @@ export class Aleph implements IAleph {
13071313
}
13081314
})
13091315
mod.hash = hasher.toString()
1310-
await this.cacheModule(mod)
13111316
callback(mod)
1312-
await this.applyCompilationSideEffect(mod, callback)
1317+
this.applyCompilationSideEffect(mod, callback)
1318+
this.writeModule(mod)
13131319
}
13141320
}
13151321
}
13161322
}
13171323

1324+
/** replace dep hash in the `jsBuffer` and remove `csrCode` cache if it exits */
1325+
private async replaceDepHash(module: Module, hashLoc: number, hash: string) {
1326+
const hashData = (new TextEncoder()).encode(hash.substr(0, 6))
1327+
await this.readModuleJS(module)
1328+
if (module.jsBuffer && !equals(hashData, module.jsBuffer.slice(hashLoc, hashLoc + 6))) {
1329+
copy(hashData, module.jsBuffer, hashLoc)
1330+
if ('csrCode' in module) {
1331+
Reflect.deleteProperty(module, 'csrCode')
1332+
}
1333+
return true
1334+
}
1335+
return false
1336+
}
1337+
13181338
private clearSSRCache(specifier: string) {
13191339
if (trimBuiltinModuleExts(specifier) === '/app') {
13201340
this.#renderer.clearCache()
@@ -1324,7 +1344,7 @@ export class Aleph implements IAleph {
13241344
}
13251345
}
13261346

1327-
private async cacheModule(module: Module) {
1347+
private async writeModule(module: Module) {
13281348
const { specifier, jsBuffer, jsFile } = module
13291349
if (jsBuffer) {
13301350
const cacheFp = join(this.buildDir, jsFile)

server/server.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import { createHash } from 'https://deno.land/[email protected]/hash/mod.ts'
21
import { join } from 'https://deno.land/[email protected]/path/mod.ts'
32
import { builtinModuleExts, trimBuiltinModuleExts } from '../framework/core/module.ts'
43
import { resolveURL } from '../framework/core/routing.ts'
54
import { existsFile } from '../shared/fs.ts'
65
import log from '../shared/log.ts'
76
import util from '../shared/util.ts'
8-
import { VERSION } from '../version.ts'
97
import { APIContext } from '../types.ts'
108
import { Aleph } from './aleph.ts'
119
import compress from './compress.ts'
@@ -130,15 +128,15 @@ export class Server {
130128
}
131129
}
132130
if (module) {
133-
const content = await app.readModuleJS(module)
131+
const content = await app.readModuleJS(module, !!app.isDev)
134132
if (content) {
135-
const etag = createHash('md5').update(VERSION).update(module.hash || module.sourceHash).toString()
136-
if (etag === req.headers.get('If-None-Match')) {
133+
const hash = module.hash || module.sourceHash
134+
if (hash === req.headers.get('If-None-Match')) {
137135
resp.writeTo(e, 304)
138136
return
139137
}
140138

141-
resp.setHeader('ETag', etag)
139+
resp.setHeader('ETag', hash)
142140
resp.setHeader('Content-Type', 'application/javascript; charset=utf-8')
143141
resp.body = content
144142
resp.writeTo(e)

0 commit comments

Comments
 (0)