@edenjs/cli
Version:
Web Application Framework built on Express.js, Redis and RiotJS
290 lines (234 loc) • 11.5 kB
JavaScript
const fs = require('fs-extra');
const path = require('path');
const glob = require('@edenjs/glob');
const commonBadFiles = [
'.npmignore',
'yarn.lock',
'npm-debug.log',
'yarn-error.log',
'.idea',
'.remote-sync.json',
'.editorconfig',
'.eslintrc.json',
'.htaccess',
];
async function edenExistsIn(dir) {
try {
if (await fs.pathExists(path.join(dir, 'app', 'bundles'))) {
// Obvious and trustworthy
return 'oldApp';
}
const configPath = path.join(dir, 'edenconfig.js');
const oldAppConfigPath = path.join(dir, 'edenappconfig.js');
const appConfigPath = path.join(dir, 'config.js');
const configExists = await fs.pathExists(configPath);
// eslint-disable-next-line max-len
if (await fs.pathExists(oldAppConfigPath) || (configExists && await fs.pathExists(appConfigPath))) {
// Obvious and trustworthy
return 'app';
}
const legacyConfigPath = path.join(dir, 'config.js');
if (await fs.pathExists(legacyConfigPath)) {
// eslint-disable-next-line global-require, import/no-dynamic-require
const configFile = require(legacyConfigPath);
// Make sure its edenjs-like first
if (typeof configFile === 'object' && typeof configFile.domain === 'string' && typeof configFile.version === 'string') {
// Pretty safe
return 'app';
}
}
const rootJsPaths = await glob('./*.js');
if (await fs.pathExists(path.join(dir, 'bundles'))) {
if (rootJsPaths.length === 0) {
// This is a bit of a gamble
return 'module';
} if (rootJsPaths.length === 1 && rootJsPaths[0] === 'index.js') {
const indexText = await fs.readFile(path.join(dir, 'index.js'), 'utf8');
if (indexText.match(/^\/\/ EdenJS module does not require an index$/m)) {
// Obvious and trustworthy
return 'module';
}
}
}
// Look files in the edenjs structure TODO maybe look for multiple different types together
if ((await glob('./*/{controllers,models,views,daemons,helpers}/*.js')).length >= 3) {
// Pretty safe
return 'oldAppBundles';
}
if (configExists) {
// Remember, apps can have this too, thats why this one is last
return 'module';
}
// Look to see if there is anything javascript-like at all
// obviously this must be very very last
if ((await glob('./**/{node_modules,package.json,package-lock.json,*.js}')).length >= 3) {
return 'none';
}
} catch (err) { /* */ }
// We don't know what it is, lets not add a package.json to some random C++ project
return 'danger';
}
async function initEden(suppliedDirType = null, migrateGit = false) {
let dirType = await edenExistsIn(process.cwd());
// If input for supplied dir type is empty and directory might be dangerous, throw an error
if (dirType === 'danger' && suppliedDirType === null) {
throw new Error('Directory type was detected as potentially dangerous to modify. Please manually specify a type if you are sure');
}
// Lets make things simpler now
const inputDirType = suppliedDirType === 'none' ? null : suppliedDirType;
const shouldMakeIntoApp = (inputDirType === null && (dirType === 'app' || dirType === 'oldApp' || dirType === 'oldAppBundles')) || inputDirType === 'app';
const shouldMakeIntoModule = (inputDirType === null && dirType === 'module') || inputDirType === 'module';
if (inputDirType === 'module' && shouldMakeIntoApp) {
// This should be roughly ok, given apps are just more on top of a module
}
if (inputDirType === 'app' && shouldMakeIntoModule) {
throw new Error('Turning apps into modules is currently unsupported');
}
// oldApp => app
if (dirType === 'oldApp' && shouldMakeIntoApp) {
// Move config and bundles to correct place
await fs.move(path.join(process.cwd(), 'app', 'bundles'), path.join(process.cwd(), 'bundles'));
if (await fs.pathExists(path.join(process.cwd(), 'app', 'config.js'))) {
await fs.move(path.join(process.cwd(), 'app', 'config.js'), path.join(process.cwd(), 'config.js'));
}
// Remove edenjs install and junk
await Promise.all([
fs.remove(path.join(process.cwd(), 'CONTRIBUTING.md')),
fs.remove(path.join(process.cwd(), 'LICENSE')),
fs.remove(path.join(process.cwd(), 'README.md')),
fs.remove(path.join(process.cwd(), 'TUTORIAL.md')),
fs.remove(path.join(process.cwd(), 'app.js')),
fs.remove(path.join(process.cwd(), 'gulpfile.js')),
fs.remove(path.join(process.cwd(), 'lib')),
fs.remove(path.join(process.cwd(), 'tests')),
fs.remove(path.join(process.cwd(), 'package.json')),
fs.remove(path.join(process.cwd(), 'package-lock.json')),
fs.remove(path.join(process.cwd(), 'www')),
fs.remove(path.join(process.cwd(), 'app')),
fs.remove(path.join(process.cwd(), '.git')),
fs.remove(path.join(process.cwd(), '.gitignore')),
fs.remove(path.join(process.cwd(), '.gitattributes')),
fs.remove(path.join(process.cwd(), 'node_modules')),
...commonBadFiles.map(file => fs.remove(path.join(process.cwd(), file))),
]);
dirType = 'app';
}
// oldAppBundles => app
if (dirType === 'oldAppBundles' && shouldMakeIntoApp) {
const paths = await glob(['./*'], {
nodir : false,
});
await fs.ensureDir(path.join(process.cwd(), 'bundles'));
for (const p of paths) {
await fs.move(path.join(process.cwd(), p), path.join(process.cwd(), 'bundles', p));
}
dirType = 'app';
}
// Remove common junk
await Promise.all(commonBadFiles.map(file => fs.remove(path.join(process.cwd(), file))));
// Clean up directories which should be apps
if (shouldMakeIntoApp) {
// Clean the data directory
await fs.ensureDir(path.join(process.cwd(), 'data'));
// Move old config location to new location
if (await fs.pathExists(path.join(process.cwd(), 'edenappconfig.js'))) {
await fs.move(path.join(process.cwd(), 'edenappconfig.js'), path.join(process.cwd(), 'config.js'));
}
// Put default app config into apps without config
if (!(await fs.pathExists(path.join(process.cwd(), 'config.js')))) {
await fs.copy(path.join(__dirname, '..', '..', 'template', 'appconfig.js'), path.join(process.cwd(), 'config.js'));
}
}
// If either EdenJS type, create a bundles directory for code
if ((shouldMakeIntoApp || shouldMakeIntoModule) && !(await fs.pathExists(path.join(process.cwd(), 'bundles')))) {
await fs.mkdir(path.join(process.cwd(), 'bundles'));
}
// If directory type currently isn't none, clean up bundles
if (dirType !== 'none') {
await Promise.all([
fs.remove(path.join(process.cwd(), 'bundles', 'package.json')),
fs.remove(path.join(process.cwd(), 'bundles', 'package-lock.json')),
fs.remove(path.join(process.cwd(), 'bundles', 'node_modules')),
...commonBadFiles.map(file => fs.remove(path.join(process.cwd(), 'bundles', file))),
]);
}
// Forcefully put standard edenjs files into place
await fs.copy(path.join(__dirname, '..', '..', '.eslintrc.json'), path.join(process.cwd(), '.eslintrc.json'));
await fs.copy(path.join(__dirname, '..', '..', '.editorconfig'), path.join(process.cwd(), '.editorconfig'));
// Copy Dockerfile
if (!(await fs.pathExists(path.join(process.cwd(), 'Dockerfile')))) {
await fs.copy(path.join(__dirname, '..', '..', 'template', 'Dockerfile'), path.join(process.cwd(), 'Dockerfile'));
}
// Copy .dockerignore
if (!(await fs.pathExists(path.join(process.cwd(), '.dockerignore')))) {
await fs.copy(path.join(__dirname, '..', '..', 'template', '.dockerignore'), path.join(process.cwd(), '.dockerignore'));
}
// Copy kubernetes.yml
if (!(await fs.pathExists(path.join(process.cwd(), 'kubernetes.yml')))) {
await fs.copy(path.join(__dirname, '..', '..', 'template', 'kubernetes.yml'), path.join(process.cwd(), 'kubernetes.yml'));
}
// Copy docker-compse.yml
if (!(await fs.pathExists(path.join(process.cwd(), 'docker-compose.yml')))) {
await fs.copy(path.join(__dirname, '..', '..', 'template', 'docker-compose.yml'), path.join(process.cwd(), 'docker-compose.yml'));
}
// Copy gitlab-ci.yml
if (!(await fs.pathExists(path.join(process.cwd(), '.gitlab-ci.yml')))) {
await fs.copy(path.join(__dirname, '..', '..', 'template', '.gitlab-ci.yml'), path.join(process.cwd(), '.gitlab-ci.yml'));
}
// Copy travis.yml
if (!(await fs.pathExists(path.join(process.cwd(), '.travis.yml')))) {
await fs.copy(path.join(__dirname, '..', '..', 'template', '.travis.yml'), path.join(process.cwd(), '.travis.yml'));
}
// Put standard edenjs files into place if they dont exist
if (!(await fs.pathExists(path.join(process.cwd(), '.gitignore')))) {
await fs.copy(path.join(__dirname, '..', '..', 'template', 'gitignore'), path.join(process.cwd(), '.gitignore'));
}
if (!(await fs.pathExists(path.join(process.cwd(), '.gitattributes')))) {
await fs.copy(path.join(__dirname, '..', '..', '.gitattributes'), path.join(process.cwd(), '.gitattributes'));
}
// Make sure any EdenJS type has an edenconfig.js (good for identification too)
if ((shouldMakeIntoApp || shouldMakeIntoModule) && !(await fs.pathExists(path.join(process.cwd(), 'edenconfig.js')))) {
fs.copy(path.join(__dirname, '..', '..', 'template', 'emptyconfig.js'), path.join(process.cwd(), 'edenconfig.js'));
}
// Add to or create package.json
let packageData = {};
// Look for a package.json regardless of any factor to ensure loss is avoided
if (await fs.pathExists(path.join(process.cwd(), 'package.json'))) {
// Read existing package data
packageData = await fs.readJSON(path.join(process.cwd(), 'package.json'));
}
// Apply default data
packageData = Object.assign({}, {
name : path.basename(process.cwd()),
description : 'nothing yet',
version : '1.0.0',
dependencies : {},
engines : {
node : '>= 8.0.0',
},
}, packageData);
// Add core dependency if is missing from app
if (shouldMakeIntoApp && packageData.dependencies['@edenjs/core'] === undefined) {
packageData.dependencies['@edenjs/core'] = 'latest';
}
const edenPackageData = await fs.readJSON(path.join(__dirname, '..', '..', 'package.json'));
// Add our dev dependencies for eslint
packageData.devDependencies = Object.assign({}, packageData.devDependencies, {
eslint : edenPackageData.devDependencies.eslint,
'@edenjs/eslint-config-eden' : edenPackageData.devDependencies['@edenjs/eslint-config-eden'],
'eslint-config-airbnb' : edenPackageData.devDependencies['eslint-config-airbnb'],
'eslint-plugin-import' : edenPackageData.devDependencies['eslint-plugin-import'],
'eslint-plugin-jsx-a11y' : edenPackageData.devDependencies['eslint-plugin-jsx-a11y'],
'eslint-plugin-react' : edenPackageData.devDependencies['eslint-plugin-react'],
});
// Save new package data
await fs.writeJSON(path.join(process.cwd(), 'package.json'), packageData, {
spaces : 2,
});
// Migrate .git from bundles to cwd if requested
if (migrateGit && dirType !== 'none' && await fs.pathExists(path.join(process.cwd(), 'bundles', '.git')) && !(await fs.pathExists(path.join(process.cwd(), '.git')))) {
await fs.move(path.join(process.cwd(), 'bundles', '.git'), path.join(process.cwd(), '.git'));
}
return (suppliedDirType || dirType);
}
module.exports = initEden;