Skip to content

fix(plugin): resolve resolveVar() arguments imported from other modules - #92

Open
jttommy wants to merge 1 commit into
stenciljs:mainfrom
jttommy:fix/resolve-var-imported-constants
Open

jttommy wants to merge 1 commit into
stenciljs:mainfrom
jttommy:fix/resolve-var-imported-constants

Conversation

@jttommy

@jttommy jttommy commented Oct 1, 2026 •

Copy link
Copy Markdown

Pull request checklist

  • Tests for the changes have been added (for bug fixes / features)
  • Build (pnpm build) was run locally and passed
  • Tests (pnpm test:unit and pnpm test:e2e) were run locally and passed
  • Linting (pnpm lint) was run locally and passed

Pull request type

  • Bugfix
  • Feature
  • Refactoring (no functional changes)
  • Documentation
  • Other (please describe):

What is the current behavior?

resolveVar() lets @Event({ eventName }) and @Listen() read event names from a constant. Under stencil build that constant can live in another module. stencilVitestPlugin compiles each component with transpile() from @stencil/core/compiler, which type-checks a single file with module resolution disabled (noResolve: true). An imported catalog therefore fails:

[stencil-vitest-plugin] Transform error in .../my-emitter.tsx:
Error: resolveVar() cannot find property "PING_EVENT" on object "EVENTS.MY_EMITTER" at compile time.

The component never registers. Every resolveVar() test in stenciljs/core declares the constant in the same file, so this path was uncovered. Companion pull request: stenciljs/core#6924

Issue URL:

What is the new behavior?

Before handing a .tsx component to transpile(), the plugin resolves resolveVar() calls whose argument is rooted at a value import and inlines the string literal.

  • Same-file constants are left to Stencil.
  • A type-check-only ts.Program is built for that file with real module resolution, seeded from the nearest tsconfig.json, using the in-memory source Vite provides.
  • Values that do not resolve to a string literal are left untouched, so Stencil still reports its usual diagnostic.
  • The pre-pass falls back to the original source on failure.
  • Uses the TypeScript instance bundled in @stencil/core/compiler, so no new dependency is added.
  • If the type is widened to string, the declaration's string-literal initializer is read instead, matching the compiler.
  • The program is cached across files, and non-root sources are cached by mtime.

Does this introduce a breaking change?

  • Yes
  • No

Made with Cursor

Stencil's `transpile()` type-checks a single file with module resolution
disabled (`noResolve: true`, a compiler host whose `fileExists` only matches
the root file). `resolveVar()` therefore only works for constants declared in
the same file. Components that read event names from a shared catalog, e.g.

    import { EVENTS } from '@my-org/design-tokens';
    @event({ eventName: resolveVar(EVENTS.MY_EMITTER.PING_EVENT) })

compile fine under `stencil build` but fail inside `stencilVitestPlugin` with
`resolveVar() cannot find property "PING_EVENT" on object "EVENTS.MY_EMITTER"`,
which leaves the decorators unconverted and the component unable to load.

Before handing a component to `transpile()`, the plugin now:

- detects `resolveVar()` calls whose argument is rooted at a value import
- builds a type-check-only `ts.Program` for that file with real module
  resolution, seeded from the nearest `tsconfig.json` (so `paths`, `baseUrl`
  and `moduleResolution` match the project)
- inlines the resolved string literal in place of the call

Same-file constants are untouched (Stencil already handles them), and
arguments that do not resolve to a string literal are passed through so the
Stencil compiler reports its usual diagnostic. The TypeScript instance is the
one bundled in `@stencil/core/compiler`, so no new dependency is added.

Adds unit coverage for the helper and a `my-emitter` fixture + plugin spec in
`test/project` that fails without this change.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jttommy

jttommy commented Oct 1, 2026

Copy link
Copy Markdown
Author

Companion: stenciljs/core#6924

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.

1 participant