(
client: NormalizedHotChannelClient,
files: string[],
hmrOutput: HmrOutput,
invalidateInformation?: { firstInvalidatedBy: string },
)
| 408 | } |
| 409 | |
| 410 | private handleHmrOutput( |
| 411 | client: NormalizedHotChannelClient, |
| 412 | files: string[], |
| 413 | hmrOutput: HmrOutput, |
| 414 | invalidateInformation?: { firstInvalidatedBy: string }, |
| 415 | ) { |
| 416 | if (hmrOutput.type === 'Noop') return |
| 417 | |
| 418 | const shortFile = files |
| 419 | .map((file) => getShortName(file, this.environment.config.root)) |
| 420 | .join(', ') |
| 421 | if (hmrOutput.type === 'FullReload') { |
| 422 | const reason = hmrOutput.reason |
| 423 | ? colors.dim(` (${hmrOutput.reason})`) |
| 424 | : '' |
| 425 | this.environment.logger.info( |
| 426 | colors.green(`trigger page reload `) + colors.dim(shortFile) + reason, |
| 427 | { clear: !invalidateInformation, timestamp: true }, |
| 428 | ) |
| 429 | if (invalidateInformation) { |
| 430 | // Invalidate does not get upgraded to `rebuild`, |
| 431 | // so `onOutput` will not be triggered and thus the reload needs to be triggered here. |
| 432 | this.devEngine.ensureLatestBuildOutput().then(async () => { |
| 433 | this.debouncedFullReload() |
| 434 | }) |
| 435 | } else { |
| 436 | // Use a flag to defer the reload until the `onOutput` callback to avoid error lay flashes. |
| 437 | this.fullReloadPending = true |
| 438 | } |
| 439 | return |
| 440 | } |
| 441 | |
| 442 | debug?.(`handle hmr output for ${shortFile}`, { |
| 443 | ...hmrOutput, |
| 444 | code: typeof hmrOutput.code === 'string' ? '[code]' : hmrOutput.code, |
| 445 | }) |
| 446 | |
| 447 | this.memoryFiles.set(hmrOutput.filename, { |
| 448 | // ensure that the generated hmr patch contains ESM syntax |
| 449 | // this is to avoid attacks like GHSA-4v9v-hfq4-rm2v |
| 450 | // https://github.com/webpack/webpack-dev-server/security/advisories/GHSA-4v9v-hfq4-rm2v |
| 451 | // https://green.sapphi.red/blog/local-server-security-best-practices#_2-using-xssi-and-modifying-the-prototype |
| 452 | // https://green.sapphi.red/blog/local-server-security-best-practices#properly-check-the-request-origin |
| 453 | // we can also use `Cross-Origin Resource Policy` header instead of this |
| 454 | // but we cannot use `Sec-Fetch-*` headers as they are only sent to potentially-trustworthy origins |
| 455 | source: hmrOutput.code + '\n; export {}', |
| 456 | }) |
| 457 | if (hmrOutput.sourcemapFilename && hmrOutput.sourcemap) { |
| 458 | this.memoryFiles.set(hmrOutput.sourcemapFilename, { |
| 459 | source: hmrOutput.sourcemap, |
| 460 | }) |
| 461 | } |
| 462 | const updates: Update[] = hmrOutput.hmrBoundaries.map((boundary: any) => { |
| 463 | return { |
| 464 | type: 'js-update', |
| 465 | url: hmrOutput.filename, |
| 466 | path: boundary.boundary, |
| 467 | acceptedPath: boundary.acceptedVia, |
no test coverage detected