diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/.bitmap b/e2e/fixtures/workspace-with-tsconfig-issue/.bitmap new file mode 100644 index 000000000000..f5942eb8c3ef --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/.bitmap @@ -0,0 +1,37 @@ +/* THIS IS A BIT-AUTO-GENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. */ + +/** + * The Bitmap file is an auto generated file used by Bit to track all your Bit components. It maps the component to a folder in your file system. + * This file should be committed to VCS(version control). + * Components are listed using their component ID (https://bit.dev/docs/components/component-id). + * If you want to delete components you can use the "bit remove " command. + * See the docs (https://bit.dev/docs/components/removing-components) for more information, or use "bit remove --help". + */ + +{ + "buttons/js-button": { + "scope": "", + "version": "", + "mainFile": "index.js", + "rootDir": "my-scope/buttons/js-button", + "config": { + "my-org.my-scope/envs/my-react-env": {}, + "teambit.envs/envs": { + "env": "my-org.my-scope/envs/my-react-env" + } + } + }, + "envs/my-react-env": { + "scope": "", + "version": "", + "defaultScope": "my-org.my-scope", + "mainFile": "index.ts", + "rootDir": "my-scope/envs/my-react-env", + "config": { + "teambit.envs/envs": { + "env": "teambit.envs/env" + } + } + }, + "$schema-version": "15.0.0" +} diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/.gitignore b/e2e/fixtures/workspace-with-tsconfig-issue/.gitignore new file mode 100644 index 000000000000..1521c8b7652b --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/.gitignore @@ -0,0 +1 @@ +dist diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/index.js b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/index.js new file mode 100644 index 000000000000..18b3cf35acc7 --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/index.js @@ -0,0 +1 @@ +export { JsButton } from './js-button'; diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/js-button.composition.jsx b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/js-button.composition.jsx new file mode 100644 index 000000000000..1c87494d5cc8 --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/js-button.composition.jsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { JsButton } from './js-button'; + +export const BasicJsButton = () => { + return ( + hello world! + ); +} diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/js-button.jsx b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/js-button.jsx new file mode 100644 index 000000000000..1880c834b6d1 --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/js-button.jsx @@ -0,0 +1,9 @@ +import React from 'react'; + +export function JsButton({ children }) { + return ( +
+ {children} +
+ ); +} diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/js-button.spec.jsx b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/js-button.spec.jsx new file mode 100644 index 000000000000..5aa659cd0ad2 --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/buttons/js-button/js-button.spec.jsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { BasicJsButton } from './js-button.composition'; + +it('should render with the correct text', () => { + const { getByText } = render(); + const rendered = getByText('hello world!'); + expect(rendered).toBeTruthy(); +}); diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/config/tsconfig.json b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/config/tsconfig.json new file mode 100644 index 000000000000..8b3abc66c036 --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/config/tsconfig.json @@ -0,0 +1,7 @@ +/** + * @see https://bit.dev/reference/typescript/typescript-config + */ +{ + "extends": "@teambit/react.react-env/config/tsconfig.json", + "include": ["**/*", "**/*.json"] +} diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/env.jsonc b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/env.jsonc new file mode 100644 index 000000000000..d7b5e3c06f91 --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/env.jsonc @@ -0,0 +1,101 @@ +{ + /** + * standardize your component dependencies. + * @see https://bit.dev/docs/react-env/dependencies + **/ + "policy": { + /** + * peer dependencies for components using that env. + */ + "peers": [ + { + "name": "react", + /* the version to be installed and used by the env */ + "version": "^18.0.0", + /* the range of versions this env's components are compatible with */ + "supportedRange": "^17.0.0 || ^18.0.0" + }, + { + "name": "react-dom", + "version": "^18.0.0", + "supportedRange": "^17.0.0 || ^18.0.0" + }, + { + "name": "graphql", + "version": "14.7.0", + "supportedRange": "^14.7.0" + }, + { + "name": "react-router-dom", + "version": "6.8.1", + "supportedRange": "^6.8.1" + }, + { + "name": "@mdx-js/react", + "version": "1.6.22", + "supportedRange": "^1.6.22" + }, + { + "name": "@teambit/mdx.ui.mdx-scope-context", + "version": "0.0.496", + "supportedRange": "^0.0.496" + } + ], + /** + * dev dependencies for components using that env + */ + "dev": [ + { + "name": "@types/react", + "version": "^18.0.0", + /** + * hide the dependency from bit's inspection tools. + * in most cases, a component should only list its env as a dev dependency + */ + "hidden": true, + /* add this dependency to components, even if they don't directly import it */ + "force": true + }, + { + "name": "@types/react-dom", + "version": "^18.0.0", + "hidden": true, + "force": true + }, + { + "name": "@types/jest", + "version": "^29.2.2", + "hidden": true, + "force": true + }, + { + "name": "@testing-library/react", + "version": "^13.4.0" + }, + { + "name": "@testing-library/react-hooks", + "version": "^8.0.1" + } + ] + }, + + /** + * associate files with a specific dev service. + * associated files are considered as dev files. + * @see https://bit.dev/docs/react-env/dependencies#configure-files-as-dev-files + **/ + "patterns": { + /** + * files to be loaded and displayed in the 'preview' tab. + * @see https://bit.dev/docs/react-env/component-previews + */ + "compositions": ["**/*.composition.*", "**/*.preview.*"], + /** + * files to be loaded and displayed in the 'overview' tab. + * @see https://bit.dev/docs/react-env/component-docs + */ + "docs": ["**/*.docs.*"], + /* files to be included in the component testing */ + "tests": ["**/*.spec.*", "**/*.test.*"] + } +} diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/index.ts b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/index.ts new file mode 100644 index 000000000000..4b1710a85f96 --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/index.ts @@ -0,0 +1,3 @@ +import { MyReactEnv } from './my-react-env.bit-env'; +export { MyReactEnv }; +export default MyReactEnv; diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/my-react-env.bit-env.ts b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/my-react-env.bit-env.ts new file mode 100644 index 000000000000..39a6b3abc519 --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/my-scope/envs/my-react-env/my-react-env.bit-env.ts @@ -0,0 +1,30 @@ +/** + * this env extends react-env + * @see https://bit.cloud/teambit/react/react-env + */ +import { ReactEnv } from '@teambit/react.react-env'; + +import { Compiler } from '@teambit/compiler'; +import { EnvHandler } from '@teambit/envs'; +import { + TypescriptCompiler, + resolveTypes, +} from '@teambit/typescript.typescript-compiler'; + +export class MyReactEnv extends ReactEnv { + /* a shorthand name for the env */ + name = 'my-react-env'; + + /* the compiler to use during development */ + compiler(): EnvHandler { + /** + * @see https://bit.dev/reference/typescript/using-typescript + * */ + return TypescriptCompiler.from({ + tsconfig: require.resolve('./config/tsconfig.json'), + types: resolveTypes(__dirname, ['./types']), + }); + } +} + +export default new MyReactEnv(); diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/tsconfig.json b/e2e/fixtures/workspace-with-tsconfig-issue/tsconfig.json new file mode 100644 index 000000000000..375253ade1a4 --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/tsconfig.json @@ -0,0 +1,20 @@ +/** + * this is NOT the config for your components. + * it is only used by your IDE, for a better development experience. + * to change your component compilation, customize your env. + * @see https://bit.dev/reference/typescript/typescript-config + */ +{ + "extends": "./node_modules/@teambit/react.react-env/config/tsconfig.json", + "compilerOptions": { + "noEmit": true, + "allowJs": true, + "jsx": "react", + }, + "include": [ + "**/*.js" + ], + "exclude": [ + "dist" + ] +} \ No newline at end of file diff --git a/e2e/fixtures/workspace-with-tsconfig-issue/workspace.jsonc b/e2e/fixtures/workspace-with-tsconfig-issue/workspace.jsonc new file mode 100644 index 000000000000..57b349c04cc1 --- /dev/null +++ b/e2e/fixtures/workspace-with-tsconfig-issue/workspace.jsonc @@ -0,0 +1,56 @@ +/** + * this is the main configuration file of your bit workspace. + * for full documentation, please see: https://bit.dev/docs/workspace/workspace-configuration + **/{ + "$schema": "https://static.bit.dev/teambit/schemas/schema.json", + /** + * main configuration of the Bit workspace. + **/ + "teambit.workspace/workspace": { + /** + * the name of the component workspace. used for development purposes. + **/ + "name": "my-workspace", + /** + * set the icon to be shown on the Bit server. + **/ + "icon": "https://static.bit.dev/bit-logo.svg", + /** + * default directory to place a component during `bit import` and `bit create`. + * the following placeholders are available: + * name - component name includes namespace, e.g. 'ui/button'. + * scopeId - full scope-id includes the owner, e.g. 'teambit.compilation'. + * scope - scope name only, e.g. 'compilation'. + * owner - owner name in bit.dev, e.g. 'teambit'. + **/ + "defaultDirectory": "{scope}/{name}", + /** + * default scope for all components in workspace. + **/ + "defaultScope": "my-org.my-scope" + }, + /** + * main configuration for component dependency resolution. + **/ + "teambit.dependencies/dependency-resolver": { + /** + * choose the package manager for Bit to use. you can choose between 'yarn', 'pnpm' + */ + "packageManager": "teambit.dependencies/pnpm", + "policy": { + "dependencies": { + "@teambit/mdx.ui.mdx-scope-context": "0.0.496", + "@teambit/react.react-env": "0.0.55", + "@teambit/typescript.typescript-compiler": "0.0.7" + }, + "peerDependencies": {} + }, + "linkCoreAspects": true, + "rootComponents": true + }, + "teambit.generator/generator": { + "envs": [ + "my-org.my-scope/envs/my-react-env" + ] + } +} diff --git a/e2e/harmony/install-and-compile.e2e.ts b/e2e/harmony/install-and-compile.e2e.ts index 28f0f9f174c3..eb40810902ea 100644 --- a/e2e/harmony/install-and-compile.e2e.ts +++ b/e2e/harmony/install-and-compile.e2e.ts @@ -195,3 +195,15 @@ describe('skipping compilation on install', function () { expect(path.join(helper.scopes.localPath, `node_modules/@${helper.scopes.remote}/comp1/dist`)).to.not.be.a.path(); }); }); + +describe('do not fail on environment loading files from a location inside node_modules that does not exist', function () { + this.timeout(0); + let helper: Helper; + before(() => { + helper = new Helper(); + helper.fixtures.copyFixtureDir('workspace-with-tsconfig-issue', helper.scopes.localPath); + }); + it('should not fail', () => { + helper.command.install(); + }); +}); diff --git a/scopes/dependencies/dependency-resolver/dependency-installer.ts b/scopes/dependencies/dependency-resolver/dependency-installer.ts index 7e04731ae964..6002797c4ae0 100644 --- a/scopes/dependencies/dependency-resolver/dependency-installer.ts +++ b/scopes/dependencies/dependency-resolver/dependency-installer.ts @@ -40,6 +40,7 @@ export type InstallOptions = { packageManagerConfigRootDir?: string; resolveVersionsFromDependenciesOnly?: boolean; linkedDependencies?: Record>; + pruneNodeModules?: boolean; }; export type GetComponentManifestsOptions = { @@ -179,6 +180,7 @@ export class DependencyInstaller { engineStrict: this.engineStrict, packageManagerConfigRootDir: options.packageManagerConfigRootDir, peerDependencyRules: this.peerDependencyRules, + pruneNodeModules: options.pruneNodeModules, hidePackageManagerOutput, ...packageManagerOptions, }; diff --git a/scopes/dependencies/dependency-resolver/package-manager.ts b/scopes/dependencies/dependency-resolver/package-manager.ts index 0e4cd7c84268..07c9b9d6b443 100644 --- a/scopes/dependencies/dependency-resolver/package-manager.ts +++ b/scopes/dependencies/dependency-resolver/package-manager.ts @@ -53,6 +53,8 @@ export type PackageManagerInstallOptions = { updateAll?: boolean; hidePackageManagerOutput?: boolean; + + pruneNodeModules?: boolean; }; export type PackageManagerGetPeerDependencyIssuesOptions = PackageManagerInstallOptions; diff --git a/scopes/dependencies/pnpm/lynx.ts b/scopes/dependencies/pnpm/lynx.ts index 2bf295cbed1f..8206e0aab3a9 100644 --- a/scopes/dependencies/pnpm/lynx.ts +++ b/scopes/dependencies/pnpm/lynx.ts @@ -166,6 +166,7 @@ export async function install( updateAll?: boolean; nodeLinker?: 'hoisted' | 'isolated'; overrides?: Record; + pruneNodeModules?: boolean; rootComponents?: boolean; rootComponentsForCapsules?: boolean; includeOptionalDeps?: boolean; @@ -225,7 +226,7 @@ export async function install( workspacePackages, preferFrozenLockfile: true, pruneLockfileImporters: true, - modulesCacheMaxAge: 0, + modulesCacheMaxAge: options.pruneNodeModules ? 0 : undefined, neverBuiltDependencies: ['core-js'], registries: registriesMap, resolutionMode: 'highest', diff --git a/scopes/dependencies/pnpm/pnpm.package-manager.ts b/scopes/dependencies/pnpm/pnpm.package-manager.ts index 4ceb0f13cad5..d46ad0105ed5 100644 --- a/scopes/dependencies/pnpm/pnpm.package-manager.ts +++ b/scopes/dependencies/pnpm/pnpm.package-manager.ts @@ -74,6 +74,7 @@ export class PnpmPackageManager implements PackageManager { sideEffectsCacheRead: installOptions.sideEffectsCache ?? true, sideEffectsCacheWrite: installOptions.sideEffectsCache ?? true, pnpmHomeDir: config.pnpmHomeDir, + pruneNodeModules: installOptions.pruneNodeModules, updateAll: installOptions.updateAll, hidePackageManagerOutput: installOptions.hidePackageManagerOutput, }, diff --git a/scopes/workspace/install/install.main.runtime.ts b/scopes/workspace/install/install.main.runtime.ts index d191d3f3e89e..cc25d9a62faa 100644 --- a/scopes/workspace/install/install.main.runtime.ts +++ b/scopes/workspace/install/install.main.runtime.ts @@ -247,7 +247,12 @@ export class InstallMain { current.manifests, mergedRootPolicy, current.componentDirectoryMap, - { installTeambitBit: false }, + { + installTeambitBit: false, + // We clean node_modules only on the first install. + // Otherwise, we might load an env from a location that we later remove. + pruneNodeModules: installCycle === 0, + }, pmInstallOptions ); // Core aspects should be relinked after installation because Yarn removes all symlinks created not by Yarn.