Skip to content

Remove hmr runtime ts expect errors#797

Draft
vykimnguyen wants to merge 4 commits intomainfrom
remove-hmr-runtime-ts-expect-errors
Draft

Remove hmr runtime ts expect errors#797
vykimnguyen wants to merge 4 commits intomainfrom
remove-hmr-runtime-ts-expect-errors

Conversation

@vykimnguyen
Copy link
Copy Markdown
Contributor

Motivation

Remove ts-expect-errors in hmr-runtime.ts

Changes

This is all rovodev's work with Marcin's prompt

Checklist

  • Existing or new tests cover this change
  • There is a changeset for this change, or one is not required
  • Added documentation for any new features to the docs/ folder

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Sep 17, 2025

🦋 Changeset detected

Latest commit: 40cab87

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@atlaspack/runtime-browser-hmr Patch
@atlaspack/config-default Patch
atlaspack Patch
@atlaspack/config-webextension Patch
@atlaspack/cli Patch
@atlaspack/register Patch
@atlaspack/test-utils Patch
@atlaspack/inspector Patch
@atlaspack/inspector-frontend Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Sep 17, 2025

📊 Type Coverage Report

Coverage Comparison

Metric Baseline Current Change
Coverage Percentage 92.26% 92.30% 📈 +0.04%
Correctly Typed 220,731 221,029 +298
Total Expressions 239,225 239,454 +229
Untyped Expressions 18,494 18,425 -69

Files with Most Type Issues (Top 15)

File Issues Affected Lines
packages/core/integration-tests/test/javascript.ts 1152 745
packages/core/integration-tests/test/cache.ts 885 626
packages/core/integration-tests/test/scope-hoisting.ts 622 489
packages/utils/node-resolver-core/test/resolver.ts 476 177
packages/core/integration-tests/test/html.ts 468 294
packages/core/integration-tests/test/sourcemaps.ts 356 176
packages/core/test-utils/src/utils.ts 330 205
packages/core/integration-tests/test/incremental-bundling.ts 298 206
packages/core/core/src/dumpGraphToGraphViz.ts 251 108
packages/core/integration-tests/test/transpilation.ts 230 139
packages/core/integration-tests/test/output-formats.ts 227 161
packages/transformers/webextension/src/WebExtensionTransformer.ts 210 80
packages/core/core/src/requests/BundleGraphRequestRust.ts 194 67
packages/core/integration-tests/test/css-modules.ts 191 107
packages/core/core/src/requests/TargetRequest.ts 190 133

This report was generated by the Type Coverage GitHub Action

} catch (err: any) {
if (err.message) {
} catch (err: unknown) {
if (err instanceof Error && err.message) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty minor, but best to avoid changing implementation where possible. If we change impl, it needs to be feature flagged and becomes a whole thing

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't entirely disagree, but I think this is the standard pattern in typescript for dealing with errors. I don't think catch (err: Error) is allowed.

// support for source maps is better with eval.
(0, eval)(asset.output);
if (asset.output) {
(0, eval)(asset.output);

Check failure

Code scanning / CodeQL

Code injection Critical

This code execution depends on a
user-provided value
.

Copilot Autofix

AI 7 months ago

To fix this issue, we should avoid blindly evaluating arbitrary code received from a network source (even in a local development context). The safest mitigation is to eliminate or strictly limit dynamic code execution via eval. For HMR systems, one standard solution is to validate both the type and contents of incoming assets, and to only allow code that matches expected patterns, or ideally, to load updated modules via <script> tags (allowing built-in CSP and browser script loading protections) rather than evaluating strings.

For the context here:

  • Instead of (0, eval)(asset.output);, inject a <script> tag with the code if it is JavaScript and is from a trusted source.
  • At minimum, verify that the WebSocket origin is trusted before processing updates.
  • If dynamic eval is absolutely required for HMR (which is commonly the case only in development), make sure to restrict communications to authenticated channels and/or validate the format of asset.output to only correspond to bundles you expect.
  • If using the <script> method:
    • Create a <script> element
    • Set text content to asset.output
    • Set nonce/integrity if appropriate
    • Add to document.head
    • Remove after execution

Necessary changes:

  • Replace the direct call to eval(asset.output) at line 579 in packages/runtimes/hmr/src/loaders/hmr-runtime.ts with a script injection pattern.
  • Add a helper function for safe script injection if necessary.
  • Ensure that the asset type is 'js' before any code-injection logic is performed (already checked, but can reinforce).
  • Document that this change is only a partial mitigation, and the overall HMR protocol should have authentication/integrity checks.
Suggested changeset 1
packages/runtimes/hmr/src/loaders/hmr-runtime.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/runtimes/hmr/src/loaders/hmr-runtime.ts b/packages/runtimes/hmr/src/loaders/hmr-runtime.ts
--- a/packages/runtimes/hmr/src/loaders/hmr-runtime.ts
+++ b/packages/runtimes/hmr/src/loaders/hmr-runtime.ts
@@ -576,7 +576,13 @@
         // Global eval. We would use `new Function` here but browser
         // support for source maps is better with eval.
         if (asset.output) {
-          (0, eval)(asset.output);
+          // safer: inject script tag rather than direct eval
+          const script = document.createElement('script');
+          script.textContent = asset.output;
+          script.type = 'text/javascript';
+          // Optionally: set nonce and/or other attributes as per CSP requirements
+          document.head.appendChild(script);
+          document.head.removeChild(script);
         }
       }
 
EOF
@@ -576,7 +576,13 @@
// Global eval. We would use `new Function` here but browser
// support for source maps is better with eval.
if (asset.output) {
(0, eval)(asset.output);
// safer: inject script tag rather than direct eval
const script = document.createElement('script');
script.textContent = asset.output;
script.type = 'text/javascript';
// Optionally: set nonce and/or other attributes as per CSP requirements
document.head.appendChild(script);
document.head.removeChild(script);
}
}

Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth checking if HMR still works (you can setup a simple standalone project and use atlaspack-link) as I believe this file ends up running in the browser - this would be why the original used comment based flow types. Or at least you'd need to make sure that the client side is "compiled" so that all of the type checks are removed.

} catch (err: any) {
if (err.message) {
} catch (err: unknown) {
if (err instanceof Error && err.message) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't entirely disagree, but I think this is the standard pattern in typescript for dealing with errors. I don't think catch (err: Error) is allowed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants