├── README.md ├── packages ├── cli │ ├── README.md │ ├── .npmignore │ ├── tsconfig.build.json │ ├── src │ │ ├── lib │ │ │ ├── logo │ │ │ │ ├── small.ts │ │ │ │ └── large.ts │ │ │ ├── SolutionConfiguration.ts │ │ │ ├── initContext.ts │ │ │ ├── loadCommands.ts │ │ │ └── loadOptions.ts │ │ ├── module.d.ts │ │ ├── bin │ │ │ └── nowa.ts │ │ └── index.ts │ ├── package.json │ ├── CHANGELOG.md │ └── package-lock.json ├── core │ ├── README.md │ ├── .nycrc.json │ ├── .npmignore │ ├── test │ │ ├── lib │ │ │ ├── utils.spec.ts │ │ │ ├── runner.spec.ts │ │ │ └── core │ │ │ │ ├── runnable.spec.ts │ │ │ │ ├── module.spec.ts │ │ │ │ └── hookable.spec.ts │ │ └── index.spec.ts │ ├── tsconfig.build.json │ ├── src │ │ ├── lib │ │ │ ├── plugins │ │ │ │ ├── parseConfig.ts │ │ │ │ ├── runError.ts │ │ │ │ ├── initError.ts │ │ │ │ ├── loadAdvanced.ts │ │ │ │ ├── loadPlugins.ts │ │ │ │ ├── parseSolution.ts │ │ │ │ ├── loadConfig.ts │ │ │ │ ├── loadSolution.ts │ │ │ │ └── loadModules.ts │ │ │ ├── core │ │ │ │ ├── runnable.ts │ │ │ │ ├── module.ts │ │ │ │ └── hookable.ts │ │ │ ├── utils.ts │ │ │ ├── moduleQueue.ts │ │ │ ├── types.ts │ │ │ └── runner.ts │ │ └── index.ts │ ├── package.json │ ├── package-lock.json │ └── CHANGELOG.md ├── module-file │ ├── .npmignore │ ├── src │ │ ├── module.d.ts │ │ └── index.ts │ ├── tsconfig.build.json │ ├── package.json │ ├── CHANGELOG.md │ └── README.md ├── module-script │ ├── .npmignore │ ├── tsconfig.build.json │ ├── package.json │ ├── README.md │ ├── CHANGELOG.md │ ├── src │ │ └── index.ts │ └── package-lock.json └── module-webpack │ ├── .npmignore │ ├── src │ ├── module.d.ts │ └── index.ts │ ├── tsconfig.build.json │ ├── package.json │ ├── README.md │ └── CHANGELOG.md ├── .prettierrc.json ├── .mocha.opts ├── .lintstagedrc.json ├── tsconfig.json ├── .editorconfig ├── lerna.json ├── .nycrc.json ├── .gitignore ├── package.json ├── tslint.json ├── tsconfig.build.json └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # nowa-cli 2 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # nowa-core 2 | -------------------------------------------------------------------------------- /packages/core/.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.nycrc.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/cli/.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .* 3 | /coverage 4 | /src/ 5 | /test/ 6 | /tsconfig.* 7 | -------------------------------------------------------------------------------- /packages/core/.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .* 3 | /coverage 4 | /src/ 5 | /test/ 6 | /tsconfig.* 7 | -------------------------------------------------------------------------------- /packages/module-file/.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .* 3 | /coverage 4 | /src/ 5 | /test/ 6 | /tsconfig.* 7 | -------------------------------------------------------------------------------- /packages/module-script/.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .* 3 | /coverage 4 | /src/ 5 | /test/ 6 | /tsconfig.* 7 | -------------------------------------------------------------------------------- /packages/module-webpack/.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .* 3 | /coverage 4 | /src/ 5 | /test/ 6 | /tsconfig.* 7 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /.mocha.opts: -------------------------------------------------------------------------------- 1 | --colors 2 | --require ts-node/register 3 | --require source-map-support/register 4 | --recursive ./test/**/*.spec.ts 5 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.{js,json}": ["prettier --write", "git add"], 3 | "*.ts": ["prettier --write", "tslint --fix", "git add"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/module-file/src/module.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'is-glob' { 2 | const isGlob: (stringGlob: string) => boolean; 3 | export = isGlob; 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.build.json", 3 | "compilerOptions": { 4 | "baseUrl": "./packages", 5 | "paths": { 6 | "@nowa/*": ["./*/src"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/*"], 3 | "version": "independent", 4 | "command": { 5 | "version": { 6 | "message": "chore(release): publish", 7 | "conventionalCommits": true 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/module-webpack/src/module.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'webpack/lib/Stats' { 2 | export = Stats; 3 | class Stats { 4 | public static presetToOptions(name: any): object; 5 | } 6 | namespace Stats { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "check-coverage": false, 3 | "per-file": true, 4 | "include": ["src/**/*.ts"], 5 | "exclude": ["**/*.d.ts"], 6 | "extension": [".ts"], 7 | "cache": true, 8 | "all": true, 9 | "reporter": ["text"] 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.log 3 | .nyc_output 4 | .vscode/ 5 | .idea 6 | .DS_Store 7 | .nyc 8 | coverage/ 9 | 10 | /packages/*/*/ 11 | !/packages/*/src/ 12 | !/packages/*/test/ 13 | /packages/**/*.js 14 | /packages/**/*.d.ts 15 | !/packages/*/src/**/*.js 16 | !/packages/*/src/**/*.d.ts 17 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.build.json", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "outDir": "./", 6 | "rootDir": "./src", 7 | "sourceMap": false 8 | }, 9 | "include": ["./src/**/*"], 10 | "exclude": ["node_modules"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/test/lib/utils.spec.ts: -------------------------------------------------------------------------------- 1 | // import * as assert from 'assert'; 2 | // import 'mocha'; // tslint:disable-line 3 | 4 | // import {} from '../../src/lib/utils'; 5 | 6 | // describe('Utils', () => { 7 | // describe('xx', () => { 8 | // it('xxx', async () => {}); 9 | // }); 10 | // }); 11 | -------------------------------------------------------------------------------- /packages/core/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.build.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "outDir": "./", 6 | "rootDir": "./src", 7 | "sourceMap": false 8 | }, 9 | "include": ["./src/**/*"], 10 | "exclude": ["node_modules"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/module-file/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.build.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "outDir": "./", 6 | "rootDir": "./src", 7 | "sourceMap": false 8 | }, 9 | "include": ["./src/**/*"], 10 | "exclude": ["node_modules"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/module-script/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.build.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "outDir": "./", 6 | "rootDir": "./src", 7 | "sourceMap": false 8 | }, 9 | "include": ["./src/**/*"], 10 | "exclude": ["node_modules"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/test/lib/runner.spec.ts: -------------------------------------------------------------------------------- 1 | // import * as assert from 'assert'; 2 | // import 'mocha'; // tslint:disable-line 3 | 4 | // import { Runner } from '../../src/lib/runner'; 5 | 6 | // describe('Runner', () => { 7 | // describe('xx', () => { 8 | // it('xxx', async () => {}); 9 | // }); 10 | // }); 11 | -------------------------------------------------------------------------------- /packages/module-webpack/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.build.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "outDir": "./", 6 | "rootDir": "./src", 7 | "sourceMap": false 8 | }, 9 | "include": ["./src/**/*"], 10 | "exclude": ["node_modules"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/cli/src/lib/logo/small.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | export default (coreVersion: string, cliVersion: string) => chalk`{magenta _ ______ _ _____ 4 | / |/ / __ \\ | /| / / _ |\tcore @ {cyan ${coreVersion}} 5 | / / /_/ / |/ |/ / __ |\tcli @ {cyan ${cliVersion}} 6 | /_/|_/\\____/|__/|__/_/ |_|} 7 | `; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nowa", 3 | "version": "1.0.0", 4 | "description": "the @nowa monorepo", 5 | "private": true, 6 | "scripts": { 7 | "bootstrap": "npx lerna bootstrap", 8 | "build": "lerna run build", 9 | "ver": "lerna version", 10 | "pub": "lerna publish from-git" 11 | }, 12 | "devDependencies": { 13 | "@types/node": "^13.1.1", 14 | "lerna": "^3.20.0", 15 | "prettier": "^1.19.1", 16 | "typescript": "^3.7.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest", "tslint-config-prettier"], 3 | "rules": { 4 | "interface-over-type-literal": false, 5 | "max-classes-per-file": false, 6 | "no-console": false, 7 | "no-empty-interface": false, 8 | "no-namespace": false, 9 | "no-shadowed-variable": false, 10 | "no-unused-expression": [true, "allow-fast-null-checks"], 11 | "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"], 12 | "no-submodule-imports": false 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/lib/plugins/parseConfig.ts: -------------------------------------------------------------------------------- 1 | import { Runner } from '../runner'; 2 | import { parser } from '../utils'; 3 | 4 | export class ParseConfigPlugin { 5 | public apply(runner: Runner, { logger }: Runner.Utils) { 6 | runner.$register('parse-config', async ({ config, commands }) => { 7 | const parseResult = parser('config.config', commands, logger.debug, config.config); 8 | logger.debug('parseResult', parseResult && parseResult.result); 9 | return (parseResult && parseResult.result) || [{}]; 10 | }); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/plugins/runError.ts: -------------------------------------------------------------------------------- 1 | import { Runner } from '../runner'; 2 | 3 | export class RunErrorPlugin { 4 | public apply(runner: Runner, { logger }: Runner.Utils) { 5 | runner.$register('run-error', ({ error }) => { 6 | logger.error('during running'); 7 | logger.error(error); 8 | process.exit(1); 9 | }); 10 | runner.$register('init-module-queue', ({ moduleQueue }) => { 11 | moduleQueue.$register('run-error', ({ error }) => { 12 | logger.error('during module running'); 13 | logger.error(error); 14 | process.exit(1); 15 | }); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "forceConsistentCasingInFileNames": true, 5 | "lib": ["es5", "es2015", "es2016", "es2017", "esnext"], 6 | "importHelpers": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "newLine": "LF", 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "noImplicitReturns": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "removeComments": true, 16 | "sourceMap": true, 17 | "strict": true, 18 | "target": "es2015", 19 | "types": [] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/src/lib/plugins/initError.ts: -------------------------------------------------------------------------------- 1 | import { Runner } from '../runner'; 2 | 3 | export class InitErrorPlugin { 4 | public apply(runner: Runner, { logger }: Runner.Utils) { 5 | runner.$register('init-error', ({ error }) => { 6 | logger.error('during initialization'); 7 | logger.error(error); 8 | process.exit(1); 9 | }); 10 | runner.$register('init-module-queue', ({ moduleQueue }) => { 11 | moduleQueue.$register('init-error', ({ error }) => { 12 | logger.error('during module initialization'); 13 | logger.error(error); 14 | process.exit(1); 15 | }); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/cli/src/lib/SolutionConfiguration.ts: -------------------------------------------------------------------------------- 1 | import { dirname, resolve } from 'path'; 2 | 3 | export interface ISolution { 4 | name: string; 5 | version: string; 6 | } 7 | 8 | export class SolutionConfiguration { 9 | private _name: string = ''; 10 | private _version: string = ''; 11 | 12 | constructor(configFilePath: string) { 13 | this.init(configFilePath); 14 | } 15 | 16 | public toJS(): ISolution { 17 | return { 18 | name: this._name, 19 | version: this._version, 20 | }; 21 | } 22 | 23 | private init(configFilePath: string): void { 24 | const content = require(configFilePath); 25 | this._name = content.solution; 26 | const pkgJsonObj = require(resolve(dirname(configFilePath), `node_modules/${this._name}/package.json`)); 27 | this._version = pkgJsonObj.version; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/src/lib/core/runnable.ts: -------------------------------------------------------------------------------- 1 | import { Hookable } from './hookable'; 2 | 3 | export namespace Runnable { 4 | export interface IBase { 5 | $type: 'callback' | 'async'; 6 | run(done?: (error?: Error) => void): void | Promise; 7 | } 8 | 9 | export abstract class Callback extends Hookable implements IBase { 10 | public $type: 'callback' = 'callback'; 11 | public abstract run(done: (error?: Error) => void): void; 12 | } 13 | 14 | export abstract class Async extends Hookable implements IBase { 15 | public $type: 'async' = 'async'; 16 | public abstract async run(): Promise; 17 | } 18 | 19 | export type InstanceType = Callback | Async; 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/src/lib/plugins/loadAdvanced.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | import { Runner } from '../runner'; 3 | 4 | export class LoadAdvancedPlugin { 5 | public apply(runner: Runner, { logger }: Runner.Utils) { 6 | runner.$register('load-advanced', async ({ context }) => { 7 | const packageJSON = require(resolve(context, './package.json')); 8 | const config = packageJSON.nowa; 9 | if (config && config.solution) { 10 | logger.debug(`got config from package.json`); 11 | try { 12 | const solution = require(config.solution); 13 | return { config, solution }; 14 | } catch (e) { 15 | logger.error(e); 16 | return null; 17 | } 18 | } 19 | logger.debug(`got config failed, fallback to normal`); 20 | return null; 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/src/lib/plugins/loadPlugins.ts: -------------------------------------------------------------------------------- 1 | import { Runner } from '../runner'; 2 | import { IConfig, IPlugin, ISolution } from '../types'; 3 | 4 | export class LoadPluginsPlugin { 5 | public apply(runner: Runner, { logger }: Runner.Utils) { 6 | // tslint:disable-next-line: no-object-literal-type-assertion 7 | runner.$register('load-plugins', async ({ config = {} as Partial, solution = {} as Partial }) => { 8 | const allPlugins = [...((solution.nowa && solution.nowa.plugins) || []), ...((config.nowa && config.nowa.plugins) || [])]; 9 | logger.debug(`got ${allPlugins.length} plugin(s) to load`); 10 | return allPlugins.map(p => { 11 | if (typeof p === 'string') { 12 | logger.debug(`instantiate ${p}`); 13 | return new (require(p))() as IPlugin; 14 | } 15 | logger.debug(`instantiate ${p[0]} with config ${p[1]}`); 16 | return new (require(p[0]))(p[1]) as IPlugin; 17 | }); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/cli/src/module.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'yargs-parser' { 2 | import { Arguments } from 'yargs'; 3 | const parser: (args: string[]) => Arguments; 4 | export = parser; 5 | } 6 | 7 | declare module 'import-local' { 8 | const importLocal: (filename: string) => boolean; 9 | export = importLocal; 10 | } 11 | 12 | declare module 'is-elevated' { 13 | const isElevated: () => Promise; 14 | export = isElevated; 15 | } 16 | 17 | declare module 'cliui' { 18 | interface IColumn { 19 | text?: string; 20 | width?: string; 21 | align?: 'right' | 'center'; 22 | padding?: [number, number, number, number]; 23 | border?: boolean; 24 | } 25 | type ICliUi = (arg?: { width?: number; wrap?: boolean }) => ICliUiInstance; 26 | interface ICliUiInstance { 27 | div(a1: string | IColumn, a2?: string | IColumn, a3?: string | IColumn): void; 28 | span(a1: string | IColumn, a2?: string | IColumn, a3?: string | IColumn): void; 29 | toString(): string; 30 | } 31 | const cliUi: ICliUi; 32 | export = cliUi; 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 - present Nowa Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/module-script/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nowa/module-script", 3 | "version": "0.3.2", 4 | "description": "the nowa script module", 5 | "scripts": { 6 | "clean": "rm -f ./index.*", 7 | "build": "npm run clean && tsc -p tsconfig.build.json", 8 | "prepublishOnly": "npm run build" 9 | }, 10 | "author": { 11 | "name": "Tommy Troy Lin", 12 | "email": "tommytroylin@gmail.com", 13 | "url": "https://github.com/tommytroylin" 14 | }, 15 | "homepage": "https://github.com/nowa-webpack/nowa2", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/nowa-webpack/nowa2.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/nowa-webpack/nowa2/issues" 22 | }, 23 | "license": "MIT", 24 | "dependencies": { 25 | "execa": "^0.10.0" 26 | }, 27 | "devDependencies": { 28 | "@nowa/core": "^0.7.7", 29 | "@types/execa": "^0.9.0" 30 | }, 31 | "peerDependencies": { 32 | "@nowa/core": "^0.7.1" 33 | }, 34 | "types": "./", 35 | "publishConfig": { 36 | "access": "public", 37 | "registry": "https://registry.npmjs.org/" 38 | }, 39 | "engines": { 40 | "node": ">=6.5" 41 | }, 42 | "gitHead": "910c1d9f5b24033268a792efc383f4aaf1c2dcc3" 43 | } 44 | -------------------------------------------------------------------------------- /packages/cli/src/lib/logo/large.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | export default `-. --- .-- .- -. --- .-- .- -. --- .-- .- -. --- .-- .- 4 | . ${chalk`{magenta ___ ___ ___ ___}`} . 5 | ${chalk`{magenta /\\__\\ /\\ \\ /\\__\\ /\\ \\}`} 6 | - ${chalk`{magenta /::| | /::\\ \\ /:/ _/_ /::\\ \\}`} - 7 | - ${chalk`{magenta /:|:| | /:/\\:\\ \\ /:/ /\\__\\ /:/\\:\\ \\}`} - 8 | - ${chalk`{magenta /:/|:| |__ /:/ \\:\\ \\ /:/ /:/ _/_ /::\\~\\:\\ \\}`} . 9 | ${chalk`{magenta /:/ |:| /\\__\\ /:/__/ \\:\\__\\ /:/_/:/ /\\__\\ /:/\\:\\ \\:\\__\\}`} 10 | ${chalk`{magenta \\/__|:|/:/ / \\:\\ \\ /:/ / \\:\\/:/ /:/ / \\/__\\:\\/:/ /}`} 11 | . ${chalk`{magenta |:/:/ / \\:\\ /:/ / \\::/_/:/ / \\::/ /}`} - 12 | - ${chalk`{magenta |::/ / \\:\\/:/ / \\:\\/:/ / /:/ /}`} - 13 | - ${chalk`{magenta /:/ / \\::/ / \\::/ / /:/ /}`} - 14 | ${chalk`{magenta \\/__/ \\/__/ \\/__/ \\/__/}`} 15 | . . 16 | -. --- .-- .- -. --- .-- .- -. --- .-- .- -. --- .-- .-`; 17 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nowa/core", 3 | "version": "0.7.7", 4 | "description": "the nowa core", 5 | "scripts": { 6 | "clean": "rm -rf ./lib & rm -f ./index.*", 7 | "build": "npm run clean && tsc -p tsconfig.build.json", 8 | "test": "mocha --opts ../../.mocha.opts --require espower-typescript/guess", 9 | "coverage": "nyc mocha --opts ../../.mocha.opts" 10 | }, 11 | "author": { 12 | "name": "Tommy Troy Lin", 13 | "email": "tommytroylin@gmail.com", 14 | "url": "https://github.com/tommytroylin" 15 | }, 16 | "homepage": "https://github.com/nowa-webpack/nowa2", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/nowa-webpack/nowa2.git" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/nowa-webpack/nowa2/issues" 23 | }, 24 | "license": "MIT", 25 | "dependencies": { 26 | "chalk": "^2.4.1", 27 | "fs-extra": "^6.0.1", 28 | "path-to-regexp": "^2.2.1", 29 | "tslib": "^1.9.1" 30 | }, 31 | "devDependencies": { 32 | "@types/fs-extra": "^5.0.2" 33 | }, 34 | "types": "./", 35 | "publishConfig": { 36 | "access": "public", 37 | "registry": "https://registry.npmjs.org/" 38 | }, 39 | "engines": { 40 | "node": ">=6.5" 41 | }, 42 | "gitHead": "910c1d9f5b24033268a792efc383f4aaf1c2dcc3" 43 | } 44 | -------------------------------------------------------------------------------- /packages/module-file/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nowa/module-file", 3 | "version": "0.3.2", 4 | "description": "the nowa file module", 5 | "scripts": { 6 | "clean": "rm -f ./index.*", 7 | "build": "npm run clean && tsc -p tsconfig.build.json", 8 | "prepublishOnly": "npm run build" 9 | }, 10 | "author": { 11 | "name": "Tommy Troy Lin", 12 | "email": "tommytroylin@gmail.com", 13 | "url": "https://github.com/tommytroylin" 14 | }, 15 | "homepage": "https://github.com/nowa-webpack/nowa2", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/nowa-webpack/nowa2.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/nowa-webpack/nowa2/issues" 22 | }, 23 | "license": "MIT", 24 | "dependencies": { 25 | "fs-extra": "^6.0.1", 26 | "globby": "^8.0.1", 27 | "is-glob": "^4.0.0" 28 | }, 29 | "devDependencies": { 30 | "@nowa/core": "^0.7.7", 31 | "@types/fs-extra": "^5.0.2", 32 | "@types/globby": "^6.1.0" 33 | }, 34 | "peerDependencies": { 35 | "@nowa/core": "^0.7.1" 36 | }, 37 | "types": "./", 38 | "publishConfig": { 39 | "access": "public", 40 | "registry": "https://registry.npmjs.org/" 41 | }, 42 | "engines": { 43 | "node": ">=6.5" 44 | }, 45 | "gitHead": "910c1d9f5b24033268a792efc383f4aaf1c2dcc3" 46 | } 47 | -------------------------------------------------------------------------------- /packages/core/test/index.spec.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import { readdirSync } from 'fs'; 3 | import { resolve } from 'path'; 4 | import 'mocha'; // tslint:disable-line 5 | 6 | import { createDefaultRunner, createRunner, Runner } from '../src'; 7 | 8 | class TestPlugin { 9 | public apply(runner: Runner) { 10 | runner.$register('init-start', () => undefined); 11 | } 12 | } 13 | 14 | describe('Index', () => { 15 | it('createRunner returns Runner', async () => { 16 | const runner = await createRunner([]); 17 | assert(runner instanceof Runner); 18 | }); 19 | 20 | it('createRunner applies plugins', async () => { 21 | let runner = await createRunner([]); 22 | assert(Object.keys(runner.$hooks).length === 0); 23 | runner = await createRunner([new TestPlugin()]); 24 | assert(Object.keys(runner.$hooks).length === 1); 25 | assert(runner.$hooks['init-start']!.length === 1); 26 | }); 27 | 28 | it('createDefaultRunner returns Runner', async () => { 29 | const runner = await createDefaultRunner([]); 30 | assert(runner instanceof Runner); 31 | }); 32 | 33 | it('createDefaultRunner applies all plugins', async () => { 34 | const hookCount = readdirSync(resolve(__dirname, '../src/lib/plugins')).length; 35 | const runner = await createDefaultRunner([]); 36 | assert(Object.keys(runner.$hooks).length === hookCount); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/core/test/lib/core/runnable.spec.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import 'mocha'; // tslint:disable-line 3 | 4 | import { Hookable } from '../../../src/lib/core/hookable'; 5 | import { Runnable } from '../../../src/lib/core/runnable'; 6 | 7 | class AsyncRunnable extends Runnable.Async<{}> { 8 | public async run() { 9 | return; 10 | } 11 | } 12 | 13 | class CallbackRunnable extends Runnable.Callback<{}> { 14 | public run(done: () => void) { 15 | setTimeout(done, 0); 16 | } 17 | } 18 | 19 | describe('Module', () => { 20 | describe('Async', () => { 21 | let instance: AsyncRunnable; 22 | beforeEach(() => { 23 | instance = new AsyncRunnable(); 24 | }); 25 | 26 | it('correctly extends Hookable', () => { 27 | assert(instance instanceof Hookable); 28 | }); 29 | 30 | it('all methods / properties exist', () => { 31 | assert(instance.$type === 'async'); 32 | assert(instance.run); 33 | }); 34 | }); 35 | 36 | describe('Callback', () => { 37 | let instance: CallbackRunnable; 38 | beforeEach(() => { 39 | instance = new CallbackRunnable(); 40 | }); 41 | 42 | it('correctly extends Hookable', () => { 43 | assert(instance instanceof Hookable); 44 | }); 45 | 46 | it('all methods / properties exist', () => { 47 | assert(instance.$type === 'callback'); 48 | assert(instance.run); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/core/src/lib/plugins/parseSolution.ts: -------------------------------------------------------------------------------- 1 | import { Runner } from '../runner'; 2 | import { parser } from '../utils'; 3 | 4 | export class ParseSolutionPlugin { 5 | public apply(runner: Runner, { logger }: Runner.Utils) { 6 | runner.$register('parse-solution', async ({ config, commands, solution }) => { 7 | if (commands.length === 0) { 8 | logger.debug('no command found'); 9 | return { options: {}, actions: [] }; 10 | } 11 | const configResult = parser('config.commands', commands, logger.debug, config.commands); 12 | if (configResult) { 13 | logger.debug('using config.commands'); 14 | if (typeof configResult.result === 'function') { 15 | return (configResult.result as any)({ params: configResult.params }); // TODO: fix any 16 | } 17 | return configResult.result; 18 | } 19 | const solutionResult = parser('solution.commands', commands, logger.debug, solution.commands); 20 | if (solutionResult) { 21 | logger.debug('using solution.commands'); 22 | if (typeof solutionResult.result === 'function') { 23 | return (solutionResult.result as any)({ params: solutionResult.params }); // TODO: fix any 24 | } 25 | return solutionResult.result; 26 | } 27 | logger.error(`neither of config / solution has described commands.${commands.join('.')}`); 28 | throw new Error('no correspond command'); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/module-webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nowa/module-webpack", 3 | "version": "0.8.0", 4 | "description": "the nowa webpack module", 5 | "scripts": { 6 | "clean": "rm -f ./index.*", 7 | "build": "npm run clean && tsc -p tsconfig.build.json", 8 | "prepublishOnly": "npm run build" 9 | }, 10 | "author": { 11 | "name": "Tommy Troy Lin", 12 | "email": "tommytroylin@gmail.com", 13 | "url": "https://github.com/tommytroylin" 14 | }, 15 | "homepage": "https://github.com/nowa-webpack/nowa2", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/nowa-webpack/nowa2.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/nowa-webpack/nowa2/issues" 22 | }, 23 | "license": "MIT", 24 | "dependencies": { 25 | "supports-color": "^6.1.0" 26 | }, 27 | "devDependencies": { 28 | "@nowa/core": "^0.7.7", 29 | "@types/node": "^10.9.4", 30 | "@types/webpack": "^4.41.0", 31 | "@types/webpack-dev-server": "^3.9.0", 32 | "webpack": "^4.34.0", 33 | "webpack-dev-server": "^3.10.0" 34 | }, 35 | "peerDependencies": { 36 | "@nowa/core": "^0.7.1", 37 | "webpack": "^4.0.0", 38 | "webpack-dev-server": "^3.10.0" 39 | }, 40 | "types": "./", 41 | "publishConfig": { 42 | "access": "public", 43 | "registry": "https://registry.npmjs.org/" 44 | }, 45 | "engines": { 46 | "node": ">=6.5" 47 | }, 48 | "gitHead": "910c1d9f5b24033268a792efc383f4aaf1c2dcc3" 49 | } 50 | -------------------------------------------------------------------------------- /packages/cli/src/lib/initContext.ts: -------------------------------------------------------------------------------- 1 | import { constants } from 'fs'; 2 | import { resolve } from 'path'; 3 | 4 | import { Runner } from '@nowa/core'; 5 | import { access } from 'fs-extra'; 6 | 7 | export class InitContextPlugin { 8 | constructor(public options: InitContextPlugin.IOptions = {}) {} 9 | public apply(runner: Runner, utils: Runner.Utils) { 10 | const { logger } = utils; 11 | runner.$register('init-context', async () => { 12 | const path = this.options.context || process.cwd(); 13 | logger.debug(`locate context @ ${path}`); 14 | const packageJSONPath = resolve(path, './package.json'); 15 | try { 16 | await access(packageJSONPath, constants.R_OK); 17 | logger.debug(`locate package.json @ ${packageJSONPath}`); 18 | logger.debug(`access package.json succeed`); 19 | try { 20 | require(packageJSONPath); 21 | logger.debug(`reqire package.json succeed`); 22 | } catch (e) { 23 | logger.debug(`reqire package.json failed`); 24 | logger.error(e); 25 | process.exit(1); 26 | } 27 | } catch { 28 | logger.debug(`access package.json failed`); 29 | logger.warn(`package.json can't be found right here`); 30 | logger.warn(`@ ${path}`); 31 | logger.warn(`maybe you run nowa at the wrong place`); 32 | } 33 | return path; 34 | }); 35 | } 36 | } 37 | 38 | export namespace InitContextPlugin { 39 | export interface IOptions { 40 | context?: string; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/cli/src/bin/nowa.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | import { resolve } from 'path'; 5 | 6 | import chalk from 'chalk'; 7 | import * as importLocal from 'import-local'; 8 | import * as isElevated from 'is-elevated'; 9 | import * as semver from 'semver'; 10 | 11 | import { run } from '../index'; 12 | import logo from '../lib/logo/small'; 13 | // import { SolutionConfiguration } from '../lib/SolutionConfiguration'; 14 | const isDebug = !!process.env.NOWA_DEBUG; 15 | 16 | if (isDebug || !importLocal(__filename)) { 17 | const { version: cliVersion } = require(resolve(__dirname, '../package.json')); // tslint:disable-line:no-var-requires 18 | const { version: coreVersion } = require(`@nowa/core/package.json`); // tslint:disable-line:no-var-requires no-submodule-imports 19 | const isWindows: boolean = process.platform === 'win32'; 20 | const nodeVersion: string = process.versions.node; 21 | if (semver.lt(nodeVersion, '6.5.0')) { 22 | console.log(chalk`{red Nowa needs node @ {bold 6.5+} but found ${nodeVersion}\nPlease upgrade your environment}`); 23 | process.exit(1); 24 | } 25 | // const solution = new SolutionConfiguration(resolve(process.cwd(), 'nowa.config.js')); 26 | console.log(`\n${logo(coreVersion, cliVersion)}`); 27 | isElevated().then(e => { 28 | if (e) { 29 | console.log( 30 | chalk`{${isWindows ? 'yellow' : 'red'} Nowa is elevated ${ 31 | isWindows ? '(administrator)' : '(root privilege)' 32 | }\nUse at your own risk}`, 33 | ); 34 | } 35 | run().catch((e: any) => { 36 | console.log(chalk`{red ${e.stack ? e.stack : e}}`); 37 | }); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Module } from './lib/core/module'; 2 | import { Runner } from './lib/runner'; 3 | import * as Types from './lib/types'; 4 | import * as utils from './lib/utils'; 5 | 6 | import { InitErrorPlugin } from './lib/plugins/initError'; 7 | import { LoadAdvancedPlugin } from './lib/plugins/loadAdvanced'; 8 | import { LoadConfigPlugin } from './lib/plugins/loadConfig'; 9 | import { LoadModulesPlugin } from './lib/plugins/loadModules'; 10 | import { LoadPluginsPlugin } from './lib/plugins/loadPlugins'; 11 | import { LoadSolutionPlugin } from './lib/plugins/loadSolution'; 12 | import { ParseConfigPlugin } from './lib/plugins/parseConfig'; 13 | import { ParseSolutionPlugin } from './lib/plugins/parseSolution'; 14 | import { RunErrorPlugin } from './lib/plugins/runError'; 15 | 16 | export const createRunner = async (createUtils: Runner.UtilsCreator, plugins: Array>) => { 17 | const runner = new Runner(createUtils); 18 | for (const plugin of plugins) { 19 | await plugin.apply(runner, createUtils(plugin.constructor.name)); 20 | } 21 | return runner; 22 | }; 23 | 24 | export const createDefaultRunner = async (createUtils: Runner.UtilsCreator, plugins: Array>) => { 25 | const allPlugins = [ 26 | new InitErrorPlugin(), 27 | new LoadAdvancedPlugin(), 28 | new LoadConfigPlugin(), 29 | new LoadModulesPlugin(), 30 | new LoadPluginsPlugin(), 31 | new LoadSolutionPlugin(), 32 | new ParseConfigPlugin(), 33 | new ParseSolutionPlugin(), 34 | new RunErrorPlugin(), 35 | ...plugins, 36 | ]; 37 | return createRunner(createUtils, allPlugins); 38 | }; 39 | 40 | export { Runner, Module, utils, Types }; 41 | -------------------------------------------------------------------------------- /packages/core/src/lib/core/module.ts: -------------------------------------------------------------------------------- 1 | import { IUtils } from '../types'; 2 | import { Hookable } from './hookable'; 3 | import { Runnable } from './runnable'; 4 | 5 | export namespace Module { 6 | export interface IBase { 7 | $name: string; 8 | $runtime: IRuntime; 9 | $utils: IUtils; 10 | init(): Promise; 11 | } 12 | 13 | export interface IConstructor extends Async { 14 | prototype: { 15 | init(): any; 16 | run(cb?: any): any; 17 | }; 18 | new ($runtime: IRuntime, $utils: IUtils): this; 19 | } 20 | 21 | export interface IRuntime { 22 | context: string; 23 | commands: string[]; 24 | options: object; 25 | config: ModuleConfig; 26 | } 27 | 28 | export abstract class Async extends Runnable.Async< 29 | HookGroup 30 | > implements IBase { 31 | public abstract $name: string; 32 | constructor(public $runtime: IRuntime, public $utils: IUtils) { 33 | super(); 34 | } 35 | public abstract async init(): Promise; 36 | } 37 | 38 | export abstract class Callback extends Runnable.Callback< 39 | HookGroup 40 | > implements IBase { 41 | public abstract $name: string; 42 | constructor(public $runtime: IRuntime, public $utils: IUtils) { 43 | super(); 44 | } 45 | public abstract async init(): Promise; 46 | } 47 | 48 | export type InstanceType = Async | Callback; 49 | } 50 | -------------------------------------------------------------------------------- /packages/core/src/lib/plugins/loadConfig.ts: -------------------------------------------------------------------------------- 1 | import { constants } from 'fs'; 2 | import { resolve } from 'path'; 3 | 4 | import { access } from 'fs-extra'; 5 | 6 | import { Runner } from '../runner'; 7 | import { handleESModuleDefault } from '../utils'; 8 | 9 | const configPaths = ['./nowa.config.js', './.nowa.config.js', './nowa.js', './.nowa.js', './nowa', './.nowa']; 10 | 11 | export class LoadConfigPlugin { 12 | constructor(public options: LoadConfigPlugin.IOptions = {}) {} 13 | public apply(runner: Runner, { logger }: Runner.Utils) { 14 | runner.$register('load-config', async ({ context }) => { 15 | if (this.options.config) { 16 | logger.debug('use provided config instead of project config file'); 17 | return this.options.config; 18 | } 19 | const allPaths = [...(this.options.filePaths || []), ...configPaths]; 20 | for (const configPath of allPaths) { 21 | const filePath = resolve(context, configPath); 22 | try { 23 | await access(filePath, constants.R_OK); 24 | logger.debug(`access ${filePath} succeed`); 25 | } catch { 26 | logger.debug(`access ${filePath} failed`); 27 | continue; 28 | } 29 | logger.debug(`found config @ ${filePath}`); 30 | try { 31 | logger.debug(`resolving config from ${filePath}`); 32 | return handleESModuleDefault(require(filePath)); 33 | } catch (e) { 34 | logger.error(e); 35 | } 36 | } 37 | logger.error('can not find any valid config'); 38 | throw new Error('no config'); 39 | }); 40 | } 41 | } 42 | 43 | export namespace LoadConfigPlugin { 44 | export interface IOptions { 45 | filePaths?: string[]; 46 | config?: object; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nowa/cli", 3 | "version": "0.6.6", 4 | "description": "The nowa command line tool", 5 | "scripts": { 6 | "clean": "rm -rf ./lib & rm -rf ./bin & rm -f ./index.*", 7 | "build": "npm run clean && tsc -p tsconfig.build.json" 8 | }, 9 | "author": { 10 | "name": "Tommy Troy Lin", 11 | "email": "tommytroylin@gmail.com", 12 | "url": "https://github.com/tommytroylin" 13 | }, 14 | "homepage": "https://github.com/nowa-webpack/nowa2", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/nowa-webpack/nowa2.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/nowa-webpack/nowa2/issues" 21 | }, 22 | "license": "MIT", 23 | "dependencies": { 24 | "@types/archy": "^0.0.31", 25 | "archy": "^1.0.0", 26 | "chalk": "^2.4.1", 27 | "cliui": "^4.1.0", 28 | "fs-extra": "^6.0.1", 29 | "import-local": "^1.0.0", 30 | "inquirer": "^5.0.1", 31 | "is-elevated": "^2.0.1", 32 | "ora": "^2.1.0", 33 | "semver": "^5.5.0", 34 | "tslib": "^1.9.1", 35 | "yargs": "^11.0.0", 36 | "yargs-parser": "^13.1.2" 37 | }, 38 | "devDependencies": { 39 | "@nowa/core": "^0.7.7", 40 | "@types/fs-extra": "^5.0.2", 41 | "@types/inquirer": "^0.0.41", 42 | "@types/node": "^10.1.2", 43 | "@types/ora": "^1.3.4", 44 | "@types/semver": "^5.5.0", 45 | "@types/yargs": "^11.0.0" 46 | }, 47 | "peerDependencies": { 48 | "@nowa/core": "^0.7.1" 49 | }, 50 | "bin": { 51 | "nowa2": "./bin/nowa.js" 52 | }, 53 | "types": "./", 54 | "publishConfig": { 55 | "access": "public", 56 | "registry": "https://registry.npmjs.org/" 57 | }, 58 | "engines": { 59 | "node": ">=6.5" 60 | }, 61 | "gitHead": "910c1d9f5b24033268a792efc383f4aaf1c2dcc3" 62 | } 63 | -------------------------------------------------------------------------------- /packages/core/test/lib/core/module.spec.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import 'mocha'; // tslint:disable-line 3 | 4 | import { Module } from '../../../src/lib/core/module'; 5 | import { Runnable } from '../../../src/lib/core/runnable'; 6 | 7 | class AsyncModule extends Module.Async { 8 | public $name = 'async module'; 9 | public async init() { 10 | return; 11 | } 12 | public async run() { 13 | return; 14 | } 15 | } 16 | 17 | class CallbackModule extends Module.Callback { 18 | public $name = 'callback module'; 19 | public async init() { 20 | return; 21 | } 22 | public run(done: () => void) { 23 | setTimeout(done, 0); 24 | } 25 | } 26 | 27 | describe('Module', () => { 28 | describe('Async', () => { 29 | let instance: AsyncModule; 30 | beforeEach(() => { 31 | instance = new AsyncModule({ context: process.cwd(), commands: [], options: [], moduleOptions: {} }); 32 | }); 33 | 34 | it('correctly extends Runnable.Async', () => { 35 | assert(instance instanceof Runnable.Async); 36 | }); 37 | 38 | it('all methods / properties exist', () => { 39 | assert(instance.$name === 'async module'); 40 | assert(instance.$runtime); 41 | assert(instance.init); 42 | }); 43 | }); 44 | 45 | describe('Callback', () => { 46 | let instance: CallbackModule; 47 | beforeEach(() => { 48 | instance = new CallbackModule({ context: process.cwd(), commands: [], options: [], moduleOptions: {} }); 49 | }); 50 | 51 | it('correctly extends Runnable.Async', () => { 52 | assert(instance instanceof Runnable.Callback); 53 | }); 54 | 55 | it('all methods / properties exist', () => { 56 | assert(instance.$name === 'callback module'); 57 | assert(instance.$runtime); 58 | assert(instance.init); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /packages/module-script/README.md: -------------------------------------------------------------------------------- 1 | # nowa-module-script 2 | 3 | ## Module Config 4 | 5 | ```ts 6 | export type SingleScript = string | (() => void | Promise); 7 | export interface IOptions { 8 | // all default false 9 | parallel?: boolean; 10 | noWait?: boolean; 11 | noRetrigger?: boolean; 12 | } 13 | export type Config = ['script', SingleScript | SingleScript[], IOptions]; 14 | ``` 15 | 16 | ## Usage 17 | 18 | ```js 19 | const config1 = ['script', 'echo start']; 20 | const config2 = ['script', ['rm -rf dist', 'mkdir dist']]; // multiple scripts 21 | const config3 = ['script', ['rm -rf dist', 'rm -rf build'], { parallel: true }]; // with options 22 | // no guarantee on script running orders , but less time-consuming probably 23 | 24 | // be careful, shell scripts are not cross-platform 25 | // you'd better perform file system operations with @nowa/module-file 26 | 27 | const config4 = [ 28 | 'script', 29 | () => { 30 | // js script 31 | console.log('done'); 32 | }, 33 | ]; 34 | ``` 35 | 36 | ## noWait noRetrigger 37 | 38 | consider this workflow 39 | 40 | 1. script `start`: [`echo start`, ``] 41 | 1. webpack watch 42 | 1. script `end`: [`echo end`] 43 | 44 | the first-run output should be something like 45 | 46 | 1. 'start' 47 | 1. `` 48 | 1. `` 49 | 1. 'end' 50 | 51 | and when you trigger a recompile (change source file), these are append to the output 52 | 53 | 1. `` 54 | 1. 'end' 55 | 56 | ### noWait: true 57 | 58 | with `noWait` option on `start` script, the first output should be 59 | 60 | 1. 'start' + `` + `` 61 | 1. 'end' 62 | 63 | the next module `module-webpack` won't wait for the script to finish 64 | 65 | ### noRetrigger: true 66 | 67 | with `noRetrigger` option on `end` script the recompile output should be 68 | 69 | 1. `` 70 | 71 | no 'end' output since it won't `retrigger` 72 | -------------------------------------------------------------------------------- /packages/core/src/lib/plugins/loadSolution.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | 3 | import { Runner } from '../runner'; 4 | import { handleESModuleDefault } from '../utils'; 5 | 6 | export class LoadSolutionPlugin { 7 | constructor(public options: LoadSolutionPlugin.IOptions = {}) {} 8 | public apply(runner: Runner, { logger }: Runner.Utils) { 9 | runner.$register('load-solution', async ({ context, config }) => { 10 | if (this.options.solution) { 11 | logger.debug('use provided solution instead of project solution file/object'); 12 | return this.options.solution; 13 | } 14 | let solution = config.solution; 15 | if (!solution) { 16 | logger.debug('config.solution is falsy'); 17 | if (this.options.fallbackSolution) { 18 | solution = this.options.fallbackSolution; 19 | logger.debug(`fallback to fallbackSolution ${solution}`); 20 | } else { 21 | logger.error('solution in your config is falsy, required path / object'); 22 | throw new Error('config.solution falsy'); 23 | } 24 | } 25 | if (typeof solution === 'object') { 26 | logger.debug('config.solution is object, returning'); 27 | return solution; 28 | } 29 | logger.debug(`config.solution is string: ${solution}`); 30 | const isModule = /^[@a-z]{1}/.test(solution); 31 | logger.debug(`it ${isModule ? 'is' : `isn't`} a node module`); 32 | const solutionPath = isModule ? solution : resolve(context, solution); 33 | logger.debug(`using solution ${solutionPath}`); 34 | if (isModule) { 35 | logger.debug(`solution path ${require.resolve(solutionPath)}`); 36 | const { version } = require(`${solutionPath}/package.json`); 37 | version && logger.info(`Using ${solution}@${version}`); 38 | } 39 | try { 40 | return handleESModuleDefault(require(solutionPath)); 41 | } catch (e) { 42 | logger.error(e); 43 | throw new Error(`resolve solution failed`); 44 | } 45 | }); 46 | } 47 | } 48 | 49 | export namespace LoadSolutionPlugin { 50 | export interface IOptions { 51 | solution?: object; 52 | fallbackSolution?: string | object; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/module-script/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.3.2](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-script@0.3.1...@nowa/module-script@0.3.2) (2019-12-29) 7 | 8 | **Note:** Version bump only for package @nowa/module-script 9 | 10 | 11 | 12 | 13 | 14 | ## [0.3.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-script@0.3.0...@nowa/module-script@0.3.1) (2019-12-29) 15 | 16 | **Note:** Version bump only for package @nowa/module-script 17 | 18 | 19 | 20 | 21 | 22 | 23 | # [0.3.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-script@0.2.2...@nowa/module-script@0.3.0) (2018-07-18) 24 | 25 | 26 | ### Features 27 | 28 | * adapt to [@nowa](https://github.com/nowa)/core 0.7.1 ([4837070](https://github.com/nowa-webpack/nowa2/commit/4837070)) 29 | 30 | 31 | 32 | 33 | 34 | ## [0.2.2](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-script@0.2.1...@nowa/module-script@0.2.2) (2018-05-27) 35 | 36 | 37 | 38 | 39 | **Note:** Version bump only for package @nowa/module-script 40 | 41 | 42 | ## [0.2.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-script@0.2.0...@nowa/module-script@0.2.1) (2018-05-27) 43 | 44 | 45 | 46 | 47 | **Note:** Version bump only for package @nowa/module-script 48 | 49 | 50 | # [0.2.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-script@0.1.1...@nowa/module-script@0.2.0) (2018-02-07) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * this bind when `parallel` is true ([aeeea8b](https://github.com/nowa-webpack/nowa2/commit/aeeea8b)) 56 | 57 | 58 | ### Features 59 | 60 | * compatible for [@nowa](https://github.com/nowa)/core break change ([3e29281](https://github.com/nowa-webpack/nowa2/commit/3e29281)) 61 | * debug log module config ([a3927ef](https://github.com/nowa-webpack/nowa2/commit/a3927ef)) 62 | 63 | 64 | 65 | 66 | 67 | ## 0.1.1 (2018-01-31) 68 | 69 | 70 | ### Bug Fixes 71 | 72 | * info output format ([6ee50a5](https://github.com/nowa-webpack/nowa2/commit/6ee50a5)) 73 | -------------------------------------------------------------------------------- /packages/module-file/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.3.2](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-file@0.3.1...@nowa/module-file@0.3.2) (2019-12-29) 7 | 8 | **Note:** Version bump only for package @nowa/module-file 9 | 10 | 11 | 12 | 13 | 14 | ## [0.3.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-file@0.3.0...@nowa/module-file@0.3.1) (2019-12-29) 15 | 16 | 17 | ### Bug Fixes 18 | 19 | * path issues when copying directories (temporary fix) ([44bc68f](https://github.com/nowa-webpack/nowa2/commit/44bc68f937a0e0387715af4d087e1bc166adb458)) 20 | 21 | 22 | 23 | 24 | 25 | 26 | # [0.3.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-file@0.2.2...@nowa/module-file@0.3.0) (2018-07-18) 27 | 28 | 29 | ### Features 30 | 31 | * adapt to [@nowa](https://github.com/nowa)/core 0.7.1 ([4837070](https://github.com/nowa-webpack/nowa2/commit/4837070)) 32 | 33 | 34 | 35 | 36 | 37 | ## [0.2.2](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-file@0.2.1...@nowa/module-file@0.2.2) (2018-05-27) 38 | 39 | 40 | 41 | 42 | **Note:** Version bump only for package @nowa/module-file 43 | 44 | 45 | ## [0.2.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-file@0.2.0...@nowa/module-file@0.2.1) (2018-05-27) 46 | 47 | 48 | 49 | 50 | **Note:** Version bump only for package @nowa/module-file 51 | 52 | 53 | # [0.2.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-file@0.1.1...@nowa/module-file@0.2.0) (2018-02-07) 54 | 55 | 56 | ### Features 57 | 58 | * action.from support absolute path with glob ([4cc317d](https://github.com/nowa-webpack/nowa2/commit/4cc317d)) 59 | * compatible for [@nowa](https://github.com/nowa)/core break change ([3e29281](https://github.com/nowa-webpack/nowa2/commit/3e29281)) 60 | * debug log module config ([a3927ef](https://github.com/nowa-webpack/nowa2/commit/a3927ef)) 61 | * remove supporting string[] in action.from ([52b379e](https://github.com/nowa-webpack/nowa2/commit/52b379e)) 62 | 63 | 64 | 65 | 66 | 67 | ## 0.1.1 (2018-01-31) 68 | 69 | 70 | ### Bug Fixes 71 | 72 | * glob path issue ([e3138be](https://github.com/nowa-webpack/nowa2/commit/e3138be)) 73 | -------------------------------------------------------------------------------- /packages/cli/src/lib/loadCommands.ts: -------------------------------------------------------------------------------- 1 | import { Runner, Types } from '@nowa/core'; 2 | import * as archy from 'archy'; 3 | import * as cliUI from 'cliui'; 4 | 5 | import * as parser from 'yargs-parser'; 6 | 7 | export class LoadCommandsPlugin { 8 | public apply(runner: Runner, utils: Runner.Utils) { 9 | const { logger, chalk } = utils; 10 | runner.$register('load-commands', async ({ solution }) => { 11 | logger.debug(`got argv ${process.argv.join(' ')}`); 12 | const commands: string[] = []; 13 | for (const arg of process.argv.slice(2)) { 14 | if (arg.startsWith('-')) { 15 | break; // only the strings before first option are considered as commands 16 | } 17 | commands.push(arg); 18 | } 19 | logger.debug(`got actual argv ${commands.join(' ')}`); 20 | const { _ } = parser(commands); 21 | logger.debug(`got actual commands ${_.join(' ')}`); 22 | if (_.length === 0) { 23 | const helpInfo = solution.intro; 24 | if (helpInfo) { 25 | logger.debug(`got help information`, helpInfo); 26 | logger.log(`Available NOWA Commands:`); 27 | const ui = cliUI({ width: 80 }); 28 | const archyString = archy(convertHelpToArchy(helpInfo)); 29 | archyString.split('\n').forEach(line => { 30 | const [left, right] = line.split('_NOWA_'); 31 | ui.div( 32 | `${left}`, 33 | right && { 34 | align: 'right', 35 | text: chalk`{blueBright ${right}}`, 36 | }, 37 | ); 38 | }); 39 | logger.log(ui.toString()); 40 | } 41 | } 42 | return _; 43 | }); 44 | } 45 | } 46 | 47 | function convertSubHelp(key: string, help: string | Types.ISolutionHelpRegistry | undefined): archy.Data | string { 48 | if (!help || typeof help === 'string') { 49 | return `${key}${(help && `_NOWA_${help}`) || ''}`; 50 | } 51 | return { 52 | label: `${key}${(help._default && `_NOWA_${help._default}`) || ''}`, 53 | nodes: Object.keys(help) 54 | .filter(key => !key.startsWith('_')) 55 | .map(key => { 56 | return convertSubHelp(key, help[key]); 57 | }), 58 | }; 59 | } 60 | function convertHelpToArchy(help: Types.ISolution['intro'] = {}): archy.Data { 61 | return { 62 | label: '', 63 | nodes: Object.keys(help).map(key => { 64 | return convertSubHelp(key, help[key]); 65 | }), 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /packages/module-file/README.md: -------------------------------------------------------------------------------- 1 | # nowa-module-file 2 | 3 | ## Module Config 4 | 5 | ```ts 6 | export interface IBaseAction { 7 | type: 'copy' | 'move' | 'remove' | 'empty' | 'ensure'; 8 | from: string; 9 | } 10 | export interface ISingleArgAction extends IBaseAction { 11 | type: 'remove' | 'empty' | 'ensure'; 12 | } 13 | export interface IDoubleArgAction extends IBaseAction { 14 | type: 'copy' | 'move'; 15 | to: string; 16 | } 17 | export type SingleAction = ISingleArgAction | IDoubleArgAction; 18 | 19 | export type Config = ['file', SingleAction | SingleAction[]]; 20 | ``` 21 | 22 | ## Usage 23 | 24 | ```js 25 | const config1 = ['file', { type: 'copy', from: './src/lib/', to: './dist/' }]; // copy files 26 | const config2 = ['file', { type: 'copy', from: './src/lib/*.js', to: './dist/' }]; // support glob 27 | const config3 = ['file', { type: 'empty', from: './build/' }]; // empty a folder 28 | const config4 = ['file', { type: 'ensure', from: './build/assets/js/' }]; // ensure a path 29 | const config5 = ['file', { type: 'move', from: './build/index.js', to: './build/entry.js' }]; // move (rename) a file 30 | const config6 = ['file', [{ type: 'remove', from: './build/' }, { type: 'remove', from: './dist/' }]]; // multiple actions 31 | ``` 32 | 33 | ## Actions 34 | 35 | `module-file` provides 5 kinds of actions 36 | 37 | * `copy` copy files or folders 38 | * `move` move files or folders 39 | * `remove` delete files or folders 40 | * `empty` empty folders 41 | * `ensure` ensure a path is valid, create any folder if needed 42 | 43 | some actions can have `to` property. 44 | 45 | ## Action.from 46 | 47 | `from` can be a normal path or path with glob patterns, `module-webpack` uses [globby](https://www.npmjs.com/package/globby) to resolve them; 48 | 49 | relative paths will be treated from `project root` (actually `context` in `nowa2`). 50 | 51 | ## Action.to 52 | 53 | `to` can only be used in action `copy` & `move`. 54 | 55 | **Caution, If you want to specify a folder, always add a slash `/` to the end. Or it will be treaded as a file** 56 | 57 | ### Example 58 | 59 | if you have a project with a file `file.js` 60 | 61 | ``` 62 | └── file.js 63 | ``` 64 | 65 | with config `{ type: 'copy', from: './file.js', to: './dist' }`, it will be 66 | 67 | ``` 68 | ├── dist ---- file, content same as file.js 69 | └── file.js 70 | ``` 71 | 72 | with config `{ type: 'copy', from: './file.js', to: './dist/' }`, it will be 73 | 74 | ``` 75 | ├── dist ---- folder 76 | │ └── file.js ---- file 77 | └── file.js 78 | ``` 79 | -------------------------------------------------------------------------------- /packages/core/src/lib/core/hookable.ts: -------------------------------------------------------------------------------- 1 | export abstract class Hookable { 2 | public $hooks: Hookable.Registry = {}; 3 | 4 | public $register( 5 | hookName: HookName, 6 | handler: Hookable.Handler, 7 | ): void { 8 | this.$hooks[hookName as string] || (this.$hooks[hookName as string] = []); 9 | this.$hooks[hookName as string]!.push(handler); 10 | } 11 | 12 | public async $applyHook(hookName: HookName, param?: HookGroup[HookName][0]): Promise { 13 | const plugins = this.$hooks[hookName as string]; 14 | if (plugins) { 15 | await Promise.all(plugins.map(handler => handler.call(this, param))); 16 | } 17 | return; 18 | } 19 | 20 | public async $applyHookBail( 21 | hookName: HookName, 22 | param?: HookGroup[HookName][0], 23 | FIFO: boolean = false, 24 | ): Promise { 25 | const plugins = 26 | this.$hooks[hookName as string] && (FIFO ? this.$hooks[hookName as string] : Array.from(this.$hooks[hookName as string]!).reverse()); 27 | if (plugins) { 28 | for (const handler of plugins!) { 29 | const result = await handler.call(this, param); 30 | if (result !== undefined) { 31 | return result; 32 | } 33 | } 34 | } 35 | throw new Error(`All ${hookName} hooks returns undefined`); 36 | } 37 | 38 | public async $applyHookWaterfall( 39 | hookName: HookName, 40 | initial: HookGroup[HookName][0], 41 | FIFO: boolean = false, 42 | ): Promise { 43 | const plugins = 44 | this.$hooks[hookName as string] && (FIFO ? this.$hooks[hookName as string] : Array.from(this.$hooks[hookName as string]!).reverse()); 45 | let prevResult = initial; 46 | if (plugins) { 47 | for (const func of plugins!) { 48 | const result = await func.call(this, prevResult); 49 | if (result !== undefined) { 50 | prevResult = result; 51 | } 52 | } 53 | } 54 | return prevResult as HookGroup[HookName][1]; 55 | } 56 | } 57 | 58 | export namespace Hookable { 59 | export type Handler = (this: This, param: Param) => Promise | Expected | undefined; 60 | 61 | export interface IHookGroup { 62 | [hookName: string]: [any, any]; // hook-name: [param-pass-to-hook, expected-result-from-hook] 63 | } 64 | 65 | export type Registry = { 66 | [hookName in keyof IHookGroup]: Array> | undefined 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /packages/module-script/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nowa/core'; 2 | import { shell } from 'execa'; 3 | 4 | export default class ModuleScript extends Module.Async { 5 | public $name = 'script'; 6 | public scripts?: ModuleScript.SingleScript[]; 7 | public options?: ModuleScript.IOptions; 8 | public alreadyRun: boolean = false; 9 | 10 | public async init() { 11 | const { logger } = this.$utils; 12 | const [scripts, options] = this.$runtime.config; 13 | this.options = options || {}; 14 | this.scripts = ([] as ModuleScript.SingleScript[]).concat(scripts).map(this.validateScript); 15 | logger.info(`got ${this.scripts.length} scripts`); 16 | logger.debug(this.scripts); 17 | } 18 | 19 | public async run() { 20 | const { logger } = this.$utils; 21 | if (this.alreadyRun && this.options && this.options.noRetrigger) { 22 | logger.debug('skip run scripts since noRetrigger has been set'); 23 | } else { 24 | logger.debug('start to run scripts'); 25 | if (this.options && this.options.noWait) { 26 | logger.debug('noWait mode'); 27 | this._run().then( 28 | () => logger.debug('finish all scripts'), 29 | e => { 30 | logger.error(e); 31 | process.exit(1); 32 | }, 33 | ); 34 | } else { 35 | logger.debug('normal mode'); 36 | await this._run(); 37 | } 38 | logger.debug('finish all scripts'); 39 | this.alreadyRun = true; 40 | } 41 | } 42 | 43 | private async _run() { 44 | const { logger } = this.$utils; 45 | if (this.options && this.options.parallel) { 46 | logger.debug('parallel mode'); 47 | await Promise.all(this.scripts!.map(this._runScript, this)); 48 | } else { 49 | logger.debug('sequential mode'); 50 | for (const script of this.scripts!) { 51 | await this._runScript(script); 52 | } 53 | } 54 | } 55 | 56 | private async _runScript(script: string | (() => void | Promise)) { 57 | const { logger } = this.$utils; 58 | if (typeof script === 'string') { 59 | logger.info('run shell', script); 60 | await shell(script, { stdio: 'inherit', cwd: this.$runtime.context, maxBuffer: 100000000 }); 61 | } else if (typeof script === 'function') { 62 | logger.info('run js', script.name || 'anonymous'); 63 | await script(); 64 | } 65 | } 66 | 67 | private validateScript(script: any) { 68 | if (typeof script === 'string' || typeof script === 'function') { 69 | return script; 70 | } 71 | const { logger } = this.$utils; 72 | logger.error(`script should be string / function but received ${typeof script}`); 73 | throw new Error('invalid typeof script'); 74 | } 75 | } 76 | 77 | export namespace ModuleScript { 78 | export type SingleScript = string | (() => void | Promise); 79 | export interface IOptions { 80 | parallel?: boolean; 81 | noWait?: boolean; 82 | noRetrigger?: boolean; 83 | } 84 | export type Config = [SingleScript | SingleScript[], IOptions | undefined]; 85 | } 86 | -------------------------------------------------------------------------------- /packages/cli/src/index.ts: -------------------------------------------------------------------------------- 1 | import { format } from 'util'; 2 | 3 | import { createDefaultRunner, Runner } from '@nowa/core'; 4 | import chalk from 'chalk'; 5 | import * as inquirer from 'inquirer'; 6 | import * as ora from 'ora'; 7 | import * as yargs from 'yargs'; 8 | 9 | import { InitContextPlugin } from './lib/initContext'; 10 | import { LoadCommandsPlugin } from './lib/loadCommands'; 11 | import { LoadOptionsPlugin } from './lib/loadOptions'; 12 | 13 | const isDebug = !!process.env.NOWA_DEBUG; 14 | 15 | const getRandomColor = () => { 16 | const letters = '0123456789ABCDEF'; 17 | const colorArr = ['#']; 18 | for (let i = 0; i < 6; i++) { 19 | colorArr.push(letters[(Math.random() * 16) | 0]); // tslint:disable-line:no-bitwise 20 | } 21 | return colorArr.join(''); 22 | }; 23 | 24 | export const createUtils: Runner.UtilsCreator = (name: string = 'unknown') => { 25 | const color = getRandomColor(); 26 | const oraInstance = ora(); 27 | const spinner = { 28 | clear: () => { 29 | oraInstance.clear(); 30 | return oraInstance; 31 | }, 32 | fail: (text?: string) => { 33 | oraInstance.fail(text ? `${name} ${text}` : undefined); 34 | return oraInstance; 35 | }, 36 | info: (text?: string) => { 37 | oraInstance.info(text ? `${name} ${text}` : undefined); 38 | return oraInstance; 39 | }, 40 | promise: (promise: Promise, text?: string) => { 41 | return (ora as any).promise(promise, text); // @types/ora is not compatible with ora@2 42 | }, 43 | start: (text: string = 'N/A') => { 44 | oraInstance.start(`${name} ${text}`); 45 | return spinner; 46 | }, 47 | stop: () => { 48 | oraInstance.stop(); 49 | return spinner; 50 | }, 51 | succeed: (text?: string) => { 52 | oraInstance.succeed(text ? `${name} ${text}` : undefined); 53 | return oraInstance; 54 | }, 55 | warn: (text?: string) => { 56 | oraInstance.warn(text ? `${name} ${text}` : undefined); 57 | return oraInstance; 58 | }, 59 | }; 60 | const prompt = inquirer.createPromptModule(); 61 | const prefix = chalk`{magenta nowa} {hex('${color}') ${name}}`; 62 | return { 63 | chalk, 64 | logger: { 65 | debug: isDebug ? (first: any, ...rest: any[]) => console.log(`${prefix} ${chalk`{gray ${format(first, ...rest)}}`}`) : () => {}, // tslint:disable-line:no-empty 66 | error: (first: any, ...rest: any[]) => console.error(`${prefix} ${chalk`{red ${format(first, ...rest)}}`}`), 67 | info: (first: any, ...rest: any[]) => console.log(`${prefix} ${chalk`{blueBright ${format(first, ...rest)}}`}`), 68 | log: console.log, 69 | warn: (first: any, ...rest: any[]) => console.warn(`${prefix} ${chalk`{yellow ${format(first, ...rest)}}`}`), 70 | }, 71 | prompt: async (desc: string, options: object = {}) => { 72 | const question = { ...options, message: desc, name: `quick_prompt` }; 73 | const { quick_prompt } = await prompt<{ quick_prompt: any }>(question); 74 | return quick_prompt; 75 | }, 76 | spinner, 77 | }; 78 | }; 79 | 80 | export const run = async () => { 81 | const runner = await createDefaultRunner(createUtils, [ 82 | new InitContextPlugin(), 83 | new LoadCommandsPlugin(), 84 | new LoadOptionsPlugin({ yargs, inquirer }), 85 | ]); 86 | await runner.init(); 87 | await runner.run(); 88 | }; 89 | -------------------------------------------------------------------------------- /packages/core/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { constants } from 'fs'; 2 | import { access } from 'fs-extra'; 3 | import * as pathToRegexp from 'path-to-regexp'; 4 | 5 | import { IConfigConfigRegistry, IConfigConfigValues, ISolutionCommandDescription, ISolutionCommandRegistry } from './types'; 6 | 7 | export function parser( 8 | target: 'config.config', 9 | commands: string[], 10 | debug: (...args: any[]) => void, 11 | source: IConfigConfigRegistry | undefined, 12 | ): { params: { [paramName: string]: string }; result: IConfigConfigValues } | undefined; 13 | export function parser( 14 | target: 'solution.commands' | 'config.commands', 15 | commands: string[], 16 | debug: (...args: any[]) => void, 17 | source: ISolutionCommandRegistry | undefined, 18 | ): { params: { [paramName: string]: string }; result: ISolutionCommandDescription } | undefined; 19 | export function parser( 20 | target: string, 21 | commands: string[], 22 | debug: (...args: any[]) => void, 23 | source: { [name: string]: any } | undefined, 24 | ): { params: { [paramName: string]: string }; result: any } | undefined; 25 | export function parser( 26 | target: string, 27 | commands: string[], 28 | debug: (...args: any[]) => void, 29 | source: { [name: string]: any } | undefined, 30 | ): { params: { [paramName: string]: string }; result: any } | undefined { 31 | // TODO: Stop this overload, remove parser 32 | if (!source) { 33 | debug(`${target} is falsy`); 34 | return undefined; 35 | } 36 | const commandPath = '/' + commands.join('/'); 37 | const routes = Object.keys(source).map(path => { 38 | const keys: pathToRegexp.Key[] = []; 39 | const re = pathToRegexp(`/${path}`, keys); 40 | const test = (path: string): { [paramName: string]: string } | null => { 41 | const result = re.exec(path); 42 | if (!result || keys.length === 0) { 43 | return result ? {} : null; 44 | } 45 | const params: { [paramName: string]: string } = {}; 46 | keys.forEach(({ name }, index) => { 47 | params[name] = result[index + 1]; 48 | }); 49 | return params; 50 | }; 51 | return { 52 | result: source[path], 53 | test, 54 | }; 55 | }); 56 | for (const route of routes) { 57 | const { test, result } = route; 58 | const params = test(commandPath); 59 | if (params) { 60 | return { params, result }; 61 | } 62 | } 63 | return undefined; 64 | } 65 | 66 | export const handleESModuleDefault = (moduleExport: T): T => { 67 | if (moduleExport.__esModule) { 68 | return (moduleExport.default as any) as T; 69 | } else { 70 | return moduleExport; 71 | } 72 | }; 73 | 74 | export const deleteUndefined = (obj: T, recursive = false): T => { 75 | Object.keys(obj).forEach(key => { 76 | const value = (obj as any)[key]; 77 | if (value === undefined) { 78 | delete (obj as any)[key]; 79 | } 80 | if (recursive) { 81 | if (value && typeof value === 'object') { 82 | deleteUndefined(value, true); 83 | } 84 | } 85 | }); 86 | return obj; 87 | }; 88 | 89 | export const requireFile = async (filePath: string) => { 90 | try { 91 | await access(filePath, constants.R_OK); 92 | } catch { 93 | return undefined; 94 | } 95 | return handleESModuleDefault(require(filePath)); 96 | }; 97 | 98 | // tslint:disable-next-line:variable-name 99 | export const captureStack = (message: string, Constructor: ErrorConstructor = Error) => { 100 | const error = new Constructor(message); 101 | return error.stack; 102 | }; 103 | -------------------------------------------------------------------------------- /packages/cli/src/lib/loadOptions.ts: -------------------------------------------------------------------------------- 1 | import { Runner, utils } from '@nowa/core'; 2 | import * as inquirer from 'inquirer'; 3 | import * as Yargs from 'yargs'; 4 | 5 | export class LoadOptionsPlugin { 6 | constructor(public options: LoadOptionsPlugin.IOptions) {} 7 | public apply(runner: Runner, pluginUtils: Runner.Utils) { 8 | const { logger } = pluginUtils; 9 | runner.$register('load-options', async ({ config, solution }) => { 10 | const yargs = this.options.yargs; 11 | const inquirer = this.options.inquirer; 12 | yargs 13 | .version(false) 14 | .help('help') 15 | .alias('h', 'help'); 16 | const { options: optionDescriptions, description } = solution; // ignore moduleDescriptions 17 | const configDefaults = config; 18 | if (description) { 19 | yargs.usage(description); 20 | } 21 | const questions: { [order: number]: inquirer.Question[] } = {}; 22 | Object.keys(optionDescriptions).forEach(name => { 23 | const desc = optionDescriptions[name]; 24 | const yargOption: any = { 25 | alias: desc.alias, 26 | default: configDefaults[name] === undefined ? desc.default : configDefaults[name], 27 | description: desc.description, 28 | group: desc.group, 29 | hidden: desc.hidden, 30 | }; 31 | switch (desc.type) { 32 | case 'string': 33 | case 'number': 34 | case 'array': 35 | case 'boolean': 36 | yargOption.type = desc.type; 37 | yargs.option(name, utils.deleteUndefined(yargOption)); 38 | break; 39 | case 'choice': 40 | yargOption.choices = desc.choices; 41 | yargs.option(name, utils.deleteUndefined(yargOption)); 42 | break; 43 | case 'prompt': 44 | const order = desc.order || 0; 45 | if (!questions[order]) { 46 | questions[order] = []; 47 | } 48 | const question: any = { 49 | default: configDefaults[name] === undefined ? desc.default : configDefaults[name], 50 | filter: desc.filter, 51 | message: desc.description, 52 | name, 53 | type: desc.prompt, 54 | validate: desc.validate, 55 | }; 56 | switch (desc.prompt) { 57 | case 'list': 58 | case 'checkbox': 59 | question.choice = desc.choices; 60 | case 'input': 61 | case 'password': 62 | case 'confirm': 63 | questions[order].push(utils.deleteUndefined(question)); 64 | break; 65 | default: 66 | logger.warn(`unknown prumpt option type ${(desc as any).prompt} for ${name}, ignored`); 67 | } 68 | break; 69 | default: 70 | logger.warn(`unknown option type ${(desc as any).type} for ${name}, ignored`); 71 | } 72 | }); 73 | if (Object.keys(questions).length > 0) { 74 | const orderedQuestions: inquirer.Question[] = []; 75 | Object.keys(questions) 76 | .map(i => Number(i)) 77 | .sort((a, b) => a - b) 78 | .forEach(order => { 79 | orderedQuestions.push(...questions[order]); 80 | }); 81 | const prompt = inquirer.createPromptModule(); 82 | const result = await prompt(orderedQuestions); 83 | return { ...yargs.argv, ...result }; 84 | } 85 | return { ...yargs.argv }; 86 | }); 87 | } 88 | } 89 | 90 | export namespace LoadOptionsPlugin { 91 | export interface IOptions { 92 | yargs: typeof Yargs; 93 | inquirer: typeof inquirer; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /packages/core/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nowa/core", 3 | "version": "0.7.7", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/fs-extra": { 8 | "version": "5.1.0", 9 | "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.1.0.tgz", 10 | "integrity": "sha512-AInn5+UBFIK9FK5xc9yP5e3TQSPNNgjHByqYcj9g5elVBnDQcQL7PlO1CIRy2gWlbwK7UPYqi7vRvFA44dCmYQ==", 11 | "dev": true, 12 | "requires": { 13 | "@types/node": "*" 14 | } 15 | }, 16 | "@types/node": { 17 | "version": "13.1.1", 18 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.1.tgz", 19 | "integrity": "sha512-hx6zWtudh3Arsbl3cXay+JnkvVgCKzCWKv42C9J01N2T2np4h8w5X8u6Tpz5mj38kE3M9FM0Pazx8vKFFMnjLQ==", 20 | "dev": true 21 | }, 22 | "ansi-styles": { 23 | "version": "3.2.1", 24 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 25 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 26 | "requires": { 27 | "color-convert": "^1.9.0" 28 | } 29 | }, 30 | "chalk": { 31 | "version": "2.4.2", 32 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 33 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 34 | "requires": { 35 | "ansi-styles": "^3.2.1", 36 | "escape-string-regexp": "^1.0.5", 37 | "supports-color": "^5.3.0" 38 | } 39 | }, 40 | "color-convert": { 41 | "version": "1.9.3", 42 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 43 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 44 | "requires": { 45 | "color-name": "1.1.3" 46 | } 47 | }, 48 | "color-name": { 49 | "version": "1.1.3", 50 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 51 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 52 | }, 53 | "escape-string-regexp": { 54 | "version": "1.0.5", 55 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 56 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 57 | }, 58 | "fs-extra": { 59 | "version": "6.0.1", 60 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", 61 | "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", 62 | "requires": { 63 | "graceful-fs": "^4.1.2", 64 | "jsonfile": "^4.0.0", 65 | "universalify": "^0.1.0" 66 | } 67 | }, 68 | "graceful-fs": { 69 | "version": "4.2.3", 70 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 71 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" 72 | }, 73 | "has-flag": { 74 | "version": "3.0.0", 75 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 76 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 77 | }, 78 | "jsonfile": { 79 | "version": "4.0.0", 80 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 81 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 82 | "requires": { 83 | "graceful-fs": "^4.1.6" 84 | } 85 | }, 86 | "path-to-regexp": { 87 | "version": "2.4.0", 88 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", 89 | "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==" 90 | }, 91 | "supports-color": { 92 | "version": "5.5.0", 93 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 94 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 95 | "requires": { 96 | "has-flag": "^3.0.0" 97 | } 98 | }, 99 | "tslib": { 100 | "version": "1.10.0", 101 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", 102 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" 103 | }, 104 | "universalify": { 105 | "version": "0.1.2", 106 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 107 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /packages/module-webpack/README.md: -------------------------------------------------------------------------------- 1 | # nowa-module-webpack 2 | 3 | ## Module Config 4 | 5 | ```ts 6 | export interface IOptions { 7 | mode?: 'run' | 'watch' | 'devServer'; 8 | } 9 | export type ConfigFileContent = 10 | | (( 11 | { context, options }: { context: string; options: object }, 12 | ) => Webpack.Configuration | Webpack.Configuration[] | Promise) 13 | | Webpack.Configuration 14 | | Webpack.Configuration[]; 15 | export type SingleConfig = /* path to configFile */ string | ConfigFileContent; 16 | export type Config = ['webpack', SingleConfig | SingleConfig[], IOptions | undefined]; 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```js 22 | const config1 = ['webpack', 'sompath/webpack.config.js']; // config file 23 | const config2 = ['webpack', ['sompath/webpack.app.js', 'sompath/webpack.page.js']]; // MultiCompiler 24 | const config3 = ['webpack', { entry: './src/index.js', ...otherWebpackConfig }]; // raw config 25 | const config4 = ['webpack', { watch: true, ...o }]; // watch mode 26 | const config5 = ['webpack', { devServer: { ...d }, ...o }]; // devServer mode 27 | const config6 = ['webpack', { devServer: { ...d }, ...o }, { mode: 'run' }]; // run mode (ignore devServer) 28 | ``` 29 | 30 | ## Mode 31 | 32 | there are 3 modes now 33 | 34 | * webpack run 35 | * webpack watch 36 | * webpack-dev-server 37 | 38 | if `mode` is not set, `module-webpack` will decide it directly from the final config. 39 | 40 | 1. `config.devServer` is truthy => webpack-dev-server 41 | 1. `config.watch` is truthy => webpack watch source files and changes triggers recompile 42 | 1. else => simple webpack build 43 | 44 | ## Function Type Webpack Config 45 | 46 | Webpack supports [exporting a function as a config](https://webpack.js.org/configuration/configuration-types/#exporting-a-function). 47 | But its hard to use. 48 | 49 | Therefore, `module-webpack` replace that support with a more advanced solution. 50 | 51 | Instead of `function (env, argv) {}` from native webapck, `module-webapck` supports `function ({ context, options }) {}` 52 | 53 | * string `context` is the project root (`context` in `nowa2`) 54 | * object `options` is the `nowa options` from your command line arguments, config and solution 55 | 56 | ### Examples 57 | 58 | ```shell 59 | nowa2 xxxx --language en --multiPage true 60 | ``` 61 | 62 | ```js 63 | const config1 = [ 64 | 'webpack', 65 | { 66 | config: ({ context, options }) => ({ 67 | context, 68 | entry: `./src/index.${options.language}.js`, // ./src/index.en.js 69 | ...otherWebpackConfig, 70 | }), 71 | }, 72 | ]; 73 | ``` 74 | 75 | ```js 76 | const config2 = ['webpack', 'sompath/webpack.config.js']; 77 | ``` 78 | 79 | with `sompath/webpack.config.js` 80 | 81 | ```js 82 | module.exports = async ({ context, options }) => { 83 | if (option.multiPage /* true */) { 84 | // ... 85 | } 86 | // ... 87 | }; 88 | ``` 89 | 90 | ## Overwrite Final Webpack Config 91 | 92 | In some cases we need modify `webpack` config, but we cannot change `nowa soltion` directly (in a npm package). 93 | 94 | We can create a `webpack.config.js` in project root. In this file you can access then final webpack config and return a new one to replace it. 95 | 96 | This file can export a fucntion, the function signature is `function (originalConfig, rumtime, webpack) {}` 97 | 98 | * originalConfig is the final config generated by `nowa`, will be passed to webpack soon 99 | * runtime is a object with properties 100 | > * string `context` 101 | > * object `options` 102 | > * Array `commands` is the actual command you type 103 | > e.g. `nowa2 build prod` => `['build', 'prod']` 104 | > * object `config` is the module config for `module-webpack` in you `solution` 105 | 106 | it also supports specify which command the overwrite will take place like `config` / `solution` 107 | 108 | ### Examples 109 | 110 | ```js 111 | module.exports = (config, rumtime, webpack) => { 112 | // overwrite all command using module-webpack 113 | config.plugins.push(new webpack.SomeBuiltinPlugin()); 114 | return config; 115 | }; 116 | ``` 117 | 118 | ```js 119 | module.exports = { 120 | // export an object instead of fucntion 121 | build: [ 122 | (config, rumtime, webpack) => { 123 | // overwrite on build command only 124 | config.plugins.push(new webpack.SomeBuiltinPlugin()); 125 | return config; 126 | }, 127 | ], 128 | dev: [ 129 | (config, rumtime, webpack) => { 130 | // overwrite on dev command only 131 | config.plugins.push(new webpack.SomeOtherBuiltinPlugin()); 132 | return config; 133 | }, 134 | ], 135 | }; 136 | ``` 137 | -------------------------------------------------------------------------------- /packages/module-script/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nowa/module-script", 3 | "version": "0.3.2", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/execa": { 8 | "version": "0.9.0", 9 | "resolved": "https://registry.npmjs.org/@types/execa/-/execa-0.9.0.tgz", 10 | "integrity": "sha512-mgfd93RhzjYBUHHV532turHC2j4l/qxsF/PbfDmprHDEUHmNZGlDn1CEsulGK3AfsPdhkWzZQT/S/k0UGhLGsA==", 11 | "dev": true, 12 | "requires": { 13 | "@types/node": "*" 14 | } 15 | }, 16 | "@types/node": { 17 | "version": "13.1.1", 18 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.1.tgz", 19 | "integrity": "sha512-hx6zWtudh3Arsbl3cXay+JnkvVgCKzCWKv42C9J01N2T2np4h8w5X8u6Tpz5mj38kE3M9FM0Pazx8vKFFMnjLQ==", 20 | "dev": true 21 | }, 22 | "cross-spawn": { 23 | "version": "6.0.5", 24 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 25 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 26 | "requires": { 27 | "nice-try": "^1.0.4", 28 | "path-key": "^2.0.1", 29 | "semver": "^5.5.0", 30 | "shebang-command": "^1.2.0", 31 | "which": "^1.2.9" 32 | } 33 | }, 34 | "execa": { 35 | "version": "0.10.0", 36 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", 37 | "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", 38 | "requires": { 39 | "cross-spawn": "^6.0.0", 40 | "get-stream": "^3.0.0", 41 | "is-stream": "^1.1.0", 42 | "npm-run-path": "^2.0.0", 43 | "p-finally": "^1.0.0", 44 | "signal-exit": "^3.0.0", 45 | "strip-eof": "^1.0.0" 46 | } 47 | }, 48 | "get-stream": { 49 | "version": "3.0.0", 50 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 51 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" 52 | }, 53 | "is-stream": { 54 | "version": "1.1.0", 55 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 56 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 57 | }, 58 | "isexe": { 59 | "version": "2.0.0", 60 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 61 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 62 | }, 63 | "nice-try": { 64 | "version": "1.0.5", 65 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 66 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" 67 | }, 68 | "npm-run-path": { 69 | "version": "2.0.2", 70 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 71 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 72 | "requires": { 73 | "path-key": "^2.0.0" 74 | } 75 | }, 76 | "p-finally": { 77 | "version": "1.0.0", 78 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 79 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 80 | }, 81 | "path-key": { 82 | "version": "2.0.1", 83 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 84 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" 85 | }, 86 | "semver": { 87 | "version": "5.7.1", 88 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 89 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 90 | }, 91 | "shebang-command": { 92 | "version": "1.2.0", 93 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 94 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 95 | "requires": { 96 | "shebang-regex": "^1.0.0" 97 | } 98 | }, 99 | "shebang-regex": { 100 | "version": "1.0.0", 101 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 102 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 103 | }, 104 | "signal-exit": { 105 | "version": "3.0.2", 106 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 107 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 108 | }, 109 | "strip-eof": { 110 | "version": "1.0.0", 111 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 112 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" 113 | }, 114 | "which": { 115 | "version": "1.3.1", 116 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 117 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 118 | "requires": { 119 | "isexe": "^2.0.0" 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /packages/core/src/lib/plugins/loadModules.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | 3 | import { Module } from '../core/module'; 4 | import { Runner } from '../runner'; 5 | import { handleESModuleDefault } from '../utils'; 6 | 7 | const modulePrefixes = ['@nowa/module-', '']; 8 | 9 | export class LoadModulesPlugin { 10 | constructor(public options: LoadModulesPlugin.IOptions = {}) {} 11 | 12 | public apply(runner: Runner, { logger }: Runner.Utils) { 13 | const prefixes = [...(this.options.modulePrefixes || []), ...modulePrefixes]; 14 | runner.$register('load-modules', async ({ context, commands, options, solution, createUtils }) => { 15 | logger.debug('module name prefixes', prefixes); 16 | const moduleArray = solution.actions; 17 | logger.debug(`got ${moduleArray.length} module(s) to load`); 18 | const result: Module.InstanceType[] = []; 19 | for (let [index, module] of moduleArray.entries()) { 20 | logger.debug(`resolving module ${index}`); 21 | let instance: any; 22 | if (typeof module === 'function') { 23 | logger.debug(`got an funciton as module, calling it`); 24 | module = await module({ context, options }); 25 | } 26 | if (module === undefined) { 27 | logger.debug(`got an undefined module, ignored`); 28 | continue; 29 | } else if (typeof module === 'string') { 30 | logger.debug(`got module definition ${module}`); 31 | const ClassConstructor = this._loadModule(module, context, prefixes, logger); // tslint:disable-line:variable-name 32 | if (this._checkIsNowaModule(ClassConstructor)) { 33 | logger.debug(`instantiating ${module}`); 34 | instance = new ClassConstructor({ context, commands, options, config: [] }, createUtils(ClassConstructor.name)); 35 | } else { 36 | logger.debug(`${module} is not a nowa module`); 37 | } 38 | } else if (Array.isArray(module)) { 39 | logger.debug(`got module definition ${module[0]} with ${typeof module[1]} config`); 40 | const ClassConstructor = this._loadModule(module[0], context, prefixes, logger); // tslint:disable-line:variable-name 41 | let moduleConfig = module.slice(1); 42 | if (typeof moduleConfig[0] === 'function') { 43 | logger.debug(`module config is a function, calling`); 44 | moduleConfig = [].concat(moduleConfig[0]({ context, options })); 45 | } 46 | logger.debug(`got moduleConfig ${moduleConfig}`); 47 | if (this._checkIsNowaModule(ClassConstructor)) { 48 | logger.debug(`instantiating ${module[0]}`); 49 | instance = new ClassConstructor({ context, commands, options, config: moduleConfig }, createUtils(ClassConstructor.name)); 50 | } else { 51 | logger.debug(`${module} is not a nowa module`); 52 | } 53 | } else { 54 | logger.warn(`un support module type ${typeof module} @ index, ignored`); 55 | } 56 | if (instance) { 57 | logger.debug(`instantiation success, loading`); 58 | !instance.$name && (instance.$name = 'unknown'); 59 | result.push(instance as Module.InstanceType); 60 | } else { 61 | logger.debug(`ignore ${Array.isArray(module) ? module[0] : module} since its instance is falsy`); 62 | } 63 | } 64 | return result; 65 | }); 66 | } 67 | 68 | private _loadModule(pathOrModuleName: string, context: string, prefixes: string[], logger: Runner.Utils['logger']): Module.IConstructor { 69 | const isModule = /^[@a-z]{1}/.test(pathOrModuleName); 70 | logger.debug(`${pathOrModuleName} ${isModule ? 'is' : `isn't`} a node module`); 71 | if (isModule) { 72 | for (const prefix of prefixes) { 73 | const modulePath = `${prefix}${pathOrModuleName}`; 74 | try { 75 | return handleESModuleDefault(require(modulePath)); 76 | } catch (e) { 77 | if (e.stack.indexOf(`Error: Cannot find module '${modulePath}'`) === 0) { 78 | logger.debug(e); 79 | } else { 80 | throw e; 81 | } 82 | } 83 | } 84 | } else { 85 | const modulePath = resolve(context, pathOrModuleName); 86 | return handleESModuleDefault(require(modulePath)); 87 | } 88 | logger.error(`can not load nowa module ${pathOrModuleName}`); 89 | throw new Error('module load error'); 90 | } 91 | private _checkIsNowaModule(module: Module.IConstructor): boolean { 92 | return !!(module && module.prototype && module.prototype.init && module.prototype.run); 93 | } 94 | } 95 | 96 | export namespace LoadModulesPlugin { 97 | export interface IOptions { 98 | modulePrefixes?: string[]; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /packages/core/test/lib/core/hookable.spec.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | import 'mocha'; // tslint:disable-line 3 | 4 | import { Hookable } from '../../../src/lib/core/hookable'; 5 | 6 | class TestHookable extends Hookable<{ 7 | name1: [{ name: 'first' }, { name: string }]; 8 | name2: [undefined, number]; 9 | name3: [undefined, void]; 10 | }> {} 11 | 12 | describe('Hookable', () => { 13 | let instance: TestHookable; 14 | beforeEach(() => { 15 | instance = new TestHookable(); 16 | }); 17 | 18 | it('all methods / properties exist', () => { 19 | assert(instance.$hooks); 20 | assert(instance.$register); 21 | assert(instance.$applyHook); 22 | assert(instance.$applyHookBail); 23 | assert(instance.$applyHookWaterfall); 24 | }); 25 | 26 | it('#$register works', () => { 27 | instance.$register('name3', () => {}); // tslint:disable-line 28 | assert(instance.$hooks.name3!.length === 1); 29 | instance.$register('name3', () => {}); // tslint:disable-line 30 | assert(instance.$hooks.name3!.length === 2); 31 | }); 32 | 33 | it('#$applyHook works', done => { 34 | instance.$register('name3', () => { 35 | done(); 36 | }); 37 | instance.$applyHook('name3'); 38 | }); 39 | 40 | it('#$applyHookBail works', async () => { 41 | instance.$register('name2', () => { 42 | return 1; 43 | }); 44 | instance.$register('name2', () => { 45 | return 2; 46 | }); 47 | instance.$register('name2', () => { 48 | return 3; 49 | }); 50 | assert((await instance.$applyHookBail('name2')) === 3); 51 | }); 52 | 53 | it('#$applyHookBail works in FIFO mode', async () => { 54 | instance.$register('name2', () => { 55 | return 1; 56 | }); 57 | instance.$register('name2', () => { 58 | return 2; 59 | }); 60 | instance.$register('name2', () => { 61 | return 3; 62 | }); 63 | assert((await instance.$applyHookBail('name2', undefined, true)) === 1); 64 | }); 65 | 66 | it('#$applyHookBail fails when no return found', async () => { 67 | instance.$register('name2', () => { 68 | return undefined as any; 69 | }); 70 | instance.$register('name2', () => { 71 | return undefined as any; 72 | }); 73 | instance.$register('name2', () => { 74 | return undefined as any; 75 | }); 76 | try { 77 | await instance.$applyHookBail('name2'); 78 | } catch (e) { 79 | assert(e instanceof Error); 80 | return; 81 | } 82 | assert.fail('No error thrown'); 83 | }); 84 | 85 | it('#$applyHookBail fails when no return found', async () => { 86 | instance.$register('name2', () => { 87 | return undefined as any; 88 | }); 89 | instance.$register('name2', () => { 90 | return undefined as any; 91 | }); 92 | instance.$register('name2', () => { 93 | return undefined as any; 94 | }); 95 | try { 96 | await instance.$applyHookBail('name2'); 97 | } catch (e) { 98 | assert(e instanceof Error); 99 | return; 100 | } 101 | assert.fail('No error thrown'); 102 | }); 103 | 104 | it('#$applyHookWaterfall works', async () => { 105 | instance.$register('name1', ({ name }) => { 106 | assert((name as string) === 'second'); 107 | return { name: 'finish' }; 108 | }); 109 | instance.$register('name1', ({ name }) => { 110 | assert((name as string) === 'second'); 111 | return undefined; 112 | }); 113 | instance.$register('name1', ({ name }) => { 114 | assert(name === 'first'); 115 | return { name: 'second' }; 116 | }); 117 | const data = await instance.$applyHookWaterfall('name1', { name: 'first' }); 118 | assert(data.name === 'finish'); 119 | }); 120 | 121 | it('#$applyHookWaterfall works in FIFO mode', async () => { 122 | instance.$register('name1', ({ name }) => { 123 | assert(name === 'first'); 124 | return { name: 'second' }; 125 | }); 126 | instance.$register('name1', ({ name }) => { 127 | assert((name as string) === 'second'); 128 | return undefined; 129 | }); 130 | instance.$register('name1', ({ name }) => { 131 | assert((name as string) === 'second'); 132 | return { name: 'finish' }; 133 | }); 134 | assert.deepEqual(await instance.$applyHookWaterfall('name1', { name: 'first' }, true), { name: 'finish' }); 135 | }); 136 | 137 | it('no hook cases', async () => { 138 | assert((await instance.$applyHook('name3')) === undefined); 139 | assert.deepEqual(await instance.$applyHookWaterfall('name1', { name: 'first' }), { name: 'first' }); 140 | try { 141 | await instance.$applyHookBail('name3'); 142 | } catch (e) { 143 | assert(e instanceof Error); 144 | return; 145 | } 146 | assert.fail('No error thrown'); 147 | }); 148 | }); 149 | -------------------------------------------------------------------------------- /packages/module-file/src/index.ts: -------------------------------------------------------------------------------- 1 | import { basename, dirname, resolve } from 'path'; 2 | 3 | import { Module } from '@nowa/core'; 4 | import { copy, emptyDir, ensureDir, move, remove, stat } from 'fs-extra'; 5 | import * as globby from 'globby'; 6 | import * as isGlob from 'is-glob'; 7 | 8 | export default class ModuleFile extends Module.Async { 9 | public $name = 'file'; 10 | public actions?: ModuleFile.SingleAction[]; 11 | 12 | public async init() { 13 | const { logger } = this.$utils; 14 | const [actions] = this.$runtime.config; 15 | this.actions = ([] as ModuleFile.SingleAction[]).concat(actions); 16 | logger.info(`got ${this.actions.length} file actions`); 17 | logger.debug(this.actions); 18 | } 19 | 20 | public async run() { 21 | const { logger } = this.$utils; 22 | const { context } = this.$runtime; 23 | for (const [index, action] of this.actions!.entries()) { 24 | const files = (await this._getFiles(action.from)).map(file => resolve(context, file)); 25 | logger.debug(`find ${files.length} paths in action ${index}`); 26 | logger.debug(files); 27 | switch (action.type) { 28 | case 'remove': 29 | await Promise.all( 30 | files.map(file => { 31 | logger.debug(`removing ${file}`); 32 | return remove(file); 33 | }), 34 | ); 35 | logger.info(`emptied ${files.length} file(s) / folder(s)`); 36 | continue; 37 | case 'empty': 38 | await Promise.all( 39 | files.map(file => { 40 | logger.debug(`emptyDir ${file}`); 41 | return emptyDir(file); 42 | }), 43 | ); 44 | logger.info(`emptied ${files.length} folder(s)`); 45 | continue; 46 | case 'ensure': 47 | await Promise.all( 48 | files.map(file => { 49 | logger.debug(`ensureDir ${file}`); 50 | return ensureDir(file); 51 | }), 52 | ); 53 | logger.info(`ensured ${files.length} folder(s)`); 54 | continue; 55 | case 'move': 56 | case 'copy': { 57 | const target = resolve(context, action.to); 58 | const targetIsDir = action.to.endsWith('/'); 59 | if (!targetIsDir && files.length > 1) { 60 | logger.error(`in`, action); 61 | logger.error(`${action.type} multiple files to a single file is not valid`); 62 | throw new Error(`${action.type} multiple to single file`); 63 | } 64 | if (targetIsDir) { 65 | await ensureDir(target); 66 | } else { 67 | await ensureDir(dirname(target)); 68 | } 69 | if (action.type === 'copy') { 70 | await Promise.all( 71 | files.map(file => { 72 | return stat(file) // FIXME: file & directory 73 | .then(stat => stat.isDirectory()) 74 | .then(sourceIsDir => { 75 | const dest = targetIsDir ? (sourceIsDir ? target : resolve(target, basename(file))) : target; 76 | logger.debug(`copy ${file} to ${dest}`); 77 | return copy(file, dest, { overwrite: true }); 78 | }); 79 | }), 80 | ); 81 | logger.info(`copied ${files.length} file(s) / folder(s)`); 82 | } else if (action.type === 'move') { 83 | await Promise.all( 84 | files.map(file => { 85 | const dest = targetIsDir ? resolve(target, basename(file)) : target; 86 | logger.debug(`copy ${file} to ${dest}`); 87 | return move(file, dest, { overwrite: true }); 88 | }), 89 | ); 90 | logger.info(`moved ${files.length} file(s) / folder(s)`); 91 | } 92 | continue; 93 | } 94 | default: 95 | logger.error(`find invalid type ${(action as ModuleFile.SingleAction).type} @ action ${index}`); 96 | throw new Error('invalid action type'); 97 | } 98 | } 99 | } 100 | 101 | private async _getFiles(filePath: string): Promise { 102 | if (isGlob(filePath)) { 103 | return globby(filePath, { cwd: this.$runtime.context }); 104 | } else { 105 | return [filePath]; 106 | } 107 | } 108 | } 109 | 110 | export namespace ModuleFile { 111 | export interface IBaseAction { 112 | type: 'copy' | 'move' | 'remove' | 'empty' | 'ensure'; 113 | from: string; 114 | } 115 | export interface ISingleArgAction extends IBaseAction { 116 | type: 'remove' | 'empty' | 'ensure'; 117 | } 118 | export interface IDoubleArgAction extends IBaseAction { 119 | type: 'copy' | 'move'; 120 | to: string; 121 | } 122 | export type SingleAction = ISingleArgAction | IDoubleArgAction; 123 | 124 | export type Config = [SingleAction | SingleAction[]]; 125 | } 126 | -------------------------------------------------------------------------------- /packages/core/src/lib/moduleQueue.ts: -------------------------------------------------------------------------------- 1 | import { Module } from './core/module'; 2 | import { Runnable } from './core/runnable'; 3 | import { Runner } from './runner'; 4 | 5 | export class ModuleQueue extends Runnable.Callback { 6 | public runtime: ModuleQueue.IRuntime; 7 | constructor(public modules: Module.InstanceType[], public utils: Runner.Utils) { 8 | super(); 9 | utils.logger.debug(`construct with ${modules.length} modules`); 10 | this.runtime = { 11 | loopModules: new Map(), 12 | validLoopID: 1, 13 | }; 14 | for (const [index, module] of this.modules.entries()) { 15 | if (module.$type === 'callback') { 16 | utils.logger.debug(`register callback module ${module.$name}`); 17 | this.runtime.loopModules.set(module, index); 18 | } 19 | } 20 | utils.logger.debug(`got ${this.runtime.loopModules.size} callback modules`); 21 | } 22 | 23 | public async init() { 24 | const { logger } = this.utils; 25 | logger.debug('apply init-start'); 26 | await this.$applyHook('init-start'); 27 | for (const [index, module] of this.modules.entries()) { 28 | logger.debug(`init ${module.$name} @ ${index}`); 29 | await this._initModule(module); 30 | } 31 | logger.debug('apply init-end'); 32 | await this.$applyHook('init-end'); 33 | } 34 | 35 | public async run(done?: (error?: Error) => void) { 36 | const { logger } = this.utils; 37 | this.runtime.done = done; 38 | logger.debug('apply run-start'); 39 | await this.$applyHook('run-start'); 40 | const loopID = 1; 41 | for (const module of this.modules) { 42 | await this._runModule(module, loopID); 43 | } 44 | logger.debug('apply run-end'); 45 | await this.$applyHook('run-end'); 46 | this.runtime.done && this.runtime.done(); 47 | } 48 | private async _initModule(module: Module.InstanceType) { 49 | try { 50 | await module.init(); 51 | } catch (error) { 52 | this._handleInitError(error); 53 | } 54 | } 55 | 56 | private async _runModule(module: Module.InstanceType, loopID: number) { 57 | const { logger } = this.utils; 58 | logger.debug(`try to run ${module.$name}`); 59 | if (!this._checkLoopIsValid(loopID)) { 60 | logger.debug(`loop ${loopID} is outDated and skipped, current valid loop is ${this.runtime.validLoopID}`); 61 | return; 62 | } 63 | logger.debug(`call ${module.$name}#run`); 64 | if (module.$type === 'async') { 65 | try { 66 | await module.run(); 67 | } catch (error) { 68 | await this._handleRunError(error); 69 | } 70 | } else { 71 | await new Promise(resolve => { 72 | let isCalled = false; 73 | const done = async (error?: Error) => { 74 | error && (await this._handleRunError(error)); 75 | if (!isCalled) { 76 | isCalled = true; 77 | resolve(); 78 | } else { 79 | this._runNewLoop(module, error); 80 | } 81 | }; 82 | try { 83 | module.run(done); 84 | } catch (error) { 85 | done(error); 86 | } 87 | }); 88 | } 89 | } 90 | 91 | private async _runNewLoop(module: Module.Callback, error?: Error) { 92 | const { logger } = this.utils; 93 | logger.debug(`${module.$name} try to create a new loop`); 94 | if (error) { 95 | logger.debug(`found error`, error); 96 | await this._handleRunError(error); 97 | return; 98 | } 99 | const loopID = (this.runtime.validLoopID += 1); 100 | const currentModuleIndex = this.runtime.loopModules.get(module); 101 | if (currentModuleIndex === undefined) { 102 | await this._handleRunError(new Error(`can not locale which module to start`)); 103 | return; 104 | } 105 | this.modules[currentModuleIndex + 1] && logger.debug(`continue on module`, this.modules[currentModuleIndex + 1].$name); 106 | for (const module of this.modules.slice(currentModuleIndex + 1)) { 107 | await this._runModule(module, loopID); 108 | } 109 | logger.debug('apply run-end'); 110 | await this.$applyHook('run-end'); 111 | this.runtime.done && this.runtime.done(); 112 | } 113 | 114 | private _checkLoopIsValid(loopID: number) { 115 | return this.runtime.validLoopID === loopID; 116 | } 117 | private async _handleInitError(error: any) { 118 | const { logger } = this.utils; 119 | logger.debug('apply init-error'); 120 | this.$applyHook('init-error', { error }); 121 | } 122 | 123 | private async _handleRunError(error: any) { 124 | const { logger } = this.utils; 125 | logger.debug('apply run-error'); 126 | this.$applyHook('run-error', { error }); 127 | } 128 | } 129 | 130 | export namespace ModuleQueue { 131 | export interface IRuntime { 132 | loopModules: Map, number>; 133 | validLoopID: number; 134 | done?: (error?: Error) => void; 135 | } 136 | 137 | export type IPluginGroup = { 138 | 'init-start': [undefined, void]; 139 | 'init-end': [undefined, void]; 140 | 'run-start': [undefined, void]; 141 | 'run-end': [undefined, void]; 142 | 'init-error': [{ error: any }, void]; 143 | 'run-error': [{ error: any }, void]; 144 | }; 145 | } 146 | -------------------------------------------------------------------------------- /packages/cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.6.6](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.6.5...@nowa/cli@0.6.6) (2019-12-29) 7 | 8 | **Note:** Version bump only for package @nowa/cli 9 | 10 | 11 | 12 | 13 | 14 | ## [0.6.5](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.6.4...@nowa/cli@0.6.5) (2019-12-29) 15 | 16 | **Note:** Version bump only for package @nowa/cli 17 | 18 | 19 | 20 | 21 | 22 | 23 | ## [0.6.4](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.6.3...@nowa/cli@0.6.4) (2018-09-14) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * chalk error ([eb4939b](https://github.com/nowa-webpack/nowa2/commit/eb4939b)) 29 | 30 | 31 | 32 | 33 | 34 | ## [0.6.3](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.6.2...@nowa/cli@0.6.3) (2018-09-14) 35 | 36 | 37 | ### Bug Fixes 38 | 39 | * logo issue ([498e76e](https://github.com/nowa-webpack/nowa2/commit/498e76e)) 40 | 41 | 42 | 43 | 44 | 45 | # [0.7.0-beta.498e76e6](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.6.2...@nowa/cli@0.7.0-beta.498e76e6) (2018-09-14) 46 | 47 | 48 | ### Bug Fixes 49 | 50 | * logo issue ([498e76e](https://github.com/nowa-webpack/nowa2/commit/498e76e)) 51 | 52 | 53 | 54 | 55 | 56 | ## [0.6.2](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.6.1...@nowa/cli@0.6.2) (2018-09-04) 57 | 58 | 59 | ### Features 60 | 61 | * add the solution version right the logo ([ba03988](https://github.com/nowa-webpack/nowa2/commit/ba03988)) 62 | 63 | 64 | 65 | 66 | 67 | ## [0.6.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.6.0...@nowa/cli@0.6.1) (2018-07-18) 68 | 69 | 70 | 71 | 72 | **Note:** Version bump only for package @nowa/cli 73 | 74 | 75 | # [0.6.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.5.1...@nowa/cli@0.6.0) (2018-07-18) 76 | 77 | 78 | ### Features 79 | 80 | * support new command description format ([10ca293](https://github.com/nowa-webpack/nowa2/commit/10ca293)) 81 | 82 | 83 | 84 | 85 | 86 | ## [0.5.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.5.0...@nowa/cli@0.5.1) (2018-05-27) 87 | 88 | 89 | 90 | 91 | **Note:** Version bump only for package @nowa/cli 92 | 93 | 94 | # [0.5.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.4.3...@nowa/cli@0.5.0) (2018-05-27) 95 | 96 | 97 | ### Bug Fixes 98 | 99 | * **loadOptions:** multiple undefined output ([9897fa4](https://github.com/nowa-webpack/nowa2/commit/9897fa4)) 100 | * compile error after [@types](https://github.com/types)/inquirer version bump ([481263a](https://github.com/nowa-webpack/nowa2/commit/481263a)) 101 | 102 | 103 | ### Features 104 | 105 | * disable import-local in debug mode ([e6e9034](https://github.com/nowa-webpack/nowa2/commit/e6e9034)) 106 | * output command help as a tree ([7442405](https://github.com/nowa-webpack/nowa2/commit/7442405)) 107 | * support reading description from new help format ([a6243e0](https://github.com/nowa-webpack/nowa2/commit/a6243e0)) 108 | 109 | 110 | 111 | 112 | 113 | ## [0.4.3](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.4.2...@nowa/cli@0.4.3) (2018-02-07) 114 | 115 | 116 | 117 | 118 | **Note:** Version bump only for package @nowa/cli 119 | 120 | 121 | ## [0.4.2](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.4.1...@nowa/cli@0.4.2) (2018-01-31) 122 | 123 | 124 | 125 | 126 | **Note:** Version bump only for package @nowa/cli 127 | 128 | 129 | ## [0.4.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.4.0...@nowa/cli@0.4.1) (2018-01-22) 130 | 131 | 132 | ### Bug Fixes 133 | 134 | * logger output issue ([3901fdf](https://github.com/nowa-webpack/nowa2/commit/3901fdf)) 135 | 136 | 137 | 138 | 139 | 140 | # [0.4.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.3.1...@nowa/cli@0.4.0) (2018-01-18) 141 | 142 | 143 | ### Features 144 | 145 | * add logo ([c1f99d2](https://github.com/nowa-webpack/nowa2/commit/c1f99d2)) 146 | * output cli & core version with logo ([5486026](https://github.com/nowa-webpack/nowa2/commit/5486026)) 147 | * **ininContext:** package.json parsing error terminates nowa immediately ([86ce8d6](https://github.com/nowa-webpack/nowa2/commit/86ce8d6)) 148 | * **loadOptions:** support question prompts ([a77e51c](https://github.com/nowa-webpack/nowa2/commit/a77e51c)) 149 | * support core@0.4.0 ([7884742](https://github.com/nowa-webpack/nowa2/commit/7884742)) 150 | * warn if nowa command is elevated ([d734a42](https://github.com/nowa-webpack/nowa2/commit/d734a42)) 151 | 152 | 153 | 154 | 155 | 156 | ## [0.3.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.3.0...@nowa/cli@0.3.1) (2018-01-16) 157 | 158 | 159 | ### Bug Fixes 160 | 161 | * env check failure on node 6.11.x ([044790e](https://github.com/nowa-webpack/nowa2/commit/044790e)) 162 | 163 | 164 | 165 | 166 | 167 | # [0.3.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/cli@0.2.0...@nowa/cli@0.3.0) (2018-01-04) 168 | 169 | 170 | ### Bug Fixes 171 | 172 | * **cli:** should exit on node@6.5- instead of 6- ([29061c3](https://github.com/nowa-webpack/nowa2/commit/29061c3)) 173 | 174 | 175 | ### Features 176 | 177 | * **cli:** disable --version and alias -h to --help ([4739b57](https://github.com/nowa-webpack/nowa2/commit/4739b57)) 178 | * **cli:** prefer the local installation ([ff1e77a](https://github.com/nowa-webpack/nowa2/commit/ff1e77a)) 179 | 180 | 181 | 182 | 183 | 184 | # 0.2.0 (2018-01-01) 185 | 186 | 187 | ### Features 188 | 189 | * **cli:** [@nowa](https://github.com/nowa)/cli @ 0.2.0 ([f1be85d](https://github.com/nowa-webpack/nowa2/commit/f1be85d)) 190 | -------------------------------------------------------------------------------- /packages/core/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | import { Chalk } from 'chalk'; 2 | 3 | export interface IPlugin { 4 | name?: string; 5 | apply(hookable: For, utils: IUtils): void | Promise; 6 | } 7 | 8 | export interface ILogger { 9 | error: (...args: any[]) => void; 10 | warn: (...args: any[]) => void; 11 | log: (...args: any[]) => void; 12 | info: (...args: any[]) => void; 13 | debug: (...args: any[]) => void; 14 | } 15 | 16 | export interface ISpinner extends ISpinnerInstance { 17 | promise(promise: Promise, text?: string): ISpinnerInstance; 18 | } 19 | export interface ISpinnerInstance { 20 | start: (text?: string) => ISpinnerInstance; 21 | stop: () => ISpinnerInstance; 22 | succeed: (text?: string) => ISpinnerInstance; 23 | fail: (text?: string) => ISpinnerInstance; 24 | warn: (text?: string) => ISpinnerInstance; 25 | info: (text?: string) => ISpinnerInstance; 26 | clear: () => ISpinnerInstance; 27 | } 28 | 29 | export interface IUtils { 30 | chalk: Chalk; 31 | logger: ILogger; 32 | prompt: (desc: string, options?: Partial) => Promise; 33 | spinner: ISpinner; 34 | } 35 | 36 | export interface IConfig { 37 | config?: IConfigConfigRegistry; 38 | commands?: ISolutionCommandRegistry; 39 | nowa?: { 40 | plugins?: Array; 41 | }; 42 | solution: string | object; 43 | } 44 | 45 | export interface IConfigConfigRegistry { 46 | [commandPath: string]: 47 | | IConfigConfigValues 48 | | ((arg?: { params: { [paramName: string]: string }; context: string }) => IConfigConfigValues); 49 | } 50 | 51 | export type IConfigConfigValues = { [optionName: string]: any }; 52 | 53 | export interface ISolution { 54 | commands: ISolutionCommandRegistry; 55 | intro?: { 56 | [commandName: string]: ISolutionHelpRegistry; 57 | }; 58 | nowa?: { 59 | plugins?: Array; 60 | }; 61 | } 62 | export interface ISolutionHelpRegistry { 63 | _label?: string; 64 | _default?: string; 65 | [commandName: string]: ISolutionHelpRegistry | string | undefined; 66 | } 67 | 68 | export type ISolutionCommandModuleDescription = 69 | | undefined 70 | | /* modulePath */ string 71 | | [/* modulePath */ string, ((arg?: { options: IConfigConfigValues[0]; context: string }) => /* moduleConfig */ any[] | Promise)] 72 | | ISolutionCommandModuleDescriptionWithConfig; 73 | 74 | export interface ISolutionCommandModuleDescriptionWithConfig extends Array { 75 | '0': string; 76 | } 77 | 78 | export interface ISolutionCommandDescription { 79 | options: { [optionName: string]: IOptionDescription }; 80 | actions: Array< 81 | | ISolutionCommandModuleDescription 82 | | (( 83 | arg?: { options: IConfigConfigValues[0]; context: string }, 84 | ) => ISolutionCommandModuleDescription | Promise) 85 | >; 86 | description?: string | undefined; 87 | } 88 | export interface ISolutionCommandRegistry { 89 | [commandPath: string]: 90 | | ISolutionCommandDescription 91 | | ((arg?: { options: IConfigConfigValues[0]; context: string }) => ISolutionCommandDescription); 92 | } 93 | 94 | export interface IBaseOptionDescription { 95 | type: 'string' | 'number' | 'array' | 'boolean' | 'choice' | 'prompt'; 96 | description: string; 97 | default?: boolean | string | number | string[]; 98 | alias?: string; 99 | group?: string; 100 | hidden?: boolean; 101 | } 102 | 103 | export type IOptionDescription = 104 | | IChoiceOptionDescription 105 | | IStringOptionDescription 106 | | INumberOptionDescription 107 | | IArrayOptionDescription 108 | | IBooleanOptionDescription 109 | | IPromptOptionDescription; 110 | 111 | export type IPromptOptionDescription = 112 | | ITextPromptOptionDescription 113 | | IConfirmPromptOptionDescription 114 | | IListPromptOptionDescription 115 | | ICheckboxPromptOptionDescription; 116 | 117 | export interface IChoiceOptionDescription extends IBaseOptionDescription { 118 | type: 'choice'; 119 | choices: string[]; 120 | default?: string; 121 | } 122 | 123 | export interface IStringOptionDescription extends IBaseOptionDescription { 124 | type: 'string'; 125 | default?: string; 126 | } 127 | 128 | export interface INumberOptionDescription extends IBaseOptionDescription { 129 | type: 'number'; 130 | default?: number; 131 | } 132 | 133 | export interface IBooleanOptionDescription extends IBaseOptionDescription { 134 | type: 'boolean'; 135 | default?: boolean; 136 | } 137 | 138 | export interface IArrayOptionDescription extends IBaseOptionDescription { 139 | type: 'array'; 140 | default?: string[]; 141 | } 142 | 143 | export interface IBasePromptOptionDescription extends IBaseOptionDescription { 144 | type: 'prompt'; 145 | prompt: 'input' | 'confirm' | 'list' | 'password' | 'checkbox'; 146 | order: number; 147 | filter?: (input: any) => Promise | any; 148 | validate?: (result: any) => Promise | any; 149 | choices?: any[]; 150 | } 151 | 152 | export interface ITextPromptOptionDescription extends IBasePromptOptionDescription { 153 | prompt: 'input' | 'password'; 154 | default: string; 155 | } 156 | 157 | export interface IConfirmPromptOptionDescription extends IBasePromptOptionDescription { 158 | prompt: 'confirm'; 159 | default: boolean; 160 | } 161 | export interface IListPromptOptionDescriptionChoices { 162 | name: string; 163 | value?: string; 164 | short?: string; 165 | } 166 | 167 | export interface IListPromptOptionDescription extends IBasePromptOptionDescription { 168 | prompt: 'list'; 169 | choices: Array; 170 | default: string; 171 | } 172 | 173 | export interface ICheckboxPromptOptionDescriptionChoices { 174 | name: string; 175 | value?: string; 176 | checked?: boolean; 177 | } 178 | 179 | export interface ICheckboxPromptOptionDescription extends IBasePromptOptionDescription { 180 | prompt: 'checkbox'; 181 | choices: Array; 182 | default: string[]; 183 | } 184 | -------------------------------------------------------------------------------- /packages/module-webpack/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [0.8.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.8.0-alpha.4...@nowa/module-webpack@0.8.0) (2019-12-29) 7 | 8 | **Note:** Version bump only for package @nowa/module-webpack 9 | 10 | 11 | 12 | 13 | 14 | # [0.8.0-alpha.4](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.8.0-alpha.3...@nowa/module-webpack@0.8.0-alpha.4) (2019-12-29) 15 | 16 | 17 | ### Bug Fixes 18 | 19 | * **module-webpack:** output filename issue ([7640086](https://github.com/nowa-webpack/nowa2/commit/76400861782a6ca27828c2d322628e1fe2add0dd)) 20 | 21 | 22 | 23 | 24 | 25 | 26 | # [0.8.0-alpha.3](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.8.0-alpha.2...@nowa/module-webpack@0.8.0-alpha.3) (2019-12-29) 27 | 28 | 29 | ### Features 30 | 31 | * **module-webpack:** support latest webpack & dev-server ([fc52fa7](https://github.com/nowa-webpack/nowa2/commit/fc52fa7)) 32 | 33 | 34 | 35 | 36 | 37 | 38 | # [0.8.0-alpha.2](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.8.0-alpha.1...@nowa/module-webpack@0.8.0-alpha.2) (2019-06-15) 39 | 40 | 41 | ### Bug Fixes 42 | 43 | * publicPath handing issue ([50d8ed3](https://github.com/nowa-webpack/nowa2/commit/50d8ed3)) 44 | 45 | 46 | 47 | 48 | 49 | 50 | # [0.8.0-alpha.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.8.0-alpha.0...@nowa/module-webpack@0.8.0-alpha.1) (2019-06-15) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * this.startDevServer is not a function ([aaed3d6](https://github.com/nowa-webpack/nowa2/commit/aaed3d6)) 56 | 57 | 58 | ### Features 59 | 60 | * update peer dev-server to ^3.7.0 ([7e056af](https://github.com/nowa-webpack/nowa2/commit/7e056af)) 61 | 62 | 63 | 64 | 65 | 66 | 67 | # [0.8.0-alpha.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.7.6...@nowa/module-webpack@0.8.0-alpha.0) (2019-06-15) 68 | 69 | 70 | ### Features 71 | 72 | * support webpack-dev-server 3.7.0 ([13fa0c5](https://github.com/nowa-webpack/nowa2/commit/13fa0c5)) 73 | 74 | 75 | 76 | 77 | 78 | 79 | ## [0.7.6](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.7.5...@nowa/module-webpack@0.7.6) (2019-05-30) 80 | 81 | 82 | ### Features 83 | 84 | * **webpack:** 增加 PREVIEW_URL 配置 ([61e3607](https://github.com/nowa-webpack/nowa2/commit/61e3607)), closes [#12](https://github.com/nowa-webpack/nowa2/issues/12) 85 | 86 | 87 | 88 | 89 | 90 | 91 | ## [0.7.5](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.7.4...@nowa/module-webpack@0.7.5) (2018-11-13) 92 | 93 | 94 | 95 | 96 | **Note:** Version bump only for package @nowa/module-webpack 97 | 98 | 99 | ## [0.7.4](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.7.3...@nowa/module-webpack@0.7.4) (2018-11-13) 100 | 101 | 102 | ### Bug Fixes 103 | 104 | * replace .plugin with .hooks ([0ba76ac](https://github.com/nowa-webpack/nowa2/commit/0ba76ac)) 105 | * support webpack-dev-server 3.1.10 ([c019cb0](https://github.com/nowa-webpack/nowa2/commit/c019cb0)) 106 | 107 | 108 | 109 | 110 | 111 | ## [0.7.3](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.7.2...@nowa/module-webpack@0.7.3) (2018-09-30) 112 | 113 | 114 | ### Bug Fixes 115 | 116 | * open 127.0.0.1 instead of localhost ([142a2ac](https://github.com/nowa-webpack/nowa2/commit/142a2ac)) 117 | 118 | 119 | ### Features 120 | 121 | * optimize webpack output ([b2f409f](https://github.com/nowa-webpack/nowa2/commit/b2f409f)) 122 | * reportReadiness after webpack finishing compiling ([1d9eb24](https://github.com/nowa-webpack/nowa2/commit/1d9eb24)) 123 | * support webpack-dev-server@3.1.6+ ([6309a20](https://github.com/nowa-webpack/nowa2/commit/6309a20)) 124 | 125 | 126 | 127 | 128 | 129 | ## [0.7.2](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.7.1...@nowa/module-webpack@0.7.2) (2018-09-05) 130 | 131 | 132 | ### Bug Fixes 133 | 134 | * the issue of opening the localhost url ([ce4f11d](https://github.com/nowa-webpack/nowa2/commit/ce4f11d)) 135 | 136 | 137 | 138 | 139 | 140 | ## [0.7.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.7.0...@nowa/module-webpack@0.7.1) (2018-09-04) 141 | 142 | 143 | ### Features 144 | 145 | * change the page webpack-dev-server openning ([84b76b6](https://github.com/nowa-webpack/nowa2/commit/84b76b6)) 146 | 147 | 148 | 149 | 150 | 151 | # [0.7.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.6.2...@nowa/module-webpack@0.7.0) (2018-07-18) 152 | 153 | 154 | ### Features 155 | 156 | * adapt to [@nowa](https://github.com/nowa)/core 0.7.1 ([4837070](https://github.com/nowa-webpack/nowa2/commit/4837070)) 157 | 158 | 159 | 160 | 161 | 162 | ## [0.6.2](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.6.1...@nowa/module-webpack@0.6.2) (2018-06-21) 163 | 164 | 165 | ### Bug Fixes 166 | 167 | * peerDependencies ranges ([96b0dc0](https://github.com/nowa-webpack/nowa2/commit/96b0dc0)) 168 | 169 | 170 | 171 | 172 | 173 | ## [0.6.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.6.0...@nowa/module-webpack@0.6.1) (2018-05-27) 174 | 175 | 176 | 177 | 178 | **Note:** Version bump only for package @nowa/module-webpack 179 | 180 | 181 | # [0.6.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.5.0...@nowa/module-webpack@0.6.0) (2018-05-27) 182 | 183 | 184 | ### Bug Fixes 185 | 186 | * compile errors after dependencies upgrade ([54b6241](https://github.com/nowa-webpack/nowa2/commit/54b6241)) 187 | * remove info logs ([d6d2c7f](https://github.com/nowa-webpack/nowa2/commit/d6d2c7f)) 188 | 189 | 190 | ### Features 191 | 192 | * support mode change ([d2c2fa9](https://github.com/nowa-webpack/nowa2/commit/d2c2fa9)) 193 | * support webpack 4 ([7baeb99](https://github.com/nowa-webpack/nowa2/commit/7baeb99)) 194 | 195 | 196 | 197 | 198 | 199 | # [0.5.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.4.1...@nowa/module-webpack@0.5.0) (2018-02-07) 200 | 201 | 202 | ### Bug Fixes 203 | 204 | * parser can't output debug log ([b251b23](https://github.com/nowa-webpack/nowa2/commit/b251b23)) 205 | 206 | 207 | ### Features 208 | 209 | * compatible for [@nowa](https://github.com/nowa)/core break change ([3e29281](https://github.com/nowa-webpack/nowa2/commit/3e29281)) 210 | * debug log module config ([a3927ef](https://github.com/nowa-webpack/nowa2/commit/a3927ef)) 211 | 212 | 213 | 214 | 215 | 216 | ## [0.4.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.4.0...@nowa/module-webpack@0.4.1) (2018-01-31) 217 | 218 | 219 | ### Bug Fixes 220 | 221 | * done() triggered when nothing changed ([dc6165d](https://github.com/nowa-webpack/nowa2/commit/dc6165d)) 222 | 223 | 224 | 225 | 226 | 227 | # [0.4.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.3.0...@nowa/module-webpack@0.4.0) (2018-01-22) 228 | 229 | 230 | ### Features 231 | 232 | * support core@0.4.0 ([848fa51](https://github.com/nowa-webpack/nowa2/commit/848fa51)) 233 | * support receiving array as moduleOptions ([452c334](https://github.com/nowa-webpack/nowa2/commit/452c334)) 234 | * warn if nowa command is elevated ([d734a42](https://github.com/nowa-webpack/nowa2/commit/d734a42)) 235 | 236 | 237 | 238 | 239 | 240 | # [0.3.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/module-webpack@0.2.0...@nowa/module-webpack@0.3.0) (2018-01-04) 241 | 242 | 243 | ### Features 244 | 245 | * **module-webpack:** pass final config through ./webpack.config.js ([379cc09](https://github.com/nowa-webpack/nowa2/commit/379cc09)) 246 | * **module-webpack:** pass webpack module to ./webpack.config.js ([a0496ff](https://github.com/nowa-webpack/nowa2/commit/a0496ff)) 247 | 248 | 249 | 250 | 251 | 252 | # 0.2.0 (2018-01-01) 253 | 254 | 255 | ### Features 256 | 257 | * **module-webpack:** [@nowa](https://github.com/nowa)/module-webpack @ 0.2.0 ([e35da68](https://github.com/nowa-webpack/nowa2/commit/e35da68)) 258 | -------------------------------------------------------------------------------- /packages/core/src/lib/runner.ts: -------------------------------------------------------------------------------- 1 | import { Module } from './core/module'; 2 | import { Runnable } from './core/runnable'; 3 | import { ModuleQueue } from './moduleQueue'; 4 | import { IConfig, IConfigConfigValues, IPlugin, ISolution, ISolutionCommandDescription, IUtils } from './types'; 5 | import { captureStack } from './utils'; 6 | 7 | export class Runner extends Runnable.Callback { 8 | public runtime: Runner.IRuntime = { parsed: {}, raw: {} } as any; 9 | public utils: Runner.Utils; 10 | constructor(public $createUtils: Runner.UtilsCreator) { 11 | super(); 12 | this.utils = $createUtils('Runner'); 13 | } 14 | public async init(): Promise { 15 | const { logger } = this.utils; 16 | try { 17 | logger.debug('apply init-start'); 18 | await this.$applyHook('init-start'); 19 | logger.debug('apply init-context'); 20 | this.runtime.context = await this.$applyHookBail('init-context'); 21 | logger.debug('apply load-advanced-config'); 22 | const advanced = await this.$applyHookBail('load-advanced', { context: this.runtime.context }); 23 | if (advanced) { 24 | const advancedPlugins = await this.$applyHookBail('load-plugins', { 25 | config: advanced.config, 26 | context: this.runtime.context, 27 | solution: advanced.solution, 28 | }); 29 | logger.debug(`load ${advancedPlugins.length} advanced plugin(s)`); 30 | for (const plugin of advancedPlugins) { 31 | await plugin.apply(this, this.$createUtils(plugin.name)); 32 | } 33 | } 34 | logger.debug('apply load-config'); 35 | this.runtime.raw.config = await this.$applyHookBail('load-config', { context: this.runtime.context }); 36 | logger.debug('apply load-solution'); 37 | this.runtime.raw.solution = await this.$applyHookBail('load-solution', { 38 | config: this.runtime.raw.config, 39 | context: this.runtime.context, 40 | }); 41 | logger.debug('apply load-plugins'); 42 | const plugins = await this.$applyHookBail('load-plugins', { 43 | config: this.runtime.raw.config, 44 | context: this.runtime.context, 45 | solution: this.runtime.raw.solution, 46 | }); 47 | logger.debug(`load ${plugins.length} plugin(s) from config and solution`); 48 | for (const plugin of plugins) { 49 | await plugin.apply(this, this.$createUtils(plugin.name)); 50 | } 51 | logger.debug('apply load-commands'); 52 | this.runtime.raw.commands = await this.$applyHookBail('load-commands', { 53 | config: this.runtime.raw.config, 54 | context: this.runtime.context, 55 | solution: this.runtime.raw.solution, 56 | }); 57 | logger.debug('apply parse-config'); 58 | this.runtime.parsed.config = await this.$applyHookBail('parse-config', { 59 | commands: this.runtime.parsed.commands, 60 | context: this.runtime.context, 61 | ...this.runtime.raw, 62 | }); 63 | logger.debug('apply parse-solution'); 64 | const solutionResult = await this.$applyHookBail('parse-solution', { 65 | commands: this.runtime.parsed.commands, 66 | context: this.runtime.context, 67 | ...this.runtime.raw, 68 | }); 69 | this.runtime.parsed.solution = solutionResult; 70 | logger.debug('apply load-options'); 71 | const options = await this.$applyHookBail('load-options', { 72 | commands: this.runtime.parsed.commands, 73 | config: this.runtime.parsed.config, 74 | context: this.runtime.context, 75 | rawConfig: this.runtime.raw.config, 76 | rawSolution: this.runtime.raw.solution, 77 | solution: this.runtime.parsed.solution, 78 | }); 79 | const logOptionsWarning = (message: string) => { 80 | logger.warn(message); 81 | logger.debug(captureStack(message)); 82 | }; 83 | this.runtime.parsed.options = new Proxy(options, { 84 | get(t, p) { 85 | if (!t.hasOwnProperty(p)) { 86 | logOptionsWarning(`used a non-exist options property ${String(p)}`); 87 | } 88 | return (t as any)[p]; 89 | }, 90 | set(t, p, v) { 91 | logOptionsWarning('should not modify options property'); 92 | return Reflect.set(t, p, v); 93 | }, 94 | deleteProperty(t, p) { 95 | logOptionsWarning('should not delete options property'); 96 | return Reflect.deleteProperty(t, p); 97 | }, 98 | }); 99 | logger.debug('apply load-modules'); 100 | this.runtime.modules = await this.$applyHookBail('load-modules', { 101 | context: this.runtime.context, 102 | createUtils: this.$createUtils, 103 | ...this.runtime.parsed, 104 | }); 105 | logger.debug(`load ${this.runtime.modules.length} module(s)`); 106 | logger.debug('create & init moduleQueue'); 107 | this.runtime.moduleQueue = new ModuleQueue(this.runtime.modules, this.$createUtils('ModuleQueue')); 108 | logger.debug('apply init-module-queue'); 109 | await this.$applyHook('init-module-queue', { 110 | context: this.runtime.context, 111 | moduleQueue: this.runtime.moduleQueue, 112 | modules: this.runtime.modules, 113 | ...this.runtime.parsed, 114 | }); 115 | await this.runtime.moduleQueue.init(); 116 | logger.debug('apply init-end'); 117 | await this.$applyHook('init-end', this); 118 | } catch (e) { 119 | logger.debug(`apply init-error because of ${e}`); 120 | await this.$applyHook('init-error', { error: e }); 121 | } 122 | } 123 | 124 | public async run(): Promise { 125 | let isFirstRun = true; 126 | const { logger } = this.utils; 127 | process.on('SIGINT', () => { 128 | logger.debug('signal SIGINT received'); 129 | logger.debug('apply run-end'); 130 | this.$applyHook('run-end', this).then(() => process.exit(0)); 131 | }); 132 | logger.debug('apply run-start'); 133 | await this.$applyHook('run-start', this); 134 | logger.debug('run modules'); 135 | await this.runtime.moduleQueue.run(() => { 136 | logger.debug('apply run-end'); 137 | this.$applyHook('run-end', this); 138 | if (isFirstRun) { 139 | logger.debug('apply first-run-end'); 140 | this.$applyHook('first-run-end', this); 141 | isFirstRun = false; 142 | } 143 | }); 144 | } 145 | } 146 | 147 | export namespace Runner { 148 | export type PluginGroup = { 149 | 'init-start': [undefined, void]; 150 | 'init-context': [undefined, IRuntime['context']]; 151 | 'load-advanced': [Pick, { config: IRuntime['raw']['config']; solution: IRuntime['raw']['solution'] } | null]; 152 | 'load-config': [Pick, IRuntime['raw']['config']]; 153 | 'load-solution': [Pick & Pick, IRuntime['raw']['solution']]; 154 | 'load-plugins': [Pick & Partial>, Array>]; 155 | 'load-commands': [Pick & Pick, IRuntime['raw']['commands']]; 156 | 'parse-config': [Pick & IRuntime['raw'], IRuntime['parsed']['config']]; 157 | 'parse-solution': [Pick & IRuntime['raw'], IRuntime['parsed']['solution']]; 158 | 'load-options': [ 159 | Pick & 160 | Pick & { 161 | rawConfig: IRuntime['raw']['config']; 162 | rawSolution: IRuntime['raw']['solution']; 163 | }, 164 | IRuntime['parsed']['options'] 165 | ]; 166 | 'load-modules': [Pick & IRuntime['parsed'] & { createUtils: Runner.UtilsCreator }, IRuntime['modules']]; 167 | 'init-module-queue': [ 168 | Pick & IRuntime['parsed'] & { modules: IRuntime['modules']; moduleQueue: IRuntime['moduleQueue'] }, 169 | void 170 | ]; 171 | 'init-end': [Runner, void]; 172 | 'run-start': [Runner, void]; 173 | 'first-run-end': [Runner, void]; 174 | 'run-end': [Runner, void]; 175 | 'init-error': [{ error: any }, void]; 176 | 'run-error': [{ error: any }, void]; 177 | }; 178 | 179 | export type UtilsCreator = (name?: string) => IUtils; 180 | 181 | export type Utils = IUtils; 182 | 183 | export interface IRuntime { 184 | context: string; 185 | raw: { 186 | config: IConfig; 187 | solution: ISolution; 188 | commands: string[]; 189 | }; 190 | parsed: { 191 | config: IConfigConfigValues; 192 | solution: ISolutionCommandDescription; 193 | commands: string[]; 194 | options: object; 195 | }; 196 | modules: Module.InstanceType[]; 197 | moduleQueue: ModuleQueue; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /packages/core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.7.7](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.6...@nowa/core@0.7.7) (2019-12-29) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **core:** if (void) ([d3e3330](https://github.com/nowa-webpack/nowa2/commit/d3e333064a9d8b40372c5aaef5ca7976e89a8c26)) 12 | 13 | 14 | 15 | 16 | 17 | ## [0.7.6](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.5...@nowa/core@0.7.6) (2019-12-29) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * **core:** hookable handler this is null ([3c95d95](https://github.com/nowa-webpack/nowa2/commit/3c95d95b147439fb82bfd962746dacaf7cea64cc)) 23 | 24 | 25 | 26 | 27 | 28 | 29 | ## [0.7.5](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.4...@nowa/core@0.7.5) (2018-11-13) 30 | 31 | 32 | 33 | 34 | **Note:** Version bump only for package @nowa/core 35 | 36 | 37 | ## [0.7.4](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.3...@nowa/core@0.7.4) (2018-11-13) 38 | 39 | 40 | ### Features 41 | 42 | * ouput the non-exist options property ([0ac9e33](https://github.com/nowa-webpack/nowa2/commit/0ac9e33)) 43 | * output the solution name and version ([5049eef](https://github.com/nowa-webpack/nowa2/commit/5049eef)) 44 | 45 | 46 | 47 | 48 | 49 | ## [0.7.3](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.2...@nowa/core@0.7.3) (2018-09-14) 50 | 51 | 52 | ### Bug Fixes 53 | 54 | * forgot to register load-advanced plugin ([21a556a](https://github.com/nowa-webpack/nowa2/commit/21a556a)) 55 | * logo issue ([498e76e](https://github.com/nowa-webpack/nowa2/commit/498e76e)) 56 | 57 | 58 | ### Features 59 | 60 | * support loading advanced config & solution ([29048b3](https://github.com/nowa-webpack/nowa2/commit/29048b3)) 61 | 62 | 63 | 64 | 65 | 66 | # [0.8.0-beta.498e76e6](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.2...@nowa/core@0.8.0-beta.498e76e6) (2018-09-14) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * forgot to register load-advanced plugin ([21a556a](https://github.com/nowa-webpack/nowa2/commit/21a556a)) 72 | * logo issue ([498e76e](https://github.com/nowa-webpack/nowa2/commit/498e76e)) 73 | 74 | 75 | ### Features 76 | 77 | * support loading advanced config & solution ([29048b3](https://github.com/nowa-webpack/nowa2/commit/29048b3)) 78 | 79 | 80 | 81 | 82 | 83 | # [0.8.0-beta.498e76e6](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.2...@nowa/core@0.8.0-beta.498e76e6) (2018-09-14) 84 | 85 | 86 | ### Bug Fixes 87 | 88 | * forgot to register load-advanced plugin ([21a556a](https://github.com/nowa-webpack/nowa2/commit/21a556a)) 89 | * logo issue ([498e76e](https://github.com/nowa-webpack/nowa2/commit/498e76e)) 90 | 91 | 92 | ### Features 93 | 94 | * support loading advanced config & solution ([29048b3](https://github.com/nowa-webpack/nowa2/commit/29048b3)) 95 | 96 | 97 | 98 | 99 | 100 | # [0.8.0-beta.21a556ab](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.2...@nowa/core@0.8.0-beta.21a556ab) (2018-09-14) 101 | 102 | 103 | ### Bug Fixes 104 | 105 | * forgot to register load-advanced plugin ([21a556a](https://github.com/nowa-webpack/nowa2/commit/21a556a)) 106 | 107 | 108 | ### Features 109 | 110 | * support loading advanced config & solution ([29048b3](https://github.com/nowa-webpack/nowa2/commit/29048b3)) 111 | 112 | 113 | 114 | 115 | 116 | # [0.8.0-beta.29048b3e](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.2...@nowa/core@0.8.0-beta.29048b3e) (2018-09-14) 117 | 118 | 119 | ### Features 120 | 121 | * support loading advanced config & solution ([29048b3](https://github.com/nowa-webpack/nowa2/commit/29048b3)) 122 | 123 | 124 | 125 | 126 | 127 | ## [0.7.2](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.1...@nowa/core@0.7.2) (2018-09-04) 128 | 129 | 130 | ### Features 131 | 132 | * solution log level from info to debug ([5b75d74](https://github.com/nowa-webpack/nowa2/commit/5b75d74)) 133 | 134 | 135 | 136 | 137 | 138 | ## [0.7.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.7.0...@nowa/core@0.7.1) (2018-07-18) 139 | 140 | 141 | 142 | 143 | **Note:** Version bump only for package @nowa/core 144 | 145 | 146 | # [0.7.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.6.0...@nowa/core@0.7.0) (2018-07-18) 147 | 148 | 149 | ### Bug Fixes 150 | 151 | * command path parsing issue ([31783c1](https://github.com/nowa-webpack/nowa2/commit/31783c1)) 152 | * module loading error is not handled correctly ([ec874a9](https://github.com/nowa-webpack/nowa2/commit/ec874a9)) 153 | * parse issues ([d63daf2](https://github.com/nowa-webpack/nowa2/commit/d63daf2)) 154 | 155 | 156 | ### Features 157 | 158 | * support new command description format ([10ca293](https://github.com/nowa-webpack/nowa2/commit/10ca293)) 159 | * support path-like command description ([50e46e6](https://github.com/nowa-webpack/nowa2/commit/50e46e6)) 160 | 161 | 162 | 163 | 164 | 165 | # [0.6.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.5.0...@nowa/core@0.6.0) (2018-05-27) 166 | 167 | 168 | ### Bug Fixes 169 | 170 | * **loadModules:** debug output order issue ([5112264](https://github.com/nowa-webpack/nowa2/commit/5112264)) 171 | * compile errors after dependencies upgrade ([54b6241](https://github.com/nowa-webpack/nowa2/commit/54b6241)) 172 | 173 | 174 | ### Features 175 | 176 | * check options usage in runtime ([68a7922](https://github.com/nowa-webpack/nowa2/commit/68a7922)) 177 | * export Types for solution development ([991ae51](https://github.com/nowa-webpack/nowa2/commit/991ae51)) 178 | * **loadSolution:** output module solution's version & path ([5c8344e](https://github.com/nowa-webpack/nowa2/commit/5c8344e)) 179 | * **runner:** add 'first-run-end' hook ([1623bb4](https://github.com/nowa-webpack/nowa2/commit/1623bb4)) 180 | * output command help as a tree ([7442405](https://github.com/nowa-webpack/nowa2/commit/7442405)) 181 | * support function module descriptions ([dae3799](https://github.com/nowa-webpack/nowa2/commit/dae3799)) 182 | 183 | 184 | 185 | 186 | 187 | # [0.5.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.4.1...@nowa/core@0.5.0) (2018-02-07) 188 | 189 | 190 | ### Code Refactoring 191 | 192 | * rename moduleOptions to config ([42eec53](https://github.com/nowa-webpack/nowa2/commit/42eec53)) 193 | 194 | 195 | ### Features 196 | 197 | * **loadModules:** update plugin for break change ([8bbed0b](https://github.com/nowa-webpack/nowa2/commit/8bbed0b)) 198 | 199 | 200 | ### BREAKING CHANGES 201 | 202 | * all 0.4.0 compatible modules will break 203 | 204 | 205 | 206 | 207 | 208 | ## [0.4.1](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.4.0...@nowa/core@0.4.1) (2018-01-31) 209 | 210 | 211 | ### Bug Fixes 212 | 213 | * **loadModules:** checkIsNowaModule always return false ([d2e692e](https://github.com/nowa-webpack/nowa2/commit/d2e692e)) 214 | * ctrl+c exits with code 1 ([e48a47f](https://github.com/nowa-webpack/nowa2/commit/e48a47f)) 215 | * logger output issue ([3901fdf](https://github.com/nowa-webpack/nowa2/commit/3901fdf)) 216 | 217 | 218 | 219 | 220 | 221 | # [0.4.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.3.0...@nowa/core@0.4.0) (2018-01-18) 222 | 223 | 224 | ### Bug Fixes 225 | 226 | * **types:** type errors ([dcc040f](https://github.com/nowa-webpack/nowa2/commit/dcc040f)) 227 | 228 | 229 | ### Features 230 | 231 | * **loadModules:** check if module is a nowa module before instantiate it ([7c62b24](https://github.com/nowa-webpack/nowa2/commit/7c62b24)) 232 | * **utils:** parser supports providing a string to set alias ([de8032c](https://github.com/nowa-webpack/nowa2/commit/de8032c)) 233 | * add utils definition ([5c75cc3](https://github.com/nowa-webpack/nowa2/commit/5c75cc3)) 234 | * export IUtils as Runner.Utils ([144d3be](https://github.com/nowa-webpack/nowa2/commit/144d3be)) 235 | * pass utils to modules / plugins ([7d3c0ed](https://github.com/nowa-webpack/nowa2/commit/7d3c0ed)) 236 | * support prompt option ([04a8b9c](https://github.com/nowa-webpack/nowa2/commit/04a8b9c)) 237 | * warn if nowa command is elevated ([d734a42](https://github.com/nowa-webpack/nowa2/commit/d734a42)) 238 | 239 | 240 | 241 | 242 | 243 | # [0.3.0](https://github.com/nowa-webpack/nowa2/compare/@nowa/core@0.2.0...@nowa/core@0.3.0) (2018-01-04) 244 | 245 | 246 | ### Bug Fixes 247 | 248 | * **core:** ISolution should always contain a commands property ([13dc3bb](https://github.com/nowa-webpack/nowa2/commit/13dc3bb)) 249 | * **core/utils:** infinite loop issue on parser ([cd9991a](https://github.com/nowa-webpack/nowa2/commit/cd9991a)) 250 | * **core/utils:** parser doesn't continue traveling ([3e2eccf](https://github.com/nowa-webpack/nowa2/commit/3e2eccf)) 251 | 252 | 253 | ### Features 254 | 255 | * **core/utils:** add requireFile function ([6fa765d](https://github.com/nowa-webpack/nowa2/commit/6fa765d)) 256 | * **core/utils:** requireFile silently returns undefined when target file cannot be accessed ([5ab1253](https://github.com/nowa-webpack/nowa2/commit/5ab1253)) 257 | * **core/utils:** use __esModule to detect ES modules ([84fc39a](https://github.com/nowa-webpack/nowa2/commit/84fc39a)) 258 | 259 | 260 | 261 | 262 | 263 | # 0.2.0 (2018-01-01) 264 | 265 | 266 | ### Features 267 | 268 | * **core:** [@nowa](https://github.com/nowa)/core @ 0.2.0 ([16bfb04](https://github.com/nowa-webpack/nowa2/commit/16bfb04)) 269 | * **core/module:** add moduleOption generics type ([a4d1436](https://github.com/nowa-webpack/nowa2/commit/a4d1436)) 270 | -------------------------------------------------------------------------------- /packages/module-webpack/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Module, utils } from '@nowa/core'; 2 | import { resolve } from 'path'; 3 | import * as Webpack from 'webpack'; 4 | import * as WebpackDevServer from 'webpack-dev-server'; 5 | 6 | export default class ModuleWebpack extends Module.Callback { 7 | public $name = 'webpack'; 8 | public mode: ModuleWebpack.IOptions['mode']; 9 | public compiler?: Webpack.Compiler; 10 | public getCompilerCallback?: (done: (error?: any) => void) => (err: any, stats: any) => void; 11 | public lastHash?: string; 12 | public server?: WebpackDevServer; 13 | public startDevServer?: (done: () => void) => void; 14 | public config?: Webpack.Configuration | Webpack.Configuration[]; 15 | public alreadyRun = false; 16 | public alreadyOpen = false; 17 | 18 | public async init() { 19 | const { logger } = this.$utils; 20 | const [webpackConfigs, options] = this.$runtime.config; 21 | const userConfigs: ModuleWebpack.SingleConfig[] = ([] as ModuleWebpack.SingleConfig[]).concat(webpackConfigs); 22 | const configs: Array = []; 23 | logger.debug(`find ${userConfigs.length} configs`); 24 | for (const config of userConfigs) { 25 | configs.push(await this._initConfig(config)); 26 | } 27 | let finalConfigs = configs.reduce((p: Webpack.Configuration[], c) => p.concat(c), []); 28 | const overwriteConfigPath = resolve(this.$runtime.context, './webpack.overwrite.js'); 29 | let overwriteConfig = await utils.requireFile(overwriteConfigPath); 30 | if (overwriteConfig && typeof overwriteConfig === 'object') { 31 | logger.debug(`find overwrite config is a object, send it to parser`); 32 | const parserResult = utils.parser('webpack.config', this.$runtime.commands, logger.debug, overwriteConfig); // tslint:disable-line:no-empty 33 | overwriteConfig = (parserResult && parserResult.result && parserResult.result[0]) || overwriteConfig; 34 | if (typeof overwriteConfig === 'function') { 35 | logger.debug(`find overwrite config for this command`); 36 | } 37 | } 38 | if (typeof overwriteConfig === 'function') { 39 | logger.warn(`overwrite configs from ${overwriteConfigPath}`); 40 | finalConfigs = await finalConfigs.map(config => overwriteConfig(config, this.$runtime, Webpack)); 41 | } 42 | logger.debug(`got ${finalConfigs.length} webpack configs`); 43 | logger.debug(finalConfigs); 44 | this.config = finalConfigs.length === 1 ? finalConfigs[0] : finalConfigs; 45 | const firstConfig = finalConfigs[0]; 46 | 47 | const autoMode = async () => { 48 | if (firstConfig.devServer) { 49 | this.mode = 'devServer'; 50 | return this._initWebpackDevServer(); 51 | } else if (firstConfig.watch) { 52 | this.mode = 'watch'; 53 | } else { 54 | this.mode = 'run'; 55 | } 56 | return this._initWebpack(); 57 | }; 58 | 59 | if (options && options.mode) { 60 | this.mode = options.mode; 61 | switch (options.mode) { 62 | case 'devServer': 63 | await this._initWebpackDevServer(); 64 | break; 65 | case 'run': 66 | case 'watch': 67 | await this._initWebpack(); 68 | break; 69 | default: 70 | logger.error(`unknown mode ${options.mode}, ignored`); 71 | await autoMode(); 72 | } 73 | } else { 74 | await autoMode(); 75 | } 76 | } 77 | 78 | public run(done: () => void) { 79 | if (!this.alreadyRun) { 80 | if (this.mode === 'devServer') { 81 | this.startDevServer!(done); 82 | } 83 | if (this.mode === 'watch') { 84 | const options = this.config!; 85 | const firstOptions: Webpack.Configuration = ([] as Webpack.Configuration[]).concat(options)[0]; 86 | const watchOptions = 87 | firstOptions.watchOptions || (options as any).watchOptions || firstOptions.watch || (options as any).watch || {}; 88 | if (watchOptions.stdin) { 89 | process.stdin.on('end', function(_: any) { 90 | process.exit(); // eslint-disable-line 91 | }); 92 | process.stdin.resume(); 93 | } 94 | this.compiler!.watch(watchOptions, this.getCompilerCallback!(done)); 95 | } else { 96 | this.compiler!.run((err, stats) => { 97 | if ((this.compiler as any).close) { 98 | (this.compiler as any).close((err2: any) => { 99 | this.getCompilerCallback!(done)(err || err2, stats); 100 | }); 101 | } else { 102 | this.getCompilerCallback!(done)(err, stats); 103 | } 104 | }); 105 | } 106 | 107 | this.alreadyRun = true; 108 | } 109 | } 110 | 111 | private async _initConfig(config: ModuleWebpack.SingleConfig): Promise { 112 | const userConfig = typeof config === 'string' ? utils.handleESModuleDefault(require(config)) : config; 113 | if (typeof userConfig === 'function') { 114 | return userConfig({ context: this.$runtime.context, options: this.$runtime.options }); 115 | } else { 116 | return userConfig; 117 | } 118 | } 119 | 120 | private async _initWebpack(): Promise { 121 | // from webpack-cli 122 | // https://github.com/webpack/webpack/blob/master/bin/webpack.js 123 | // 3.3.10 124 | 125 | require('v8-compile-cache'); 126 | const options = this.config!; 127 | 128 | const firstOptions: Webpack.Configuration = ([] as Webpack.Configuration[]).concat(options)[0]; 129 | const statsPresetToOptions = require('webpack').Stats.presetToOptions; 130 | 131 | let outputOptions = (options as any).stats; 132 | if (typeof outputOptions === 'boolean' || typeof outputOptions === 'string') { 133 | outputOptions = statsPresetToOptions(outputOptions); 134 | } else if (!outputOptions) { 135 | outputOptions = {}; 136 | } 137 | 138 | outputOptions = Object.create(outputOptions); 139 | if (Array.isArray(options) && !outputOptions.children) { 140 | outputOptions.children = options.map(o => o.stats); 141 | } 142 | if (typeof outputOptions.context === 'undefined') outputOptions.context = firstOptions.context; 143 | 144 | if (typeof outputOptions.colors === 'undefined') outputOptions.colors = require('supports-color').stdout; 145 | 146 | if (!outputOptions.json) { 147 | if (typeof outputOptions.cached === 'undefined') outputOptions.cached = false; 148 | if (typeof outputOptions.cachedAssets === 'undefined') outputOptions.cachedAssets = false; 149 | 150 | if (!outputOptions.exclude) outputOptions.exclude = ['node_modules', 'bower_components', 'components']; 151 | } 152 | 153 | const webpack = require('webpack'); 154 | 155 | let lastHash: string | null = null; 156 | 157 | try { 158 | this.compiler = webpack(options); 159 | } catch (err) { 160 | if (err.name === 'WebpackOptionsValidationError') { 161 | if (outputOptions.colors) console.error(`\u001b[1m\u001b[31m${err.message}\u001b[39m\u001b[22m`); 162 | else console.error(err.message); 163 | // eslint-disable-next-line no-process-exit 164 | process.exit(1); 165 | } 166 | 167 | throw err; 168 | } 169 | this.getCompilerCallback = done => { 170 | return (err, stats) => { 171 | if (this.mode !== 'watch' || err) { 172 | // Do not keep cache anymore 173 | (this.compiler! as any).purgeInputFileSystem(); 174 | } 175 | if (err) { 176 | lastHash = null; 177 | console.error(err.stack || err); 178 | if (err.details) console.error(err.details); 179 | process.exitCode = 1; 180 | return; 181 | } else if (stats.hash && stats.hash !== lastHash) { 182 | done(); 183 | } 184 | if (outputOptions.json) { 185 | process.stdout.write(JSON.stringify(stats.toJson(outputOptions), null, 2) + '\n'); 186 | } else if (stats.hash !== lastHash) { 187 | lastHash = stats.hash; 188 | if (stats.compilation && stats.compilation.errors.length !== 0) { 189 | const errors = stats.compilation.errors; 190 | if (errors[0].name === 'EntryModuleNotFoundError') { 191 | console.error('\n\u001b[1m\u001b[31mInsufficient number of arguments or no entry found.'); 192 | } 193 | } 194 | const statsString = stats.toString(outputOptions); 195 | const delimiter = outputOptions.buildDelimiter ? `${outputOptions.buildDelimiter}\n` : ''; 196 | if (statsString) process.stdout.write(`${statsString}\n${delimiter}`); 197 | } 198 | if (this.mode !== 'watch' && stats.hasErrors()) { 199 | process.exitCode = 2; 200 | } 201 | }; 202 | }; 203 | } 204 | 205 | private async _initWebpackDevServer(): Promise { 206 | // from webpack-dev-server 207 | // https://github.com/webpack/webpack-dev-server/blob/master/bin/webpack-dev-server.js 208 | // 3.10.0 209 | const fs = require('fs'); 210 | const net = require('net'); 211 | const webpack = require('webpack'); 212 | const Server = require('webpack-dev-server/lib/Server'); 213 | const setupExitSignals = require('webpack-dev-server/lib/utils/setupExitSignals'); 214 | const colors = require('webpack-dev-server/lib/utils/colors'); 215 | const processOptions = require('webpack-dev-server/lib/utils/processOptions'); 216 | const createLogger = require('webpack-dev-server/lib/utils/createLogger'); 217 | await this._initWebpack(); 218 | const serverData = { 219 | server: null, 220 | }; 221 | 222 | setupExitSignals(serverData); 223 | const config = this.config!; 224 | 225 | const startDevServer = (config: any, options: any, done: () => void) => { 226 | const log = createLogger(options); 227 | 228 | let compiler; 229 | 230 | try { 231 | compiler = webpack(config); 232 | } catch (err) { 233 | if (err instanceof webpack.WebpackOptionsValidationError) { 234 | log.error(colors.error(options.stats.colors, err.message)); 235 | // eslint-disable-next-line no-process-exit 236 | process.exit(1); 237 | } 238 | 239 | throw err; 240 | } 241 | 242 | try { 243 | this.server = new Server(compiler, options, log); 244 | serverData.server = this.server as any; 245 | } catch (err) { 246 | if (err.name === 'ValidationError') { 247 | log.error(colors.error(options.stats.colors, err.message)); 248 | // eslint-disable-next-line no-process-exit 249 | process.exit(1); 250 | } 251 | 252 | throw err; 253 | } 254 | 255 | const server = this.server as any; 256 | done(); 257 | if (options.socket) { 258 | server.listeningApp.on('error', (e: any) => { 259 | if (e.code === 'EADDRINUSE') { 260 | const clientSocket = new net.Socket(); 261 | 262 | clientSocket.on('error', (err: any) => { 263 | if (err.code === 'ECONNREFUSED') { 264 | // No other server listening on this socket so it can be safely removed 265 | fs.unlinkSync(options.socket); 266 | 267 | server.listen(options.socket, options.host, (error: any) => { 268 | if (error) { 269 | throw error; 270 | } 271 | }); 272 | } 273 | }); 274 | 275 | clientSocket.connect({ path: options.socket }, () => { 276 | throw new Error('This socket is already used'); 277 | }); 278 | } 279 | }); 280 | 281 | server.listen(options.socket, options.host, (err: any) => { 282 | if (err) { 283 | throw err; 284 | } 285 | 286 | // chmod 666 (rw rw rw) 287 | const READ_WRITE = 438; 288 | 289 | fs.chmod(options.socket, READ_WRITE, (err: any) => { 290 | if (err) { 291 | throw err; 292 | } 293 | }); 294 | }); 295 | } else { 296 | server.listen(options.port, options.host, (err: any) => { 297 | if (err) { 298 | throw err; 299 | } 300 | }); 301 | } 302 | }; 303 | return new Promise((resolve, reject) => { 304 | try { 305 | processOptions(config, {}, (config: any, options: any) => { 306 | this.startDevServer = function _startDevServer(done) { 307 | startDevServer(config, options, done); 308 | }; 309 | resolve(); 310 | }); 311 | } catch (e) { 312 | reject(e); 313 | } 314 | }); 315 | } 316 | } 317 | 318 | export namespace ModuleWebpack { 319 | export interface IOptions { 320 | mode?: 'run' | 'watch' | 'devServer'; 321 | } 322 | export type ConfigFileContent = 323 | | (({ 324 | context, 325 | options, 326 | }: { 327 | context: string; 328 | options: object; 329 | }) => Webpack.Configuration | Webpack.Configuration[] | Promise) 330 | | Webpack.Configuration 331 | | Webpack.Configuration[]; 332 | export type SingleConfig = /* path to configFile */ string | ConfigFileContent; 333 | export type Config = [SingleConfig | SingleConfig[], IOptions | undefined]; 334 | } 335 | -------------------------------------------------------------------------------- /packages/cli/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nowa/cli", 3 | "version": "0.6.6", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@nowa/core": { 8 | "version": "0.7.7", 9 | "resolved": "https://registry.npmjs.org/@nowa/core/-/core-0.7.7.tgz", 10 | "integrity": "sha512-qo/LQj5cGrYFHaqYZoT2O2kcOEUX2X2atD9fDJi/9G6yCaRB5yXM029+4v2MzcX2T36N6CZrkCCMN8rWwUO/jQ==", 11 | "dev": true, 12 | "requires": { 13 | "chalk": "^2.4.1", 14 | "fs-extra": "^6.0.1", 15 | "path-to-regexp": "^2.2.1", 16 | "tslib": "^1.9.1" 17 | } 18 | }, 19 | "@types/archy": { 20 | "version": "0.0.31", 21 | "resolved": "https://registry.npmjs.org/@types/archy/-/archy-0.0.31.tgz", 22 | "integrity": "sha512-v+dxizsFVyXgD3EpFuqT9YjdEjbJmPxNf1QIX9ohZOhxh1ZF2yhqv3vYaeum9lg3VghhxS5S0a6yldN9J9lPEQ==" 23 | }, 24 | "@types/fs-extra": { 25 | "version": "5.1.0", 26 | "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.1.0.tgz", 27 | "integrity": "sha512-AInn5+UBFIK9FK5xc9yP5e3TQSPNNgjHByqYcj9g5elVBnDQcQL7PlO1CIRy2gWlbwK7UPYqi7vRvFA44dCmYQ==", 28 | "dev": true, 29 | "requires": { 30 | "@types/node": "*" 31 | } 32 | }, 33 | "@types/inquirer": { 34 | "version": "0.0.41", 35 | "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-0.0.41.tgz", 36 | "integrity": "sha512-kIWkK3FECGKt9OrURxRvi59gwMNiWTePXWOvaJn+xhplbEvu91hIDMfLe5PUu+cEEMmD6EFU4VFJJKKp5kzCtw==", 37 | "dev": true, 38 | "requires": { 39 | "@types/rx": "*", 40 | "@types/through": "*" 41 | } 42 | }, 43 | "@types/node": { 44 | "version": "10.17.13", 45 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz", 46 | "integrity": "sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg==", 47 | "dev": true 48 | }, 49 | "@types/ora": { 50 | "version": "1.3.5", 51 | "resolved": "https://registry.npmjs.org/@types/ora/-/ora-1.3.5.tgz", 52 | "integrity": "sha512-CZe3oXbO1XylJT1feg+/aCzNt6tfR4XO+IkLetc85O/yaZRw271cZtS8LL/2mknd+PoR5IKAjFLzo4KWZXxung==", 53 | "dev": true, 54 | "requires": { 55 | "@types/node": "*" 56 | } 57 | }, 58 | "@types/rx": { 59 | "version": "4.1.1", 60 | "resolved": "https://registry.npmjs.org/@types/rx/-/rx-4.1.1.tgz", 61 | "integrity": "sha1-WY/JSla67ZdfGUV04PVy/Y5iekg=", 62 | "dev": true, 63 | "requires": { 64 | "@types/rx-core": "*", 65 | "@types/rx-core-binding": "*", 66 | "@types/rx-lite": "*", 67 | "@types/rx-lite-aggregates": "*", 68 | "@types/rx-lite-async": "*", 69 | "@types/rx-lite-backpressure": "*", 70 | "@types/rx-lite-coincidence": "*", 71 | "@types/rx-lite-experimental": "*", 72 | "@types/rx-lite-joinpatterns": "*", 73 | "@types/rx-lite-testing": "*", 74 | "@types/rx-lite-time": "*", 75 | "@types/rx-lite-virtualtime": "*" 76 | } 77 | }, 78 | "@types/rx-core": { 79 | "version": "4.0.3", 80 | "resolved": "https://registry.npmjs.org/@types/rx-core/-/rx-core-4.0.3.tgz", 81 | "integrity": "sha1-CzNUsSOM7b4rdPYybxOdvHpZHWA=", 82 | "dev": true 83 | }, 84 | "@types/rx-core-binding": { 85 | "version": "4.0.4", 86 | "resolved": "https://registry.npmjs.org/@types/rx-core-binding/-/rx-core-binding-4.0.4.tgz", 87 | "integrity": "sha512-5pkfxnC4w810LqBPUwP5bg7SFR/USwhMSaAeZQQbEHeBp57pjKXRlXmqpMrLJB4y1oglR/c2502853uN0I+DAQ==", 88 | "dev": true, 89 | "requires": { 90 | "@types/rx-core": "*" 91 | } 92 | }, 93 | "@types/rx-lite": { 94 | "version": "4.0.6", 95 | "resolved": "https://registry.npmjs.org/@types/rx-lite/-/rx-lite-4.0.6.tgz", 96 | "integrity": "sha512-oYiDrFIcor9zDm0VDUca1UbROiMYBxMLMaM6qzz4ADAfOmA9r1dYEcAFH+2fsPI5BCCjPvV9pWC3X3flbrvs7w==", 97 | "dev": true, 98 | "requires": { 99 | "@types/rx-core": "*", 100 | "@types/rx-core-binding": "*" 101 | } 102 | }, 103 | "@types/rx-lite-aggregates": { 104 | "version": "4.0.3", 105 | "resolved": "https://registry.npmjs.org/@types/rx-lite-aggregates/-/rx-lite-aggregates-4.0.3.tgz", 106 | "integrity": "sha512-MAGDAHy8cRatm94FDduhJF+iNS5//jrZ/PIfm+QYw9OCeDgbymFHChM8YVIvN2zArwsRftKgE33QfRWvQk4DPg==", 107 | "dev": true, 108 | "requires": { 109 | "@types/rx-lite": "*" 110 | } 111 | }, 112 | "@types/rx-lite-async": { 113 | "version": "4.0.2", 114 | "resolved": "https://registry.npmjs.org/@types/rx-lite-async/-/rx-lite-async-4.0.2.tgz", 115 | "integrity": "sha512-vTEv5o8l6702ZwfAM5aOeVDfUwBSDOs+ARoGmWAKQ6LOInQ8J4/zjM7ov12fuTpktUKdMQjkeCp07Vd73mPkxw==", 116 | "dev": true, 117 | "requires": { 118 | "@types/rx-lite": "*" 119 | } 120 | }, 121 | "@types/rx-lite-backpressure": { 122 | "version": "4.0.3", 123 | "resolved": "https://registry.npmjs.org/@types/rx-lite-backpressure/-/rx-lite-backpressure-4.0.3.tgz", 124 | "integrity": "sha512-Y6aIeQCtNban5XSAF4B8dffhIKu6aAy/TXFlScHzSxh6ivfQBQw6UjxyEJxIOt3IT49YkS+siuayM2H/Q0cmgA==", 125 | "dev": true, 126 | "requires": { 127 | "@types/rx-lite": "*" 128 | } 129 | }, 130 | "@types/rx-lite-coincidence": { 131 | "version": "4.0.3", 132 | "resolved": "https://registry.npmjs.org/@types/rx-lite-coincidence/-/rx-lite-coincidence-4.0.3.tgz", 133 | "integrity": "sha512-1VNJqzE9gALUyMGypDXZZXzR0Tt7LC9DdAZQ3Ou/Q0MubNU35agVUNXKGHKpNTba+fr8GdIdkC26bRDqtCQBeQ==", 134 | "dev": true, 135 | "requires": { 136 | "@types/rx-lite": "*" 137 | } 138 | }, 139 | "@types/rx-lite-experimental": { 140 | "version": "4.0.1", 141 | "resolved": "https://registry.npmjs.org/@types/rx-lite-experimental/-/rx-lite-experimental-4.0.1.tgz", 142 | "integrity": "sha1-xTL1y98/LBXaFt7Ykw0bKYQCPL0=", 143 | "dev": true, 144 | "requires": { 145 | "@types/rx-lite": "*" 146 | } 147 | }, 148 | "@types/rx-lite-joinpatterns": { 149 | "version": "4.0.1", 150 | "resolved": "https://registry.npmjs.org/@types/rx-lite-joinpatterns/-/rx-lite-joinpatterns-4.0.1.tgz", 151 | "integrity": "sha1-9w/jcFGKhDLykVjMkv+1a05K/D4=", 152 | "dev": true, 153 | "requires": { 154 | "@types/rx-lite": "*" 155 | } 156 | }, 157 | "@types/rx-lite-testing": { 158 | "version": "4.0.1", 159 | "resolved": "https://registry.npmjs.org/@types/rx-lite-testing/-/rx-lite-testing-4.0.1.tgz", 160 | "integrity": "sha1-IbGdEfTf1v/vWp0WSOnIh5v+Iek=", 161 | "dev": true, 162 | "requires": { 163 | "@types/rx-lite-virtualtime": "*" 164 | } 165 | }, 166 | "@types/rx-lite-time": { 167 | "version": "4.0.3", 168 | "resolved": "https://registry.npmjs.org/@types/rx-lite-time/-/rx-lite-time-4.0.3.tgz", 169 | "integrity": "sha512-ukO5sPKDRwCGWRZRqPlaAU0SKVxmWwSjiOrLhoQDoWxZWg6vyB9XLEZViKOzIO6LnTIQBlk4UylYV0rnhJLxQw==", 170 | "dev": true, 171 | "requires": { 172 | "@types/rx-lite": "*" 173 | } 174 | }, 175 | "@types/rx-lite-virtualtime": { 176 | "version": "4.0.3", 177 | "resolved": "https://registry.npmjs.org/@types/rx-lite-virtualtime/-/rx-lite-virtualtime-4.0.3.tgz", 178 | "integrity": "sha512-3uC6sGmjpOKatZSVHI2xB1+dedgml669ZRvqxy+WqmGJDVusOdyxcKfyzjW0P3/GrCiN4nmRkLVMhPwHCc5QLg==", 179 | "dev": true, 180 | "requires": { 181 | "@types/rx-lite": "*" 182 | } 183 | }, 184 | "@types/semver": { 185 | "version": "5.5.0", 186 | "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", 187 | "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==", 188 | "dev": true 189 | }, 190 | "@types/through": { 191 | "version": "0.0.29", 192 | "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.29.tgz", 193 | "integrity": "sha512-9a7C5VHh+1BKblaYiq+7Tfc+EOmjMdZaD1MYtkQjSoxgB69tBjW98ry6SKsi4zEIWztLOMRuL87A3bdT/Fc/4w==", 194 | "dev": true, 195 | "requires": { 196 | "@types/node": "*" 197 | } 198 | }, 199 | "@types/yargs": { 200 | "version": "11.1.3", 201 | "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-11.1.3.tgz", 202 | "integrity": "sha512-moBUF6X8JsK5MbLZGP3vCfG/TVHZHsaePj3EimlLKp8+ESUjGjpXalxyn90a2L9fTM2ZGtW4swb6Am1DvVRNGA==", 203 | "dev": true 204 | }, 205 | "ansi-escapes": { 206 | "version": "3.2.0", 207 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", 208 | "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" 209 | }, 210 | "ansi-regex": { 211 | "version": "3.0.0", 212 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 213 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 214 | }, 215 | "ansi-styles": { 216 | "version": "3.2.1", 217 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 218 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 219 | "requires": { 220 | "color-convert": "^1.9.0" 221 | } 222 | }, 223 | "archy": { 224 | "version": "1.0.0", 225 | "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", 226 | "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" 227 | }, 228 | "camelcase": { 229 | "version": "4.1.0", 230 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", 231 | "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" 232 | }, 233 | "chalk": { 234 | "version": "2.4.2", 235 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 236 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 237 | "requires": { 238 | "ansi-styles": "^3.2.1", 239 | "escape-string-regexp": "^1.0.5", 240 | "supports-color": "^5.3.0" 241 | } 242 | }, 243 | "chardet": { 244 | "version": "0.4.2", 245 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", 246 | "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" 247 | }, 248 | "cli-cursor": { 249 | "version": "2.1.0", 250 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", 251 | "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", 252 | "requires": { 253 | "restore-cursor": "^2.0.0" 254 | } 255 | }, 256 | "cli-spinners": { 257 | "version": "1.3.1", 258 | "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.3.1.tgz", 259 | "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==" 260 | }, 261 | "cli-width": { 262 | "version": "2.2.0", 263 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 264 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" 265 | }, 266 | "cliui": { 267 | "version": "4.1.0", 268 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", 269 | "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", 270 | "requires": { 271 | "string-width": "^2.1.1", 272 | "strip-ansi": "^4.0.0", 273 | "wrap-ansi": "^2.0.0" 274 | } 275 | }, 276 | "clone": { 277 | "version": "1.0.4", 278 | "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", 279 | "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" 280 | }, 281 | "code-point-at": { 282 | "version": "1.1.0", 283 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 284 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 285 | }, 286 | "color-convert": { 287 | "version": "1.9.3", 288 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 289 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 290 | "requires": { 291 | "color-name": "1.1.3" 292 | } 293 | }, 294 | "color-name": { 295 | "version": "1.1.3", 296 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 297 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 298 | }, 299 | "cross-spawn": { 300 | "version": "5.1.0", 301 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 302 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 303 | "requires": { 304 | "lru-cache": "^4.0.1", 305 | "shebang-command": "^1.2.0", 306 | "which": "^1.2.9" 307 | } 308 | }, 309 | "decamelize": { 310 | "version": "1.2.0", 311 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 312 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 313 | }, 314 | "defaults": { 315 | "version": "1.0.3", 316 | "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", 317 | "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", 318 | "requires": { 319 | "clone": "^1.0.2" 320 | } 321 | }, 322 | "end-of-stream": { 323 | "version": "1.4.4", 324 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 325 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 326 | "requires": { 327 | "once": "^1.4.0" 328 | } 329 | }, 330 | "escape-string-regexp": { 331 | "version": "1.0.5", 332 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 333 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 334 | }, 335 | "execa": { 336 | "version": "0.6.3", 337 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.6.3.tgz", 338 | "integrity": "sha1-V7aaWU8IF1nGnlNw8NF7nLEWWP4=", 339 | "requires": { 340 | "cross-spawn": "^5.0.1", 341 | "get-stream": "^3.0.0", 342 | "is-stream": "^1.1.0", 343 | "npm-run-path": "^2.0.0", 344 | "p-finally": "^1.0.0", 345 | "signal-exit": "^3.0.0", 346 | "strip-eof": "^1.0.0" 347 | } 348 | }, 349 | "external-editor": { 350 | "version": "2.2.0", 351 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", 352 | "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", 353 | "requires": { 354 | "chardet": "^0.4.0", 355 | "iconv-lite": "^0.4.17", 356 | "tmp": "^0.0.33" 357 | } 358 | }, 359 | "figures": { 360 | "version": "2.0.0", 361 | "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", 362 | "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", 363 | "requires": { 364 | "escape-string-regexp": "^1.0.5" 365 | } 366 | }, 367 | "find-up": { 368 | "version": "2.1.0", 369 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", 370 | "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", 371 | "requires": { 372 | "locate-path": "^2.0.0" 373 | } 374 | }, 375 | "fs-extra": { 376 | "version": "6.0.1", 377 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", 378 | "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", 379 | "requires": { 380 | "graceful-fs": "^4.1.2", 381 | "jsonfile": "^4.0.0", 382 | "universalify": "^0.1.0" 383 | } 384 | }, 385 | "get-caller-file": { 386 | "version": "1.0.3", 387 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", 388 | "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" 389 | }, 390 | "get-stream": { 391 | "version": "3.0.0", 392 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 393 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" 394 | }, 395 | "graceful-fs": { 396 | "version": "4.2.3", 397 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 398 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" 399 | }, 400 | "has-flag": { 401 | "version": "3.0.0", 402 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 403 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 404 | }, 405 | "iconv-lite": { 406 | "version": "0.4.24", 407 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 408 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 409 | "requires": { 410 | "safer-buffer": ">= 2.1.2 < 3" 411 | } 412 | }, 413 | "import-local": { 414 | "version": "1.0.0", 415 | "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", 416 | "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", 417 | "requires": { 418 | "pkg-dir": "^2.0.0", 419 | "resolve-cwd": "^2.0.0" 420 | } 421 | }, 422 | "inquirer": { 423 | "version": "5.2.0", 424 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", 425 | "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", 426 | "requires": { 427 | "ansi-escapes": "^3.0.0", 428 | "chalk": "^2.0.0", 429 | "cli-cursor": "^2.1.0", 430 | "cli-width": "^2.0.0", 431 | "external-editor": "^2.1.0", 432 | "figures": "^2.0.0", 433 | "lodash": "^4.3.0", 434 | "mute-stream": "0.0.7", 435 | "run-async": "^2.2.0", 436 | "rxjs": "^5.5.2", 437 | "string-width": "^2.1.0", 438 | "strip-ansi": "^4.0.0", 439 | "through": "^2.3.6" 440 | } 441 | }, 442 | "invert-kv": { 443 | "version": "2.0.0", 444 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", 445 | "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" 446 | }, 447 | "is-admin": { 448 | "version": "2.1.1", 449 | "resolved": "https://registry.npmjs.org/is-admin/-/is-admin-2.1.1.tgz", 450 | "integrity": "sha1-eYaQMOwC0sv4+Ky1cFR1wiY5T7o=", 451 | "requires": { 452 | "execa": "^0.6.1" 453 | } 454 | }, 455 | "is-elevated": { 456 | "version": "2.0.1", 457 | "resolved": "https://registry.npmjs.org/is-elevated/-/is-elevated-2.0.1.tgz", 458 | "integrity": "sha1-CisKArXdvlYOM5kqzUtURUdJmZg=", 459 | "requires": { 460 | "is-admin": "^2.1.0", 461 | "is-root": "^1.0.0" 462 | } 463 | }, 464 | "is-fullwidth-code-point": { 465 | "version": "2.0.0", 466 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 467 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 468 | }, 469 | "is-promise": { 470 | "version": "2.1.0", 471 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", 472 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" 473 | }, 474 | "is-root": { 475 | "version": "1.0.0", 476 | "resolved": "https://registry.npmjs.org/is-root/-/is-root-1.0.0.tgz", 477 | "integrity": "sha1-B7bCM7w5TNnQK6FclmvWZg1jQtU=" 478 | }, 479 | "is-stream": { 480 | "version": "1.1.0", 481 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 482 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 483 | }, 484 | "isexe": { 485 | "version": "2.0.0", 486 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 487 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 488 | }, 489 | "jsonfile": { 490 | "version": "4.0.0", 491 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 492 | "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", 493 | "requires": { 494 | "graceful-fs": "^4.1.6" 495 | } 496 | }, 497 | "lcid": { 498 | "version": "2.0.0", 499 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", 500 | "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", 501 | "requires": { 502 | "invert-kv": "^2.0.0" 503 | } 504 | }, 505 | "locate-path": { 506 | "version": "2.0.0", 507 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", 508 | "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", 509 | "requires": { 510 | "p-locate": "^2.0.0", 511 | "path-exists": "^3.0.0" 512 | } 513 | }, 514 | "lodash": { 515 | "version": "4.17.19", 516 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", 517 | "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" 518 | }, 519 | "log-symbols": { 520 | "version": "2.2.0", 521 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", 522 | "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", 523 | "requires": { 524 | "chalk": "^2.0.1" 525 | } 526 | }, 527 | "lru-cache": { 528 | "version": "4.1.5", 529 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 530 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 531 | "requires": { 532 | "pseudomap": "^1.0.2", 533 | "yallist": "^2.1.2" 534 | } 535 | }, 536 | "map-age-cleaner": { 537 | "version": "0.1.3", 538 | "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", 539 | "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", 540 | "requires": { 541 | "p-defer": "^1.0.0" 542 | } 543 | }, 544 | "mem": { 545 | "version": "4.3.0", 546 | "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", 547 | "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", 548 | "requires": { 549 | "map-age-cleaner": "^0.1.1", 550 | "mimic-fn": "^2.0.0", 551 | "p-is-promise": "^2.0.0" 552 | }, 553 | "dependencies": { 554 | "mimic-fn": { 555 | "version": "2.1.0", 556 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 557 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" 558 | } 559 | } 560 | }, 561 | "mimic-fn": { 562 | "version": "1.2.0", 563 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", 564 | "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" 565 | }, 566 | "mute-stream": { 567 | "version": "0.0.7", 568 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", 569 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" 570 | }, 571 | "nice-try": { 572 | "version": "1.0.5", 573 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 574 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" 575 | }, 576 | "npm-run-path": { 577 | "version": "2.0.2", 578 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 579 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 580 | "requires": { 581 | "path-key": "^2.0.0" 582 | } 583 | }, 584 | "number-is-nan": { 585 | "version": "1.0.1", 586 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 587 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 588 | }, 589 | "once": { 590 | "version": "1.4.0", 591 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 592 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 593 | "requires": { 594 | "wrappy": "1" 595 | } 596 | }, 597 | "onetime": { 598 | "version": "2.0.1", 599 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", 600 | "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", 601 | "requires": { 602 | "mimic-fn": "^1.0.0" 603 | } 604 | }, 605 | "ora": { 606 | "version": "2.1.0", 607 | "resolved": "https://registry.npmjs.org/ora/-/ora-2.1.0.tgz", 608 | "integrity": "sha512-hNNlAd3gfv/iPmsNxYoAPLvxg7HuPozww7fFonMZvL84tP6Ox5igfk5j/+a9rtJJwqMgKK+JgWsAQik5o0HTLA==", 609 | "requires": { 610 | "chalk": "^2.3.1", 611 | "cli-cursor": "^2.1.0", 612 | "cli-spinners": "^1.1.0", 613 | "log-symbols": "^2.2.0", 614 | "strip-ansi": "^4.0.0", 615 | "wcwidth": "^1.0.1" 616 | } 617 | }, 618 | "os-locale": { 619 | "version": "3.1.0", 620 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", 621 | "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", 622 | "requires": { 623 | "execa": "^1.0.0", 624 | "lcid": "^2.0.0", 625 | "mem": "^4.0.0" 626 | }, 627 | "dependencies": { 628 | "cross-spawn": { 629 | "version": "6.0.5", 630 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 631 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 632 | "requires": { 633 | "nice-try": "^1.0.4", 634 | "path-key": "^2.0.1", 635 | "semver": "^5.5.0", 636 | "shebang-command": "^1.2.0", 637 | "which": "^1.2.9" 638 | } 639 | }, 640 | "execa": { 641 | "version": "1.0.0", 642 | "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", 643 | "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", 644 | "requires": { 645 | "cross-spawn": "^6.0.0", 646 | "get-stream": "^4.0.0", 647 | "is-stream": "^1.1.0", 648 | "npm-run-path": "^2.0.0", 649 | "p-finally": "^1.0.0", 650 | "signal-exit": "^3.0.0", 651 | "strip-eof": "^1.0.0" 652 | } 653 | }, 654 | "get-stream": { 655 | "version": "4.1.0", 656 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", 657 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", 658 | "requires": { 659 | "pump": "^3.0.0" 660 | } 661 | } 662 | } 663 | }, 664 | "os-tmpdir": { 665 | "version": "1.0.2", 666 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 667 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 668 | }, 669 | "p-defer": { 670 | "version": "1.0.0", 671 | "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", 672 | "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" 673 | }, 674 | "p-finally": { 675 | "version": "1.0.0", 676 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 677 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 678 | }, 679 | "p-is-promise": { 680 | "version": "2.1.0", 681 | "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", 682 | "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" 683 | }, 684 | "p-limit": { 685 | "version": "1.3.0", 686 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", 687 | "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", 688 | "requires": { 689 | "p-try": "^1.0.0" 690 | } 691 | }, 692 | "p-locate": { 693 | "version": "2.0.0", 694 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", 695 | "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", 696 | "requires": { 697 | "p-limit": "^1.1.0" 698 | } 699 | }, 700 | "p-try": { 701 | "version": "1.0.0", 702 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", 703 | "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" 704 | }, 705 | "path-exists": { 706 | "version": "3.0.0", 707 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 708 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" 709 | }, 710 | "path-key": { 711 | "version": "2.0.1", 712 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 713 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" 714 | }, 715 | "path-to-regexp": { 716 | "version": "2.4.0", 717 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", 718 | "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==", 719 | "dev": true 720 | }, 721 | "pkg-dir": { 722 | "version": "2.0.0", 723 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", 724 | "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", 725 | "requires": { 726 | "find-up": "^2.1.0" 727 | } 728 | }, 729 | "pseudomap": { 730 | "version": "1.0.2", 731 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 732 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 733 | }, 734 | "pump": { 735 | "version": "3.0.0", 736 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 737 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 738 | "requires": { 739 | "end-of-stream": "^1.1.0", 740 | "once": "^1.3.1" 741 | } 742 | }, 743 | "require-directory": { 744 | "version": "2.1.1", 745 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 746 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 747 | }, 748 | "require-main-filename": { 749 | "version": "1.0.1", 750 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", 751 | "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" 752 | }, 753 | "resolve-cwd": { 754 | "version": "2.0.0", 755 | "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", 756 | "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", 757 | "requires": { 758 | "resolve-from": "^3.0.0" 759 | } 760 | }, 761 | "resolve-from": { 762 | "version": "3.0.0", 763 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", 764 | "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" 765 | }, 766 | "restore-cursor": { 767 | "version": "2.0.0", 768 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", 769 | "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", 770 | "requires": { 771 | "onetime": "^2.0.0", 772 | "signal-exit": "^3.0.2" 773 | } 774 | }, 775 | "run-async": { 776 | "version": "2.3.0", 777 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", 778 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", 779 | "requires": { 780 | "is-promise": "^2.1.0" 781 | } 782 | }, 783 | "rxjs": { 784 | "version": "5.5.12", 785 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", 786 | "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", 787 | "requires": { 788 | "symbol-observable": "1.0.1" 789 | } 790 | }, 791 | "safer-buffer": { 792 | "version": "2.1.2", 793 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 794 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 795 | }, 796 | "semver": { 797 | "version": "5.7.1", 798 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 799 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 800 | }, 801 | "set-blocking": { 802 | "version": "2.0.0", 803 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 804 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 805 | }, 806 | "shebang-command": { 807 | "version": "1.2.0", 808 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 809 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 810 | "requires": { 811 | "shebang-regex": "^1.0.0" 812 | } 813 | }, 814 | "shebang-regex": { 815 | "version": "1.0.0", 816 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 817 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 818 | }, 819 | "signal-exit": { 820 | "version": "3.0.2", 821 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 822 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 823 | }, 824 | "string-width": { 825 | "version": "2.1.1", 826 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 827 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 828 | "requires": { 829 | "is-fullwidth-code-point": "^2.0.0", 830 | "strip-ansi": "^4.0.0" 831 | } 832 | }, 833 | "strip-ansi": { 834 | "version": "4.0.0", 835 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 836 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 837 | "requires": { 838 | "ansi-regex": "^3.0.0" 839 | } 840 | }, 841 | "strip-eof": { 842 | "version": "1.0.0", 843 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 844 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" 845 | }, 846 | "supports-color": { 847 | "version": "5.5.0", 848 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 849 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 850 | "requires": { 851 | "has-flag": "^3.0.0" 852 | } 853 | }, 854 | "symbol-observable": { 855 | "version": "1.0.1", 856 | "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", 857 | "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" 858 | }, 859 | "through": { 860 | "version": "2.3.8", 861 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 862 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 863 | }, 864 | "tmp": { 865 | "version": "0.0.33", 866 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 867 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 868 | "requires": { 869 | "os-tmpdir": "~1.0.2" 870 | } 871 | }, 872 | "tslib": { 873 | "version": "1.10.0", 874 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", 875 | "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" 876 | }, 877 | "universalify": { 878 | "version": "0.1.2", 879 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 880 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" 881 | }, 882 | "wcwidth": { 883 | "version": "1.0.1", 884 | "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", 885 | "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", 886 | "requires": { 887 | "defaults": "^1.0.3" 888 | } 889 | }, 890 | "which": { 891 | "version": "1.3.1", 892 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 893 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 894 | "requires": { 895 | "isexe": "^2.0.0" 896 | } 897 | }, 898 | "which-module": { 899 | "version": "2.0.0", 900 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 901 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" 902 | }, 903 | "wrap-ansi": { 904 | "version": "2.1.0", 905 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 906 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 907 | "requires": { 908 | "string-width": "^1.0.1", 909 | "strip-ansi": "^3.0.1" 910 | }, 911 | "dependencies": { 912 | "ansi-regex": { 913 | "version": "2.1.1", 914 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 915 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 916 | }, 917 | "is-fullwidth-code-point": { 918 | "version": "1.0.0", 919 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 920 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 921 | "requires": { 922 | "number-is-nan": "^1.0.0" 923 | } 924 | }, 925 | "string-width": { 926 | "version": "1.0.2", 927 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 928 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 929 | "requires": { 930 | "code-point-at": "^1.0.0", 931 | "is-fullwidth-code-point": "^1.0.0", 932 | "strip-ansi": "^3.0.0" 933 | } 934 | }, 935 | "strip-ansi": { 936 | "version": "3.0.1", 937 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 938 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 939 | "requires": { 940 | "ansi-regex": "^2.0.0" 941 | } 942 | } 943 | } 944 | }, 945 | "wrappy": { 946 | "version": "1.0.2", 947 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 948 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 949 | }, 950 | "y18n": { 951 | "version": "3.2.1", 952 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", 953 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" 954 | }, 955 | "yallist": { 956 | "version": "2.1.2", 957 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 958 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 959 | }, 960 | "yargs": { 961 | "version": "11.1.1", 962 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.1.tgz", 963 | "integrity": "sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw==", 964 | "requires": { 965 | "cliui": "^4.0.0", 966 | "decamelize": "^1.1.1", 967 | "find-up": "^2.1.0", 968 | "get-caller-file": "^1.0.1", 969 | "os-locale": "^3.1.0", 970 | "require-directory": "^2.1.1", 971 | "require-main-filename": "^1.0.1", 972 | "set-blocking": "^2.0.0", 973 | "string-width": "^2.0.0", 974 | "which-module": "^2.0.0", 975 | "y18n": "^3.2.1", 976 | "yargs-parser": "^9.0.2" 977 | }, 978 | "dependencies": { 979 | "yargs-parser": { 980 | "version": "9.0.2", 981 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", 982 | "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", 983 | "requires": { 984 | "camelcase": "^4.1.0" 985 | } 986 | } 987 | } 988 | }, 989 | "yargs-parser": { 990 | "version": "13.1.2", 991 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 992 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 993 | "requires": { 994 | "camelcase": "^5.0.0", 995 | "decamelize": "^1.2.0" 996 | }, 997 | "dependencies": { 998 | "camelcase": { 999 | "version": "5.3.1", 1000 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 1001 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 1002 | } 1003 | } 1004 | } 1005 | } 1006 | } 1007 | --------------------------------------------------------------------------------