This PR updates app and lib generators in the following packages such that they will generate files with the TS solution setup if it is detected. - `@nx/react` - `@nx/next` - `@nx/remix` - `@nx/expo` - `@nx/react-native` React apps and libs will be linked using npm/pnpm/yarn/bun workspaces feature rather than through tsconfig paths. This means that local aliases like `@/` will work with Next.js and Remix apps. Note: This will be behind `--workspaces` flag when using `npx create-nx-workspace` and choosing React stack. If you use the None/TS stack then adding plugins like `nx add @nx/react` then generating apps, it will automatically pick up the new TS solution setup. <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior React generators are not compatible with TS solution setup (i.e. workspaces + TS project references). ## Expected Behavior React generators work with new TS solution setup (Plain, Next.js, Remix, Expo, React Native). ## Related Issue(s) #28322 --------- Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com> Co-authored-by: Nicholas Cunningham <ndcunningham@gmail.com>
142 lines
3.6 KiB
TypeScript
142 lines
3.6 KiB
TypeScript
import { ChildProcess } from 'child_process';
|
|
import {
|
|
runCLI,
|
|
cleanupProject,
|
|
newProject,
|
|
uniq,
|
|
runCommandUntil,
|
|
killProcessAndPorts,
|
|
fileExists,
|
|
checkFilesExist,
|
|
runE2ETests,
|
|
} from 'e2e/utils';
|
|
|
|
describe('@nx/react-native', () => {
|
|
let appName: string;
|
|
|
|
beforeAll(() => {
|
|
newProject();
|
|
appName = uniq('app');
|
|
runCLI(
|
|
`generate @nx/react-native:app ${appName} --install=false --no-interactive --unitTestRunner=jest --linter=eslint`
|
|
);
|
|
});
|
|
|
|
afterAll(() => cleanupProject());
|
|
|
|
it('should bundle the app', async () => {
|
|
expect(() =>
|
|
runCLI(
|
|
`bundle ${appName} --platform=ios --bundle-output=dist.js --entry-file=src/main.tsx`
|
|
)
|
|
).not.toThrow();
|
|
fileExists(` ${appName}/dist.js`);
|
|
}, 200_000);
|
|
|
|
it('should start the app', async () => {
|
|
let process: ChildProcess;
|
|
const port = 8081;
|
|
|
|
try {
|
|
process = await runCommandUntil(
|
|
`start ${appName} --no-interactive --port=${port}`,
|
|
(output) => {
|
|
return (
|
|
output.includes(`http://localhost:${port}`) ||
|
|
output.includes('Starting JS server...') ||
|
|
output.includes('Welcome to Metro')
|
|
);
|
|
}
|
|
);
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
|
|
// port and process cleanup
|
|
if (process && process.pid) {
|
|
await killProcessAndPorts(process.pid, port);
|
|
}
|
|
});
|
|
|
|
it('should serve', async () => {
|
|
let process: ChildProcess;
|
|
const port = 8081;
|
|
|
|
try {
|
|
process = await runCommandUntil(
|
|
`serve ${appName} --port=${port}`,
|
|
(output) => {
|
|
return output.includes(`http://localhost:${port}`);
|
|
}
|
|
);
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
|
|
// port and process cleanup
|
|
try {
|
|
if (process && process.pid) {
|
|
await killProcessAndPorts(process.pid, port);
|
|
}
|
|
} catch (err) {
|
|
expect(err).toBeFalsy();
|
|
}
|
|
});
|
|
|
|
it('should run e2e for cypress', async () => {
|
|
if (runE2ETests()) {
|
|
expect(() => runCLI(`e2e ${appName}-e2e`)).not.toThrow();
|
|
|
|
expect(() =>
|
|
runCLI(`e2e ${appName}-e2e --configuration=ci`)
|
|
).not.toThrow();
|
|
|
|
// port and process cleanup
|
|
try {
|
|
if (process && process.pid) {
|
|
await killProcessAndPorts(process.pid, 4200);
|
|
}
|
|
} catch (err) {
|
|
expect(err).toBeFalsy();
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should create storybook with application', async () => {
|
|
runCLI(
|
|
`generate @nx/react:storybook-configuration ${appName} --generateStories --no-interactive`
|
|
);
|
|
checkFilesExist(
|
|
`${appName}/.storybook/main.ts`,
|
|
`${appName}/src/app/App.stories.tsx`
|
|
);
|
|
});
|
|
|
|
it('should run build with vite bundler and e2e with playwright', async () => {
|
|
const appName2 = uniq('my-app');
|
|
runCLI(
|
|
`generate @nx/react-native:application ${appName2} --directory=apps/${appName2} --bundler=vite --e2eTestRunner=playwright --install=false --no-interactive --unitTestRunner=jest --linter=eslint`
|
|
);
|
|
expect(() => runCLI(`build ${appName2}`)).not.toThrow();
|
|
if (runE2ETests()) {
|
|
expect(() => runCLI(`e2e ${appName2}-e2e`)).not.toThrow();
|
|
// port and process cleanup
|
|
try {
|
|
if (process && process.pid) {
|
|
await killProcessAndPorts(process.pid, 4200);
|
|
}
|
|
} catch (err) {
|
|
expect(err).toBeFalsy();
|
|
}
|
|
}
|
|
|
|
runCLI(
|
|
`generate @nx/react-native:storybook-configuration ${appName2} --generateStories --no-interactive`
|
|
);
|
|
checkFilesExist(
|
|
`apps/${appName2}/.storybook/main.ts`,
|
|
`apps/${appName2}/src/app/App.stories.tsx`
|
|
);
|
|
});
|
|
});
|