react.cordova
Version:
Reco unifies React.js and Cordova into one CLI which bundles both platforms together and provides the developer with the ability to generate Cordova hybrid cross-platform applications built in React .
932 lines (760 loc) • 39.2 kB
JavaScript
const fs = require("fs");
const path = require("path");
var colors = require('colors');
let reco = {
constructor: (args) => {
try {
reco.state = {
args: args,
clientArgsAfter: "",
clientArgsAfter_Space: "",
callBack_replaceWwwRootDir: () => { },
child_process: require('child_process'),
error: false,
// emulatorRunning: false,
// emulatorBusy: false,
}
//----- save the args after index
var clientArgsAfter = "";
for (let index = 1; index < args.slice(2).length; index++)
clientArgsAfter += (args.slice(2)[index] + " ");
reco.setState({ clientArgsAfter: clientArgsAfter });
//--
let clientArgsAfter_Space = "";
for (let index = 1; index < args.slice(2).length; index++)
clientArgsAfter_Space += ('"' + args.slice(2)[index] + '"' + ' ');
reco.setState({ clientArgsAfter_Space: clientArgsAfter_Space });
///------////
switch (args[2].split(2)[0]) {
case "version":
reco.version();
break;
case "init":
reco.init();
break;
case "serve":
reco.bundleServe();
break;
case "build":
reco.build();
break;
case "react":
reco.react();
break;
case "start":
reco.bundleServe();
break;
case "test":
reco.reactTest();
break;
case "install":
reco.reactInstall();
break;
case "i":
reco.reactInstall();
break;
case "uninstall":
reco.reactUninstall();
break;
case "cordova":
reco.cordova();
break;
case "plugin":
reco.cordovaPlugin();
break;
case "platform":
reco.cordovaPlatform();
break;
case "-info":
reco.info();
break;
case "":
reco.map();
break;
default:
console.log();
console.log(args.slice(2)[0], "it is not exec in reco cli");
console.log('try => ');
reco.map();
break;
}
} catch (error) {
reco.map();
}
},
//------------------------------------build------------------------------------//
build: () => {
console.log();
console.log('start build react');
console.log();
reco.state.child_process.exec(
'npm run build'
, { cwd: 'react-js' }
, function (error, stdout, stderr) {
if (error) {
console.error('reco-react-cli ERROR : ' + error);
return;
}
console.log(stdout);
}).on('close', function () {
console.log();
console.log('reco start to build cordova');
console.log();
reco.state.callBack_replaceWwwRootDir = function () {
function execCB(error, stdout, stderr) {
if (error) {
console.error('reco-cli-build-cordova ERROR : ' + error);
reco.setState({ error: true });
return;
}
// if (stdout) console.log(stdout);
// if (stderr) console.log(stderr);
}
// if (os.platform() === "darwin") {
reco.state.child_process.exec(
'cordova build ' + reco.state.clientArgsAfter
, { maxBuffer: 5120 * 5120, cwd: 'cordova' }
, execCB).on('close', function () {
if (!reco.state.error) reco.succeeded();
}).stdout.on('data', (data) => {
console.log(data.toString().replace("reco", "react"));
});
// } else {
// reco.state.child_process.exec(
// 'cordova build ' + reco.state.clientArgsAfter
// , { cwd: 'cordova' }
// , execCB).on('close', function () {
// if (!reco.state.error) reco.succeeded();
// }).stdout.on('data', (data) => {
// console.log(data.toString().replace("reco", "react"));
// });
// }
};
reco.replaceWwwRootDir();
});
},
//------------------------------------init------------------------------------//
init: async () => {
const choicesOptions = ['Reco template', 'Empty'];
const defaultTemplate = choicesOptions[0];
const inquirer = require('inquirer');
const questions = [];
questions.push({
type: 'list',
name: 'template',
message: 'Please select project template',
choices: choicesOptions,
default: defaultTemplate,
});
const answer = await inquirer.prompt(questions);
const withTemplate = answer.template === choicesOptions[0];
const template = withTemplate ? "recoTemp" : "empty";
let folderName = reco.state.args.slice(2)[2];
while (folderName.indexOf(" ") >= 0) {
folderName = folderName.replace(" ", "_");
}
const dir = "./" + folderName;
if (fs.existsSync(dir)) {
console.log("----------The name: " + folderName + " exist--------------")
return
} else {
fs.mkdirSync(dir);
}
if (fs.existsSync(dir + "/react-js") || fs.existsSync(dir + "/cordova")) {
console.log("exists reco project.");
console.log('if you wont to start a new project delete all folders in this directory and run agin: reco init <com.myAppId> <"my app name">');
return;
}
console.log();
console.log('---------reco start to build react-app---------');
reco.state.child_process.exec(
'npx create-react-app react-js'
, { cwd: dir }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-init-react ERROR : ' + error);
return;
}
if (stdout)
console.log(stdout.toString());
if (stderr)
console.log(stderr.toString());
}).stdout.on('data', (data) => {
console.log(data.toString());
})
.on('close', function () {
// fs.renameSync(`./reco`, `./react`
// , function (error, stdout, stderr) {
// if (error) {
// reco.setState({ error: true });
// console.error('reco-cli-init-renameReactFolder ERROR : ' + error);
// return;
// }
// console.log(stdout);
// }
// );
reco.state.child_process.exec(
withTemplate ? 'npm i navigation-controller react-browser-notifications'
: 'npm i navigation-controller'
, { cwd: dir + "/react-js" }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-init--install-navigation-controller ERROR : ' + error);
return;
}
console.log(stdout);
}).on('data', (data) => {
console.log(data.toString());
}).on('close', () => {
})
reco.state.child_process.exec(
'npm i cordova_script'
, { cwd: dir }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-init--install-navigation-controller ERROR : ' + error);
return;
}
console.log(stdout);
}).on('data', (data) => {
console.log(data.toString());
}).on('close', () => {
// destination.txt will be created or overwritten by default.
fs.copyFile(reco.state.args[1].substring(0, reco.state.args[1].lastIndexOf(".bin")) + "templates\\" + "package.json"
, dir + '/package.json', (err) => {
if (err) {
console.log('package.json of reco not copy');
console.log('ERROR: ', err)
}
});
const copydir = require("copy-dir");
fs.readdir(reco.state.args[1].substring(0, reco.state.args[1].lastIndexOf(".bin")) + "templates\\" + template, (err, files) => {
if (err) console.log(err);
else files.forEach(file => {
if (fs.existsSync(dir + "/react-js/src/" + file))
fs.unlink(dir + "/react-js/src/" + file, (err) => {
if (err) console.log("ERROR: reco can't copy template files.(unlink) :" + err);
});
copydir.sync(reco.state.args[1].substring(0
, reco.state.args[1].lastIndexOf(".bin")) + "templates\\" + template + "\\" + file
, dir + "/react-js/src/" + file, {}, () => {
if (err) console.log("ERROR: reco can't copy template files :" + err);
});
});
});
//--
//---------reco start to build cordova-app---------//
console.log();
console.log('---------reco start to build cordova-app---------');
console.log();
reco.state.child_process.exec(
'cordova create cordova ' + reco.state.clientArgsAfter_Space
, { cwd: dir }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-init-cordova-(cordova create cordova) ERROR :' + error);
return;
}
console.log(stdout);
}).on('data', (data) => {
console.log(data.toString());
})
.on('close', function () {
reco.state.child_process.exec(
'cordova platform add android'
, { cwd: dir + '/cordova' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-init-cordova--(cordova platform add android) ERROR :' + error);
return;
}
console.log(stdout);
}).stdout.on('data', (data) => {
console.log(data.toString());
}).on('close', function () {
reco.state.child_process.exec(
'cordova platform add ios'
, { cwd: dir + '/cordova' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-init-cordova--(cordova platform add ios) ERROR :' + error);
return;
}
console.log(stdout);
}).stdout.on('data', (data) => {
console.log(data.toString());
}).on('close', function () {
reco.state.child_process.exec(
'cordova platform add browser'
, { cwd: dir + '/cordova' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-init-cordova--(cordova platform add browser) ERROR :' + error);
return;
}
console.log(stdout);
}).stdout.on('data', (data) => {
console.log(data.toString());
}).on('close', function () {
reco.state.child_process.exec(
'cordova platform ls'
, { cwd: dir + '/cordova' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-init-cordova--(cordova platform ls) ERROR :' + error);
return;
}
console.log(stdout);
}).on('data', (data) => {
console.log(data.toString());
}).on('close', function () {
const build_App = () => {
reco.state.child_process.exec(
'npm run build'
, { cwd: dir + '/react-js' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-react-cli ERROR : ' + error);
return;
}
console.log(stdout);
}).on('close', function () {
reco.state.callBack_replaceWwwRootDir = function () {
if (!reco.state.error) {
reco.succeeded();
console.log();
console.log("run 'cd " + dir.replace("./", "") + "'")
}
};
reco.recoFiles(dir);
});
}
if (withTemplate) {
reco.state.child_process.exec(
'cordova plugin add cordova-plugin-local-notification'
, { cwd: dir + '/cordova' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-init-cordova--(cordova platform ls) ERROR :' + error);
return;
}
console.log(stdout);
}).on('data', (data) => {
console.log(data.toString());
}).on('close', function () {
build_App();
});
} else {
build_App();
}
});
});
});
});
});
//--
});
});
},
//------------------------------------react------------------------------------//
react: () => {
reco.state.child_process.exec(
'npm ' + reco.state.clientArgsAfter
, { cwd: 'react-js' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-react ERROR : ' + error);
return;
}
console.log(stdout);
}).stdout.on('data', (data) => {
console.log(data.toString());
});
},
//------------------------------------react start------------------------------------//
reactStart: () => {
reco.state.child_process.exec(
'npm start'
, { cwd: 'react-js' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-reactStart ERROR : ' + error);
return;
}
console.log(stdout);
}).stdout.on('data', (data) => {
console.log(data.toString());
return;
});
},
//------------------------------------react test------------------------------------//
reactTest: () => {
reco.state.child_process.exec(
'npm test'
, { cwd: 'react-js' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-reactTest ERROR : ' + error);
return;
}
console.log(stdout);
}).on('data', (data) => {
console.log(data.toString());
});
},
//------------------------------------react install------------------------------------//
reactInstall: () => {
reco.state.child_process.exec(
'npm i ' + reco.state.clientArgsAfter
, { cwd: 'react-js' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-reactInstall ERROR : ' + error);
return;
}
console.log(stdout);
}).on('data', (data) => {
console.log(data.toString());
});
},
//------------------------------------react uninstall------------------------------------//
reactUninstall: () => {
reco.state.child_process.exec(
'npm uninstall ' + reco.state.clientArgsAfter
, { cwd: 'react-js' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-reactInstall ERROR : ' + error);
return;
}
console.log(stdout);
}).on('data', (data) => {
console.log(data.toString());
});
},
//------------------------------------cordova------------------------------------//
cordova: () => {
reco.state.child_process.exec(
'cordova ' + reco.state.clientArgsAfter
, { cwd: 'cordova' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-cordova ERROR : ' + error);
return;
}
}).stdout.on('data', (data) => {
console.log(data.toString());
});
},
//------------------------------------cordovaPlugin------------------------------------//
cordovaPlugin: () => {
reco.state.child_process.exec(
'cordova plugin ' + reco.state.clientArgsAfter
, { cwd: 'cordova' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-cordovaPlugin ERROR : ' + error);
return;
}
}).stdout.on('data', (data) => {
console.log(data.toString());
});
},
//------------------------------------cordovaPlatform------------------------------------//
cordovaPlatform: () => {
reco.state.child_process.exec(
'cordova platform ' + reco.state.clientArgsAfter
, { cwd: 'cordova' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-cordovaPlatform ERROR : ' + error);
return;
}
}).stdout.on('data', (data) => {
console.log(data.toString());
}).on('data', (data) => {
console.log(data.toString());
});
},
//------------------------------------bundleServe------------------------------------//
bundleServe: () => {
console.log(colors.blue('Emulator running...'));
console.log('please wait, processing...');
reco.state.child_process.exec(
'cordova serve 8597'
, { cwd: 'cordova' }
, function (error) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-bundleServe, error at cordova serve-run.');
console.error('ERROR :' + error);
return;
}
}).stdout.on('data', (dataCordo) => {
if (dataCordo.includes("localhost")) {
reco.state.child_process.exec(
'npm start'
, { cwd: 'react-js' }
, function (error) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-bundleServe, error at react start serve.');
console.error('ERROR :' + error);
return;
}
}).stdout.on('data', (data) => {
if (data.includes("localhost")) {
console.log(colors.blue(data));
}
else { console.log(data); }
});
}
});
},
//------------------------------------info------------------------------------//
info: () => {
console.log(' info=> https://github.com/orchoban/react.cordova');
console.log();
reco.map();
},
//------------------------------------map------------------------------------//
map: () => {
console.log(colors.yellow.underline.bold('-info:') + "");
console.log();
console.log(" " + colors.underline.bold('init'));
console.log(" " + colors.bold('create new project. both react-app and cordova-app and then will merge one into the other. '));
console.log(' example command: ' + colors.green('reco init com.example.hello "Hello World"'));
console.log();
console.log(" " + colors.underline('build'));
console.log(" " + colors.bold(`build react-app and cordova-app. `));
console.log(' command: ' + colors.green('reco build'));
console.log(' command: ' + colors.green('reco build <cordova-platform>'));
console.log();
console.log(" " + colors.underline('react'));
console.log(" " + colors.bold(`to run any react command. `));
console.log(' command: ' + colors.green('reco react <command>'));
console.log();
console.log(" " + colors.underline('start/serve'));
console.log(" " + colors.bold(`run a React or Cordova simulation`));
console.log(' command: ' + colors.green('reco start'));
console.log();
console.log(" " + colors.underline('test'));
console.log(" " + colors.bold(`react test. `));
console.log(' command: ' + colors.green('reco test'));
console.log();
console.log(" " + colors.underline('install/uninstall'));
console.log(" " + colors.bold(`install react package from npm. `));
console.log(' command: ' + colors.green('reco install <npm-package>'));
console.log(' command: ' + colors.green('reco i <npm-package>'));
console.log(' command: ' + colors.green('reco uninstall <npm-package>'));
console.log();
console.log(" " + colors.underline('plugin'));
console.log(" " + colors.bold(`add cordova plugin. `));
console.log(' command: ' + colors.green('reco plugin add <cordova-plugin>'));
console.log();
console.log(" " + colors.underline('remove'));
console.log(" " + colors.bold(`remove cordova plugin. `));
console.log(' command: ' + colors.green('reco plugin remove <cordova-plugin>'));
console.log(' command: ' + colors.green('reco plugin rm <cordova-plugin>'));
console.log();
console.log(" " + colors.underline('cordova'));
console.log(" " + colors.bold(`to run any cordova command. `));
console.log(' command: ' + colors.green('reco cordova'));
console.log();
console.log('--------------------');
console.log('reco -info');
},
//------------------------------------credit------------------------------------//
credit: () => {
console.log(`
#####Created by Or Chuban (Choban)#####
info => https://github.com/orchoban/react.cordova`);
},
//------------------------------------succeeded------------------------------------//
succeeded: () => {
console.log();
console.log("----------------------------------------------");
console.log('---------------!reco succeeded!---------------');
console.log("----------------------------------------------");
console.log();
reco.credit();
reco.version(true);
},
//-------------------------------------------------------------------------------//
//------------------------------------helpers------------------------------------//
//-------------------------------------------------------------------------------//
setState: (state) => {
for (var item in state) {
reco.state[item] = state[item]
}
},
//-------------------------remove all files and folders in =>./cordova/www--------------------------//
replaceWwwRootDir: (dirPath1 = './cordova/www') => {
const ncp = require('ncp').ncp;
if (fs.existsSync("./cordova/www")) {
async function rmWwwRootDir(dirPath, options = {}) {
const
{ removeContentOnly = false, drillDownSymlinks = false } = options,
{ promisify } = require('util'),
readdirAsync = promisify(fs.readdir),
unlinkAsync = promisify(fs.unlink),
rmdirAsync = promisify(fs.rmdir),
lstatAsync = promisify(fs.lstat) // fs.lstat can detect symlinks, fs.stat can't
let
files
try {
files = await readdirAsync(dirPath)
} catch (e) {
reco.setState({ error: true });
throw new Error(e)
}
if (files.length) {
for (let fileName of files) {
let
filePath = path.join(dirPath, fileName),
fileStat = await lstatAsync(filePath),
isSymlink = fileStat.isSymbolicLink(),
isDir = fileStat.isDirectory()
if (isDir || (isSymlink && drillDownSymlinks)) {
await rmWwwRootDir(filePath)
} else {
await unlinkAsync(filePath)
}
}
}
if (!removeContentOnly)
await rmdirAsync(dirPath);
if (!fs.existsSync(dirPath1)) {
ncp.limit = 9999999999999999999;
let parentDir = dirPath1.startsWith("./cordova")
? "./" : dirPath1.substring(0, dirPath1.indexOf("/cordova")) + "/"
ncp(parentDir + "react-js/build", parentDir + "cordova/www", function (err) {
if (err) {
reco.setState({ error: true });
return console.error("ERROR ncp1, copy react-js/build tocordova/www : " + err);
}
reco.state.callBack_replaceWwwRootDir(); // callBack();
});
}
}
rmWwwRootDir(dirPath1);
} else {
let parentDir = dirPath1.startsWith("./cordova") ? "./" : dirPath1.substring(0, dirPath1.indexOf("/cordova")) + "/";
ncp(parentDir + "react-js/build", parentDir + "cordova/www", function (err) {
if (err) {
reco.setState({ error: true });
return console.error("ERROR ncp2, copy react-js/build tocordova/www : " + err);
}
reco.state.callBack_replaceWwwRootDir(); // callBack();
});
}
},
//------------------------------------recoFiles------------------------------------//
recoFiles: (dir) => {
//-- ./react/public/index.html --//
fs.readFile(dir + "/react-js/public/index.html", function (err, data) {
// res.writeHead(200, {'Content-Type': 'text/html'});
// res.write(data);
// res.end();
if (err) {
reco.setState({ error: true });
console.log("error: ", err);
}
let dataString = data.toString();
dataString = dataString.replace(dataString.substr(
dataString.indexOf(`<meta name="viewport"`)
, dataString.substr(dataString.indexOf(`<meta name="viewport"`)).indexOf("/>") + 2
), ` <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, shrink-to-fit=no" />`);
fs.writeFile(dir + "/react-js/public/index.html", dataString, function (err) {
if (err) {
return console.log(err);
} else {
console.log(dir + "/react-js/public/index.html ready to by mobile app with cordova");
//-- react package.json --//
const jsonfile = require('jsonfile');
const file = dir + '/react-js/package.json';
jsonfile.readFile(file)
.then(obj => {
obj.homepage = "./";
jsonfile.writeFile(file, obj, function (err) {
if (err) {
console.error("ERROR: add homepage to react package.json . ", err);
return;
} else {
console.log("update homepage in react-js package.json , now it's ready to by mobile app with cordova.");
reco.replaceWwwRootDir(dir + '/cordova/www');
}
})
})
.catch(error => {
reco.setState({ error: true });
console.error("reco-cli-recoFiles=> ERROR: ", error);
})
}
});
});
},
version: (automatic) => {
reco.state.child_process.exec(
'npm view react.cordova --json'
// , { cwd: 'react-js' }
, function (error, stdout, stderr) {
if (error) {
reco.setState({ error: true });
console.error('reco-cli-react ERROR : ' + error);
return;
}
// console.log(stdout);
}).stdout.on('data', (dataView) => {
dataView = JSON.parse(dataView);
const jsonfile = require('jsonfile');
const file = reco.state.args[1].substring(0, reco.state.args[1].lastIndexOf(".bin")) + "package.json";
jsonfile.readFile(file)
.then(obj => {
const thisVersion = obj.version;
const newVersion = dataView.versions[dataView.versions.length - 1];
if (automatic && thisVersion !== newVersion) {
console.log();
console.log();
console.log(
colors.cyan(`
╭─────────────────────────────────────────────╮
│ │
│ `)
, `Update available ${thisVersion} → `
, colors.green(newVersion)
, colors.cyan(` │
│ `)
, `Run`
, colors.blue(`npm i -g react.cordova`)
, ` to update`
, colors.cyan(` │
│ │
╰─────────────────────────────────────────────╯
`));
} else if (!automatic) {
console.log(thisVersion);
}
});
});
}
}
module.exports = reco;