refactor: CLI
This commit is contained in:
parent
73f36ab3c0
commit
54d46ec04c
|
@ -65,6 +65,7 @@
|
||||||
"eslint": "^7.0.0",
|
"eslint": "^7.0.0",
|
||||||
"eslint-import-resolver-webpack": "0.13.0",
|
"eslint-import-resolver-webpack": "0.13.0",
|
||||||
"js-yaml": "3.14.0",
|
"js-yaml": "3.14.0",
|
||||||
|
"jsonparse": "^1.3.1",
|
||||||
"lodash.flatten": "^4.4.0",
|
"lodash.flatten": "^4.4.0",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.intersection": "^4.4.0",
|
"lodash.intersection": "^4.4.0",
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {join, resolve, sep} from 'path';
|
||||||
import {Command} from 'commander';
|
import {Command} from 'commander';
|
||||||
|
|
||||||
import D from './debug';
|
import D from './debug';
|
||||||
|
import {processCode} from './server/commands';
|
||||||
import Flecks from './server/flecks';
|
import Flecks from './server/flecks';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -63,17 +64,16 @@ else {
|
||||||
process.exitCode = child;
|
process.exitCode = child;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const reject = (error) => {
|
try {
|
||||||
|
const code = await processCode(child);
|
||||||
|
debug('action exited with code %d', code);
|
||||||
|
process.exitCode = code;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(error);
|
console.error(error);
|
||||||
process.exitCode = child.exitCode || 1;
|
process.exitCode = child.exitCode || 1;
|
||||||
};
|
}
|
||||||
child.on('error', reject);
|
|
||||||
child.on('exit', (code) => {
|
|
||||||
child.off('error', reject);
|
|
||||||
debug('action exited with code %d', code);
|
|
||||||
process.exitCode = code;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
// Initialize Commander.
|
// Initialize Commander.
|
||||||
const program = new Command();
|
const program = new Command();
|
||||||
|
|
|
@ -14,12 +14,23 @@ const {
|
||||||
const debug = D('@flecks/core/commands');
|
const debug = D('@flecks/core/commands');
|
||||||
const flecksRoot = normalize(FLECKS_CORE_ROOT);
|
const flecksRoot = normalize(FLECKS_CORE_ROOT);
|
||||||
|
|
||||||
export const spawnWith = (cmd, localEnv, spawnArgs) => {
|
export const processCode = (child) => new Promise((resolve, reject) => {
|
||||||
debug('spawning:\n%s %s\nwith local environment: %O', cmd, spawnArgs.join(' '), localEnv);
|
child.on('error', reject);
|
||||||
const spawnOptions = {
|
child.on('exit', (code) => {
|
||||||
env: {...localEnv, ...process.env},
|
child.off('error', reject);
|
||||||
};
|
resolve(code);
|
||||||
const child = spawn('npx', [cmd, ...spawnArgs], spawnOptions);
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export const spawnWith = (cmd, opts = {}) => {
|
||||||
|
debug("spawning: '%s' with options: %O", cmd.join(' '), opts);
|
||||||
|
const child = spawn(cmd[0], cmd.slice(1), {
|
||||||
|
...opts,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
...(opts.env || {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
child.stderr.pipe(process.stderr);
|
child.stderr.pipe(process.stderr);
|
||||||
child.stdout.pipe(process.stdout);
|
child.stdout.pipe(process.stdout);
|
||||||
return child;
|
return child;
|
||||||
|
@ -97,19 +108,24 @@ export default (program, flecks) => {
|
||||||
} = opts;
|
} = opts;
|
||||||
debug('Building...', opts);
|
debug('Building...', opts);
|
||||||
const webpackConfig = flecks.localConfig('webpack.config.js', '@flecks/core');
|
const webpackConfig = flecks.localConfig('webpack.config.js', '@flecks/core');
|
||||||
const localEnv = {
|
const cmd = [
|
||||||
...targetNeutrinos(flecks),
|
'npx', 'webpack',
|
||||||
...(target ? {FLECKS_CORE_BUILD_LIST: target} : {}),
|
|
||||||
...(hot ? {FLECKS_ENV_FLECKS_SERVER_hot: 'true'} : {}),
|
|
||||||
};
|
|
||||||
const spawnArgs = [
|
|
||||||
'--colors',
|
'--colors',
|
||||||
'--config', webpackConfig,
|
'--config', webpackConfig,
|
||||||
'--mode', (production && !hot) ? 'production' : 'development',
|
'--mode', (production && !hot) ? 'production' : 'development',
|
||||||
...(verbose ? ['--stats', 'verbose'] : []),
|
...(verbose ? ['--stats', 'verbose'] : []),
|
||||||
...((watch || hot) ? ['--watch'] : []),
|
...((watch || hot) ? ['--watch'] : []),
|
||||||
];
|
];
|
||||||
return spawnWith('webpack', localEnv, spawnArgs);
|
return spawnWith(
|
||||||
|
cmd,
|
||||||
|
{
|
||||||
|
env: {
|
||||||
|
...targetNeutrinos(flecks),
|
||||||
|
...(target ? {FLECKS_CORE_BUILD_LIST: target} : {}),
|
||||||
|
...(hot ? {FLECKS_ENV_FLECKS_SERVER_hot: 'true'} : {}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
commands.lint = {
|
commands.lint = {
|
||||||
|
@ -126,7 +142,8 @@ export default (program, flecks) => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
process.env.FLECKS_CORE_BUILD_TARGET = target;
|
process.env.FLECKS_CORE_BUILD_TARGET = target;
|
||||||
const spawnArgs = [
|
const cmd = [
|
||||||
|
'npx', 'eslint',
|
||||||
'--config', flecks.localConfig(
|
'--config', flecks.localConfig(
|
||||||
`${target}.eslintrc.js`,
|
`${target}.eslintrc.js`,
|
||||||
'@flecks/core',
|
'@flecks/core',
|
||||||
|
@ -136,12 +153,16 @@ export default (program, flecks) => {
|
||||||
'--ext', 'js',
|
'--ext', 'js',
|
||||||
'.',
|
'.',
|
||||||
];
|
];
|
||||||
const localEnv = {
|
promises.push(new Promise((resolve, reject) => {
|
||||||
|
const child = spawnWith(
|
||||||
|
cmd,
|
||||||
|
{
|
||||||
|
env: {
|
||||||
FLECKS_CORE_BUILD_TARGET: target,
|
FLECKS_CORE_BUILD_TARGET: target,
|
||||||
...targetNeutrinos(flecks),
|
...targetNeutrinos(flecks),
|
||||||
};
|
},
|
||||||
promises.push(new Promise((resolve, reject) => {
|
},
|
||||||
const child = spawnWith('eslint', localEnv, spawnArgs);
|
);
|
||||||
child.on('error', reject);
|
child.on('error', reject);
|
||||||
child.on('exit', (code) => {
|
child.on('exit', (code) => {
|
||||||
child.off('error', reject);
|
child.off('error', reject);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import R from '../bootstrap/require';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
default as commands,
|
default as commands,
|
||||||
|
processCode,
|
||||||
spawnWith,
|
spawnWith,
|
||||||
targetNeutrino,
|
targetNeutrino,
|
||||||
targetNeutrinos,
|
targetNeutrinos,
|
||||||
|
@ -13,6 +14,7 @@ export {
|
||||||
|
|
||||||
export {default as Flecks} from './flecks';
|
export {default as Flecks} from './flecks';
|
||||||
export {default as require} from '../bootstrap/require';
|
export {default as require} from '../bootstrap/require';
|
||||||
|
export {JsonStream, transform} from './stream';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
[Hooks]: {
|
[Hooks]: {
|
||||||
|
|
57
packages/core/src/server/stream.js
Normal file
57
packages/core/src/server/stream.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// eslint-disable-next-line max-classes-per-file
|
||||||
|
import JsonParse from 'jsonparse';
|
||||||
|
import {Transform} from 'stream';
|
||||||
|
|
||||||
|
export class JsonStream extends Transform {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
const self = this;
|
||||||
|
this.done = undefined;
|
||||||
|
this.parser = new JsonParse();
|
||||||
|
this.parser.onValue = function onValue(O) {
|
||||||
|
if (0 === this.stack.length) {
|
||||||
|
self.push(JSON.stringify(O));
|
||||||
|
self.done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
|
_transform(chunk, encoding, done) {
|
||||||
|
this.done = done;
|
||||||
|
this.parser.write(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonStream.PrettyPrint = class extends Transform {
|
||||||
|
|
||||||
|
constructor(indent = 2) {
|
||||||
|
super();
|
||||||
|
this.indent = indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
|
async _transform(chunk, encoding, done) {
|
||||||
|
this.push(JSON.stringify(JSON.parse(chunk), null, this.indent));
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export const transform = (fn, opts = {}) => {
|
||||||
|
class EasyTransform extends Transform {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-underscore-dangle, class-methods-use-this
|
||||||
|
_transform(chunk, encoding, done) {
|
||||||
|
fn(chunk, encoding, done, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return new EasyTransform();
|
||||||
|
};
|
|
@ -22,12 +22,16 @@
|
||||||
"files": [
|
"files": [
|
||||||
"cli.js",
|
"cli.js",
|
||||||
"cli.js.map",
|
"cli.js.map",
|
||||||
|
"server.js",
|
||||||
|
"server.js.map",
|
||||||
"src",
|
"src",
|
||||||
"template"
|
"template"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@flecks/core": "^1.1.1",
|
"@flecks/core": "^1.1.1",
|
||||||
"fs-extra": "10.0.0"
|
"glob": "^7.2.0",
|
||||||
|
"minimatch": "^5.0.1",
|
||||||
|
"validate-npm-package-name": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@flecks/fleck": "^1.1.1"
|
"@flecks/fleck": "^1.1.1"
|
||||||
|
|
9
packages/create-app/src/build.js
Normal file
9
packages/create-app/src/build.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import {processCode, spawnWith} from '@flecks/core/server';
|
||||||
|
|
||||||
|
export default async (cwd) => {
|
||||||
|
const code = await processCode(spawnWith(['yarn'], {cwd}));
|
||||||
|
if (0 !== code) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
return processCode(spawnWith(['yarn', 'build'], {cwd}));
|
||||||
|
};
|
|
@ -1,50 +1,36 @@
|
||||||
import {spawn} from 'child_process';
|
|
||||||
import {readFileSync, writeFileSync} from 'fs';
|
|
||||||
import {join, normalize} from 'path';
|
import {join, normalize} from 'path';
|
||||||
|
|
||||||
import {
|
import {Flecks} from '@flecks/core/server';
|
||||||
copySync,
|
import validate from 'validate-npm-package-name';
|
||||||
mkdirpSync,
|
|
||||||
moveSync,
|
|
||||||
} from 'fs-extra';
|
|
||||||
|
|
||||||
const cwd = normalize(process.cwd());
|
import build from './build';
|
||||||
|
import move from './move';
|
||||||
|
|
||||||
const forwardProcessCode = (fn) => async (...args) => {
|
const {
|
||||||
process.exitCode = await fn(args.slice(0, -2));
|
FLECKS_CORE_ROOT = process.cwd(),
|
||||||
};
|
} = process.env;
|
||||||
|
|
||||||
const processCode = (child) => new Promise((resolve, reject) => {
|
const cwd = normalize(FLECKS_CORE_ROOT);
|
||||||
child.on('error', reject);
|
|
||||||
child.on('exit', (code) => {
|
|
||||||
child.off('error', reject);
|
|
||||||
resolve(code);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const create = () => async () => {
|
const create = async (flecks) => {
|
||||||
const name = process.argv[2];
|
const name = process.argv[2];
|
||||||
const path = name.split('/').pop();
|
const {errors} = validate(name);
|
||||||
copySync(join(__dirname, 'template'), join(cwd, path), {recursive: true});
|
if (errors) {
|
||||||
mkdirpSync(join(cwd, path, 'packages'));
|
throw new Error(`@flecks/create-app: invalid app name: ${errors.join(', ')}`);
|
||||||
moveSync(join(cwd, path, '.gitignore.extraneous'), join(cwd, path, '.gitignore'));
|
|
||||||
moveSync(join(cwd, path, 'package.json.extraneous'), join(cwd, path, 'package.json'));
|
|
||||||
writeFileSync(
|
|
||||||
join(cwd, path, 'package.json'),
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
name: `@${name}/monorepo`,
|
|
||||||
...JSON.parse(readFileSync(join(cwd, path, 'package.json')).toString()),
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
const code = await processCode(spawn('yarn', [], {cwd: join(cwd, path), stdio: 'inherit'}));
|
|
||||||
if (0 !== code) {
|
|
||||||
return code;
|
|
||||||
}
|
}
|
||||||
return processCode(spawn('yarn', ['build'], {cwd: join(cwd, path), stdio: 'inherit'}));
|
const destination = join(cwd, name);
|
||||||
|
await move(name, join(__dirname, 'template'), destination, flecks);
|
||||||
|
await build(destination);
|
||||||
};
|
};
|
||||||
|
|
||||||
forwardProcessCode(create())();
|
(async () => {
|
||||||
|
const flecks = await Flecks.bootstrap();
|
||||||
|
try {
|
||||||
|
await create(flecks);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(error.message);
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
58
packages/create-app/src/move.js
Normal file
58
packages/create-app/src/move.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import {
|
||||||
|
stat,
|
||||||
|
} from 'fs/promises';
|
||||||
|
import {basename} from 'path';
|
||||||
|
|
||||||
|
import {JsonStream, transform} from '@flecks/core/server';
|
||||||
|
|
||||||
|
import FileTree from './tree';
|
||||||
|
|
||||||
|
const testDestination = async (destination) => {
|
||||||
|
try {
|
||||||
|
await stat(destination);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if ('ENOENT' !== error.code) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async (name, source, destination, flecks) => {
|
||||||
|
if (!await testDestination(destination)) {
|
||||||
|
const error = new Error(
|
||||||
|
`@flecks/create-fleck: destination '${destination} already exists: aborting`,
|
||||||
|
);
|
||||||
|
error.code = 129;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
const fileTree = await FileTree.loadFrom(source);
|
||||||
|
// Renamed to avoid conflicts.
|
||||||
|
const {files} = fileTree;
|
||||||
|
fileTree.glob('**/*.noconflict')
|
||||||
|
.forEach((path) => {
|
||||||
|
files[basename(path, '.noconflict')] = files[path];
|
||||||
|
delete files[path];
|
||||||
|
});
|
||||||
|
// Defaults.
|
||||||
|
flecks.set('@flecks/create-fleck.packager', flecks.get('@flecks/create-fleck.packager', ['...']));
|
||||||
|
// Send it out.
|
||||||
|
await flecks.invokeSequentialAsync('@flecks/create-fleck/packager', fileTree);
|
||||||
|
// Add project name to `package.json`.
|
||||||
|
fileTree.pipe(
|
||||||
|
'package.json',
|
||||||
|
transform((chunk, encoding, done, stream) => {
|
||||||
|
stream.push(JSON.stringify({name, ...JSON.parse(chunk)}));
|
||||||
|
done();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
// Pretty print all JSON.
|
||||||
|
fileTree.glob('**/*.json')
|
||||||
|
.forEach((path) => {
|
||||||
|
fileTree.pipe(path, new JsonStream.PrettyPrint());
|
||||||
|
});
|
||||||
|
// Write the tree.
|
||||||
|
await fileTree.writeTo(destination);
|
||||||
|
};
|
5
packages/create-app/src/server.js
Normal file
5
packages/create-app/src/server.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export {default as validate} from 'validate-npm-package-name';
|
||||||
|
|
||||||
|
export {default as build} from './build';
|
||||||
|
export {default as move} from './move';
|
||||||
|
export {default as FileTree} from './tree';
|
72
packages/create-app/src/tree.js
Normal file
72
packages/create-app/src/tree.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import {createReadStream, createWriteStream} from 'fs';
|
||||||
|
import {mkdir, stat} from 'fs/promises';
|
||||||
|
|
||||||
|
import glob from 'glob';
|
||||||
|
import minimatch from 'minimatch';
|
||||||
|
import {dirname, join} from 'path';
|
||||||
|
|
||||||
|
export default class FileTree {
|
||||||
|
|
||||||
|
constructor(files = {}) {
|
||||||
|
this.files = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
addDirectory(path) {
|
||||||
|
this.files[path] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
addFile(path, stream) {
|
||||||
|
this.files[path] = stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
glob(glob) {
|
||||||
|
return Object.keys(this.files).filter((path) => minimatch(path, glob, {dot: true}));
|
||||||
|
}
|
||||||
|
|
||||||
|
static async loadFrom(cwd) {
|
||||||
|
const paths = await new Promise((r, e) => {
|
||||||
|
glob(
|
||||||
|
'**/*',
|
||||||
|
{cwd, dot: true},
|
||||||
|
(error, paths) => (error ? e(error) : r(paths)),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return new FileTree(
|
||||||
|
await paths
|
||||||
|
.reduce(
|
||||||
|
async (r, path) => {
|
||||||
|
const origin = join(cwd, path);
|
||||||
|
const stats = await stat(origin);
|
||||||
|
return {
|
||||||
|
...await r,
|
||||||
|
[path]: stats.isDirectory() ? null : createReadStream(origin),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe(path, stream) {
|
||||||
|
this.files[path] = this.files[path] ? this.files[path].pipe(stream) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
async writeTo(destination) {
|
||||||
|
return Promise.all(
|
||||||
|
Object.entries(this.files)
|
||||||
|
.map(async ([path, stream]) => {
|
||||||
|
if (null === stream) {
|
||||||
|
return mkdir(path, {recursive: true});
|
||||||
|
}
|
||||||
|
await mkdir(dirname(join(destination, path)), {recursive: true});
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const writer = createWriteStream(join(destination, path));
|
||||||
|
writer.on('finish', resolve);
|
||||||
|
writer.on('error', reject);
|
||||||
|
stream.pipe(writer);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,4 @@
|
||||||
{
|
{
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
|
@ -14,11 +11,11 @@
|
||||||
"resolveSourceMapLocations": [],
|
"resolveSourceMapLocations": [],
|
||||||
"sourceMapPathOverrides": {
|
"sourceMapPathOverrides": {
|
||||||
"webpack:///*": "${workspaceFolder}/*",
|
"webpack:///*": "${workspaceFolder}/*",
|
||||||
"webpack://*": "node_modules/*",
|
"webpack://*": "node_modules/*"
|
||||||
},
|
},
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"runtimeExecutable": "npm",
|
"runtimeExecutable": "npm",
|
||||||
"runtimeArgs": ["run", "start"],
|
"runtimeArgs": ["run", "start"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -2,7 +2,10 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "flecks build",
|
"build": "flecks build",
|
||||||
"dev": "npm run -- build -h"
|
"debug": "DEBUG=*,-babel* npm run dev",
|
||||||
|
"dev": "npm run -- build -h",
|
||||||
|
"repl": "npx flecks repl --rlwrap",
|
||||||
|
"start": "DEBUG=@flecks*,-@flecks/core/flecks* npm run dev",
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@flecks/core": "^1.0.0",
|
"@flecks/core": "^1.0.0",
|
|
@ -27,8 +27,7 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@flecks/core": "^1.1.1",
|
"@flecks/core": "^1.1.1",
|
||||||
"fs-extra": "10.0.0",
|
"@flecks/create-app": "^1.1.1"
|
||||||
"validate-npm-package-name": "^3.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@flecks/fleck": "^1.1.1"
|
"@flecks/fleck": "^1.1.1"
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
import {spawn} from 'child_process';
|
import {stat} from 'fs/promises';
|
||||||
import {
|
|
||||||
readFileSync,
|
|
||||||
statSync,
|
|
||||||
writeFileSync,
|
|
||||||
} from 'fs';
|
|
||||||
import {join, normalize} from 'path';
|
import {join, normalize} from 'path';
|
||||||
|
|
||||||
import {copySync, moveSync} from 'fs-extra';
|
import {build, move, validate} from '@flecks/create-app/server';
|
||||||
import validate from 'validate-npm-package-name';
|
import {Flecks} from '@flecks/core/server';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
FLECKS_CORE_ROOT = process.cwd(),
|
FLECKS_CORE_ROOT = process.cwd(),
|
||||||
|
@ -15,97 +10,68 @@ const {
|
||||||
|
|
||||||
const cwd = normalize(FLECKS_CORE_ROOT);
|
const cwd = normalize(FLECKS_CORE_ROOT);
|
||||||
|
|
||||||
const forwardProcessCode = (fn) => async (...args) => {
|
const hasPackages = async (cwd) => {
|
||||||
process.exitCode = await fn(args.slice(0, -2));
|
try {
|
||||||
|
await stat(join(cwd, 'packages'));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if ('ENOENT' !== error.code) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const processCode = (child) => new Promise((resolve, reject) => {
|
const monorepoScope = async (cwd) => {
|
||||||
child.on('error', reject);
|
|
||||||
child.on('exit', (code) => {
|
|
||||||
child.off('error', reject);
|
|
||||||
resolve(code);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const monorepoScope = () => {
|
|
||||||
try {
|
try {
|
||||||
statSync(join(cwd, 'packages'));
|
|
||||||
const {name} = __non_webpack_require__(join(cwd, 'package.json'));
|
const {name} = __non_webpack_require__(join(cwd, 'package.json'));
|
||||||
const [scope] = name.split('/');
|
const [scope] = name.split('/');
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
if ('ENOENT' !== error.code) {
|
if ('MODULE_NOT_FOUND' !== error.code) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const testDestination = (destination) => {
|
const target = async (name) => {
|
||||||
try {
|
const {errors} = validate(name);
|
||||||
statSync(destination);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
if ('ENOENT' !== error.code) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const create = () => async () => {
|
|
||||||
const rawname = process.argv[2];
|
|
||||||
const {errors} = validate(rawname);
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
// eslint-disable-next-line no-console
|
throw new Error(`@flecks/create-fleck: invalid fleck name: ${errors.join(', ')}`);
|
||||||
console.error(`@flecks/create-fleck: invalid fleck name: ${errors.join(', ')}`);
|
|
||||||
return 128;
|
|
||||||
}
|
}
|
||||||
const parts = rawname.split('/');
|
const parts = name.split('/');
|
||||||
let path = cwd;
|
|
||||||
let pkg;
|
let pkg;
|
||||||
let scope;
|
let scope;
|
||||||
if (1 === parts.length) {
|
if (1 === parts.length) {
|
||||||
pkg = rawname;
|
pkg = name;
|
||||||
|
if (await hasPackages(cwd)) {
|
||||||
|
scope = await monorepoScope(cwd);
|
||||||
}
|
}
|
||||||
else {
|
return [scope, pkg];
|
||||||
[scope, pkg] = parts;
|
|
||||||
}
|
}
|
||||||
if (!scope) {
|
return parts;
|
||||||
scope = monorepoScope();
|
|
||||||
if (scope) {
|
|
||||||
path = join(path, 'packages');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const name = [scope, pkg].filter((e) => !!e).join('/');
|
|
||||||
const destination = join(path, pkg);
|
|
||||||
if (!testDestination(destination)) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(`@flecks/create-fleck: destination '${destination} already exists: aborting`);
|
|
||||||
return 129;
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line no-unreachable
|
|
||||||
copySync(join(__dirname, 'template'), destination, {recursive: true});
|
|
||||||
moveSync(join(destination, '.gitignore.extraneous'), join(destination, '.gitignore'));
|
|
||||||
moveSync(join(destination, 'package.json.extraneous'), join(destination, 'package.json'));
|
|
||||||
writeFileSync(
|
|
||||||
join(destination, 'package.json'),
|
|
||||||
JSON.stringify(
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
...JSON.parse(readFileSync(join(destination, 'package.json')).toString()),
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
const code = await processCode(spawn('yarn', [], {cwd: destination, stdio: 'inherit'}));
|
|
||||||
if (0 !== code) {
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
return processCode(spawn('yarn', ['build'], {cwd: destination, stdio: 'inherit'}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
forwardProcessCode(create())();
|
const create = async (flecks) => {
|
||||||
|
const [scope, pkg] = await target(process.argv[2]);
|
||||||
|
const path = scope && (await hasPackages(cwd)) ? join(cwd, 'packages') : cwd;
|
||||||
|
const name = [scope, pkg].filter((e) => !!e).join('/');
|
||||||
|
const destination = join(path, pkg);
|
||||||
|
await move(name, join(__dirname, 'template'), destination, flecks);
|
||||||
|
await build(destination);
|
||||||
|
};
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const flecks = await Flecks.bootstrap();
|
||||||
|
try {
|
||||||
|
await create(flecks);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(error.message);
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"test": "flecks test"
|
"test": "flecks test"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
"build",
|
||||||
"index.js",
|
"index.js",
|
||||||
"index.js.map",
|
"index.js.map",
|
||||||
"src",
|
"src",
|
|
@ -55,13 +55,13 @@ export default (program, flecks) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const spawnMocha = () => {
|
const spawnMocha = () => {
|
||||||
const localEnv = {};
|
const cmd = [
|
||||||
const spawnArgs = [
|
'npx', 'mocha',
|
||||||
'--colors',
|
'--colors',
|
||||||
'--reporter', 'min',
|
'--reporter', 'min',
|
||||||
testLocation,
|
testLocation,
|
||||||
];
|
];
|
||||||
return spawnWith('mocha', localEnv, spawnArgs);
|
return spawnWith(cmd);
|
||||||
};
|
};
|
||||||
if (!watch) {
|
if (!watch) {
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
|
|
|
@ -18,15 +18,20 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Otherwise, spawn `webpack-dev-server` (WDS).
|
// Otherwise, spawn `webpack-dev-server` (WDS).
|
||||||
const localEnv = {
|
const cmd = [
|
||||||
FLECKS_CORE_BUILD_LIST: 'http',
|
'npx', 'webpack-dev-server',
|
||||||
};
|
|
||||||
const spawnArgs = [
|
|
||||||
'--mode', 'development',
|
'--mode', 'development',
|
||||||
'--hot',
|
'--hot',
|
||||||
'--config', flecks.localConfig('webpack.config.js', '@flecks/core'),
|
'--config', flecks.localConfig('webpack.config.js', '@flecks/core'),
|
||||||
];
|
];
|
||||||
spawnWith('webpack-dev-server', localEnv, spawnArgs);
|
spawnWith(
|
||||||
|
cmd,
|
||||||
|
{
|
||||||
|
env: {
|
||||||
|
FLECKS_CORE_BUILD_LIST: 'http',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
// Remove the build config since we're handing off to WDS.
|
// Remove the build config since we're handing off to WDS.
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
delete neutrinoConfigs.http;
|
delete neutrinoConfigs.http;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user