Skip to content

Commit ece319f

Browse files
authored
fix(preview): support generating preview for envs without a compiler (#10406)
## Problem Running `bit build` on a component whose env has a bundler/preview but **no compiler** failed during the `teambit.preview/preview:GeneratePreview` task. An env without a compiler became possible a while back, but it was only tested with the preview disabled (e.g. `EmptyEnv` e2e tests, where the env has no bundler so the preview task bails out early). When the preview is actually needed, two issues surfaced: 1. `context.env.getCompiler is not a function` — the component bundling strategy called `getCompiler()` directly, unlike every sibling call site which already uses optional chaining. 2. After guarding that, `ENOENT` when writing the preview link file — it targets the compiler's `dist` dir, which a compiler-less env never creates. ## Fix - Guard the `getCompiler()` call in `ComponentBundlingStrategy.getComponentOutputPath`, matching the other call sites. - `ensureDirSync(targetDir)` before writing the preview link in `writeLinkContents`, so a missing dist dir is created. Verified end-to-end: a component on a bundler-but-no-compiler env now builds through `GeneratePreview` and `PreBundlePreview` successfully.
1 parent 743fe6b commit ece319f

5 files changed

Lines changed: 61 additions & 2 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { ReactNoCompilerEnv } from './react-no-compiler-env.bit-env';
2+
3+
export default ReactNoCompilerEnv;
4+
export { ReactNoCompilerEnv };
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @bit-no-check
2+
// @ts-nocheck
3+
import { ReactEnv } from '@teambit/react.react-env';
4+
5+
/**
6+
* an env that has a preview/bundler but no compiler. it reproduces the (rare) scenario of an env
7+
* without a compiler that still needs to generate a preview during `bit build` (e.g.
8+
* bitdev.general/envs/js-env).
9+
*
10+
* it extends the react env (to inherit a working preview/bundler) and removes the compiler and the
11+
* build pipeline: `compiler` is unset so no `getCompiler` is exposed on the env, and `build` is
12+
* unset so the env contributes no build tasks (in particular no compiler task). the global preview
13+
* tasks still run, so the preview is generated for a component on this compiler-less env.
14+
*/
15+
export class ReactNoCompilerEnv extends ReactEnv {
16+
name = 'react-no-compiler-env';
17+
18+
icon = 'https://static.bit.dev/extensions-icons/react.svg';
19+
20+
compiler = undefined;
21+
22+
build = undefined;
23+
}
24+
25+
export default new ReactNoCompilerEnv();

e2e/harmony/custom-env.e2e.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,33 @@ export default createMounter(MyReactProvider) as any;`
636636
expect(output).to.not.have.string('failed');
637637
});
638638
});
639+
describe('an env with a preview/bundler but without a compiler', () => {
640+
let buildOutput: string;
641+
before(() => {
642+
// preview is disabled by default in e2e to speed up tagging. enable it so the GeneratePreview
643+
// task actually runs - this is the path that used to fail for a compiler-less env.
644+
helper.scopeHelper.setWorkspaceWithRemoteScope({ disablePreview: false });
645+
// a react-based env with the compiler (and the env build pipe) removed. so the env has a
646+
// preview/bundler but no compiler, exactly like bitdev.general/envs/js-env.
647+
const envName = helper.env.setCustomNewEnv('react-no-compiler-env');
648+
const envId = `${helper.scopes.remote}/${envName}`;
649+
helper.fixtures.populateComponents(1, false);
650+
helper.command.setEnv('comp1', envId);
651+
helper.command.install();
652+
});
653+
it('bit build should not fail generating the preview', () => {
654+
// before the fix it used to throw "context.env.getCompiler is not a function" and then
655+
// ENOENT when writing the preview link into the (never created) dist dir.
656+
// skip the TSCompiler task: comp1's env has no compiler anyway, and skipping it avoids
657+
// compiling the env component itself (irrelevant to this scenario - the user's env was a
658+
// resolved dependency, not built). the global GeneratePreview task still runs.
659+
buildOutput = helper.command.build('comp1', '--skip-tasks TSCompiler');
660+
expect(buildOutput).to.have.string('build succeeded');
661+
});
662+
it('the GeneratePreview task should have run (preview was needed, not skipped)', () => {
663+
expect(buildOutput).to.have.string('GeneratePreview');
664+
});
665+
});
639666
describe('custom env with invalid env.jsonc', () => {
640667
before(() => {
641668
helper.scopeHelper.setWorkspaceWithRemoteScope();

scopes/preview/preview/preview.main.runtime.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,9 @@ export class PreviewMain {
809809
const targetPath = join(targetDir, `${prefix}-${this.timestamp}.js`);
810810
// write only if link has changed (prevents triggering fs watches)
811811
if (this.writeHash.get(targetPath) !== hash) {
812+
// the target dir is normally the compiler's dist dir, which already exists. for envs without a
813+
// compiler (the dist dir was never created), make sure the dir exists before writing the link.
814+
ensureDirSync(targetDir);
812815
writeFileSync(targetPath, contents);
813816
this.writeHash.set(targetPath, hash);
814817
}

scopes/preview/preview/strategies/component-strategy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,8 @@ export class ComponentBundlingStrategy implements BundlingStrategy {
382382

383383
private getComponentOutputPath(capsule: Capsule, context: ComputeTargetsContext) {
384384
const capsulePath = resolve(`${capsule.path}`);
385-
const compiler: Compiler = context.env.getCompiler();
386-
const distDir = compiler.getDistDir?.() || 'dist';
385+
const compiler: Compiler | undefined = context.env.getCompiler?.();
386+
const distDir = compiler?.getDistDir?.() || 'dist';
387387
return join(capsulePath, distDir);
388388
}
389389

0 commit comments

Comments
 (0)