UNPKG

@sabertazimi/react-scripts

Version:

Configuration and scripts for Bod CLI.

413 lines (364 loc) 11.3 kB
// @remove-file-on-eject /** * Copyright (c) 2015-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /* eslint-disable no-console */ // Makes the script crash on unhandled rejections instead of silently // ignoring them. In the future, promise rejections that are not handled will // terminate the Node.js process with a non-zero exit code. const process = require('node:process') process.on('unhandledRejection', (err) => { throw err }) const path = require('node:path') const execSync = require('node:child_process').execSync const os = require('node:os') const fs = require('fs-extra') const chalk = require('react-dev-utils/chalk') const spawn = require('react-dev-utils/crossSpawn') const { defaultBrowsers } = require('react-dev-utils/browsersHelper') const verifyTypeScriptSetup = require('./utils/verifyTypeScriptSetup') function isInGitRepository() { try { execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }) return true } catch (e) { return false } } function isInMercurialRepository() { try { execSync('hg --cwd . root', { stdio: 'ignore' }) return true } catch (e) { return false } } function tryGitInit() { try { execSync('git --version', { stdio: 'ignore' }) if (isInGitRepository() || isInMercurialRepository()) return false execSync('git init', { stdio: 'ignore' }) return true } catch (e) { console.warn('Git repo not initialized', e) return false } } module.exports = function ( appPath, appName, verbose, originalDirectory, templateName, ) { const appPackage = require(path.join(appPath, 'package.json')) const useYarn = fs.existsSync(path.join(appPath, 'yarn.lock')) if (!templateName) { console.log('') console.error( `A template was not provided. This is likely because you're using an outdated version of ${chalk.cyan( 'create-react-app', )}.`, ) console.error( `Please note that global installs of ${chalk.cyan( 'create-react-app', )} are no longer supported.`, ) console.error( `You can fix this by running ${chalk.cyan( 'npm uninstall -g create-react-app', )} or ${chalk.cyan( 'yarn global remove create-react-app', )} before using ${chalk.cyan('create-react-app')} again.`, ) return } const templatePath = path.dirname( require.resolve(`${templateName}/package.json`, { paths: [appPath] }), ) const templateJsonPath = path.join(templatePath, 'template.json') let templateJson = {} if (fs.existsSync(templateJsonPath)) templateJson = require(templateJsonPath) const templatePackage = templateJson.package || {} // This was deprecated in CRA v5. if (templateJson.dependencies || templateJson.scripts) { console.log() console.log( chalk.red( 'Root-level `dependencies` and `scripts` keys in `template.json` were deprecated for Create React App 5.\n' + 'This template needs to be updated to use the new `package` key.', ), ) console.log('For more information, visit https://cra.link/templates') } // Keys to ignore in templatePackage const templatePackageBlacklist = [ 'name', 'version', 'description', 'keywords', 'bugs', 'license', 'author', 'contributors', 'files', 'browser', 'bin', 'man', 'directories', 'repository', 'peerDependencies', 'bundledDependencies', 'optionalDependencies', 'engineStrict', 'os', 'cpu', 'preferGlobal', 'private', 'publishConfig', ] // Keys from templatePackage that will be merged with appPackage const templatePackageToMerge = ['dependencies', 'scripts'] // Keys from templatePackage that will be added to appPackage, // replacing any existing entries. const templatePackageToReplace = Object.keys(templatePackage).filter((key) => { return ( !templatePackageBlacklist.includes(key) && !templatePackageToMerge.includes(key) ) }) // Copy over some of the devDependencies appPackage.dependencies = appPackage.dependencies || {} // Setup the script rules const templateScripts = templatePackage.scripts || {} appPackage.scripts = Object.assign( { 'build': 'react-scripts build', 'build:profile': 'react-scripts build --profile', 'start': 'react-scripts start', 'start:https': 'HTTPS=true react-scripts start', 'test': 'react-scripts test', 'test:coverage': 'react-scripts test --coverage', 'test:debug': 'react-scripts --inspect-brk test --runInBand --no-cache', 'test:node': 'react-scripts test --env=node', }, templateScripts, ) // Update scripts for Yarn users if (useYarn) { appPackage.scripts = Object.entries(appPackage.scripts).reduce( (acc, [key, value]) => ({ ...acc, [key]: value.replace(/(npm run |npm )/, 'yarn '), }), {}, ) } // Setup the stylelint config appPackage.stylelint = { extends: ['stylelint-config-bod'], plugins: ['stylelint-prettier'], rules: { 'prettier/prettier': true, }, } // Setup the prettier config appPackage.prettier = { arrowParens: 'avoid', printWidth: 80, semi: true, singleQuote: true, tabWidth: 2, trailingComma: 'es5', } // Setup the browsers list appPackage.browserslist = defaultBrowsers // Add templatePackage keys/values to appPackage, replacing existing entries templatePackageToReplace.forEach((key) => { appPackage[key] = templatePackage[key] }) fs.writeFileSync( path.join(appPath, 'package.json'), JSON.stringify(appPackage, null, 2) + os.EOL, ) const readmeExists = fs.existsSync(path.join(appPath, 'README.md')) if (readmeExists) { fs.renameSync( path.join(appPath, 'README.md'), path.join(appPath, 'README.old.md'), ) } // Copy the files for the user const templateDir = path.join(templatePath, 'template') if (fs.existsSync(templateDir)) { fs.copySync(templateDir, appPath) } else { console.error( `Could not locate supplied template: ${chalk.green(templateDir)}`, ) return } // modifies README.md commands based on user used package manager. if (useYarn) { try { const readme = fs.readFileSync(path.join(appPath, 'README.md'), 'utf8') fs.writeFileSync( path.join(appPath, 'README.md'), readme.replace(/(npm run |npm )/g, 'yarn '), 'utf8', ) } catch (err) { // Silencing the error. As it fall backs to using default npm commands. } } const gitignoreExists = fs.existsSync(path.join(appPath, '.gitignore')) if (gitignoreExists) { // Append if there's already a `.gitignore` file there const data = fs.readFileSync(path.join(appPath, 'gitignore')) fs.appendFileSync(path.join(appPath, '.gitignore'), data) fs.unlinkSync(path.join(appPath, 'gitignore')) } else { // Rename gitignore after the fact to prevent npm from renaming it to .npmignore // See: https://github.com/npm/npm/issues/1862 fs.moveSync( path.join(appPath, 'gitignore'), path.join(appPath, '.gitignore'), [], ) } // Initialize git repo let initializedGit = false if (tryGitInit()) { initializedGit = true console.log() console.log('Initialized a git repository.') } let command let remove let args if (useYarn) { command = 'yarnpkg' remove = 'remove' args = ['add'] } else { command = 'npm' remove = 'uninstall' args = [ 'install', '--no-audit', // https://github.com/facebook/create-react-app/issues/11174 '--save', verbose && '--verbose', ].filter(e => e) } // Install additional template dependencies, if present. const dependenciesToInstall = Object.entries({ ...templatePackage.dependencies, ...templatePackage.devDependencies, }) if (dependenciesToInstall.length) { args = args.concat( dependenciesToInstall.map(([dependency, version]) => { return `${dependency}@${version}` }), ) } // Install react and react-dom for backward compatibility with old CRA cli // which doesn't install react and react-dom along with react-scripts if (!isReactInstalled(appPackage)) args = args.concat(['react', 'react-dom']) // Install template dependencies, and react and react-dom if missing. if ((!isReactInstalled(appPackage) || templateName) && args.length > 1) { console.log() console.log(`Installing template dependencies using ${command}...`) const proc = spawn.sync(command, args, { stdio: 'inherit' }) if (proc.status !== 0) { console.error(`\`${command} ${args.join(' ')}\` failed`) return } } if (args.find(arg => arg.includes('typescript'))) { console.log() verifyTypeScriptSetup() } // Remove template console.log(`Removing template package using ${command}...`) console.log() const proc = spawn.sync(command, [remove, templateName], { stdio: 'inherit', }) if (proc.status !== 0) { console.error(`\`${command} ${args.join(' ')}\` failed`) return } // Create git commit if git repo was initialized if (initializedGit) { console.log() console.log('Please create your first git commit.') } // Display the most elegant way to cd. // This needs to handle an undefined originalDirectory for // backward compatibility with old global-cli's. let cdpath if (originalDirectory && path.join(originalDirectory, appName) === appPath) cdpath = appName else cdpath = appPath // Change displayed command to yarn instead of yarnpkg const displayedCommand = useYarn ? 'yarn' : 'npm' console.log() console.log(`Success! Created ${appName} at ${appPath}`) console.log('Inside that directory, you can run several commands:') console.log() console.log(chalk.cyan(` ${displayedCommand} start`)) console.log(' Starts the development server.') console.log() console.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}build`)) console.log(' Bundles the app into static files for production.') console.log() console.log(chalk.cyan(` ${displayedCommand} test`)) console.log(' Starts the test runner.') console.log() console.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}eject`)) console.log( ' Removes this tool and copies build dependencies, configuration files', ) console.log( ' and scripts into the app directory. If you do this, you can’t go back!', ) console.log() console.log('We suggest that you begin by typing:') console.log() console.log(chalk.cyan(' cd'), cdpath) console.log(` ${chalk.cyan(`${displayedCommand} start`)}`) if (readmeExists) { console.log() console.log( chalk.yellow( 'You had a `README.md` file, we renamed it to `README.old.md`', ), ) } console.log() console.log('Happy hacking!') } function isReactInstalled(appPackage) { const dependencies = appPackage.dependencies || {} return ( typeof dependencies.react !== 'undefined' && typeof dependencies['react-dom'] !== 'undefined' ) }