cleanup(misc): redirect to nx init when running cra-to-nx (#15568)
This commit is contained in:
parent
ad12ab2ed3
commit
29be9dbaee
8
packages/cra-to-nx/index.ts
Normal file
8
packages/cra-to-nx/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const args = process.argv.slice(2).join(' ');
|
||||
execSync(`npx --yes nx@latest init ${args}`, {
|
||||
stdio: [0, 1, 2],
|
||||
});
|
||||
@ -15,20 +15,13 @@
|
||||
"CLI"
|
||||
],
|
||||
"bin": {
|
||||
"cra-to-nx": "src/index.js"
|
||||
"cra-to-nx": "./index.js"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/nrwl/nx/issues"
|
||||
},
|
||||
"homepage": "https://nx.dev",
|
||||
"dependencies": {
|
||||
"fs-extra": "^11.1.0",
|
||||
"glob": "7.1.4",
|
||||
"nx": "file:../nx",
|
||||
"tslib": "^2.3.0",
|
||||
"yargs": "^17.6.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cra-to-nx",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/cra-to-nx/src",
|
||||
"sourceRoot": "packages/cra-to-nx",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"lint": {},
|
||||
@ -9,7 +9,7 @@
|
||||
"build-base": {
|
||||
"executor": "@nrwl/js:tsc",
|
||||
"options": {
|
||||
"main": "packages/cra-to-nx/src/index.ts",
|
||||
"main": "packages/cra-to-nx/index.ts",
|
||||
"assets": [
|
||||
"packages/cra-to-nx/*.md",
|
||||
"packages/cra-to-nx/package.json",
|
||||
@ -27,7 +27,7 @@
|
||||
"options": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "node ./scripts/chmod build/packages/cra-to-nx/src/index.js"
|
||||
"command": "node ./scripts/chmod build/packages/cra-to-nx/index.js"
|
||||
},
|
||||
{
|
||||
"command": "node ./scripts/copy-readme.js cra-to-nx"
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as yargs from 'yargs';
|
||||
import { createNxWorkspaceForReact } from './lib/cra-to-nx';
|
||||
|
||||
export * from './lib/cra-to-nx';
|
||||
|
||||
export const commandsObject = yargs
|
||||
.parserConfiguration({ 'strip-dashed': true })
|
||||
.option('force', {
|
||||
type: 'boolean',
|
||||
describe: 'Skip validation and run the migration.',
|
||||
default: false,
|
||||
})
|
||||
.option('e2e', {
|
||||
type: 'boolean',
|
||||
describe: 'Generate end-to-end tests with Cypress.',
|
||||
default: false,
|
||||
})
|
||||
.option('nxCloud', {
|
||||
type: 'boolean',
|
||||
describe: 'Setup Nx Cloud.',
|
||||
default: true,
|
||||
})
|
||||
.option('vite', {
|
||||
type: 'boolean',
|
||||
describe:
|
||||
'Use Vite and Vitest (instead of Webpack and Jest). Pass --vite=false to use Webpack.',
|
||||
default: true,
|
||||
})
|
||||
.option('integrated', {
|
||||
type: 'boolean',
|
||||
describe: 'Use integrated folder structure, with apps folder.',
|
||||
default: false,
|
||||
})
|
||||
.help();
|
||||
|
||||
createNxWorkspaceForReact(commandsObject.argv).catch((e) => {
|
||||
console.log(e);
|
||||
process.exit(1);
|
||||
});
|
||||
@ -1,22 +0,0 @@
|
||||
import { readJsonFile, writeJsonFile } from 'nx/src/utils/fileutils';
|
||||
|
||||
export function addCracoCommandsToPackageScripts(
|
||||
appName: string,
|
||||
isStandalone: boolean
|
||||
) {
|
||||
const packageJsonPath = isStandalone
|
||||
? 'package.json'
|
||||
: `apps/${appName}/package.json`;
|
||||
const distPath = isStandalone
|
||||
? `dist/${appName}`
|
||||
: `../../dist/apps/${appName}`;
|
||||
const packageJson = readJsonFile(packageJsonPath);
|
||||
packageJson.scripts = {
|
||||
...packageJson.scripts,
|
||||
start: 'nx exec -- craco start',
|
||||
serve: 'npm start',
|
||||
build: `cross-env BUILD_PATH=${distPath} nx exec -- craco build`,
|
||||
test: 'nx exec -- craco test',
|
||||
};
|
||||
writeJsonFile(packageJsonPath, packageJson);
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
import { readJsonFile, writeJsonFile } from 'nx/src/utils/fileutils';
|
||||
|
||||
export function addViteCommandsToPackageScripts(
|
||||
appName: string,
|
||||
isStandalone: boolean
|
||||
) {
|
||||
const packageJsonPath = isStandalone
|
||||
? 'package.json'
|
||||
: `apps/${appName}/package.json`;
|
||||
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',
|
||||
};
|
||||
writeJsonFile(packageJsonPath, packageJson, { spaces: 2 });
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
import { readJsonFile } from 'nx/src/utils/fileutils';
|
||||
|
||||
export function checkForCustomWebpackSetup() {
|
||||
const packageJson = readJsonFile('package.json');
|
||||
const combinedDeps = {
|
||||
...packageJson.dependencies,
|
||||
...packageJson.devDependencies,
|
||||
};
|
||||
['react-app-rewired', '@craco/craco'].forEach((pkg) => {
|
||||
if (combinedDeps[pkg]) {
|
||||
console.log(
|
||||
`Skipping migration due to custom webpack setup. Found "${pkg}" usage. Use --force to continue anyway.`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export function checkForUncommittedChanges() {
|
||||
const gitResult = execSync(`git status --porcelain`);
|
||||
if (gitResult.length > 0) {
|
||||
console.log('❗️ Careful!');
|
||||
console.log('You have uncommited changes in your repository.');
|
||||
console.log('');
|
||||
console.log(gitResult.toString());
|
||||
console.log('Please commit your changes before running the migrator!');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
import { removeSync } from 'fs-extra';
|
||||
import { readJsonFile, writeJsonFile } from 'nx/src/utils/fileutils';
|
||||
|
||||
export function cleanUpFiles(appName: string, isStandalone: boolean) {
|
||||
// Delete targets from project since we delegate to npm scripts.
|
||||
const projectJsonPath = isStandalone
|
||||
? 'project.json'
|
||||
: `apps/${appName}/project.json`;
|
||||
const json = readJsonFile(projectJsonPath);
|
||||
delete json.targets;
|
||||
if (isStandalone) {
|
||||
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');
|
||||
|
||||
if (isStandalone) {
|
||||
removeSync('babel.config.json');
|
||||
removeSync('jest.preset.js');
|
||||
removeSync('jest.config.ts');
|
||||
removeSync('libs');
|
||||
removeSync('tools');
|
||||
}
|
||||
}
|
||||
@ -1,317 +0,0 @@
|
||||
import { execSync } from 'child_process';
|
||||
import { join } from 'path';
|
||||
import { copySync, moveSync, readdirSync, removeSync } from 'fs-extra';
|
||||
|
||||
import { fileExists, readJsonFile } from 'nx/src/utils/fileutils';
|
||||
import { output } from 'nx/src/utils/output';
|
||||
import {
|
||||
detectPackageManager,
|
||||
getPackageManagerCommand,
|
||||
PackageManagerCommands,
|
||||
} from 'nx/src/utils/package-manager';
|
||||
|
||||
import { checkForUncommittedChanges } from './check-for-uncommitted-changes';
|
||||
import { setupE2eProject } from './setup-e2e-project';
|
||||
import { readNameFromPackageJson } from './read-name-from-package-json';
|
||||
import { setupTsConfig } from './tsconfig-setup';
|
||||
import { writeCracoConfig } from './write-craco-config';
|
||||
import { cleanUpFiles } from './clean-up-files';
|
||||
import { writeViteConfig } from './write-vite-config';
|
||||
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;
|
||||
isStandalone: 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] });
|
||||
}
|
||||
|
||||
function removeDependencies(pmc: PackageManagerCommands, ...deps: string[]) {
|
||||
const depsArg = deps.join(' ');
|
||||
output.log({ title: `📦 Removing dependencies: ${depsArg}` });
|
||||
execSync(`${pmc.rm} ${depsArg}`, { stdio: [0, 1, 2] });
|
||||
}
|
||||
|
||||
export function normalizeOptions(options: Options): NormalizedOptions {
|
||||
const packageManager = detectPackageManager();
|
||||
const pmc = getPackageManagerCommand(packageManager);
|
||||
|
||||
const appIsJs = !fileExists(`tsconfig.json`);
|
||||
|
||||
const reactAppName = readNameFromPackageJson();
|
||||
const packageJson = readJsonFile('package.json');
|
||||
const deps = {
|
||||
...packageJson.dependencies,
|
||||
...packageJson.devDependencies,
|
||||
};
|
||||
const isCRA5 = /^[^~]?5/.test(deps['react-scripts']);
|
||||
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;
|
||||
const isStandalone = !options.integrated;
|
||||
|
||||
return {
|
||||
...options,
|
||||
packageManager,
|
||||
pmc,
|
||||
appIsJs,
|
||||
reactAppName,
|
||||
isCRA5,
|
||||
npxYesFlagNeeded,
|
||||
isVite,
|
||||
isStandalone,
|
||||
};
|
||||
}
|
||||
|
||||
export async function createNxWorkspaceForReact(options: Record<string, any>) {
|
||||
if (!options.force) {
|
||||
checkForUncommittedChanges();
|
||||
checkForCustomWebpackSetup();
|
||||
}
|
||||
|
||||
output.log({ title: '✨ Nx initialization' });
|
||||
|
||||
const normalizedOptions = normalizeOptions(options as Options);
|
||||
await reorgnizeWorkspaceStructure(normalizedOptions);
|
||||
}
|
||||
|
||||
async function reorgnizeWorkspaceStructure(options: NormalizedOptions) {
|
||||
createTempWorkspace(options);
|
||||
|
||||
moveFilesToTempWorkspace(options);
|
||||
|
||||
await addBundler(options);
|
||||
|
||||
output.log({ title: '🧶 Updating .gitignore file' });
|
||||
|
||||
execSync(`echo "node_modules" >> .gitignore`, { stdio: [0, 1, 2] });
|
||||
execSync(`echo "dist" >> .gitignore`, { stdio: [0, 1, 2] });
|
||||
|
||||
process.chdir('..');
|
||||
|
||||
copyFromTempWorkspaceToRoot();
|
||||
|
||||
cleanUpUnusedFilesAndAddConfigFiles(options);
|
||||
|
||||
output.log({ title: '🙂 Please be patient, one final step remaining!' });
|
||||
|
||||
output.log({
|
||||
title: '🧶 Adding npm packages to your new Nx workspace',
|
||||
});
|
||||
|
||||
addDependencies(
|
||||
options.pmc,
|
||||
'@testing-library/jest-dom',
|
||||
'eslint-config-react-app',
|
||||
'web-vitals',
|
||||
'jest-watch-typeahead'
|
||||
);
|
||||
|
||||
if (options.isVite) {
|
||||
addDependencies(options.pmc, 'vite', 'vitest', '@vitejs/plugin-react');
|
||||
} else {
|
||||
addDependencies(
|
||||
options.pmc,
|
||||
'@craco/craco',
|
||||
'cross-env',
|
||||
'react-scripts',
|
||||
'tsconfig-paths-webpack-plugin'
|
||||
);
|
||||
}
|
||||
|
||||
output.log({ title: '🎉 Done!' });
|
||||
output.note({
|
||||
title: 'First time using Nx? Check out this interactive Nx tutorial.',
|
||||
bodyLines: [
|
||||
`https://nx.dev/react-tutorial/1-code-generation`,
|
||||
` `,
|
||||
`Prefer watching videos? Check out this free Nx course on Egghead.io.`,
|
||||
`https://egghead.io/playlists/scale-react-development-with-nx-4038`,
|
||||
],
|
||||
});
|
||||
|
||||
if (options.isVite) {
|
||||
const indexPath = options.isStandalone
|
||||
? 'index.html'
|
||||
: join('apps', options.reactAppName, 'index.html');
|
||||
const oldIndexPath = options.isStandalone
|
||||
? join('public', 'index.html')
|
||||
: join('apps', options.reactAppName, 'public', 'index.html');
|
||||
output.note({
|
||||
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: [
|
||||
options.integrated ? `npx nx serve ${options.reactAppName}` : 'npm start',
|
||||
options.integrated
|
||||
? `npx nx build ${options.reactAppName}`
|
||||
: 'npm run build',
|
||||
options.integrated ? `npx nx test ${options.reactAppName}` : `npm test`,
|
||||
` `,
|
||||
`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-monorepo --style=css --bundler=${
|
||||
options.isVite ? 'vite' : 'webpack'
|
||||
} --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(
|
||||
join('temp-workspace', 'apps', options.reactAppName, 'project.json'),
|
||||
'project.json'
|
||||
);
|
||||
removeSync(join('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.isStandalone ? 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.isStandalone
|
||||
? join('temp-workspace', f)
|
||||
: join('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.isStandalone);
|
||||
writeViteConfig(
|
||||
options.reactAppName,
|
||||
options.isStandalone,
|
||||
options.appIsJs
|
||||
);
|
||||
writeViteIndexHtml(
|
||||
options.reactAppName,
|
||||
options.isStandalone,
|
||||
options.appIsJs
|
||||
);
|
||||
renameJsToJsx(options.reactAppName, options.isStandalone);
|
||||
} else {
|
||||
output.log({ title: '🧑🔧 Setting up craco + Webpack' });
|
||||
const { addCracoCommandsToPackageScripts } = await import(
|
||||
'./add-craco-commands-to-package-scripts'
|
||||
);
|
||||
addCracoCommandsToPackageScripts(
|
||||
options.reactAppName,
|
||||
options.isStandalone
|
||||
);
|
||||
|
||||
writeCracoConfig(
|
||||
options.reactAppName,
|
||||
options.isCRA5,
|
||||
options.isStandalone
|
||||
);
|
||||
|
||||
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(join('temp-workspace', f), f, { overwrite: true });
|
||||
});
|
||||
}
|
||||
|
||||
function cleanUpUnusedFilesAndAddConfigFiles(options: NormalizedOptions) {
|
||||
output.log({ title: '🧹 Cleaning up.' });
|
||||
|
||||
cleanUpFiles(options.reactAppName, options.isStandalone);
|
||||
|
||||
output.log({ title: "📃 Extend the app's tsconfig.json from the base" });
|
||||
|
||||
setupTsConfig(options.reactAppName, options.isStandalone);
|
||||
|
||||
if (options.e2e && !options.isStandalone) {
|
||||
output.log({ title: '📃 Setup e2e tests' });
|
||||
setupE2eProject(options.reactAppName);
|
||||
} else {
|
||||
removeSync(join('apps', `${options.reactAppName}-e2e`));
|
||||
execSync(`${options.pmc.rm} @nrwl/cypress eslint-plugin-cypress`);
|
||||
}
|
||||
|
||||
if (options.isStandalone) {
|
||||
removeSync('apps');
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
import { fileExists, readJsonFile } from 'nx/src/utils/fileutils';
|
||||
|
||||
export function readNameFromPackageJson(): string {
|
||||
let appName = 'webapp';
|
||||
if (fileExists('package.json')) {
|
||||
const json = readJsonFile('package.json');
|
||||
if (
|
||||
json['name'] &&
|
||||
json['name'].length &&
|
||||
json['name'].replace(/\s/g, '').length
|
||||
) {
|
||||
appName = json['name'].replace(/\s/g, '');
|
||||
}
|
||||
}
|
||||
return appName;
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
import { sync } from 'glob';
|
||||
import { renameSync, readFileSync } from 'fs-extra';
|
||||
|
||||
// Vite cannot process JSX like <div> or <Header> unless the file is named .jsx or .tsx
|
||||
export function renameJsToJsx(appName: string, isStandalone: boolean) {
|
||||
const files = sync(
|
||||
isStandalone ? 'src/**/*.js' : `apps/${appName}/src/**/*.js`
|
||||
);
|
||||
|
||||
files.forEach((file) => {
|
||||
const content = readFileSync(file).toString();
|
||||
// Try to detect JSX before renaming to .jsx
|
||||
// Files like setupTests.js from CRA should not be renamed
|
||||
if (/<[a-zA-Z0-9]+/.test(content)) {
|
||||
renameSync(file, `${file}x`);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
import {
|
||||
fileExists,
|
||||
readJsonFile,
|
||||
writeJsonFile,
|
||||
} from 'nx/src/utils/fileutils';
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
export function setupE2eProject(appName: string) {
|
||||
const json = readJsonFile(`apps/${appName}-e2e/project.json`);
|
||||
json.targets.e2e = {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
commands: [`nx e2e-serve ${appName}-e2e`, `nx e2e-run ${appName}-e2e`],
|
||||
},
|
||||
};
|
||||
json.targets['e2e-run'] = {
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
cypressConfig: `apps/${appName}-e2e/cypress.json`,
|
||||
tsConfig: `apps/${appName}-e2e/tsconfig.e2e.json`,
|
||||
baseUrl: 'http://localhost:3000',
|
||||
},
|
||||
};
|
||||
json.targets['e2e-serve'] = {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
commands: [`nx serve ${appName}`],
|
||||
readyWhen: 'can now view',
|
||||
},
|
||||
};
|
||||
writeJsonFile(`apps/${appName}-e2e/project.json`, json);
|
||||
|
||||
if (fileExists(`apps/${appName}-e2e/src/integration/app.spec.ts`)) {
|
||||
const integrationE2eTest = `
|
||||
describe('${appName}', () => {
|
||||
beforeEach(() => cy.visit('/'));
|
||||
it('should contain a body', () => {
|
||||
cy.get('body').should('exist');
|
||||
});
|
||||
});`;
|
||||
writeFileSync(
|
||||
`apps/${appName}-e2e/src/integration/app.spec.ts`,
|
||||
integrationE2eTest
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
import {
|
||||
fileExists,
|
||||
readJsonFile,
|
||||
writeJsonFile,
|
||||
} from 'nx/src/utils/fileutils';
|
||||
import { join } from 'path';
|
||||
|
||||
const defaultTsConfig = (relativePathToRoot: string) => ({
|
||||
extends: relativePathToRoot
|
||||
? join(relativePathToRoot, 'tsconfig.base.json')
|
||||
: './tsconfig.base.json',
|
||||
compilerOptions: {
|
||||
jsx: 'react',
|
||||
allowJs: true,
|
||||
esModuleInterop: true,
|
||||
allowSyntheticDefaultImports: true,
|
||||
},
|
||||
files: [],
|
||||
include: [],
|
||||
references: [
|
||||
{
|
||||
path: './tsconfig.app.json',
|
||||
},
|
||||
{
|
||||
path: './tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const defaultTsConfigApp = (relativePathToRoot: string) => ({
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: join(relativePathToRoot, 'dist/out-tsc'),
|
||||
types: ['node'],
|
||||
},
|
||||
files: [
|
||||
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 = (relativePathToRoot: string) => ({
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: join(relativePathToRoot, 'dist/out-tsc'),
|
||||
module: 'commonjs',
|
||||
types: ['jest', 'node'],
|
||||
},
|
||||
include: [
|
||||
'**/*.spec.ts',
|
||||
'**/*.spec.tsx',
|
||||
'**/*.spec.js',
|
||||
'**/*.spec.jsx',
|
||||
'**/*.d.ts',
|
||||
],
|
||||
files: [
|
||||
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, isStandalone: boolean) {
|
||||
const tsconfigPath = isStandalone
|
||||
? 'tsconfig.json'
|
||||
: `apps/${appName}/tsconfig.json`;
|
||||
const tsconfigAppPath = isStandalone
|
||||
? 'tsconfig.app.json'
|
||||
: `apps/${appName}/tsconfig.app.json`;
|
||||
const tsconfiSpecPath = isStandalone
|
||||
? 'tsconfig.spec.json'
|
||||
: `apps/${appName}/tsconfig.spec.json`;
|
||||
const tsconfigBasePath = isStandalone
|
||||
? './tsconfig.base.json'
|
||||
: '../../tsconfig.base.json';
|
||||
const relativePathToRoot = isStandalone ? '' : '../../';
|
||||
if (fileExists(tsconfigPath)) {
|
||||
const json = readJsonFile(tsconfigPath);
|
||||
json.extends = tsconfigBasePath;
|
||||
if (json.compilerOptions) {
|
||||
json.compilerOptions.jsx = 'react';
|
||||
} else {
|
||||
json.compilerOptions = {
|
||||
jsx: 'react',
|
||||
allowJs: true,
|
||||
esModuleInterop: true,
|
||||
allowSyntheticDefaultImports: true,
|
||||
};
|
||||
}
|
||||
writeJsonFile(tsconfigPath, json);
|
||||
} else {
|
||||
writeJsonFile(tsconfigPath, defaultTsConfig(relativePathToRoot));
|
||||
}
|
||||
|
||||
if (fileExists(tsconfigAppPath)) {
|
||||
const json = readJsonFile(tsconfigAppPath);
|
||||
json.extends = './tsconfig.json';
|
||||
writeJsonFile(tsconfigAppPath, json);
|
||||
} else {
|
||||
writeJsonFile(tsconfigAppPath, defaultTsConfigApp(relativePathToRoot));
|
||||
}
|
||||
|
||||
if (fileExists(tsconfiSpecPath)) {
|
||||
const json = readJsonFile(tsconfiSpecPath);
|
||||
json.extends = './tsconfig.json';
|
||||
writeJsonFile(tsconfiSpecPath, json);
|
||||
} else {
|
||||
writeJsonFile(tsconfiSpecPath, defaultTsConfigSpec(relativePathToRoot));
|
||||
}
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
export function writeCracoConfig(
|
||||
appName: string,
|
||||
isCRA5: boolean,
|
||||
isStandalone: boolean
|
||||
) {
|
||||
const configOverride = `
|
||||
const path = require('path');
|
||||
const TsConfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
module.exports = {
|
||||
webpack: {
|
||||
configure: (config) => {
|
||||
// Remove guard against importing modules outside of \`src\`.
|
||||
// Needed for workspace projects.
|
||||
config.resolve.plugins = config.resolve.plugins.filter(
|
||||
(plugin) => !(plugin instanceof ModuleScopePlugin)
|
||||
);
|
||||
// Add support for importing workspace projects.
|
||||
config.resolve.plugins.push(
|
||||
new TsConfigPathsPlugin({
|
||||
configFile: path.resolve(__dirname, 'tsconfig.json'),
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
||||
mainFields: ['browser', 'module', 'main'],
|
||||
})
|
||||
);
|
||||
${
|
||||
isCRA5
|
||||
? `
|
||||
// Replace include option for babel loader with exclude
|
||||
// so babel will handle workspace projects as well.
|
||||
config.module.rules[1].oneOf.forEach((r) => {
|
||||
if (r.loader && r.loader.indexOf('babel') !== -1) {
|
||||
r.exclude = /node_modules/;
|
||||
delete r.include;
|
||||
}
|
||||
});`
|
||||
: `
|
||||
// Replace include option for babel loader with exclude
|
||||
// so babel will handle workspace projects as well.
|
||||
config.module.rules.forEach((r) => {
|
||||
if (r.oneOf) {
|
||||
const babelLoader = r.oneOf.find(
|
||||
(rr) => rr.loader.indexOf('babel-loader') !== -1
|
||||
);
|
||||
babelLoader.exclude = /node_modules/;
|
||||
delete babelLoader.include;
|
||||
}
|
||||
});
|
||||
`
|
||||
}
|
||||
return config;
|
||||
},
|
||||
},
|
||||
jest: {
|
||||
configure: (config) => {
|
||||
config.resolver = '@nrwl/jest/plugins/resolver';
|
||||
return config;
|
||||
},
|
||||
},
|
||||
};
|
||||
`;
|
||||
writeFileSync(
|
||||
isStandalone ? 'craco.config.js' : `apps/${appName}/craco.config.js`,
|
||||
configOverride
|
||||
);
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
import * as fs from 'fs';
|
||||
|
||||
export function writeViteConfig(
|
||||
appName: string,
|
||||
isStandalone: boolean,
|
||||
isJs: boolean
|
||||
) {
|
||||
let port = 4200;
|
||||
|
||||
// Use PORT from .env file if it exists in project.
|
||||
if (fs.existsSync(`../.env`)) {
|
||||
const envFile = fs.readFileSync(`../.env`).toString();
|
||||
const result = envFile.match(/\bport=(?<port>\d{4})/i);
|
||||
const portCandidate = Number(result?.groups?.port);
|
||||
if (!isNaN(portCandidate)) {
|
||||
port = portCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
isStandalone ? '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: ${
|
||||
isStandalone ? `'./dist/${appName}'` : `'../../dist/apps/${appName}'`
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: ${port},
|
||||
open: true,
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
setupFiles: 'src/setupTests.${isJs ? 'js' : 'ts'}',
|
||||
css: true,
|
||||
},
|
||||
plugins: [react()],
|
||||
});
|
||||
`
|
||||
);
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
import * as fs from 'fs';
|
||||
|
||||
export function writeViteIndexHtml(
|
||||
appName: string,
|
||||
isStandalone: boolean,
|
||||
isJs: boolean
|
||||
) {
|
||||
const indexPath = isStandalone ? '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(
|
||||
indexPath,
|
||||
`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + Nx</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="${indexFile}"></script>
|
||||
</body>
|
||||
</html>`
|
||||
);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user