Skip to content

Conversation

robobun
Copy link
Collaborator

@robobun robobun commented Oct 6, 2025

Fixes #23299

What does this PR do?

Fixes a bug where TypeScript files imported with type: "text" import attributes were incorrectly executed as TypeScript instead of being treated as plain text.

Root cause: The bundler's pathToSourceIndexMap cache didn't account for files being imported with different loaders via import attributes. When the same file was encountered with a different loader, it would skip re-parsing and reuse the cached version with the wrong loader.

Solution: Skip the path cache lookup when an import has an explicit loader from import attributes (e.g., with { type: "text" }). This ensures files are parsed with the correct loader even if they were previously parsed with a different loader.

How did you verify your code works?

Added regression tests in test/regression/issue/23299.test.ts:

  1. Test that .ts files imported with type: "text" are treated as text, not executed as TypeScript
  2. Test that TypeScript files can be correctly imported as text-only

All tests pass:

✓ TypeScript file imported with type: 'text' should be treated as text, not executed - issue #23299
✓ TypeScript file should compile when imported normally even if also imported as text - issue #23299

🤖 Generated with Claude Code

When a TypeScript file was imported with `type: "text"` import attribute,
the bundler would incorrectly reuse a cached parsed version of the file
that had been compiled as TypeScript, instead of treating it as plain text.

Root cause: The bundler's path-to-source-index cache didn't account for
files being imported with different loaders via import attributes.

Fix: Skip the path cache lookup when an import has an explicit loader
from import attributes (e.g., `with { type: "text" }`). This ensures
files are parsed with the correct loader even if they were previously
parsed with a different loader.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@robobun
Copy link
Collaborator Author

robobun commented Oct 6, 2025

Updated 10:07 AM PT - Oct 6th, 2025

❌ Your commit 1e9b35dd has 1 failures in Build #28180 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 23300

That installs a local version of the PR into your bun-23300 executable, so you can run:

bun-23300 --bun

@github-actions github-actions bot added the claude label Oct 6, 2025
Copy link
Contributor

coderabbitai bot commented Oct 6, 2025

Walkthrough

Implements explicit loader-aware import handling in the bundler by bypassing cached source indices and avoiding parse-task reuse when loader types differ. Adjusts resolution to compare loaders before reuse. Adds regression tests ensuring TypeScript imports with with { type: "text" } are treated as text and not executed.

Changes

Cohort / File(s) Summary
Bundler loader handling
src/bundler/bundle_v2.zig
Adds has_explicit_loader path that bypasses source-index cache; prevents ParseTask reuse on loader mismatch; updates resolution to compare existing vs. import_record loader before reuse; retains prior behavior when no explicit loader; adds clarifying comments.
Regression tests for text loader
test/regression/issue/23299.test.ts
Adds tests for issue \#23299 verifying imports with with { type: "text" } bundle as string literals and are not executed; confirms normal TS imports still compile alongside text imports.

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly describes the core change by indicating a fix for handling import attributes with type: "text", matching the modifications to loader logic in the bundler and aligning with the pull request’s intent.
Linked Issues Check ✅ Passed The changes implement loader-aware caching by skipping the source-index cache when an import has an explicit loader or when the loader mismatches the existing task (as required by issue #23299), and the added regression tests verify that .ts files imported with type: "text" are delivered as raw text and not executed while preserving normal imports.
Out of Scope Changes Check ✅ Passed All modifications are confined to loader handling in bundle_v2.zig and relevant regression tests under test/regression/issue/23299.test.ts, directly addressing the linked issue without introducing unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Description Check ✅ Passed The pull request description includes both required sections “What does this PR do?” and “How did you verify your code works?” exactly as specified in the repository template, and it provides clear details on the root cause, solution, and test verification steps.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/fix-text-import-23299

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/bundler/bundle_v2.zig (1)

3474-3487: Comment is misleading about control flow.

The comment on line 3482 states "Fall through" but the code executes continue on line 3486, which does not fall through—it skips to the next loop iteration. This creates confusion about the intended behavior.

Additionally, this code path reveals a limitation: within a single file, if the same path is imported multiple times with different explicit loaders, only the first loader will be used. Consider clarifying the comment to explain this is a known limitation rather than suggesting a fall-through behavior.

Apply this diff to clarify the comment:

-                // Fall through: Different loader required, but we can't add to resolve_queue
-                // with the same key. Set the path and move on - the loader mismatch will
-                // be handled when this import is resolved.
+                // Known limitation: Different loader required, but we can't add another entry
+                // to resolve_queue with the same key. The first loader wins for this path.
+                // This limitation only affects files that import the same path multiple times
+                // with different explicit loaders, which is uncommon.
                 import_record.path = resolve_entry.value_ptr.*.path;
                 continue;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 166c8ff and 1e9b35d.

📒 Files selected for processing (2)
  • src/bundler/bundle_v2.zig (2 hunks)
  • test/regression/issue/23299.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
test/**

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place all tests under the test/ directory

Files:

  • test/regression/issue/23299.test.ts
test/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable

Files:

  • test/regression/issue/23299.test.ts
test/**/*.test.ts

📄 CodeRabbit inference engine (test/CLAUDE.md)

test/**/*.test.ts: Name test files *.test.ts and use bun:test
Do not write flaky tests: never wait for arbitrary time; wait for conditions instead
Never hardcode port numbers in tests; use port: 0 to get a random port
When spawning Bun in tests, use bunExe() and bunEnv from harness
Prefer async/await in tests; for a single callback, use Promise.withResolvers()
Do not set explicit test timeouts; rely on Bun’s built-in timeouts
Use tempDir/tempDirWithFiles from harness for temporary files and directories in tests
For large/repetitive strings in tests, prefer Buffer.alloc(count, fill).toString() over "A".repeat(count)
Import common test utilities from harness (e.g., bunExe, bunEnv, tempDirWithFiles, tmpdirSync, platform checks, GC helpers)
In error tests, assert non-zero exit codes for failing processes and use toThrow for synchronous errors
Use describe blocks for grouping, describe.each for parameterized tests, snapshots with toMatchSnapshot, and lifecycle hooks (beforeAll, beforeEach, afterEach); track resources for cleanup in afterEach
Use using/await using with Bun resources (e.g., Bun.listen/connect/spawn/serve) to ensure cleanup in tests

Files:

  • test/regression/issue/23299.test.ts
test/regression/issue/**

📄 CodeRabbit inference engine (test/CLAUDE.md)

Place regression tests under test/regression/issue/ and organize by issue number

Place regression tests under test/regression/issue/ (one per bug fix)

Files:

  • test/regression/issue/23299.test.ts
test/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

test/**/*.test.{ts,tsx}: Test files must live under test/ and end with .test.ts or .test.tsx
In tests, always use port: 0; do not hardcode ports or roll your own random port
Prefer normalizeBunSnapshot for snapshotting test output instead of asserting raw strings
Do not write tests that assert absence of crashes (e.g., 'no panic' or 'no uncaught exception')
Use Bun’s Jest-compatible runner (import { test, expect } from "bun:test") for tests
Avoid shell commands like find or grep in tests; use Bun’s Glob and built-in tools instead
Prefer running tests via bun bd test and use provided harness utilities (bunEnv, bunExe, tempDir)
Use Bun.spawn with proper stdio handling and await proc.exited in process-spawning tests

Files:

  • test/regression/issue/23299.test.ts
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

Implement debug logs in Zig using const log = bun.Output.scoped(.${SCOPE}, false); and invoking log("...", .{})

In Zig code, manage memory carefully and use defer for cleanup of allocations/resources

src/**/*.zig: In Zig source files, place all @import statements at the bottom of the file
Use @import("bun") instead of @import("root").bun when importing Bun in Zig

Files:

  • src/bundler/bundle_v2.zig
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

Files:

  • src/bundler/bundle_v2.zig
🧠 Learnings (10)
📚 Learning: 2025-08-30T00:09:39.100Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.100Z
Learning: Applies to test/bake/dev/css.test.ts : css.test.ts should contain CSS bundling tests in dev mode

Applied to files:

  • test/regression/issue/23299.test.ts
📚 Learning: 2025-10-04T09:51:30.265Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T09:51:30.265Z
Learning: Applies to test/**/*.test.{ts,tsx} : Prefer running tests via bun bd test <file> and use provided harness utilities (bunEnv, bunExe, tempDir)

Applied to files:

  • test/regression/issue/23299.test.ts
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : When spawning Bun in tests, use `bunExe()` and `bunEnv` from `harness`

Applied to files:

  • test/regression/issue/23299.test.ts
📚 Learning: 2025-10-04T09:51:30.265Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T09:51:30.265Z
Learning: Applies to test/**/*.test.{ts,tsx} : Use Bun’s Jest-compatible runner (import { test, expect } from "bun:test") for tests

Applied to files:

  • test/regression/issue/23299.test.ts
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/**/*.{js,ts} : Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test

Applied to files:

  • test/regression/issue/23299.test.ts
📚 Learning: 2025-10-04T09:51:30.265Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T09:51:30.265Z
Learning: Applies to test/**/*.test.{ts,tsx} : Prefer normalizeBunSnapshot for snapshotting test output instead of asserting raw strings

Applied to files:

  • test/regression/issue/23299.test.ts
📚 Learning: 2025-10-04T09:51:30.265Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-04T09:51:30.265Z
Learning: Applies to test/**/*.test.{ts,tsx} : Use Bun.spawn with proper stdio handling and await proc.exited in process-spawning tests

Applied to files:

  • test/regression/issue/23299.test.ts
📚 Learning: 2025-08-30T00:12:56.803Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.803Z
Learning: Applies to test/cli/**/*.{js,ts} : When testing Bun as a CLI, use spawn with bunExe() and bunEnv from harness, and capture stdout/stderr via pipes

Applied to files:

  • test/regression/issue/23299.test.ts
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Import common test utilities from `harness` (e.g., `bunExe`, `bunEnv`, `tempDirWithFiles`, `tmpdirSync`, platform checks, GC helpers)

Applied to files:

  • test/regression/issue/23299.test.ts
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Name test files `*.test.ts` and use `bun:test`

Applied to files:

  • test/regression/issue/23299.test.ts
🧬 Code graph analysis (1)
test/regression/issue/23299.test.ts (1)
test/harness.ts (3)
  • tempDir (278-285)
  • bunExe (103-106)
  • normalizeBunSnapshot (1809-1840)
🔇 Additional comments (3)
test/regression/issue/23299.test.ts (2)

4-51: LGTM! Well-structured regression test.

The test correctly verifies that TypeScript files imported with type: "text" are treated as plain text and not executed. Good use of harness utilities, proper resource cleanup with using, and appropriate assertions including snapshot verification.

Based on learnings.


53-80: LGTM! Clean and focused test.

The test properly verifies that TypeScript source code is preserved as text in the bundle when imported with type: "text", ensuring no compilation occurs.

Based on learnings.

src/bundler/bundle_v2.zig (1)

3448-3467: Core fix correctly skips cache for explicit loaders.

The introduction of has_explicit_loader and the conditional cache lookup properly address the issue where cached parse results were reused regardless of the loader specified in import attributes. When an explicit loader is present (e.g., with { type: "text" }), the cache is bypassed, ensuring the file is parsed with the correct loader.

@TNTSuperMan
Copy link

I tested this PR using bun-23300 to verify whether it resolves issue #23299, but the problem still persists.

@robobun
Copy link
Collaborator Author

robobun commented Oct 7, 2025

Closing PR - user @TNTSuperMan tested and confirmed the issue still persists. The fix doesn't actually work. Additionally, CI is failing on unrelated tests.

@robobun robobun closed this Oct 7, 2025
@robobun robobun reopened this Oct 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

When saving a .ts asset as text, it gets executed in the browser
2 participants