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": {
|
"workspaces": {
|
||||||
"packages": [
|
"packages": [
|
||||||
|
"dapps/creators/*",
|
||||||
"dapps/templates/*",
|
"dapps/templates/*",
|
||||||
"dapps/tests/*",
|
"dapps/tests/*",
|
||||||
"packages/*",
|
"packages/*",
|
||||||
|
|
|
@ -82,6 +82,7 @@
|
||||||
"embark-engine": "^5.2.2",
|
"embark-engine": "^5.2.2",
|
||||||
"embark-graph": "^5.2.0",
|
"embark-graph": "^5.2.0",
|
||||||
"embark-i18n": "^5.1.1",
|
"embark-i18n": "^5.1.1",
|
||||||
|
"embark-init": "^5.2.0",
|
||||||
"embark-logger": "^5.2.0",
|
"embark-logger": "^5.2.0",
|
||||||
"embark-reset": "^5.1.1",
|
"embark-reset": "^5.1.1",
|
||||||
"embark-suggestions": "^5.2.2",
|
"embark-suggestions": "^5.2.2",
|
||||||
|
|
|
@ -992,6 +992,7 @@ function isDappCmd(cmd) {
|
||||||
'--help',
|
'--help',
|
||||||
'new',
|
'new',
|
||||||
'demo',
|
'demo',
|
||||||
|
'init',
|
||||||
'version',
|
'version',
|
||||||
'help'
|
'help'
|
||||||
].indexOf(cmd) === -1;
|
].indexOf(cmd) === -1;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { __, setOrDetectLocale } from 'embark-i18n';
|
import { __, setOrDetectLocale } from 'embark-i18n';
|
||||||
|
import { cli as initCli } from 'embark-init';
|
||||||
import { diagramPath } from 'embark-utils';
|
import { diagramPath } from 'embark-utils';
|
||||||
const program = require('commander');
|
const program = require('commander');
|
||||||
const EmbarkController = require('./cmd_controller.js');
|
const EmbarkController = require('./cmd_controller.js');
|
||||||
|
@ -13,6 +14,7 @@ class Cmd {
|
||||||
process(args) {
|
process(args) {
|
||||||
this.newApp();
|
this.newApp();
|
||||||
this.demo();
|
this.demo();
|
||||||
|
this.init();
|
||||||
this.build();
|
this.build();
|
||||||
this.run();
|
this.run();
|
||||||
this.exec();
|
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() {
|
helpCmd() {
|
||||||
program
|
program
|
||||||
.command('help')
|
.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);
|
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) => {
|
const getStdout = (cmd) => {
|
||||||
let out;
|
let out;
|
||||||
try {
|
try {
|
||||||
|
@ -30,8 +35,16 @@ if (process.platform === 'win32') {
|
||||||
path.join(npmGlobalBin, 'embark.cmd'),
|
path.join(npmGlobalBin, 'embark.cmd'),
|
||||||
`@node "${embarkBinPath}" %*${EOL}`
|
`@node "${embarkBinPath}" %*${EOL}`
|
||||||
);
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(npmGlobalBin, 'embark-init.cmd'),
|
||||||
|
`@node "${embarkInitBinPath}" %*${EOL}`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
const linkPath = path.join(npmGlobalBin, 'embark');
|
let linkPath = path.join(npmGlobalBin, 'embark');
|
||||||
if (fs.existsSync(linkPath)) fs.unlinkSync(linkPath);
|
if (fs.existsSync(linkPath)) fs.unlinkSync(linkPath);
|
||||||
fs.symlinkSync(embarkBinPath, 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"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970"
|
||||||
integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==
|
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:
|
commander@3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
|
||||||
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
|
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:
|
commander@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.0.1.tgz#b67622721785993182e807f4883633e6401ba53c"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-4.0.1.tgz#b67622721785993182e807f4883633e6401ba53c"
|
||||||
|
|
Loading…
Reference in New Issue