Skip to content

Commit

Permalink
fix(core): re-enable CRA migration to Vite (#30082)
Browse files Browse the repository at this point in the history
This PR brings back the CRA migration that was missing since Nx 18.

## Current Behavior
<!-- This is the behavior we have today -->
`nx init` does not migrate CRA apps 

## Expected Behavior
`nx init` migrates CRA apps to Vite since CRA is deprecated

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
  • Loading branch information
jaysoo authored Feb 25, 2025
1 parent 32341d5 commit 202b49b
Show file tree
Hide file tree
Showing 11 changed files with 188 additions and 493 deletions.
15 changes: 8 additions & 7 deletions docs/generated/cli/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ Install `nx` globally to invoke the command directly using `nx`, or use `npx nx`

## Options

| Option | Type | Description |
| ------------------------ | ------- | --------------------------------------------------------------------------------------------------- |
| `--help` | boolean | Show help. |
| `--interactive` | boolean | When false disables interactive input prompts for options. (Default: `true`) |
| `--nxCloud` | boolean | Set up distributed caching with Nx Cloud. |
| `--useDotNxInstallation` | boolean | Initialize an Nx workspace setup in the .nx directory of the current repository. (Default: `false`) |
| `--version` | boolean | Show version number. |
| Option | Type | Description |
| ------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `--force` | boolean | Force the migration to continue and ignore custom webpack setup or uncommitted changes. Only for CRA projects. (Default: `false`) |
| `--help` | boolean | Show help. |
| `--interactive` | boolean | When false disables interactive input prompts for options. (Default: `true`) |
| `--nxCloud` | boolean | Set up distributed caching with Nx Cloud. |
| `--useDotNxInstallation` | boolean | Initialize an Nx workspace setup in the .nx directory of the current repository. (Default: `false`) |
| `--version` | boolean | Show version number. |
15 changes: 8 additions & 7 deletions docs/generated/packages/nx/documents/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ Install `nx` globally to invoke the command directly using `nx`, or use `npx nx`

## Options

| Option | Type | Description |
| ------------------------ | ------- | --------------------------------------------------------------------------------------------------- |
| `--help` | boolean | Show help. |
| `--interactive` | boolean | When false disables interactive input prompts for options. (Default: `true`) |
| `--nxCloud` | boolean | Set up distributed caching with Nx Cloud. |
| `--useDotNxInstallation` | boolean | Initialize an Nx workspace setup in the .nx directory of the current repository. (Default: `false`) |
| `--version` | boolean | Show version number. |
| Option | Type | Description |
| ------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `--force` | boolean | Force the migration to continue and ignore custom webpack setup or uncommitted changes. Only for CRA projects. (Default: `false`) |
| `--help` | boolean | Show help. |
| `--interactive` | boolean | When false disables interactive input prompts for options. (Default: `true`) |
| `--nxCloud` | boolean | Set up distributed caching with Nx Cloud. |
| `--useDotNxInstallation` | boolean | Initialize an Nx workspace setup in the .nx directory of the current repository. (Default: `false`) |
| `--version` | boolean | Show version number. |
138 changes: 41 additions & 97 deletions e2e/nx-init/src/nx-init-react.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
checkFilesDoNotExist,
checkFilesExist,
createFile,
getPackageManagerCommand,
getPublishedVersion,
getSelectedPackageManager,
Expand All @@ -19,139 +20,82 @@ import {
updateJson,
} from '../../utils';

describe('nx init (for React - legacy)', () => {
describe('nx init (React)', () => {
let pmc: ReturnType<typeof getPackageManagerCommand>;

beforeAll(() => {
pmc = getPackageManagerCommand({
packageManager: getSelectedPackageManager(),
});

process.env.NX_ADD_PLUGINS = 'false';
});

afterAll(() => {
delete process.env.NX_ADD_PLUGINS;
});

// TODO(@jaysoo): Please investigate why this test is failing
xit('should convert to an integrated workspace with craco (webpack)', () => {
it('should convert a CRA project to Vite', () => {
const appName = 'my-app';
createReactApp(appName);

const craToNxOutput = runCommand(
`${
pmc.runUninstalledPackage
} nx@${getPublishedVersion()} init --no-interactive --integrated --vite=false`
} nx@${getPublishedVersion()} init --no-interactive`
);

expect(craToNxOutput).toContain('🎉 Done!');

const packageJson = readJson('package.json');
expect(packageJson.devDependencies['@nx/jest']).toBeDefined();
expect(packageJson.devDependencies['@nx/vite']).toBeUndefined();
expect(packageJson.devDependencies['@nx/webpack']).toBeDefined();
expect(packageJson.dependencies['redux']).toBeDefined();
expect(packageJson.name).toEqual(appName);

runCLI(`build ${appName}`, {
env: {
// since craco 7.1.0 the NODE_ENV is used, since the tests set it
// to "test" is causes an issue with React Refresh Babel
NODE_ENV: undefined,
},
});
checkFilesExist(`dist/apps/${appName}/index.html`);
});

// TODO(crystal, @jaysoo): Investigate why this is failing
xit('should convert to an integrated workspace with Vite', () => {
// TODO investigate why this is broken
const originalPM = process.env.SELECTED_PM;
process.env.SELECTED_PM = originalPM === 'pnpm' ? 'yarn' : originalPM;
expect(craToNxOutput).toContain('Done!');

const appName = 'my-app';
createReactApp(appName);

const craToNxOutput = runCommand(
`${
pmc.runUninstalledPackage
} nx@${getPublishedVersion()} init --no-interactive --integrated`
checkFilesDoNotExist(
'libs/.gitkeep',
'tools/tsconfig.tools.json',
'babel.config.json',
'jest.preset.js',
'jest.config.ts'
);

expect(craToNxOutput).toContain('🎉 Done!');

const packageJson = readJson('package.json');
expect(packageJson.devDependencies['@nx/jest']).toBeUndefined();
expect(packageJson.devDependencies['@nx/vite']).toBeDefined();
expect(packageJson.devDependencies['@nx/webpack']).toBeUndefined();
expect(packageJson.dependencies['redux']).toBeDefined();
expect(packageJson.name).toEqual(appName);

const viteConfig = readFile(`apps/${appName}/vite.config.js`);
expect(viteConfig).toContain('port: 4200'); // default port
const viteConfig = readFile(`vite.config.mjs`);
expect(viteConfig).toContain('port: 3000'); // default port

runCLI(`build ${appName}`);
checkFilesExist(`dist/apps/${appName}/index.html`);
checkFilesExist(`dist/${appName}/index.html`);

const unitTestsOutput = runCLI(`test ${appName}`);
expect(unitTestsOutput).toContain('Successfully ran target test');
process.env.SELECTED_PM = originalPM;
});

// TODO(crystal, @jaysoo): Investigate why this is failing
xit('should convert to an integrated workspace with Vite with custom port', () => {
// TODO investigate why this is broken
const originalPM = process.env.SELECTED_PM;
process.env.SELECTED_PM = originalPM === 'pnpm' ? 'yarn' : originalPM;
it('should support path aliases', () => {
const appName = 'my-app';
createReactApp(appName);
updateFile(`.env`, `NOT_THE_PORT=8000\nPORT=3000\nSOMETHING_ELSE=whatever`);

runCommand(
`${
pmc.runUninstalledPackage
} nx@${getPublishedVersion()} init --no-interactive --force --integrated`
createFile(
'jsconfig.json',
JSON.stringify({
compilerOptions: {
baseUrl: '.',
paths: {
'foo/*': ['src/foo/*'],
},
},
})
);
createFile('src/foo/Foo.js', `export const Foo = () => <p>Foo</p>;`);
updateFile(
'src/App.js',
`
import { Foo } from 'foo/Foo';
function App() {
return <Foo />;
}
export default App;
`
);

const viteConfig = readFile(`apps/${appName}/vite.config.js`);
expect(viteConfig).toContain('port: 3000');

const unitTestsOutput = runCLI(`test ${appName}`);
expect(unitTestsOutput).toContain('Successfully ran target test');
process.env.SELECTED_PM = originalPM;
});

it('should convert to an standalone workspace with Vite', () => {
const appName = 'my-app';
createReactApp(appName);

const craToNxOutput = runCommand(
runCommand(
`${
pmc.runUninstalledPackage
} nx@${getPublishedVersion()} init --no-interactive --vite`
} nx@${getPublishedVersion()} init --no-interactive`
);

expect(craToNxOutput).toContain('🎉 Done!');

checkFilesDoNotExist(
'libs/.gitkeep',
'tools/tsconfig.tools.json',
'babel.config.json',
'jest.preset.js',
'jest.config.ts'
);

const packageJson = readJson('package.json');
expect(packageJson.dependencies['redux']).toBeDefined();
expect(packageJson.name).toEqual(appName);

const viteConfig = readFile(`vite.config.js`);
expect(viteConfig).toContain('port: 4200'); // default port

runCLI(`build ${appName}`);
checkFilesExist(`dist/${appName}/index.html`);

const unitTestsOutput = runCLI(`test ${appName}`);
expect(unitTestsOutput).toContain('Successfully ran target test');
expect(() => runCLI(`build ${appName}`)).not.toThrow();
});

function createReactApp(appName: string) {
Expand Down
6 changes: 6 additions & 0 deletions packages/nx/src/command-line/init/command-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ async function withInitOptions(yargs: Argv) {
description:
'Initialize an Nx workspace setup in the .nx directory of the current repository.',
default: false,
})
.option('force', {
describe:
'Force the migration to continue and ignore custom webpack setup or uncommitted changes. Only for CRA projects.',
type: 'boolean',
default: false,
});
} else {
return yargs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ export function addViteCommandsToPackageScripts(
const packageJson = readJsonFile(packageJsonPath);
packageJson.scripts = {
...packageJson.scripts,
start: 'nx exec -- vite',
serve: 'nx exec -- vite',
build: `nx exec -- vite build`,
test: 'nx exec -- vitest',
// These should be replaced by the vite init generator later.
start: 'vite',
test: 'vitest',
dev: 'vite',
build: 'vite build',
eject: undefined,
};
writeJsonFile(packageJsonPath, packageJson, { spaces: 2 });
}
Loading

0 comments on commit 202b49b

Please sign in to comment.