Skip to content

test: add missing file after an error should trigger a rebuild#8560

Closed
sapphi-red wants to merge 1 commit intomainfrom
03-06-test_add_missing_file_after_an_error_should_trigger_a_rebuild
Closed

test: add missing file after an error should trigger a rebuild#8560
sapphi-red wants to merge 1 commit intomainfrom
03-06-test_add_missing_file_after_an_error_should_trigger_a_rebuild

Conversation

@sapphi-red
Copy link
Copy Markdown
Member

No description provided.

Copy link
Copy Markdown
Member Author


How to use the Graphite Merge Queue

Add the label graphite: merge-when-ready to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@sapphi-red sapphi-red assigned hyf0 and unassigned sapphi-red Mar 6, 2026
@netlify
Copy link
Copy Markdown

netlify bot commented Mar 6, 2026

Deploy Preview for rolldown-rs ready!

Name Link
🔨 Latest commit 4020274
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/69aa60b22500fe0007a25ab0
😎 Deploy Preview https://deploy-preview-8560--rolldown-rs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Comment on lines +1135 to +1136
// Create the missing file — should trigger a successful rebuild
await editFile(path.join(cwd, 'foo.js'), `export const foo = 'added'`);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Rollup requires editing main.js after the missing file is created, we can require that as well.

hyf0 added a commit that referenced this pull request Mar 7, 2026
When a build fails because an imported file doesn't exist, the watcher
now tracks the target directory so it can detect when the missing file
is created and automatically trigger a rebuild.

## How it works

```
                     BUILD N (has error)
                     ══════════════════

watch_task.rs: build()

 1. active_missing_dirs.clear()
 2. bundler.clear_resolver_cache()
 3. scan_modules()
      │
      ▼
 resolve_utils.rs: resolve_dependencies()
      │
      │  import './utils/helper.js'
      │  → ResolveError::NotFound
      │
      ▼
 track_missing_imports()
      │
      │  computes target_dir:
      │    importer: /src/index.js
      │    specifier: ./utils/helper.js
      │    → /src/utils/  (parent of joined path)
      │
      ▼
 plugin_driver.missing_import_dirs.insert("/src/utils")

 4. update_watch_files_from()
      │
      │  reads missing_import_dirs → ["/src/utils"]
      │
      ├─► active_missing_dirs.insert("/src/utils")
      │
      └─► register with notify:
            /src/utils exists?
            ├─ YES → watch /src/utils (NonRecursive)
            │        registered_missing_dirs.insert()
            └─ NO  → walk up ancestors:
                     /src exists? YES → watch /src
                     (do NOT add to registered — retry
                      next build)

 5. Build errors → emit error event to user
    (files still watched even on error)


                     WAITING
                     ═══════

User creates /src/utils/helper.js

 notify emits: EventKind::Create
      │
      ▼
 task_fs_event_handler.rs: handle_event()
      │
      │  maps Create → WatcherChangeKind::Create
      │  path: "/src/utils/helper.js"
      │
      ▼
 watcher.rs: receives FileChanges message
      │
      ▼
 watch_task.rs: mark_needs_rebuild()
      │
      │  watched_files.contains(path)?  NO
      │  kind == Create?                YES
      │
      │  Check 1: active_missing_dirs
      │    .contains("/src/utils/helper.js")?  NO
      │
      │  Check 2: parent = "/src/utils"
      │    active_missing_dirs
      │    .contains("/src/utils")?  YES ✓
      │
      └─► needs_rebuild = true


                     BUILD N+1 (recovery)
                     ════════════════════

watch_task.rs: build()

 1. active_missing_dirs.clear()
 2. bundler.clear_resolver_cache()  ← stale cache gone
 3. scan_modules()
      │
      │  import './utils/helper.js'
      │  → resolves to /src/utils/helper.js  ✓
      │
      │  (no ResolveError → no track_missing_imports)
      │
 4. active_missing_dirs stays empty
    (registered_missing_dirs still has "/src/utils"
     — harmless, just means notify keeps watching it)

 5. Build succeeds → emit success event


             ANCESTOR FALLBACK VARIANT
             ═════════════════════════
             (when target dir doesn't exist either)

import './new-folder/file.js'  →  target_dir = /src/new-folder
/src/new-folder doesn't exist  →  watch /src (ancestor)
NOT added to registered_missing_dirs

User creates /src/new-folder/  →  notify: Create "/src/new-folder"
mark_needs_rebuild:
  active_missing_dirs.contains("/src/new-folder")?  YES → rebuild

Build N+1:
  /src/new-folder exists now  →  watch it directly
  registered_missing_dirs.insert("/src/new-folder")
  import still fails (file.js not there yet)
  → track_missing_imports records /src/new-folder again

User creates /src/new-folder/file.js  →  parent check matches → rebuild
Build N+2: resolves successfully ✓
```

## Key design points

- **`active_missing_dirs`** is per-build (cleared + repopulated) — stale
dirs from fixed imports don't trigger spurious rebuilds
- **`registered_missing_dirs`** is monotonic — purely prevents redundant
`notify` registrations
- **Ancestor fallback** retried each build — only "locks in" when the
actual target dir is watched directly
- **Resolver cache always cleared** — `package.json`/`tsconfig` edits
also invalidate resolutions
- **Watch files registered even on error** — user can fix the problem
and recovery happens automatically

## Tests

Includes test fixes from #8560 — the tests now use named imports
(`import { foo } from './foo.js'`) instead of side-effect imports
(`import './foo.js'`) to avoid tree-shaking removing the imported
content from output.

All 27 watch tests pass with zero regressions.

Closes the functionality gap noted in #8560.

---------

Co-authored-by: sapphi-red <[email protected]>
Co-authored-by: hyf0-agent <[email protected]>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Yunfei He <[email protected]>
@hyf0
Copy link
Copy Markdown
Member

hyf0 commented Mar 7, 2026

Fixed in #8562.

@hyf0 hyf0 closed this Mar 7, 2026
graphite-app bot pushed a commit that referenced this pull request Mar 8, 2026
Partially reverts #8562 but still keeps the fix for #8560
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.

2 participants