Skip to content

vitest can't import a bundle's own index.ts: decorators aren't downleveled for SSR, and Node doesn't support them natively yet #816

Description

@Akshay-2007-1

What's broken

Any test that imports a bundle's own index.ts (a Conductor BaseModulePlugin subclass using @moduleMethod(...) decorators) fails at import time under vitest, even with a trivial test that does nothing but import the module:

import { describe, it, expect } from 'vitest';
import SoundModulePlugin from '../index';
describe('scratch', () => {
  it('imports', () => { expect(SoundModulePlugin).toBeTruthy(); });
});
FAIL src/__tests__/scratch-import-test.test.ts
SyntaxError: Invalid or unexpected token

No file/line is reported - it's a bare, native SyntaxError, not an esbuild-formatted transform error.

Root cause

  • src/bundles/*/tsconfig.json sets experimentalDecorators: false deliberately (standard/stage-3 decorators, matching @moduleMethod's own definition).
  • Under Vite's dev/SSR transform (used by vitest to run test files), esbuild transpiles the file with a target that it treats as "supports standard decorators natively," so it leaves the @moduleMethod(...) syntax untouched in the output rather than downleveling it to a __decorateElement-style helper call.
  • Vitest's own module runner then evaluates that output via new AsyncFunction(...) (ESModulesEvaluator.runInlinedModule in vite/dist/node/module-runner.js) in the current Node process.
  • Node (confirmed on v24.13.0) does not actually support this decorator syntax natively yet - reproducible with nothing vitest- or esbuild-related at all:
    node --input-type=module -e "
    function dec(v, ctx) { return v; }
    @dec
    class Foo {}
    "
    # SyntaxError: Invalid or unexpected token
    
  • By contrast, running plain esbuild from the CLI on the same file (no Vite involved) transpiles it fine, because esbuild's default CLI target is conservative enough that it downlevels the decorators into __decorateElement(...) calls - valid, portable JS. It's specifically Vite's SSR/test transform path picking a newer effective target that skips that downleveling.

I confirmed this is decorator-specific: stripping the @moduleMethod(...) lines from index.ts (nothing else changed) makes the exact same trivial import test pass.

Why this hasn't been noticed before

No bundle's own index.ts (the Conductor plugin file) currently has any test that imports it directly - every bundle's tests exercise functions.ts/conversion helpers instead, so this gap has been silently latent across every Conductor-migrated bundle, not just sound.

Attempted fix (didn't fully work)

Setting esbuild: { target: 'es2021' } at the top level of the root vitest.config.ts did not change the outcome - the SSR module transform path Vite uses for vitest doesn't appear to pick that up the same way optimizeDeps/build transforms would. Whatever the actual fix is (a different config knob, or forcing decorator downleveling some other way), I didn't want to guess further and land a monorepo-wide vitest config change without being sure it's right and without broader test-suite validation.

Repro

// any bundle's src/__tests__/*.test.ts
import SoundModulePlugin from '../index'; // or any other bundle's default plugin export

Fails the same way regardless of bundle - not specific to sound.


Raised from review discussion on #796.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions