mirror of https://github.com/embarklabs/embark.git
WIP: first steps
This commit is contained in:
parent
e271cebbe3
commit
1931f78a2d
|
@ -0,0 +1,4 @@
|
|||
engine-strict = true
|
||||
package-lock = false
|
||||
save-exact = true
|
||||
scripts-prepend-node-path = true
|
|
@ -0,0 +1,6 @@
|
|||
# `embark-create-react-dapp`
|
||||
|
||||
> DApp creator utility that uses Create React App
|
||||
|
||||
Visit [framework.embarklabs.io](https://framework.embarklabs.io/) to get started with
|
||||
[Embark](https://github.com/embarklabs/embark).
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/* global process require */
|
||||
|
||||
require('source-map-support/register');
|
||||
require('..').main()
|
||||
.then(code => {
|
||||
process.exit(code ? code : 0);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
});
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"name": "@embarklabs/create-react-dapp",
|
||||
"version": "5.2.1",
|
||||
"author": "Michael Bradley <michaelsbradleyjr@gmail.com> (https://github.com/michaelsbradleyjr/)",
|
||||
"description": "DApp creator utility that uses Create React App",
|
||||
"homepage": "https://github.com/embarklabs/embark/tree/master/dapps/creators/react#readme",
|
||||
"repository": {
|
||||
"directory": "dapps/creators/react",
|
||||
"type": "git",
|
||||
"url": "https://github.com/embarklabs/embark.git"
|
||||
},
|
||||
"keywords": [
|
||||
"blockchain",
|
||||
"create-react-app",
|
||||
"dapps",
|
||||
"ethereum",
|
||||
"ipfs",
|
||||
"react",
|
||||
"serverless",
|
||||
"solc",
|
||||
"solidity"
|
||||
],
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"bin": "./bin/create",
|
||||
"main": "./dist/index.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"embark-collective": {
|
||||
"build:node": true
|
||||
},
|
||||
"scripts": {
|
||||
"_build": "npm run solo -- build",
|
||||
"ci": "npm run qa",
|
||||
"clean": "npm run reset",
|
||||
"lint": "eslint bin/create src/",
|
||||
"qa": "npm-run-all lint _build",
|
||||
"reset": "npx rimraf dist embark-*.tgz package",
|
||||
"solo": "embark-solo"
|
||||
},
|
||||
"dependencies": {
|
||||
"embark-i18n": "^5.1.1",
|
||||
"embark-init": "^5.2.1",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"source-map-support": "0.5.13"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "../../../.eslintrc.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/runtime-corejs3": "7.7.4",
|
||||
"embark-solo": "^5.1.1",
|
||||
"eslint": "5.7.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"rimraf": "3.0.0"
|
||||
},
|
||||
"indirect": {
|
||||
"dependencies": {
|
||||
"create-react-app": "^3.3.1"
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.17.0",
|
||||
"npm": ">=6.11.3",
|
||||
"yarn": ">=1.19.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* global __dirname require */
|
||||
|
||||
import { __ } from 'embark-i18n';
|
||||
import { creatorDefaults, makeCreatorMain } from 'embark-init';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import { join } from 'path';
|
||||
|
||||
const pkgJsonPath = join(__dirname, "..", "package.json");
|
||||
const pkgJson = require(pkgJsonPath);
|
||||
const version = pkgJson.version;
|
||||
|
||||
const creator = cloneDeep(creatorDefaults);
|
||||
creator.extraHelp = () => {
|
||||
console.log('');
|
||||
console.log('EXTRA HELP SECTION FOR react-dapp');
|
||||
};
|
||||
creator.version = version;
|
||||
|
||||
delete creator.init.options.contractsOnly;
|
||||
delete creator.init.options.simple;
|
||||
delete creator.init.presets.contractsOnly;
|
||||
const defaultPreset = '@embarklabs/dapps-presets-react-boilerplate';
|
||||
creator.init.presets.default = `${defaultPreset}@${pkgJson.devDependencies[defaultPreset]}`;
|
||||
const demoPreset = '@embarklabs/dapps-presets-react-demo';
|
||||
creator.init.presets.demo = `${demoPreset}@${pkgJson.devDependencies[demoPreset]}`;
|
||||
|
||||
const subcreator = 'create-react-app';
|
||||
creator.subcreator.abbrev = 'cra';
|
||||
creator.subcreator.command = ['{{subcreator.package}}', [2], {abc:123}];
|
||||
creator.subcreator.name = subcreator;
|
||||
creator.subcreator.package = '{{subcreator.name}}@{{subcreator.version}}';
|
||||
creator.subcreator.version = pkgJson.indirect.dependencies[subcreator];
|
||||
|
||||
// const subcreator = '@angular/cli';
|
||||
// creator.subcreator.abbrev = 'ng';
|
||||
// creator.subcreator.command = ['-p', '{{subcreator.package}}', 'ng', 'new'];
|
||||
// creator.subcreator.name = subcreator;
|
||||
// creator.subcreator.package = '{{subcreator.name}}@{{subcreator.version}}';
|
||||
// creator.subcreator.version = pkgJson.indirect.dependencies[subcreator];
|
||||
|
||||
export const main = makeCreatorMain(creator);
|
|
@ -86,6 +86,7 @@
|
|||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"dapps/creators/*",
|
||||
"dapps/templates/*",
|
||||
"dapps/tests/*",
|
||||
"packages/*",
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
"embark-engine": "^5.2.2",
|
||||
"embark-graph": "^5.2.0",
|
||||
"embark-i18n": "^5.1.1",
|
||||
"embark-init": "^5.2.0",
|
||||
"embark-logger": "^5.2.0",
|
||||
"embark-reset": "^5.1.1",
|
||||
"embark-suggestions": "^5.2.2",
|
||||
|
|
|
@ -992,6 +992,7 @@ function isDappCmd(cmd) {
|
|||
'--help',
|
||||
'new',
|
||||
'demo',
|
||||
'init',
|
||||
'version',
|
||||
'help'
|
||||
].indexOf(cmd) === -1;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { __, setOrDetectLocale } from 'embark-i18n';
|
||||
import { cli as initCli } from 'embark-init';
|
||||
import { diagramPath } from 'embark-utils';
|
||||
const program = require('commander');
|
||||
const EmbarkController = require('./cmd_controller.js');
|
||||
|
@ -13,6 +14,7 @@ class Cmd {
|
|||
process(args) {
|
||||
this.newApp();
|
||||
this.demo();
|
||||
this.init();
|
||||
this.build();
|
||||
this.run();
|
||||
this.exec();
|
||||
|
@ -439,6 +441,19 @@ class Cmd {
|
|||
});
|
||||
}
|
||||
|
||||
async init() {
|
||||
let reject, resolve;
|
||||
let promise = new Promise((res, rej) => { resolve = res; reject = rej; });
|
||||
initCli(program.command('init'), { reject, resolve });
|
||||
try {
|
||||
const code = await promise;
|
||||
process.exit(code ?? 0);
|
||||
} catch (error) {
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
helpCmd() {
|
||||
program
|
||||
.command('help')
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
engine-strict = true
|
||||
package-lock = false
|
||||
save-exact = true
|
||||
scripts-prepend-node-path = true
|
|
@ -0,0 +1,6 @@
|
|||
# `embark-init`
|
||||
|
||||
> DApp initializer utility
|
||||
|
||||
Visit [framework.embarklabs.io](https://framework.embarklabs.io/) to get started with
|
||||
[Embark](https://github.com/embarklabs/embark).
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/* global process require */
|
||||
|
||||
require('source-map-support/register');
|
||||
require('..').main()
|
||||
.then(code => {
|
||||
process.exit(code ? code : 0);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"name": "embark-init",
|
||||
"version": "5.2.1",
|
||||
"author": "Michael Bradley <michaelsbradleyjr@gmail.com> (https://github.com/michaelsbradleyjr/)",
|
||||
"description": "DApp initializer utility",
|
||||
"homepage": "https://github.com/embarklabs/embark/tree/master/packages/utils/init#readme",
|
||||
"repository": {
|
||||
"directory": "packages/utils/init",
|
||||
"type": "git",
|
||||
"url": "https://github.com/embarklabs/embark.git"
|
||||
},
|
||||
"keywords": [
|
||||
"blockchain",
|
||||
"dapps",
|
||||
"ethereum",
|
||||
"ipfs",
|
||||
"serverless",
|
||||
"solc",
|
||||
"solidity"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": "./bin/init",
|
||||
"main": "./dist/index.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"embark-collective": {
|
||||
"build:node": true
|
||||
},
|
||||
"scripts": {
|
||||
"_build": "npm run solo -- build",
|
||||
"ci": "npm run qa",
|
||||
"clean": "npm run reset",
|
||||
"lint": "eslint bin/init src/",
|
||||
"qa": "npm-run-all lint _build",
|
||||
"reset": "npx rimraf dist embark-*.tgz package",
|
||||
"solo": "embark-solo"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "2.4.2",
|
||||
"commander": "2.20.3",
|
||||
"embark-i18n": "^5.1.1",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"lodash.isplainobject": "4.0.6",
|
||||
"minimist": "1.2.0",
|
||||
"source-map-support": "0.5.13"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "../../../.eslintrc.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/runtime-corejs3": "7.7.4",
|
||||
"embark-solo": "^5.1.1",
|
||||
"eslint": "5.7.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"rimraf": "3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.17.0",
|
||||
"npm": ">=6.11.3",
|
||||
"yarn": ">=1.19.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/* global process */
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import { Command } from 'commander';
|
||||
import { __, setOrDetectLocale } from 'embark-i18n';
|
||||
import minimist from 'minimist';
|
||||
|
||||
import { creatorDefaults } from './defaults';
|
||||
import { main as initMain } from './index';
|
||||
import { makeCreatorCommanderOptions, replaceTokens, runCommand } from './util';
|
||||
|
||||
export { creatorDefaults };
|
||||
|
||||
const procArgv = process.argv.slice();
|
||||
|
||||
export function makeCreatorMain(creator = creatorDefaults) {
|
||||
if (!Array.isArray(creator.subcreator.command)) {
|
||||
creator.subcreator.command = [creator.subcreator.command];
|
||||
}
|
||||
|
||||
async function handleCliOptions(
|
||||
cliOptions,
|
||||
{ argv, promise, reject, resolve } = {}
|
||||
) {
|
||||
setOrDetectLocale(cliOptions.locale);
|
||||
|
||||
const npxCmd = process.platform === 'win32' ? 'npx.cmd': 'npx';
|
||||
|
||||
console.log('hi from handleCliOptions in creator.js');
|
||||
|
||||
// let error = new Error('bad stuff happened in creator.js');
|
||||
// if (reject) return reject(error);
|
||||
// throw error;
|
||||
if (resolve) return resolve(19);
|
||||
return 19;
|
||||
|
||||
// should display command/s that will be run, similar to what the release
|
||||
// script does, so it's easier to figure out what's happening and how
|
||||
// options are combining
|
||||
|
||||
// are there prompts?
|
||||
|
||||
// if so, and --yes was NOT spec'd then run the prompts and upate options
|
||||
// with answers
|
||||
|
||||
// --yes should apply to creator and init, but not to subcreator; if
|
||||
// subcreator has a --yes option then it should be sepc'd with `-- --yes`
|
||||
// ...could support --<subshortname>-yes
|
||||
|
||||
// support a --cra-version options
|
||||
|
||||
// support all options embark-init supports and forward them to embark-init
|
||||
// if a project name isn't specified then pass '.' to create-react-app the
|
||||
// embark-init options should be an export from embark-init, that way the
|
||||
// creator doesn't have to "know about" specifics of embark-init
|
||||
|
||||
// if `--` was spec'd then ignore subcreator.options, consider it an
|
||||
// implicit --yes to prompts and supply options that followed `--`
|
||||
|
||||
// ^ should probably have an --init-prompts for the creator that can
|
||||
// combine with `--yes` and `--`
|
||||
|
||||
// let subp = spawn(npxCmd, [
|
||||
// ...creator.subcreator.command,
|
||||
// ...creator.subcreator.options
|
||||
// ], {
|
||||
// stdio: 'inherit'
|
||||
// });
|
||||
|
||||
// let _reject, _resolve;
|
||||
// let promise = new Promise((res, rej) => { _resolve = res; _reject = rej; });
|
||||
|
||||
// subp.on('error', error => { _reject(error); });
|
||||
// subp.on('exit', code => { _resolve(code); });
|
||||
|
||||
// try {
|
||||
// const code = await promise;
|
||||
// if (code) {
|
||||
// if (resolve) return resolve(code);
|
||||
// return code;
|
||||
// }
|
||||
// } catch (error) {
|
||||
// if (reject) return reject(error);
|
||||
// throw error;
|
||||
// }
|
||||
|
||||
// process init.options and build an array that combines/overrides re:
|
||||
// matching options spec'd in the creator's cli
|
||||
const initCliArgs = [];
|
||||
|
||||
// should report what final options get forwarded to embark-init
|
||||
return initMain(null, initCliArgs, {promise, reject, resolve});
|
||||
}
|
||||
|
||||
function cli(
|
||||
program,
|
||||
{
|
||||
argv = [],
|
||||
promise,
|
||||
reject,
|
||||
resolve
|
||||
} = {}
|
||||
) {
|
||||
if (!program) {
|
||||
program = new Command();
|
||||
program.version(creator.version);
|
||||
}
|
||||
program.description(
|
||||
`Creates a new Embark dapp using ${creator.subcreator.name}`
|
||||
);
|
||||
program.usage(`[options] [-- [${creator.subcreator.abbrev}-options]]`);
|
||||
// console.log('before replace:', require('util').inspect(creator, {depth: null}));
|
||||
creator = replaceTokens(creator);
|
||||
// console.log('after replace:', require('util').inspect(creator, {depth: null}));
|
||||
makeCreatorCommanderOptions(creator).forEach(opt => {
|
||||
program.option(...opt);
|
||||
});
|
||||
program.action((...args) => handleCliOptions(
|
||||
args, { argv, promise, reject, resolve }
|
||||
));
|
||||
return program;
|
||||
}
|
||||
|
||||
return function creatorMain(
|
||||
program,
|
||||
argv = procArgv,
|
||||
{ promise, reject, resolve } = {}
|
||||
) {
|
||||
if (!(promise && reject && resolve)) {
|
||||
promise = new Promise((res, rej) => { resolve = res; reject = rej; });
|
||||
}
|
||||
program = cli(program, { argv: argv.slice(), promise, reject, resolve });
|
||||
program.parse(argv);
|
||||
return promise;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
import { __ } from 'embark-i18n';
|
||||
import { join } from 'path';
|
||||
|
||||
const pkgJsonPath = join(__dirname, "..", "package.json");
|
||||
const pkgJson = require(pkgJsonPath);
|
||||
|
||||
const contractsOnlyPreset = '@embarklabs/dapps-presets-init-boilerplate';
|
||||
const defaultPreset = '@embarklabs/dapps-presets-init-boilerplate';
|
||||
|
||||
const defaultInitContractsOnlyPreset = `${contractsOnlyPreset}@${pkgJson.devDependencies[contractsOnlyPreset]}`;
|
||||
const defaultInitDefaultPreset = `${defaultPreset}@${pkgJson.devDependencies[defaultPreset]}`;
|
||||
|
||||
const defaultInitOptions = {
|
||||
contractsOnly: {
|
||||
description: [
|
||||
__('create a barebones project meant only for contract development'),
|
||||
',',
|
||||
'\n ',
|
||||
__('alias for %s', '--preset {{init.presets.contractsOnly}}')
|
||||
].join(''),
|
||||
long: 'contracts-only'
|
||||
},
|
||||
|
||||
force: {
|
||||
description: __('overwrite existing files'),
|
||||
long: 'force',
|
||||
short: 'f'
|
||||
},
|
||||
|
||||
locale: {
|
||||
default: 'en',
|
||||
description: __('language to use'),
|
||||
long: 'locale [locale]'
|
||||
},
|
||||
|
||||
preset: {
|
||||
default: `{{init.presets.default}}`,
|
||||
description: [
|
||||
__('preset to use'),
|
||||
', ',
|
||||
__('can be any valid package specifier'),
|
||||
'\n '
|
||||
].join(''),
|
||||
long: 'preset [pkg]',
|
||||
short: 'p'
|
||||
},
|
||||
|
||||
simple: {
|
||||
description: __('alias for %s', '--contracts-only'),
|
||||
long: 'simple'
|
||||
},
|
||||
|
||||
template: {
|
||||
description: __('alias for %s', '--preset [pkg]'),
|
||||
long: 'template [pkg]'
|
||||
},
|
||||
|
||||
yarn: {
|
||||
description: __('use yarn instead of npm'),
|
||||
long: 'yarn'
|
||||
},
|
||||
|
||||
yes: {
|
||||
description: __('skip prompts, accept defaults for unspecified options'),
|
||||
long: 'yes',
|
||||
short: 'y'
|
||||
}
|
||||
};
|
||||
|
||||
export const initDefaults = {
|
||||
options: defaultInitOptions,
|
||||
presets: {
|
||||
contractsOnly: defaultInitContractsOnlyPreset,
|
||||
default: defaultInitDefaultPreset
|
||||
}
|
||||
};
|
||||
|
||||
const defaultCreatorDemoPreset = null;
|
||||
const defaultCreatorExtraHelp = null;
|
||||
|
||||
const defaultCreatorOptions = {
|
||||
demo: {
|
||||
description: [
|
||||
__('create a working dapp with a SimpleStorage contract'),
|
||||
', ',
|
||||
__('alias for %s', '--preset {{init.presets.demo}}')
|
||||
].join(''),
|
||||
long: 'demo'
|
||||
},
|
||||
|
||||
noInit: {
|
||||
description: [
|
||||
__('only run %s', '{{subcreator.name}}'),
|
||||
', ',
|
||||
__('do not initialize embark-related files')
|
||||
].join(''),
|
||||
long: 'no-init'
|
||||
},
|
||||
|
||||
overrideSubcreator: {
|
||||
default: '{{subcreator.package}}',
|
||||
description: [
|
||||
__('specify the %s', '{{subcreator.name}}'),
|
||||
' ',
|
||||
__('package'),
|
||||
', ',
|
||||
__('can be any valid package specifier'),
|
||||
', e.g. ',
|
||||
__('to use a different version or fork')
|
||||
].join(''),
|
||||
long: '{{subcreator.abbrev}}-package [pkg]'
|
||||
}
|
||||
};
|
||||
|
||||
const defaultSubcreatorAbbrev = null;
|
||||
const defaultSubcreatorCommand = [];
|
||||
const defaultSubcreatorName = null;
|
||||
const defaultSubcreatorOptions = [];
|
||||
const defaultSubcreatorPackage = null;
|
||||
const defaultSubcreatorVersion = null;
|
||||
|
||||
const subcreatorDefaults = {
|
||||
abbrev: defaultSubcreatorAbbrev,
|
||||
command: defaultSubcreatorCommand,
|
||||
name: defaultSubcreatorName,
|
||||
options: defaultSubcreatorOptions,
|
||||
package: defaultSubcreatorPackage,
|
||||
version: defaultSubcreatorVersion
|
||||
};
|
||||
|
||||
export const creatorDefaults = {
|
||||
extraHelp: defaultCreatorExtraHelp,
|
||||
init: {
|
||||
options: initDefaults.options,
|
||||
presets: {
|
||||
demo: defaultCreatorDemoPreset,
|
||||
...initDefaults.presets
|
||||
}
|
||||
},
|
||||
options: defaultCreatorOptions,
|
||||
subcreator: subcreatorDefaults,
|
||||
version: null
|
||||
};
|
|
@ -0,0 +1,153 @@
|
|||
/* global __dirname process require */
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import { Command } from 'commander';
|
||||
import { __, setOrDetectLocale } from 'embark-i18n';
|
||||
import minimist from 'minimist';
|
||||
import { join } from 'path';
|
||||
|
||||
import { initDefaults } from './defaults';
|
||||
import { makeInitCommanderOptions, replaceTokens, runCommand } from './util';
|
||||
|
||||
export { initDefaults };
|
||||
export * from './creator';
|
||||
|
||||
const pkgJsonPath = join(__dirname, "..", "package.json");
|
||||
const pkgJson = require(pkgJsonPath);
|
||||
const version = pkgJson.version;
|
||||
|
||||
async function handleCliOptions(
|
||||
cliOptions,
|
||||
{ argv, init, promise, reject, resolve } = {}
|
||||
) {
|
||||
setOrDetectLocale(cliOptions.locale);
|
||||
|
||||
const npxCmd = process.platform === 'win32' ? 'npx.cmd': 'npx';
|
||||
|
||||
console.log('hi from handleCliOptions in embark-init');
|
||||
|
||||
// let error = new Error('bad stuff happened in embark-init');
|
||||
// if (reject) return reject(error);
|
||||
// throw error;
|
||||
// if (resolve) return resolve(19);
|
||||
// return 19;
|
||||
|
||||
// should display command/s that will be run, similar to what the release
|
||||
// script does, so it's easier to figure out what's happening and how options
|
||||
// are combining
|
||||
|
||||
// was a packge-command given as a positional?
|
||||
|
||||
// if so, ignore LHS options and:
|
||||
// `npx [package-command] [--RHS-opts] -- [--CREATOR-opts]`
|
||||
// BUT first resolve [package-command]:
|
||||
// first choice: embark-create-[package-command]
|
||||
// fallback: create-[package-command]
|
||||
|
||||
// ^ embark-init should check if it is in the monorepo and attempt to find
|
||||
// the creator package inside the monorepo and invoke its bin directly
|
||||
// instead of using npx, though npx can be a fallback
|
||||
|
||||
// if not...
|
||||
// are there prompts?
|
||||
|
||||
// if so, and --yes was NOT spec'd then run the prompts and upate options
|
||||
// with answers
|
||||
|
||||
// presets should (somehow) be able to contain metadata that will affect
|
||||
// creator and subcreator options, e.g. `--cra-version` and `-- --typescript`
|
||||
|
||||
// hydrate the preset, which may involve additional prompts
|
||||
// ?? maybe all init prompts should be in presets w/ no preliminary prompts
|
||||
// spec'able to main ??
|
||||
|
||||
// the idea is to attempt to reuse Vue's presets system as much as possible,
|
||||
// if necessary repurposing the source code as sources in embark-init, but
|
||||
// hopefully it's flexible enough to be leveraged with copying/modifying the
|
||||
// source code
|
||||
}
|
||||
|
||||
export function cli(
|
||||
program,
|
||||
{
|
||||
argv = [],
|
||||
init = initDefaults,
|
||||
promise,
|
||||
reject,
|
||||
resolve
|
||||
} = {}
|
||||
) {
|
||||
if (!program) {
|
||||
program = new Command();
|
||||
program.version(version);
|
||||
}
|
||||
program.description('Initializes a project as an Embark dapp');
|
||||
program.usage('[options] [creator] [creator-options] [-- [extra-options]]');
|
||||
({init} = replaceTokens({init}));
|
||||
makeInitCommanderOptions(init).forEach(opt => { program.option(...opt); });
|
||||
program.action((...args) => handleCliOptions(
|
||||
args, { argv, init, promise, reject, resolve }
|
||||
));
|
||||
program.on('--help', () => {
|
||||
let embarkInit = 'embark-init';
|
||||
if (program.parent) embarkInit = 'embark init';
|
||||
|
||||
console.log('');
|
||||
console.log('SIMPLE USAGE:');
|
||||
console.log('');
|
||||
console.log(' ', `${embarkInit} [options]`);
|
||||
console.log('');
|
||||
console.log(
|
||||
'Initialization is performed in the current working directory;',
|
||||
'will first run \`npm init\` if no package.json is present.'
|
||||
);
|
||||
|
||||
console.log('');
|
||||
console.log('WITH CREATOR:');
|
||||
console.log('');
|
||||
console.log(' ', `${embarkInit} [creator] [options] [-- [extra-options]]`);
|
||||
console.log('');
|
||||
console.log(
|
||||
'A creator is a name such as "react-dapp".',
|
||||
'Options to the left of the creator are ignored.',
|
||||
'Any options to the right of "--" are passed to the underlying tool,',
|
||||
'e.g. create-react-app, and will override conflicting options computed by the creator.'
|
||||
);
|
||||
console.log('');
|
||||
console.log(
|
||||
'Creator names are resolved to packages by prepending "@embarklabs/create-", then trying with "embark-", and finally using the bare name.'
|
||||
);
|
||||
console.log('');
|
||||
console.log(
|
||||
'Example: "react-dapp" resolves to "@embarklabs/create-react-dapp", and the following are equivalent:'
|
||||
);
|
||||
console.log('');
|
||||
console.log(' ', `${embarkInit} react-dapp mydapp [options] [-- [extra-options]]`);
|
||||
console.log('');
|
||||
console.log(' ', `npx @embarklabs/crete-react-dapp mydapp [options] [-- [extra-options]]`);
|
||||
console.log('');
|
||||
console.log('To see the help output of a creator do:');
|
||||
console.log('');
|
||||
console.log(' ', `${embarkInit} [creator] --help`);
|
||||
console.log('');
|
||||
console.log('To see the help output of a creator\'s underlying tool do:');
|
||||
console.log('');
|
||||
console.log(' ', `${embarkInit} [creator] -- --help`);
|
||||
});
|
||||
return program;
|
||||
}
|
||||
|
||||
const procArgv = process.argv.slice();
|
||||
|
||||
export function main(
|
||||
program,
|
||||
argv = procArgv,
|
||||
{ promise, reject, resolve } = {}
|
||||
) {
|
||||
if (!(promise && reject && resolve)) {
|
||||
promise = new Promise((res, rej) => { resolve = res; reject = rej; });
|
||||
}
|
||||
program = cli(program, { argv: argv.slice(), promise, reject, resolve });
|
||||
program.parse(argv);
|
||||
return promise;
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
import chalk from 'chalk';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
import isPlainObject from 'lodash.isplainobject';
|
||||
|
||||
import { creatorDefaults } from './defaults';
|
||||
|
||||
const cyan = (str) => chalk.cyan(str);
|
||||
const log = (mark, str, which = 'log') => console[which](
|
||||
mark, str.filter(s => !!s).join(` `)
|
||||
);
|
||||
export const logError = (...str) => log(chalk.red(`✘`), str, 'error');
|
||||
export const logInfo = (...str) => log(chalk.blue(`ℹ`), str);
|
||||
export const logSuccess = (...str) => log(chalk.green(`✔`), str);
|
||||
export const logWarning = (...str) => log(chalk.yellow('‼︎'), str);
|
||||
|
||||
export function makeInitCommanderOptions({options}) {
|
||||
return Object.values(options)
|
||||
.sort(({long: aLong}, {long: bLong}) => {
|
||||
if (aLong < bLong) return -1;
|
||||
if (aLong > bLong) return 1;
|
||||
return 0;
|
||||
})
|
||||
.reduce((acc, {default: def, description, long, short}) => {
|
||||
const copt = [];
|
||||
if (short) {
|
||||
copt.push(`-${short}, --${long}`);
|
||||
} else {
|
||||
copt.push(`--${long}`);
|
||||
}
|
||||
copt.push(description);
|
||||
if (def) copt.push(def);
|
||||
return acc.concat([copt]);
|
||||
}, []);
|
||||
}
|
||||
|
||||
export function makeCreatorCommanderOptions(creator) {
|
||||
return makeInitCommanderOptions({
|
||||
options: {
|
||||
...creator.init.options,
|
||||
...creator.options
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// !!! needs refactor because intend to use spawn
|
||||
export function runCommand(cmd, inherit = true, display) {
|
||||
logInfo(`Running command ${cyan(display || cmd)}.`);
|
||||
let out;
|
||||
if (inherit) {
|
||||
// execSyncInherit(cmd);
|
||||
} else {
|
||||
// out = execSync(cmd);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// NOTE: does not detect/handle recursive references, e.g. `{{foo}}` refers to
|
||||
// `{{bar}}` and `{{bar}}` refers to `{{foo}}`, but assume for now that won't
|
||||
// be a common pitfall
|
||||
export function replaceTokens(obj) {
|
||||
obj = cloneDeep(obj);
|
||||
let dict = {};
|
||||
|
||||
let entries = Object.entries(obj).map(
|
||||
([key, value]) => [key, value, '', 'object']
|
||||
);
|
||||
while (entries.length) {
|
||||
let [key, value, level, kind] = entries.shift();
|
||||
let nextLevel;
|
||||
if (kind === 'object') {
|
||||
nextLevel = `${level}${level ? '.' : ''}${key}`;
|
||||
} else if (kind === 'array') {
|
||||
nextLevel = `${level}[${key}]`;
|
||||
}
|
||||
if (typeof value === 'number') value = value.toString();
|
||||
if (typeof value !== 'string') {
|
||||
if (isPlainObject(value)) {
|
||||
const nextKind = 'object';
|
||||
entries.unshift(...(Object.entries(value).map(
|
||||
([key, value]) => [key, value, nextLevel, nextKind]
|
||||
)));
|
||||
} else if (Array.isArray(value)) {
|
||||
const nextKind = 'array';
|
||||
entries.unshift(...(value.map(
|
||||
(value, index) => [index, value, nextLevel, nextKind]
|
||||
)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const token = nextLevel;
|
||||
dict[token] = value;
|
||||
}
|
||||
|
||||
const unknowns = [];
|
||||
Object.values(dict).forEach(value => {
|
||||
const matches = value.match(/\{\{([^}]+)\}\}/g);
|
||||
if (!matches) return;
|
||||
unknowns.push(...matches.map(
|
||||
curly => curly.match(/\{\{\s*(\S+)\s*\}\}/)[1]
|
||||
).filter(
|
||||
token => !dict.hasOwnProperty(token)
|
||||
));
|
||||
});
|
||||
|
||||
if (unknowns.length) {
|
||||
throw new Error(
|
||||
`Unknown token${unknowns.length > 1 ? 's' : ''} ${unknowns.join(', ')}`
|
||||
);
|
||||
}
|
||||
|
||||
const keys = Object.keys(dict);
|
||||
const dependencies = {};
|
||||
const dependents = {};
|
||||
while (keys.length) {
|
||||
const key = keys.shift();
|
||||
if (!dependencies[key]) dependencies[key] = new Set();
|
||||
if (!dependents[key]) dependents[key] = new Set();
|
||||
let unresolvedDep;
|
||||
const matches = dict[key].match(/\{\{([^}]+)\}\}/g);
|
||||
if (matches) {
|
||||
matches
|
||||
.map(curly => curly.match(/\{\{\s*(\S+)\s*\}\}/)[1])
|
||||
.forEach(token => {
|
||||
if (!dependents[token]) dependents[token] = new Set();
|
||||
dependents[token].add(key);
|
||||
dependencies[key].add(token);
|
||||
});
|
||||
[...dependencies[key]].forEach(dependency => {
|
||||
if (dependents[dependency] && dependents[dependency].has(key)) {
|
||||
throw new Error(
|
||||
`circular dependency between tokens ${key} and ${dependency}`
|
||||
);
|
||||
}
|
||||
if (dict[dependency].match(/\{\{([^}]+)\}\}/g)) {
|
||||
if (!unresolvedDep) {
|
||||
unresolvedDep = true;
|
||||
keys.push(key);
|
||||
}
|
||||
} else {
|
||||
const token = `{{${dependency}}}`;
|
||||
let value = dict[key];
|
||||
let replaced;
|
||||
while (true) {
|
||||
replaced = value.replace(token, dict[dependency]);
|
||||
if (replaced === value) break;
|
||||
value = replaced;
|
||||
}
|
||||
dict[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
entries = Object.entries(obj).map(([key, value]) => [key, value, obj]);
|
||||
while (entries.length) {
|
||||
let [key, value, context] = entries.shift();
|
||||
const nextContext = value;
|
||||
if (isPlainObject(value)) {
|
||||
entries.unshift(...(Object.entries(value).map(
|
||||
([key, value]) => [key, value, nextContext]
|
||||
)));
|
||||
continue;
|
||||
} else if (Array.isArray(value)) {
|
||||
entries.unshift(...(value.map(
|
||||
(value, index) => [index, value, nextContext]
|
||||
)));
|
||||
continue;
|
||||
} else if (typeof value === 'string') {
|
||||
Object.entries(dict).forEach(([token, tokenVal]) => {
|
||||
token = `{{${token}}}`;
|
||||
let replaced;
|
||||
while (true) {
|
||||
replaced = value.replace(token, tokenVal);
|
||||
if (replaced === value) break;
|
||||
value = replaced;
|
||||
}
|
||||
context[key] = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
|
@ -10,6 +10,11 @@ const embarkBinPath = path.resolve(
|
|||
);
|
||||
if (!fs.existsSync(embarkBinPath)) process.exit(1);
|
||||
|
||||
const embarkInitBinPath = path.resolve(
|
||||
path.join(__dirname, '../packages/utils/init/bin/init')
|
||||
);
|
||||
if (!fs.existsSync(embarkInitBinPath)) process.exit(1);
|
||||
|
||||
const getStdout = (cmd) => {
|
||||
let out;
|
||||
try {
|
||||
|
@ -30,8 +35,16 @@ if (process.platform === 'win32') {
|
|||
path.join(npmGlobalBin, 'embark.cmd'),
|
||||
`@node "${embarkBinPath}" %*${EOL}`
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(npmGlobalBin, 'embark-init.cmd'),
|
||||
`@node "${embarkInitBinPath}" %*${EOL}`
|
||||
);
|
||||
} else {
|
||||
const linkPath = path.join(npmGlobalBin, 'embark');
|
||||
let linkPath = path.join(npmGlobalBin, 'embark');
|
||||
if (fs.existsSync(linkPath)) fs.unlinkSync(linkPath);
|
||||
fs.symlinkSync(embarkBinPath, linkPath);
|
||||
|
||||
linkPath = path.join(npmGlobalBin, 'embark-init');
|
||||
if (fs.existsSync(linkPath)) fs.unlinkSync(linkPath);
|
||||
fs.symlinkSync(embarkInitBinPath, linkPath);
|
||||
}
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -7511,16 +7511,16 @@ commander@2.18.0:
|
|||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970"
|
||||
integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==
|
||||
|
||||
commander@2.20.3, commander@^2.11.0, commander@^2.12.1, commander@^2.13.0, commander@^2.15.0, commander@^2.19.0, commander@^2.20.0, commander@^2.9.0, commander@~2.20.0:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
commander@3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
|
||||
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
|
||||
|
||||
commander@^2.11.0, commander@^2.12.1, commander@^2.13.0, commander@^2.15.0, commander@^2.19.0, commander@^2.20.0, commander@^2.9.0, commander@~2.20.0:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
commander@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.0.1.tgz#b67622721785993182e807f4883633e6401ba53c"
|
||||
|
|
Loading…
Reference in New Issue