├── src ├── scaffold │ ├── ssl │ │ ├── router.js │ │ └── server.js │ ├── src │ │ ├── app │ │ │ ├── app.component.css │ │ │ ├── app.component.scss │ │ │ ├── app.component.html │ │ │ ├── app.component.ts │ │ │ ├── shared │ │ │ │ └── components │ │ │ │ │ ├── lazy │ │ │ │ │ ├── lazy.component.html │ │ │ │ │ ├── lazy.module.ts │ │ │ │ │ ├── lazy.component.scss │ │ │ │ │ ├── lazy.component.ts │ │ │ │ │ └── lazy.component.css │ │ │ │ │ └── home │ │ │ │ │ ├── home.component.html │ │ │ │ │ ├── home.module.ts │ │ │ │ │ ├── home.component.scss │ │ │ │ │ ├── home.component.ts │ │ │ │ │ └── home.component.css │ │ │ ├── app-routing.module.ts │ │ │ └── app.module.ts │ │ ├── style │ │ │ ├── util │ │ │ │ ├── _colors.scss │ │ │ │ ├── _variables.scss │ │ │ │ ├── _fonts.scss │ │ │ │ ├── _mixins.scss │ │ │ │ └── _normalize.scss │ │ │ ├── style.scss │ │ │ └── _app.scss │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── assets │ │ │ │ ├── angular.png │ │ │ │ └── ng2-stealth.png │ │ │ ├── system.import.js │ │ │ ├── system.config.prod.js │ │ │ ├── index.html │ │ │ └── system.config.js │ │ ├── tsconfig.jit.json │ │ ├── tsconfig.dev.json │ │ └── tsconfig.prod.json │ ├── ivy │ │ └── src │ │ │ ├── app │ │ │ ├── app.component.scss │ │ │ ├── app.component.html │ │ │ ├── app.module.ts │ │ │ └── app.component.ts │ │ │ ├── main.ts │ │ │ ├── tsconfig.prod.json │ │ │ ├── tsconfig.dev.json │ │ │ └── public │ │ │ ├── index.html │ │ │ └── system.config.js │ ├── lib │ │ ├── index.ts │ │ ├── src │ │ │ └── components │ │ │ │ └── default │ │ │ │ ├── default.component.html │ │ │ │ ├── default.component.css │ │ │ │ ├── default.component.scss │ │ │ │ ├── default.component.ts │ │ │ │ └── default.module.ts │ │ ├── config │ │ │ ├── rollup.config.lib.js │ │ │ ├── rollup.config.lib-es5.js │ │ │ ├── rollup.config.lib-umd.js │ │ │ ├── tsconfig.lib.es5.json │ │ │ ├── tsconfig.lib.umd.json │ │ │ ├── tsconfig.lib.fesm2015.json │ │ │ ├── tsconfig.lib.esm5.json │ │ │ └── tsconfig.lib.esm2015.json │ │ ├── package.json │ │ ├── tsconfig.lib.json │ │ └── lib.config.json │ ├── root │ │ ├── README.md │ │ ├── config │ │ │ ├── server.config.prod.js │ │ │ ├── server.config.dev.js │ │ │ └── rollup.rxjs.js │ │ ├── closure.externs.js │ │ ├── postcss.config.js │ │ ├── closure.rollup.conf │ │ ├── .gitignore │ │ ├── backend │ │ │ ├── router.js │ │ │ └── server.js │ │ ├── rollup.config.js │ │ ├── closure.conf │ │ └── ngr.config.js │ ├── pretty │ │ ├── .prettierrc │ │ └── tslint.json │ └── standalone │ │ ├── tsconfig.json │ │ ├── protractor.config.js │ │ ├── package.json │ │ ├── karma-test-shim.js │ │ └── karma.conf.js ├── generate │ ├── index.js │ └── lib.js ├── build │ ├── index.js │ ├── jit.js │ ├── dev.js │ ├── prod.js │ └── lib.js ├── bundle │ ├── rollup.js │ └── closure.js ├── compile │ ├── uglify.js │ ├── babel.js │ ├── tsc.js │ └── ngc.js ├── config.js ├── style │ ├── postcss.js │ └── sass.js ├── watch.js ├── util.js └── log.js ├── .gitignore ├── .editorconfig ├── package.json ├── index.js └── README.md /src/scaffold/ssl/router.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scaffold/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scaffold/ivy/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scaffold/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scaffold/src/style/util/_colors.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scaffold/src/style/util/_variables.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scaffold/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scaffold/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './src/components/default/default.module'; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | package-lock.json 4 | cli.config.json 5 | .idea 6 | -------------------------------------------------------------------------------- /src/scaffold/ivy/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | Hello {{name}} 4 |

5 |
-------------------------------------------------------------------------------- /src/scaffold/lib/src/components/default/default.component.html: -------------------------------------------------------------------------------- 1 | A Bundled Component Is A Happy Component 2 | -------------------------------------------------------------------------------- /src/scaffold/src/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steveblue/angular2-rollup/HEAD/src/scaffold/src/public/favicon.ico -------------------------------------------------------------------------------- /src/scaffold/src/public/assets/angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steveblue/angular2-rollup/HEAD/src/scaffold/src/public/assets/angular.png -------------------------------------------------------------------------------- /src/scaffold/root/README.md: -------------------------------------------------------------------------------- 1 | # {{projectName}} 2 | 3 | This project was built with [angular-rollup](https://www.npmjs.com/package/angular-rollup). 4 | -------------------------------------------------------------------------------- /src/scaffold/root/config/server.config.prod.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | origin: 'localhost', 3 | port: 4200 4 | }; 5 | 6 | module.exports = config; -------------------------------------------------------------------------------- /src/scaffold/src/public/assets/ng2-stealth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steveblue/angular2-rollup/HEAD/src/scaffold/src/public/assets/ng2-stealth.png -------------------------------------------------------------------------------- /src/scaffold/root/config/server.config.dev.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | origin: 'localhost', 3 | port: 4200 4 | }; 5 | 6 | module.exports = config; 7 | -------------------------------------------------------------------------------- /src/scaffold/lib/src/components/default/default.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | font-family: "Lato", sans-serif; 3 | background: red; 4 | color: white; 5 | padding: 20px; 6 | } -------------------------------------------------------------------------------- /src/scaffold/lib/src/components/default/default.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | font-family: "Lato", sans-serif; 3 | background: red; 4 | color: white; 5 | padding: 20px; 6 | } -------------------------------------------------------------------------------- /src/scaffold/ivy/src/main.ts: -------------------------------------------------------------------------------- 1 | import { ɵrenderComponent as renderComponent } from '@angular/core'; 2 | import { AppComponent } from './app/app.component'; 3 | 4 | renderComponent(AppComponent); -------------------------------------------------------------------------------- /src/scaffold/pretty/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "useTabs": false, 5 | "tabWidth": 2, 6 | "semi": true, 7 | "bracketSpacing": true 8 | } 9 | -------------------------------------------------------------------------------- /src/scaffold/src/public/system.import.js: -------------------------------------------------------------------------------- 1 | System.import('system.config.prod.js').then(function () { 2 | Promise.all([ 3 | System.import('bundle') 4 | ]); 5 | }).catch(console.error.bind(console)); 6 | -------------------------------------------------------------------------------- /src/scaffold/src/style/style.scss: -------------------------------------------------------------------------------- 1 | // Authored Dependencies 2 | @import "util/variables"; 3 | @import "util/colors"; 4 | @import "util/fonts"; 5 | @import "util/mixins"; 6 | 7 | // Application Styles 8 | @import "app"; 9 | -------------------------------------------------------------------------------- /src/scaffold/root/closure.externs.js: -------------------------------------------------------------------------------- 1 | /** @externs */ 2 | 3 | var System = function(){}; 4 | var Hammer = function(){}; 5 | var global = function(){}; 6 | var COMPILED = function(){}; 7 | var ngDevMode = function(){}; 8 | var process = function(){}; -------------------------------------------------------------------------------- /src/scaffold/src/style/util/_fonts.scss: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 4 | font-weight:400; 5 | } 6 | -------------------------------------------------------------------------------- /src/scaffold/root/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = ctx => ({ 2 | plugins: { 3 | 'postcss-discard-comments': ctx.env === 'prod' ? {} : false, 4 | 'autoprefixer': { remove: false }, 5 | 'postcss-csso': ctx.env === 'prod' ? {} : false 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /src/scaffold/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: 'app.component.html', 6 | styleUrls: ['app.component.css'] 7 | }) 8 | export class AppComponent { 9 | constructor() {} 10 | } 11 | -------------------------------------------------------------------------------- /src/scaffold/ivy/src/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./../out-tsc", 5 | "module": "es2015" 6 | }, 7 | "angularCompilerOptions": { 8 | "enableIvy": true 9 | }, 10 | "include": [ 11 | "./../src/**/*.ts" 12 | ] 13 | } -------------------------------------------------------------------------------- /src/scaffold/ivy/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { AppComponent } from './app.component'; 4 | 5 | @NgModule({ 6 | declarations: [ 7 | AppComponent 8 | ], 9 | imports: [], 10 | providers: [], 11 | bootstrap: [AppComponent] 12 | }) 13 | export class AppModule { } -------------------------------------------------------------------------------- /src/scaffold/ivy/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: 'app.component.html', 6 | styleUrls: ['app.component.css'] 7 | }) 8 | export class AppComponent { 9 | public name: string = 'Ivy!'; 10 | constructor() {} 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | end_of_line = lf 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /src/scaffold/src/public/system.config.prod.js: -------------------------------------------------------------------------------- 1 | (function (global) { 2 | System.defaultJSExtensions = true; 3 | System.config({ 4 | 'map': { 5 | 'bundle': 'bundle.js', 6 | 'vendor': 'vendor.js' 7 | }, 8 | 'meta': { 9 | 'bundle': { 10 | deps: [] 11 | } 12 | } 13 | }); 14 | })(this); 15 | 16 | -------------------------------------------------------------------------------- /src/generate/index.js: -------------------------------------------------------------------------------- 1 | const cli = require('./../../cli.config.json'); 2 | const config = require('./../config'); 3 | 4 | class Generator { 5 | 6 | constructor() { 7 | this.outputPath = config.processRoot; 8 | this.name = process.argv[process.argv.indexOf(cli.program.generate) + 1]; 9 | } 10 | } 11 | 12 | module.exports = Generator; -------------------------------------------------------------------------------- /src/scaffold/lib/src/components/default/default.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'default', 5 | templateUrl: 'default.component.html', 6 | styleUrls: ['default.component.css'] 7 | }) 8 | 9 | export class DefaultComponent { 10 | 11 | 12 | constructor() { 13 | 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/scaffold/lib/config/rollup.config.lib.js: -------------------------------------------------------------------------------- 1 | // rollup.config.lib.js 2 | 3 | export default { 4 | input: 'out-tsc/es2015/{{folderName}}.js', 5 | output: { 6 | file: 'dist/{{projectName}}/fesm2015/{{folderName}}.js', 7 | format: 'es', 8 | sourcemap: false 9 | }, 10 | onwarn: function ( message ) { 11 | 12 | return; 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/scaffold/src/app/shared/components/lazy/lazy.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 7 | 8 |
-------------------------------------------------------------------------------- /src/scaffold/lib/config/rollup.config.lib-es5.js: -------------------------------------------------------------------------------- 1 | // rollup.config.lib-es5.js 2 | 3 | export default { 4 | input: 'out-tsc/es5/{{folderName}}.js', 5 | output: { 6 | file: 'dist/{{projectName}}/fesm5/{{folderName}}.js', 7 | format: 'es', 8 | sourcemap: true 9 | }, 10 | onwarn: function ( message ) { 11 | 12 | return; 13 | 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/scaffold/lib/config/rollup.config.lib-umd.js: -------------------------------------------------------------------------------- 1 | // rollup.config.lib-umd.js 2 | 3 | export default { 4 | input: 'out-tsc/es5/{{folderName}}.js', 5 | output: { 6 | file: 'dist/{{projectName}}/bundles/{{folderName}}.umd.js', 7 | format: 'cjs', 8 | sourcemap: true 9 | }, 10 | onwarn: function ( message ) { 11 | 12 | return; 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/scaffold/src/app/shared/components/home/home.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 7 | 8 |
-------------------------------------------------------------------------------- /src/scaffold/ivy/src/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./../tsconfig.json", 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "outDir": "./../dist/{{projectName}}", 6 | "target": "es5", 7 | "module": "commonjs" 8 | }, 9 | "angularCompilerOptions": { 10 | "enableIvy": true 11 | }, 12 | "include": [ 13 | "./../src/**/*.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/scaffold/src/tsconfig.jit.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./../tsconfig.json", 3 | "compileOnSave": false, 4 | "buildOnSave": false, 5 | "compilerOptions": { 6 | "outDir": "./../dist/{{projectName}}/src", 7 | "target": "es5", 8 | "module": "commonjs", 9 | "noImplicitAny": false, 10 | "removeComments": false 11 | }, 12 | "include": [ 13 | "./../src/**/*.ts" 14 | ] 15 | } -------------------------------------------------------------------------------- /src/scaffold/src/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./../tsconfig.json", 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "outDir": "./../dist/{{projectName}}", 6 | "target": "es5", 7 | "module": "commonjs", 8 | "noImplicitAny": false 9 | }, 10 | "angularCompilerOptions": { 11 | "skipMetadataEmit": true 12 | }, 13 | "include": [ 14 | "./../src/**/*.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/scaffold/lib/src/components/default/default.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { DefaultComponent } from './default.component'; 4 | 5 | export * from './default.component'; 6 | 7 | @NgModule({ 8 | imports: [ CommonModule ], 9 | declarations: [ DefaultComponent ], 10 | exports: [ DefaultComponent ] 11 | }) 12 | 13 | export class DefaultModule {} 14 | -------------------------------------------------------------------------------- /src/scaffold/src/app/shared/components/home/home.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { CommonModule } from '@angular/common'; 4 | 5 | import { HomeComponent } from './home.component'; 6 | 7 | @NgModule({ 8 | imports: [ RouterModule, 9 | CommonModule ], 10 | declarations: [ HomeComponent ], 11 | exports: [ HomeComponent ] 12 | }) 13 | 14 | export class HomeModule {} 15 | -------------------------------------------------------------------------------- /src/scaffold/src/app/shared/components/lazy/lazy.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | import { CommonModule } from '@angular/common'; 4 | 5 | import { LazyComponent } from './lazy.component'; 6 | 7 | @NgModule({ 8 | imports: [ RouterModule, 9 | CommonModule ], 10 | declarations: [ LazyComponent ], 11 | exports: [ LazyComponent ] 12 | }) 13 | 14 | export class LazyModule {} 15 | -------------------------------------------------------------------------------- /src/scaffold/lib/config/tsconfig.lib.es5.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.lib.json", 3 | "compilerOptions": { 4 | "outDir": "{{relativePath}}out-tsc/es5", 5 | "module": "es2015", 6 | "target": "es5" 7 | }, 8 | "angularCompilerOptions": { 9 | "strictMetadataEmit": true, 10 | "skipTemplateCodegen": true, 11 | "annotateForClosureCompiler": true, 12 | "flatModuleOutFile": "{{folderName}}.js", 13 | "flatModuleId": "{{projectName}}" 14 | } 15 | } -------------------------------------------------------------------------------- /src/scaffold/lib/config/tsconfig.lib.umd.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.lib.json", 3 | "compilerOptions": { 4 | "outDir": "{{relativePath}}out-tsc/es5", 5 | "module": "es2015", 6 | "target": "es5" 7 | }, 8 | "angularCompilerOptions": { 9 | "strictMetadataEmit": true, 10 | "skipTemplateCodegen": true, 11 | "annotateForClosureCompiler": true, 12 | "flatModuleOutFile": "{{folderName}}.js", 13 | "flatModuleId": "{{projectName}}" 14 | } 15 | } -------------------------------------------------------------------------------- /src/scaffold/lib/config/tsconfig.lib.fesm2015.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.lib.json", 3 | "compilerOptions": { 4 | "outDir": "{{relativePath}}out-tsc/es2015", 5 | "target": "es2015", 6 | "module": "es2015" 7 | }, 8 | "angularCompilerOptions": { 9 | "strictMetadataEmit": true, 10 | "skipTemplateCodegen": true, 11 | "annotateForClosureCompiler": true, 12 | "flatModuleOutFile": "{{folderName}}.js", 13 | "flatModuleId": "{{projectName}}" 14 | } 15 | } -------------------------------------------------------------------------------- /src/scaffold/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import { HomeComponent } from './shared/components/home/home.component'; 5 | import { LazyComponent } from './shared/components/lazy/lazy.component'; 6 | 7 | const routes: Routes = [ 8 | { path: '', component: HomeComponent }, 9 | { path: 'lazy', component: LazyComponent } 10 | ]; 11 | 12 | export const routing: ModuleWithProviders = RouterModule.forRoot(routes); 13 | -------------------------------------------------------------------------------- /src/scaffold/src/app/shared/components/lazy/lazy.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | position: absolute; 3 | width: 100%; 4 | height: 100%; 5 | background: red; 6 | } 7 | 8 | .is--center { 9 | width: 256px; 10 | height: 256px; 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | transform: translateX(-50%) translateY(-50%); 15 | perspective: 1000px; 16 | .app__icon { 17 | width: 100%; 18 | height: 100%; 19 | img { 20 | width: 100%; 21 | height: 100%; 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/scaffold/src/app/shared/components/home/home.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | width: 100%; 3 | height: 100%; 4 | background: black; 5 | } 6 | 7 | .is--center { 8 | width: 256px; 9 | height: 256px; 10 | position: absolute; 11 | top: 50%; 12 | left: 50%; 13 | transform: translateX(-50%) translateY(-50%); 14 | perspective: 1000px; 15 | .app__icon { 16 | width: 100%; 17 | height: 100%; 18 | img { 19 | width: 100%; 20 | height: 100%; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/scaffold/standalone/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "moduleResolution": "node", 6 | "declaration": false, 7 | "sourceMap": true, 8 | "typeRoots": [ 9 | "node_modules/@types" 10 | ], 11 | "types": [ 12 | "node", 13 | "jasmine", 14 | "karma" 15 | ], 16 | "lib": [ 17 | "es2017", 18 | "dom" 19 | ] 20 | } 21 | } -------------------------------------------------------------------------------- /src/scaffold/lib/config/tsconfig.lib.esm5.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.lib.json", 3 | "compilerOptions": { 4 | "outDir": "{{relativePath}}out-tsc/esm5", 5 | "module": "es2015", 6 | "target": "es5" 7 | }, 8 | "angularCompilerOptions": { 9 | "strictMetadataEmit": true, 10 | "skipTemplateCodegen": true, 11 | "annotateForClosureCompiler": true, 12 | "flatModuleOutFile": "{{folderName}}.js", 13 | "flatModuleId": "{{projectName}}" 14 | }, 15 | "files": [ 16 | "{{relativePath}}tmp/index.ts" 17 | ] 18 | } -------------------------------------------------------------------------------- /src/scaffold/root/closure.rollup.conf: -------------------------------------------------------------------------------- 1 | --compilation_level=ADVANCED_OPTIMIZATIONS 2 | --language_in=ECMASCRIPT6 3 | --language_out=ECMASCRIPT5 4 | --variable_renaming_report=closure/variable_renaming_report 5 | --property_renaming_report=closure/property_renaming_report 6 | --create_source_map=%outname%.map 7 | 8 | --warning_level=QUIET 9 | --dependency_mode=STRICT 10 | --rewrite_polyfills=false 11 | 12 | --externs closure.externs.js 13 | --externs node_modules/zone.js/dist/zone_externs.js 14 | 15 | --js dist/{{projectName}}/bundle.es2015.js 16 | 17 | --entry_point=./dist/{{projectName}}/bundle.es2015.js -------------------------------------------------------------------------------- /src/scaffold/lib/config/tsconfig.lib.esm2015.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.lib.json", 3 | "compilerOptions": { 4 | "outDir": "{{relativePath}}out-tsc/esm2015", 5 | "module": "es2015", 6 | "target": "es2015" 7 | }, 8 | "angularCompilerOptions": { 9 | "strictMetadataEmit": true, 10 | "skipTemplateCodegen": true, 11 | "annotateForClosureCompiler": true, 12 | "flatModuleOutFile": "{{folderName}}.js", 13 | "flatModuleId": "{{projectName}}" 14 | }, 15 | "files": [ 16 | "{{relativePath}}tmp/index.ts" 17 | ] 18 | } -------------------------------------------------------------------------------- /src/scaffold/src/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./../out-tsc", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "stripInternal": true, 8 | "inlineSources": true, 9 | "skipLibCheck": true, 10 | "allowSyntheticDefaultImports": true, 11 | "noImplicitAny": false, 12 | "removeComments": true, 13 | "allowUnreachableCode": false 14 | }, 15 | "angularCompilerOptions": { 16 | "annotationsAs": "static fields", 17 | "annotateForClosureCompiler": true, 18 | "skipMetadataEmit": false 19 | }, 20 | "include": [ 21 | "./../src/**/*.ts" 22 | ] 23 | } -------------------------------------------------------------------------------- /src/scaffold/lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{projectName}}", 3 | "version": "1.0.0", 4 | "description": "", 5 | "license": "MIT", 6 | "module": "./esm5/{{folderName}}.js", 7 | "es2015": "./fesm2015/{{folderName}}.js", 8 | "esm5": "./esm5/{{folderName}}.js", 9 | "esm2015": "./esm2015/{{folderName}}.js", 10 | "fesm5": "./fesm5/{{folderName}}.js", 11 | "fesm2015": "./fesm2015/{{folderName}}.js", 12 | "main": "./bundles/{{folderName}}.umd.js", 13 | "typings": "{{folderName}}.d.ts", 14 | "devDependencies": { 15 | "@angular/core": "^6.0.0", 16 | "@angular/compiler": "^6.0.0", 17 | "@angular/compiler-cli": "^6.0.0", 18 | "rollup": "^0.55.0", 19 | "rxjs": "^6.0.0", 20 | "typescript": "^2.7.2", 21 | "zone.js": "^0.8.26" 22 | } 23 | } -------------------------------------------------------------------------------- /src/scaffold/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { BrowserModule } from '@angular/platform-browser'; 4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 5 | import { AppComponent } from './app.component'; 6 | import { routing } from './app-routing.module'; 7 | import { HomeModule } from './shared/components/home/home.module'; 8 | import { LazyModule } from './shared/components/lazy/lazy.module'; 9 | 10 | @NgModule({ 11 | imports: [BrowserModule, 12 | BrowserAnimationsModule, 13 | CommonModule, 14 | HomeModule, 15 | LazyModule, 16 | routing], 17 | declarations: [AppComponent], 18 | bootstrap: [AppComponent] 19 | }) 20 | export class AppModule {} 21 | -------------------------------------------------------------------------------- /src/scaffold/lib/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "{{relativePath}}tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "rootDir": "./{{relativePath}}tmp", 6 | "sourceMap": false, 7 | "declaration": true, 8 | "stripInternal": true, 9 | "allowSyntheticDefaultImports": true, 10 | "noImplicitAny": false, 11 | "removeComments": true, 12 | "allowUnreachableCode": false, 13 | "skipLibCheck": true 14 | }, 15 | "angularCompilerOptions": { 16 | "strictMetadataEmit": true, 17 | "skipTemplateCodegen": true, 18 | "annotateForClosureCompiler": true, 19 | "flatModuleOutFile": "{{folderName}}.js", 20 | "flatModuleId": "{{projectName}}" 21 | }, 22 | "files": [ 23 | "./{{relativePath}}tmp/index.ts" 24 | ] 25 | } -------------------------------------------------------------------------------- /src/scaffold/standalone/protractor.config.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | 'build/src/app/**/*.e2e-spec.js' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function () { } 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'tsconfig.jit.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /src/scaffold/root/config/rollup.rxjs.js: -------------------------------------------------------------------------------- 1 | export default [{ 2 | input: 'node_modules/rxjs/_esm2015/index.js', 3 | output: { 4 | file: 'node_modules/rxjs/_fesm2015/index.js', 5 | format: 'es' 6 | } 7 | }, 8 | { 9 | input: 'node_modules/rxjs/_esm2015/operators/index.js', 10 | output: { 11 | file: 'node_modules/rxjs/_fesm2015/operators/index.js', 12 | format: 'es' 13 | } 14 | }, 15 | { 16 | input: 'node_modules/rxjs/_esm2015/ajax/index.js', 17 | output: { 18 | file: 'node_modules/rxjs/_fesm2015/ajax/index.js', 19 | format: 'es' 20 | } 21 | }, 22 | { 23 | input: 'node_modules/rxjs/_esm2015/testing/index.js', 24 | output: { 25 | file: 'node_modules/rxjs/_fesm2015/testing/index.js', 26 | format: 'es' 27 | } 28 | }, 29 | { 30 | input: 'node_modules/rxjs/_esm2015/websocket/index.js', 31 | output: { 32 | file: 'node_modules/rxjs/_fesm2015/websocket/index.js', 33 | format: 'es' 34 | } 35 | } 36 | ]; -------------------------------------------------------------------------------- /src/scaffold/lib/lib.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": "{{projectPath}}", 3 | "dist": "dist/{{projectName}}", 4 | "filename": "{{folderName}}", 5 | "es2015": { 6 | "tsConfig": "tsconfig.lib.fesm2015.json", 7 | "rollupConfig": "rollup.config.lib.js", 8 | "outFile": "dist/{{projectName}}/fesm2015/{{folderName}}.js" 9 | }, 10 | "es5": { 11 | "tsConfig": "tsconfig.lib.es5.json", 12 | "rollupConfig": "rollup.config.lib-es5.js", 13 | "outFile": "dist/{{projectName}}/fesm5/{{folderName}}.js" 14 | }, 15 | "umd": { 16 | "tsConfig": "tsconfig.lib.umd.json", 17 | "rollupConfig": "rollup.config.lib-umd.js", 18 | "outFile": "dist/{{projectName}}/bundles/{{folderName}}.umd.js" 19 | }, 20 | "esm5": { 21 | "tsConfig": "tsconfig.lib.esm5.json", 22 | "outFile": "dist/{{projectName}}/esm5/{{folderName}}.js" 23 | }, 24 | "esm2015": { 25 | "tsConfig": "tsconfig.lib.esm2015.json", 26 | "outFile": "dist/{{projectName}}/esm2015/{{folderName}}.js" 27 | } 28 | } -------------------------------------------------------------------------------- /src/build/index.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events'); 2 | const moment = require('moment'); 3 | const colors = require('colors'); 4 | const log = require('./../log.js'); 5 | const cli = require('./../../cli.config.json'); 6 | const config = require('./../config'); 7 | const path = require('path'); 8 | 9 | class BuildEmitter extends EventEmitter {} 10 | 11 | class Build { 12 | constructor() { 13 | 14 | if (!config.projects) { 15 | log.break(); 16 | log.error('NGR ERROR: ngr >= 2.0.0 requires projects schema in ngr.config.js'); 17 | log.break(); 18 | } 19 | this.outputPath = config.projects[config.project].architect.build.options.outputPath; 20 | this.startTime = moment(new Date()); 21 | this.emitter = new BuildEmitter(); 22 | 23 | if (!cli.program.webpack) { 24 | const args = cli.program.rawArgs.filter((str) => { 25 | return !str.includes('node'); 26 | }); 27 | // .map((str) => { 28 | // return path.basename(str); 29 | // }); 30 | log.process('ngr ' + args.join(' ')); 31 | 32 | } 33 | } 34 | } 35 | 36 | module.exports = Build; 37 | -------------------------------------------------------------------------------- /src/scaffold/ivy/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{projectName}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/scaffold/root/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Users Environment Variables 23 | .lock-wscript 24 | 25 | # OS generated files 26 | .DS_Store 27 | ehthumbs.db 28 | Icon? 29 | Thumbs.db 30 | 31 | # Node Files 32 | /node_modules 33 | /bower_components 34 | 35 | # Coverage 36 | /coverage/ 37 | 38 | # Typing 39 | /src/typings/tsd/ 40 | /typings/ 41 | /tsd_typings/ 42 | 43 | # Src 44 | .tmp 45 | /tmp 46 | /out-tsc 47 | /out-css 48 | /closure 49 | compiler.jar 50 | main.js 51 | main.js.map 52 | src/app/**/*.css 53 | src/app/app.component.css 54 | 55 | # Dist 56 | /build 57 | /closure 58 | /dist 59 | 60 | # Doc 61 | /doc 62 | 63 | # Tests 64 | 65 | _test-output/ 66 | 67 | # IDE 68 | .idea/ 69 | *.swp 70 | .vscode 71 | documentation/ 72 | 73 | -------------------------------------------------------------------------------- /src/bundle/rollup.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const exec = require('child_process').exec; 3 | const config = require('./../config'); 4 | const log = require('./../log.js'); 5 | 6 | 7 | class RollupBuilder { 8 | 9 | constructor() { 10 | this.isBundling = false; 11 | } 12 | 13 | bundle(rollupConfigPath) { 14 | 15 | return new Promise((res, rej) => { 16 | 17 | log.process('rollup'); 18 | this.isBundling = true; 19 | 20 | exec(path.normalize(config.projectRoot + '/node_modules/.bin/rollup') + 21 | ' -c ' + rollupConfigPath, {silent: true}, (error, stdout, stderr) => { 22 | log.stop('rollup'); 23 | if (stderr.includes('Error')) { 24 | if (rej) rej(error); 25 | this.isBundling = false; 26 | log.error(stderr); 27 | 28 | } else { 29 | this.isBundling = false; 30 | log.message(stderr); 31 | res(); 32 | } 33 | 34 | }); 35 | }) 36 | } 37 | 38 | } 39 | 40 | 41 | module.exports = RollupBuilder; -------------------------------------------------------------------------------- /src/scaffold/root/backend/router.js: -------------------------------------------------------------------------------- 1 | // router.js 2 | 3 | const express = require('express'); 4 | const router = require('express').Router(); 5 | const compression = require('compression'); 6 | const config = require(process.cwd()+'/angular.json'); 7 | 8 | 9 | module.exports = function(app) { 10 | 'use strict'; 11 | let projectRoot = config.projects[config.defaultProject].architect.build.options.outputPath; 12 | // ROUTER CONFIG 13 | 14 | 15 | app.use(function(req, res, next) { 16 | 17 | // CAUTION: Wide open CORS! 18 | res.header('Access-Control-Allow-Origin', req.headers.origin); 19 | res.header('Access-Control-Allow-Credentials', 'true'); 20 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); 21 | res.header('Access-Control-Allow-Headers', 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'); 22 | 23 | if (req.method == 'OPTIONS' ) { 24 | res.send(200); 25 | } 26 | else { 27 | next(); 28 | } 29 | 30 | }); 31 | 32 | 33 | // ROUTES 34 | 35 | app.use(compression()); 36 | 37 | app.use('/', express.static( process.cwd() + '/' + projectRoot )); 38 | 39 | 40 | app.get('*', function (req, res) { 41 | res.sendFile('index.html', { root: process.cwd() + '/' + projectRoot }); 42 | }); 43 | 44 | 45 | 46 | return router; 47 | 48 | }; 49 | -------------------------------------------------------------------------------- /src/scaffold/src/app/shared/components/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy } from '@angular/core'; 2 | import { trigger, state, style, transition, animate } from '@angular/animations'; 3 | 4 | @Component({ 5 | selector: 'app-home', 6 | templateUrl: 'home.component.html', 7 | styleUrls: ['home.component.css'], 8 | animations: [ 9 | trigger('intro', [ 10 | state('void', style({ 11 | opacity: '0.0', 12 | transform: 'translateZ(-1000px)' 13 | })), 14 | state('active', style({ 15 | opacity: '1.0', 16 | transform: 'translateZ(0px)', 17 | perspective: '1000px' 18 | })), 19 | state('inactive', style({ 20 | opacity: '0.0', 21 | transform: 'translateZ(-1000px)', 22 | perspective: '1000px' 23 | })), 24 | transition('active => void', animate('5000ms cubic-bezier(0.19, 1, 0.22, 1)')), 25 | transition('void => active', animate('5000ms cubic-bezier(0.19, 1, 0.22, 1)')), 26 | transition('inactive => active', animate('5000ms cubic-bezier(0.19, 1, 0.22, 1)')), 27 | transition('active => inactive', animate('5000ms cubic-bezier(0.19, 1, 0.22, 1)')) 28 | ]) 29 | ] 30 | }) 31 | 32 | export class HomeComponent implements OnDestroy { 33 | 34 | stealthMode: string; 35 | 36 | constructor() { 37 | 38 | this.stealthMode = 'active'; 39 | 40 | } 41 | 42 | ngOnDestroy() { 43 | 44 | this.stealthMode = 'inactive'; 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/scaffold/src/app/shared/components/lazy/lazy.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy } from '@angular/core'; 2 | import { trigger, state, style, transition, animate } from '@angular/animations'; 3 | 4 | @Component({ 5 | selector: 'app-lazy', 6 | templateUrl: 'lazy.component.html', 7 | styleUrls: ['lazy.component.css'], 8 | animations: [ 9 | trigger('intro', [ 10 | state('void', style({ 11 | opacity: '0.0', 12 | transform: 'translateZ(-1000px)' 13 | })), 14 | state('active', style({ 15 | opacity: '1.0', 16 | transform: 'translateZ(0px)', 17 | perspective: '1000px' 18 | })), 19 | state('inactive', style({ 20 | opacity: '0.0', 21 | transform: 'translateZ(-1000px)', 22 | perspective: '1000px' 23 | })), 24 | transition('active => void', animate('5000ms cubic-bezier(0.19, 1, 0.22, 1)')), 25 | transition('void => active', animate('5000ms cubic-bezier(0.19, 1, 0.22, 1)')), 26 | transition('inactive => active', animate('5000ms cubic-bezier(0.19, 1, 0.22, 1)')), 27 | transition('active => inactive', animate('5000ms cubic-bezier(0.19, 1, 0.22, 1)')) 28 | ]) 29 | ] 30 | }) 31 | 32 | export class LazyComponent implements OnDestroy { 33 | 34 | angularMode: string; 35 | 36 | constructor() { 37 | 38 | this.angularMode = 'active'; 39 | 40 | } 41 | 42 | ngOnDestroy() { 43 | 44 | this.angularMode = 'inactive'; 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/compile/uglify.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const UglifyJS = require('uglify-js'); 4 | const config = require('./../config'); 5 | const log = require('./../log.js'); 6 | 7 | class UglifyBuilder { 8 | 9 | constructor() { } 10 | 11 | optimize() { 12 | 13 | return new Promise((res, rej) => { 14 | 15 | log.process('uglify'); 16 | 17 | let outputPath = config.projects[config.project].architect.build.options.outputPath; 18 | 19 | exec(path.normalize(config.projectRoot + '/node_modules/.bin/uglifyjs') + 20 | ' ' + path.join(outputPath, 'bundle.js') + ' -o ' + path.join(outputPath, 'bundle.js')+' ---compress --mangle --toplevel --verbose', { silent: true }, (error, stdout, stderr) => { 21 | log.stop('uglify'); 22 | if (stderr.includes('Error')) { 23 | if (rej) rej(error); 24 | log.error(stderr); 25 | 26 | } else { 27 | log.message(stderr); 28 | res(); 29 | } 30 | 31 | }); 32 | }) 33 | } 34 | 35 | minify(filePath) { 36 | fs.readFile(filePath, 'utf-8', (err, contents) => { 37 | let result = UglifyJS.minify(contents, { toplevel: true, mangle: true, compress: true }); 38 | return fs.writeFileSync(filePath, result.code); 39 | }); 40 | } 41 | 42 | } 43 | 44 | 45 | module.exports = UglifyBuilder; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-rollup", 3 | "version": "2.0.5", 4 | "description": "cli for building angular with closure compiler and rollup", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/steveblue/angular2-rollup.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "javascript", 16 | "framework", 17 | "cli" 18 | ], 19 | "author": "Steve Belovarich", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/steveblue/angular2-rollup/issues" 23 | }, 24 | "homepage": "https://github.com/steveblue/angular2-rollup#readme", 25 | "dependencies": { 26 | "@angular-devkit/build-optimizer": "^0.6.8", 27 | "async": "^2.6.0", 28 | "chokidar": "^2.0.2", 29 | "colors": "^1.1.2", 30 | "commander": "^2.14.1", 31 | "detect-installed": "^2.0.4", 32 | "findup": "^0.1.5", 33 | "gzip-size": "^4.1.0", 34 | "html-minifier": "^3.5.11", 35 | "htmlprocessor": "^0.2.6", 36 | "js-string-escape": "^1.0.1", 37 | "magic-string": "^0.23.2", 38 | "moment": "^2.21.0", 39 | "node-sass": "^4.13.1", 40 | "ora": "^2.1.0", 41 | "prettier": "^1.14.2", 42 | "shelljs": "^0.8.1", 43 | "single-line-log": "^1.1.2", 44 | "uglify-js": "^3.4.0", 45 | "uuid": "^3.2.1" 46 | }, 47 | "bin": { 48 | "ngr": "index.js" 49 | }, 50 | "engines": { 51 | "node": ">8.0.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/scaffold/root/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | 3 | import nodeResolve from 'rollup-plugin-node-resolve'; 4 | 5 | class ResolveRxjs { 6 | 7 | resolveId(importee, importer) { 8 | if (importee.startsWith('rxjs')) { 9 | let pkg = importee.replace('rxjs', ''); 10 | if (importee.includes('/')) { 11 | return `node_modules/rxjs/_fesm2015${pkg}/index.js`; 12 | } else { 13 | return `node_modules/rxjs/_fesm2015/${pkg}index.js`; 14 | } 15 | } 16 | } 17 | } 18 | 19 | 20 | class ResolveAngular { 21 | 22 | resolveId(importee, importer) { 23 | if (importee.startsWith('@angular')) { 24 | let pkg = importee.replace('@angular', ''); 25 | if (importee.split('/').length > 2) { 26 | return `node_modules/${importee.split('/')[0]}/${importee.split('/')[1]}/fesm2015/${importee.split('/')[2]}.js`; 27 | } else { 28 | return `node_modules/${importee}/fesm2015${pkg}.js`; 29 | } 30 | } 31 | } 32 | } 33 | 34 | 35 | export default { 36 | input: 'out-tsc/src/main.js', 37 | treeshake: true, 38 | output: { 39 | file: 'dist/{{projectName}}/bundle.es2015.js', 40 | format: 'iife' 41 | }, 42 | plugins: [ 43 | new ResolveRxjs(), 44 | new ResolveAngular(), 45 | nodeResolve({ 46 | mainFields: ['module', 'jsnext'] 47 | }) 48 | ], 49 | onwarn: function (message) { 50 | 51 | console.log(message); 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/scaffold/src/app/shared/components/home/home.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | width: 100%; 3 | height: 100%; 4 | background: black; 5 | } 6 | 7 | .is--center { 8 | width: 256px; 9 | height: 256px; 10 | position: absolute; 11 | top: 50%; 12 | left: 50%; 13 | -webkit-transform: translateX(-50%) translateY(-50%); 14 | transform: translateX(-50%) translateY(-50%); 15 | -webkit-perspective: 1000px; 16 | perspective: 1000px; 17 | } 18 | 19 | .is--center .app__icon { 20 | width: 100%; 21 | height: 100%; 22 | } 23 | 24 | .is--center .app__icon img { 25 | width: 100%; 26 | height: 100%; 27 | } 28 | 29 | /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImhvbWUuY29tcG9uZW50LmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtFQUNFLFlBQVk7RUFDWixhQUFhO0VBQ2Isa0JBQWtCO0NBQ25COztBQUVEO0VBQ0UsYUFBYTtFQUNiLGNBQWM7RUFDZCxtQkFBbUI7RUFDbkIsU0FBUztFQUNULFVBQVU7RUFDVixxREFBNkM7VUFBN0MsNkNBQTZDO0VBQzdDLDRCQUFvQjtVQUFwQixvQkFBb0I7Q0FDckI7O0FBRUQ7RUFDRSxZQUFZO0VBQ1osYUFBYTtDQUNkOztBQUVEO0VBQ0UsWUFBWTtFQUNaLGFBQWE7Q0FDZCIsImZpbGUiOiJob21lLmNvbXBvbmVudC5jc3MiLCJzb3VyY2VzQ29udGVudCI6WyI6aG9zdCB7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG4gIGJhY2tncm91bmQ6IGJsYWNrO1xufVxuXG4uaXMtLWNlbnRlciB7XG4gIHdpZHRoOiAyNTZweDtcbiAgaGVpZ2h0OiAyNTZweDtcbiAgcG9zaXRpb246IGFic29sdXRlO1xuICB0b3A6IDUwJTtcbiAgbGVmdDogNTAlO1xuICB0cmFuc2Zvcm06IHRyYW5zbGF0ZVgoLTUwJSkgdHJhbnNsYXRlWSgtNTAlKTtcbiAgcGVyc3BlY3RpdmU6IDEwMDBweDtcbn1cblxuLmlzLS1jZW50ZXIgLmFwcF9faWNvbiB7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG59XG5cbi5pcy0tY2VudGVyIC5hcHBfX2ljb24gaW1nIHtcbiAgd2lkdGg6IDEwMCU7XG4gIGhlaWdodDogMTAwJTtcbn1cbiJdfQ== */ -------------------------------------------------------------------------------- /src/scaffold/src/app/shared/components/lazy/lazy.component.css: -------------------------------------------------------------------------------- 1 | :host { 2 | position: absolute; 3 | width: 100%; 4 | height: 100%; 5 | background: red; 6 | } 7 | 8 | .is--center { 9 | width: 256px; 10 | height: 256px; 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | -webkit-transform: translateX(-50%) translateY(-50%); 15 | transform: translateX(-50%) translateY(-50%); 16 | -webkit-perspective: 1000px; 17 | perspective: 1000px; 18 | } 19 | 20 | .is--center .app__icon { 21 | width: 100%; 22 | height: 100%; 23 | } 24 | 25 | .is--center .app__icon img { 26 | width: 100%; 27 | height: 100%; 28 | } 29 | 30 | /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxhenkuY29tcG9uZW50LmNzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtFQUNFLG1CQUFtQjtFQUNuQixZQUFZO0VBQ1osYUFBYTtFQUNiLGdCQUFnQjtDQUNqQjs7QUFFRDtFQUNFLGFBQWE7RUFDYixjQUFjO0VBQ2QsbUJBQW1CO0VBQ25CLFNBQVM7RUFDVCxVQUFVO0VBQ1YscURBQTZDO1VBQTdDLDZDQUE2QztFQUM3Qyw0QkFBb0I7VUFBcEIsb0JBQW9CO0NBQ3JCOztBQUVEO0VBQ0UsWUFBWTtFQUNaLGFBQWE7Q0FDZDs7QUFFRDtFQUNFLFlBQVk7RUFDWixhQUFhO0NBQ2QiLCJmaWxlIjoibGF6eS5jb21wb25lbnQuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiOmhvc3Qge1xuICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG4gIGJhY2tncm91bmQ6IHJlZDtcbn1cblxuLmlzLS1jZW50ZXIge1xuICB3aWR0aDogMjU2cHg7XG4gIGhlaWdodDogMjU2cHg7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgdG9wOiA1MCU7XG4gIGxlZnQ6IDUwJTtcbiAgdHJhbnNmb3JtOiB0cmFuc2xhdGVYKC01MCUpIHRyYW5zbGF0ZVkoLTUwJSk7XG4gIHBlcnNwZWN0aXZlOiAxMDAwcHg7XG59XG5cbi5pcy0tY2VudGVyIC5hcHBfX2ljb24ge1xuICB3aWR0aDogMTAwJTtcbiAgaGVpZ2h0OiAxMDAlO1xufVxuXG4uaXMtLWNlbnRlciAuYXBwX19pY29uIGltZyB7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG59XG4iXX0= */ -------------------------------------------------------------------------------- /src/scaffold/src/style/_app.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background: #000000; 3 | color: #fdfdfd; 4 | font-size: 16px; 5 | -webkit-font-smoothing: antialiased; 6 | -moz-osx-font-smoothing: grayscale; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | 11 | a:link, a:visited { 12 | text-decoration: none; 13 | } 14 | 15 | .loading__icon { 16 | display: block; 17 | position: absolute; 18 | border-radius: 50%; 19 | border-style: solid; 20 | border-color: #444 #fff #fff; 21 | animation: spin 1.5s cubic-bezier(0.66, 1, 0.66, 1) infinite; 22 | opacity: 0.5; 23 | transform: translate(-50%, -50%) rotate(0deg); 24 | backface-visibility: none; 25 | &.is--center { 26 | top: 50%; 27 | left: 50%; 28 | } 29 | &.is--left { 30 | top: 50%; 31 | left: 0%; 32 | } 33 | &.is--right { 34 | top: 50%; 35 | right: 0%; 36 | } 37 | &.is--large { 38 | border-width: 8px; 39 | width: 64px; 40 | height: 64px; 41 | } 42 | &.is--medium { 43 | border-width: 4px; 44 | width: 36px; 45 | height: 36px; 46 | } 47 | &.is--small { 48 | border-width: 2px; 49 | width: 18px; 50 | height: 18px; 51 | } 52 | } 53 | 54 | @keyframes spin { 55 | 0% { transform: translate(-50%, -50%) rotate(0deg); } 56 | 100% { transform: translate(-50%, -50%) rotate(359deg); } 57 | } 58 | 59 | @media only screen 60 | and (min-device-width : 375px) 61 | and (max-device-width : 667px) { 62 | 63 | .loading__icon { 64 | 65 | &.is--small { 66 | margin-left: -10px; 67 | margin-top:-6px; 68 | } 69 | 70 | } 71 | 72 | } 73 | 74 | 75 | @media only screen 76 | and (min-device-width : 414px) 77 | and (max-device-width : 736px) { 78 | 79 | .loading__icon { 80 | 81 | &.is--small { 82 | margin-left: -10px; 83 | margin-top:-6px; 84 | } 85 | 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/scaffold/root/backend/server.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // server.js 3 | 4 | const express = require('express'); 5 | const http = require('http'); 6 | const fs = require('fs'); 7 | const app = express(); 8 | const config = require(process.cwd()+'/angular.json'); 9 | const serverConfig = { 10 | dev: require(process.cwd()+'/config/server.config.dev.js'), 11 | prod: require(process.cwd()+'/config/server.config.prod.js') 12 | }; 13 | 14 | let projectRoot = config.projects[config.defaultProject].architect.build.options.outputPath; 15 | let env = process.env.NODE_ENV || 'dev'; 16 | const port = serverConfig[env].port || process.env.PORT; 17 | const host = serverConfig[env].origin; 18 | let canWatch = false; 19 | let server; 20 | 21 | process.argv.forEach(function(arg){ 22 | 23 | if (arg.includes('watch')) { 24 | canWatch = arg.split('=')[1].trim() === 'true' ? true : false; 25 | } 26 | 27 | }); 28 | 29 | 30 | // Livereload Server Start 31 | 32 | let live = function() { 33 | let livereload = require('livereload'); 34 | let liveserver = livereload.createServer({ 35 | port: 35729 36 | }); 37 | liveserver.watch([process.cwd() + '/'+projectRoot+'/assets', 38 | process.cwd() + '/'+projectRoot+'/src', 39 | process.cwd() + '/'+projectRoot+'/style', 40 | process.cwd() + '/'+projectRoot+'/*.html', 41 | process.cwd() + '/'+projectRoot+'/*.js', 42 | process.cwd() + '/'+projectRoot+'/*.css']); 43 | console.log('Livereload available at '+host+':'+35729); 44 | }; 45 | 46 | // Create Server 47 | 48 | server = http.createServer(app); 49 | 50 | if (canWatch === true) { 51 | 52 | live(); 53 | 54 | } 55 | 56 | 57 | // Express Middleware 58 | 59 | 60 | 61 | // Load Modules 62 | 63 | const routes = require('./router')(app); 64 | 65 | // Server Start 66 | 67 | server.listen(port); 68 | 69 | console.log('Express available at '+host+':'+port); 70 | 71 | 72 | module.exports = app; 73 | -------------------------------------------------------------------------------- /src/scaffold/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{projectName}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 |
39 | 40 | 41 | 42 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/scaffold/root/closure.conf: -------------------------------------------------------------------------------- 1 | --compilation_level=ADVANCED_OPTIMIZATIONS 2 | --language_in=ECMASCRIPT6 3 | --language_out=ECMASCRIPT5 4 | --variable_renaming_report=closure/variable_renaming_report 5 | --property_renaming_report=closure/property_renaming_report 6 | --create_source_map=%outname%.map 7 | 8 | --warning_level=QUIET 9 | --dependency_mode=STRICT 10 | --rewrite_polyfills=false 11 | --module_resolution=NODE 12 | 13 | --externs closure.externs.js 14 | --externs node_modules/zone.js/dist/zone_externs.js 15 | --externs node_modules/@angular/core/src/testability/testability.externs.js 16 | 17 | --js node_modules/rxjs/package.json 18 | --js node_modules/rxjs/_fesm2015/index.js 19 | 20 | --js node_modules/rxjs/operators/package.json 21 | --js node_modules/rxjs/_fesm2015/operators/index.js 22 | 23 | --js node_modules/@angular/core/package.json 24 | --js node_modules/@angular/core/fesm2015/core.js 25 | 26 | --js node_modules/@angular/common/package.json 27 | --js node_modules/@angular/common/fesm2015/common.js 28 | 29 | --js node_modules/@angular/platform-browser/package.json 30 | --js node_modules/@angular/platform-browser/fesm2015/platform-browser.js 31 | 32 | --js node_modules/@angular/forms/package.json 33 | --js node_modules/@angular/forms/fesm2015/forms.js 34 | 35 | --js node_modules/@angular/common/http/package.json 36 | --js node_modules/@angular/common/fesm2015/http.js 37 | 38 | --js node_modules/@angular/router/package.json 39 | --js node_modules/@angular/router/fesm2015/router.js 40 | 41 | --js node_modules/@angular/animations/package.json 42 | --js node_modules/@angular/animations/fesm2015/animations.js 43 | 44 | --js node_modules/@angular/animations/browser/package.json 45 | --js node_modules/@angular/animations/fesm2015/browser.js 46 | 47 | --js node_modules/@angular/platform-browser/animations/package.json 48 | --js node_modules/@angular/platform-browser/fesm2015/animations.js 49 | 50 | --js out-tsc/**.js 51 | 52 | --package_json_entry_names es2015 53 | 54 | --entry_point=./out-tsc/src/main.js -------------------------------------------------------------------------------- /src/scaffold/standalone/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{projectName}}", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "description": "", 6 | "repository": "", 7 | "contributors": [], 8 | "scripts": { 9 | "ngr": "ngr", 10 | "ngr:prod": "ngr build prod", 11 | "serve": "node backend/server.js" 12 | }, 13 | "dependencies": { 14 | "@angular/animations": "~7.0.0", 15 | "@angular/common": "~7.0.0", 16 | "@angular/compiler": "~7.0.0", 17 | "@angular/core": "~7.0.0", 18 | "@angular/forms": "~7.0.0", 19 | "@angular/platform-browser": "~7.0.0", 20 | "@angular/platform-browser-dynamic": "~7.0.0", 21 | "@angular/router": "~7.0.0", 22 | "angular-polyfills": "^1.0.1", 23 | "classlist.js": "^1.1.20150312", 24 | "compression": "^1.7.4", 25 | "console-polyfill": "^0.3.0", 26 | "core-js": "^2.4.1", 27 | "express": "^4.17.0", 28 | "ie9-oninput-polyfill": "^1.1.1", 29 | "reflect-metadata": "^0.1.13", 30 | "rxjs": "^6.0.0", 31 | "systemjs": "^0.21.4", 32 | "web-animations-js": "^2.3.2", 33 | "zone.js": "^0.8.26" 34 | }, 35 | "devDependencies": { 36 | "@angular/compiler-cli": "~7.0.0", 37 | "@angular/language-service": "~7.0.0", 38 | "autoprefixer": "^8.6.3", 39 | "codelyzer": "^4.0.1", 40 | "google-closure-compiler": "20190618.0.0", 41 | "livereload": "^0.8.0", 42 | "node-sass": "^4.12.0", 43 | "postcss": "^7.0.0", 44 | "postcss-cli": "^6.0.0", 45 | "postcss-csso": "^3.0.0", 46 | "postcss-discard-comments": "^4.0.0", 47 | "postcss-prettify": "^0.3.4", 48 | "protractor": "^5.1.2", 49 | "rollup": "^1.0.0", 50 | "rollup-plugin-node-resolve": "^5.2.0", 51 | "shelljs": "^0.8.3", 52 | "ts-node": "^8.3.0", 53 | "tsickle": "0.35.0", 54 | "tslint": "^5.18.0", 55 | "typescript": "3.1.3" 56 | }, 57 | "engines": { 58 | "node": ">8.11.0" 59 | } 60 | } -------------------------------------------------------------------------------- /src/scaffold/src/public/system.config.js: -------------------------------------------------------------------------------- 1 | (function (global) { 2 | 3 | System.config({ 4 | paths: { 5 | // paths serve as alias 6 | 'npm:': 'node_modules/', 7 | 'lib:': 'lib/' 8 | }, 9 | // map tells the System loader where to look for things 10 | map: { 11 | // our app is within the app folder 12 | app: '.', 13 | // angular bundles 14 | '@angular/animations': 'lib:@angular/animations/bundles/animations.umd.js', 15 | '@angular/animations/browser': 'lib:@angular/animations/bundles/animations-browser.umd.js', 16 | '@angular/platform-browser/animations': 'lib:@angular/platform-browser/bundles/platform-browser-animations.umd.js', 17 | '@angular/core': 'lib:@angular/core/bundles/core.umd.js', 18 | '@angular/common': 'lib:@angular/common/bundles/common.umd.js', 19 | '@angular/compiler': 'lib:@angular/compiler/bundles/compiler.umd.js', 20 | '@angular/platform-browser': 'lib:@angular/platform-browser/bundles/platform-browser.umd.js', 21 | '@angular/platform-browser-dynamic': 'lib:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 22 | '@angular/common/http': 'lib:@angular/common/bundles/common-http.umd.js', 23 | '@angular/http': 'lib:@angular/http/bundles/http.umd.js', 24 | '@angular/router': 'lib:@angular/router/bundles/router.umd.js', 25 | '@angular/forms': 'lib:@angular/forms/bundles/forms.umd.js', 26 | // other libraries 27 | 'rxjs': 'lib:rxjs', 28 | 'tslib': 'lib:tslib/tslib.js' 29 | }, 30 | // packages tells the System loader how to load when no filename and/or no extension 31 | packages: { 32 | 'app': { main: './main.js', defaultExtension: 'js' }, 33 | 'rxjs/ajax': { main: 'index.js', defaultExtension: 'js' }, 34 | 'rxjs/operators': { main: 'index.js', defaultExtension: 'js' }, 35 | 'rxjs/testing': { main: 'index.js', defaultExtension: 'js' }, 36 | 'rxjs/websocket': { main: 'index.js', defaultExtension: 'js' }, 37 | 'rxjs': { main: 'index.js', defaultExtension: 'js' } 38 | } 39 | }); 40 | 41 | })(this); 42 | -------------------------------------------------------------------------------- /src/scaffold/ivy/src/public/system.config.js: -------------------------------------------------------------------------------- 1 | (function (global) { 2 | 3 | System.config({ 4 | paths: { 5 | // paths serve as alias 6 | 'npm:': 'node_modules/', 7 | 'lib:': 'lib/' 8 | }, 9 | // map tells the System loader where to look for things 10 | map: { 11 | // our app is within the app folder 12 | app: '.', 13 | // angular bundles 14 | '@angular/animations': 'lib:@angular/animations/bundles/animations.umd.js', 15 | '@angular/animations/browser': 'lib:@angular/animations/bundles/animations-browser.umd.js', 16 | '@angular/platform-browser/animations': 'lib:@angular/platform-browser/bundles/platform-browser-animations.umd.js', 17 | '@angular/core': 'lib:@angular/core/bundles/core.umd.js', 18 | '@angular/common': 'lib:@angular/common/bundles/common.umd.js', 19 | '@angular/compiler': 'lib:@angular/compiler/bundles/compiler.umd.js', 20 | '@angular/platform-browser': 'lib:@angular/platform-browser/bundles/platform-browser.umd.js', 21 | '@angular/platform-browser-dynamic': 'lib:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 22 | '@angular/common/http': 'lib:@angular/common/bundles/common-http.umd.js', 23 | '@angular/http': 'lib:@angular/http/bundles/http.umd.js', 24 | '@angular/router': 'lib:@angular/router/bundles/router.umd.js', 25 | '@angular/forms': 'lib:@angular/forms/bundles/forms.umd.js', 26 | // other libraries 27 | 'rxjs': 'lib:rxjs', 28 | 'tslib': 'lib:tslib/tslib.js' 29 | }, 30 | // packages tells the System loader how to load when no filename and/or no extension 31 | packages: { 32 | 'app': { main: './src/main.js', defaultExtension: 'js' }, 33 | 'rxjs/ajax': { main: 'index.js', defaultExtension: 'js' }, 34 | 'rxjs/operators': { main: 'index.js', defaultExtension: 'js' }, 35 | 'rxjs/testing': { main: 'index.js', defaultExtension: 'js' }, 36 | 'rxjs/websocket': { main: 'index.js', defaultExtension: 'js' }, 37 | 'rxjs': { main: 'index.js', defaultExtension: 'js' } 38 | } 39 | }); 40 | 41 | })(this); 42 | -------------------------------------------------------------------------------- /src/scaffold/pretty/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["node_modules/codelyzer"], 3 | "rules": { 4 | "arrow-return-shorthand": true, 5 | "callable-types": true, 6 | "class-name": true, 7 | "comment-format": [true, "check-space"], 8 | "curly": true, 9 | "deprecation": { 10 | "severity": "warn" 11 | }, 12 | "eofline": true, 13 | "forin": true, 14 | "import-blacklist": [true, "rxjs/Rx"], 15 | "import-spacing": true, 16 | "indent": [true, "spaces"], 17 | "interface-over-type-literal": true, 18 | "label-position": true, 19 | "member-access": false, 20 | "member-ordering": [ 21 | true, 22 | { 23 | "order": ["static-field", "instance-field", "static-method", "instance-method"] 24 | } 25 | ], 26 | "no-arg": true, 27 | "no-bitwise": false, 28 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 29 | "no-construct": true, 30 | "no-debugger": true, 31 | "no-duplicate-super": true, 32 | "no-empty": false, 33 | "no-empty-interface": true, 34 | "no-eval": true, 35 | "no-inferrable-types": [true, "ignore-params"], 36 | "no-misused-new": true, 37 | "no-non-null-assertion": true, 38 | "no-shadowed-variable": true, 39 | "no-string-literal": false, 40 | "no-string-throw": true, 41 | "no-switch-case-fall-through": true, 42 | "no-unnecessary-initializer": true, 43 | "no-unused-expression": true, 44 | "no-use-before-declare": true, 45 | "no-var-keyword": true, 46 | "object-literal-sort-keys": false, 47 | "prefer-const": true, 48 | "radix": true, 49 | "unified-signatures": true, 50 | "variable-name": false, 51 | "no-output-on-prefix": true, 52 | "use-input-property-decorator": true, 53 | "use-output-property-decorator": true, 54 | "use-host-property-decorator": true, 55 | "no-input-rename": true, 56 | "no-output-rename": true, 57 | "use-life-cycle-interface": true, 58 | "use-pipe-transform-interface": true, 59 | "component-class-suffix": true, 60 | "directive-class-suffix": true 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/scaffold/ssl/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // server.js 3 | 4 | const express = require('express'); 5 | const https = require('https'); 6 | const path = require('path'); 7 | const fs = require('fs'); 8 | const app = express(); 9 | const config = require(process.cwd() + '/angular.json'); 10 | const serverConfig = { 11 | dev: require(process.cwd() + '/config/server.config.dev.js'), 12 | prod: require(process.cwd() + '/config/server.config.prod.js') 13 | }; 14 | const sslOptions = { 15 | key: fs.readFileSync(path.join(__dirname, 'key.pem')), 16 | cert: fs.readFileSync(path.join(__dirname, 'cert.pem')), 17 | requestCert: false, 18 | rejectUnauthorized: false 19 | }; 20 | 21 | let projectRoot = config.projects[config.defaultProject].architect.build.options.outputPath; 22 | let env = process.env.NODE_ENV || 'dev'; 23 | const port = serverConfig[env].port || process.env.PORT; 24 | const host = serverConfig[env].origin; 25 | let canWatch = false; 26 | let server; 27 | 28 | process.argv.forEach(function(arg) { 29 | if (arg.includes('watch')) { 30 | canWatch = arg.split('=')[1].trim() === 'true' ? true : false; 31 | } 32 | }); 33 | 34 | // Create Server 35 | 36 | server = https.createServer(sslOptions, app); 37 | 38 | // Livereload Server Start 39 | 40 | let live = function() { 41 | let livereload = require('livereload'); 42 | let liveserver = livereload.createServer({ 43 | port: 35729, 44 | https: { 45 | key: fs.readFileSync(path.join(__dirname, 'key.pem')), 46 | cert: fs.readFileSync(path.join(__dirname, 'cert.pem')) 47 | } 48 | }); 49 | liveserver.watch([ 50 | process.cwd() + '/' + projectRoot + '/assets', 51 | process.cwd() + '/' + projectRoot + '/src', 52 | process.cwd() + '/' + projectRoot + '/style', 53 | process.cwd() + '/' + projectRoot + '/*.html', 54 | process.cwd() + '/' + projectRoot + '/*.js', 55 | process.cwd() + '/' + projectRoot + '/*.css' 56 | ]); 57 | console.log('Livereload available at ' + host + ':' + 35729); 58 | }; 59 | 60 | if (canWatch === true) { 61 | live(); 62 | } 63 | 64 | // Express Middleware 65 | 66 | // Load Modules 67 | 68 | const routes = require('./router')(app); 69 | 70 | // Server Start 71 | 72 | server.listen(port, () => { 73 | console.log('SSL connection available at ' + host + ':' + port); 74 | }); 75 | 76 | module.exports = app; 77 | -------------------------------------------------------------------------------- /src/bundle/closure.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const exec = require('child_process').exec; 3 | const util = require('./../util.js'); 4 | const log = require('./../log.js'); 5 | const config = require('./../config'); 6 | const cli = require('./../../cli.config.json'); 7 | 8 | class ClosureBuilder { 9 | 10 | constructor() { 11 | this.jarPath = util.hasConfigProperty('jarPath', config.prodOptions) ? config.prodOptions.jarPath : path.resolve('node_modules', 'google-closure-compiler-java', 'compiler.jar'); 12 | this.warningLevel = util.hasConfigProperty('warningLevel', config.prodOptions) ? config.prodOptions.warningLevel : 'QUIET'; 13 | this.confPath = util.hasConfigProperty('confPath', config.prodOptions) ? config.prodOptions.confPath : path.normalize('closure.conf'); 14 | this.outFile = util.hasConfigProperty('outBundle', config.prodOptions) ? config.prodOptions.outBundle : './' + config.build + '/bundle.js'; 15 | this.manifestPath = util.hasConfigProperty('manifestPath', config.prodOptions) ? config.prodOptions.manifestPath : path.normalize('closure/manifest.MF'); 16 | } 17 | 18 | bundle() { 19 | return new Promise((res) => { 20 | 21 | if (cli.program.rollup) { 22 | this.confPath = path.normalize('closure.rollup.conf') 23 | } 24 | 25 | exec(`java -jar ${this.jarPath} --warning_level=${this.warningLevel} --flagfile ${this.confPath} --js_output_file ${this.outFile} --output_manifest=${this.manifestPath}`, 26 | { silent: true }, 27 | (error, stdout, stderr) => { 28 | log.stop('closure compiler'); 29 | if (stdout.includes('ERROR')) { 30 | error.split(/\n\n/g).forEach((str) => { 31 | log.formatClosureError(str); 32 | }); 33 | } 34 | else if (stderr.includes('ERROR')) { 35 | stderr.split(/\n\n/g).forEach((str) => { 36 | log.formatClosureError(str); 37 | }); 38 | } 39 | else { 40 | log.success('Optimization complete.', ['closure']); 41 | if (res) { 42 | res('done'); 43 | } 44 | } 45 | 46 | }); 47 | }) 48 | } 49 | 50 | } 51 | 52 | 53 | module.exports = ClosureBuilder; 54 | -------------------------------------------------------------------------------- /src/scaffold/standalone/karma-test-shim.js: -------------------------------------------------------------------------------- 1 | // #docregion 2 | // /*global jasmine, __karma__, window*/ 3 | Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing. 4 | 5 | // Uncomment to get full stacktrace output. Sometimes helpful, usually not. 6 | // Error.stackTraceLimit = Infinity; // 7 | 8 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 9 | 10 | var builtPath = '/base/build/src/app/'; 11 | 12 | __karma__.loaded = function () { }; 13 | 14 | function isJsFile(path) { 15 | return path.slice(-3) == '.js'; 16 | } 17 | 18 | function isSpecFile(path) { 19 | return /\.spec\.(.*\.)?js$/.test(path); 20 | } 21 | 22 | function isBuiltFile(path) { 23 | return isJsFile(path) && (path.substr(0, builtPath.length) == builtPath); 24 | } 25 | 26 | var allSpecFiles = Object.keys(window.__karma__.files) 27 | .filter(isSpecFile) 28 | .filter(isBuiltFile); 29 | 30 | System.config({ 31 | baseURL: '/base/build/', 32 | // Extend usual application package list with test folder 33 | packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } }, 34 | 35 | // Assume npm: is set in `paths` in systemjs.config 36 | // Map the angular testing umd bundles 37 | map: { 38 | 'app': '/build', 39 | '@angular/core/testing': 'lib/@angular/core/bundles/core-testing.umd.js', 40 | '@angular/common/testing': 'lib/@angular/common/bundles/common-testing.umd.js', 41 | '@angular/compiler/testing': 'lib/@angular/compiler/bundles/compiler-testing.umd.js', 42 | '@angular/platform-browser/testing': 'lib/@angular/platform-browser/bundles/platform-browser-testing.umd.js', 43 | '@angular/platform-browser-dynamic/testing': 'lib/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js', 44 | '@angular/http/testing': 'lib/@angular/http/bundles/http-testing.umd.js', 45 | '@angular/router/testing': 'lib/@angular/router/bundles/router-testing.umd.js', 46 | '@angular/forms/testing': 'lib/@angular/forms/bundles/forms-testing.umd.js', 47 | 'rxjs': 'node_modules/rxjs' 48 | }, 49 | }); 50 | 51 | System.import('/base/build/system.config.js') 52 | .then(initTestBed) 53 | .then(initTesting); 54 | 55 | 56 | function initTestBed() { 57 | return Promise.all([ 58 | System.import('/base/build/lib/@angular/core/bundles/core-testing.umd.js'), 59 | System.import('/base/build/lib/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js') 60 | ]) 61 | 62 | .then(function (providers) { 63 | var coreTesting = providers[0]; 64 | var browserTesting = providers[1]; 65 | 66 | coreTesting.TestBed.initTestEnvironment( 67 | browserTesting.BrowserDynamicTestingModule, 68 | browserTesting.platformBrowserDynamicTesting()); 69 | }) 70 | } 71 | 72 | // Import all spec files and start karma 73 | function initTesting() { 74 | return Promise.all( 75 | allSpecFiles.map(function (moduleName) { 76 | return System.import(moduleName); 77 | }) 78 | ) 79 | .then(__karma__.start, __karma__.error); 80 | } 81 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | 2 | const findup = require('findup'); 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const log = require('./log'); 6 | const processRoot = path.join(path.dirname(process.cwd()), path.basename(process.cwd())); 7 | const cliRoot = findup.sync(__dirname, 'package.json'); 8 | let projectRoot = require(path.join(cliRoot, 'cli.config.json')).projectRoot; 9 | 10 | class Config { 11 | constructor() { 12 | 13 | let config = new Object(); 14 | 15 | if (fs.existsSync(projectRoot + '/ngr.config.js')) { 16 | config = require(projectRoot + '/ngr.config.js'); 17 | } else { // for processes not in the root 18 | 19 | try{ 20 | projectRoot = findup.sync(projectRoot, 'ngr.config.js'); 21 | config = require(projectRoot + '/ngr.config.js'); 22 | } catch(e){ 23 | log.break(); 24 | log.error('ngr command requires to be run in an Angular project scaffolded with angular-rollup'); 25 | log.break(); 26 | log.break(); 27 | process.exit(1); 28 | } 29 | 30 | } 31 | 32 | if (fs.existsSync(projectRoot + '/angular.json')) { 33 | 34 | let angularConfig = require(projectRoot + '/angular.json'); 35 | let ngrConfig = require(projectRoot + '/ngr.config.js'); 36 | 37 | config.angular = angularConfig; 38 | // if next argument after build is not an option, assume argument is the project name 39 | if (process.argv.indexOf('build') !== -1 && 40 | process.argv[process.argv.indexOf('build') + 2] && 41 | process.argv[process.argv.indexOf('build') + 2].includes('--') === false) { 42 | 43 | config.project = process.argv[process.argv.indexOf('build') + 2]; 44 | config.src = path.join(ngrConfig.projects[config.project].root, ngrConfig.projects[config.project].sourceRoot); 45 | config.build = ngrConfig.projects[config.project].architect.build.options.outputPath; 46 | config.style = { 47 | files : ngrConfig.projects[config.project].architect.build.options.styles 48 | } 49 | 50 | 51 | } else { 52 | 53 | // use default project name 54 | config.project = ngrConfig.defaultProject; 55 | config.src = path.join(ngrConfig.projects[config.project].root, ngrConfig.projects[config.project].sourceRoot); 56 | config.build = ngrConfig.projects[config.project].architect.build.options.outputPath; 57 | config.style = { 58 | files: ngrConfig.projects[config.project].architect.build.options.styles 59 | } 60 | 61 | } 62 | 63 | 64 | } 65 | 66 | config.processRoot = processRoot;  67 | config.projectRoot = projectRoot; 68 | config.cliRoot = cliRoot; 69 | 70 | return config; 71 | 72 | } 73 | } 74 | 75 | module.exports = new Config(); -------------------------------------------------------------------------------- /src/compile/babel.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const exec = require('child_process').exec; 4 | const util = require('./../util.js'); 5 | const log = require('./../log.js'); 6 | const config = require('./../config'); 7 | const cli = require('./../../cli.config.json'); 8 | 9 | class BabelBuilder { 10 | 11 | constructor() { } 12 | 13 | compileUMD(outFile) { 14 | return new Promise((res, rej) => { 15 | 16 | let transpile = exec(path.normalize(config.processRoot + '/node_modules/.bin/babel') + 17 | ' --source-maps' + 18 | ' --presets=es2015-rollup ' + 19 | ' --plugins=transform-es2015-modules-commonjs ' + 20 | ' --module umd ' + 21 | path.normalize(outFile) + 22 | ' --out-file ' + path.normalize(outFile), (code, output, error) => { 23 | 24 | let fetchHelpers = exec(path.normalize(config.processRoot + '/node_modules/.bin/babel-external-helpers') + 25 | ' --output-type global ', { silent: true }, (code, output, error) => { 26 | 27 | fs.readFile(path.normalize(outFile), 'utf8', (err, contents) => { 28 | if (err) rej(err); 29 | if (!err) { 30 | contents = contents.replace("'use strict';", "'use strict';" + "\n" + output); 31 | fs.writeFile(path.normalize(outFile), contents, 'utf-8', () => { }); 32 | } 33 | }); 34 | }); 35 | 36 | log.alert('babel', 'transpiled', path.normalize(outFile)); 37 | res(outFile); 38 | 39 | }); 40 | 41 | 42 | }); 43 | } 44 | 45 | compileES5(outFile) { 46 | return new Promise((res, rej) => { 47 | 48 | 49 | let transpile = exec(path.normalize(config.processRoot + '/node_modules/.bin/babel') + 50 | ' --source-maps' + 51 | ' --presets=es2015-rollup ' + (outFile) + 52 | ' --out-file ' + (outFile), (code, output, error) => { 53 | 54 | let fetchHelpers = exec(path.normalize(config.processRoot + '/node_modules/.bin/babel-external-helpers') + 55 | ' --output-type global ', { silent: true }, (code, output, error) => { 56 | 57 | fs.readFile(path.normalize(outFile), 'utf8', (err, contents) => { 58 | if (!err) { 59 | contents = output + '\n' + contents; 60 | fs.writeFile(path.normalize(outFile), contents, 'utf-8', () => { }); 61 | } 62 | }); 63 | }); 64 | 65 | log.alert('babel', 'transpiled', path.normalize(outFile)); 66 | res(outFile); 67 | 68 | }); 69 | 70 | }); 71 | } 72 | 73 | } 74 | 75 | 76 | module.exports = BabelBuilder; 77 | -------------------------------------------------------------------------------- /src/scaffold/standalone/karma.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = function (config) { 4 | config.set({ 5 | 6 | basePath: './', 7 | 8 | frameworks: ['jasmine'], 9 | 10 | plugins: [ 11 | require('karma-jasmine'), 12 | require('karma-chrome-launcher'), 13 | require('karma-jasmine-html-reporter'), 14 | require('karma-coverage'), 15 | require('karma-remap-coverage') 16 | ], 17 | client: { 18 | clearContext: false 19 | }, 20 | 21 | 22 | remapCoverageReporter: { 23 | 'text-summary': null, // to show summary in console 24 | html: './coverage/html', 25 | cobertura: './coverage/cobertura.xml' 26 | }, 27 | 28 | files: [ 29 | // Polyfills. 30 | 'node_modules/core-js/client/shim.min.js', 31 | 32 | 'node_modules/reflect-metadata/Reflect.js', 33 | 34 | // System.js for module loading 35 | 'node_modules/systemjs/dist/system.src.js', 36 | 37 | // Zone.js dependencies 38 | 'node_modules/zone.js/dist/zone.js', 39 | 'node_modules/zone.js/dist/long-stack-trace-zone.js', 40 | 'node_modules/zone.js/dist/proxy.js', 41 | 'node_modules/zone.js/dist/sync-test.js', 42 | 'node_modules/zone.js/dist/jasmine-patch.js', 43 | 'node_modules/zone.js/dist/async-test.js', 44 | 'node_modules/zone.js/dist/fake-async-test.js', 45 | 46 | // RxJs. 47 | { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, 48 | { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false }, 49 | 50 | 51 | { pattern: 'karma-test-shim.js', included: true, watched: true }, 52 | 53 | // paths loaded via module imports 54 | // Angular itself 55 | { pattern: 'node_modules/@angular/**/*.js', included: false, watched: true }, 56 | { pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: true }, 57 | 58 | // Our built application code 59 | { pattern: 'build/**/*.js', included: false, watched: true }, 60 | 61 | // paths loaded via Angular's component compiler 62 | // (these paths need to be rewritten, see proxies section) 63 | { pattern: 'build/**/*.html', included: false, watched: true }, 64 | { pattern: 'build/**/*.css', included: false, watched: true }, 65 | 66 | // paths to support debugging with source maps in dev tools 67 | { pattern: 'src/**/*.ts', included: false, watched: true }, 68 | //{pattern: 'build/**/*.js.map', included: false, watched: false} 69 | ], 70 | 71 | // proxied base paths 72 | proxies: { 73 | // required for component assests fetched by Angular's compiler 74 | "/app/": "/build/src/" 75 | }, 76 | 77 | preprocessors: { 78 | 'build/src/app/shared/lib/**/*.component.js': ['coverage'] 79 | }, 80 | 81 | coverageReporter: { 82 | type: 'in-memory' 83 | }, 84 | 85 | reporters: ['progress', 'kjhtml', 'coverage', 'remap-coverage'], 86 | port: 9876, 87 | colors: true, 88 | logLevel: config.LOG_INFO, 89 | autoWatch: true, 90 | browsers: ['Chrome'], 91 | singleRun: false 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /src/style/postcss.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | const exec = require('child_process').exec; 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const log = require('./../log.js'); 6 | const config = require('./../config'); 7 | const cli = require('./../../cli.config.json'); 8 | 9 | class PostCSS { 10 | 11 | constructor(cssConfig) { 12 | this.cssConfig = cssConfig; 13 | } 14 | 15 | batch(fileList) { 16 | 17 | return new Promise((res) => { 18 | 19 | try { 20 | 21 | const files = fileList.map((filePath) => { 22 | return this.file(filePath); 23 | }); 24 | 25 | Promise.all(files).then((css) => { 26 | res(css); 27 | }); 28 | 29 | } 30 | catch (err) { 31 | err.service = 'postcss'; 32 | log.error(err); 33 | 34 | } 35 | 36 | }); 37 | 38 | } 39 | 40 | file(filePath) { 41 | 42 | // if (filePath.includes('out-css')) { // fixes issue with filePath in --watch 43 | // filePath = filePath.replace('out-css/', '').replace('out-css\\', ''); 44 | // } 45 | 46 | 47 | return new Promise((res) => { 48 | 49 | const globalBaseNames = config.projects[config.project].architect.build.options.styles.map((stylePath) => { 50 | return path.dirname(stylePath); 51 | }).filter((value, index, self) => { 52 | return self.indexOf(value) === index; 53 | }); 54 | 55 | const isGlobal = new RegExp(globalBaseNames.join('|')).test(filePath); 56 | 57 | let outFile = filePath; 58 | 59 | if (isGlobal) { 60 | 61 | globalBaseNames.forEach((baseName) => { 62 | if (outFile.includes(baseName)) { 63 | outFile = path.join(this.cssConfig.dist, outFile).replace('src/', '').replace('src\\', ''); 64 | } 65 | }) 66 | 67 | } 68 | 69 | // outFile = outFile.replace('scss', 'css'); 70 | 71 | if (!fs.existsSync(path.normalize(outFile.substring(0, outFile.replace(/\\/g, "/").lastIndexOf("/"))))) { 72 | mkdir('-p', path.normalize(outFile.substring(0, outFile.replace(/\\/g, "/").lastIndexOf("/")))); 73 | } 74 | 75 | exec(path.normalize(path.join(config.projectRoot, 'node_modules/.bin/postcss')) + 76 | ' ' + outFile + 77 | (this.cssConfig.sourceMap === false ? ' --no-map' : '') + 78 | ' --env ' + (cli.env) + 79 | ' --output ' + outFile, 80 | { silent: true }, (error, stdout, stderr) => { 81 | 82 | if (stderr) { 83 | //stderr.service = 'postcss'; 84 | log.error(stderr); 85 | } 86 | 87 | if (res) { 88 | res(filePath, outFile); 89 | } 90 | 91 | }); 92 | 93 | }); 94 | 95 | } 96 | 97 | } 98 | 99 | 100 | module.exports = PostCSS; 101 | -------------------------------------------------------------------------------- /src/watch.js: -------------------------------------------------------------------------------- 1 | 2 | const findup = require('findup'); 3 | const path = require('path'); 4 | const utils = require('./util'); 5 | const chokidar = require('chokidar'); 6 | const SassBuilder = require('./style/sass.js'); 7 | const PostCSSBuilder = require('./style/postcss.js'); 8 | const TSBuilder = require('./compile/tsc.js'); 9 | const config = require('./config'); 10 | const util = require('./util'); 11 | const log = require('./log'); 12 | const cli = require('./../cli.config.json'); 13 | 14 | let = lastPath = ''; 15 | 16 | class Watcher { 17 | constructor() { 18 | 19 | const sassBuilder = new SassBuilder({ dist: config.build }); 20 | const postcssBuilder = new PostCSSBuilder({ dist: config.build, sourceMap: (cli.build === 'dev') ? false : true }); 21 | const jitBuilder = new TSBuilder(); 22 | const watcher = chokidar.watch([config.src], { 23 | ignored: /[\/\\]\./, 24 | persistent: true 25 | }).on('change', filePath => { 26 | 27 | if (filePath.includes('out-css')) { 28 | return; 29 | } 30 | 31 | if (cli.program.verbose) log.message(filePath + ' changed'); 32 | 33 | if (filePath.includes(path.join(config.src, 'public'))) { 34 | this.updatePublic(filePath); 35 | } 36 | else if (filePath.indexOf('.scss') > -1) { 37 | 38 | (async () => { 39 | 40 | const sass = await sassBuilder.file(filePath); 41 | 42 | if (Array.isArray(sass)) { 43 | const postcss = await postcssBuilder.batch(sass); 44 | log.cancelError('sass'); 45 | log.cancelError('postcss'); 46 | // log.success('libsass and postcss compiled', ['sass', 'postcss']); 47 | } else { 48 | const postcss = await postcssBuilder.file(sass); 49 | log.cancelError('sass'); 50 | log.cancelError('postcss'); 51 | //log.success('libsass and postcss compiled', ['sass', 'postcss']); 52 | } 53 | 54 | 55 | })(); 56 | } 57 | else if (filePath.indexOf('.ts') > -1 && cli.build === 'jit') { 58 | jitBuilder.compile(path.join('src', 'tsconfig.' + cli.build + '.json')); 59 | } 60 | else if (filePath.indexOf('.html') > -1 && cli.build === 'jit') { 61 | util.copyFile(filePath, path.join(config.build, filePath)); 62 | } 63 | 64 | if (util.hasHook('watch') && config.projects[config.project].architect.build.hooks[cli.build].watch.src) { 65 | config.projects[config.project].architect.build.hooks[cli.build].watch.src(filePath); 66 | } 67 | 68 | }).on('unlink', filePath => log.warn(filePath, 'has been removed')) 69 | .on('error', error => log.warn('ERROR:', error)); 70 | //.on('ready', error => log.message('listening for changes')); 71 | 72 | return watcher; 73 | 74 | } 75 | 76 | updatePublic(filePath) { 77 | 78 | if (filePath.includes(path.join(config.src, 'public', 'index.html'))) { 79 | util.formatIndex(path.normalize(config.src + '/public/index.html')); 80 | } else { 81 | util.copyFile(filePath, path.join(config.build, filePath.replace(path.normalize('src/public/'), ''))); 82 | } 83 | 84 | } 85 | } 86 | 87 | module.exports = Watcher; 88 | -------------------------------------------------------------------------------- /src/generate/lib.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const log = require('./../log'); 5 | const util = require('./../util'); 6 | const cli = require('./../../cli.config.json'); 7 | const config = require('./../config'); 8 | const Scaffold = require('./../scaffold/index'); 9 | const Generator = require('./index'); 10 | 11 | class LibraryGenerator extends Generator { 12 | 13 | constructor() { 14 | super(); 15 | this.folderName = this.name.includes('/') ? this.name.split('/')[1] : this.name; 16 | this.addProjectToConfig = new Scaffold().addProjectToConfig; 17 | } 18 | 19 | copy() { 20 | util.copyDir(path.join(config.cliRoot, 'src', 'scaffold', 'lib'), path.join(this.outputPath, this.name)); 21 | } 22 | 23 | list(directoryPath) { 24 | 25 | ls(directoryPath).forEach((file) => { 26 | let depth = this.directoryDepth; 27 | if (this.name.includes('/')) { 28 | depth = depth + 1; 29 | } 30 | if (fs.statSync(path.join(directoryPath, file)).isFile()) { 31 | if (!path.join(directoryPath, file).includes('tsconfig.lib.json')) { 32 | depth = depth + 1; 33 | } 34 | this.replacePathInFile(path.join(directoryPath, file), depth); 35 | this.replaceFolderNameInFile(path.join(directoryPath, file)); 36 | this.replaceNameInFile(path.join(directoryPath, file)); 37 | this.replaceProjectPathInFile(path.join(directoryPath, file)); 38 | } 39 | else if (fs.statSync(path.join(directoryPath, file)).isDirectory()) { 40 | this.list(path.join(directoryPath, file)); 41 | } 42 | }); 43 | } 44 | 45 | replacePathInFile(filePath, depth) { 46 | let relativePath = ''; 47 | for (let i = 0; i < depth; i++) { 48 | relativePath += '../'; 49 | } 50 | sed('-i', /{{relativePath}}/g, relativePath, path.normalize(filePath)); 51 | } 52 | 53 | replaceFolderNameInFile(filePath) { 54 | sed('-i', /{{folderName}}/g, this.folderName, path.normalize(filePath)); 55 | } 56 | 57 | replaceNameInFile(filePath) { 58 | sed('-i', /{{projectName}}/g, this.name, path.normalize(filePath)); 59 | } 60 | 61 | replaceProjectPathInFile(filePath) { 62 | sed('-i', /{{projectPath}}/g, this.srcPath, path.normalize(filePath)); 63 | } 64 | 65 | getFileDirectoryDepth(filePath) { 66 | let directoryCount = 0; 67 | if (this.outputPath.match(/\//g)) { 68 | directoryCount = filePath.match(/\//g).length; 69 | } 70 | if (this.outputPath.match(/\\/g)) { 71 | directoryCount = filePath.replace(':\\\\', '\\').match(/\\/g).length; 72 | } 73 | return directoryCount; 74 | } 75 | 76 | init() { 77 | 78 | log.message(config.projectRoot + ' '+ this.outputPath); 79 | this.srcPath = this.outputPath.replace(config.projectRoot, '').slice(1).replace(/\\/gi, '/') + '/'+ this.name; 80 | 81 | this.directoryDepth = this.getFileDirectoryDepth(this.outputPath) - 82 | this.getFileDirectoryDepth(config.projectRoot) + 1; 83 | 84 | mkdir('-p', path.join(this.outputPath, this.name)); 85 | this.copy(); 86 | this.list(path.join(this.outputPath, this.name)); 87 | this.addProjectToConfig(this.name, { 88 | root: this.srcPath, 89 | projectType: 'library', 90 | configFile: 'lib.config.json' 91 | }); 92 | log.spinner.stop(); 93 | 94 | } 95 | 96 | } 97 | 98 | module.exports = LibraryGenerator; -------------------------------------------------------------------------------- /src/scaffold/root/ngr.config.js: -------------------------------------------------------------------------------- 1 | const spawn = require('child_process').spawn; 2 | const fs = require('fs'); 3 | 4 | module.exports = { 5 | defaultProject: '{{projectName}}', 6 | projects: { 7 | '{{projectName}}': { 8 | root: '', 9 | sourceRoot: 'src', 10 | projectType: 'application', 11 | configFile: '', 12 | architect: { 13 | build: { 14 | builder: 'angular-rollup', 15 | options: { 16 | outputPath: 'dist/{{projectName}}', 17 | styles: ['src/style/style.scss'], 18 | stylePreprocessorOptions: { 19 | includePaths: ['src/style'], 20 | outputStyle: 'expanded', 21 | sourceComments: true 22 | }, 23 | lib: { 24 | dev: [ 25 | "core-js/client/shim.min.js", 26 | "core-js/client/shim.min.js.map", 27 | "zone.js/dist/zone.min.js", 28 | "web-animations-js/web-animations.min.js", 29 | "web-animations-js/web-animations.min.js.map", 30 | "ie9-oninput-polyfill/ie9-oninput-polyfill.js", 31 | "angular-polyfills/dist/blob.js", 32 | "angular-polyfills/dist/classList.js", 33 | "angular-polyfills/dist/formdata.js", 34 | "angular-polyfills/dist/intl.js", 35 | "angular-polyfills/dist/typedarray.js", 36 | "console-polyfill/index.js", 37 | "systemjs/dist/system.js", 38 | "systemjs/dist/system.js.map", 39 | "reflect-metadata/Reflect.js", 40 | "tslib/tslib.js", 41 | "@angular", 42 | "rxjs" 43 | ], 44 | prod: [ 45 | "core-js/client/shim.min.js", 46 | "zone.js/dist/zone.min.js", 47 | "web-animations-js/web-animations.min.js", 48 | "ie9-oninput-polyfill/ie9-oninput-polyfill.js", 49 | "angular-polyfills/dist/blob.js", 50 | "angular-polyfills/dist/classList.js", 51 | "angular-polyfills/dist/formdata.js", 52 | "angular-polyfills/dist/intl.js", 53 | "angular-polyfills/dist/typedarray.js", 54 | "console-polyfill/index.js", 55 | "systemjs/dist/system.js" 56 | ], 57 | src: "node_modules", 58 | dist: "dist/{{projectName}}/lib" 59 | } 60 | 61 | }, 62 | hooks: { 63 | prod: { 64 | pre: () => { 65 | return new Promise((res) => { 66 | // put togic in here for before the production build 67 | res(); 68 | }); 69 | }, 70 | post: () => { 71 | return new Promise((res) => { 72 | // put togic in here for after the production build 73 | res(); 74 | }); 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/scaffold/src/style/util/_mixins.scss: -------------------------------------------------------------------------------- 1 | //----------------------------------------- 2 | // Responsive 3 | //----------------------------------------- 4 | 5 | $sm-breakpoint: 0px; 6 | $md-breakpoint: 768px; 7 | $lg-breakpoint: 1025px; 8 | $xl-breakpoint: 1441px; 9 | 10 | 11 | //----------------------------------------- 12 | // Media Queries 13 | //----------------------------------------- 14 | 15 | // Use these mixins to ensure similar media queries are used throughout the project, mqpacker will collapse all instances into one block 16 | 17 | @mixin media-sm { 18 | @media (max-width: #{$md-breakpoint - 1px}) { 19 | @content; 20 | } 21 | } 22 | 23 | @mixin media-md { 24 | @media (min-width: #{$md-breakpoint}) and (max-width: #{$lg-breakpoint - 1px}) { 25 | @content; 26 | } 27 | } 28 | 29 | @mixin media-lg { 30 | @media (min-width: #{$lg-breakpoint}) and (max-width: #{$xl-breakpoint - 1px}) { 31 | @content; 32 | } 33 | } 34 | 35 | @mixin media-xl { 36 | @media (min-width: #{$xl-breakpoint} ) { 37 | @content; 38 | } 39 | } 40 | 41 | 42 | 43 | //----------------------------------------- 44 | // Font Weight 45 | //----------------------------------------- 46 | @mixin font-weight($weight) { 47 | font-weight: $weight; 48 | } 49 | 50 | // font anti-aliasing 51 | %anti-aliasing { 52 | -webkit-font-smoothing: antialiased; 53 | -moz-osx-font-smoothing: grayscale; 54 | } 55 | 56 | 57 | //----------------------------------------- 58 | // Font Size 59 | //----------------------------------------- 60 | 61 | /** 62 | * Convert font-size from px to rem with px fallback 63 | * 64 | * @param $size - the value in pixel you want to convert 65 | * 66 | * e.g. p {@include fontSize(12px);} 67 | * 68 | */ 69 | 70 | // Function for converting a px based font-size to rem. 71 | @function calculate-rem($size) { 72 | $remSize: $size / 16px; 73 | //Default font size on html element is 100%, equivalent to 16px; 74 | @return #{$remSize}rem; 75 | } 76 | 77 | // Mixin that will include the fall back px declaration as well as the calculated rem value. 78 | @mixin font-size($size) { 79 | font-size: $size; 80 | font-size: calculate-rem($size); 81 | } 82 | 83 | 84 | //----------------------------------------- 85 | // Cross Browser CSS Transforms 86 | //----------------------------------------- 87 | 88 | 89 | @function str-replace($string, $search, $replace: '') { 90 | $index: str-index($string, $search); 91 | @if $index { 92 | @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); 93 | } 94 | @return $string; 95 | } 96 | 97 | @mixin transform2($trans...) { 98 | $fallback: unquote(str-replace(str-replace("#{$trans}", '3d', ''), ', 0)', ')')); 99 | -ms-transform: $fallback; 100 | 101 | $prefixes: (ms, moz, o, webkit); 102 | @each $prefix in $prefixes { 103 | #{'-' + $prefix + '-transform'}: $trans; 104 | } 105 | 106 | transform: $trans; 107 | } 108 | 109 | 110 | //----------------------------------------- 111 | // Layout 112 | //----------------------------------------- 113 | @mixin vertical-align { 114 | position: relative; 115 | top: 50%; 116 | -webkit-transform: translateY(-50%); 117 | -ms-transform: translateY(-50%); 118 | transform: translateY(-50%); 119 | } 120 | 121 | @mixin horizontal-align { 122 | position: relative; 123 | left: 50%; 124 | -webkit-transform: translateX(-50%); 125 | -ms-transform: translateX(-50%); 126 | transform: translateX(-50%); 127 | } 128 | 129 | //----------------------------------------- 130 | // clearfix 131 | //----------------------------------------- 132 | 133 | 134 | @mixin clearfix { 135 | &:after { 136 | content: ""; 137 | display: table; 138 | clear: both; 139 | } 140 | } 141 | 142 | //----------------------------------------- 143 | // Reset 144 | //----------------------------------------- 145 | 146 | @mixin ul-reset() { 147 | padding: 0; 148 | margin: 0; 149 | list-style: none; 150 | -webkit-margin-start: 0px; 151 | -webkit-margin-end: 0px; 152 | -webkit-padding-start: 0px; 153 | -webkit-margin-before: 0px; 154 | -webkit-margin-after: 0px; 155 | 156 | } 157 | 158 | -------------------------------------------------------------------------------- /src/compile/tsc.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const exec = require('child_process').exec; 4 | const util = require('./../util.js'); 5 | const log = require('./../log.js'); 6 | const config = require('./../config'); 7 | const cli = require('./../../cli.config.json'); 8 | 9 | class TSBuilder { 10 | 11 | constructor() {} 12 | 13 | compile(tsConfigPath) { 14 | 15 | return new Promise((res) => { 16 | 17 | let hasCompiled = false; 18 | log.process('typescript'); 19 | 20 | let tsc = exec(path.normalize(path.resolve('node_modules', '.bin', 'tsc') + 21 | ' -p ' + tsConfigPath), {}, function (error, stdout, stderr) { 22 | log.stop('typescript'); 23 | if (error) { 24 | log.warn(stdout); 25 | } else { 26 | log.message('Compilation complete.', ['TypeScript']); 27 | res('done'); 28 | } 29 | }); 30 | 31 | }); 32 | } 33 | 34 | 35 | compileToCommonJS(filePath, outFile) { 36 | 37 | const tscPath = path.join(config.projectRoot, 'node_modules', '.bin', 'tsc'); 38 | return new Promise((res) => { 39 | 40 | if(!outFile) outFile = filePath.replace('.ts', '.js'); 41 | 42 | log.process('typescript'); 43 | 44 | exec(`${tscPath} ${filePath} --outFile ${outFile} --target es5 --module commonjs --emitDecoratorMetadata true --experimentalDecorators true --sourceMap true --moduleResolution node --typeRoots node --lib dom,es2017`, 45 | { silent: true }, 46 | (error) => { 47 | log.stop('typescript'); 48 | if (error.killed) { 49 | log.error(error); 50 | } else { 51 | res(); 52 | } 53 | }); 54 | 55 | }); 56 | } 57 | 58 | compileMain() { 59 | 60 | return new Promise((res, rej) => { 61 | 62 | const outFile = path.join(config.projectRoot, config.build, 'src', 'main.ts'); 63 | const tscPath = path.join(config.projectRoot, 'node_modules', '.bin', 'tsc'); 64 | 65 | fs.readFile(path.join(config.projectRoot, 'src', 'main.ts'), 'utf8', (err, contents) => { 66 | 67 | if (!err) { 68 | contents = contents.replace("./out-tsc/" + config.src + "/app/app.module.ngfactory", config.src + "/app/app.module"); 69 | contents = contents.replace("import { enableProdMode } from '@angular/core';", ''); 70 | contents = contents.replace("enableProdMode();", ""); 71 | contents = contents.replace(/platformBrowser/g, "platformBrowserDynamic"); 72 | contents = contents.replace(/AppModuleNgFactory/g, "AppModule"); 73 | contents = contents.replace("@angular/platform-browser", "@angular/platform-browser-dynamic"); 74 | contents = contents.replace("bootstrapModuleFactory", "bootstrapModule"); 75 | fs.writeFile(outFile, contents, (err) => { 76 | if (!err) { 77 | 78 | let transpile = exec(`${tscPath} ${outFile} --target es5 --module commonjs --emitDecoratorMetadata true --experimentalDecorators true --sourceMap true --moduleResolution node --typeRoots node --lib dom,es2017`, 79 | { silent: true }, 80 | (error, stdout, stderr) => { 81 | rm(outFile); 82 | if(error.killed) { 83 | log.error(error); 84 | } else { 85 | res(); 86 | } 87 | }); 88 | } else { 89 | rej(err); 90 | } 91 | }); 92 | } else { 93 | rej(err); 94 | } 95 | 96 | }); 97 | 98 | }); 99 | } 100 | 101 | 102 | 103 | } 104 | 105 | 106 | module.exports = TSBuilder; 107 | -------------------------------------------------------------------------------- /src/build/jit.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | const chokidar = require('chokidar'); 5 | const Build = require('./index.js'); 6 | const SassBuilder = require('./../style/sass.js'); 7 | const PostCSSBuilder = require('./../style/postcss.js'); 8 | const TSBuilder = require('./../compile/tsc.js'); 9 | const Watcher = require('./../watch.js'); 10 | const util = require('./../util.js'); 11 | const log = require('./../log.js'); 12 | const config = require('./../config'); 13 | const cli = require('./../../cli.config.json'); 14 | 15 | class JitBuild extends Build { 16 | 17 | constructor() { 18 | super(); 19 | } 20 | 21 | init() { 22 | this.pre(); 23 | } 24 | 25 | 26 | build() { 27 | 28 | const env = 'dev'; 29 | const sassBuilder = new SassBuilder({ dist: config.build }); 30 | const postcssBuilder = new PostCSSBuilder({ dist: config.build, sourceMap: true }); 31 | const jitBuilder = new TSBuilder(); 32 | const libCheck = config.projects[config.project].architect.build.options.lib && config.projects[config.project].architect.build.options.lib[cli.env]; 33 | 34 | (async () => { 35 | const lib = await util.copyLib(libCheck ? config.projects[config.project].architect.build.options.lib[cli.env] : config.lib['dev'], 36 | libCheck ? config.projects[config.project].architect.build.options.lib.src : config.lib.src, 37 | libCheck ? config.projects[config.project].architect.build.options.lib.dist : config.lib.dist); 38 | })(); 39 | 40 | (async () => { 41 | const html = await util.copyBatch(ls(path.normalize(config.src + '/app/**/*.html')), config.build); 42 | const publicDir = await util.copyDir(path.normalize(config.src + '/public'), config.build); 43 | const template = await util.formatIndex(path.normalize(config.src + '/public/index.html')); 44 | })(); 45 | 46 | (async () => { 47 | const sass = await sassBuilder.batch(ls(path.normalize(config.src + '/**/*.scss'))); 48 | const postcss = await postcssBuilder.batch(sass); 49 | const src = await jitBuilder.compile(path.join('src', 'tsconfig.' + cli.build + '.json')); 50 | this.post(); 51 | })(); 52 | 53 | //if (!fs.existsSync(path.join(config.build, 'main.js'))) { 54 | (async () => { 55 | const main = await jitBuilder.compileMain().then((res) => { 56 | log.message('compiled main.js'); 57 | }); 58 | })(); 59 | //} 60 | 61 | } 62 | 63 | pre() { 64 | 65 | if (cli.program.clean === true) { 66 | util.cleanBuild(); 67 | } 68 | 69 | if (util.hasHook('pre')) { 70 | 71 | config.projects[config.project].architect.build.hooks[cli.env].pre(process.argv).then(() => { 72 | this.build(); 73 | }); 74 | 75 | } else { 76 | 77 | this.build(); 78 | 79 | } 80 | 81 | this.emitter.emit('hook', { 82 | payload: { 83 | step: 'pre' 84 | } 85 | }); 86 | 87 | } 88 | 89 | post() { 90 | 91 | if (cli.program.env) { 92 | cp(path.join(this.outputPath, 'src', 'environments', 'environment.' + cli.program.env + '.js'), path.join(this.outputPath, 'src', 'environments', 'environment.js')); 93 | cp(path.join(this.outputPath, 'src', 'environments', 'environment.' + cli.program.env + '.js.map'), path.join(this.outputPath, 'src', 'environments', 'environment.js.map')); 94 | cp(path.join(this.outputPath, 'src', 'environments', 'environment.' + cli.program.env + '.ngsummary.json'), path.join(this.outputPath, 'src', 'environments', 'environment.ngsummary.json')); 95 | } 96 | 97 | if (util.hasHook('post')) config.projects[config.project].architect.build.hooks[cli.env].post(process.argv); 98 | 99 | if (cli.program.watch === true) { 100 | const watcher = new Watcher(); 101 | } 102 | 103 | if (cli.program.watch === true && util.hasHook('watch') && config.projects[config.project].architect.build.hooks[cli.env].watch.dist) { 104 | 105 | const distWatcher = chokidar.watch([this.outputPath], { 106 | ignored: /[\/\\]\./, 107 | persistent: true 108 | }).on('change', filePath => { 109 | 110 | config.projects[config.project].architect.build.hooks[cli.env].watch.dist(filePath); 111 | 112 | }); 113 | 114 | } 115 | 116 | if (!util.hasArg('watch')) { 117 | log.break(); 118 | ls(this.outputPath).forEach((file) => { 119 | log.logFileStats(path.join(this.outputPath, file)); 120 | }); 121 | } 122 | 123 | if (util.hasArg('serve')) { 124 | util.serve(cli.program.watch); 125 | } 126 | 127 | this.emitter.emit('hook', { 128 | payload: { 129 | step: 'post' 130 | } 131 | }); 132 | 133 | 134 | } 135 | 136 | } 137 | 138 | module.exports = JitBuild; -------------------------------------------------------------------------------- /src/style/sass.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | 3 | const sass = require('node-sass'); 4 | const path = require('path'); 5 | const fs = require('fs'); 6 | const util = require('./../util.js'); 7 | const log = require('./../log.js'); 8 | const config = require('./../config'); 9 | const cli = require('./../../cli.config.json'); 10 | 11 | class Sass { 12 | constructor(sassConfig) { 13 | this.sassConfig = sassConfig; 14 | } 15 | 16 | batch(fileList) { 17 | 18 | if (!fs.existsSync(path.join(this.sassConfig.dist, 'style'))) { 19 | // TODO: figure out best way to handle use case without any global style 20 | mkdir('-p', path.join(this.sassConfig.dist, 'style')); 21 | } 22 | 23 | return new Promise(res => { 24 | try { 25 | 26 | const styles = config.projects[config.project].architect.build.options.styles; 27 | const globalFiles = styles.map((stylePath) => { 28 | return this.file(stylePath); 29 | }); 30 | 31 | const files = fileList.filter((filePath, index) => { 32 | return (filePath && filePath.replace(/^.*[\\\/]/, '')[0] !== '_' && 33 | styles.indexOf(filePath) === -1); 34 | }).map((filePath) => { 35 | return this.file(filePath) 36 | }); 37 | 38 | Promise.all(files.concat(globalFiles)).then((css) => { 39 | res(css); 40 | }); 41 | 42 | } catch (err) { 43 | err.service = 'sass'; 44 | log.error(err); 45 | } 46 | }); 47 | } 48 | 49 | file(filePath) { 50 | 51 | let env; 52 | 53 | if (cli.env === 'jit') { 54 | env = 'dev'; 55 | } else { 56 | env = cli.env; 57 | } 58 | 59 | const srcPath = util.getFilePath(filePath); 60 | const filename = util.getFileName(filePath); 61 | const styles = config.projects[config.project].architect.build.options.styles; 62 | const globalBaseNames = styles.map((stylePath) => { 63 | return path.dirname(stylePath); 64 | }).filter((value, index, self) => { 65 | return self.indexOf(value) === index; 66 | }); 67 | // determine if file is global or not, swap .scss to .css in filename 68 | const isGlobal = new RegExp(globalBaseNames.join('|')).test(filePath); 69 | let outFile = filePath; 70 | 71 | if (isGlobal) { 72 | 73 | globalBaseNames.forEach((baseName) => { 74 | if (outFile.includes(config.src)) { 75 | outFile = path.normalize(outFile.replace(config.src, this.sassConfig.dist)); 76 | } 77 | }) 78 | 79 | } 80 | 81 | let outFilePath = util.getFilePath(outFile); 82 | 83 | 84 | if (cli.env === 'dev' || cli.env === 'prod' || cli.env === 'lib') { 85 | outFilePath = util.getFilePath(outFile); 86 | } 87 | if (cli.env === 'jit' && srcPath.indexOf(config.src + '/style') === -1) { 88 | outFilePath = util.getFilePath(path.join(this.sassConfig.dist, outFile)); 89 | } 90 | 91 | outFile = path.join(outFilePath, filename.replace('scss', 'css')); 92 | 93 | // this file is global w/ underscore and should not be compiled, compile global files instead 94 | 95 | if (isGlobal && filename[0] === '_') { 96 | return Promise.all( 97 | styles.map(filePath => { 98 | return this.file(filePath); 99 | }) 100 | ); 101 | } 102 | 103 | // TODO: figure out better way to transform paths based on needs 104 | 105 | // if (cli.program.build === 'prod' && 106 | // isGlobal === false) { 107 | // outFilePath = path.join('out-tsc', outFilePath); 108 | // outFile = path.join('out-tsc', outFile); 109 | // } 110 | 111 | if (cli.program.build === 'lib' && 112 | isGlobal === true) { 113 | outFilePath = path.join(this.sassConfig.dist, outFilePath.replace('src/', '').replace('src\\', '')); 114 | outFile = path.join(outFilePath, path.basename(outFile)); 115 | } 116 | 117 | 118 | return new Promise(res => { 119 | 120 | const renderConfig = config.projects[config.project].architect.build.options.stylePreprocessorOptions; 121 | renderConfig.file = filePath; 122 | renderConfig.outFile = outFile; 123 | 124 | if (fs.existsSync(outFilePath) == false) { 125 | mkdir('-p', outFilePath); 126 | } 127 | 128 | if (env === 'dev') { 129 | renderConfig.sourceComments = true; 130 | } 131 | 132 | if (this.sassConfig.sourceMap) { 133 | renderConfig.sourceMap = this.sassConfig.sourceMap; 134 | } 135 | 136 | sass.render(renderConfig, (error, result) => { 137 | if (error) { 138 | log.line(); 139 | error.service = 'sass'; 140 | log.error(error); 141 | } else { 142 | fs.writeFile(outFile, result.css, err => { 143 | if (err) { 144 | log.line(); 145 | err.service = 'sass'; 146 | log.error(err); 147 | } 148 | if (res) { 149 | res(outFile); 150 | } 151 | }); 152 | } 153 | }); 154 | }); 155 | } 156 | } 157 | 158 | module.exports = Sass; 159 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('shelljs/global'); 4 | const findup = require('findup'); 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const colors = require('colors'); 8 | const program = require('commander'); 9 | const cliRoot = findup.sync(__dirname, 'package.json'); 10 | const processRoot = path.join(path.dirname(process.cwd()), path.basename(process.cwd())); 11 | const package = require(__dirname + '/package.json'); 12 | 13 | if (process.argv.indexOf('scaffold') > -1) { 14 | process.argv.push('--verbose'); 15 | } 16 | 17 | program 18 | .version(package.version) 19 | .usage('') 20 | .option('new [string]', 'scaffold new development environment in directory by name, i.e. ngr new my-app') 21 | .option('--src [string]', 'specify a path to an existing src folder') 22 | .option('--skip-install [bool]', 'prevents install during scaffold') 23 | .option('--yarn [bool]', 'use yarn instead of npm to install') 24 | .option('--prettier [bool]', 'scaffold a new workspace with prettier installed') 25 | .option('--ssl [bool]', 'scaffold a new workspace with https express server') 26 | .option('--angularVersion [string]', 'scaffold a new workspace with a specific version of angular') 27 | .option('build [env]', 'build the application') 28 | .option('--env [string]', 'use that particular environment.ts during the build, just like @angular/cli') 29 | .option('--clean [bool]', 'destroy the build folder prior to compilation, default for prod') 30 | .option('--watch [bool]', 'listen for changes in filesystem and rebuild') 31 | .option('--config [string]', 'path to configuration file for library build') 32 | .option('--deploy [bool]', 'call deploy build hook for library build') 33 | .option('--verbose [bool]', 'log all messages in list format') 34 | .option('--closure [bool]', 'bundle and optimize with closure compiler (default)') 35 | .option('--rollup [bool]', 'bundle with rollup and optimize with closure compiler') 36 | .option('--webpack [bool]', 'use @angular/cli to build') 37 | .option('--keepTempFiles [bool]', 'retain the /tmp and /out-tsc directories post production build') 38 | .option('g, generate [string]', 'generate schematics packaged with angular-rollup') 39 | .option('serve, --serve [bool]', 'spawn the local express server') 40 | .parse(process.argv); 41 | 42 | let cli = () => { 43 | let config = require('./src/config'); 44 | let util = require('./src/util'); 45 | let log = require('./src/log'); 46 | let Scaffold = require('./src/scaffold/index'); 47 | 48 | if (program.generate) { 49 | if (program.generate === 'library') program.generate = 'lib'; 50 | const Generator = require('./src/generate/' + program.generate + '.js'); 51 | let build = new Generator().init(); 52 | } 53 | 54 | if (program.build) { 55 | log.destroy(); 56 | const BuildScript = require('./src/build/' + program.build + '.js'); 57 | let build = new BuildScript().init(); 58 | } 59 | 60 | if (program.new) { 61 | log.destroy(); 62 | let scaffold = new Scaffold(program.new, path.join(processRoot, program.new)); 63 | scaffold.basic(); 64 | } 65 | 66 | if (program.serve && !program.build) { 67 | log.destroy(); 68 | util.serve(); 69 | } 70 | }; 71 | 72 | 73 | if (process.argv.indexOf('new') > -1) { 74 | if (fs.existsSync(path.join(processRoot, program.new))) { 75 | console.log(colors.red(program.new + ' already exists')); 76 | process.exit(1); 77 | } 78 | if (!fs.existsSync(path.join(processRoot, program.new))) mkdir(path.join(processRoot, program.new)); 79 | cp(path.join(cliRoot, 'src', 'scaffold', 'root', 'ngr.config.js'), path.join(processRoot, program.new)); 80 | } 81 | 82 | 83 | const env = program.env ? program.env : (program.build === 'dev' ? 'dev' : 'prod'); 84 | 85 | fs.writeFile( 86 | __dirname + '/cli.config.json', 87 | JSON.stringify( 88 | { 89 | env: env, 90 | program: program, 91 | build: program.build, 92 | projectRoot: program.new ? path.join(processRoot, program.new) : processRoot, 93 | }, 94 | null, 95 | 4 96 | ), 97 | 'utf-8', 98 | cli 99 | ); 100 | 101 | let exitHandler = (options, err) => { 102 | if (fs.existsSync(path.join('config', 'environments'))) { 103 | rm('-rf', path.join('src', 'environments')); 104 | cp('-R', path.join('config', 'environments'), 'src'); 105 | rm('-rf', path.join('config', 'environments')); 106 | } 107 | if (err && err !== 'SIGINT') { 108 | process.stdout.write('\n'); 109 | console.log(colors.red('NGR ERROR', err)); 110 | process.stdout.write('\n'); 111 | process.exit(1); 112 | } 113 | if (options.exit) { 114 | process.exit(0); 115 | } 116 | }; 117 | 118 | // do something when app is closing 119 | process.on('exit', exitHandler.bind(null, { cleanup: true })); 120 | 121 | // catches ctrl+c event 122 | process.on('SIGINT', exitHandler.bind(null, { exit: true })); 123 | 124 | // catches "kill pid" (for example: nodemon restart) 125 | process.on('SIGUSR1', exitHandler.bind(null, { exit: true })); 126 | process.on('SIGUSR2', exitHandler.bind(null, { exit: true })); 127 | 128 | //catches uncaught exceptions 129 | process.on('uncaughtException', exitHandler.bind(null, { exit: true })); 130 | 131 | process.on('unhandledRejection', err => { 132 | process.stdout.write('\n'); 133 | console.log(colors.red('NGR ERROR', err)) 134 | process.stdout.write('\n'); 135 | }); 136 | -------------------------------------------------------------------------------- /src/compile/ngc.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const colors = require('colors'); 4 | const exec = require('child_process').exec; 5 | const util = require('./../util.js'); 6 | const log = require('./../log.js'); 7 | const config = require('./../config'); 8 | const cli = require('./../../cli.config.json'); 9 | 10 | let interval; 11 | 12 | class AOTBuilder { 13 | 14 | constructor() { } 15 | 16 | compile(tsConfigPath) { 17 | 18 | return new Promise((res) => { 19 | 20 | let hasCompiled = false; 21 | 22 | if (util.hasArg('watch')) { 23 | 24 | log.process('@angular/compiler'); 25 | 26 | const ngc = exec(path.join(config.projectRoot, 'node_modules', '.bin', 'ngc') + ' -p ' + tsConfigPath + ' --watch', { silent: true }); 27 | 28 | ngc.stderr.on('data', (stderr) => { 29 | 30 | log.stop('@angular/compiler'); 31 | let hasLine = false; 32 | 33 | if (hasCompiled == false && stderr.includes('Compilation complete.')) { 34 | hasCompiled = true; 35 | if (!cli.program.verbose) log.destroy(); 36 | log.message('Compilation complete. Watching for file changes.', ['TypeScript']); 37 | res(); 38 | } else { 39 | this.handleError(stderr); 40 | } 41 | 42 | }); 43 | 44 | } else { 45 | 46 | log.process('@angular/compiler'); 47 | 48 | let ngc = exec(path.join(config.projectRoot, 'node_modules', '.bin', 'ngc') + ' -p ' + tsConfigPath, { silent: true }, (error, stdout, stderr) => { 49 | //if (config.build !== 'lib') clearInterval(interval); 50 | log.stop('@angular/compiler'); 51 | if (stderr) { 52 | this.handleError(stderr); 53 | } else { 54 | if (!cli.program.verbose) log.destroy(); 55 | log.message('Compilation complete.', ['TypeScript']); 56 | if (cli.build === 'dev') { 57 | log.break(); 58 | } 59 | 60 | res(); 61 | } 62 | }); 63 | 64 | 65 | } 66 | 67 | }); 68 | } 69 | 70 | handleError(stderr) { 71 | 72 | if (stderr.includes('Compilation complete.')) { 73 | log.success(stderr, ['TypeScript']); 74 | } 75 | else if (stderr === ': Compilation failed. Watching for file changes.') { 76 | log.fail(stderr); 77 | } 78 | else if (stderr.includes('File change')) { 79 | log.success(stderr, ['TypeScript']); 80 | } 81 | else { 82 | 83 | if (stderr.split('\n').length > 0) { 84 | //if (!hasLine) log.line(); 85 | //hasLine = true; 86 | let tsError = stderr.split('\n').filter((e) => { 87 | return e.length > 0; 88 | }).forEach((e) => { 89 | if (e.includes('error TS')) { 90 | //console.log('TS ERROR:', e); 91 | log.formatTSError(e) 92 | } 93 | }); 94 | 95 | } 96 | 97 | if (stderr.split(/\n:\s/g).length > 0) { 98 | //if (!hasLine) log.line(); 99 | //hasLine = true; 100 | let templateErr = stderr.split(/\n:\s/g).filter((e) => { 101 | return e.includes('error TS') === false; 102 | }).forEach((e) => { 103 | //console.log('Template ERROR:', e); 104 | log.formatTemplateError(e) 105 | }); 106 | 107 | } 108 | 109 | } 110 | 111 | } 112 | 113 | 114 | compileMain() { 115 | 116 | return new Promise((res, rej) => { 117 | let inFile = path.join(config.src, 'main.ts'); 118 | let outFile = path.join(config.projectRoot, config.build, 'main.ts'); 119 | 120 | const tscPath = path.join(config.projectRoot, 'node_modules', '.bin', 'tsc'); 121 | const tsConfig = JSON.parse(fs.readFileSync(path.join(config.projectRoot, 'src', 'tsconfig.' + cli.build + '.json'), 'utf8')); 122 | const modulePattern = tsConfig.compilerOptions.module; 123 | const jsTarget = tsConfig.compilerOptions.target; 124 | 125 | if (cli.build === 'prod') { 126 | outFile = path.join('out-tsc/tmp/main.ts'); 127 | } 128 | 129 | fs.readFile(inFile, 'utf8', (err, contents) => { 130 | if (!err) { 131 | 132 | contents = contents.replace(/platformBrowserDynamic/g, 'platformBrowser'); 133 | contents = contents.replace(/platform-browser-dynamic/g, 'platform-browser'); 134 | contents = contents.replace(/bootstrapModule/g, 'bootstrapModuleFactory'); 135 | contents = contents.replace(/AppModule/g, 'AppModuleNgFactory'); 136 | if (cli.build === 'dev') { 137 | contents = contents.replace('./app/app.module', './src/app/app.module.ngfactory'); 138 | } if (cli.build === 'prod') { 139 | contents = contents.replace('./app/app.module', './app/app.module.ngfactory'); 140 | } 141 | 142 | fs.writeFile(outFile, contents, (err) => { 143 | if (!err) { 144 | 145 | let transpile = exec(`${tscPath} ${outFile} --target ${jsTarget} --module ${modulePattern} --emitDecoratorMetadata true --experimentalDecorators true --sourceMap true --moduleResolution node --typeRoots node --lib dom,es2017`, 146 | { silent: true }, 147 | (error, stdout, stderr) => { 148 | rm(outFile); 149 | if (error && error.killed) { 150 | log.formatTSError(error); 151 | } else { 152 | res(); 153 | } 154 | }); 155 | } 156 | else { 157 | rej(err); 158 | } 159 | }); 160 | } else { 161 | rej(err); 162 | } 163 | 164 | }); 165 | 166 | }); 167 | } 168 | 169 | 170 | 171 | } 172 | 173 | 174 | module.exports = AOTBuilder; 175 | -------------------------------------------------------------------------------- /src/build/dev.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const chokidar = require('chokidar'); 4 | const spawn = require('child_process').spawn; 5 | const Build = require('./index.js'); 6 | const SassBuilder = require('./../style/sass.js'); 7 | const PostCSSBuilder = require('./../style/postcss.js'); 8 | const AOTBuilder = require('./../compile/ngc.js'); 9 | const Watcher = require('./../watch.js'); 10 | const util = require('./../util.js'); 11 | const log = require('./../log.js'); 12 | const config = require('./../config'); 13 | const cli = require('./../../cli.config.json'); 14 | 15 | class DevBuild extends Build { 16 | constructor() { 17 | super(); 18 | } 19 | 20 | init() { 21 | this.pre(); 22 | } 23 | 24 | build() { 25 | const sassBuilder = new SassBuilder({ dist: config.build }); 26 | const postcssBuilder = new PostCSSBuilder({ dist: config.build, sourceMap: true }); 27 | const aotBuilder = new AOTBuilder(); 28 | const libCheck = 29 | config.projects[config.project].architect.build.options.lib && 30 | config.projects[config.project].architect.build.options.lib[cli.build]; 31 | 32 | (async () => { 33 | const lib = await util.copyLib( 34 | libCheck ? config.projects[config.project].architect.build.options.lib[cli.build] : config.lib['dev'], 35 | libCheck ? config.projects[config.project].architect.build.options.lib.src : config.lib.src, 36 | libCheck ? config.projects[config.project].architect.build.options.lib.dist : config.lib.dist 37 | ); 38 | })(); 39 | 40 | (async () => { 41 | const publicDir = await util.copyDir(path.normalize(config.src + '/public'), config.build); 42 | const template = await util.formatIndex(path.normalize(config.src + '/public/index.html')); 43 | })(); 44 | 45 | (async () => { 46 | const sass = await sassBuilder.batch(ls(path.normalize(config.src + '/**/*.scss'))); 47 | const postcss = await postcssBuilder.batch(sass); 48 | await log.message('styled components'); 49 | const main = await aotBuilder.compileMain(); 50 | await log.message('compiled main.js'); 51 | const src = await aotBuilder.compile(path.join('src', 'tsconfig.' + cli.build + '.json')); 52 | this.post(); 53 | })(); 54 | } 55 | 56 | pre() { 57 | let build = () => { 58 | //cp(path.normalize('config/postcss.' + cli.build + '.js'), 'postcss.config.js'); 59 | 60 | if (util.hasHook('pre')) { 61 | config.projects[config.project].architect.build.hooks[cli.build].pre(process.argv).then(() => { 62 | if (cli.program.webpack === true) { 63 | spawn('ng', ['serve'], { shell: true, stdio: 'inherit' }); 64 | } else { 65 | this.build(); 66 | } 67 | }); 68 | } else { 69 | if (cli.program.webpack === true) { 70 | spawn('ng', ['serve'], { shell: true, stdio: 'inherit' }); 71 | } else { 72 | this.build(); 73 | } 74 | } 75 | }; 76 | 77 | if (cli.program.clean) { 78 | util.cleanBuild().then(() => { 79 | build(); 80 | }); 81 | } else { 82 | build(); 83 | } 84 | 85 | this.emitter.emit('hook', { 86 | payload: { 87 | step: 'pre' 88 | } 89 | }); 90 | } 91 | 92 | post() { 93 | 94 | 95 | if (!fs.existsSync(path.join(this.outputPath, 'environments'))) { 96 | mkdir('-p', path.join(this.outputPath, 'environments')); 97 | } 98 | 99 | if (cli.env === 'dev') { 100 | cp( 101 | path.join(this.outputPath, 'src', 'environments', 'environment.js'), 102 | path.join(this.outputPath, 'environments', 'environment.js') 103 | ); 104 | cp( 105 | path.join(this.outputPath, 'src', 'environments', 'environment.js.map'), 106 | path.join(this.outputPath, 'environments', 'environment.js.map') 107 | ); 108 | if (fs.existsSync(path.join(this.outputPath, 'src', 'environments', 'environment.ngsummary.json'))) { 109 | cp( 110 | path.join(this.outputPath, 'src', 'environments', 'environment.ngsummary.json'), 111 | path.join(this.outputPath, 'environments', 'environment.ngsummary.json') 112 | ); 113 | } 114 | } else { 115 | cp( 116 | path.join(this.outputPath, 'src', 'environments', 'environment.' + cli.env + '.js'), 117 | path.join(this.outputPath, 'environments', 'environment.js') 118 | ); 119 | 120 | fs.readFile(path.join(this.outputPath, 'environments', 'environment.js'), 'utf-8', (err, stdout) => { 121 | stdout = stdout.replace(`environment.${cli.env}.js`, 'environment.js'); 122 | fs.writeFile(path.join(this.outputPath, 'environments', 'environment.js'), stdout, 'utf-8', () => {}); 123 | }); 124 | 125 | cp( 126 | path.join(this.outputPath, 'src', 'environments', 'environment.' + cli.env + '.js.map'), 127 | path.join(this.outputPath, 'environments', 'environment.js.map') 128 | ); 129 | 130 | fs.readFile(path.join(this.outputPath, 'environments', 'environment.js.map'), 'utf-8', (err, stdout) => { 131 | stdout = stdout.replace(`environment.${cli.env}.ts`, 'environment.ts'); 132 | stdout = stdout.replace(`environment.${cli.env}.js`, 'environment.js'); 133 | fs.writeFile(path.join(this.outputPath, 'environments', 'environment.js.map'), stdout, 'utf-8', () => {}); 134 | }); 135 | 136 | cp( 137 | path.join(this.outputPath, 'src', 'environments', 'environment.' + cli.env + '.ngsummary.json'), 138 | path.join(this.outputPath, 'environments', 'environment.ngsummary.json') 139 | ); 140 | 141 | fs.readFile(path.join(this.outputPath, 'environments', 'environment.ngsummary.json'), 'utf-8', (err, stdout) => { 142 | stdout = stdout.replace(`environment.${cli.env}`, 'environment'); 143 | fs.writeFile(path.join(this.outputPath, 'environments', 'environment.ngsummary.json'), stdout, 'utf-8', () => {}); 144 | }); 145 | 146 | } 147 | 148 | if (util.hasHook('post')) config.projects[config.project].architect.build.hooks[cli.build].post(process.argv); 149 | 150 | if (cli.program.watch === true) { 151 | const watcher = new Watcher(); 152 | } 153 | 154 | if (cli.program.watch === true && util.hasHook('watch') && config.projects[config.project].architect.build.hooks[cli.build].watch.dist) { 155 | const distWatcher = chokidar 156 | .watch([this.outputPath], { 157 | ignored: /[\/\\]\./, 158 | persistent: true, 159 | }) 160 | .on('change', filePath => { 161 | config.projects[config.project].architect.build.hooks[cli.build].watch.dist(filePath); 162 | }); 163 | } 164 | 165 | if (!util.hasArg('watch')) { 166 | log.break(); 167 | log.buildStats(this.startTime); 168 | } 169 | 170 | if (util.hasArg('serve')) { 171 | util.serve(cli.program.watch); 172 | } 173 | this.emitter.emit('hook', { 174 | payload: { 175 | step: 'post' 176 | } 177 | }); 178 | } 179 | } 180 | 181 | module.exports = DevBuild; 182 | -------------------------------------------------------------------------------- /src/build/prod.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | const spawn = require('child_process').spawn; 5 | const Build = require('./index.js'); 6 | const SassBuilder = require('./../style/sass.js'); 7 | const PostCSSBuilder = require('./../style/postcss.js'); 8 | const AOTBuilder = require('./../compile/ngc.js'); 9 | const ClosureBuilder = require('./../bundle/closure.js'); 10 | const RollupBuilder = require('./../bundle/rollup.js'); 11 | const util = require('./../util.js'); 12 | const log = require('./../log.js'); 13 | const config = require('./../config'); 14 | const cli = require('./../../cli.config.json'); 15 | const buildOptimizer = require('@angular-devkit/build-optimizer').buildOptimizer; 16 | 17 | 18 | class ProdBuild extends Build { 19 | 20 | constructor() { 21 | super(); 22 | } 23 | 24 | init() { 25 | this.pre(); 26 | } 27 | 28 | build() { 29 | 30 | const sassBuilder = new SassBuilder({ dist: config.build, sourceMap: false }); 31 | const postcssBuilder = new PostCSSBuilder({ dist: config.build, sourceMap: false }); 32 | const aotBuilder = new AOTBuilder(); 33 | const closureBuilder = new ClosureBuilder(); 34 | const rollupBuilder = new RollupBuilder(); 35 | const libCheck = config.projects[config.project].architect.build.options.lib && config.projects[config.project].architect.build.options.lib[cli.build]; 36 | 37 | (async () => { 38 | const publicDir = await util.copyDir(path.normalize(config.src + '/public'), config.build); 39 | const template = await util.formatIndex(path.normalize(config.src + '/public/index.html')); 40 | const vendor = await util.formatVendorScripts(libCheck ? config.projects[config.project].architect.build.options.lib[cli.build] : config.lib['prod'], 41 | libCheck ? config.projects[config.project].architect.build.options.lib.src : config.lib.src, 42 | libCheck ? config.build : config.build); 43 | const concatVendor = await util.concatVendorScripts(libCheck ? config.build : config.build); 44 | })(); 45 | 46 | (async () => { 47 | // const copyMain = await cp('main.ts', 'main.js'); 48 | const copy = await cp('-R', path.normalize(config.src + '/'), path.normalize('./tmp')); 49 | // // remove moduleId prior to ngc build. TODO: look for another method. 50 | // const stripModuleId = await ls(path.normalize('out-tsc/**/*.ts')).forEach(function (file) { 51 | // sed('-i', /^.*moduleId: module.id,.*$/, '', file); 52 | // }); 53 | if (ls(path.normalize('tmp/**/*.scss')).length > 0) { 54 | 55 | const sass = await sassBuilder.batch(ls(path.normalize('./tmp/**/*.scss'))); 56 | await postcssBuilder.batch(sass); 57 | await this.transformSCSSPathstoCSS(); 58 | 59 | } 60 | else if (ls(path.normalize('tmp/**/*.css')).length > 0) { 61 | 62 | const cssFileList = ls(path.normalize('tmp/**/*.css')); 63 | await postcssBuilder.batch(cssFileList); 64 | 65 | } 66 | 67 | // rewrite tsconfig to compile tmp instead of src 68 | await sed('-i', 'src/', 'tmp/', path.join('tmp', 'tsconfig.' + cli.build + '.json')); 69 | // use tsconfig copied to tmp instead of src 70 | const src = await aotBuilder.compile(path.join('tmp', 'tsconfig.' + cli.build + '.json')); 71 | 72 | const main = await aotBuilder.compileMain(); 73 | await log.message('copied main.ts'); 74 | const buildOptimize = await ls(path.normalize('out-tsc/**/*.component.js')).forEach(function (file) { 75 | let content = fs.readFileSync(file, 'utf-8'); 76 | fs.writeFileSync(file, buildOptimizer({ content: content }).content); 77 | }); 78 | 79 | await mv(path.normalize('out-tsc/tmp'), path.normalize('out-tsc/src')); 80 | 81 | if (cli.program.rollup) { 82 | const prepRxjs = await this.buildRxjsFESM(); 83 | const bundle = await rollupBuilder.bundle(path.join(config.projectRoot, 'rollup.config.js')); 84 | const optimize = await closureBuilder.bundle(); 85 | } else { 86 | // use fesm instead for closure compiler, results in smaller bundles 87 | const prepRxjs = await this.buildRxjsFESM(); 88 | const bundle = await closureBuilder.bundle(); 89 | } 90 | 91 | this.post(); 92 | 93 | })(); 94 | 95 | 96 | } 97 | 98 | pre() { 99 | 100 | let build = () => { 101 | 102 | cp('-R', path.join('src', 'environments'), 'config'); 103 | rm('-rf', path.join('tmp')); 104 | rm('-f', path.join('src', 'environments', 'environment.ts')); 105 | if (cli.env === 'dev') { 106 | cp(path.join('config', 'environments', 'environment.ts'), path.join('src', 'environments', 'environment.ts')); 107 | } else { 108 | cp(path.join('config', 'environments', 'environment.' + cli.env + '.ts'), path.join('src', 'environments', 'environment.ts')); 109 | } 110 | if (util.hasHook('pre')) { 111 | 112 | config.projects[config.project].architect.build.hooks[cli.env].pre(process.argv).then(() => { 113 | if (cli.program.webpack === true) { 114 | exec('ng build --prod', { shell: true, stdio: 'inherit' }, () => { 115 | this.post(); 116 | }); 117 | } else { 118 | this.build(); 119 | } 120 | }); 121 | 122 | } else { 123 | 124 | if (cli.program.webpack === true) { 125 | exec('ng build --prod', { shell: true, stdio: 'inherit' }, () => { 126 | this.post(); 127 | }); 128 | } else { 129 | this.build(); 130 | } 131 | 132 | } 133 | 134 | } 135 | 136 | if (cli.program.clean !== false) { 137 | 138 | util.cleanBuild().then(() => { 139 | build(); 140 | }); 141 | } else { 142 | 143 | build(); 144 | } 145 | 146 | this.emitter.emit('hook', { 147 | payload: { 148 | step: 'pre' 149 | } 150 | }); 151 | 152 | } 153 | 154 | buildRxjsFESM() { 155 | return new Promise((res) => { 156 | 157 | let editFile = (filePath) => { 158 | return new Promise((res) => { 159 | fs.readFile(filePath, 'utf-8', (error, stdout, stderr) => { 160 | let pack = JSON.parse(stdout); 161 | pack.es2015 = pack.es2015.replace('_esm2015', '_fesm2015'); 162 | log.message('editing ' + filePath); 163 | fs.writeFile(filePath, JSON.stringify(pack), () => { 164 | res(filePath); 165 | }) 166 | }); 167 | }); 168 | }; 169 | 170 | let rollup; 171 | 172 | if (process.platform === 'win32') { 173 | rollup = spawn('cmd', ['/c', path.join(config.projectRoot, 'node_modules', '.bin', 'rollup'), '-c', path.join('config', 'rollup.rxjs.js')]); 174 | } else { 175 | rollup = spawn(path.join(config.projectRoot, 'node_modules', '.bin', 'rollup'), ['-c', path.join('config', 'rollup.rxjs.js')]); 176 | } 177 | 178 | rollup.stdout.on('data', (msg) => { 179 | log.message(msg); 180 | }); 181 | 182 | rollup.on('exit', () => { 183 | log.message('rollup completed'); 184 | Promise.all([editFile('node_modules/rxjs/package.json'), 185 | editFile('node_modules/rxjs/operators/package.json'), 186 | editFile('node_modules/rxjs/ajax/package.json'), 187 | editFile('node_modules/rxjs/testing/package.json'), 188 | editFile('node_modules/rxjs/webSocket/package.json')]) 189 | .then(data => { 190 | res(); 191 | }); 192 | 193 | }); 194 | }); 195 | } 196 | 197 | transformSCSSPathstoCSS() { 198 | 199 | return Promise.all(ls(path.normalize('tmp/**/*.ts')).map((filePath) => { 200 | return new Promise((res, rej) => { 201 | 202 | try { 203 | sed('-i', '.scss', '.css', filePath); 204 | res(); 205 | } catch(err) { 206 | rej(err); 207 | } 208 | 209 | }); 210 | })); 211 | 212 | } 213 | 214 | 215 | post() { 216 | 217 | if (!cli.program.keepTempFiles) { 218 | rm('-rf', 'tmp'); 219 | rm('-rf', 'out-tsc'); 220 | rm('-rf', 'dist/out-tsc'); 221 | } 222 | 223 | const bundleArtifact = path.join(this.outputPath, 'bundle.es2015.js'); 224 | // return environments to rightful place 225 | if (fs.existsSync(path.join('config', 'environments'))) { 226 | rm('-rf', path .join('src', 'environments')); 227 | cp('-R', path.join('config', 'environments'), 'src'); 228 | rm('-rf', path.join('config', 'environments')); 229 | } 230 | 231 | if (util.hasHook('post')) config.projects[config.project].architect.build.hooks[cli.env].post(process.argv); 232 | if (fs.existsSync(path.normalize('main.js'))) { 233 | rm(path.normalize('main.js')); 234 | } 235 | if (fs.existsSync(bundleArtifact)) { 236 | rm(bundleArtifact); 237 | } 238 | 239 | log.break(); 240 | 241 | if (cli.program.webpack === true) { 242 | ls(this.outputPath).forEach((file) => { 243 | log.logFileStats(path.join(this.outputPath, file)); 244 | }); 245 | } else { 246 | log.buildStats(this.startTime, this.outputPath); 247 | } 248 | 249 | if (util.hasArg('serve')) { 250 | util.serve(cli.program.watch); 251 | } 252 | 253 | this.emitter.emit('hook', { 254 | payload: { 255 | step: 'post' 256 | } 257 | }); 258 | } 259 | 260 | } 261 | 262 | module.exports = ProdBuild; 263 | -------------------------------------------------------------------------------- /src/scaffold/src/style/util/_normalize.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | 6 | /* http://meyerweb.com/eric/tools/css/reset/ 7 | v2.0 | 20110126 8 | License: none (public domain) 9 | */ 10 | 11 | html, body, div, span, applet, object, iframe, 12 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 13 | a, abbr, acronym, address, big, cite, code, 14 | del, dfn, em, img, ins, kbd, q, s, samp, 15 | small, strike, strong, sub, sup, tt, var, 16 | b, u, i, center, 17 | dl, dt, dd, ol, ul, li, 18 | fieldset, form, label, legend, 19 | table, caption, tbody, tfoot, thead, tr, th, td, 20 | article, aside, canvas, details, embed, 21 | figure, figcaption, footer, header, hgroup, 22 | menu, nav, output, ruby, section, summary, 23 | time, mark, audio, video { 24 | margin: 0; 25 | padding: 0; 26 | border: 0; 27 | vertical-align: baseline; 28 | } 29 | /* HTML5 display-role reset for older browsers */ 30 | article, aside, details, figcaption, figure, 31 | footer, header, hgroup, menu, nav, section { 32 | display: block; 33 | } 34 | body { 35 | line-height: 1; 36 | width: 100%; 37 | height: 100%; 38 | min-height: 100%; 39 | overflow: hidden; 40 | } 41 | blockquote, q { 42 | quotes: none; 43 | } 44 | blockquote:before, blockquote:after, 45 | q:before, q:after { 46 | content: ''; 47 | content: none; 48 | } 49 | table { 50 | border-collapse: collapse; 51 | border-spacing: 0; 52 | } 53 | 54 | /*! normalize.css v3.0.1 | MIT License | git.io/normalize */ 55 | 56 | /** 57 | * 1. Set default font family to sans-serif. 58 | * 2. Prevent iOS text size adjust after orientation change, without disabling 59 | * user zoom. 60 | */ 61 | 62 | html { 63 | font-family: sans-serif; /* 1 */ 64 | -ms-text-size-adjust: 100%; /* 2 */ 65 | -webkit-text-size-adjust: 100%; /* 2 */ 66 | min-height: 100%; 67 | } 68 | 69 | /** 70 | * Remove default margin. 71 | */ 72 | 73 | body { 74 | margin: 0; 75 | } 76 | 77 | /* HTML5 display definitions 78 | ========================================================================== */ 79 | 80 | /** 81 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 82 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. 83 | * Correct `block` display not defined for `main` in IE 11. 84 | */ 85 | 86 | article, 87 | aside, 88 | details, 89 | figcaption, 90 | figure, 91 | footer, 92 | header, 93 | hgroup, 94 | main, 95 | nav, 96 | section, 97 | summary { 98 | display: block; 99 | } 100 | 101 | /** 102 | * 1. Correct `inline-block` display not defined in IE 8/9. 103 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 104 | */ 105 | 106 | audio, 107 | canvas, 108 | progress, 109 | video { 110 | display: inline-block; /* 1 */ 111 | vertical-align: baseline; /* 2 */ 112 | } 113 | 114 | /** 115 | * Prevent modern browsers from displaying `audio` without controls. 116 | * Remove excess height in iOS 5 devices. 117 | */ 118 | 119 | audio:not([controls]) { 120 | display: none; 121 | height: 0; 122 | } 123 | 124 | /** 125 | * Address `[hidden]` styling not present in IE 8/9/10. 126 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 127 | */ 128 | 129 | [hidden], 130 | template { 131 | display: none; 132 | } 133 | 134 | /* Links 135 | ========================================================================== */ 136 | 137 | /** 138 | * Remove the gray background color from active links in IE 10. 139 | */ 140 | 141 | a { 142 | background: transparent; 143 | text-decoration: none; 144 | } 145 | 146 | /** 147 | * Improve readability when focused and also mouse hovered in all browsers. 148 | */ 149 | 150 | a:active, 151 | a:hover { 152 | outline: 0; 153 | } 154 | 155 | /* Text-level semantics 156 | ========================================================================== */ 157 | 158 | /** 159 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 160 | */ 161 | 162 | abbr[title] { 163 | border-bottom: 1px dotted; 164 | } 165 | 166 | /** 167 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 168 | */ 169 | 170 | b, 171 | strong { 172 | font-weight: bold; 173 | } 174 | 175 | /** 176 | * Address styling not present in Safari and Chrome. 177 | */ 178 | 179 | dfn { 180 | font-style: italic; 181 | } 182 | 183 | /** 184 | * Address variable `h1` font-size and margin within `section` and `article` 185 | * contexts in Firefox 4+, Safari, and Chrome. 186 | */ 187 | 188 | h1 { 189 | font-size: 2em; 190 | margin: 0.67em 0; 191 | } 192 | 193 | /** 194 | * Address styling not present in IE 8/9. 195 | */ 196 | 197 | mark { 198 | background: #ff0; 199 | color: #000; 200 | } 201 | 202 | /** 203 | * Address inconsistent and variable font size in all browsers. 204 | */ 205 | 206 | small { 207 | font-size: 80%; 208 | } 209 | 210 | /** 211 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 212 | */ 213 | 214 | sub, 215 | sup { 216 | font-size: 75%; 217 | line-height: 0; 218 | position: relative; 219 | vertical-align: baseline; 220 | } 221 | 222 | sup { 223 | top: -0.5em; 224 | } 225 | 226 | sub { 227 | bottom: -0.25em; 228 | } 229 | 230 | /* Embedded content 231 | ========================================================================== */ 232 | 233 | /** 234 | * Remove border when inside `a` element in IE 8/9/10. 235 | */ 236 | 237 | img { 238 | border: 0; 239 | } 240 | 241 | /** 242 | * Correct overflow not hidden in IE 9/10/11. 243 | */ 244 | 245 | svg:not(:root) { 246 | overflow: hidden; 247 | } 248 | 249 | /* Grouping content 250 | ========================================================================== */ 251 | 252 | /** 253 | * Address margin not present in IE 8/9 and Safari. 254 | */ 255 | 256 | figure { 257 | margin: 1em 40px; 258 | } 259 | 260 | /** 261 | * Address differences between Firefox and other browsers. 262 | */ 263 | 264 | hr { 265 | -moz-box-sizing: content-box; 266 | box-sizing: content-box; 267 | height: 0; 268 | } 269 | 270 | /** 271 | * Contain overflow in all browsers. 272 | */ 273 | 274 | pre { 275 | overflow: auto; 276 | } 277 | 278 | /** 279 | * Address odd `em`-unit font size rendering in all browsers. 280 | */ 281 | 282 | code, 283 | kbd, 284 | pre, 285 | samp { 286 | font-family: monospace, monospace; 287 | font-size: 1em; 288 | } 289 | 290 | /* Forms 291 | ========================================================================== */ 292 | 293 | /** 294 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 295 | * styling of `select`, unless a `border` property is set. 296 | */ 297 | 298 | /** 299 | * 1. Correct color not being inherited. 300 | * Known issue: affects color of disabled elements. 301 | * 2. Correct font properties not being inherited. 302 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 303 | */ 304 | 305 | button, 306 | input, 307 | optgroup, 308 | select, 309 | textarea { 310 | color: inherit; /* 1 */ 311 | font: inherit; /* 2 */ 312 | margin: 0; /* 3 */ 313 | } 314 | 315 | /** 316 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 317 | */ 318 | 319 | button { 320 | overflow: visible; 321 | } 322 | 323 | /** 324 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 325 | * All other form control elements do not inherit `text-transform` values. 326 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 327 | * Correct `select` style inheritance in Firefox. 328 | */ 329 | 330 | button, 331 | select { 332 | text-transform: none; 333 | } 334 | 335 | /** 336 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 337 | * and `video` controls. 338 | * 2. Correct inability to style clickable `input` types in iOS. 339 | * 3. Improve usability and consistency of cursor style between image-type 340 | * `input` and others. 341 | */ 342 | 343 | button, 344 | html input[type="button"], /* 1 */ 345 | input[type="reset"], 346 | input[type="submit"] { 347 | -webkit-appearance: button; /* 2 */ 348 | cursor: pointer; /* 3 */ 349 | } 350 | 351 | /** 352 | * Re-set default cursor for disabled elements. 353 | */ 354 | 355 | button[disabled], 356 | html input[disabled] { 357 | cursor: default; 358 | } 359 | 360 | /** 361 | * Remove inner padding and border in Firefox 4+. 362 | */ 363 | 364 | button::-moz-focus-inner, 365 | input::-moz-focus-inner { 366 | border: 0; 367 | padding: 0; 368 | } 369 | 370 | /** 371 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 372 | * the UA stylesheet. 373 | */ 374 | 375 | input { 376 | line-height: normal; 377 | } 378 | 379 | /** 380 | * It's recommended that you don't attempt to style these elements. 381 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 382 | * 383 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 384 | * 2. Remove excess padding in IE 8/9/10. 385 | */ 386 | 387 | input[type="checkbox"], 388 | input[type="radio"] { 389 | box-sizing: border-box; /* 1 */ 390 | padding: 0; /* 2 */ 391 | } 392 | 393 | /** 394 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 395 | * `font-size` values of the `input`, it causes the cursor style of the 396 | * decrement button to change from `default` to `text`. 397 | */ 398 | 399 | input[type="number"]::-webkit-inner-spin-button, 400 | input[type="number"]::-webkit-outer-spin-button { 401 | height: auto; 402 | } 403 | 404 | /** 405 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 406 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 407 | * (include `-moz` to future-proof). 408 | */ 409 | 410 | input[type="search"] { 411 | -webkit-appearance: textfield; /* 1 */ 412 | -moz-box-sizing: content-box; 413 | -webkit-box-sizing: content-box; /* 2 */ 414 | box-sizing: content-box; 415 | } 416 | 417 | /** 418 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 419 | * Safari (but not Chrome) clips the cancel button when the search input has 420 | * padding (and `textfield` appearance). 421 | */ 422 | 423 | input[type="search"]::-webkit-search-cancel-button, 424 | input[type="search"]::-webkit-search-decoration { 425 | -webkit-appearance: none; 426 | } 427 | 428 | /** 429 | * Define consistent border, margin, and padding. 430 | */ 431 | 432 | fieldset { 433 | border: 1px solid #c0c0c0; 434 | margin: 0 2px; 435 | padding: 0.35em 0.625em 0.75em; 436 | } 437 | 438 | /** 439 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 440 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 441 | */ 442 | 443 | legend { 444 | border: 0; /* 1 */ 445 | padding: 0; /* 2 */ 446 | } 447 | 448 | /** 449 | * Remove default vertical scrollbar in IE 8/9/10/11. 450 | */ 451 | 452 | textarea { 453 | overflow: auto; 454 | } 455 | 456 | /** 457 | * Don't inherit the `font-weight` (applied by a rule above). 458 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 459 | */ 460 | 461 | optgroup { 462 | font-weight: bold; 463 | } 464 | 465 | /* Tables 466 | ========================================================================== */ 467 | 468 | /** 469 | * Remove most spacing between table cells. 470 | */ 471 | 472 | table { 473 | border-collapse: collapse; 474 | border-spacing: 0; 475 | } 476 | 477 | td, 478 | th { 479 | padding: 0; 480 | } 481 | -------------------------------------------------------------------------------- /src/build/lib.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | const Build = require('./index.js'); 5 | const SassBuilder = require('./../style/sass.js'); 6 | const PostCSSBuilder = require('./../style/postcss.js'); 7 | const AOTBuilder = require('./../compile/ngc.js'); 8 | const RollupBuilder = require('./../bundle/rollup.js'); 9 | const Watcher = require('./../watch.js'); 10 | const util = require('./../util.js'); 11 | const log = require('./../log.js'); 12 | const config = require('./../config'); 13 | const cli = require('./../../cli.config.json'); 14 | 15 | class LibBuild extends Build { 16 | 17 | constructor(libName) { 18 | super(); 19 | 20 | this.hasInit = false; 21 | this.hasPost = false; 22 | 23 | 24 | if (!cli.program.config) { 25 | if (!libName) { 26 | libName = cli.program.build; 27 | const libConfig = config.projects[cli.program.rawArgs[cli.program.rawArgs.indexOf(libName) + 1]]; 28 | this.libConfigPath = path.join(libConfig.root, libConfig.configFile); 29 | } else { 30 | const libConfig = config.projects[libName]; 31 | this.libConfigPath = path.join(libConfig.root, libConfig.configFile); 32 | } 33 | 34 | } else { 35 | this.libConfigPath = cli.program.config.trim(); 36 | } 37 | 38 | } 39 | 40 | init() { 41 | this.pre(); 42 | } 43 | 44 | 45 | build() { 46 | 47 | // TODO: figure out best way to abstract styling tasks for the builds, should be able to support LESS, Stylus, etc. 48 | const sassBuilder = new SassBuilder({ dist: this.libConfig.dist, sourceMap: false }); 49 | const postcssBuilder = new PostCSSBuilder({ dist: this.libConfig.dist, sourceMap: false }); 50 | const styles = config.projects[config.project].architect.build.options.styles; 51 | 52 | 53 | if (ls(path.normalize('tmp/**/*.scss')).length > 0) { 54 | 55 | const sassFileList = ls(path.normalize('tmp/**/*.scss')); 56 | (async () => { 57 | const sass = await sassBuilder.batch(sassFileList); 58 | const postcss = await postcssBuilder.batch(sass); 59 | const convert = await this.transformSCSSPathstoCSS(); 60 | const bundle = await this.bundleLib(); 61 | })(); 62 | 63 | } 64 | else if (ls(path.normalize('tmp/**/*.css')).length > 0) { 65 | 66 | const cssFileList = ls(path.normalize('tmp/**/*.css')); 67 | 68 | (async () => { 69 | const postcss = await postcssBuilder.batch(cssFileList); 70 | const bundle = await this.bundleLib(); 71 | })(); 72 | 73 | 74 | } else { // dont barf on a lib without styling 75 | (async () => { 76 | const bundle = await this.bundleLib(); 77 | })(); 78 | } 79 | 80 | // process global styles 81 | 82 | // if (styles.length > 0) { 83 | // const globalFiles = styles.map((stylePath) => { 84 | // return sassBuilder.file(stylePath); 85 | // }); 86 | 87 | // Promise.all(globalFiles).then((css) => { 88 | // postcssBuilder.batch(css); 89 | // }); 90 | // } 91 | 92 | } 93 | 94 | transformSCSSPathstoCSS() { 95 | 96 | return Promise.all(ls(path.normalize('tmp/**/*.ts')).map((filePath) => { 97 | return new Promise((res, rej) => { 98 | 99 | try { 100 | sed('-i', '.scss', '.css', filePath); 101 | res(); 102 | } catch(err) { 103 | rej(err); 104 | } 105 | 106 | }); 107 | })); 108 | 109 | } 110 | 111 | bundleLib() { 112 | 113 | const aotBuilder = new AOTBuilder(); 114 | const rollupBuilder = new RollupBuilder(); 115 | 116 | ls(path.normalize('tmp/**/*.component.ts')).forEach((file) => { 117 | util.inline(file); 118 | }); 119 | 120 | return new Promise((res, rej) => { 121 | 122 | log.process('compiling library'); 123 | 124 | (async () => { 125 | const compileFESM = await aotBuilder.compile(path.join(this.libConfig.src, 'config', this.libConfig.es2015.tsConfig)); 126 | const rollupFESM = await rollupBuilder.bundle(path.join(this.libConfig.src, 'config', this.libConfig.es2015.rollupConfig)); 127 | const post = await this.checkBuild(); 128 | })(); 129 | (async () => { 130 | const compileUMD = await aotBuilder.compile(path.join(this.libConfig.src, 'config', this.libConfig.es5.tsConfig)); 131 | const rollupUMD = await rollupBuilder.bundle(path.join(this.libConfig.src, 'config', this.libConfig.umd.rollupConfig)); 132 | const post = await this.checkBuild(); 133 | })(); 134 | (async () => { 135 | const compileES5 = await aotBuilder.compile(path.join(this.libConfig.src, 'config', this.libConfig.es5.tsConfig)); 136 | const rollupES5 = await rollupBuilder.bundle(path.join(this.libConfig.src, 'config', this.libConfig.es5.rollupConfig)); 137 | const post = await this.checkBuild(); 138 | })(); 139 | (async () => { 140 | const compileESM5 = await aotBuilder.compile(path.join(this.libConfig.src, 'config', this.libConfig.esm5.tsConfig)); 141 | const post = await this.checkBuild(); 142 | })(); 143 | (async () => { 144 | const compileESM2015 = await aotBuilder.compile(path.join(this.libConfig.src, 'config', this.libConfig.esm2015.tsConfig)); 145 | const post = await this.checkBuild(); 146 | })(); 147 | 148 | }); 149 | } 150 | 151 | checkBuild() { 152 | 153 | return new Promise((res, rej) => { 154 | if (fs.existsSync(this.libConfig.es2015.outFile) && 155 | fs.existsSync(this.libConfig.es5.outFile) && 156 | fs.existsSync(this.libConfig.umd.outFile) && 157 | fs.existsSync(path.join('out-tsc', 'esm5', 'index.js')) && 158 | fs.existsSync(path.join('out-tsc', 'esm2015', 'index.js'))) { 159 | if (!this.hasPost) { 160 | this.post(); 161 | } 162 | log.stop('compiling library'); 163 | res(); 164 | } else { 165 | log.stop('compiling library'); 166 | res(); // fail silently 167 | } 168 | }); 169 | 170 | } 171 | 172 | fetchLibConfig() { 173 | 174 | return new Promise((res, rej) => { 175 | fs.readFile(this.libConfigPath, 'utf8', (err, contents) => { 176 | if (!err) { 177 | this.libConfig = JSON.parse(contents); 178 | res(); 179 | } else { 180 | rej(err); 181 | } 182 | }); 183 | }) 184 | 185 | } 186 | 187 | processESM() { 188 | 189 | return new Promise((res, rej) => { 190 | 191 | let copyFile = (filePath, distFilePath) => { 192 | if (!fs.existsSync(util.getFilePath(distFilePath))) { 193 | mkdir('-p', util.getFilePath(distFilePath)); 194 | } 195 | cp('-R', filePath, distFilePath); 196 | }; 197 | 198 | try { 199 | 200 | // copy typings 201 | find(path.normalize('./out-tsc/esm5')) 202 | .filter(function (file) { return file.match(/\.d.ts$/); }) 203 | .forEach((filePath) => { 204 | copyFile(filePath, path.join(this.libConfig.dist, filePath.replace(path.normalize('out-tsc/esm5'), ''))); 205 | }); 206 | // copy esm5 207 | find(path.normalize('./out-tsc/esm5')) 208 | .filter(function (file) { return file.match(/\.js$/); }) 209 | .forEach((filePath) => { 210 | copyFile(filePath, path.join(this.libConfig.dist, filePath.replace('out-tsc', ''))); 211 | }); 212 | // copy esm2015 213 | find(path.normalize('./out-tsc/esm2015')) 214 | .filter(function (file) { return file.match(/\.js$/); }) 215 | .forEach((filePath) => { 216 | copyFile(filePath, path.join(this.libConfig.dist, filePath.replace('out-tsc', ''))); 217 | }); 218 | // cophy meteadata 219 | cp(path.join('out-tsc', 'esm2015', this.libConfig.filename + '.metadata.json'), 220 | path.join(this.libConfig.dist, this.libConfig.filename + '.metadata.json')); 221 | 222 | res(); 223 | 224 | } 225 | catch (err) { 226 | rej(err); 227 | } 228 | 229 | }); 230 | } 231 | 232 | 233 | pre() { 234 | 235 | util.cleanBuild(); 236 | 237 | this.fetchLibConfig().then((res) => { 238 | 239 | rm('-rf', 'out-tsc'); 240 | // rm('-rf', this.libConfig.dist); 241 | mkdir('-p', this.libConfig.dist); 242 | 243 | cp('-R', path.normalize(this.libConfig.src + '/') + '.', path.normalize('./tmp')); 244 | 245 | // remove moduleId prior to ngc build, inline template and styles 246 | ls(path.normalize('tmp/**/*.ts')).forEach((file) => { 247 | sed('-i', /^.*moduleId: module.id,.*$/, '', file); 248 | }); 249 | 250 | if (util.hasHook('pre')) { 251 | log.message('processing pre task'); 252 | config.projects[config.project].architect.build.hooks[cli.env].pre(process.argv).then(() => { 253 | this.build(); 254 | }); 255 | 256 | } else { 257 | 258 | this.build(); 259 | 260 | } 261 | 262 | this.emitter.emit('hook', { 263 | payload: { 264 | step: 'pre' 265 | } 266 | }); 267 | 268 | }).catch((err) => { 269 | log.warn(err); // TODO: exit process 270 | }) 271 | 272 | } 273 | 274 | post() { 275 | 276 | this.hasPost = true; 277 | 278 | this.processESM().then((res) => { 279 | 280 | 281 | // copy package.json to dist 282 | exec('cp ' + this.libConfig.src + '/package.json' + ' ' + this.libConfig.dist + '/package.json', () => { 283 | 284 | log.message('package.json copied to ./' + this.libConfig.dist); 285 | 286 | if (util.hasHook('post')) { 287 | log.message('processing post task'); 288 | config.projects[config.project].architect.build.hooks[cli.env].post(process.argv); 289 | } 290 | 291 | log.destroy(); 292 | log.buildStats(this.startTime, this.libConfig.dist); 293 | 294 | this.emitter.emit('hook', { 295 | payload: { 296 | step: 'post' 297 | } 298 | }); 299 | 300 | }); 301 | 302 | 303 | }).catch((err) => { 304 | log.warn(err); 305 | }); 306 | 307 | } 308 | 309 | } 310 | 311 | module.exports = LibBuild; -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | require('shelljs/global'); 2 | 3 | const exec = require('child_process').exec; 4 | const path = require('path'); 5 | const fs = require('fs'); 6 | const UglifyJS = require('uglify-js'); 7 | const MagicString = require('magic-string'); 8 | const escape = require('js-string-escape'); 9 | const minifyHtml = require('html-minifier').minify; 10 | const spawn = require('child_process').spawn; 11 | const config = require('./config.js'); 12 | const cli = require('./../cli.config.json'); 13 | const log = require('./log'); 14 | 15 | class Util { 16 | 17 | constructor() { 18 | this.moduleIdRegex = /moduleId\s*:(.*)/g; 19 | this.directiveRegex = /@Directive\(\s?{([\s\S]*)}\s?\)$/gm; 20 | this.componentRegex = /@Component\(\s?{([\s\S]*)}\s?\)$/gm; 21 | this.templateUrlRegex = /templateUrl\s*:(.*)/g; 22 | this.styleUrlsRegex = /styleUrls\s*:(\s*\[[\s\S]*?\])/g; 23 | this.stringRegex = /(['"])((?:[^\\]\\\1|.)*?)\1/g; 24 | this.multilineComment = /^[\t\s]*\/\*\*?[^!][\s\S]*?\*\/[\r\n]/gm; 25 | this.singleLineComment = /^[\t\s]*(\/\/)[^\n\r]*[\n\r]/gm; 26 | this.lastError = { 27 | message: '' 28 | }; 29 | } 30 | 31 | hasArg(arg) { 32 | return process.argv.indexOf(arg) > -1 || process.argv.indexOf('--' + arg) > -1; 33 | } 34 | 35 | getFilePath(filePath) { 36 | 37 | return path.normalize(filePath.substring(0, filePath.replace(/\\/g, '/').lastIndexOf('/'))); 38 | 39 | } 40 | 41 | getFileName(filePath) { 42 | 43 | return filePath.replace(/^.*[\\\/]/, ''); 44 | 45 | } 46 | 47 | copyFile(src, dist, options) { 48 | 49 | if (options && options.force) { 50 | rm('-f', dist); 51 | cp(src, dist); 52 | } else { 53 | cp(src, dist); 54 | } 55 | if (options && options.silent !== true) log.message(src + ' copied to ' + dist); 56 | 57 | } 58 | 59 | copyDir(src, dist, options) { 60 | 61 | if (!fs.existsSync(dist)) mkdir('-p', dist); 62 | if (options && options.force) { 63 | rm('-rf', path.normalize(path.join(dist, '/'))); 64 | mkdir('-p', path.normalize(path.join(dist, '/'))); 65 | cp('-R', path.normalize(src + '/*'), path.normalize(path.join(dist, '/'))); 66 | } else { 67 | cp('-R', path.normalize(src + '/*'), path.normalize(path.join(dist, '/'))); 68 | } 69 | if (options && options.silent !== true) log.message(this.getFileName(src) + ' copied to ' + this.getFileName(dist)); 70 | 71 | } 72 | 73 | copyTo(filePath, dist) { 74 | 75 | const outFile = path.join(dist, filePath.replace(/(node_modules)[\\\/]/g, '')); 76 | return new Promise((res) => { 77 | if (!fs.existsSync(outFile)) { 78 | if (!fs.existsSync(path.dirname(outFile))) { 79 | mkdir('-p', path.dirname(outFile)); 80 | } 81 | cp('-R', path.join(filePath), outFile); 82 | log.message(this.getFileName(filePath) + ' copied to ' + dist); 83 | res(); 84 | } 85 | }); 86 | 87 | } 88 | 89 | copyLib(fileList, src, dist) { 90 | 91 | return Promise.all(fileList.map((filePath) => { 92 | return this.copyTo(path.join(src, filePath), dist); 93 | })); 94 | 95 | } 96 | 97 | copyBatch(fileList, dist) { 98 | 99 | return Promise.all(fileList.map((filePath) => { 100 | return this.copyTo(filePath, dist); 101 | })); 102 | 103 | } 104 | 105 | hasHook(step) { 106 | if (cli.build === 'lib') { 107 | return (config.projects[config.project].architect.build.hooks && config.projects[config.project].architect.build.hooks[cli.env] && config.projects[config.project].architect.build.hooks[cli.env][step]) ? true : false; 108 | } else { 109 | return (config.projects[config.project].architect.build.hooks && config.projects[config.project].architect.build.hooks[cli.build] && config.projects[config.project].architect.build.hooks[cli.build][step]) ? true : false; 110 | } 111 | } 112 | 113 | hasConfigProperty(prop, obj) { 114 | return obj ? obj.hasOwnProperty(prop) : config.hasOwnProperty(prop); 115 | } 116 | 117 | cleanBuild() { 118 | 119 | return new Promise((res, rej) => { 120 | if (fs.existsSync(path.normalize(config.build))) rm('-rf', path.normalize(config.build)); 121 | if (fs.existsSync(path.normalize('./closure'))) rm('-rf', path.normalize('./closure')); 122 | if (fs.existsSync(path.normalize('./out-tsc'))) rm('-rf', path.normalize('./out-tsc')); 123 | mkdir(path.normalize('./out-tsc')); 124 | mkdir(path.normalize('./closure')); 125 | res(); 126 | }); 127 | 128 | } 129 | 130 | formatIndex(template) { 131 | 132 | return new Promise((res, rej) => { 133 | 134 | let env; 135 | 136 | if (cli.build === 'jit') { 137 | env = 'dev'; 138 | } else { 139 | env = cli.build; 140 | } 141 | 142 | exec(path.join(config.cliRoot, path.normalize('node_modules/.bin/htmlprocessor')) + 143 | ' ' + path.normalize(template) + 144 | ' -o ' + path.normalize(path.join(config.build, '/') + 'index.html') + 145 | ' -e ' + env, { silent: true }, (error, stdout, stderr) => { 146 | //log.message('htmlprocessor' + ' formatted ' + this.getFileName(template)); 147 | if (error) { 148 | log.warn(error); 149 | if (rej) rej(error); 150 | } 151 | if (res) res(); 152 | }); 153 | 154 | }); 155 | 156 | 157 | } 158 | 159 | inline(filePath) { 160 | 161 | const outFile = filePath ? filePath : path.normalize('./' + this.libConfig.dist + '/bundle.js'); 162 | let inline = ''; 163 | 164 | fs.readFile(outFile, 'utf8', (err, contents) => { 165 | if (!err) { 166 | contents = contents.replace(this.multilineComment, ''); 167 | contents = contents.replace(this.singleLineComment, ''); 168 | 169 | if (contents.search(this.componentRegex) > -1) { 170 | inline = this.inlineHTMLandCSS({ 171 | preprocessors: { 172 | template: template => minifyHtml(template, { 173 | caseSensitive: true, 174 | collapseWhitespace: true, 175 | removeComments: true, 176 | quoteCharacter: '"' 177 | }) 178 | } 179 | }, contents, filePath.substring(0, filePath.replace(/\\/g, "/").lastIndexOf('/'))); 180 | 181 | if (inline) { 182 | contents = inline.code; 183 | } 184 | 185 | } 186 | 187 | fs.writeFile(outFile, contents, (err) => { 188 | if (!err && this.getFileName(outFile).includes('component')) { 189 | log.message('inline template and styles for ' + this.getFileName(outFile)); 190 | } else if (err) { 191 | log.warn(err); 192 | } 193 | }); 194 | } else { 195 | log.warn(err); 196 | } 197 | 198 | }); 199 | } 200 | 201 | inlineHTMLandCSS(options, source, dir) { 202 | 203 | 204 | let stringRegex = this.stringRegex; 205 | 206 | /* Logic for inling styles adapted from rollup-plugin-angular CREDIT Felix Itzenplitz */ 207 | function insertText(str, dir, preprocessor = res => res, processFilename = false) { 208 | return str.replace(stringRegex, function (match, quote, url) { 209 | const includePath = path.join(dir, url); 210 | if (processFilename) { 211 | let text = preprocessor(includePath); 212 | text = escape(text); 213 | return '"' + text + '"'; 214 | } 215 | let text = fs.readFileSync(includePath).toString(); 216 | text = preprocessor(text, includePath); 217 | text = escape(text); 218 | return '"' + text + '"'; 219 | }); 220 | } 221 | 222 | options.preprocessors = options.preprocessors || {}; 223 | // ignore @angular/** modules 224 | options.exclude = options.exclude || []; 225 | if (typeof options.exclude === 'string' || options.exclude instanceof String) options.exclude = [options.exclude]; 226 | if (options.exclude.indexOf('node_modules/@angular/**') === -1) options.exclude.push('node_modules/@angular/**'); 227 | 228 | const magicString = new MagicString(source); 229 | 230 | let hasReplacements = false; 231 | let match; 232 | let start, end, replacement; 233 | 234 | while ((match = this.componentRegex.exec(source)) !== null) { 235 | start = match.index; 236 | end = start + match[0].length; 237 | 238 | replacement = match[0] 239 | .replace(this.templateUrlRegex, function (match, url) { 240 | hasReplacements = true; 241 | return 'template:' + insertText(url, dir, options.preprocessors.template, options.processFilename); 242 | }) 243 | .replace(this.styleUrlsRegex, function (match, urls) { 244 | hasReplacements = true; 245 | return 'styles:' + insertText(urls, dir, options.preprocessors.style, options.processFilename); 246 | }) 247 | .replace(this.moduleIdRegex, function (match, moduleId) { 248 | hasReplacements = true; 249 | return ''; 250 | }); 251 | 252 | if (hasReplacements) magicString.overwrite(start, end, replacement); 253 | } 254 | 255 | if (!hasReplacements) return null; 256 | 257 | let result = { code: magicString.toString() }; 258 | if (options.sourceMap !== false) result.map = magicString.generateMap({ hires: true }); 259 | 260 | return result; 261 | 262 | } 263 | 264 | concatVendorScripts(dist) { 265 | let result = UglifyJS.minify(this.vendorScripts, { toplevel: true }); 266 | return fs.writeFileSync(path.normalize('./' + dist + '/vendor.js'), result.code); 267 | } 268 | 269 | formatVendorScripts(fileList, src, dist) { 270 | this.vendorScripts = {}; 271 | fs.openSync(path.normalize('./' + dist + '/vendor.js'), 'w'); 272 | return Promise.all(fileList.map((filePath) => { 273 | return this.concatFile(path.join(src, filePath), src, dist); 274 | })); 275 | } 276 | 277 | concatFile(file, src, dist, code) { 278 | return new Promise((res, rej) => { 279 | fs.readFile(file, 'utf8', (err, fileContent) => { 280 | this.vendorScripts[this.getFileName(file)] = fileContent; 281 | res(); 282 | }); 283 | }); 284 | } 285 | 286 | serve(watch, isUniversal) { 287 | 288 | if (isUniversal === true) { 289 | 290 | spawn('npm run universal', { shell: true, stdio: 'inherit' }); 291 | 292 | } else { 293 | 294 | let serverCommand = 'npm run serve'; 295 | 296 | if (watch === true) { 297 | serverCommand += ' watch=true'; 298 | } 299 | else { 300 | serverCommand += ' watch=false'; 301 | } 302 | spawn(serverCommand, { shell: true, stdio: 'inherit' }); 303 | 304 | } 305 | 306 | } 307 | 308 | } 309 | 310 | 311 | 312 | module.exports = new Util(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ngr 2 | 3 | CLI for building Angular with Rollup and Closure Compiler 4 | 5 | [![Join the chat at https://gitter.im/angular2-rollup/Lobby](https://badges.gitter.im/angular2-rollup/Lobby.svg)](https://gitter.im/angular2-rollup/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | 7 | ## Main Features 8 | 9 | * Extends `@angular/cli`. Run `ng` and `ngr` commands in the same project 10 | 11 | * Generates highly optimized bundle using Closure Compiler 12 | 13 | * Builds library packages formatted with [Angular Package Format](https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs/preview) 14 | 15 | * Includes Express server for ramping up backend NodeJS production 16 | 17 | * Stylesheets with [SASS](http://sass-lang.com/) and [PostCSS](http://postcss.org) 18 | 19 | * Follows [Angular Styleguide](https://angular.io/guide/styleguide) 20 | 21 | * Pretty printed error reporting with [TSLint](http://palantir.github.io/tslint/) and [Codelyzer](https://github.com/mgechev/codelyzer) 22 | 23 | 24 | ## Table of Contents 25 | 26 | * [Getting Started](#getting-started) 27 | * [Install](#install) 28 | * [Scaffold](#scaffold) 29 | * [Help](#help) 30 | * [Development](#development) 31 | * [Build](#build) 32 | * [Production](#production) 33 | * [Hooks](#buildhooks) 34 | * [Config](#buildconfig) 35 | * [Server](#server) 36 | * [Libraries](#library) 37 | * [Schematics, Testing, and i18n](#testing) 38 | 39 | * [FAQ](#faq) 40 | * [License](#license) 41 | 42 | 43 | # Getting Started 44 | 45 | ## Install 46 | 47 | 48 | - Install dependencies 49 | 50 | Install the [Java JDK](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) 51 | 52 | - Install the cli and global npm dependencies 53 | 54 | `$ npm install -g angular-rollup @angular/cli` 55 | 56 | - Apps are scaffolded just like the `@angular/cli`. Use `ngr` instead of `ng`. 57 | 58 | ``` 59 | 60 | $ ngr new my-app 61 | 62 | ``` 63 | 64 | ## Scaffold 65 | 66 | To scaffold a new app run `ngr new my-app`. This command will copy required files into the a new directory called my-app and run `npm install`. Use the `--yarn` flag to install with it instead. 67 | 68 | Migrate existing angular-rollup and @angular/cli projects with `--src`. 69 | 70 | `ngr new my-new-app --src /path/to/old/src` 71 | 72 | 73 | ## Help 74 | 75 | `ngr --help` will list all commands and arguments provided in the cli. 76 | 77 | ``` 78 | 79 | Options: 80 | 81 | -V, --version output the version number 82 | new [string] scaffold new development environment in directory by name, i.e. ngr new my-app 83 | --src [string] specify a path to an existing src folder 84 | --skip-install [bool] prevents install during scaffold 85 | --yarn [bool] use yarn instead of npm to install 86 | --prettier [bool] scaffold a new workspace with prettier installed 87 | --ssl [bool] scaffold a new workspace with https express server 88 | --angularVersion [string] scaffold a new workspace with a specific version of angular 89 | build [env] build the application 90 | --env [string] use that particular environment.ts during the build, just like @angular/cli 91 | --clean [bool] destroy the build folder prior to compilation, default for prod 92 | --watch [bool] listen for changes in filesystem and rebuild 93 | --config [string] path to configuration file for library build 94 | --deploy [bool] call deploy build hook for library build 95 | --verbose [bool] log all messages in list format 96 | --closure [bool] bundle and optimize with closure compiler (default) 97 | --rollup [bool] bundle with rollup and optimize with closure compiler 98 | --webpack [bool] use @angular/cli to build 99 | g, generate [string] generate schematics packaged with angular-rollup 100 | serve, --serve [bool] spawn the local express server 101 | -h, --help output usage information 102 | ``` 103 | 104 | 105 | # Development 106 | 107 | ## Build 108 | 109 | `ng serve` to build for development using `@angular/cli`. 110 | 111 | 112 | ## Production 113 | 114 | * `$ ngr build prod` 115 | 116 | You can choose to bundle with Rollup instead and simplify Closure Compiler's configuration, at the expense of bundle size. 117 | 118 | * `$ ngr build prod --rollup` 119 | 120 | Use native `ng` commands for webpack. 121 | 122 | * `$ ng build --prod` 123 | 124 | To build an application for production and serve it locally: 125 | 126 | * `$ ngr build prod --serve` 127 | 128 | 129 | ## Build Hooks 130 | 131 | Hooks are points in the build where you can inject custom functionality. Each build has a `pre` and `post` hook. All hooks except `post` require that you return a `Promise`. There is a `watch` hook for the development build that takes two parameters: `dist` and `src`. There is a `deploy` hook for the library build to run additional scripts for deployment. 132 | 133 | ``` 134 | hooks: { 135 | prod: { 136 | pre: () => { 137 | return new Promise((res, rej) => { 138 | // do something 139 | res(); 140 | }) 141 | }, 142 | post: () => { 143 | // do something 144 | } 145 | } 146 | } 147 | ``` 148 | 149 | 150 | ## ngr.config.js 151 | 152 | Config is shared with `angular.json` but for non Webpack builds the following files allow you configure filepaths, SASS options, and includes callbacks for steps in each build. 153 | 154 | 155 | | Script | Description | 156 | | ------------- |:-------------:| 157 | | ngr.config.js | Configures project filepaths, build hooks | 158 | | postcss.config.js | Configures postcss step in each build | 159 | | rollup.config.js | Configures rollup when using `ngr build --rollup` | 160 | | closure.rollup.conf | Configures Closure Compiler when using `ngr build --rollup` | 161 | | closure.conf | Configures Closure Compiler when using `ngr build prod` | 162 | | server.config.*.js | Configures express server | 163 | | tsconfig.*.json | Configures TypeScript (dev) or @angular/compiler (lib,prod) 164 | 165 | 166 | ## Server 167 | 168 | Express is used mainly to provide a development server, but it could also boilerplate for a MEAN stack. 169 | 170 | `server.js` and `router.js` are stored in the `backend` folder. 171 | 172 | `ngr serve` will start up the Express server, so will `--serve` with any build. 173 | 174 | 175 | ### Configure Server 176 | 177 | Change the host and/or port in `/config/server.config.dev.js` if needed. This config is used for the Express Server. `/config/server.config.prod.js` is used for production. 178 | 179 | ``` 180 | { 181 | origin: 'localhost', 182 | port: 4200 183 | }; 184 | 185 | ``` 186 | 187 | ## Libraries 188 | 189 | `ngr` provides a build for developing Angular libraries that conforms to the Angular Package Format. 190 | 191 | Jason Aden gave a presentation about Angular Package Format at ng-conf 2017. [Packaging Angular](https://youtu.be/unICbsPGFIA). 192 | 193 | 194 | ### Generate A Library Package 195 | 196 | Generate library packages with `ngr generate lib my-lib-name`. 197 | 198 | This will generate a library package in the current folder with the necessary configuration set in `ngr.config.js`. 199 | 200 | 201 | ### Tips For Developing A Library 202 | 203 | - Keep your code strictly typed. 204 | - Do not create monolithic `@NgModule`. Separate modules by discrete functionality. This allows the library to be treeshaken. 205 | - In each `module.ts` `export` public components, directives, services, etc. 206 | - Update the library index.ts with `export` for each module. 207 | 208 | ### Build Library 209 | 210 | After you have generated some components for the library, use `ngr build lib` to build the library in the `dist` folder. 211 | 212 | `ngr build lib my-lib-name` 213 | 214 | 215 | ## Use `@angular/cli` for Schematics, Testing, and i18n 216 | 217 | `ng generate` works in `angular-rollup`! 218 | 219 | Use `ng test` and `ng e2e` for unit and end to end tests, respectively. 220 | 221 | 222 | # FAQ 223 | 224 | ### How do I include third party libraries? 225 | 226 | The production build relies heavily on Closure Compiler, which optimizes the bundle far better than other tools. Closure Compiler relies on annotations in JavaScript to optimize the bundle. Third party libraries are often not annotated. If a library package follows the Angular Package Format it will be closure annotated because when configured, the angular compiler will convert TypeScript annotations to closure annoations using a tool called [tsickle](https://github.com/angular/tsickle). Luckily for third party libraries that are not annotated, Closure Compiler can be configured to leave variables found in external scripts unmangled. Follow this step by step to figure out which method to use. 227 | 228 | - Does the library conform to Angular Package Format? 229 | YES: Will be bundled by `ngc`, inject the NgModule into your application, add to `closure.conf` 230 | NO: See next question 231 | 232 | - Is the library written in ES2015? 233 | YES: Include the necessary library files in `closure.conf` 234 | NO: See next question 235 | 236 | - Is the library formatted with UMD modules? 237 | YES: Include the necessary library files in `closure.conf` 238 | NO: You must include the library globally via `` or `SystemJS`. 239 | Add the necessary externs to `closure.externs.js` 240 | 241 | 242 | 243 | ### How do I load a library in index.html? 244 | 245 | If a library must be loaded prior to bootstrap, add the folder name in `ngr.config.js` to have it copied into `dist` directory during the build. 246 | 247 | Add the script in the `` or you can include third party dependencies with `SystemJS`. 248 | 249 | ``` 250 | 258 | ``` 259 | 260 | 261 | For production, `ngr` will concatenante library packages into `vendor.js`. 262 | For development, all libarary files are copied to the `dist` folder. 263 | 264 | Vendor files are configured in `ngr.config.js` like in this example: 265 | 266 | ``` 267 | lib: { 268 | dev: [ 269 | 'core-js/client/shim.min.js', 270 | 'core-js/client/shim.min.js.map', 271 | 'zone.js/dist/zone.min.js', 272 | 'systemjs/dist/system.js', 273 | 'systemjs/dist/system.js.map', 274 | 'reflect-metadata/Reflect.js', 275 | 'reflect-metadata/Reflect.js.map', 276 | 'tslib/tslib.js', 277 | '@angular', 278 | 'rxjs' 279 | ], 280 | prod: [ 281 | 'core-js/client/shim.min.js', 282 | 'zone.js/dist/zone.min.js', 283 | 'systemjs/dist/system.js' 284 | ], 285 | src: 'node_modules', 286 | dist: 'dist/path/to/lib' 287 | } 288 | ``` 289 | 290 | 291 | ### Why are there 2 different index.html? 292 | 293 | `angular-rollup` uses `htmlprocessor` to manipulate `index.html` while webpack works it's magic with the `index.html` provided by `@angular/cli`. 294 | 295 | `src/index.html` is used by `@angular/cli` and `webpack`. 296 | `src/public/index.html` is used by `angular-rollup`. 297 | 298 | For more information about [htmlprocessor](https://www.npmjs.com/package/htmlprocessor); `src/public/index.html` is manipulated by htmlprocessor. `dev` and `prod` environment variables declared via inline comments include chunks of the `index.html` per environment. 299 | 300 | 301 | ### How do I configure SystemJS for dev for jit builds? 302 | 303 | You must configure `system.config.js` in order to inject third party libaries for development. All JavaScript in the development build is compiled into commonjs modules, however the source code is pointing to files packaged with ES2105 modules. In `system.config.js` map each request for a library script to the umd bundle for the library. The build places each library in the `dist/path/to/project/lib` folder. SystemJS needs to know where the library is located in the `dist/path/to/project/lib` folder. 304 | 305 | 306 | Here is an example of mapping requests for library bundles to umd bundles in `system.config.js`. 307 | 308 | ``` 309 | map: { 310 | // angular bundles 311 | '@angular/core': 'lib:@angular/core/bundles/core.umd.js', 312 | '@angular/common': 'lib:@angular/common/bundles/common.umd.js', 313 | '@angular/compiler': 'lib:@angular/compiler/bundles/compiler.umd.js', 314 | '@angular/platform-browser': 'lib:@angular/platform-browser/bundles/platform-browser.umd.js', 315 | '@angular/platform-browser-dynamic': 'lib:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js', 316 | '@angular/http': 'lib:@angular/http/bundles/http.umd.js', 317 | '@angular/router': 'lib:@angular/router/bundles/router.umd.js', 318 | '@angular/forms': 'lib:@angular/forms/bundles/forms.umd.js', 319 | // other libraries 320 | 'rxjs/Observable': 'lib:rxjs/Observable', 321 | 'tslib': 'lib:tslib/tslib.js' 322 | } 323 | ``` 324 | 325 | 326 | #### How do I import libraries the most optimal way for treeshaking? 327 | 328 | It is a best practice to treeshake and bundle third party libraries for production, however this process only works if the third party library is packaged with a module pattern such as ES2015 modules. 329 | 330 | It is NOT recommended to import an entire library that is treeshakable. 331 | 332 | DON'T DO THIS : `import * from 'rxjs';` 333 | DO THIS : `import { Observable, Observer } from 'rxjs';` 334 | 335 | It should be noted Closure Compiler relies on named ES2015 modules and cannot handle libraries that import with `*`. If you want a third party library to be compatible with closure compiler, it is recommended to contribute named imports and exports to the open source project. 336 | 337 | 338 | 339 | ### How do I provide typings for external libraries? 340 | 341 | Type definitions are typically packaged with the `@types` scope. Install type definitions for third party libraries with npm and list them in the tsconfig.json file in the types Array. 342 | 343 | ``` 344 | "compilerOptions": { 345 | "typeRoots": [ "node_modules/@types" ], 346 | "types": [ 347 | "node" 348 | ] 349 | } 350 | ``` 351 | 352 | 353 | ### How do I update my project to the latest CLI? 354 | 355 | `npm install -g angular-rollup@latest` 356 | 357 | 358 | ### How do I update my project to the latest versions of Angular? 359 | 360 | - `$ ng update` 361 | 362 | ### How do I deploy? 363 | 364 | The build command has an optional `--deploy` flag. 365 | 366 | Use the post build hook in ngr.config.json to deploy a build. The following example is for a library, but you could use a similar hook for a production build. 367 | 368 | Below is an example of copying the dist folder to a sibling directory that also is a git repository. The example uses `shelljs`. 369 | 370 | ``` 371 | 372 | hooks: { 373 | lib: { 374 | post: (args) => { 375 | cp('-R', './dist/.', '../'+folderName); 376 | rm('-rf', './dist'); 377 | 378 | if (args.indexOf('deploy=true')) { 379 | cd('../'+folderName); 380 | exec('git add --all .'); 381 | exec('git commit -a -m "version bump"'); 382 | exec('git push origin master'); 383 | } 384 | } 385 | } 386 | } 387 | 388 | ``` 389 | 390 | ## VSCode Extensions 391 | 392 | We like [Visual Studio Code](https://code.visualstudio.com/). Below are some VS Code Extensions we find useful when developing Angular applications. 393 | 394 | | Extension | Description | 395 | | ------------- |:-------------:| 396 | | Angular Language Service | Editor services for Angular templates | 397 | | Angular Support | Go to / peek angular specific definitions | 398 | | angular2-inline | Syntax highlighting of inline html and css | 399 | | SCSS Intellisense | Autocompletion and refactoring of SCSS | 400 | | Path Intellisense | Autocomplete for paths in the project | 401 | | NPM Intellisense | Autocomplete paths to node_modules | 402 | | Auto Import ES6 & TS | Auto import for TypeScript | 403 | 404 | 405 | # License 406 | 407 | [MIT](/LICENSE) -------------------------------------------------------------------------------- /src/log.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const exec = require('child_process').exec; 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const ora = require('ora'); 6 | const colors = require('colors'); 7 | const logger = require('single-line-log').stdout; 8 | const config = require('./config'); 9 | const uuid = require('uuid/v4'); 10 | const gzipSize = require('gzip-size'); 11 | const moment = require('moment'); 12 | const cli = require('./../cli.config.json'); 13 | 14 | const NGR_LOG_CACHE = Symbol('ngrProcess_'+uuid()); 15 | global[NGR_LOG_CACHE] = { 16 | process: {} 17 | }; 18 | 19 | const spinner = ora({ 20 | text: '', 21 | spinner: 'dots10', 22 | color: 'white', 23 | hideCursor: true 24 | }).start(); 25 | 26 | 27 | class Log { 28 | 29 | constructor() { 30 | this.spinner = spinner; 31 | } 32 | 33 | break() { 34 | process.stdout.write('\n'); 35 | } 36 | 37 | clear () { 38 | if (!cli.program.verbose) { 39 | logger.clear(); 40 | } 41 | } 42 | 43 | stop(str) { 44 | this.spinner.stop(); 45 | } 46 | 47 | hasArg(arg) { 48 | return process.argv.indexOf(arg) > -1 || process.argv.indexOf('--'+arg) > -1; 49 | } 50 | 51 | destroy() { 52 | this.spinner.stop(); 53 | if (!cli.program.verbose) { 54 | process.stdout.write('\x1B[2J\x1B[0f\u001b[0;0H'); 55 | } 56 | } 57 | 58 | line() { 59 | process.stdout.write('\n'); 60 | const col = process.stdout.columns; 61 | let line = ' '; 62 | for (let i = 0; i < col - 2; i++) { 63 | line += '\u2500'; 64 | } 65 | line += '\n'; 66 | process.stdout.write(colors.white(colors.dim(line))); 67 | } 68 | 69 | errorLine() { 70 | process.stdout.write('\n'); 71 | const col = process.stdout.columns; 72 | let line = ' '; 73 | for (let i = 0; i < col - 4; i++) { 74 | line += '\u2500'; 75 | } 76 | line += '💥'; 77 | line += '\n'; 78 | process.stdout.write(colors.red(line).dim); 79 | } 80 | 81 | bare(msg) { 82 | logger(msg); 83 | process.stderr.write('\x1B[?25l'); 84 | if (cli.program.verbose) this.break(); 85 | } 86 | 87 | message(msg) { 88 | //this.spinner.stop(); 89 | msg = msg ? '' + colors.white(msg).dim : ''; 90 | logger(msg); 91 | process.stderr.write('\x1B[?25l'); 92 | if (cli.program.verbose) this.break(); 93 | } 94 | 95 | process(msg) { 96 | 97 | if (!cli.program.verbose) this.destroy(); 98 | 99 | msg = msg ? '' + colors.white(msg).dim : ''; 100 | 101 | this.spinner.text = msg; 102 | this.spinner.start(); 103 | 104 | if (cli.program.verbose) this.break(); 105 | 106 | } 107 | 108 | success(msg, services) { 109 | services.forEach((service) => { this.cancelError(service); }); 110 | if (!this.hasError()) { 111 | this.destroy(); 112 | } 113 | msg = '\n'+ (msg ? '' + colors.white(msg) : ''); 114 | logger(msg); 115 | process.stderr.write('\x1B[?25l'); 116 | //if (!cli.program.verbose) this.line(); 117 | if (cli.program.verbose) this.break(); 118 | } 119 | 120 | fail(msg) { 121 | // if (!this.hasError()) { 122 | // this.destroy(); 123 | // } 124 | msg = msg ? '' + colors.red(msg) : ''; 125 | logger(msg); 126 | process.stderr.write('\x1B[?25l'); 127 | if (cli.program.verbose) this.break(); 128 | } 129 | 130 | alert(msg) { 131 | // if (!this.hasError()) { 132 | // this.destroy(); 133 | // } 134 | msg = msg ? '' + colors.white(msg) : ''; 135 | process.stdout.write(msg); 136 | process.stdout.write('\n'); 137 | } 138 | 139 | warn(msg) { 140 | msg = msg ? '' + colors.yellow(msg) : ''; 141 | process.stdout.write(msg); 142 | process.stdout.write('\n'); 143 | } 144 | 145 | error(err) { 146 | 147 | this.registerError(err); 148 | 149 | if (typeof err === 'string') { 150 | process.stdout.write('\n'); 151 | process.stdout.write(colors.red(err)); 152 | } else { 153 | 154 | this.break(); 155 | let msg = ' '; 156 | let link = ''; 157 | 158 | if (!err.line) { 159 | err.line = ''; 160 | } 161 | 162 | if (!err.column) { 163 | err.column = ''; 164 | } 165 | 166 | if (typeof err.line === 'number') { 167 | err.line = err.line.toString(); 168 | } 169 | 170 | if (typeof err.column === 'number') { 171 | err.column = err.column.toString(); 172 | } 173 | 174 | 175 | let lineNumbers = (err.line.length > 0) ? colors.white(err.line + ':' + err.column).dim : ''; 176 | 177 | if (err.file && err.file.length > 0) { 178 | msg += err.message.replace(/'(.*?)'/g, colors.red("'") + colors.red("$1") + colors.red("'")) 179 | .replace(/(error)( )((?:TS[a-z0-9]*))(:)/g, colors.white("$1$2$3").dim); 180 | link += err.file.includes(config.projectRoot) ? 181 | colors.dim(' vscode://file/' + err.file + ':' + lineNumbers) + '\n' : 182 | colors.dim(' vscode://file/' + config.projectRoot + '/' + err.file + ':' + lineNumbers) + '\n'; 183 | process.stdout.write(colors.red(' ' + err.service.toUpperCase() + ' ERROR') + ' ' + 184 | ((err.file.length > 0) ? colors.white(colors.dim(err.file) + ' ' + lineNumbers) : '') + '\n\n' + 185 | colors.white(msg) + '\n\n'+ 186 | ((err.file.length > 0) ? link : '') + '\n'); 187 | 188 | } else { 189 | msg = err.message; 190 | process.stdout.write(colors.red(' ' + err.service.toUpperCase() + ' ERROR') + ' ' + 191 | colors.white(msg) + '\n\n'); 192 | 193 | } 194 | 195 | 196 | 197 | this.line(); 198 | 199 | } 200 | 201 | } 202 | 203 | hasError() { 204 | for (let prop in global[NGR_LOG_CACHE].process) { 205 | if (global[NGR_LOG_CACHE].process[prop].length > 0) { 206 | return true; 207 | } 208 | } 209 | return false; 210 | } 211 | 212 | registerError(err) { 213 | if (!err || !err.service) { 214 | return; 215 | } 216 | if (!global[NGR_LOG_CACHE].process[err.service.toLowerCase()]) { 217 | global[NGR_LOG_CACHE].process[err.service.toLowerCase()] = []; 218 | } 219 | global[NGR_LOG_CACHE].process[err.service.toLowerCase()].push(err); 220 | } 221 | 222 | cancelError(service) { 223 | delete global[NGR_LOG_CACHE].process[service.toLowerCase()]; 224 | } 225 | 226 | getFilePath(filePath) { 227 | return path.normalize(filePath.substring(0, filePath.replace(/\\/g, '/').lastIndexOf('/'))); 228 | } 229 | 230 | getFileName(filePath) { 231 | return filePath.replace(/^.*[\\\/]/, ''); 232 | } 233 | 234 | catchError(str, type) { 235 | 236 | if (str.length) { 237 | this.error(str); 238 | } 239 | 240 | } 241 | 242 | formatTemplateError(str) { 243 | 244 | str = str.replace('Template parse errors:\n', ''); 245 | 246 | let msg = (/^(.*?)\(/).exec(str); 247 | let code = (/\(([^)]+)\)/).exec(str); 248 | let lookup = (/\(([^)]+)\)/).exec(str); 249 | 250 | let lineNumberLookup = str.split('"):'); 251 | 252 | if (msg != null && code != null) { 253 | 254 | msg[1] = msg[1].replace(': ', ''); 255 | code[1] = code[1].replace('[ERROR ->]', colors.red('[ERROR ->]')); 256 | 257 | if (lineNumberLookup.length === 1 && lineNumberLookup[0].match(/\(([1-9]\d{0,5})(,)([1-9]\d{0,5})\)/g) === null) { 258 | 259 | try { 260 | 261 | lookup[1] = lookup[1].substr(1).slice(0, -1); //.replace('[ERROR ->]', '') 262 | 263 | let errorLine = ''; 264 | let errorLines = lookup[1].split('\n').filter((line) => { 265 | return line.includes('[ERROR ->]'); 266 | }); 267 | 268 | errorLine = errorLines[0].replace('[ERROR ->]', ''); 269 | if (code[1][0] === '"') code[1] = code[1].substr(1); 270 | if (code[1][code[1].length -1] === '"') code[1] = code[1].slice(0, -1); 271 | let cmd = "grep -rnw '" + config.src + "' -e '" + code[1].replace('[ERROR ->]', '') + "'"; 272 | //TODO: figure out if this is possible in Windows 273 | exec(cmd, {silent: true}, (error, stdout, stderr) => { 274 | 275 | try { 276 | let lineNumber = stdout.replace('\n', ''); 277 | let columnNumber = errorLines[0].indexOf('[ERROR ->]'); 278 | 279 | lineNumber = parseInt(lineNumber.match(/(:)(\d)(:)/)[2]) - 1; //TODO: figure out if - 1 is always true 280 | if (lineNumber < 1) lineNumber = 0; 281 | 282 | this.error({ 283 | service: 'Template', 284 | file: stdout.replace('\n', '').split(':')[0], 285 | line: lineNumber.toString(), 286 | column: columnNumber, 287 | message: msg[1] + '\n\n' + code[1].substr(1).slice(0, -1) 288 | }); 289 | } 290 | catch (e) { 291 | this.error({ 292 | service: 'Template', 293 | file: stdout.replace('\n', '').split(':')[0], 294 | line: '', 295 | column: '', 296 | message: msg[1] + '\n\n' + code[1].substr(1).slice(0, -1) 297 | }); 298 | } 299 | 300 | }); 301 | 302 | } 303 | catch(e) { 304 | if (cli.program.verbose) this.message(e); 305 | this.catchError(str, 'Template'); 306 | } 307 | 308 | } else { 309 | 310 | try { 311 | let lineNoRegex = /\(([1-9]\d{0,5})(,)([1-9]\d{0,5})\)/g; 312 | 313 | 314 | if (str.indexOf(': :') > 0) { // this is janky may need better regex here 315 | 316 | let lineNumber = lineNumberLookup[lineNumberLookup.length - 1].match(lineNoRegex)[0].replace('(', '').replace(')', ''); 317 | 318 | this.error({ 319 | service: 'Template', 320 | file: lineNumberLookup[0].trim().split(':')[0].replace(lineNoRegex, ''), 321 | line: lineNumber.split(',')[0], 322 | column: lineNumber.split(',')[1], 323 | message: lineNumberLookup[0].trim().split(': :')[1], 324 | }); 325 | 326 | 327 | } else { 328 | 329 | let line = lineNumberLookup[lineNumberLookup.length - 1].replace(/\n/g, '').split('@'); 330 | 331 | this.error({ 332 | service: 'Template', 333 | file: line[0].trim(), 334 | line: line[1].split(':')[0], 335 | column: line[1].split(':')[1], 336 | message: msg[1] + '\n\n' + code[1].substr(1).slice(0, -1) 337 | }); 338 | 339 | } 340 | 341 | 342 | } catch(e) { 343 | 344 | if (cli.program.verbose) this.message(e); 345 | this.catchError(str, 'Template'); 346 | // this.error(str); 347 | 348 | } 349 | 350 | 351 | } 352 | 353 | } else { 354 | this.catchError(str, 'Template'); 355 | } 356 | 357 | } 358 | 359 | formatTSError(str) { 360 | 361 | try { 362 | let lineNumbers = str.slice(str.indexOf('(') + 1, str.indexOf(')')).split(','); 363 | let err = { 364 | service: 'TypeScript', 365 | file: str.slice(0, str.indexOf('(')), 366 | line: lineNumbers[0], 367 | column: lineNumbers[1], 368 | message: str.slice(str.indexOf(':') + 2, str.length) 369 | }; 370 | this.error(err); 371 | } 372 | catch(e) { 373 | if (cli.program.verbose) this.message(e); 374 | this.catchError(str, 'TypeScript'); 375 | } 376 | 377 | } 378 | 379 | formatClosureError(str) { 380 | 381 | let lineNumber = str.match(/:([0-9]+):/) ? str.match(/:([0-9]+):/)[1] : null; 382 | try { 383 | let err = { 384 | service: 'Closure', 385 | file: str.slice(0, str.indexOf(':')), 386 | line: lineNumber ? lineNumber : '', 387 | column: '', 388 | message: str.slice(str.indexOf('ERROR'), str.length).length ? str.slice(str.indexOf('ERROR'), str.length).replace(/\n/g, '\n ') : str 389 | }; 390 | this.error(err); 391 | } 392 | catch(e) { 393 | if (cli.program.verbose) this.message(e); 394 | this.catchError(str, 'Closure'); 395 | } 396 | 397 | } 398 | 399 | logFileStats(file) { 400 | if (fs.lstatSync(path.join(file)).isFile()) { 401 | this.alert(colors.dim('') + colors.white(file) + ' ' + 402 | colors.dim((fs.statSync(path.join(file)).size / 1000).toFixed(2) + ' kB') + ' ' + 403 | colors.green(colors.dim('(') + (gzipSize.sync(fs.readFileSync(path.join(file))) / 1000).toFixed(2) + ' kB' + ' ' + colors.dim('gzipped') + colors.dim(')') + ' ') 404 | ); 405 | } 406 | } 407 | 408 | buildStats(startTime, dist) { 409 | 410 | if (dist) { 411 | config.build = dist; 412 | } 413 | 414 | let endTime = moment(new Date()); 415 | let duration = moment.duration(endTime.diff(startTime)); 416 | // this.destroy(); 417 | this.alert(colors.green('✅ build complete')); 418 | 419 | this.alert(colors.dim('Date: ')+ new Date().toISOString()); 420 | this.alert(colors.dim('Time: ')+colors.white(duration.asMilliseconds() + 'ms')); 421 | this.alert(colors.dim('Environment: ')+ colors.white(cli.env)); 422 | this.alert(colors.dim('Location: ')+ colors.white(path.join(config.build))); 423 | 424 | ls(config.build).forEach((file) => { 425 | if (fs.lstatSync(path.join(config.build,file)).isFile()) { 426 | this.logFileStats(path.join(config.build, file)); 427 | } 428 | }); 429 | 430 | if (fs.existsSync(path.join(config.build, 'style'))) { 431 | ls(path.join(config.build, 'style')).forEach((file) => { 432 | this.logFileStats(path.join(config.build, 'style', file)); 433 | }); 434 | } 435 | 436 | if (fs.existsSync(path.join(config.build, 'fesm2015'))) { 437 | ls(path.join(config.build, 'fesm2015')).forEach((file) => { 438 | if (fs.lstatSync(path.join(config.build, 'fesm2015', file)).isFile()) { 439 | this.logFileStats(path.join(config.build, 'fesm2015', file)); 440 | } 441 | }); 442 | } 443 | 444 | if (fs.existsSync(path.join(config.build, 'fesm5'))) { 445 | ls(path.join(config.build, 'fesm5')).forEach((file) => { 446 | if (fs.lstatSync(path.join(config.build, 'fesm5', file)).isFile()) { 447 | this.logFileStats(path.join(config.build, 'fesm5', file)); 448 | } 449 | }); 450 | } 451 | 452 | if (fs.existsSync(path.join(config.build, 'esm2015'))) { 453 | ls(path.join(config.build, 'esm2015')).forEach((file) => { 454 | if (fs.lstatSync(path.join(config.build, 'esm2015', file)).isFile()) { 455 | this.logFileStats(path.join(config.build, 'esm2015', file)); 456 | } 457 | }); 458 | } 459 | 460 | if (fs.existsSync(path.join(config.build, 'esm5'))) { 461 | ls(path.join(config.build, 'esm5')).forEach((file) => { 462 | if (fs.lstatSync(path.join(config.build, 'esm5', file)).isFile()) { 463 | this.logFileStats(path.join(config.build, 'esm5', file)); 464 | } 465 | }); 466 | } 467 | 468 | if (fs.existsSync(path.join(config.build, 'bundles'))) { 469 | ls(path.join(config.build, 'bundles')).forEach((file) => { 470 | if (fs.lstatSync(path.join(config.build, 'bundles', file)).isFile()) { 471 | this.logFileStats(path.join(config.build, 'bundles', file)); 472 | } 473 | }); 474 | } 475 | 476 | } 477 | 478 | 479 | } 480 | 481 | 482 | 483 | module.exports = new Log(); 484 | --------------------------------------------------------------------------------