blob: 46605c0bbca0a9e94df26de08f5422d451e6e762 [file] [log] [blame]
'use strict';
const debug = require('debug')('weex:cli');
const pkg = require('../package.json');
const colors = require('colors/safe');
const semver = require('semver');
const spawn = require('cross-spawn');
const path = require('path');
const userhome = require('userhome');
const yargsParser = require('yargs-parser');
const fse = require('fs-extra');
const got = require('got');
const inquirer = require('inquirer');
const {
getNodeArgs,
readJson,
install,
confirm
} = require('../lib/utils');
const {
installUncaughtExceptionListener
} = require('../lib/errors');
const tabtab = require('./completion')
const coreName = '@weex-cli/core';
const homePrefix = '.wx';
let argv
// environment for running prepare
let env = {}
// data for cli instance
let coreData
// data in config.json
let coreConfigurations = {
version: '',
name: '',
is_next: false,
next_version: '',
local: '',
last_update_time: '',
update_time: 7
}
installUncaughtExceptionListener();
// check if user has use `sudo` or not
if (process.env.WEEX_ALLOW_SUDO) {
console.log('root privileges downgrade skipped');
} else {
require('root-check')(`
${colors.red('Please don\'t use `sudo` to run the command.')}
If you can't run without sudo, you may have problems during installation.
Try to run ${colors.underline('sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}')} command to empower your folders.
`);
}
// check if there has userhome floder
if (!userhome()) {
console.error(colors.red('ERROR: can not find HOME directory.'));
this.process.exit(1);
}
// saving configuration into config.json
const save = async (path, config) => {
try {
await fse.ensureFile(path)
await fse.writeJson(path, config, {
spaces: '\t'
})
} catch (err) {
console.error(err)
}
}
const cli = async (processArgv) => {
let wantsCompiled;
let command;
argv = yargsParser(processArgv.slice(2))
wantsCompiled = argv.compiled
command = argv._[0]
env = {
cliVersion: pkg.version,
coreName: coreName,
coreRoot: userhome(homePrefix, 'core'),
corePath: userhome(homePrefix, 'core/node_modules', coreName),
moduleRoot: process.env.WEEX_MODULE_PATH ? process.env.WEEX_MODULE_PATH : userhome(homePrefix, 'modules'),
templatePath: userhome(homePrefix, 'templates'),
defaultWeexTemplateRepo: 'weex-templates',
defaultWeexPlatformFloder: 'platforms',
defaultWeexTemplate: 'webpack',
defaultWeexPlatformTemplate: {
ios: 'weex-playground-ios',
android: 'weex-playground-android'
},
weexAndroidConfigFilename: 'android.config.json',
weexIOSConfigFilename: 'ios.config.json',
weexAndroidProjectPath: 'platforms/android',
weexIOSProjectPath: 'platforms/ios',
defaultWeexBundleTarget: 'dist',
defaultWeexBundleEntry: 'index.js',
defaultWeexBundleCommand: 'npm run dev',
defaultPreviewWebCommand: 'npm start',
defaultWeexPlatforms: ['ios', 'android'],
platformConfigName: 'platforms.json',
defaultWeexPluginTemplateRepo: 'weex-plugin-template',
defaultWeexPluginProjectPath: 'plugins',
defaultWeexPluginConfigName: 'plugins.json',
registry: argv.registry || process.env.NPM_REGISTRY || 'https://registry.npm.taobao.org',
moduleConfigFileName: 'stores.json',
globalConfigFileName: 'config.json',
home: userhome(homePrefix),
trash: userhome(homePrefix, 'trash')
};
coreData = Object.assign({
modules: {},
configs: {},
argv: processArgv,
}, env);
// prepare for running cli
try {
await prepare(command, env)
} catch (error) {
// TODO: pipe error to solutions.
console.error(colors.red(`ERROR: ${error.stack || error}`))
}
// check if we're running in dev mode
const devMode = await fse.exists(`${coreData.corePath}/src`);
if (command === 'completion') {
// Register complete command
tabtab.start()
} else if (!process.env.WEEX_CLI_TEST) {
if (devMode && !wantsCompiled) {
// hook into ts-node so we can run typescript on the fly
require('ts-node').register({
project: `${coreData.corePath}/tsconfig.json`
})
const TSCli = require(`${coreData.corePath}/src/cli/cli`).default;
(new TSCli(coreData)).start()
} else {
try {
const Cli = require(`${coreData.corePath}/lib/cli/cli`).default;
(new Cli(coreData)).start()
} catch (error) {
debug(`Run cli error, ${error && error.stack}`)
await prepare('repair', env)
}
}
}
}
const initGlobalConfig = async () => {
const taobao = `http://registry.npm.taobao.org`
const npm = `http://registry.npmjs.org`
if (process.env.WEEX_CLI_TEST) {
return {
telemetry: false,
registry: 'https://registry.npmjs.org/'
}
}
const questions = [{
name: 'telemetry',
type: 'confirm',
message: 'May weex-toolkit anonymously report usage statistics to improve the tool over time?',
},
{
name: 'registry',
type: 'list',
choices: [{
name: 'use taobao (for Chinese)',
value: taobao,
short: 'taobao'
},
{
name: 'use npm',
value: npm,
short: 'npm'
}
],
message: 'Which npm registry you perfer to use?',
},
]
const answer = await inquirer.prompt(questions)
return {
telemetry: answer.telemetry,
registry: answer.registry
}
}
const prepare = async (command, config) => {
// TODO: check there has a new version of `weex-toolkit` or not
let corePackageJson = {}
let coreVersion
let coreName = config.coreName;
let needInstall = false
const coreConfigurationsPath = path.join(config.coreRoot, config.globalConfigFileName)
const hasConfig = await fse.exists(coreConfigurationsPath)
let corePath = process.env.WEEX_CORE_PATH ? process.env.WEEX_CORE_PATH : config.corePath
if (hasConfig) {
debug('Has configuration file and try to readJson')
coreConfigurations = await fse.readJson(coreConfigurationsPath)
if (typeof coreConfigurations.telemetry === 'undefined') {
const userConfigs = await initGlobalConfig()
coreConfigurations = Object.assign({}, coreConfigurations, userConfigs)
try {
await save(coreConfigurationsPath, coreConfigurations)
} catch (err) {
console.log(err)
}
}
coreData['configs'] = coreConfigurations
corePath = process.env.WEEX_CORE_PATH ? process.env.WEEX_CORE_PATH : coreConfigurations.local ? coreConfigurations.local : config.corePath
}
if (fse.existsSync(path.join(corePath, 'package.json'))) {
corePackageJson = await readJson(path.join(corePath, 'package.json'))
}
coreData['corePath'] = corePath
if (fse.existsSync(path.join(config.moduleRoot, config.moduleConfigFileName))) {
coreData['modules'] = await readJson(path.join(config.moduleRoot, config.moduleConfigFileName))
}
if (command === 'repair') {
const repairModule = argv._[1];
if (repairModule) {
const arg = repairModule.split('@');
if (arg.length > 1) {
coreVersion = arg.pop();
coreName = arg.join('@');
} else {
coreName = arg;
coreVersion = 'latest';
}
} else {
coreName = config.coreName
coreVersion = 'latest'
}
// If repair module isn't the core module, pipe argv to the @weex-cli/core
if (coreName !== config.coreName) {
needInstall = false;
} else {
needInstall = true;
debug(`start repair ${config.coreName}`)
console.log(colors.yellow(`Start repair ${config.coreName}, please wait ...`));
}
} else {
// checking if there has weex-cli/core
if (!corePackageJson.name || !corePackageJson.version) {
coreVersion = process.env.WEEX_CORE_VERSION || 'latest'
debug('start install a new core')
console.log(colors.yellow('Start installing Core, please wait ...'));
needInstall = true;
} else {
if (hasConfig) {
// while the has core configuration
// fetching if there has new version
if (new Date().getTime() - coreConfigurations.last_update_time <= 24 * 3600 * 1000 * (coreConfigurations.updateTimes || 7)) {
return;
}
const latest = await got.get(
config.registry + '/' + config.coreName + '/latest', {
'json': true,
'timeout': 60 * 1000,
'retries': 0
}
);
if (latest && latest.body) {
debug('coreData from %s: %o', config.registry, latest.body);
if (latest.body.version) {
coreVersion = latest.body.version;
}
if (semver.gt(coreVersion, corePackageJson.version)) {
const result = await confirm(
'New version detected ' + colors.green(coreVersion) +
', the local version is ' + colors.dim(corePackageJson.version) +
', upgrade now?[Y/n]'
);
if (result) {
console.log(colors.yellow(`upgrading ${corePackageJson.name} from ${corePackageJson.version} -> ${coreVersion} ...`));
debug('start upgrade to latest core')
console.log(colors.yellow(`Upgrading ${corePackageJson.name}, please wait ...`));
needInstall = true;
} else {
let userConfigs = {}
if (typeof config.telemetry === 'undefined') {
userConfigs = await initGlobalConfig()
}
coreConfigurations = Object.assign({
version: corePackageJson.version,
name: corePackageJson.name,
is_next: false,
next_version: coreVersion,
local: path.join(config.coreRoot, 'node_modules', corePackageJson.name),
last_update_time: (new Date()).getTime()
}, userConfigs)
}
}
}
}
}
}
if (needInstall) {
try {
await install(coreName, coreVersion, {
root: config.coreRoot,
trash: config.trash,
force: argv.force || argv.f,
registry: config.registry,
ENVS: config.ENVS,
});
} catch (error) {
if (error.stack.indexOf('module is locked') >= 0) {
console.error(colors.red('\nThe module is locked, please check if there has another installing process.'))
console.log(`Or you can run the command with ${colors.yellow('`-f`')} or ${colors.yellow('`--force`')} option to skip it.`)
process.exit(0)
}
}
const packageJson = await fse.readJson(path.join(config.coreRoot, 'node_modules', coreName, 'package.json'))
let userConfigs = {}
if (typeof config.telemetry === 'undefined') {
userConfigs = await initGlobalConfig()
}
coreConfigurations = Object.assign({
version: packageJson.version,
name: packageJson.name,
is_next: true,
next_version: '',
local: path.join(config.coreRoot, 'node_modules', coreName),
last_update_time: (new Date()).getTime(),
update_time: 7
}, userConfigs)
}
try {
await save(coreConfigurationsPath, coreConfigurations)
} catch (err) {
console.log(err)
}
return;
}
module.exports = cli;