Skip to content

fix(plugin): follow aliased/tsconfig-path imports into the MT worklet graph#190

Merged
Huxpro merged 11 commits into
Huxpro:mainfrom
KealanAU:fix/mt-worklet-loader-resolve-imports
Jul 15, 2026
Merged

fix(plugin): follow aliased/tsconfig-path imports into the MT worklet graph#190
Huxpro merged 11 commits into
Huxpro:mainfrom
KealanAU:fix/mt-worklet-loader-resolve-imports

Conversation

@KealanAU

@KealanAU KealanAU commented May 30, 2026

Copy link
Copy Markdown
Contributor

Fixes #191.

Summary

MT worklets imported through aliases, tsconfig paths, or package specifiers were dropped from the main-thread graph, so their registrations never ran.

  • Makes worklet-loader-mt async and resolves non-relative imports with this.getResolve(...).
  • Follows project/aliased source outside node_modules and re-emits the original specifier for bundler resolution.
  • Adds opt-in includeWorkletPackages (string or RegExp) for packages that intentionally ship MT worklets.
  • Keeps relative imports and shared/runtime/vue sub-module skips unchanged.

Allowlist matching (addresses review)

Both decision points now reduce their input to a package root before matching, so a specifier and a resolved path always compare as the same thing.

  • packageRootFromSpecifier reduces an import specifier (@org/foo/dist/x@org/foo).
  • packageNameFromNodeModulesPath reduces a resolved path (…/node_modules/@org/foo/dist/index.js@org/foo), using the last node_modules segment so nested deps and pnpm layouts resolve.
  • Both feed the single isWorkletPackage matcher, so /^@org\// matches at the follow checkpoint AND the loader carve-out — no more silent misses for RegExp users.

Safety

  • Unresolvable specifiers are skipped instead of failing the build.
  • Imports inside comments and string/template literals are masked before extraction, so doc examples and code-building worklets aren't followed as real edges.
  • Backward compatible: this only adds followed graph edges.

Verification

  • worklet-loader-imports.test.ts: 46 tests, including a path-vs-specifier RegExp parity test and end-to-end RegExp/subpath cases through the real loader.
  • Full testing-library suite green in CI; plugin build passes; Biome clean.

The MT worklet loader only re-emitted relative specifiers, so worklets
reached via path aliases, tsconfig paths or bare packages were dropped
from the MT module graph — their registerWorkletInternal never ran and
the worklet threw 'cannot read property bind of undefined' at runtime.

Switch worklet-loader-mt to an async loader and resolve each non-relative
specifier via this.getResolve. Imports resolving to project/aliased source
(outside node_modules) are now followed; imports into node_modules are
followed only when allowlisted via the new includeWorkletPackages option
(plus a node_modules exclude carve-out so the loader runs on them).
Relative imports are unchanged. Unresolvable specifiers are skipped.
@vercel

vercel Bot commented May 30, 2026

Copy link
Copy Markdown

@KealanAU is attempting to deploy a commit to the huxpro's projects Team on Vercel.

A member of the Team first needs to authorize it.

@KealanAU
KealanAU marked this pull request as ready for review May 31, 2026 05:32
… graph

The MT worklet loader's import extractor matched `import … from …`
text anywhere in a module, including JSDoc examples. Following aliased
imports into vue-lynx's own source pulled in runtime/src/index.ts,
whose docblock example `import App from './App.vue'` was re-emitted as
a real import and failed to resolve, breaking the dev-smoke build.

Strip line/block comments (preserving string/template literals) before
extracting specifiers in extractImportSpecifiers and extractSharedImports.
@KealanAU

Copy link
Copy Markdown
Contributor Author

Follow-up: CI Dev Smoke caught a build failure exposed by this change.

  • Following aliased imports into vue-lynx's own source now pulls in runtime/src/index.ts.
  • Its import-extractor matched a JSDoc example (* import App from './App.vue';) as a real edge
    and re-emitted it, which can't resolve → build failed.
  • Fix: stripComments (preserving string/template literals) runs before specifier extraction in
    extractImportSpecifiers and extractSharedImports.
  • Added tests for imports in line/block comments and for comment delimiters inside string
    literals.

Parser-level fix rather than deleting the docblock — comment imports shouldn't be load-bearing
for the build regardless of which followed module they live in.

KealanAU added 2 commits May 31, 2026 11:49
…imports

Tokenize literals before scanning for import edges so import-like text
inside a string or template literal is never followed, and detect
`with { … }` attributes across line breaks so a reformatted
`runtime: 'shared'` import no longer leaks through as a plain local
import. Adds a parametrized parser test table plus emitted-output and
resolver-rejection regression guards.

@Huxpro Huxpro left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Blocking from my side: includeWorkletPackages now documents/tests RegExp support, but the matching model is still inconsistent across the code path this PR is fixing.

At one decision point the regex is matched against the original import specifier/package name, but when the traversal logic decides whether to keep following into the resolved node_modules target, the effective input is the resolved absolute path. That means a documented pattern like /^@my-org\// will match @my-org/foo but can never match something like /project/node_modules/@my-org/foo/dist/index.js, so RegExp users can still silently miss worklets here.

I think this needs one stable matching input at both checkpoints, ideally the original package specifier/package root rather than a filesystem path.

KealanAU added 5 commits June 27, 2026 08:50
…ame at both checkpoints

The node_modules loader-exclude matched the allowlist against the resolved
absolute path while the import-follow checkpoint matched the original
specifier. A RegExp like /^@my-org\// matched @my-org/foo but never
.../node_modules/@my-org/foo/dist/index.js, silently dropping the package.

Derive the package name from the resolved path (packageNameFromNodeModulesPath)
and route both checkpoints through isWorkletPackage, so the allowlist always
matches a package specifier, never a filesystem path.
…gh the loader

End-to-end loader tests only used string allowlists, which pass even if
RegExp handling regresses. Add a runLoaderMT test that follows a bare npm
import via /^@org\// (resolver returns an absolute node_modules path) and
confirms a non-matching RegExp still drops it.
Previously checkpoint A matched the full import specifier while checkpoint B
matched the package root derived from the resolved path. Equivalent for
package-name patterns but divergent for subpath-sensitive ones.

Add packageRootFromSpecifier and reduce both inputs to the package root before
matching, so a specifier and a resolved path always compare as the same thing.
The allowlist matcher is now a plain exact/RegExp compare.
@KealanAU

Copy link
Copy Markdown
Contributor Author

Addressed — both checkpoints now match one stable input.

I added packageRootFromSpecifier (specifier → package root) and packageNameFromNodeModulesPath (resolved path → package root, via the last node_modules segment for nested/pnpm layouts). Both checkpoints reduce to that root and route through a single isWorkletPackage matcher, so the allowlist always compares against a package specifier, never a filesystem path.

Concretely, /^@my-org\// now matches at the import-follow checkpoint AND the node_modules loader carve-out for /project/node_modules/@my-org/foo/dist/index.js — the divergence you flagged is gone.

Pinned by tests:

  • packageNameFromNodeModulesPath path-derived name matches the same RegExp allowlist as a specifier.
  • A RegExp allowlist entry followed end-to-end through the real loader (allowed vs. dropped).
  • A subpath import (@org/motion/extras) matched against the package-root allowlist (@org/motion).

Commits: 980f3f1 (route both checkpoints through isWorkletPackage) and b485625 (reduce both inputs to the package root so subpath-sensitive patterns can't diverge).

@Huxpro Huxpro left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the thorough follow-through here. The matching model is now consistent across both checkpoints — reducing specifiers and resolved paths to a package root and running both through a single isWorkletPackage matcher fully addresses my earlier concern, and the path-vs-specifier RegExp parity test plus the end-to-end subpath case make it hard to regress. The comment/literal masking is a clean parser-level fix for the JSDoc-import build failure rather than a one-off patch. Behavior stays backward-compatible (only additive graph edges; unresolvable specifiers are skipped), CI is green, and the 46 tests cover the fragile surfaces well. LGTM.

(The red Vercel status is just the external-contributor deploy-authorization gate, not a code failure.)


Generated by Claude Code

@Huxpro Huxpro left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Second-pass review complete. Fixed the stateful RegExp allowlist edge case by testing against a cloned expression, added regression coverage for both global and sticky flags (including preserving caller lastIndex), and reran the focused suite (48/48), lint, and the full monorepo build locally. All six repository CI checks are green on 3c01812; the remaining Vercel status is the known fork authorization failure rather than a code failure.

@Huxpro
Huxpro merged commit a79f2b5 into Huxpro:main Jul 15, 2026
6 of 7 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 15, 2026
Huxpro added a commit that referenced this pull request Jul 15, 2026
…mports

fix(plugin): follow aliased/tsconfig-path imports into the MT worklet graph
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.

Aliased / package imports are silently dropped from the main-thread worklet graph

2 participants