create-yard-app
Version:
CLI tool to create indepedent ops portal apps
176 lines (157 loc) • 5.8 kB
JavaScript
import validateNPMPackage from 'validate-npm-package-name'
import chalk from 'chalk'
import commander from 'commander'
import fsExtras from 'fs-extra'
import dns from 'dns'
import path from 'path'
import spawn from 'cross-spawn'
import packageJSON from '../package.json'
function validateAppName(appName) {
const validationResult = validateNPMPackage(appName)
if (!validationResult.validForNewPackages) {
console.error(`Could not create a App called ${chalk.red(appName)} because of npm naming restrictions`)
console.error(validationResult.errors)
console.warn(validationResult.warnings)
process.exit(1)
}
}
function checkIfOnline() {
return new Promise((resolve) => {
dns.resolve('registry.yarnpkg.com', (err) => {
resolve(err === null)
})
})
}
function isSafeToCreateProjectIn(root, name) {
const validFiles = ['.DS_Store', 'Thumbs.db', '.git', '.gitignore', '.idea', 'README.md', 'LICENSE', 'web.iml']
const isSafe = fsExtras.readdirSync(root).every(function(file) {
return validFiles.indexOf(file) >= 0
})
if (!isSafe) {
console.log('The directory ' + chalk.green(name) + ' contains files that could conflict.')
console.log('Try using a new directory name.')
process.exit(1)
}
}
function install(depedencies = [], isOnline) {
return new Promise((resolve, reject) => {
const command = 'yarnpkg'
const args = ['add', '--exact'].concat(depedencies)
if (!isOnline) {
console.log(chalk.yellow('You appear to be offline'))
console.log(chalk.yellow('Falling back to yarn local cache'))
console.log()
}
spawn(command, args, { stdio: 'inherit' }).on('close', (code) => {
if (code !== 0) {
reject({
command: `${command} ${args.join(' ')}`,
})
return
}
resolve()
})
})
}
function writeBasicPackageJSON(name, root) {
const packageJSONContents = {
name,
version: '0.1.0',
private: true,
}
fsExtras.writeFileSync(path.join(root, 'package.json'), JSON.stringify(packageJSONContents, null, 2))
}
function initProject(root, packageName, name) {
const scriptsPath = path.resolve(root, 'node_modules', packageName, 'dist', 'scripts', 'init.js')
const init = require(scriptsPath).default
init(root, name)
}
function fixDependencies(packageName) {
const packagePath = path.join(process.cwd(), 'package.json')
const packageJson = require(packagePath)
if (typeof packageJson.dependencies === 'undefined') {
console.error(chalk.red('Missing dependencies in package.json'))
process.exit(1)
}
const packageVersion = packageJson.dependencies[packageName]
if (typeof packageVersion === 'undefined') {
console.error(chalk.red('Unable to find ' + packageName + ' in package.json'))
process.exit(1)
}
packageJson.devDependencies = packageJson.devDependencies || {}
packageJson.devDependencies[packageName] = packageVersion
delete packageJson.dependencies[packageName]
fsExtras.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2))
}
function installYardScripts(root, name) {
const packageToBeInstalled = 'g2-ops-scripts'
checkIfOnline()
.then((isOnline) => {
// Online
console.log(`Installing ${chalk.cyan(packageToBeInstalled)} ...`)
return install([packageToBeInstalled], isOnline)
})
.then(() => {
fixDependencies(packageToBeInstalled)
initProject(root, packageToBeInstalled)
})
.catch((reason) => {
console.log()
console.log('Aborting installation.')
if (reason.command) {
console.log(' ' + chalk.cyan(reason.command), 'has failed.')
} else {
console.log(chalk.red('Unexpected error. Please report it as a bug:'))
console.log(reason)
}
console.log()
// On 'exit' we will delete these files from target directory.
const knownGeneratedFiles = ['package.json', 'npm-debug.log', 'yarn-error.log', 'yarn-debug.log', 'node_modules']
const currentFiles = fsExtras.readdirSync(path.join(root))
currentFiles.forEach(function(file) {
knownGeneratedFiles.forEach(function(fileToMatch) {
// This will catch `(npm-debug|yarn-error|yarn-debug).log*` files
// and the rest of knownGeneratedFiles.
if ((fileToMatch.match(/.log/g) && file.indexOf(fileToMatch) === 0) || file === fileToMatch) {
console.log('Deleting generated file...', chalk.cyan(file))
fsExtras.removeSync(path.join(root, file))
}
})
})
const remainingFiles = fsExtras.readdirSync(path.join(root))
if (!remainingFiles.length) {
// Delete target folder if empty
console.log('Deleting', chalk.cyan(appName + '/'), 'from', chalk.cyan(path.resolve(root, '..')))
process.chdir(path.resolve(root, '..'))
fsExtras.removeSync(path.join(root))
}
console.log('Done.')
process.exit(1)
})
}
function createProject(name) {
validateAppName(name)
const root = path.resolve(name)
const appName = path.basename(root)
// Here we have valid app name
fsExtras.ensureDirSync(name) // Create app directory if you don't have a directory
isSafeToCreateProjectIn(root, name)
// it is safe to create project
writeBasicPackageJSON(name, root)
process.chdir(root)
installYardScripts(root, name)
// TODO: Copy Template to the destination
// TODO: Install all depedencies declared in template
}
let appName = ''
// Reading in Command Line arguments
new commander.Command(packageJSON.name)
.version(packageJSON.version)
.arguments('<project-directory>')
.usage(`${chalk.green('<project-directory>')} [options]`)
.action((name) => {
appName = name
})
.parse(process.argv)
createProject(appName)