feat(core): support nested structure for cra-to-nx (#13253)
This commit is contained in:
parent
2772fab0e7
commit
590a3dc769
@ -43,7 +43,7 @@ In this article, you’ll learn how to:
|
||||
- Convert CRA scripts for use in Nx
|
||||
- Create a library and use it in your application
|
||||
|
||||
For this example, you’ll be migrating the default CRA typescript template app into an Nx workspace. This is the code that is generated when you run `yarn create react-app webapp --template typescript`.
|
||||
For this example, you’ll be migrating the default CRA typescript template app into an Nx workspace. This is the code that is generated when you run `npx create-react-app webapp --template typescript`.
|
||||
|
||||
There is also a [repo](https://github.com/nrwl/cra-to-nx-migration) that shows the finished result of this guide and for each step a [diff](https://github.com/nrwl/cra-to-nx-migration/commits/main) will be provided to see the exact code changes that occur for that step.
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ describe('cra-to-nx', () => {
|
||||
const craToNxOutput = runCommand(
|
||||
`${
|
||||
pmc.runUninstalledPackage
|
||||
} cra-to-nx@${getPublishedVersion()} --nxCloud=false`
|
||||
} cra-to-nx@${getPublishedVersion()} --nxCloud=false --integrated`
|
||||
);
|
||||
|
||||
expect(craToNxOutput).toContain('🎉 Done!');
|
||||
@ -46,7 +46,7 @@ describe('cra-to-nx', () => {
|
||||
const craToNxOutput = runCommand(
|
||||
`${
|
||||
pmc.runUninstalledPackage
|
||||
} cra-to-nx@${getPublishedVersion()} --nxCloud=false --vite`
|
||||
} cra-to-nx@${getPublishedVersion()} --nxCloud=false --vite --integrated`
|
||||
);
|
||||
|
||||
expect(craToNxOutput).toContain('🎉 Done!');
|
||||
@ -56,6 +56,9 @@ describe('cra-to-nx', () => {
|
||||
|
||||
runCLI(`build ${appName}`);
|
||||
checkFilesExist(`dist/apps/${appName}/index.html`);
|
||||
|
||||
const unitTestsOutput = runCLI(`test ${appName}`);
|
||||
expect(unitTestsOutput).toContain('Successfully ran target test');
|
||||
});
|
||||
|
||||
it('should convert to an integrated workspace with Vite with custom port', () => {
|
||||
@ -66,10 +69,53 @@ describe('cra-to-nx', () => {
|
||||
runCommand(
|
||||
`${
|
||||
pmc.runUninstalledPackage
|
||||
} cra-to-nx@${getPublishedVersion()} --nxCloud=false --vite --force`
|
||||
} cra-to-nx@${getPublishedVersion()} --nxCloud=false --vite --force --integrated`
|
||||
);
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
it('should convert to a nested workspace with craco (webpack)', () => {
|
||||
const appName = 'my-app';
|
||||
createReactApp(appName);
|
||||
|
||||
const craToNxOutput = runCommand(
|
||||
`${
|
||||
pmc.runUninstalledPackage
|
||||
} cra-to-nx@${getPublishedVersion()} --nxCloud=false`
|
||||
);
|
||||
|
||||
expect(craToNxOutput).toContain('🎉 Done!');
|
||||
|
||||
runCLI(`build ${appName}`);
|
||||
checkFilesExist(`public/index.html`, `dist/asset-manifest.json`);
|
||||
const manifest = readJson(`dist/asset-manifest.json`);
|
||||
checkFilesExist(...manifest['entrypoints'].map((f) => `dist/${f}`));
|
||||
});
|
||||
|
||||
it('should convert to an nested workspace with Vite', () => {
|
||||
const appName = 'my-app';
|
||||
createReactApp(appName);
|
||||
|
||||
const craToNxOutput = runCommand(
|
||||
`${
|
||||
pmc.runUninstalledPackage
|
||||
} cra-to-nx@${getPublishedVersion()} --nxCloud=false --vite`
|
||||
);
|
||||
|
||||
expect(craToNxOutput).toContain('🎉 Done!');
|
||||
|
||||
const viteConfig = readFile(`vite.config.js`);
|
||||
expect(viteConfig).toContain('port: 4200'); // default port
|
||||
|
||||
runCLI(`build ${appName}`);
|
||||
checkFilesExist(`dist/index.html`);
|
||||
|
||||
const unitTestsOutput = runCLI(`test ${appName}`);
|
||||
expect(unitTestsOutput).toContain('Successfully ran target test');
|
||||
});
|
||||
});
|
||||
|
||||
@ -27,6 +27,11 @@ export const commandsObject = yargs
|
||||
describe: 'Use Vite and Vitest (instead of Webpack and Jest)',
|
||||
default: false,
|
||||
})
|
||||
.option('integrated', {
|
||||
type: 'boolean',
|
||||
describe: 'Use integrated folder structure, with apps folder',
|
||||
default: false,
|
||||
})
|
||||
.help();
|
||||
|
||||
createNxWorkspaceForReact(commandsObject.argv).catch((e) => {
|
||||
|
||||
@ -1,13 +1,20 @@
|
||||
import { readJsonFile, writeJsonFile } from 'nx/src/utils/fileutils';
|
||||
|
||||
export function addCracoCommandsToPackageScripts(appName: string) {
|
||||
const packageJson = readJsonFile(`apps/${appName}/package.json`);
|
||||
export function addCracoCommandsToPackageScripts(
|
||||
appName: string,
|
||||
isNested: boolean
|
||||
) {
|
||||
const packageJsonPath = isNested
|
||||
? 'package.json'
|
||||
: `apps/${appName}/package.json`;
|
||||
const distPath = isNested ? 'dist' : `../../dist/apps/${appName}`;
|
||||
const packageJson = readJsonFile(packageJsonPath);
|
||||
packageJson.scripts = {
|
||||
...packageJson.scripts,
|
||||
start: 'craco start',
|
||||
serve: 'npm start',
|
||||
build: `cross-env BUILD_PATH=../../dist/apps/${appName} craco build`,
|
||||
build: `cross-env BUILD_PATH=${distPath} craco build`,
|
||||
test: 'craco test',
|
||||
};
|
||||
writeJsonFile(`apps/${appName}/package.json`, packageJson);
|
||||
writeJsonFile(packageJsonPath, packageJson);
|
||||
}
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
import { readJsonFile, writeJsonFile } from 'nx/src/utils/fileutils';
|
||||
|
||||
export function addViteCommandsToPackageScripts(appName: string) {
|
||||
const packageJson = readJsonFile(`apps/${appName}/package.json`);
|
||||
export function addViteCommandsToPackageScripts(
|
||||
appName: string,
|
||||
isNested: boolean
|
||||
) {
|
||||
const packageJsonPath = isNested
|
||||
? 'package.json'
|
||||
: `apps/${appName}/package.json`;
|
||||
const packageJson = readJsonFile(packageJsonPath);
|
||||
packageJson.scripts = {
|
||||
...packageJson.scripts,
|
||||
start: 'vite',
|
||||
@ -9,5 +15,5 @@ export function addViteCommandsToPackageScripts(appName: string) {
|
||||
build: `vite build`,
|
||||
test: 'vitest',
|
||||
};
|
||||
writeJsonFile(`apps/${appName}/package.json`, packageJson, { spaces: 2 });
|
||||
writeJsonFile(packageJsonPath, packageJson, { spaces: 2 });
|
||||
}
|
||||
|
||||
@ -1,11 +1,25 @@
|
||||
import { removeSync } from 'fs-extra';
|
||||
import { readJsonFile, writeJsonFile } from 'nx/src/utils/fileutils';
|
||||
|
||||
export function cleanUpFiles(appName: string) {
|
||||
export function cleanUpFiles(appName: string, isNested: boolean) {
|
||||
// Delete targets from project since we delegate to npm scripts.
|
||||
const json = readJsonFile(`apps/${appName}/project.json`);
|
||||
const projectJsonPath = isNested
|
||||
? 'project.json'
|
||||
: `apps/${appName}/project.json`;
|
||||
const json = readJsonFile(projectJsonPath);
|
||||
delete json.targets;
|
||||
writeJsonFile(`apps/${appName}/project.json`, json);
|
||||
if (isNested) {
|
||||
if (json.sourceRoot) {
|
||||
json.sourceRoot = json.sourceRoot.replace(`apps/${appName}/`, '');
|
||||
}
|
||||
if (json['$schema']) {
|
||||
json['$schema'] = json['$schema'].replace(
|
||||
'../../node_modules',
|
||||
'node_modules'
|
||||
);
|
||||
}
|
||||
}
|
||||
writeJsonFile(projectJsonPath, json);
|
||||
|
||||
removeSync('temp-workspace');
|
||||
}
|
||||
|
||||
@ -20,27 +20,35 @@ import { renameJsToJsx } from './rename-js-to-jsx';
|
||||
import { writeViteIndexHtml } from './write-vite-index-html';
|
||||
import { checkForCustomWebpackSetup } from './check-for-custom-webpack-setup';
|
||||
|
||||
export interface Options {
|
||||
force: boolean;
|
||||
e2e: boolean;
|
||||
nxCloud: boolean;
|
||||
vite: boolean;
|
||||
integrated: boolean;
|
||||
}
|
||||
interface NormalizedOptions extends Options {
|
||||
packageManager: string;
|
||||
pmc: PackageManagerCommands;
|
||||
appIsJs: boolean;
|
||||
reactAppName: string;
|
||||
isCRA5: boolean;
|
||||
npxYesFlagNeeded: boolean;
|
||||
isVite: boolean;
|
||||
isNested: boolean;
|
||||
}
|
||||
|
||||
function addDependencies(pmc: PackageManagerCommands, ...deps: string[]) {
|
||||
const depsArg = deps.join(' ');
|
||||
output.log({ title: `📦 Adding dependencies: ${depsArg}` });
|
||||
execSync(`${pmc.addDev} ${depsArg}`, { stdio: [0, 1, 2] });
|
||||
}
|
||||
|
||||
export async function createNxWorkspaceForReact(options: Record<string, any>) {
|
||||
if (!options.force) {
|
||||
checkForUncommittedChanges();
|
||||
checkForCustomWebpackSetup();
|
||||
}
|
||||
export function normalizeOptions(options: Options): NormalizedOptions {
|
||||
const packageManager = detectPackageManager();
|
||||
const pmc = getPackageManagerCommand(packageManager);
|
||||
|
||||
output.log({ title: '✨ Nx initialization' });
|
||||
|
||||
let appIsJs = true;
|
||||
|
||||
if (fileExists(`tsconfig.json`)) {
|
||||
appIsJs = false;
|
||||
}
|
||||
const appIsJs = !fileExists(`tsconfig.json`);
|
||||
|
||||
const reactAppName = readNameFromPackageJson();
|
||||
const packageJson = readJsonFile('package.json');
|
||||
@ -52,110 +60,50 @@ export async function createNxWorkspaceForReact(options: Record<string, any>) {
|
||||
const npmVersion = execSync('npm -v').toString();
|
||||
// Should remove this check 04/2023 once Node 14 & npm 6 reach EOL
|
||||
const npxYesFlagNeeded = !npmVersion.startsWith('6'); // npm 7 added -y flag to npx
|
||||
const isVite = options.vite || options.bundler === 'vite';
|
||||
const isVite = options.vite;
|
||||
const isNested = !options.integrated;
|
||||
|
||||
execSync(
|
||||
`npx ${
|
||||
npxYesFlagNeeded ? '-y' : ''
|
||||
} create-nx-workspace@latest temp-workspace --appName=${reactAppName} --preset=react --style=css --packageManager=${packageManager} ${
|
||||
options.nxCloud ? '--nxCloud' : '--nxCloud=false'
|
||||
}`,
|
||||
{ stdio: [0, 1, 2] }
|
||||
);
|
||||
|
||||
output.log({ title: '👋 Welcome to Nx!' });
|
||||
|
||||
output.log({ title: '🧹 Clearing unused files' });
|
||||
|
||||
copySync(`temp-workspace/apps/${reactAppName}/project.json`, 'project.json');
|
||||
removeSync(`temp-workspace/apps/${reactAppName}/`);
|
||||
removeSync('node_modules');
|
||||
|
||||
output.log({ title: '🚚 Moving your React app in your new Nx workspace' });
|
||||
|
||||
const requiredCraFiles = [
|
||||
'project.json',
|
||||
'package.json',
|
||||
'src',
|
||||
'public',
|
||||
appIsJs ? null : 'tsconfig.json',
|
||||
packageManager === 'yarn' ? 'yarn.lock' : null,
|
||||
packageManager === 'pnpm' ? 'pnpm-lock.yaml' : null,
|
||||
packageManager === 'npm' ? 'package-lock.json' : null,
|
||||
];
|
||||
|
||||
const optionalCraFiles = ['README.md'];
|
||||
|
||||
const filesToMove = [...requiredCraFiles, ...optionalCraFiles].filter(
|
||||
Boolean
|
||||
);
|
||||
|
||||
filesToMove.forEach((f) => {
|
||||
try {
|
||||
moveSync(f, `temp-workspace/apps/${reactAppName}/${f}`, {
|
||||
overwrite: true,
|
||||
});
|
||||
} catch (error) {
|
||||
if (requiredCraFiles.includes(f)) {
|
||||
throw error;
|
||||
return {
|
||||
...options,
|
||||
packageManager,
|
||||
pmc,
|
||||
appIsJs,
|
||||
reactAppName,
|
||||
isCRA5,
|
||||
npxYesFlagNeeded,
|
||||
isVite,
|
||||
isNested,
|
||||
};
|
||||
}
|
||||
|
||||
export async function createNxWorkspaceForReact(options: Record<string, any>) {
|
||||
if (!options.force) {
|
||||
checkForUncommittedChanges();
|
||||
checkForCustomWebpackSetup();
|
||||
}
|
||||
});
|
||||
|
||||
process.chdir('temp-workspace/');
|
||||
output.log({ title: '✨ Nx initialization' });
|
||||
|
||||
if (isVite) {
|
||||
output.log({ title: '🧑🔧 Setting up Vite' });
|
||||
const { addViteCommandsToPackageScripts } = await import(
|
||||
'./add-vite-commands-to-package-scripts'
|
||||
);
|
||||
addViteCommandsToPackageScripts(reactAppName);
|
||||
writeViteConfig(reactAppName);
|
||||
writeViteIndexHtml(reactAppName);
|
||||
renameJsToJsx(reactAppName);
|
||||
} else {
|
||||
output.log({ title: '🧑🔧 Setting up craco + Webpack' });
|
||||
const { addCracoCommandsToPackageScripts } = await import(
|
||||
'./add-craco-commands-to-package-scripts'
|
||||
);
|
||||
addCracoCommandsToPackageScripts(reactAppName);
|
||||
|
||||
writeCracoConfig(reactAppName, isCRA5);
|
||||
|
||||
output.log({
|
||||
title: '🛬 Skip CRA preflight check since Nx manages the monorepo',
|
||||
});
|
||||
|
||||
execSync(`echo "SKIP_PREFLIGHT_CHECK=true" > .env`, { stdio: [0, 1, 2] });
|
||||
const normalizedOptions = normalizeOptions(options as Options);
|
||||
reorgnizeWorkspaceStructure(normalizedOptions);
|
||||
}
|
||||
|
||||
async function reorgnizeWorkspaceStructure(options: NormalizedOptions) {
|
||||
createTempWorkspace(options);
|
||||
|
||||
moveFilesToTempWorkspace(options);
|
||||
|
||||
await addBundler(options);
|
||||
|
||||
output.log({ title: '🧶 Add all node_modules to .gitignore' });
|
||||
|
||||
execSync(`echo "node_modules" >> .gitignore`, { stdio: [0, 1, 2] });
|
||||
|
||||
process.chdir('../');
|
||||
|
||||
output.log({ title: '🚚 Folder restructuring.' });
|
||||
copyFromTempWorkspaceToRoot();
|
||||
|
||||
readdirSync('./temp-workspace').forEach((f) => {
|
||||
moveSync(`temp-workspace/${f}`, `./${f}`, { overwrite: true });
|
||||
});
|
||||
|
||||
output.log({ title: '🧹 Cleaning up.' });
|
||||
|
||||
cleanUpFiles(reactAppName);
|
||||
|
||||
output.log({ title: "📃 Extend the app's tsconfig.json from the base" });
|
||||
|
||||
setupTsConfig(reactAppName);
|
||||
|
||||
if (options.e2e) {
|
||||
output.log({ title: '📃 Setup e2e tests' });
|
||||
setupE2eProject(reactAppName);
|
||||
} else {
|
||||
removeSync(`apps/${reactAppName}-e2e`);
|
||||
execSync(`${pmc.rm} @nrwl/cypress eslint-plugin-cypress`);
|
||||
}
|
||||
cleanUpUnusedFilesAndAddConfigFiles(options);
|
||||
|
||||
output.log({ title: '🙂 Please be patient, one final step remaining!' });
|
||||
|
||||
@ -164,17 +112,17 @@ export async function createNxWorkspaceForReact(options: Record<string, any>) {
|
||||
});
|
||||
|
||||
addDependencies(
|
||||
pmc,
|
||||
options.pmc,
|
||||
'@testing-library/jest-dom',
|
||||
'eslint-config-react-app',
|
||||
'web-vitals',
|
||||
'jest-watch-typeahead'
|
||||
);
|
||||
|
||||
if (isVite) {
|
||||
addDependencies(pmc, 'vite', 'vitest', '@vitejs/plugin-react');
|
||||
if (options.isVite) {
|
||||
addDependencies(options.pmc, 'vite', 'vitest', '@vitejs/plugin-react');
|
||||
} else {
|
||||
addDependencies(pmc, '@craco/craco', 'cross-env', 'react-scripts');
|
||||
addDependencies(options.pmc, '@craco/craco', 'cross-env', 'react-scripts');
|
||||
}
|
||||
|
||||
output.log({ title: '🎉 Done!' });
|
||||
@ -188,20 +136,148 @@ export async function createNxWorkspaceForReact(options: Record<string, any>) {
|
||||
],
|
||||
});
|
||||
|
||||
if (isVite) {
|
||||
if (options.isVite) {
|
||||
const indexPath = options.isNested
|
||||
? 'index.html'
|
||||
: `apps/${options.reactAppName}/index.html`;
|
||||
const oldIndexPath = options.isNested
|
||||
? 'public/index.html'
|
||||
: `apps/${options.reactAppName}/public/index.html`;
|
||||
output.note({
|
||||
title: `A new apps/${reactAppName}/index.html has been created. Compare it to the previous apps/${reactAppName}/public/index.html file and make any changes needed, then delete the previous file.`,
|
||||
title: `A new ${indexPath} has been created. Compare it to the previous ${oldIndexPath} file and make any changes needed, then delete the previous file.`,
|
||||
});
|
||||
}
|
||||
|
||||
output.note({
|
||||
title: 'Or, you can try the commands!',
|
||||
bodyLines: [
|
||||
`npx nx serve ${reactAppName}`,
|
||||
`npx nx build ${reactAppName}`,
|
||||
`npx nx test ${reactAppName}`,
|
||||
`npx nx serve ${options.reactAppName}`,
|
||||
`npx nx build ${options.reactAppName}`,
|
||||
`npx nx test ${options.reactAppName}`,
|
||||
` `,
|
||||
`https://nx.dev/getting-started/intro#10-try-the-commands`,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function createTempWorkspace(options: NormalizedOptions) {
|
||||
execSync(
|
||||
`npx ${
|
||||
options.npxYesFlagNeeded ? '-y' : ''
|
||||
} create-nx-workspace@latest temp-workspace --appName=${
|
||||
options.reactAppName
|
||||
} --preset=react --style=css --packageManager=${options.packageManager} ${
|
||||
options.nxCloud ? '--nxCloud' : '--nxCloud=false'
|
||||
}`,
|
||||
{ stdio: [0, 1, 2] }
|
||||
);
|
||||
|
||||
output.log({ title: '👋 Welcome to Nx!' });
|
||||
|
||||
output.log({ title: '🧹 Clearing unused files' });
|
||||
|
||||
copySync(
|
||||
`temp-workspace/apps/${options.reactAppName}/project.json`,
|
||||
'project.json'
|
||||
);
|
||||
removeSync(`temp-workspace/apps/${options.reactAppName}/`);
|
||||
removeSync('node_modules');
|
||||
}
|
||||
|
||||
function moveFilesToTempWorkspace(options: NormalizedOptions) {
|
||||
output.log({ title: '🚚 Moving your React app in your new Nx workspace' });
|
||||
|
||||
const requiredCraFiles = [
|
||||
'project.json',
|
||||
options.isNested ? null : 'package.json',
|
||||
'src',
|
||||
'public',
|
||||
options.appIsJs ? null : 'tsconfig.json',
|
||||
options.packageManager === 'yarn' ? 'yarn.lock' : null,
|
||||
options.packageManager === 'pnpm' ? 'pnpm-lock.yaml' : null,
|
||||
options.packageManager === 'npm' ? 'package-lock.json' : null,
|
||||
];
|
||||
|
||||
const optionalCraFiles = ['README.md'];
|
||||
|
||||
const filesToMove = [...requiredCraFiles, ...optionalCraFiles].filter(
|
||||
Boolean
|
||||
);
|
||||
|
||||
filesToMove.forEach((f) => {
|
||||
try {
|
||||
moveSync(
|
||||
f,
|
||||
options.isNested
|
||||
? `temp-workspace/${f}`
|
||||
: `temp-workspace/apps/${options.reactAppName}/${f}`,
|
||||
{
|
||||
overwrite: true,
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
if (requiredCraFiles.includes(f)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
process.chdir('temp-workspace/');
|
||||
}
|
||||
|
||||
async function addBundler(options: NormalizedOptions) {
|
||||
if (options.isVite) {
|
||||
output.log({ title: '🧑🔧 Setting up Vite' });
|
||||
const { addViteCommandsToPackageScripts } = await import(
|
||||
'./add-vite-commands-to-package-scripts'
|
||||
);
|
||||
addViteCommandsToPackageScripts(options.reactAppName, options.isNested);
|
||||
writeViteConfig(options.reactAppName, options.isNested, options.appIsJs);
|
||||
writeViteIndexHtml(options.reactAppName, options.isNested, options.appIsJs);
|
||||
renameJsToJsx(options.reactAppName, options.isNested);
|
||||
} else {
|
||||
output.log({ title: '🧑🔧 Setting up craco + Webpack' });
|
||||
const { addCracoCommandsToPackageScripts } = await import(
|
||||
'./add-craco-commands-to-package-scripts'
|
||||
);
|
||||
addCracoCommandsToPackageScripts(options.reactAppName, options.isNested);
|
||||
|
||||
writeCracoConfig(options.reactAppName, options.isCRA5, options.isNested);
|
||||
|
||||
output.log({
|
||||
title: '🛬 Skip CRA preflight check since Nx manages the monorepo',
|
||||
});
|
||||
|
||||
execSync(`echo "SKIP_PREFLIGHT_CHECK=true" > .env`, { stdio: [0, 1, 2] });
|
||||
}
|
||||
}
|
||||
|
||||
function copyFromTempWorkspaceToRoot() {
|
||||
output.log({ title: '🚚 Folder restructuring.' });
|
||||
|
||||
readdirSync('./temp-workspace').forEach((f) => {
|
||||
moveSync(`temp-workspace/${f}`, `./${f}`, { overwrite: true });
|
||||
});
|
||||
}
|
||||
|
||||
function cleanUpUnusedFilesAndAddConfigFiles(options: NormalizedOptions) {
|
||||
output.log({ title: '🧹 Cleaning up.' });
|
||||
|
||||
cleanUpFiles(options.reactAppName, options.isNested);
|
||||
|
||||
output.log({ title: "📃 Extend the app's tsconfig.json from the base" });
|
||||
|
||||
setupTsConfig(options.reactAppName, options.isNested);
|
||||
|
||||
if (options.e2e && !options.isNested) {
|
||||
output.log({ title: '📃 Setup e2e tests' });
|
||||
setupE2eProject(options.reactAppName);
|
||||
} else {
|
||||
removeSync(`apps/${options.reactAppName}-e2e`);
|
||||
execSync(`${options.pmc.rm} @nrwl/cypress eslint-plugin-cypress`);
|
||||
}
|
||||
|
||||
if (options.isNested) {
|
||||
removeSync('apps');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import { sync } from 'glob';
|
||||
import { renameSync, readFileSync } from 'fs-extra';
|
||||
import { performance } from 'perf_hooks';
|
||||
|
||||
// Vite cannot process JSX like <div> or <Header> unless the file is named .jsx or .tsx
|
||||
export function renameJsToJsx(appName: string) {
|
||||
const files = sync(`apps/${appName}/src/**/*.js`);
|
||||
export function renameJsToJsx(appName: string, isNested: boolean) {
|
||||
const files = sync(isNested ? 'src/**/*.js' : `apps/${appName}/src/**/*.js`);
|
||||
|
||||
files.forEach((file) => {
|
||||
const content = readFileSync(file).toString();
|
||||
|
||||
@ -3,9 +3,10 @@ import {
|
||||
readJsonFile,
|
||||
writeJsonFile,
|
||||
} from 'nx/src/utils/fileutils';
|
||||
import { join } from 'path';
|
||||
|
||||
const defaultTsConfig = {
|
||||
extends: '../../tsconfig.base.json',
|
||||
const defaultTsConfig = (relativePathToRoot: string) => ({
|
||||
extends: join(relativePathToRoot, 'tsconfig.base.json'),
|
||||
compilerOptions: {
|
||||
jsx: 'react',
|
||||
allowJs: true,
|
||||
@ -22,26 +23,26 @@ const defaultTsConfig = {
|
||||
path: './tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
const defaultTsConfigApp = {
|
||||
const defaultTsConfigApp = (relativePathToRoot: string) => ({
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: '../../dist/out-tsc',
|
||||
outDir: join(relativePathToRoot, 'dist/out-tsc'),
|
||||
types: ['node'],
|
||||
},
|
||||
files: [
|
||||
'../../node_modules/@nrwl/react/typings/cssmodule.d.ts',
|
||||
'../../node_modules/@nrwl/react/typings/image.d.ts',
|
||||
join(relativePathToRoot, 'node_modules/@nrwl/react/typings/cssmodule.d.ts'),
|
||||
join(relativePathToRoot, 'node_modules/@nrwl/react/typings/image.d.ts'),
|
||||
],
|
||||
exclude: ['**/*.spec.ts', '**/*.spec.tsx'],
|
||||
include: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'],
|
||||
};
|
||||
});
|
||||
|
||||
const defaultTsConfigSpec = {
|
||||
const defaultTsConfigSpec = (relativePathToRoot: string) => ({
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: '../../dist/out-tsc',
|
||||
outDir: join(relativePathToRoot, 'dist/out-tsc'),
|
||||
module: 'commonjs',
|
||||
types: ['jest', 'node'],
|
||||
},
|
||||
@ -53,15 +54,28 @@ const defaultTsConfigSpec = {
|
||||
'**/*.d.ts',
|
||||
],
|
||||
files: [
|
||||
'../../node_modules/@nrwl/react/typings/cssmodule.d.ts',
|
||||
'../../node_modules/@nrwl/react/typings/image.d.ts',
|
||||
join(relativePathToRoot, 'node_modules/@nrwl/react/typings/cssmodule.d.ts'),
|
||||
join(relativePathToRoot, 'node_modules/@nrwl/react/typings/image.d.ts'),
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
export function setupTsConfig(appName: string) {
|
||||
if (fileExists(`apps/${appName}/tsconfig.json`)) {
|
||||
const json = readJsonFile(`apps/${appName}/tsconfig.json`);
|
||||
json.extends = '../../tsconfig.base.json';
|
||||
export function setupTsConfig(appName: string, isNested: boolean) {
|
||||
const tsconfigPath = isNested
|
||||
? 'tsconfig.json'
|
||||
: `apps/${appName}/tsconfig.json`;
|
||||
const tsconfigAppPath = isNested
|
||||
? 'tsconfig.app.json'
|
||||
: `apps/${appName}/tsconfig.app.json`;
|
||||
const tsconfiSpecPath = isNested
|
||||
? 'tsconfig.spec.json'
|
||||
: `apps/${appName}/tsconfig.spec.json`;
|
||||
const tsconfigBasePath = isNested
|
||||
? './tsconfig.base.json'
|
||||
: '../../tsconfig.base.json';
|
||||
const relativePathToRoot = isNested ? '.' : '../../';
|
||||
if (fileExists(tsconfigPath)) {
|
||||
const json = readJsonFile(tsconfigPath);
|
||||
json.extends = tsconfigBasePath;
|
||||
if (json.compilerOptions) {
|
||||
json.compilerOptions.jsx = 'react';
|
||||
} else {
|
||||
@ -72,24 +86,24 @@ export function setupTsConfig(appName: string) {
|
||||
allowSyntheticDefaultImports: true,
|
||||
};
|
||||
}
|
||||
writeJsonFile(`apps/${appName}/tsconfig.json`, json);
|
||||
writeJsonFile(tsconfigPath, json);
|
||||
} else {
|
||||
writeJsonFile(`apps/${appName}/tsconfig.json`, defaultTsConfig);
|
||||
writeJsonFile(tsconfigPath, defaultTsConfig(relativePathToRoot));
|
||||
}
|
||||
|
||||
if (fileExists(`apps/${appName}/tsconfig.app.json`)) {
|
||||
const json = readJsonFile(`apps/${appName}/tsconfig.app.json`);
|
||||
if (fileExists(tsconfigAppPath)) {
|
||||
const json = readJsonFile(tsconfigAppPath);
|
||||
json.extends = './tsconfig.json';
|
||||
writeJsonFile(`apps/${appName}/tsconfig.app.json`, json);
|
||||
writeJsonFile(tsconfigAppPath, json);
|
||||
} else {
|
||||
writeJsonFile(`apps/${appName}/tsconfig.app.json`, defaultTsConfigApp);
|
||||
writeJsonFile(tsconfigAppPath, defaultTsConfigApp(relativePathToRoot));
|
||||
}
|
||||
|
||||
if (fileExists(`apps/${appName}/tsconfig.spec.json`)) {
|
||||
const json = readJsonFile(`apps/${appName}/tsconfig.spec.json`);
|
||||
if (fileExists(tsconfiSpecPath)) {
|
||||
const json = readJsonFile(tsconfiSpecPath);
|
||||
json.extends = './tsconfig.json';
|
||||
writeJsonFile(`apps/${appName}/tsconfig.spec.json`, json);
|
||||
writeJsonFile(tsconfiSpecPath, json);
|
||||
} else {
|
||||
writeJsonFile(`apps/${appName}/tsconfig.spec.json`, defaultTsConfigSpec);
|
||||
writeJsonFile(tsconfiSpecPath, defaultTsConfigSpec(relativePathToRoot));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
export function writeCracoConfig(appName: string, isCRA5: boolean) {
|
||||
export function writeCracoConfig(
|
||||
appName: string,
|
||||
isCRA5: boolean,
|
||||
isNested: boolean
|
||||
) {
|
||||
const configOverride = `
|
||||
const path = require('path');
|
||||
const TsConfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||
@ -57,5 +61,8 @@ export function writeCracoConfig(appName: string, isCRA5: boolean) {
|
||||
},
|
||||
};
|
||||
`;
|
||||
writeFileSync(`apps/${appName}/craco.config.js`, configOverride);
|
||||
writeFileSync(
|
||||
isNested ? 'craco.config.js' : `apps/${appName}/craco.config.js`,
|
||||
configOverride
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import * as fs from 'fs';
|
||||
|
||||
export function writeViteConfig(appName: string) {
|
||||
export function writeViteConfig(
|
||||
appName: string,
|
||||
isNested: boolean,
|
||||
isJs: boolean
|
||||
) {
|
||||
let port = 4200;
|
||||
|
||||
// Use PORT from .env file if it exists in project.
|
||||
@ -14,14 +18,14 @@ export function writeViteConfig(appName: string) {
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
`apps/${appName}/vite.config.js`,
|
||||
isNested ? 'vite.config.js' : `apps/${appName}/vite.config.js`,
|
||||
`import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: '../../dist/apps/${appName}'
|
||||
outDir: ${isNested ? `'./dist'` : `'../../dist/apps/${appName}'`}
|
||||
},
|
||||
server: {
|
||||
port: ${port},
|
||||
@ -30,7 +34,7 @@ export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
setupFiles: 'src/setupTests.js',
|
||||
setupFiles: 'src/setupTests.${isJs ? 'js' : 'ts'}',
|
||||
css: true,
|
||||
},
|
||||
plugins: [react()],
|
||||
|
||||
@ -1,8 +1,17 @@
|
||||
import * as fs from 'fs';
|
||||
|
||||
export function writeViteIndexHtml(appName: string) {
|
||||
export function writeViteIndexHtml(
|
||||
appName: string,
|
||||
isNested: boolean,
|
||||
isJs: boolean
|
||||
) {
|
||||
const indexPath = isNested ? 'index.html' : `apps/${appName}/index.html`;
|
||||
if (fs.existsSync(indexPath)) {
|
||||
fs.copyFileSync(indexPath, indexPath + '.old');
|
||||
}
|
||||
const indexFile = isJs ? '/src/index.jsx' : '/src/index.tsx';
|
||||
fs.writeFileSync(
|
||||
`apps/${appName}/index.html`,
|
||||
indexPath,
|
||||
`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -13,7 +22,7 @@ export function writeViteIndexHtml(appName: string) {
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/index.jsx"></script>
|
||||
<script type="module" src="${indexFile}"></script>
|
||||
</body>
|
||||
</html>`
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user