WIP: first steps

This commit is contained in:
Michael Bradley, Jr 2020-02-18 14:25:05 -06:00
parent e271cebbe3
commit 1931f78a2d
19 changed files with 872 additions and 6 deletions

View File

@ -0,0 +1,4 @@
engine-strict = true
package-lock = false
save-exact = true
scripts-prepend-node-path = true

View File

@ -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).

13
dapps/creators/react/bin/create Executable file
View File

@ -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);
});

View File

@ -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"
}
}

View File

@ -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);

View File

@ -86,6 +86,7 @@
}, },
"workspaces": { "workspaces": {
"packages": [ "packages": [
"dapps/creators/*",
"dapps/templates/*", "dapps/templates/*",
"dapps/tests/*", "dapps/tests/*",
"packages/*", "packages/*",

View File

@ -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",

View File

@ -992,6 +992,7 @@ function isDappCmd(cmd) {
'--help', '--help',
'new', 'new',
'demo', 'demo',
'init',
'version', 'version',
'help' 'help'
].indexOf(cmd) === -1; ].indexOf(cmd) === -1;

View File

@ -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')

View File

@ -0,0 +1,4 @@
engine-strict = true
package-lock = false
save-exact = true
scripts-prepend-node-path = true

View File

@ -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).

13
packages/utils/init/bin/init Executable file
View File

@ -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);
});

View File

@ -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"
}
}

View File

@ -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;
};
}

View File

@ -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
};

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
} }

View File

@ -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"