Skip to content

bitdev.react/react-env generated environments fail to build when vitest.config is converted from .mjs to .ts due to hardcoded TypeScript moduleResolution #10504

Description

@rvnlord

Summary

When creating a React environment using bitdev.react/react-env, changing vitest.config.mjs to vitest.config.ts causes bit build to fail while compiling bit-env. The error suggests that the TypeScript compiler is using an incompatible moduleResolution setting (node instead of node16, nodenext, or bundler) despite the required dependencies being present.

This indicates that bit-env uses a hardcoded TypeScript configuration during compilation which doesn't allow packages such as vite and @vitejs/plugin-react to be resolved correctly.

This is especially surprising given the workarounds and fixes around TypeScript configuration discussed in #10502.

Steps to Reproduce

bit create react-env envs/my-react-env --aspect bitdev.react/react-env
bit create react-app apps/react-frontend --env projects/envs/my-react-env

After creation, the generated environment has the following type:

bitdev.general/envs/bit-env@5.0.12

For comparison, manually changing the env with:

bit env set projects/commonlib/envs/my-react-env bitdev.react/react-env

causes the env var to detach and reports that bitdev.react/react-env is not of type env. I assume this behavior is intentional and simply differs from how @teambit/react-env behaved previously.

Now, rename:

vitest.config.mjs

to:

vitest.config.ts

point <env>.bit.env.ts to it, add types to your config so it's compatible with TS and run:

bit build

Actual Behavior

bit build fails during the bit-env compilation step with errors such as:

error TS2307: Cannot find module 'vite' or its corresponding type declarations.

There are types at:
.../node_modules/.../vite/dist/node/index.d.ts

but this result could not be resolved under your current 'moduleResolution' setting.
Consider updating to 'node16', 'nodenext', or 'bundler'.

The same error is emitted for @vitejs/plugin-react as well as for any custom module augmentations for vite.

The failures occur while compiling files such as:

config/vite.config.ts
config/vitest.config.ts
config/plugins/*.ts

The build output shows that the failure happens during the bit-env compilation process:

(envs/bit-env) [Compiler: TypescriptCompile]

Expected Behavior

vitest.config.ts should compile successfully when using modern ESM packages such as vite and @vitejs/plugin-react, or the generated environment should inherit/support the appropriate TypeScript moduleResolution setting (node16, nodenext, or bundler).

At the very least, the TypeScript configuration used internally by bit-env should be configurable or should not force an incompatible moduleResolution setting.

Additional Notes

  • The required type declarations are present in node_modules.
  • TypeScript explicitly reports that changing moduleResolution would resolve the imports.
  • The issue only manifests once vitest.config is written in TypeScript.
  • This completely blocks bit build.
  • It is a regression from @teambit/react.react-env as in Bit v1.13, I kept using TS based configs without issues.
  • Side effects of this (or possibly TypescriptConfigWriter incorrectly resolves tsconfig chains, serializes TS enums, generates invalid Windows paths and creates another broken config independntly of the ConfigWriterList pipeline which ignores provided options #10502), also cause weird, difficult to diagnose quirks, i.e.:
    • if you import a local module like: import { toPosixPath } from "./my-typescript-config-writer"; without providing an extension, VS Code will be fine with it but trying to run any bit command (even bit ws-config write) will tell you (and rightly so) that the env var became detached ⚠ environment with ID: projects/commonlib/envs/my-react-env configured on component projects/commonlib/components/my-css-grid was not loaded (run "bit install"). It is detached because bit-env hard coded compilerOptions (similarly to the case of moduleResolution: node I described before) break it at (bit) runtime.
    • since exclude values are not respected (not written), bit compile would take 7 minutes because it doesn't know to skip **/prod-build-electron/** which can contain an unpacked asar needed for debugging purposes. This also propagates to capsules during bit build and so on.

Workaround

I'm currently using a custom vite.d.ts file with ambient module declarations for vite and @vitejs/plugin-react in <my-env>/types to circumvent these compilatiuon issues. This way, when the parent bit-env's compiler runs, TypeScript will find the local .d.ts declarations instead of trying to resolve the actual vite package through the incorrectly forced node moduleResolution strategy.

/**
 * Type declarations for vite to work around moduleResolution issues.
 * The env's config files import from "vite" and "@vitejs/plugin-react",
 * but when the parent bit-env's TypeScript compiler runs with
 * moduleResolution: "node", it cannot resolve vite's package exports.
 *
 * These declarations provide the exact types used by:
 *   - config/vite.config.ts
 *   - config/vitest.config.ts
 *   - config/plugins/*.ts
 *
 * The actual vite runtime is provided via peer dependencies.
 */

declare module "vite" {
    // --- Plugin types (used by config/plugins/*.ts) ---

    interface Plugin {
        name: string;
        enforce?: "pre" | "post";
        resolveId?: (
            source: string,
            importer: string | undefined,
            resolveOptions: { [key: string]: any }
        ) => string | null | undefined;
        load?: (id: string) => string | null | undefined;
        transform?: (
            code: string,
            id: string
        ) => { code: string; map?: any } | null | undefined;
        config?: (config: UserConfig, env: ConfigEnv) => UserConfig | null | undefined;
        configResolved?: (config: any) => void;
        buildEnd?: (err?: Error) => void | Promise<void>;
        handleHotUpdate?: (ctx: any) => void | Promise<void>;
    }

    type PluginOption = Plugin | Plugin[] | ((...args: any[]) => Plugin | Plugin[]);

    // --- Config types (used by config/vite.config.ts, config/vitest.config.ts) ---

    interface ConfigEnv {
        mode: string;
        command: "build" | "serve";
        isSsrBuild?: boolean;
        isPreview?: boolean;
    }

    interface UserConfig {
        root?: string;
        envDir?: string | false;
        plugins?: PluginOption[];
        test?: {
            globals?: boolean;
            environment?: string;
            setupFiles?: string[];
            pool?: string;
            singleFork?: boolean;
            testTimeout?: number;
            hookTimeout?: number;
            deps?: {
                inline?: (string | RegExp)[];
                external?: string[];
            };
            include?: string[];
            exclude?: string[];
            css?: boolean | { include?: RegExp[] };
            coverage?: {
                clean?: boolean;
                include?: string[];
                provider?: string;
                reportOnFailure?: boolean;
            };
        };
        [key: string]: any;
    }

    function defineConfig(config: UserConfig): UserConfig;
    function defineConfig(config: Promise<UserConfig>): Promise<UserConfig>;
}

declare module "@vitejs/plugin-react" {
    interface Options {
        include?: string | RegExp | Array<string | RegExp>;
        exclude?: string | RegExp | Array<string | RegExp>;
        jsxImportSource?: string;
        jsxRuntime?: "classic" | "automatic";
        babel?: any;
        reactRefreshHost?: string;
    }

    const viteReact: {
        (opts?: Options): import("vite").Plugin[];
        preambleCode: string;
    };

    export default viteReact;
}

I have also updated the status here with new findings: #10480 (comment). I don't have permissions to reopen the issue as you asked.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions