├── .editorconfig ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── e2e ├── ngm-multi │ ├── .gitignore │ └── src │ │ ├── mod-1 │ │ ├── alert.component.html │ │ ├── alert.component.ts │ │ ├── alert.module.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── scss │ │ │ └── _variables.scss │ │ └── tsconfig.json │ │ └── mod-2 │ │ ├── alert.component.html │ │ ├── alert.component.ts │ │ ├── alert.module.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── scss │ │ └── _variables.scss │ │ └── tsconfig.json └── ngm-single │ ├── .gitignore │ └── src │ ├── alert.component.html │ ├── alert.component.ts │ ├── alert.module.ts │ ├── index.ts │ ├── package.json │ ├── scss │ └── _variables.scss │ └── tsconfig.json ├── package-lock.json ├── package.json ├── src ├── @types │ ├── cpy │ │ ├── index.d.ts │ │ └── package.json │ └── listr │ │ ├── index.d.ts │ │ └── package.json ├── README.md ├── bin │ └── ngm-cli.ts ├── commands │ ├── build.command.ts │ ├── dist-tag.command.ts │ ├── index.ts │ ├── init.ts │ ├── link.command.ts │ ├── publish.command.ts │ ├── test.ts │ └── version.command.ts ├── helpers │ └── inline-resources.ts ├── lib │ └── ngm.ts ├── models │ ├── rollup.globals.ts │ └── webpack-umd.config.ts ├── package.json ├── tasks │ ├── build.task.ts │ ├── bundle-es2015.ts │ ├── bundle-umd.task.ts │ ├── clean-dist.task.ts │ ├── index.ts │ ├── npm │ │ ├── build-pkg-json.task.ts │ │ ├── index.ts │ │ ├── npm-dist-tag.task.ts │ │ ├── npm-install.task.ts │ │ ├── npm-link.task.ts │ │ ├── npm-publish.task.ts │ │ └── npm-version.task.ts │ └── prepublish-git-check.task.ts ├── test │ └── e2e.ts ├── tsconfig.json ├── types.ts └── utils │ ├── constants.ts │ ├── index.ts │ ├── merge-package-json.ts │ ├── submodules-resolution.ts │ └── tasks-watch.ts └── todo.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn.lock 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | 40 | # IDEs and editors 41 | /.idea 42 | /.vscode 43 | .project 44 | .classpath 45 | *.launch 46 | .settings/ 47 | 48 | dist 49 | 50 | **/*.js 51 | **/*.js.map 52 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact = true 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Valor Software 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # npm-submodules 2 | Simple way to manage typescipt | angular2 submodules from one repository 3 | 4 | This is a base lib 5 | 6 | If you want to try this out, please check [tsm-cli](https://www.npmjs.com/package/tsm-cli) and [ngm-cli](https://www.npmjs.com/package/ngm-cli) tools at npm 7 | 8 | ## Developers 9 | How to try this monster locally? 10 | 11 | 1. Git clone source repo 12 | 2. In order to run this you need to build and link src folder 13 | with tsm-cli from npm, it is in dev dependencies already so just do: 14 | ```bash 15 | $ npm run build 16 | ``` 17 | on linux `npm link` requires sudo 18 | ```bash 19 | $ (sudo) ./node_module/.bin/tsm link -p src 20 | ``` 21 | now you have can use 2 tsm versions in parallel 22 | - local tsm - is from npm 23 | - global tsm - is your dev version 24 | 25 | Have fun! ;) -------------------------------------------------------------------------------- /e2e/ngm-multi/.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /temp 3 | /.tmp 4 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-1/alert.component.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-1/alert.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'alert', 5 | templateUrl: './alert.component.html' 6 | }) 7 | export class AlertComponent implements OnInit { 8 | @Input() public type:string = 'warning'; 9 | @Input() public dismissible:boolean; 10 | @Input() public dismissOnTimeout:number; 11 | 12 | @Output() public close:EventEmitter = new EventEmitter(false); 13 | 14 | public closed:boolean; 15 | protected classes:Array = []; 16 | 17 | public ngOnInit():any { 18 | this.classes[0] = `alert-${this.type}`; 19 | if (this.dismissible) { 20 | this.classes[1] = 'alert-dismissible'; 21 | } else { 22 | this.classes.length = 1; 23 | } 24 | 25 | if (this.dismissOnTimeout) { 26 | setTimeout(() => this.onClose(), this.dismissOnTimeout); 27 | } 28 | } 29 | 30 | // todo: mouse event + touch + pointer 31 | public onClose():void { 32 | this.closed = true; 33 | this.close.emit(this); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-1/alert.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AlertComponent } from './alert.component'; 5 | 6 | @NgModule({ 7 | imports: [CommonModule], 8 | declarations: [AlertComponent], 9 | exports: [AlertComponent] 10 | }) 11 | export class AlertModule { 12 | } 13 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-1/index.ts: -------------------------------------------------------------------------------- 1 | export { AlertComponent } from './alert.component'; 2 | export { AlertModule } from './alert.module'; 3 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng2-module", 3 | "version": "0.0.1" 4 | } 5 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-1/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | //-Brand Colors---------------- 2 | $color-primary-light: #90A4AE; 3 | $color-primary: #182642; 4 | $color-primary-dark: #1c202a; 5 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "../../dist/mod-1", 4 | "module": "commonjs", 5 | "target": "es5", 6 | "moduleResolution": "node", 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "sourceMap": true, 10 | "noEmitHelpers": false, 11 | "declaration": true, 12 | "skipLibCheck": true, 13 | "stripInternal": true, 14 | "noImplicitAny": false, 15 | "noEmitOnError": false, 16 | "lib": ["dom", "es6"] 17 | }, 18 | "exclude": [ 19 | "node_modules" 20 | ], 21 | "angularCompilerOptions": { 22 | "genDir": "../../temp/mod-1/factories", 23 | "strictMetadataEmit": true, 24 | "skipTemplateCodegen": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-2/alert.component.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-2/alert.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'alert', 5 | templateUrl: './alert.component.html' 6 | }) 7 | export class AlertComponent implements OnInit { 8 | @Input() public type:string = 'warning'; 9 | @Input() public dismissible:boolean; 10 | @Input() public dismissOnTimeout:number; 11 | 12 | @Output() public close:EventEmitter = new EventEmitter(false); 13 | 14 | public closed:boolean; 15 | protected classes:Array = []; 16 | 17 | public ngOnInit():any { 18 | this.classes[0] = `alert-${this.type}`; 19 | if (this.dismissible) { 20 | this.classes[1] = 'alert-dismissible'; 21 | } else { 22 | this.classes.length = 1; 23 | } 24 | 25 | if (this.dismissOnTimeout) { 26 | setTimeout(() => this.onClose(), this.dismissOnTimeout); 27 | } 28 | } 29 | 30 | // todo: mouse event + touch + pointer 31 | public onClose():void { 32 | this.closed = true; 33 | this.close.emit(this); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-2/alert.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AlertComponent } from './alert.component'; 5 | 6 | @NgModule({ 7 | imports: [CommonModule], 8 | declarations: [AlertComponent], 9 | exports: [AlertComponent] 10 | }) 11 | export class AlertModule { 12 | } 13 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-2/index.ts: -------------------------------------------------------------------------------- 1 | export { AlertComponent } from './alert.component'; 2 | export { AlertModule } from './alert.module'; 3 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng2-module2", 3 | "version": "0.0.1" 4 | } 5 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-2/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | //-Brand Colors---------------- 2 | $color-primary-light: #90A4AE; 3 | $color-primary: #182642; 4 | $color-primary-dark: #1c202a; 5 | -------------------------------------------------------------------------------- /e2e/ngm-multi/src/mod-2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "../../dist/mod-2", 4 | "module": "commonjs", 5 | "target": "es5", 6 | "moduleResolution": "node", 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "sourceMap": true, 10 | "noEmitHelpers": false, 11 | "declaration": true, 12 | "skipLibCheck": true, 13 | "stripInternal": true, 14 | "noImplicitAny": false, 15 | "noEmitOnError": false, 16 | "lib": ["dom", "es6"] 17 | }, 18 | "exclude": [ 19 | "node_modules" 20 | ], 21 | "angularCompilerOptions": { 22 | "genDir": "../../temp/mod-2/factories", 23 | "strictMetadataEmit": true, 24 | "skipTemplateCodegen": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /e2e/ngm-single/.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /temp 3 | /.tmp 4 | -------------------------------------------------------------------------------- /e2e/ngm-single/src/alert.component.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /e2e/ngm-single/src/alert.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'alert', 5 | templateUrl: './alert.component.html' 6 | }) 7 | export class AlertComponent implements OnInit { 8 | @Input() public type:string = 'warning'; 9 | @Input() public dismissible:boolean; 10 | @Input() public dismissOnTimeout:number; 11 | 12 | @Output() public close:EventEmitter = new EventEmitter(false); 13 | 14 | public closed:boolean; 15 | protected classes:Array = []; 16 | 17 | public ngOnInit():any { 18 | this.classes[0] = `alert-${this.type}`; 19 | if (this.dismissible) { 20 | this.classes[1] = 'alert-dismissible'; 21 | } else { 22 | this.classes.length = 1; 23 | } 24 | 25 | if (this.dismissOnTimeout) { 26 | setTimeout(() => this.onClose(), this.dismissOnTimeout); 27 | } 28 | } 29 | 30 | // todo: mouse event + touch + pointer 31 | public onClose():void { 32 | this.closed = true; 33 | this.close.emit(this); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /e2e/ngm-single/src/alert.module.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from '@angular/common'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AlertComponent } from './alert.component'; 5 | 6 | @NgModule({ 7 | imports: [CommonModule], 8 | declarations: [AlertComponent], 9 | exports: [AlertComponent] 10 | }) 11 | export class AlertModule { 12 | } 13 | -------------------------------------------------------------------------------- /e2e/ngm-single/src/index.ts: -------------------------------------------------------------------------------- 1 | export { AlertComponent } from './alert.component'; 2 | export { AlertModule } from './alert.module'; 3 | -------------------------------------------------------------------------------- /e2e/ngm-single/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng2-module", 3 | "version": "0.0.1" 4 | } 5 | -------------------------------------------------------------------------------- /e2e/ngm-single/src/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | //-Brand Colors---------------- 2 | $color-primary-light: #90A4AE; 3 | $color-primary: #182642; 4 | $color-primary-dark: #1c202a; 5 | -------------------------------------------------------------------------------- /e2e/ngm-single/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "../dist", 4 | "module": "commonjs", 5 | "target": "es5", 6 | "moduleResolution": "node", 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "sourceMap": true, 10 | "noEmitHelpers": false, 11 | "declaration": true, 12 | "skipLibCheck": true, 13 | "stripInternal": true, 14 | "noImplicitAny": false, 15 | "noEmitOnError": false, 16 | "lib": ["dom", "es6"] 17 | }, 18 | "exclude": [ 19 | "node_modules" 20 | ], 21 | "angularCompilerOptions": { 22 | "genDir": "../temp/factories", 23 | "strictMetadataEmit": true, 24 | "skipTemplateCodegen": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base-npm-submodules", 3 | "version": "1.0.4", 4 | "private": true, 5 | "description": "Simple way to manage angular submodules in one repository", 6 | "bin": { 7 | "ngm": "bin/ngm-cli.js" 8 | }, 9 | "scripts": { 10 | "build": "tsc -p src/tsconfig.json", 11 | "test": "echo 'all good, move on ;)'", 12 | "e2e-tsm": "node dist/test/e2e.js", 13 | "e2e": "echo 'dont touch it'", 14 | "ebuild-multi": "ngm build -p e2e/ngm-multi", 15 | "ebuild-single": "ngm build -p e2e/ngm-single" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+ssh://git@github.com/valor-software/npm-submodules.git" 20 | }, 21 | "keywords": [ 22 | "npm", 23 | "node", 24 | "typescript", 25 | "angular", 26 | "angular2", 27 | "angularcli" 28 | ], 29 | "author": "Dmitriy Shekhovtsov (https://twitter.com/valorkin)", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/valor-software/npm-submodules/issues" 33 | }, 34 | "homepage": "https://github.com/valor-software/npm-submodules#readme", 35 | "peerDependencies": { 36 | "ts-loader": ">=4.3.0", 37 | "node-sass": ">=4.0.0", 38 | "node-sass-tilde-importer": ">=1.0.0", 39 | "webpack": ">=4.0.0", 40 | "typescript": ">=2.7.2" 41 | }, 42 | "dependencies": { 43 | "chalk": "2.4.1", 44 | "cpy": "7.0.0", 45 | "del": "3.0.0", 46 | "execa": "0.10.0", 47 | "inquirer": "5.2.0", 48 | "listr": "0.14.1", 49 | "lodash": "4.17.10", 50 | "meow": "5.0.0", 51 | "node-sass": "^4.5.3", 52 | "node-sass-tilde-importer": "1.0.2", 53 | "read-pkg": "3.0.0", 54 | "rollup": "0.58.2", 55 | "rollup-plugin-commonjs": "9.1.3", 56 | "rollup-plugin-node-resolve": "3.3.0", 57 | "semver": "5.5.0", 58 | "split": "1.0.1", 59 | "stream-to-observable": "0.2.0", 60 | "ts-loader": "4.3.0", 61 | "tsconfig": "7.0.0", 62 | "typescript": "~2.7.2", 63 | "update-notifier": "2.5.0", 64 | "webpack": "4.6.0", 65 | "write-pkg": "3.1.0" 66 | }, 67 | "devDependencies": { 68 | "@angular/animations": "6.0.1", 69 | "@angular/common": "6.0.1", 70 | "@angular/compiler": "6.0.1", 71 | "@angular/compiler-cli": "6.0.1", 72 | "@angular/core": "6.0.1", 73 | "@angular/forms": "6.0.1", 74 | "@angular/http": "6.0.1", 75 | "@angular/platform-browser": "6.0.1", 76 | "@angular/platform-browser-dynamic": "6.0.1", 77 | "@angular/platform-server": "6.0.1", 78 | "@angular/router": "6.0.1", 79 | "@angular/tsc-wrapped": "4.4.6", 80 | "@types/chokidar": "1.7.5", 81 | "@types/node": "10.0.8", 82 | "@types/webpack": "4.1.6", 83 | "reflect-metadata": "0.1.12", 84 | "rxjs": "6.1.0", 85 | "zone.js": "0.8.26" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/@types/cpy/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'cpy' { 2 | declare interface CpyOptions { 3 | cwd?:string; 4 | parents?:boolean; 5 | rename?:string; 6 | } 7 | declare function cpy(files:string[], destination: string, 8 | options?:CpyOptions, ); 9 | export = cpy; 10 | } -------------------------------------------------------------------------------- /src/@types/cpy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "listr", 3 | "typings": "index.d.ts", 4 | "main": "index.js" 5 | } 6 | -------------------------------------------------------------------------------- /src/@types/listr/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'listr' { 2 | declare interface ListrTask { 3 | title: string; 4 | task: Function; 5 | skip?: Function; 6 | } 7 | declare interface ListrOptions { 8 | concurrent?: boolean; 9 | renderer?:string|Object; 10 | showSubtasks?:boolean; 11 | } 12 | declare class Listr { 13 | constructor(tasks:ListrTask[], opts?:ListrOptions); 14 | run(context?:Object):Promise; 15 | add(task:ListrTask):Listr; 16 | get task():ListrTask|ListrTask[]; 17 | render():any; 18 | } 19 | export = Listr; 20 | } -------------------------------------------------------------------------------- /src/@types/listr/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "listr", 3 | "typings": "index.d.ts" 4 | } 5 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | Sample projects structure 2 | ``` 3 | +-- dist // target folder 4 | +-- src 5 | | +-- package.json // module package.json 6 | angular-cli.json 7 | package.json // root package.json 8 | ``` 9 | 1. Safety: add `"private":true` to your root `package.json` 10 | 11 | 2. Module package.json (see sample project structure above) should contain dependencies and peerDepencies of module, sample: 12 | 13 | ```json 14 | { 15 | "name": "ng2-bootstrap", 16 | "dependencies": { 17 | "moment": "*" 18 | }, 19 | "peerDependencies": { 20 | "@angular/common": "*", 21 | "@angular/compiler": "*", 22 | "@angular/core": "*", 23 | "@angular/forms": "*" 24 | } 25 | } 26 | ``` 27 | 28 | 3. Module configuration: by default `ngm` reads `angular-cli.json` in projects root 29 | ```json 30 | { 31 | "module":[{ 32 | "name": "ng2-bootstrap", 33 | "root": "src", 34 | "outDir": "dist", 35 | "main":"index.ts", 36 | "tsconfig": "tsconfig.json" 37 | }] 38 | } 39 | ``` 40 | 41 | 4. Running: just add `ngm` script to your root package.json (see sample project structure above) 42 | 43 | ```json 44 | "scripts": { 45 | "build": "ngm" 46 | } 47 | ``` 48 | 49 | 5. Ready steady go: 50 | ```sh 51 | npm run build 52 | ``` 53 | 54 | 6. Now you can go to `dist` folder and do `npm publish` (will be added as a command later) 55 | -------------------------------------------------------------------------------- /src/bin/ngm-cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // todo: add support for config file 3 | // todo: add help per command (sample: tsm help build) 4 | // todd: add test and e2e command (cut of from publish tasks) 5 | // todo: move help to separate file 6 | // todo: use Observables 7 | // todo: add rollup bundle 8 | // todo: disable bundling in watch mode 9 | // todo: add info about --main? read it from package.json? 10 | // todo: validation package.json 11 | 12 | const meow = require('meow'); 13 | const updateNotifier = require('update-notifier'); 14 | 15 | // init - updates root pkg scripts with required commands, adds tsm-readme.md 16 | 17 | const cli = meow(` 18 | Usage 19 | $ ngm [options] 20 | 21 | Commands: 22 | ---------------------------------------------------------------- 23 | build - build typescript projects 24 | Usage: 25 | $ tsm build -p src 26 | Mandatory options: 27 | -p DIRECTORY, Compile the project in the given directory 28 | --project DIRECTORY 29 | Optional options (default: false): 30 | --no-local Use version numbers from local submodules when building package.json, 31 | usually needed only for publish command 32 | -w, --watch Watch input files 33 | --verbose Enable verbose mode 34 | --clean Cleaning dist folders 35 | It removes folder, so you will need to rerun commands like 'link', etc... 36 | --skip-bundles Works only in --watch mode, allows to skip umd bundling 37 | 38 | ---------------------------------------------------------------- 39 | link - runs 'npm link' in each submodule dist folder 40 | Usage: 41 | $ tsm link -p src 42 | Hint: 43 | 'npm link' doesn't track adding new files, please rerun this command if file was added/removed 44 | Mandatory options: 45 | -p DIRECTORY, Compile the project in the given directory 46 | --project DIRECTORY 47 | Optional options: 48 | --no-deep By default local submodules will be linked to each other 49 | --here Links submodules to root package 50 | 51 | ---------------------------------------------------------------- 52 | publish - runs 'npm publish' in each dist submodule folders, how it works: 53 | 54 | 1. Runs git checks (you should be on master branch, without changes in working tree, and up to date with remote history 55 | --any-branch - disables on 'master' check 56 | --skip-git-check - to skip all git checks 57 | 58 | 2. Performs clean install of dependecies 59 | --yarn - to install dependencies with 'yarn' 60 | --skip-cleanup - to not delete 'node_modules' before installing 61 | 62 | 3. Running tests (npm test) from root folder 63 | 64 | 4. Running e2e test (npm run e2e), all submodules will be built in local mode and linked (npm link) 65 | 66 | 5. Publishing, with separate clean build 67 | --skip-publish - skip publishing and clean build, in case you want to double check something 68 | --tag - same as 'npm publish --tag next', use in case you want new release without changing 'latest' npm tag 69 | --access - same as 'npm publish --access p*' 70 | Steps 1-4 can be skipped with --yolo flag 71 | 72 | ---------------------------------------------------------------- 73 | version - runs 'npm version ' in each submodule and than in root folder 74 | Usage: 75 | $ tsm version prerelease -p src 76 | Mandatory options: 77 | -m MESSAGE, Commit message when creating a version commit 78 | --message MESSAGE 79 | --no-git-tag-version Do not create a version commit and tag (applied only to root folder) 80 | Optional options: 81 | --skip-push Skip pushing of version commit and tags 82 | ---------------------------------------------------------------- 83 | 84 | `, { 85 | flags: { 86 | message: { 87 | type: 'string', 88 | alias: 'm' 89 | }, 90 | project: { 91 | type: 'string', 92 | alias: 'p', 93 | default: 'src' 94 | }, 95 | config: { 96 | type: 'string', 97 | alias: 'c' 98 | }, 99 | watch: { 100 | type: 'boolean', 101 | alias: 'w', 102 | default: false 103 | }, 104 | version: { 105 | type: 'boolean', 106 | alias: 'c', 107 | default: false 108 | }, 109 | local: { 110 | type: 'boolean', 111 | default: false 112 | }, 113 | verbose: { 114 | type: 'boolean', 115 | default: true 116 | } 117 | } 118 | }); 119 | 120 | updateNotifier({pkg: cli.pkg}).notify(); 121 | 122 | if (cli.flags.version) { 123 | console.log(`${cli.pkg.name} ${cli.pkg.version}`); 124 | process.exit(); 125 | } 126 | 127 | // show help and exit by default 128 | if (cli.input.length === 0) { 129 | cli.showHelp(0); 130 | } 131 | 132 | // project flag is mandatory for now 133 | // if (!cli.flags.project) { 134 | // console.error('Please provide path to your projects source folder, `-p DIR` '); 135 | // process.exit(1); 136 | // } 137 | 138 | import { main } from '../lib/ngm'; 139 | 140 | Promise 141 | .resolve() 142 | .then(() => { 143 | // default verbose to true 144 | cli.flags.verbose = cli.flags.verbose !== false; 145 | return main(cli.input[0], cli); 146 | }) 147 | .catch(err => { 148 | console.error(`\n`, err && err.stderr || ''); 149 | console.error(`\n`, err); 150 | process.exit(1); 151 | }); 152 | -------------------------------------------------------------------------------- /src/commands/build.command.ts: -------------------------------------------------------------------------------- 1 | // todo: add load from config file, TBD 2 | 3 | import path = require('path'); 4 | 5 | const Listr = require('listr'); 6 | const cpy = require('cpy'); 7 | const del = require('del'); 8 | const fs = require('fs'); 9 | 10 | import { findSubmodules, tasksWatch } from '../utils'; 11 | import { build, bundleUmd, buildPkgs, bundleEs2015 } from '../tasks'; 12 | import { inlineResources } from '../helpers/inline-resources'; 13 | 14 | 15 | export function buildCommand({project, verbose, clean, local, main, watch, skipBundles, buildEs2015}) { 16 | // 1. clean dist folders 17 | // 2.1 merge pkg json 18 | // todo: 2.2 validate pkg (main, module, types fields) 19 | // 2.3 write pkg 20 | // 3. compile ts 21 | return findSubmodules(project, {local}) 22 | .then(opts => new Listr([ 23 | /** 24 | * 1. Clean /dist folders 25 | * delete only dist content, but not folders itself 26 | * no not break npm link 27 | */ 28 | { 29 | title: 'Clean dist folders', 30 | task: () => new Listr( 31 | opts.map(opt => ({ 32 | title: `Cleaning ${opt.dist}`, 33 | task: () => del(path.join(opt.dist, '**/*')) 34 | })) 35 | ), 36 | skip: () => !clean 37 | }, 38 | { 39 | title: 'Copy *.md and license files', 40 | task: () => Promise.all(opts.map(opt => 41 | cpy(['*.md', 'LICENSE'], opt.dist) 42 | .then(() => 43 | cpy([ 44 | path.join(opt.src, '*.md'), 45 | path.join(opt.src, 'LICENSE') 46 | ], 47 | opt.dist)) 48 | )) 49 | }, 50 | { 51 | title: 'Build package.json files', 52 | task: () => buildPkgs(opts, {local}) 53 | }, 54 | { 55 | title: 'Copy source files to temporary folder', 56 | task: () => new Listr( 57 | opts.map(opt => ({ 58 | title: `Copying ${opt.pkg.name} source files to ${opt.src}`, 59 | task: () => cpy( 60 | ['**/*.*', '!node_modules'], 61 | // opt.tmp, 62 | path.resolve(opt.tmp), 63 | { 64 | cwd: opt.project, 65 | parents: true, 66 | overwrite: true, 67 | nodir: true 68 | } 69 | ) 70 | })) 71 | ) 72 | }, 73 | /** 74 | * 3. Inline template (.html) and style (.css) files into the component .ts files. 75 | * We do this on the /.tmp folder to avoid editing the original /src files 76 | */ 77 | { 78 | title: 'Inline template and style files into the components', 79 | task: () => new Listr( 80 | opts.map(opt => ({ 81 | title: `Inlining ${opt.pkg.name} templates and styles`, 82 | task: () => inlineResources(opt.tmp) 83 | })) 84 | ) 85 | }, 86 | { 87 | title: 'Build projects', 88 | task: () => new Listr( 89 | opts.map(opt => ({ 90 | title: `Building ${opt.pkg.name} (${opt.src})`, 91 | task: () => watch ? 92 | build(opt.tmp) : 93 | build(opt.tmp).catch(error => { 94 | console.log(error.toString()); 95 | process.exit(1); 96 | }) 97 | })) 98 | ) 99 | }, 100 | { 101 | title: 'Copy assets to dist folder', 102 | skip: () => false, 103 | task: () => new Listr( 104 | opts.map(opt => ({ 105 | title: `Copying ${opt.pkg.name} assets to ${opt.src}`, 106 | task: () => cpy( 107 | ['**/*', '!node_modules', '!**/*.{ts, html}', '!package.json', '!tsconfig.json'], 108 | path.relative(opt.project, opt.dist), 109 | { 110 | cwd: opt.project, 111 | parents: true, 112 | overwrite: true, 113 | nodir: true 114 | } 115 | ) 116 | })) 117 | ) 118 | }, 119 | { 120 | title: 'Bundling umd version', 121 | task: () => new Listr( 122 | opts.map(opt => ({ 123 | title: `Bundling ${opt.pkg.name}`, 124 | task: () => { 125 | if (Array.isArray(main) && main.length) { 126 | return Promise.all(main.map((entryPoint, i) => bundleUmd({ 127 | main: entryPoint, 128 | src: opt.tmp, 129 | dist: opt.dist, 130 | name: i === 0 ? opt.pkg.name : entryPoint.replace('.ts', ''), 131 | tsconfig: opt.tsconfig.path, 132 | minify: false 133 | }))) 134 | } 135 | 136 | return bundleUmd({ 137 | main, 138 | src: opt.tmp, 139 | dist: opt.dist, 140 | name: opt.pkg.name, 141 | tsconfig: opt.tsconfig.path, 142 | minify: false 143 | }) 144 | }, 145 | skip: () => watch || skipBundles 146 | })) 147 | ), 148 | skip: () => watch || skipBundles 149 | }, 150 | { 151 | title: 'Bundling minified umd version', 152 | task: () => new Listr( 153 | opts.map(opt => ({ 154 | title: `Bundling ${opt.pkg.name}`, 155 | task: () => { 156 | if (Array.isArray(main) && main.length) { 157 | return Promise.all(main.map((entryPoint, i) => bundleUmd({ 158 | main: entryPoint, 159 | src: opt.tmp, 160 | dist: opt.dist, 161 | name: i === 0 ? opt.pkg.name : entryPoint.replace('.ts', ''), 162 | tsconfig: opt.tsconfig.path, 163 | minify: true 164 | }))) 165 | } 166 | 167 | return bundleUmd({ 168 | main, 169 | src: opt.tmp, 170 | dist: opt.dist, 171 | name: opt.pkg.name, 172 | tsconfig: opt.tsconfig.path, 173 | minify: true 174 | }) 175 | }, 176 | skip: () => watch || skipBundles 177 | })) 178 | ), 179 | skip: () => watch || skipBundles 180 | }, 181 | { 182 | title: 'Bundling es2015 version', 183 | task: () => new Listr( 184 | opts.map(opt => ({ 185 | title: `Bundling ${opt.pkg.name}`, 186 | task: () => { 187 | const tsconfig = JSON.parse(fs.readFileSync(path.resolve(opt.tmp, 'tsconfig.json'), 'utf8')); 188 | tsconfig.compilerOptions.target = 'es2015'; 189 | tsconfig.compilerOptions.module = 'es2015'; 190 | tsconfig.compilerOptions.outDir = 'dist-es2015'; 191 | fs.writeFileSync(path.resolve(opt.tmp, 'tsconfig.json'), JSON.stringify(tsconfig), 'utf8'); 192 | 193 | if (Array.isArray(main) && main.length) { 194 | return Promise.all(main.map((entryPoint, i) => bundleEs2015({ 195 | input: entryPoint, 196 | dist: opt.dist, 197 | name: i === 0 ? opt.pkg.name : entryPoint.replace('.ts', ''), 198 | tmp: opt.tmp 199 | }))) 200 | } 201 | 202 | return bundleEs2015({ 203 | input: main, 204 | dist: opt.dist, 205 | name: opt.pkg.name, 206 | tmp: opt.tmp 207 | }); 208 | }, 209 | skip: () => !buildEs2015 || watch || skipBundles 210 | })) 211 | ), 212 | skip: () => !buildEs2015 || watch || skipBundles 213 | }, 214 | { 215 | title: 'Clean .tmp folders', 216 | task: () => new Listr( 217 | opts.map(opt => ({ 218 | title: `Cleaning ${opt.tmp}`, 219 | task: () => del(path.join(opt.tmp, '**/*'), {force: true}) 220 | })) 221 | ) 222 | } 223 | ], {renderer: verbose ? 'verbose' : 'default'})); 224 | } 225 | 226 | export function buildTsRun(cli) { 227 | const config = cli.flags.config ? JSON.parse(fs.readFileSync(path.resolve(cli.flags.config), 'utf8')) : {}; 228 | let {src, main, modules, watch, verbose, clean, local, skipBundles, buildEs2015} = config; 229 | const project = cli.flags.project || src; 230 | main = cli.flags.main || main || 'index.ts'; 231 | verbose = cli.flags.verbose || verbose; 232 | watch = cli.flags.watch || watch; 233 | clean = cli.flags.clean || clean; 234 | skipBundles = cli.flags.skipBundles || skipBundles; 235 | buildEs2015 = cli.flags.buildEs2015 || buildEs2015; 236 | const modulePaths = modules ? modules.map(module => module.src) : []; 237 | 238 | if (!project && !modulePaths) { 239 | console.error('Please provide path to your projects source folder, `-p DIR` or specify `src` or `modules` in config'); 240 | process.exit(1); 241 | } 242 | 243 | if (modulePaths.length) { 244 | const commands = modules.map((module: any) => { 245 | return { 246 | title: `Build ${module.src}`, 247 | task: () => buildCommand({project: module.src, verbose, clean, local, main: module.entryPoints, watch, skipBundles, buildEs2015}) 248 | } 249 | }); 250 | 251 | const taskQueue = new Listr(commands, {renderer: verbose ? 'verbose' : 'default'}); 252 | return tasksWatch({project: modulePaths, taskQueue, watch, paths: modulePaths}); 253 | } 254 | 255 | return buildCommand({project, verbose, clean, local, main, watch, skipBundles, buildEs2015}) 256 | .then(taskQueue => tasksWatch({project, taskQueue, watch, paths: null})); 257 | } 258 | -------------------------------------------------------------------------------- /src/commands/dist-tag.command.ts: -------------------------------------------------------------------------------- 1 | const execa = require('execa'); 2 | import Listr = require('listr'); 3 | import { npmDistTag } from '../tasks'; 4 | import { findSubmodules } from '../utils/index'; 5 | 6 | export function npmDistTagRun(cli) { 7 | const {project, verbose, yarn} = cli.flags; 8 | const cmd = cli.input[1]; 9 | const tag = cli.input[2]; 10 | 11 | return findSubmodules(project) 12 | .then((opts: TsmOptions[]) => { 13 | const tasks = new Listr([ 14 | { 15 | title: 'Version all submodules', 16 | task: () => new Listr( 17 | opts.map(opt => ({ 18 | title: `npm dist-tag ${cmd} ${opt.pkg.name}@${opt.pkg.version}`, 19 | task: () => npmDistTag({ 20 | yarn, cmd, tag, 21 | module: opt.pkg.name, 22 | version: opt.pkg.version 23 | }) 24 | })) 25 | ) 26 | } 27 | ], {renderer: verbose ? 'verbose' : 'default'}); 28 | 29 | return tasks.run(); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /src/commands/index.ts: -------------------------------------------------------------------------------- 1 | export {buildCommand, buildTsRun} from './build.command'; 2 | export { npmDistTagRun } from './dist-tag.command'; 3 | export { npmLinkCommand, run as npmLinkRun } from './link.command'; 4 | export { run as npmPublishRun } from './publish.command'; 5 | // test 6 | export { run as npmVersionRun } from './version.command'; 7 | -------------------------------------------------------------------------------- /src/commands/init.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by valorkin on 11/23/16. 3 | */ 4 | -------------------------------------------------------------------------------- /src/commands/link.command.ts: -------------------------------------------------------------------------------- 1 | const Listr = require('listr'); 2 | import { findSubmodules } from '../utils'; 3 | import { npmLink } from '../tasks'; 4 | 5 | // todo: 'npm-link` doesn't track adding new files, 6 | // so watch mode should be added 7 | 8 | export function npmLinkCommand({project, local, deep, verbose, yarn, here}) { 9 | const noDeepLinking = deep === false; 10 | // 1. clean dist folders 11 | // 2.1 merge pkg json 12 | // 2.2 validate pkg (main, module, types) 13 | // 2.3 write pkg 14 | // 3. compile ts 15 | return findSubmodules(project, {local}) 16 | .then((opts: TsmOptions[]) => new Listr([ 17 | { 18 | title: 'Link all submodules', 19 | task: () => { 20 | const linkingTasks = new Listr( 21 | opts.map(opt => ({ 22 | title: `npm link ${opt.pkg.name} (from: ${opt.dist})`, 23 | task: () => npmLink({yarn, cwd: opt.dist}) 24 | })) 25 | ); 26 | 27 | if (noDeepLinking) { 28 | return linkingTasks; 29 | } 30 | 31 | opts.filter(opt => opt.cross.length > 0) 32 | .forEach(opt => opt.cross 33 | .forEach(crossName => linkingTasks.add( 34 | { 35 | title: `npm link ${crossName} to ${opt.pkg.name} (${opt.src})`, 36 | task: () => npmLink({yarn, cwd: opt.dist, module: crossName}) 37 | } 38 | ))); 39 | return linkingTasks; 40 | } 41 | }, 42 | { 43 | title: 'Link submodules to local project', 44 | task: () => new Listr( 45 | opts.map(opt => ({ 46 | title: `npm link ${opt.pkg.name}`, 47 | task: () => npmLink({yarn, module: opt.pkg.name, cwd: '.'}) 48 | })) 49 | ), 50 | skip: () => here !== true 51 | 52 | } 53 | ], {renderer: verbose ? 'verbose' : 'default'})); 54 | } 55 | 56 | export function run(cli) { 57 | const {project, verbose, local, deep, yarn, here} = cli.flags; 58 | return npmLinkCommand({project, verbose, local, deep, yarn, here}) 59 | .then(tasks => tasks.run()); 60 | } 61 | -------------------------------------------------------------------------------- /src/commands/publish.command.ts: -------------------------------------------------------------------------------- 1 | // import { TsmOptions } from '../types'; 2 | import { findSubmodules } from '../utils'; 3 | import { npmPublish, prepublishGitCheck, npmLink, npmInstall } from '../tasks'; 4 | import { npmLinkCommand } from './link.command'; 5 | /** 6 | * Heavily inspired by https://github.com/sindresorhus/np 7 | * */ 8 | const execa = require('execa'); 9 | import Listr = require('listr'); 10 | 11 | export function run(cli, {buildCommand}) { 12 | const { 13 | project, verbose, tag, access, anyBranch, 14 | skipCleanup, skipGitCheck, yarn, yolo, skipPublish 15 | } = cli.flags; 16 | 17 | return findSubmodules(project) 18 | .then((opts: TsmOptions[]) => { 19 | const tasks = new Listr([ 20 | { 21 | title: 'Git checks', 22 | task: () => prepublishGitCheck({anyBranch}), 23 | skip: () => skipGitCheck || yolo 24 | }, 25 | // test command 26 | { 27 | title: 'Installing dependencies', 28 | task: () => new Listr(npmInstall({skipCleanup, yarn})), 29 | skip: () => yolo 30 | }, 31 | { 32 | title: 'Running unit tests tests', 33 | task: () => execa('npm', ['test']), 34 | skip: () => yolo 35 | }, 36 | // e2e command 37 | { 38 | title: 'Build submodules for e2e', 39 | task: () => buildCommand({project, verbose, clean: true, local: true}), 40 | skip: () => yolo 41 | }, 42 | { 43 | title: 'Link submodules', 44 | task: () => npmLinkCommand({project, local: true, deep: true, verbose, yarn, here: true}), 45 | skip: () => yolo 46 | }, 47 | // publish 48 | // set numeric package version before publish 49 | { 50 | title: 'Build submodules for publish', 51 | task: () => buildCommand({project, verbose, clean: true, local: false}) 52 | }, 53 | { 54 | title: 'Publish all submodules', 55 | task: () => new Listr(opts.map(opt => ({ 56 | title: `npm publish (${opt.pkg.name}) (from: ${opt.dist})`, 57 | task: () => npmPublish({yarn, cwd: opt.dist, tag, access}) 58 | })) 59 | ), 60 | skip: () => skipPublish 61 | }, 62 | { 63 | title: 'Pushing tags', 64 | task: () => execa('git', ['push', '--follow-tags']), 65 | skip: () => true 66 | } 67 | ], {renderer: verbose ? 'verbose' : 'default'}); 68 | 69 | return tasks.run(); 70 | }); 71 | } 72 | -------------------------------------------------------------------------------- /src/commands/test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by valorkin on 20/11/2016. 3 | */ 4 | -------------------------------------------------------------------------------- /src/commands/version.command.ts: -------------------------------------------------------------------------------- 1 | // import { TsmOptions } from '../types'; 2 | const execa = require('execa'); 3 | const path = require('path'); 4 | import Listr = require('listr'); 5 | import { findSubmodules } from '../utils/submodules-resolution'; 6 | import { npmVersion } from '../tasks/npm/npm-version.task'; 7 | 8 | export function run(cli) { 9 | const {project, verbose, message, gitTagVersion, yarn, skipPush} = cli.flags; 10 | const noGitTagVersion = gitTagVersion === false; 11 | const version = cli.input[1]; 12 | 13 | if (!version) { 14 | return Promise.reject('Error: please provide version like (patch, major, prerelase, 1.2.3, etc.'); 15 | } 16 | 17 | return findSubmodules(project) 18 | .then((opts: TsmOptions[]) => { 19 | // 1. version all sub modules 20 | // 2. version root package 21 | const tasks = new Listr([ 22 | { 23 | title: 'Version all submodules', 24 | task: () => new Listr( 25 | opts.map(opt => ({ 26 | title: `npm version (${opt.pkg.name}: ${opt.src})`, 27 | task: () => npmVersion({yarn, src: opt.src, version, noGitTagVersion: true}) 28 | })) 29 | ) 30 | }, 31 | { 32 | title: 'git add -A', 33 | task: () => execa.shell('git add -A'), 34 | skip: () => noGitTagVersion 35 | }, 36 | { 37 | title: 'Version root package', 38 | task: () => npmVersion({yarn, src: '.', version, message, noGitTagVersion: true}) 39 | }, 40 | { 41 | title: 'Create version commit', 42 | task: () => { 43 | const pkg = require(path.resolve('package.json')); 44 | return execa.stdout('git', ['commit', '-am', message || `v${pkg.version}`]); 45 | }, 46 | skip: () => noGitTagVersion 47 | }, 48 | { 49 | title: 'Add tag version', 50 | task: () => { 51 | const pkg = require(path.resolve('package.json')); 52 | return execa.stdout('git', ['tag', `v${pkg.version}`]); 53 | }, 54 | skip: () => noGitTagVersion 55 | }, 56 | { 57 | title: 'Push to origin with tags', 58 | task: () => { 59 | const currentBranch = execa.shellSync(`git branch | sed -n '/\* /s///p'`).stdout; 60 | return execa.stdout('git', ['push', 'origin', currentBranch, '--tags']) 61 | }, 62 | skip: () => skipPush 63 | } 64 | ], {renderer: verbose ? 'verbose' : 'default'}); 65 | 66 | return tasks.run(); 67 | }); 68 | } 69 | -------------------------------------------------------------------------------- /src/helpers/inline-resources.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // https://github.com/filipesilva/angular-quickstart-lib/blob/master/inline-resources.js 3 | 'use strict'; 4 | 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const glob = require('glob'); 8 | const sass = require('node-sass'); 9 | const tildeImporter = require('node-sass-tilde-importer'); 10 | 11 | /** 12 | * Simple Promiseify function that takes a Node API and return a version that supports promises. 13 | * We use promises instead of synchronized functions to make the process less I/O bound and 14 | * faster. It also simplifies the code. 15 | */ 16 | function promiseify(fn): Function { 17 | return function () { 18 | const args = [].slice.call(arguments, 0); 19 | return new Promise((resolve, reject) => { 20 | fn.apply(this, args.concat([function (err, value) { 21 | if (err) { 22 | reject(err); 23 | } else { 24 | resolve(value); 25 | } 26 | }])); 27 | }); 28 | }; 29 | } 30 | 31 | const readFile = promiseify(fs.readFile); 32 | const writeFile = promiseify(fs.writeFile); 33 | 34 | /** 35 | * Inline resources in a tsc/ngc compilation. 36 | * @param projectPath {string} Path to the project. 37 | */ 38 | export function inlineResources(projectPath) { 39 | 40 | // Match only TypeScript files in projectPath. 41 | const files = glob.sync('**/*.ts', {cwd: projectPath}); 42 | 43 | // For each file, inline the templates and styles under it and write the new file. 44 | return Promise.all(files.map(filePath => { 45 | const fullFilePath = path.join(projectPath, filePath); 46 | return readFile(fullFilePath, 'utf-8') 47 | .then(content => inlineResourcesFromString(content, url => { 48 | // Resolve the template url. 49 | return path.join(path.dirname(fullFilePath), url); 50 | })) 51 | .then(content => writeFile(fullFilePath, content)) 52 | .catch(err => { 53 | console.error('An error occured: ', err); 54 | }); 55 | })); 56 | } 57 | 58 | /** 59 | * Inline resources from a string content. 60 | * @param content {string} The source file's content. 61 | * @param urlResolver {Function} A resolver that takes a URL and return a path. 62 | * @returns {string} The content with resources inlined. 63 | */ 64 | export function inlineResourcesFromString(content, urlResolver) { 65 | // Curry through the inlining functions. 66 | return [ 67 | inlineTemplate, 68 | inlineStyle, 69 | removeModuleId 70 | ].reduce((content, fn) => fn(content, urlResolver), content); 71 | } 72 | 73 | /** 74 | * Inline the templates for a source file. Simply search for instances of `templateUrl: ...` and 75 | * replace with `template: ...` (with the content of the file included). 76 | * @param content {string} The source file's content. 77 | * @param urlResolver {Function} A resolver that takes a URL and return a path. 78 | * @return {string} The content with all templates inlined. 79 | */ 80 | function inlineTemplate(content, urlResolver) { 81 | return content.replace(/templateUrl:\s*'([^']+?\.html)'/g, function (m, templateUrl) { 82 | const templateFile = urlResolver(templateUrl); 83 | const templateContent = fs.readFileSync(templateFile, 'utf-8'); 84 | const shortenedTemplate = templateContent 85 | .replace(/([\n\r]\s*)+/gm, ' ') 86 | .replace(/"/g, '\\"'); 87 | return `template: "${shortenedTemplate}"`; 88 | }); 89 | } 90 | 91 | 92 | /** 93 | * Inline the styles for a source file. Simply search for instances of `styleUrls: [...]` and 94 | * replace with `styles: [...]` (with the content of the file included). 95 | * @param urlResolver {Function} A resolver that takes a URL and return a path. 96 | * @param content {string} The source file's content. 97 | * @return {string} The content with all styles inlined. 98 | */ 99 | function inlineStyle(content, urlResolver) { 100 | return content.replace(/styleUrls\s*:\s*(\[[\s\S]*?\])/gm, function (m, styleUrls) { 101 | const urls = eval(styleUrls); 102 | return 'styles: [' 103 | + urls.map(styleUrl => { 104 | const styleFile = urlResolver(styleUrl); 105 | const originContent = fs.readFileSync(styleFile, 'utf-8'); 106 | const styleContent = styleFile.endsWith('.scss') ? buildSass(originContent, styleFile) : originContent; 107 | const shortenedStyle = styleContent 108 | .replace(/([\n\r]\s*)+/gm, ' ') 109 | .replace(/\\/g, '\\\\') 110 | .replace(/"/g, '\\"'); 111 | return `"${shortenedStyle}"`; 112 | }) 113 | .join(',\n') 114 | + ']'; 115 | }); 116 | } 117 | 118 | /** 119 | * build sass content to css 120 | * @param content {string} the css content 121 | * @param sourceFile {string} the scss file sourceFile 122 | * @return {string} the generated css, empty string if error occured 123 | */ 124 | function buildSass(content, sourceFile) { 125 | try { 126 | const result = sass.renderSync({ 127 | data: content, 128 | file: sourceFile, 129 | importer: tildeImporter 130 | }); 131 | return result.css.toString() 132 | } catch (e) { 133 | console.error('\x1b[41m'); 134 | console.error('at ' + sourceFile + ':' + e.line + ":" + e.column); 135 | console.error(e.formatted); 136 | console.error('\x1b[0m'); 137 | return ""; 138 | } 139 | } 140 | 141 | /** 142 | * Remove every mention of `moduleId: module.id`. 143 | * @param content {string} The source file's content. 144 | * @returns {string} The content with all moduleId: mentions removed. 145 | */ 146 | function removeModuleId(content) { 147 | return content.replace(/\s*moduleId:\s*module\.id\s*,?\s*/gm, ''); 148 | } 149 | -------------------------------------------------------------------------------- /src/lib/ngm.ts: -------------------------------------------------------------------------------- 1 | import { buildTsRun, buildCommand, npmLinkRun, npmVersionRun, npmPublishRun, npmDistTagRun } from '../commands'; 2 | 3 | // command - string, cli.inputs[0] 4 | // cli - meow object 5 | export function main(command, cli) { 6 | // todo: can I generate this? 7 | return run(command, cli); 8 | } 9 | 10 | function run(command, cli) { 11 | switch (command) { 12 | case 'build': return buildTsRun(cli); 13 | case 'link': return npmLinkRun(cli); 14 | case 'version': return npmVersionRun(cli); 15 | case 'dist-tag': return npmDistTagRun(cli); 16 | case 'publish': return npmPublishRun(cli, {buildCommand}); 17 | default: throw new Error(`You are using unknown command '${command}', 18 | please refer to help for a list of available commands`) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/models/rollup.globals.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | // Angular dependencies 3 | '@angular/animations': 'ng.animations', 4 | '@angular/core': 'ng.core', 5 | '@angular/common': 'ng.common', 6 | '@angular/common/http': 'ng.common.http', 7 | '@angular/forms': 'ng.forms', 8 | '@angular/http': 'ng.http', 9 | '@angular/router': 'ng.router', 10 | '@angular/platform-browser': 'ng.platformBrowser', 11 | '@angular/platform-browser-dynamic': 'ng.platformBrowserDynamic', 12 | '@angular/platform-browser/animations': 'ng.platformBrowser.animations', 13 | // RxJS dependencies 14 | 'rxjs/AnonymousSubject': 'Rx', 15 | 'rxjs/AsyncSubject': 'Rx', 16 | 'rxjs/BehaviorSubject': 'Rx', 17 | 'rxjs/Notifiction': 'Rx', 18 | 'rxjs/ObservableInput': 'Rx', 19 | 'rxjs/Observable': 'Rx', 20 | 'rxjs/Observer': 'Rx', 21 | 'rxjs/ReplaySubject': 'Rx', 22 | 'rxjs/Scheduler': 'Rx', 23 | 'rxjs/Subject': 'Rx', 24 | 'rxjs/SubjectSubscriber': 'Rx', 25 | 'rxjs/SubscribableOrPromise': 'Rx', 26 | 'rxjs/Subscriber': 'Rx', 27 | 'rxjs/Subscription': 'Rx', 28 | 'rxjs/TeardownLogic': 'Rx', 29 | 'rxjs/add/observable/bindCallback': 'Rx.Observable', 30 | 'rxjs/add/observable/bindNodeCallback': 'Rx.Observable', 31 | 'rxjs/add/observable/combineLatest': 'Rx.Observable', 32 | 'rxjs/add/observable/concat': 'Rx.Observable', 33 | 'rxjs/add/observable/create': 'Rx.Observable', 34 | 'rxjs/add/observable/defer': 'Rx.Observable', 35 | 'rxjs/add/observable/empty': 'Rx.Observable', 36 | 'rxjs/add/observable/forkJoin': 'Rx.Observable', 37 | 'rxjs/add/observable/from': 'Rx.Observable', 38 | 'rxjs/add/observable/fromEvent': 'Rx.Observable', 39 | 'rxjs/add/observable/fromEventPattern': 'Rx.Observable', 40 | 'rxjs/add/observable/fromPromise': 'Rx.Observable', 41 | 'rxjs/add/observable/interval': 'Rx.Observable', 42 | 'rxjs/add/observable/merge': 'Rx.Observable', 43 | 'rxjs/add/observable/never': 'Rx.Observable', 44 | 'rxjs/add/observable/of': 'Rx.Observable', 45 | 'rxjs/add/observable/range': 'Rx.Observable', 46 | 'rxjs/add/observable/throw': 'Rx.Observable', 47 | 'rxjs/add/observable/timer': 'Rx.Observable', 48 | 'rxjs/add/observable/webSocket': 'Rx.Observable', 49 | 'rxjs/add/observable/zip': 'Rx.Observable', 50 | 'rxjs/add/observable': 'Rx.Observable', 51 | 'rxjs/add/operator/audit': 'Rx.Observable.prototype', 52 | 'rxjs/add/operator/auditTime': 'Rx.Observable.prototype', 53 | 'rxjs/add/operator/buffer': 'Rx.Observable.prototype', 54 | 'rxjs/add/operator/bufferCount': 'Rx.Observable.prototype', 55 | 'rxjs/add/operator/bufferTime': 'Rx.Observable.prototype', 56 | 'rxjs/add/operator/bufferToggle': 'Rx.Observable.prototype', 57 | 'rxjs/add/operator/bufferWhen': 'Rx.Observable.prototype', 58 | 'rxjs/add/operator/catch': 'Rx.Observable.prototype', 59 | 'rxjs/add/operator/combineAll': 'Rx.Observable.prototype', 60 | 'rxjs/add/operator/combineLatest': 'Rx.Observable.prototype', 61 | 'rxjs/add/operator/concat': 'Rx.Observable.prototype', 62 | 'rxjs/add/operator/concatAll': 'Rx.Observable.prototype', 63 | 'rxjs/add/operator/concatMap': 'Rx.Observable.prototype', 64 | 'rxjs/add/operator/concatMapTo': 'Rx.Observable.prototype', 65 | 'rxjs/add/operator/count': 'Rx.Observable.prototype', 66 | 'rxjs/add/operator/debounce': 'Rx.Observable.prototype', 67 | 'rxjs/add/operator/debounceTime': 'Rx.Observable.prototype', 68 | 'rxjs/add/operator/defaultIfEmpty': 'Rx.Observable.prototype', 69 | 'rxjs/add/operator/delay': 'Rx.Observable.prototype', 70 | 'rxjs/add/operator/delayWhen': 'Rx.Observable.prototype', 71 | 'rxjs/add/operator/dematerialize': 'Rx.Observable.prototype', 72 | 'rxjs/add/operator/distinct': 'Rx.Observable.prototype', 73 | 'rxjs/add/operator/distinctUntilChanged': 'Rx.Observable.prototype', 74 | 'rxjs/add/operator/distinctUntilKeyChanged': 'Rx.Observable.prototype', 75 | 'rxjs/add/operator/do': 'Rx.Observable.prototype', 76 | 'rxjs/add/operator/elementAt': 'Rx.Observable.prototype', 77 | 'rxjs/add/operator/every': 'Rx.Observable.prototype', 78 | 'rxjs/add/operator/exhaust': 'Rx.Observable.prototype', 79 | 'rxjs/add/operator/exhaustMap': 'Rx.Observable.prototype', 80 | 'rxjs/add/operator/expand': 'Rx.Observable.prototype', 81 | 'rxjs/add/operator/filter': 'Rx.Observable.prototype', 82 | 'rxjs/add/operator/finally': 'Rx.Observable.prototype', 83 | 'rxjs/add/operator/find': 'Rx.Observable.prototype', 84 | 'rxjs/add/operator/findIndex': 'Rx.Observable.prototype', 85 | 'rxjs/add/operator/first': 'Rx.Observable.prototype', 86 | 'rxjs/add/operator/forEach': 'Rx.Observable.prototype', 87 | 'rxjs/add/operator/groupBy': 'Rx.Observable.prototype', 88 | 'rxjs/add/operator/ignoreElements': 'Rx.Observable.prototype', 89 | 'rxjs/add/operator/isEmpty': 'Rx.Observable.prototype', 90 | 'rxjs/add/operator/last': 'Rx.Observable.prototype', 91 | 'rxjs/add/operator/let': 'Rx.Observable.prototype', 92 | 'rxjs/add/operator/letProto': 'Rx.Observable.prototype', 93 | 'rxjs/add/operator/lift': 'Rx.Observable.prototype', 94 | 'rxjs/add/operator/map': 'Rx.Observable.prototype', 95 | 'rxjs/add/operator/mapTo': 'Rx.Observable.prototype', 96 | 'rxjs/add/operator/materialize': 'Rx.Observable.prototype', 97 | 'rxjs/add/operator/max': 'Rx.Observable.prototype', 98 | 'rxjs/add/operator/merge': 'Rx.Observable.prototype', 99 | 'rxjs/add/operator/mergeAll': 'Rx.Observable.prototype', 100 | 'rxjs/add/operator/mergeMap': 'Rx.Observable.prototype', 101 | 'rxjs/add/operator/mergeMapTo': 'Rx.Observable.prototype', 102 | 'rxjs/add/operator/mergeScan': 'Rx.Observable.prototype', 103 | 'rxjs/add/operator/min': 'Rx.Observable.prototype', 104 | 'rxjs/add/operator/multicast': 'Rx.Observable.prototype', 105 | 'rxjs/add/operator/observeOn': 'Rx.Observable.prototype', 106 | 'rxjs/add/operator/pairwise': 'Rx.Observable.prototype', 107 | 'rxjs/add/operator/partition': 'Rx.Observable.prototype', 108 | 'rxjs/add/operator/pluck': 'Rx.Observable.prototype', 109 | 'rxjs/add/operator/publish': 'Rx.Observable.prototype', 110 | 'rxjs/add/operator/publishBehavior':'Rx.Observable.prototype', 111 | 'rxjs/add/operator/publishLast': 'Rx.Observable.prototype', 112 | 'rxjs/add/operator/publishReplay': 'Rx.Observable.prototype', 113 | 'rxjs/add/operator/race': 'Rx.Observable.prototype', 114 | 'rxjs/add/operator/reduce': 'Rx.Observable.prototype', 115 | 'rxjs/add/operator/repeat': 'Rx.Observable.prototype', 116 | 'rxjs/add/operator/repeatWhen': 'Rx.Observable.prototype', 117 | 'rxjs/add/operator/retry': 'Rx.Observable.prototype', 118 | 'rxjs/add/operator/retryWhen': 'Rx.Observable.prototype', 119 | 'rxjs/add/operator/sample': 'Rx.Observable.prototype', 120 | 'rxjs/add/operator/sampleTime': 'Rx.Observable.prototype', 121 | 'rxjs/add/operator/scan': 'Rx.Observable.prototype', 122 | 'rxjs/add/operator/sequenceEqual': 'Rx.Observable.prototype', 123 | 'rxjs/add/operator/share': 'Rx.Observable.prototype', 124 | 'rxjs/add/operator/single': 'Rx.Observable.prototype', 125 | 'rxjs/add/operator/skip': 'Rx.Observable.prototype', 126 | 'rxjs/add/operator/skipUntil': 'Rx.Observable.prototype', 127 | 'rxjs/add/operator/skipWhile': 'Rx.Observable.prototype', 128 | 'rxjs/add/operator/startWith': 'Rx.Observable.prototype', 129 | 'rxjs/add/operator/subscribeOn': 'Rx.Observable.prototype', 130 | 'rxjs/add/operator/switch': 'Rx.Observable.prototype', 131 | 'rxjs/add/operator/switchMap': 'Rx.Observable.prototype', 132 | 'rxjs/add/operator/switchMapTo': 'Rx.Observable.prototype', 133 | 'rxjs/add/operator/take': 'Rx.Observable.prototype', 134 | 'rxjs/add/operator/takeLast': 'Rx.Observable.prototype', 135 | 'rxjs/add/operator/takeUntil': 'Rx.Observable.prototype', 136 | 'rxjs/add/operator/takeWhile': 'Rx.Observable.prototype', 137 | 'rxjs/add/operator/throttle': 'Rx.Observable.prototype', 138 | 'rxjs/add/operator/throttleTime': 'Rx.Observable.prototype', 139 | 'rxjs/add/operator/timeInterval': 'Rx.Observable.prototype', 140 | 'rxjs/add/operator/timeout': 'Rx.Observable.prototype', 141 | 'rxjs/add/operator/timeoutWith': 'Rx.Observable.prototype', 142 | 'rxjs/add/operator/timestamp': 'Rx.Observable.prototype', 143 | 'rxjs/add/operator/toArray': 'Rx.Observable.prototype', 144 | 'rxjs/add/operator/toPromise': 'Rx.Observable.prototype', 145 | 'rxjs/add/operator/window': 'Rx.Observable.prototype', 146 | 'rxjs/add/operator/windowCount': 'Rx.Observable.prototype', 147 | 'rxjs/add/operator/windowToggle': 'Rx.Observable.prototype', 148 | 'rxjs/add/operator/windowWhen': 'Rx.Observable.prototype', 149 | 'rxjs/add/operator/withLatestFrom': 'Rx.Observable.prototype', 150 | 'rxjs/add/operator/zipAll': 'Rx.Observable.prototype', 151 | 'rxjs/add/operator/zipProto': 'Rx.Observable.prototype', 152 | 'rxjs/add/operator': 'Rx.Observable.prototype', 153 | 'rxjs/observable/bindCallback': 'Rx.Observable', 154 | 'rxjs/observable/bindNodeCallback': 'Rx.Observable', 155 | 'rxjs/observable/combineLatest': 'Rx.Observable', 156 | 'rxjs/observable/concat': 'Rx.Observable', 157 | 'rxjs/observable/defer': 'Rx.Observable', 158 | 'rxjs/observable/empty': 'Rx.Observable', 159 | 'rxjs/observable/forkJoin': 'Rx.Observable', 160 | 'rxjs/observable/from': 'Rx.Observable', 161 | 'rxjs/observable/fromEvent': 'Rx.Observable', 162 | 'rxjs/observable/fromEventPattern': 'Rx.Observable', 163 | 'rxjs/observable/fromPromise': 'Rx.Observable', 164 | 'rxjs/observable/if': 'Rx.Observable', 165 | 'rxjs/observable/interval': 'Rx.Observable', 166 | 'rxjs/observable/merge': 'Rx.Observable', 167 | 'rxjs/observable/never': 'Rx.Observable', 168 | 'rxjs/observable/of': 'Rx.Observable', 169 | 'rxjs/observable/pairs': 'Rx.Observable', 170 | 'rxjs/observable/range': 'Rx.Observable', 171 | 'rxjs/observable/throw': 'Rx.Observable', 172 | 'rxjs/observable/timer': 'Rx.Observable', 173 | 'rxjs/observable/using': 'Rx.Observable', 174 | 'rxjs/observable/zip': 'Rx.Observable', 175 | 'rxjs/observable': 'Rx.Observable', 176 | 'rxjs/operator/audit': 'Rx.Observable.prototype', 177 | 'rxjs/operator/auditTime': 'Rx.Observable.prototype', 178 | 'rxjs/operator/buffer': 'Rx.Observable.prototype', 179 | 'rxjs/operator/bufferCount': 'Rx.Observable.prototype', 180 | 'rxjs/operator/bufferTime': 'Rx.Observable.prototype', 181 | 'rxjs/operator/bufferToggle': 'Rx.Observable.prototype', 182 | 'rxjs/operator/bufferWhen': 'Rx.Observable.prototype', 183 | 'rxjs/operator/catch': 'Rx.Observable.prototype', 184 | 'rxjs/operator/combineAll': 'Rx.Observable.prototype', 185 | 'rxjs/operator/combineLatest': 'Rx.Observable.prototype', 186 | 'rxjs/operator/concat': 'Rx.Observable.prototype', 187 | 'rxjs/operator/concatAll': 'Rx.Observable.prototype', 188 | 'rxjs/operator/concatMap': 'Rx.Observable.prototype', 189 | 'rxjs/operator/concatMapTo': 'Rx.Observable.prototype', 190 | 'rxjs/operator/count': 'Rx.Observable.prototype', 191 | 'rxjs/operator/debounce': 'Rx.Observable.prototype', 192 | 'rxjs/operator/debounceTime': 'Rx.Observable.prototype', 193 | 'rxjs/operator/defaultIfEmpty': 'Rx.Observable.prototype', 194 | 'rxjs/operator/delay': 'Rx.Observable.prototype', 195 | 'rxjs/operator/delayWhen': 'Rx.Observable.prototype', 196 | 'rxjs/operator/dematerialize': 'Rx.Observable.prototype', 197 | 'rxjs/operator/distinct': 'Rx.Observable.prototype', 198 | 'rxjs/operator/distinctUntilChanged': 'Rx.Observable.prototype', 199 | 'rxjs/operator/distinctUntilKeyChanged': 'Rx.Observable.prototype', 200 | 'rxjs/operator/do': 'Rx.Observable.prototype', 201 | 'rxjs/operator/elementAt': 'Rx.Observable.prototype', 202 | 'rxjs/operator/every': 'Rx.Observable.prototype', 203 | 'rxjs/operator/exhaust': 'Rx.Observable.prototype', 204 | 'rxjs/operator/exhaustMap': 'Rx.Observable.prototype', 205 | 'rxjs/operator/expand': 'Rx.Observable.prototype', 206 | 'rxjs/operator/filter': 'Rx.Observable.prototype', 207 | 'rxjs/operator/finally': 'Rx.Observable.prototype', 208 | 'rxjs/operator/find': 'Rx.Observable.prototype', 209 | 'rxjs/operator/findIndex': 'Rx.Observable.prototype', 210 | 'rxjs/operator/first': 'Rx.Observable.prototype', 211 | 'rxjs/operator/forEach': 'Rx.Observable.prototype', 212 | 'rxjs/operator/groupBy': 'Rx.Observable.prototype', 213 | 'rxjs/operator/ignoreElements': 'Rx.Observable.prototype', 214 | 'rxjs/operator/isEmpty': 'Rx.Observable.prototype', 215 | 'rxjs/operator/last': 'Rx.Observable.prototype', 216 | 'rxjs/operator/let': 'Rx.Observable.prototype', 217 | 'rxjs/operator/letProto': 'Rx.Observable.prototype', 218 | 'rxjs/operator/lift': 'Rx.Observable.prototype', 219 | 'rxjs/operator/map': 'Rx.Observable.prototype', 220 | 'rxjs/operator/mapTo': 'Rx.Observable.prototype', 221 | 'rxjs/operator/materialize': 'Rx.Observable.prototype', 222 | 'rxjs/operator/max': 'Rx.Observable.prototype', 223 | 'rxjs/operator/merge': 'Rx.Observable.prototype', 224 | 'rxjs/operator/mergeAll': 'Rx.Observable.prototype', 225 | 'rxjs/operator/mergeMap': 'Rx.Observable.prototype', 226 | 'rxjs/operator/mergeMapTo': 'Rx.Observable.prototype', 227 | 'rxjs/operator/mergeScan': 'Rx.Observable.prototype', 228 | 'rxjs/operator/min': 'Rx.Observable.prototype', 229 | 'rxjs/operator/multicast': 'Rx.Observable.prototype', 230 | 'rxjs/operator/observeOn': 'Rx.Observable.prototype', 231 | 'rxjs/operator/pairwise': 'Rx.Observable.prototype', 232 | 'rxjs/operator/partition': 'Rx.Observable.prototype', 233 | 'rxjs/operator/pluck': 'Rx.Observable.prototype', 234 | 'rxjs/operator/publish': 'Rx.Observable.prototype', 235 | 'rxjs/operator/publishBehavior':'Rx.Observable.prototype', 236 | 'rxjs/operator/publishLast': 'Rx.Observable.prototype', 237 | 'rxjs/operator/publishReplay': 'Rx.Observable.prototype', 238 | 'rxjs/operator/race': 'Rx.Observable.prototype', 239 | 'rxjs/operator/reduce': 'Rx.Observable.prototype', 240 | 'rxjs/operator/repeat': 'Rx.Observable.prototype', 241 | 'rxjs/operator/repeatWhen': 'Rx.Observable.prototype', 242 | 'rxjs/operator/retry': 'Rx.Observable.prototype', 243 | 'rxjs/operator/retryWhen': 'Rx.Observable.prototype', 244 | 'rxjs/operator/sample': 'Rx.Observable.prototype', 245 | 'rxjs/operator/sampleTime': 'Rx.Observable.prototype', 246 | 'rxjs/operator/scan': 'Rx.Observable.prototype', 247 | 'rxjs/operator/sequenceEqual': 'Rx.Observable.prototype', 248 | 'rxjs/operator/share': 'Rx.Observable.prototype', 249 | 'rxjs/operator/single': 'Rx.Observable.prototype', 250 | 'rxjs/operator/skip': 'Rx.Observable.prototype', 251 | 'rxjs/operator/skipUntil': 'Rx.Observable.prototype', 252 | 'rxjs/operator/skipWhile': 'Rx.Observable.prototype', 253 | 'rxjs/operator/startWith': 'Rx.Observable.prototype', 254 | 'rxjs/operator/subscribeOn': 'Rx.Observable.prototype', 255 | 'rxjs/operator/switch': 'Rx.Observable.prototype', 256 | 'rxjs/operator/switchMap': 'Rx.Observable.prototype', 257 | 'rxjs/operator/switchMapTo': 'Rx.Observable.prototype', 258 | 'rxjs/operator/take': 'Rx.Observable.prototype', 259 | 'rxjs/operator/takeLast': 'Rx.Observable.prototype', 260 | 'rxjs/operator/takeUntil': 'Rx.Observable.prototype', 261 | 'rxjs/operator/takeWhile': 'Rx.Observable.prototype', 262 | 'rxjs/operator/throttle': 'Rx.Observable.prototype', 263 | 'rxjs/operator/throttleTime': 'Rx.Observable.prototype', 264 | 'rxjs/operator/timeInterval': 'Rx.Observable.prototype', 265 | 'rxjs/operator/timeout': 'Rx.Observable.prototype', 266 | 'rxjs/operator/timeoutWith': 'Rx.Observable.prototype', 267 | 'rxjs/operator/timestamp': 'Rx.Observable.prototype', 268 | 'rxjs/operator/toArray': 'Rx.Observable.prototype', 269 | 'rxjs/operator/toPromise': 'Rx.Observable.prototype', 270 | 'rxjs/operator/window': 'Rx.Observable.prototype', 271 | 'rxjs/operator/windowCount': 'Rx.Observable.prototype', 272 | 'rxjs/operator/windowToggle': 'Rx.Observable.prototype', 273 | 'rxjs/operator/windowWhen': 'Rx.Observable.prototype', 274 | 'rxjs/operator/withLatestFrom': 'Rx.Observable.prototype', 275 | 'rxjs/operator/zipAll': 'Rx.Observable.prototype', 276 | 'rxjs/operator/zipProto': 'Rx.Observable.prototype', 277 | 'rxjs/operator': 'Rx.Observable.prototype', 278 | 'rxjs/operators/audit': 'Rx.Observable.prototype', 279 | 'rxjs/operators/auditTime': 'Rx.Observable.prototype', 280 | 'rxjs/operators/buffer': 'Rx.Observable.prototype', 281 | 'rxjs/operators/bufferCount': 'Rx.Observable.prototype', 282 | 'rxjs/operators/bufferTime': 'Rx.Observable.prototype', 283 | 'rxjs/operators/bufferToggle': 'Rx.Observable.prototype', 284 | 'rxjs/operators/bufferWhen': 'Rx.Observable.prototype', 285 | 'rxjs/operators/catch': 'Rx.Observable.prototype', 286 | 'rxjs/operators/combineAll': 'Rx.Observable.prototype', 287 | 'rxjs/operators/combineLatest': 'Rx.Observable.prototype', 288 | 'rxjs/operators/concat': 'Rx.Observable.prototype', 289 | 'rxjs/operators/concatAll': 'Rx.Observable.prototype', 290 | 'rxjs/operators/concatMap': 'Rx.Observable.prototype', 291 | 'rxjs/operators/concatMapTo': 'Rx.Observable.prototype', 292 | 'rxjs/operators/count': 'Rx.Observable.prototype', 293 | 'rxjs/operators/debounce': 'Rx.Observable.prototype', 294 | 'rxjs/operators/debounceTime': 'Rx.Observable.prototype', 295 | 'rxjs/operators/defaultIfEmpty': 'Rx.Observable.prototype', 296 | 'rxjs/operators/delay': 'Rx.Observable.prototype', 297 | 'rxjs/operators/delayWhen': 'Rx.Observable.prototype', 298 | 'rxjs/operators/dematerialize': 'Rx.Observable.prototype', 299 | 'rxjs/operators/distinct': 'Rx.Observable.prototype', 300 | 'rxjs/operators/distinctUntilChanged': 'Rx.Observable.prototype', 301 | 'rxjs/operators/distinctUntilKeyChanged': 'Rx.Observable.prototype', 302 | 'rxjs/operators/do': 'Rx.Observable.prototype', 303 | 'rxjs/operators/elementAt': 'Rx.Observable.prototype', 304 | 'rxjs/operators/every': 'Rx.Observable.prototype', 305 | 'rxjs/operators/exhaust': 'Rx.Observable.prototype', 306 | 'rxjs/operators/exhaustMap': 'Rx.Observable.prototype', 307 | 'rxjs/operators/expand': 'Rx.Observable.prototype', 308 | 'rxjs/operators/filter': 'Rx.Observable.prototype', 309 | 'rxjs/operators/finally': 'Rx.Observable.prototype', 310 | 'rxjs/operators/find': 'Rx.Observable.prototype', 311 | 'rxjs/operators/findIndex': 'Rx.Observable.prototype', 312 | 'rxjs/operators/first': 'Rx.Observable.prototype', 313 | 'rxjs/operators/forEach': 'Rx.Observable.prototype', 314 | 'rxjs/operators/groupBy': 'Rx.Observable.prototype', 315 | 'rxjs/operators/ignoreElements': 'Rx.Observable.prototype', 316 | 'rxjs/operators/isEmpty': 'Rx.Observable.prototype', 317 | 'rxjs/operators/last': 'Rx.Observable.prototype', 318 | 'rxjs/operators/let': 'Rx.Observable.prototype', 319 | 'rxjs/operators/letProto': 'Rx.Observable.prototype', 320 | 'rxjs/operators/lift': 'Rx.Observable.prototype', 321 | 'rxjs/operators/map': 'Rx.Observable.prototype', 322 | 'rxjs/operators/mapTo': 'Rx.Observable.prototype', 323 | 'rxjs/operators/materialize': 'Rx.Observable.prototype', 324 | 'rxjs/operators/max': 'Rx.Observable.prototype', 325 | 'rxjs/operators/merge': 'Rx.Observable.prototype', 326 | 'rxjs/operators/mergeAll': 'Rx.Observable.prototype', 327 | 'rxjs/operators/mergeMap': 'Rx.Observable.prototype', 328 | 'rxjs/operators/mergeMapTo': 'Rx.Observable.prototype', 329 | 'rxjs/operators/mergeScan': 'Rx.Observable.prototype', 330 | 'rxjs/operators/min': 'Rx.Observable.prototype', 331 | 'rxjs/operators/multicast': 'Rx.Observable.prototype', 332 | 'rxjs/operators/observeOn': 'Rx.Observable.prototype', 333 | 'rxjs/operators/pairwise': 'Rx.Observable.prototype', 334 | 'rxjs/operators/partition': 'Rx.Observable.prototype', 335 | 'rxjs/operators/pluck': 'Rx.Observable.prototype', 336 | 'rxjs/operators/publish': 'Rx.Observable.prototype', 337 | 'rxjs/operators/publishBehavior':'Rx.Observable.prototype', 338 | 'rxjs/operators/publishLast': 'Rx.Observable.prototype', 339 | 'rxjs/operators/publishReplay': 'Rx.Observable.prototype', 340 | 'rxjs/operators/race': 'Rx.Observable.prototype', 341 | 'rxjs/operators/reduce': 'Rx.Observable.prototype', 342 | 'rxjs/operators/repeat': 'Rx.Observable.prototype', 343 | 'rxjs/operators/repeatWhen': 'Rx.Observable.prototype', 344 | 'rxjs/operators/retry': 'Rx.Observable.prototype', 345 | 'rxjs/operators/retryWhen': 'Rx.Observable.prototype', 346 | 'rxjs/operators/sample': 'Rx.Observable.prototype', 347 | 'rxjs/operators/sampleTime': 'Rx.Observable.prototype', 348 | 'rxjs/operators/scan': 'Rx.Observable.prototype', 349 | 'rxjs/operators/sequenceEqual': 'Rx.Observable.prototype', 350 | 'rxjs/operators/share': 'Rx.Observable.prototype', 351 | 'rxjs/operators/single': 'Rx.Observable.prototype', 352 | 'rxjs/operators/skip': 'Rx.Observable.prototype', 353 | 'rxjs/operators/skipUntil': 'Rx.Observable.prototype', 354 | 'rxjs/operators/skipWhile': 'Rx.Observable.prototype', 355 | 'rxjs/operators/startWith': 'Rx.Observable.prototype', 356 | 'rxjs/operators/subscribeOn': 'Rx.Observable.prototype', 357 | 'rxjs/operators/switch': 'Rx.Observable.prototype', 358 | 'rxjs/operators/switchMap': 'Rx.Observable.prototype', 359 | 'rxjs/operators/switchMapTo': 'Rx.Observable.prototype', 360 | 'rxjs/operators/take': 'Rx.Observable.prototype', 361 | 'rxjs/operators/takeLast': 'Rx.Observable.prototype', 362 | 'rxjs/operators/takeUntil': 'Rx.Observable.prototype', 363 | 'rxjs/operators/takeWhile': 'Rx.Observable.prototype', 364 | 'rxjs/operators/throttle': 'Rx.Observable.prototype', 365 | 'rxjs/operators/throttleTime': 'Rx.Observable.prototype', 366 | 'rxjs/operators/timeInterval': 'Rx.Observable.prototype', 367 | 'rxjs/operators/timeout': 'Rx.Observable.prototype', 368 | 'rxjs/operators/timeoutWith': 'Rx.Observable.prototype', 369 | 'rxjs/operators/timestamp': 'Rx.Observable.prototype', 370 | 'rxjs/operators/toArray': 'Rx.Observable.prototype', 371 | 'rxjs/operators/toPromise': 'Rx.Observable.prototype', 372 | 'rxjs/operators/window': 'Rx.Observable.prototype', 373 | 'rxjs/operators/windowCount': 'Rx.Observable.prototype', 374 | 'rxjs/operators/windowToggle': 'Rx.Observable.prototype', 375 | 'rxjs/operators/windowWhen': 'Rx.Observable.prototype', 376 | 'rxjs/operators/withLatestFrom': 'Rx.Observable.prototype', 377 | 'rxjs/operators/zipAll': 'Rx.Observable.prototype', 378 | 'rxjs/operators/zipProto': 'Rx.Observable.prototype', 379 | 'rxjs/operators': 'Rx.Observable.prototype', 380 | 'rxjs/symbol/iterator': 'Rx.Symbol', 381 | 'rxjs/symbol/observable': 'Rx.Symbol', 382 | 'rxjs/symbol/rxSubscriber': 'Rx.Symbol', 383 | 'rxjs/symbol': 'Rx.Symbol', 384 | 'rxjs/scheduler/Action': 'Rx.Scheduler', 385 | 'rxjs/scheduler/animationFrame': 'Rx.Scheduler', 386 | 'rxjs/scheduler/AnimationFrameAction': 'Rx.Scheduler', 387 | 'rxjs/scheduler/asap': 'Rx.Scheduler', 388 | 'rxjs/scheduler/AsapAction': 'Rx.Scheduler', 389 | 'rxjs/scheduler/async': 'Rx.Scheduler', 390 | 'rxjs/scheduler/AsyncAction': 'Rx.Scheduler', 391 | 'rxjs/scheduler/AsyncScheduler': 'Rx.Scheduler', 392 | 'rxjs/scheduler/queue': 'Rx.Scheduler', 393 | 'rxjs/scheduler/QueueAction': 'Rx.Scheduler', 394 | 'rxjs/scheduler/QueueScheduler': 'Rx.Scheduler', 395 | 'rxjs/scheduler/VirtualTimeScheduler': 'Rx.Scheduler', 396 | 'rxjs/scheduler': 'Rx.Scheduler' 397 | }; 398 | -------------------------------------------------------------------------------- /src/models/webpack-umd.config.ts: -------------------------------------------------------------------------------- 1 | import webpack = require('webpack'); 2 | // const TsConfigPathsPlugin = require('awesome-typescript-loader').TsConfigPathsPlugin; 3 | 4 | export function getWebpackConfig(config) { 5 | return { 6 | mode: 'production', 7 | devtool: 'source-map', 8 | 9 | resolve: { 10 | extensions: ['.ts', '.js'] 11 | }, 12 | 13 | entry: config.entry, 14 | 15 | output: { 16 | path: config.output, 17 | publicPath: '/', 18 | filename: `${config.name}.js`, 19 | libraryTarget: 'umd', 20 | library: config.name 21 | }, 22 | 23 | // require those dependencies but don't bundle them 24 | externals: [/^\@angular\//, /^rxjs\//], 25 | 26 | module: { 27 | rules: [ 28 | { 29 | test: /\.ts$/, 30 | loader: `ts-loader`, 31 | exclude: [/\.e2e\.ts$/], 32 | options: { 33 | transpileOnly: true, 34 | configFile: config.tsconfig, 35 | compilerOptions: { 36 | declaration: false, 37 | emitDecoratorMetadata: false 38 | }, 39 | } 40 | }, 41 | // in main, load css as raw text 42 | { 43 | // exclude: styles, 44 | test: /\.css$/, 45 | loaders: ['raw-loader', 'postcss-loader'] 46 | }, { 47 | // exclude: styles, 48 | test: /\.styl$/, 49 | loaders: ['raw-loader', 'postcss-loader', 'stylus-loader'] 50 | }, 51 | { 52 | // exclude: styles, 53 | test: /\.less$/, 54 | loaders: ['raw-loader', 'postcss-loader', 'less-loader'] 55 | }, { 56 | // exclude: styles, 57 | test: /\.scss$|\.sass$/, 58 | loaders: ['raw-loader', 'postcss-loader', 'sass-loader'] 59 | } 60 | ] 61 | }, 62 | 63 | plugins: [ 64 | // new TsConfigPathsPlugin(), 65 | // fix the warning in ./~/@angular/core/src/linker/system_js_ng_module_factory_loader.js 66 | new webpack.ContextReplacementPlugin( 67 | /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, 68 | config.root 69 | ) 70 | ], 71 | 72 | optimization: { 73 | minimize: false 74 | }, 75 | // Hide webpack output because its noisy. 76 | // noInfo: true, 77 | // Also prevent chunk and module display output, cleaner look. Only emit errors. 78 | stats: 'errors-only', 79 | devServer: { 80 | stats: 'errors-only' 81 | }, 82 | }; 83 | }; 84 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngm-cli", 3 | "bin": { 4 | "ngm": "bin/ngm-cli.js" 5 | }, 6 | "peerDependencies": { 7 | "@angular/compiler-cli": "*", 8 | "awesome-typescript-loader": "*", 9 | "webpack": "*", 10 | "typescript": "*" 11 | }, 12 | "dependencies": { 13 | "node-sass": "*", 14 | "node-sass-tilde-importer": "*", 15 | "chalk": "*", 16 | "cpy": "*", 17 | "del": "*", 18 | "execa": "*", 19 | "inquirer": "*", 20 | "listr": "*", 21 | "lodash": "*", 22 | "meow": "*", 23 | "read-pkg": "*", 24 | "rollup": "*", 25 | "rollup-plugin-commonjs": "*", 26 | "rollup-plugin-node-resolve": "*", 27 | "semver": "*", 28 | "split": "*", 29 | "stream-to-observable": "*", 30 | "tsconfig": "*", 31 | "update-notifier": "*", 32 | "write-pkg": "*" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/tasks/build.task.ts: -------------------------------------------------------------------------------- 1 | const execa = require('execa'); 2 | 3 | export function build(project:string) { 4 | return execa('ngc', ['-p', project], {preferLocal: true}); 5 | } 6 | -------------------------------------------------------------------------------- /src/tasks/bundle-es2015.ts: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const execa = require('execa'); 3 | const rollup = require('rollup'); 4 | const resolve = require('rollup-plugin-node-resolve'); 5 | const commonjs = require('rollup-plugin-commonjs'); 6 | 7 | import { inlineResources } from '../helpers/inline-resources'; 8 | import ROLLUP_GLOBALS from '../models/rollup.globals'; 9 | 10 | const bundlesDir = 'bundles'; 11 | 12 | 13 | export async function bundleEs2015(config) { 14 | await inlineResources(config.tmp); 15 | await execa('ngc', ['-p', config.tmp], { preferLocal: true }); 16 | return rollup.rollup({ 17 | input: path.resolve(config.tmp, 'dist-es2015', config.input.replace('.ts', '')), 18 | external: Object.keys(ROLLUP_GLOBALS), 19 | plugins: [ 20 | resolve({ 21 | module: true, 22 | main: true 23 | }), 24 | commonjs({ 25 | include: 'node_modules/**', 26 | }) 27 | ], 28 | onwarn: warning => { 29 | const skip_codes = [ 30 | 'THIS_IS_UNDEFINED', 31 | 'MISSING_GLOBAL_NAME' 32 | ]; 33 | if (skip_codes.indexOf(warning.code) != -1) return; 34 | console.error(warning); 35 | } 36 | }).then(bundle => bundle.write({ 37 | file: path.resolve(config.dist, bundlesDir, config.name + '.es2015.js'), 38 | name: config.name, 39 | format: 'es', 40 | sourcemap: true 41 | })); 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/tasks/bundle-umd.task.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import { ROOT } from '../utils'; 3 | import { getWebpackConfig } from '../models/webpack-umd.config'; 4 | const webpack = require('webpack'); 5 | 6 | // todo: move to constants and make it configurable 7 | const bundlesDir = 'bundles'; 8 | 9 | // Configure build and output; 10 | let lastHash = null; 11 | const webpackOutputOptions = { 12 | colors: true, 13 | chunks: true, 14 | modules: false, 15 | reasons: false, 16 | chunkModules: false 17 | }; 18 | 19 | // export function bundleUmd(dir, moduleConf, minify) { 20 | export function bundleUmd({src, dist, name, main, tsconfig, minify}) { 21 | const config = getWebpackConfig({ 22 | name: !minify ? `${name}.umd` : `${name}.umd.min`, 23 | root: path.resolve(ROOT, src), 24 | entry: path.resolve(ROOT, src, main), 25 | output: path.resolve(dist, bundlesDir), 26 | tsconfig: tsconfig 27 | }); 28 | 29 | if (minify) { 30 | config.optimization.minimize = true; 31 | } 32 | 33 | const webpackCompiler = webpack(config); 34 | 35 | return new Promise((resolve, reject) => { 36 | webpackCompiler.run((err, stats) => { 37 | if (err) { 38 | if (stats) { 39 | process.stdout.write(stats.toString(webpackOutputOptions) + '\n'); 40 | } 41 | return reject(err); 42 | } 43 | 44 | if (stats.hasErrors()) { 45 | process.stdout.write(stats.toString(webpackOutputOptions) + '\n'); 46 | } 47 | 48 | return stats.hasErrors() ? reject() : resolve(); 49 | }); 50 | }); 51 | } 52 | -------------------------------------------------------------------------------- /src/tasks/clean-dist.task.ts: -------------------------------------------------------------------------------- 1 | // import { TsmOptions } from '../types'; 2 | 3 | export function cleanDist(opts: TsmOptions[]){ 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/tasks/index.ts: -------------------------------------------------------------------------------- 1 | export { build } from './build.task'; 2 | export { bundleUmd } from './bundle-umd.task'; 3 | export { bundleEs2015 } from './bundle-es2015'; 4 | export * from './npm'; 5 | export { cleanDist } from './clean-dist.task'; 6 | export { prepublishGitCheck } from './prepublish-git-check.task'; 7 | -------------------------------------------------------------------------------- /src/tasks/npm/build-pkg-json.task.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import { ROOT } from '../../utils/constants'; 3 | import { mergePackageJson} from '../../utils/merge-package-json'; 4 | // import { TsmOptions } from '../../types'; 5 | // todo: replace 6 | const readPkg = require('read-pkg'); 7 | const writePkg = require('write-pkg'); 8 | 9 | /** 10 | * Smart merge of package.json files like Object.assign({}, src, dist) 11 | * @param opts 12 | * @param localDependencies 13 | * @param options 14 | */ 15 | export function buildPkgJson(opts:TsmOptions, localDependencies, options: {local: boolean}) { 16 | // read base package.json 17 | const base = readPkg.sync(ROOT); 18 | // read package.json in module root folder 19 | const module = readPkg.sync(opts.src); 20 | // merge packages 21 | localDependenciesVersionFallback(base, localDependencies); 22 | const pkg = mergePackageJson({base, module, localDependencies}); 23 | pkg.version = pkg.version || base.version; 24 | // write packages 25 | // todo: for some reason, read pkg ignores readme.md and says that readme not found, and this is not true 26 | delete pkg.readme; 27 | return writePkg(opts.dist, pkg); 28 | } 29 | 30 | /** 31 | * 32 | * @param tsmOptions 33 | * @param options 34 | */ 35 | export function buildPkgs(tsmOptions:TsmOptions[], options: {local: boolean}) { 36 | // 0. read base package.json 37 | // 1. read all sub module packages 38 | // 2. include sub module versions in modules hash 39 | // if options.local === true, resolve local dependencies as file paths: "module-a": "../module-a" 40 | // in general you need non relative dependencies only before publishing 41 | const localDependencies = tsmOptions.reduce((memo, val)=>{ 42 | memo[val.pkg.name] = !options.local ? val.pkg.version : path.resolve(val.dist); 43 | return memo; 44 | }, {}); 45 | // 3. merge packages 46 | return Promise.all(tsmOptions.map(optPkg => buildPkgJson(optPkg, localDependencies, options))); 47 | // 4. validate required fields in packages 48 | // todo: 49 | } 50 | 51 | function localDependenciesVersionFallback(base, localDependencies) { 52 | for (let pkgName in localDependencies) { 53 | localDependencies[pkgName] = localDependencies[pkgName] || base.version; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/tasks/npm/index.ts: -------------------------------------------------------------------------------- 1 | export { buildPkgJson, buildPkgs } from './build-pkg-json.task'; 2 | // export {} from './npm-dist-tag' 3 | export { npmInstall } from './npm-install.task'; 4 | export { npmLink } from './npm-link.task'; 5 | export { npmPublish } from './npm-publish.task'; 6 | export { npmVersion } from './npm-version.task'; 7 | export { npmDistTag } from './npm-dist-tag.task'; -------------------------------------------------------------------------------- /src/tasks/npm/npm-dist-tag.task.ts: -------------------------------------------------------------------------------- 1 | // npm dist-tag add @ [] 2 | // npm dist-tag rm 3 | // npm dist-tag ls []opt.dist 4 | import path = require('path'); 5 | const execa = require('execa'); 6 | 7 | export function npmDistTag({yarn, cmd, module, version, tag = ''}) { 8 | // const npm = yarn ? 'yarn' : 'npm'; 9 | const pkg = cmd === 'add' ? `${module}@${version}` : module; 10 | return execa.shell(['npm', 'dist-tag', cmd, pkg, tag].join(' ')) 11 | .then(res => { 12 | console.log(`Package ${module}`); 13 | console.log(res.stdout); 14 | }); 15 | } -------------------------------------------------------------------------------- /src/tasks/npm/npm-install.task.ts: -------------------------------------------------------------------------------- 1 | const del = require('del'); 2 | const execa = require('execa'); 3 | import Listr = require('listr'); 4 | import ListrTask = require('listr'); 5 | 6 | export function npmInstall({skipCleanup, yarn}) { 7 | return yarn ? 8 | // if yarn 9 | [{ 10 | title: 'Clean install dependencies', 11 | task: () => execa('yarn', [skipCleanup ? '' : 'upgrade']) 12 | }] 13 | // else npm 14 | : [ 15 | { 16 | title: 'Node modules cleanup', 17 | task: () => del('node_modules'), 18 | skip: () => skipCleanup 19 | }, 20 | { 21 | // todo: maybe install only dev dependencies 22 | title: 'Installing dependencies', 23 | task: () => execa('npm', ['install']), 24 | skip: () => skipCleanup 25 | } 26 | ]; 27 | } 28 | -------------------------------------------------------------------------------- /src/tasks/npm/npm-link.task.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | const execa = require('execa'); 3 | 4 | export function npmLink({cwd, yarn, module = ''}) { 5 | // const npm = yarn ? 'yarn' : 'npm'; 6 | return execa.shell(['npm', 'link', module].join(' '), { 7 | cwd: path.resolve(cwd) 8 | }); 9 | } -------------------------------------------------------------------------------- /src/tasks/npm/npm-publish.task.ts: -------------------------------------------------------------------------------- 1 | const execa = require('execa'); 2 | 3 | export function npmPublish({cwd, yarn, tag = '', access = ''}) { 4 | // const args = [yarn ? 'yarn --new-version' : 'npm', 'publish', cwd]; 5 | // todo: next time yarn, next time 6 | const args = ['npm', 'publish', cwd]; 7 | if (tag) { 8 | args.push('--tag', tag); 9 | } 10 | if (access) { 11 | args.push('--access', access); 12 | } 13 | return execa.shell(args.join(' '), {preferLocal: true}); 14 | } 15 | -------------------------------------------------------------------------------- /src/tasks/npm/npm-version.task.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | const execa = require('execa'); 3 | 4 | export function npmVersion({yarn, src, version, noGitTagVersion, message = ''}) { 5 | // we just updated subpackages versions, so working dir is not clean 6 | // but we knew it and using --force flag 7 | // so it will produce error: npm WARN using --force I sure hope you know what you are doing. 8 | // and we can swallow it 9 | const args = [' ', '--force']; 10 | const command = yarn 11 | ? `yarn version --new-version ${version}` 12 | : `npm version ${version}`; 13 | if (message && !yarn) { 14 | args.push('-m', `"${message}"`); 15 | } 16 | if (noGitTagVersion) { 17 | args.push('--no-git-tag-version'); 18 | } 19 | const cmd = command + args.join(' '); 20 | return new Promise(resolve => execa.shell(cmd, {cwd: path.resolve(src)}) 21 | .then(resolve).catch(resolve)); 22 | } 23 | -------------------------------------------------------------------------------- /src/tasks/prepublish-git-check.task.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was copied from https://github.com/sindresorhus/np/blob/858f3c6481ad63c31f98ce2597fe92e431adb91b/lib/git.js 3 | * @licence MIT 4 | * @copyright Sindre Sorhus (sindresorhus.com) 5 | * @author Sam Verschueren 6 | * @author Zeke Sikelianos 7 | */ 8 | 9 | const execa = require('execa'); 10 | const Listr = require('listr'); 11 | 12 | export function prepublishGitCheck({anyBranch}) { 13 | const tasks = [ 14 | { 15 | title: 'Check current branch', 16 | task: () => execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']).then(branch => { 17 | if (branch !== 'master') { 18 | throw new Error('Not on `master` branch. Use --any-branch to publish anyway.'); 19 | } 20 | }), 21 | skip: () => anyBranch 22 | }, 23 | { 24 | title: 'Check local working tree', 25 | task: () => execa.stdout('git', ['status', '--porcelain']).then(status => { 26 | if (status !== '') { 27 | throw new Error('Unclean working tree. Commit or stash changes first.'); 28 | } 29 | }) 30 | }, 31 | { 32 | title: 'Check remote history', 33 | task: () => execa.stdout('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']).then(result => { 34 | if (result !== '0') { 35 | throw new Error('Remote history differs. Please pull changes.'); 36 | } 37 | }) 38 | } 39 | ]; 40 | 41 | return new Listr(tasks); 42 | } -------------------------------------------------------------------------------- /src/test/e2e.ts: -------------------------------------------------------------------------------- 1 | import { bundleUmd } from '../tasks'; 2 | 3 | // build -p e2e/ngm-single/src --main index.ts 4 | 5 | const main = 'index.ts'; 6 | const opt = { 7 | src: 'e2e/ngm-single/src', 8 | dist: 'e2e/ngm-single/dist', 9 | pkg: {name: 'ng2-module'}, 10 | tsconfig: {config: {}, path: 'e2e/ngm-single/src/tsconfig.json'} 11 | }; 12 | 13 | bundleUmd({ 14 | src: opt.src, 15 | dist: opt.dist, 16 | name: opt.pkg.name, 17 | tsconfig: opt.tsconfig.path, 18 | main, 19 | minify: false 20 | }); 21 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "../dist", 4 | "module": "commonjs", 5 | "target": "es6", 6 | "declaration" :true, 7 | "inlineSourceMap": true, 8 | "moduleResolution": "node", 9 | "noEmitHelpers": false, 10 | "noImplicitAny": false, 11 | "skipLibCheck": true, 12 | "skipDefaultLibCheck": true, 13 | "stripInternal": true, 14 | "noEmitOnError": false, 15 | "removeComments": false, 16 | "lib": ["dom", "es6"], 17 | "typeRoots": ["../../node_modules/@types"], 18 | "types": ["node", "webpack"], 19 | "baseUrl": "" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | // todo: export to some typings 2 | // todo: add documentation 3 | declare interface TsmOptions { 4 | src: string; 5 | dist: string; 6 | tmp: string; 7 | project: string; 8 | pkg: any; 9 | /** 10 | * Array of local cross dependencies 11 | * */ 12 | cross?: string[]; 13 | tsconfig: {path: string; config: any;} 14 | } 15 | 16 | declare interface BuildOptions { 17 | project: string; 18 | verbose: boolean; 19 | clean: boolean; 20 | local: boolean; 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const ROOT = process.cwd(); 2 | export const pkgFileName = 'package.json'; 3 | export const tsconfigName = 'tsconfig.json'; 4 | export const ngCliConfName = 'angular-cli.json'; 5 | export const dependencyKeys = ['devDependencies', 'dependencies', 'peerDependencies']; -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | // todo: export Observable exec 2 | export * from './constants'; 3 | export { mergePackageJson } from './merge-package-json'; 4 | export { findSubmodules } from './submodules-resolution'; 5 | export { tasksWatch } from './tasks-watch'; 6 | 7 | -------------------------------------------------------------------------------- /src/utils/merge-package-json.ts: -------------------------------------------------------------------------------- 1 | //todo: add setting of cross-dependencies versions 2 | //todo: add readme field 3 | const _ = require('lodash'); 4 | 5 | const fieldsToCopy = 'main version description main module typings browser keywords author license repository'.split(' '); 6 | // read dependencies from main package.json 7 | // if dependencies duplicated they will be overwritten by each other 8 | import { dependencyKeys } from './constants' 9 | 10 | export function mergePackageJson(data) { 11 | const {base, module, localDependencies} = data; 12 | // read only needed fields from main package.json 13 | const filteredBasePkg = _.pick(base, fieldsToCopy); 14 | let dependenciesHash = _(base) 15 | .pick(dependencyKeys) 16 | .reduce((memo, v) => Object.assign(memo, v), {}); 17 | 18 | dependenciesHash = Object.assign(dependenciesHash, localDependencies); 19 | 20 | // update sub module package.json dependencies versions 21 | const newModulePkg = Object.assign(filteredBasePkg, module); 22 | _.each(dependencyKeys, (section) => { 23 | newModulePkg[section] = _.mapValues(newModulePkg[section], (version, dependency) => dependenciesHash[dependency]); 24 | }); 25 | 26 | return newModulePkg; 27 | } 28 | -------------------------------------------------------------------------------- /src/utils/submodules-resolution.ts: -------------------------------------------------------------------------------- 1 | // import { TsmOptions } from '../types'; 2 | import { pkgFileName, dependencyKeys, ROOT } from './constants'; 3 | 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const tsconfig = require('tsconfig'); 7 | const readPkg = require('read-pkg'); 8 | 9 | // todo: order by cross dependencies 10 | // todo: add --use-local-dependencies alias --local 11 | 12 | // should in the same folder as dist 13 | const _tmp = '.tmp'; 14 | 15 | /** 16 | * Will try to find package.json in src folder 17 | * if not found will search in 1st level of directories 18 | * Returns list of directories with package.json 19 | * project - string, relative path to folder 20 | */ 21 | export function findSubmodules(project: string, options?: { local: boolean }) { 22 | return listDirs(project) 23 | .then(dirs => orderByCrossDeps( 24 | dirs 25 | .filter(dir => isModuleRoot(dir)) 26 | .map(dir => ({dir, tsconfig: tsconfig.loadSync(dir)})) 27 | .map(opt => resolveOptions(project, opt))) 28 | ); 29 | } 30 | 31 | function listDirs(project: string): Promise { 32 | return Promise.resolve( 33 | [project].concat( 34 | fs 35 | .readdirSync(path.resolve(project)) 36 | .filter(file => fs.statSync(path.resolve(project, file)) 37 | .isDirectory()) 38 | .map(dir => path.join(project, dir)) 39 | )); 40 | } 41 | 42 | function isModuleRoot(dir: string) { 43 | if (fs.existsSync(path.join(dir, pkgFileName))) { 44 | return !!tsconfig.resolveSync(dir); 45 | } 46 | return false; 47 | } 48 | 49 | function resolveOptions(project: string, opt): TsmOptions { 50 | const tsOutDir = opt.tsconfig.config.compilerOptions.outDir; 51 | const tsConfigDir = path.dirname(opt.tsconfig.path); 52 | const relTsOutDir = path.relative(ROOT, path.resolve(tsConfigDir, tsOutDir)); 53 | const moduleDir = path.relative(project, opt.dir); 54 | // submodule root 55 | const src = opt.dir; 56 | // tsc out dir 57 | const dist = relTsOutDir.indexOf(moduleDir) == -1 58 | ? path.join(relTsOutDir, moduleDir) 59 | : relTsOutDir; 60 | 61 | // convert tsout ( '../dist' --> '../.tmp' ) 62 | const _distParsed = path.parse(tsOutDir); 63 | let tmp = moduleDir && moduleDir === _distParsed.base 64 | // '../dist/module-1' 65 | ? path.join(src, path.format(Object.assign(path.parse(_distParsed.dir), {base: _tmp})), moduleDir) 66 | // '../dist' 67 | : path.resolve(src, path.format(Object.assign({}, _distParsed, { base: _tmp }))); 68 | // tsconfig project 69 | return { 70 | src, dist, tmp, 71 | tsconfig: opt.tsconfig, 72 | project: path.relative(ROOT, tsConfigDir), 73 | pkg: readPkg.sync(src) 74 | }; 75 | } 76 | 77 | // todo: split it in 78 | // 1. building cross dependencies 79 | // 2. sorting by cross dependencies count 80 | /** 81 | * */ 82 | function orderByCrossDeps(options: TsmOptions[]): TsmOptions[] { 83 | const pkgName = options.map(opt => opt.pkg.name); 84 | return options 85 | .map(option => { 86 | option.cross = []; 87 | dependencyKeys.forEach(depKey => { 88 | if (!option.pkg[depKey]) { 89 | return; 90 | } 91 | pkgName.forEach(name => { 92 | if (name in option.pkg[depKey]) { 93 | option.cross.push(name); 94 | } 95 | }); 96 | }); 97 | return option; 98 | }) 99 | .sort((a: TsmOptions, b: TsmOptions) => { 100 | if (a.cross.length === b.cross.length) { 101 | return 0; 102 | } 103 | 104 | return a.cross.length > b.cross.length ? 1 : -1; 105 | }); 106 | } 107 | -------------------------------------------------------------------------------- /src/utils/tasks-watch.ts: -------------------------------------------------------------------------------- 1 | import chokidar = require('chokidar'); 2 | import path = require('path'); 3 | // todo: use observables? 4 | export function tasksWatch({project, taskQueue, watch, paths}){ 5 | let isRunning = false; 6 | let changedModule: number; 7 | 8 | runTasks(); 9 | 10 | if (watch) { 11 | chokidar.watch(project, {ignored: /[\/\\]\./}) 12 | .on('change', (event) => { 13 | changedModule = paths.indexOf(event.split(path.sep)[0]); 14 | console.log(`Changes detected: ${event}`); 15 | runTasks(); 16 | }); 17 | } 18 | 19 | return Promise.resolve(); 20 | 21 | function runTasks() { 22 | if (isRunning) { 23 | return; 24 | } 25 | 26 | isRunning = true; 27 | taskQueue.tasks.forEach((task: any, i: number) => { 28 | task.skip = () => changedModule && i !== changedModule; 29 | }); 30 | return taskQueue.run() 31 | .then(() => { 32 | console.log(`\n-------------------------------------\n`); 33 | isRunning = false; 34 | }) 35 | .catch(err => { 36 | if (err) { 37 | console.error(err); 38 | } 39 | isRunning = false; 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /todo.md: -------------------------------------------------------------------------------- 1 | - add npm unlink 2 | 3 | copy package to dist 4 | remove private: true 5 | rename to ngm-cli 6 | --------------------------------------------------------------------------------