├── src ├── assets │ └── .gitkeep ├── favicon.ico ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── styles.less ├── app │ ├── app.component.less │ ├── app-routing.module.ts │ ├── app.module.ts │ ├── app.component.ts │ ├── app.component.html │ └── app.component.spec.ts ├── index.html ├── main.ts ├── test.ts └── polyfills.ts ├── .prettierignore ├── shared ├── index.ts └── user.service.ts ├── .prettierrc ├── .vscode ├── settings.json └── extensions.json ├── e2e ├── tsconfig.json ├── src │ ├── app.po.ts │ └── app.e2e-spec.ts └── protractor.conf.js ├── tsconfig.app.json ├── .editorconfig ├── tsconfig.spec.json ├── browserslist ├── tsconfig.json ├── .stylelintrc ├── .gitignore ├── karma.conf.js ├── package.json ├── tslint.json ├── angular.json └── README.md /src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.md 2 | tslint.json 3 | -------------------------------------------------------------------------------- /shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from './user.service'; 2 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cipchk/ng-code-style-boilerplate/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/styles.less: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /src/app/app.component.less: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | width: 100px; 4 | height: 100px; 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "semi": true, 4 | "singleQuote": true, 5 | "trailingComma": "all" 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.codeActionsOnSave": { 4 | "source.fixAll": true 5 | }, 6 | "prettier.stylelintIntegration": true 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "editorconfig.editorconfig", 4 | "esbenp.prettier-vscode", 5 | "ms-vscode.vscode-typescript-tslint-plugin" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /shared/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ providedIn: 'root' }) 4 | export class UserService { 5 | AB = 1; 6 | a = 2; 7 | AA() { 8 | console.log('aa'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "include": [ 8 | "src/**/*.ts" 9 | ], 10 | "exclude": [ 11 | "src/test.ts", 12 | "src/**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | const routes: Routes = []; 5 | 6 | @NgModule({ 7 | imports: [RouterModule.forRoot(routes)], 8 | exports: [RouterModule] 9 | }) 10 | export class AppRoutingModule { } 11 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ng8 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | 7 | @NgModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | imports: [ 12 | BrowserModule, 13 | AppRoutingModule 14 | ], 15 | providers: [], 16 | bootstrap: [AppComponent] 17 | }) 18 | export class AppModule { } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ], 21 | "rootDir": ".", 22 | "paths": { 23 | "@shared": ["shared"], 24 | "@shared/*": ["shared/*"] 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "stylelint-config-standard", 4 | "stylelint-config-rational-order", 5 | "stylelint-config-prettier" 6 | ], 7 | "plugins": [ 8 | "stylelint-order", 9 | "stylelint-declaration-block-no-ignored-properties" 10 | ], 11 | "rules": { 12 | "no-descending-specificity": null, 13 | "plugin/declaration-block-no-ignored-properties": true, 14 | "selector-type-no-unknown": [ 15 | true, 16 | { 17 | "ignoreTypes": ["/^app-/"] 18 | } 19 | ], 20 | "selector-pseudo-element-no-unknown": [ 21 | true, 22 | { 23 | "ignorePseudoElements": ["ng-deep"] 24 | } 25 | ] 26 | }, 27 | "ignoreFiles": ["src/assets/**/*"] 28 | } 29 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to ng8!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | yarn.lock 13 | 14 | # profiling files 15 | chrome-profiler-events.json 16 | speed-measure-plugin.json 17 | 18 | # IDEs and editors 19 | /.idea 20 | .project 21 | .classpath 22 | .c9/ 23 | *.launch 24 | .settings/ 25 | *.sublime-workspace 26 | 27 | # IDE - VSCode 28 | .vscode/* 29 | !.vscode/settings.json 30 | !.vscode/tasks.json 31 | !.vscode/launch.json 32 | !.vscode/extensions.json 33 | .history/* 34 | 35 | # misc 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /libpeerconnection.log 40 | npm-debug.log 41 | yarn-error.log 42 | testem.log 43 | /typings 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | 'browserName': 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Directive } from '@angular/core'; 2 | import { UserService } from '@shared'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.less'], 8 | }) 9 | export class AppComponent { 10 | constructor(private us: UserService) {} 11 | 12 | title = 'ng8'; 13 | 14 | show(): void {} 15 | } 16 | 17 | @Component({ 18 | selector: 'xx-editor', 19 | template: ` 20 |

27 | Share Editor 28 |

29 |

asdf

30 | 31 | `, 32 | }) 33 | export class EditorComponent {} 34 | 35 | @Directive({ 36 | selector: '[xxDirective]', 37 | }) 38 | export class CustomDirective {} 39 | 40 | // tslint:disable-next-line: interface-name 41 | export interface IUser { 42 | id: number; 43 | } 44 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/ng8'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | Welcome to {{ title }}! 5 |

6 | Angular Logo 7 |
8 |

Here are some links to help you start:

9 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'ng8'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('ng8'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to ng8!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ng8", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "e2e": "ng e2e", 10 | "lint": "npm run lint:ts && npm run lint:style", 11 | "lint:ts": "tslint -p tsconfig.app.json -c tslint.json 'src/**/*.ts' --fix", 12 | "lint:style": "stylelint 'src/**/*.less' --syntax less --fix" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular/animations": "~8.0.0", 17 | "@angular/common": "~8.0.0", 18 | "@angular/compiler": "~8.0.0", 19 | "@angular/core": "~8.0.0", 20 | "@angular/forms": "~8.0.0", 21 | "@angular/platform-browser": "~8.0.0", 22 | "@angular/platform-browser-dynamic": "~8.0.0", 23 | "@angular/router": "~8.0.0", 24 | "rxjs": "~6.4.0", 25 | "tslib": "^1.9.0", 26 | "zone.js": "~0.9.1" 27 | }, 28 | "devDependencies": { 29 | "@angular-devkit/build-angular": "~0.800.0", 30 | "@angular/cli": "~8.0.0", 31 | "@angular/compiler-cli": "~8.0.0", 32 | "@angular/language-service": "~8.0.0", 33 | "@types/node": "~8.9.4", 34 | "@types/jasmine": "~3.3.8", 35 | "@types/jasminewd2": "~2.0.3", 36 | "codelyzer": "^5.0.0", 37 | "jasmine-core": "~3.4.0", 38 | "jasmine-spec-reporter": "~4.2.1", 39 | "karma": "~4.1.0", 40 | "karma-chrome-launcher": "~2.2.0", 41 | "karma-coverage-istanbul-reporter": "~2.0.1", 42 | "karma-jasmine": "~2.0.1", 43 | "karma-jasmine-html-reporter": "^1.4.0", 44 | "protractor": "~5.4.0", 45 | "ts-node": "~7.0.0", 46 | "tslint": "~5.15.0", 47 | "typescript": "~3.4.3", 48 | "husky": "^1.0.1", 49 | "lint-staged": "^8.1.7", 50 | "tslint-config-prettier": "^1.18.0", 51 | "prettier": "^1.17.1", 52 | "prettier-stylelint": "^0.4.2", 53 | "stylelint": "^10.0.1", 54 | "stylelint-config-prettier": "^5.1.0", 55 | "stylelint-config-rational-order": "^0.1.2", 56 | "stylelint-config-standard": "^18.3.0", 57 | "stylelint-declaration-block-no-ignored-properties": "^2.1.0", 58 | "stylelint-order": "^3.0.0" 59 | }, 60 | "lint-staged": { 61 | "src/**/*.ts": [ 62 | "npm run lint:ts", 63 | "git add" 64 | ], 65 | "src/**/*.less": [ 66 | "npm run lint:style", 67 | "git add" 68 | ] 69 | }, 70 | "husky": { 71 | "hooks": { 72 | "pre-commit": "lint-staged" 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-config-prettier" 5 | ], 6 | "rulesDirectory": [ 7 | "codelyzer" 8 | ], 9 | "rules": { 10 | "array-type": false, 11 | "arrow-parens": false, 12 | "deprecation": { 13 | "severity": "warn" 14 | }, 15 | "component-class-suffix": true, 16 | "contextual-lifecycle": true, 17 | "directive-class-suffix": true, 18 | "directive-selector": [ 19 | true, 20 | "attribute", 21 | ["app", "xx"], 22 | "camelCase" 23 | ], 24 | "component-selector": [ 25 | true, 26 | "element", 27 | ["app", "xx"], 28 | "kebab-case" 29 | ], 30 | "import-blacklist": [ 31 | true, 32 | "rxjs/Rx" 33 | ], 34 | "interface-name": false, 35 | "max-classes-per-file": false, 36 | "max-line-length": [ 37 | false, 38 | 140 39 | ], 40 | "member-access": false, 41 | "member-ordering": [ 42 | true, 43 | { 44 | "order": [ 45 | "static-field", 46 | "instance-field", 47 | "static-method", 48 | "instance-method" 49 | ] 50 | } 51 | ], 52 | "no-consecutive-blank-lines": false, 53 | "no-console": [ 54 | true, 55 | "debug", 56 | "info", 57 | "time", 58 | "timeEnd", 59 | "trace" 60 | ], 61 | "no-empty": false, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-non-null-assertion": true, 67 | "no-redundant-jsdoc": true, 68 | "no-switch-case-fall-through": true, 69 | "no-use-before-declare": true, 70 | "no-var-requires": false, 71 | "object-literal-key-quotes": [ 72 | false, 73 | "as-needed" 74 | ], 75 | "object-literal-sort-keys": false, 76 | "ordered-imports": false, 77 | "quotemark": [ 78 | false, 79 | "single" 80 | ], 81 | "trailing-comma": false, 82 | "no-conflicting-lifecycle": true, 83 | "no-host-metadata-property": true, 84 | "no-input-rename": true, 85 | "no-inputs-metadata-property": true, 86 | "no-output-native": true, 87 | "no-output-on-prefix": true, 88 | "no-output-rename": true, 89 | "no-outputs-metadata-property": true, 90 | "template-banana-in-box": true, 91 | "template-no-negated-async": true, 92 | "use-lifecycle-interface": true, 93 | "use-pipe-transform-interface": true 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ng8": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "less" 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:browser", 19 | "options": { 20 | "outputPath": "dist/ng8", 21 | "index": "src/index.html", 22 | "main": "src/main.ts", 23 | "polyfills": "src/polyfills.ts", 24 | "tsConfig": "tsconfig.app.json", 25 | "assets": [ 26 | "src/favicon.ico", 27 | "src/assets" 28 | ], 29 | "styles": [ 30 | "src/styles.less" 31 | ], 32 | "scripts": [] 33 | }, 34 | "configurations": { 35 | "production": { 36 | "fileReplacements": [ 37 | { 38 | "replace": "src/environments/environment.ts", 39 | "with": "src/environments/environment.prod.ts" 40 | } 41 | ], 42 | "optimization": true, 43 | "outputHashing": "all", 44 | "sourceMap": false, 45 | "extractCss": true, 46 | "namedChunks": false, 47 | "aot": true, 48 | "extractLicenses": true, 49 | "vendorChunk": false, 50 | "buildOptimizer": true, 51 | "budgets": [ 52 | { 53 | "type": "initial", 54 | "maximumWarning": "2mb", 55 | "maximumError": "5mb" 56 | } 57 | ] 58 | } 59 | } 60 | }, 61 | "serve": { 62 | "builder": "@angular-devkit/build-angular:dev-server", 63 | "options": { 64 | "browserTarget": "ng8:build" 65 | }, 66 | "configurations": { 67 | "production": { 68 | "browserTarget": "ng8:build:production" 69 | } 70 | } 71 | }, 72 | "extract-i18n": { 73 | "builder": "@angular-devkit/build-angular:extract-i18n", 74 | "options": { 75 | "browserTarget": "ng8:build" 76 | } 77 | }, 78 | "test": { 79 | "builder": "@angular-devkit/build-angular:karma", 80 | "options": { 81 | "main": "src/test.ts", 82 | "polyfills": "src/polyfills.ts", 83 | "tsConfig": "tsconfig.spec.json", 84 | "karmaConfig": "karma.conf.js", 85 | "assets": [ 86 | "src/favicon.ico", 87 | "src/assets" 88 | ], 89 | "styles": [ 90 | "src/styles.less" 91 | ], 92 | "scripts": [] 93 | } 94 | }, 95 | "lint": { 96 | "builder": "@angular-devkit/build-angular:tslint", 97 | "options": { 98 | "tsConfig": [ 99 | "tsconfig.app.json", 100 | "tsconfig.spec.json", 101 | "e2e/tsconfig.json" 102 | ], 103 | "exclude": [ 104 | "**/node_modules/**" 105 | ] 106 | } 107 | }, 108 | "e2e": { 109 | "builder": "@angular-devkit/build-angular:protractor", 110 | "options": { 111 | "protractorConfig": "e2e/protractor.conf.js", 112 | "devServerTarget": "ng8:serve" 113 | }, 114 | "configurations": { 115 | "production": { 116 | "devServerTarget": "ng8:serve:production" 117 | } 118 | } 119 | } 120 | } 121 | }}, 122 | "defaultProject": "ng8" 123 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular代码风格 2 | 3 | ## 写在前面 4 | 5 | 自身的良好编码风格只能律己,而无法律人;我喜欢 Angular 其中主要一个因素是有一整套的[工具](https://cli.angular.io/)及[风格指南](https://angular.io/guide/styleguide),它可以极大的简化团队开发沟通成本,但是有些小缺失例如在编码风格上官方只提供 TypeScript 的部分,对于其他文件并没有一套指南以及智能化。 6 | 7 | VSCode 是我开发 Angular 应用的首选,本文也将以此 IDE 为基准;任何提到的扩展都可以通过[市场](https://marketplace.visualstudio.com/)来获取。 8 | 9 | Angular 应用是由组件树组成,一个组件从文件来看包含:TypeScript、HTML、Less(或其他 CSS 预处理器),其中 HTML 可能被包含至 ts 文件里。 10 | 11 | > 当然除此之外还包含一些 JSON 文件、Bash 文件等,当此部分不在本文讨论内。 12 | 13 | ## TSLint 14 | 15 | Angular 创建后就已经包含 `tslint.json`(它是 TSLint 的配置文件),并且所有默认规则都按官方[风格指南](https://angular.io/guide/styleguide)具体践行。 16 | 17 | 而 TSLint 的[配置](https://palantir.github.io/tslint/usage/configuration/)文件,默认使用内置预设 [tslint:recommended](https://github.com/palantir/tslint/blob/master/src/configs/recommended.ts) 版本,并在此基础上加入 Angular 质量检查工具 [codelyzer](https://github.com/mgechev/codelyzer),所有这些规则你可以通过 [tslint rules](https://palantir.github.io/tslint/rules/)、[codelyzer](https://github.com/mgechev/codelyzer#rules-status) 找到每项规则的说明。 18 | 19 | > 规则的写法要么是 `boolean` 类型,或者使用数组对该规则指定额外参数。 20 | 21 | 运行 `ng lint` 命令时,当你某个字符串变量使用双引号,它会提示: 22 | 23 | ``` 24 | ERROR: /src/app/app.component.ts[9, 16]: " should be ' 25 | ``` 26 | 27 | 我们也可以安装 [TSLint](https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-tslint-plugin) 扩展让这个触发机制放在正在编码过程中实时反馈: 28 | 29 | 1. 当有不符合风格指南会出现一个绿色的波浪线,按 `command+.` > `Fix: " Should be '` 30 | 2. 通过终端 `PROBLEMS` 面板查看所有已打开文件且不符合风格指南的明细 31 | 32 | 嗯,让你按五次 `command+.` 快捷键,我一定会疯掉;TSLint 扩展支持在保存文件时自动修复,只需要在项目根目录 `.vscode/settings.json` 配置: 33 | 34 | ```json 35 | { 36 | "editor.codeActionsOnSave": { 37 | "source.fixAll": true 38 | } 39 | } 40 | ``` 41 | 42 | ### 配置 43 | 44 | `tsline.json` 有许多规则是针对任何 TypeScript,而 codelyzer 是专门针对 Angular,以下几个可能你需要认识项: 45 | 46 | **directive-selector**、**component-selector** 47 | 48 | 限定 Direcitve、Component 的 `selector` 选择器属性值的风格,默认必须是 `app` 开头,有时候对于一些共享型组件设置不同的风格,例如一个业务型富文本框 `xx-editor`,需要在 `tslint.json` 修改配置: 49 | 50 | ```json 51 | "directive-selector": [ 52 | true, 53 | "attribute", 54 | ["app", "xx"], 55 | "camelCase" 56 | ], 57 | "component-selector": [ 58 | true, 59 | "element", 60 | ["app", "xx"], 61 | "kebab-case" 62 | ] 63 | ``` 64 | 65 | **component-class-suffix**、**directive-class-suffix** 66 | 67 | 指令或组件类名必须是写驼峰命名法来命名,且必须使用 `Component`、`Directive` 后缀;若团队可能已经习惯类似 `View` 作为后缀,则: 68 | 69 | ```json 70 | "component-class-suffix": [true, "Component", "View"], 71 | "directive-class-suffix": [true, "Directive", "View"] 72 | ``` 73 | 74 | **use-life-cycle-interface** 75 | 76 | 强制实现生命周期钩子接口,例如 `ngOnInit`: 77 | 78 | ```ts 79 | export class HeroButtonComponent implements OnInit { 80 | ngOnInit() { 81 | console.log('The component is initialized'); 82 | } 83 | } 84 | ``` 85 | 86 | **interface-name** 87 | 88 | [TypeScript 指导原则](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#names)**不建议**使用 “I” 前缀;因此建议增加: 89 | 90 | ```json 91 | "interface-name": [true, "never-prefix"] 92 | ``` 93 | 94 | ```ts 95 | // 错误写法 96 | export interface IUser { 97 | id: number; 98 | } 99 | // 正确写法 100 | export interface User { 101 | id: number; 102 | } 103 | ``` 104 | 105 | ## 美化 106 | 107 | TSLint 并不支持美化(虽然有几个项看起来像是在“美化”),而美化的工作取决于你采用什么 IDE,例如 VSCode 默认是使用 `4` 个空格表示一个 Tab 键。 108 | 109 | ### EditorConfig 110 | 111 | 我不建议依赖 IDE 默认的代码格式配置,所以就有一个 [.editorconfig](https://editorconfig.org/) 组织来规范一些简单的统一规范配置。 112 | 113 | Angular 项目时也会有 `.editorconfig` 的配置,虽然有这个配置文件,但在 VSCode 也有自己的一套规范并且优先级更高,所以要想让 EditorConfig 生效需要额外安装 [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) 插件。 114 | 115 | Editorconfig 只包含一些最基础的项,要想让代码统一风格的美化还是需要更强大的 Prettier。 116 | 117 | ### Prettier 118 | 119 | 她支持市场上许多语言,其中包含 TypeScript、HTML、Less 这一些都符合 Angular 项目的必备;你需要引入 [prettier](https://prettier.io/) 以及 VSCode [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) 扩展。 120 | 121 | **配置** 122 | 123 | 在根目录下创建 `.prettierrc` 配置文件以及 `.prettierignore` 忽略美化配置文件;Editorconfig 选项与 Prettier 选项高度重叠,并且后者会强制替换前者,例如: 124 | 125 | ```yaml 126 | # .editorconfig 127 | indent_size = 4 128 | ``` 129 | 130 | ```json 131 | # .prettierrc 132 | { 133 | "tabWidth": 2 134 | } 135 | ``` 136 | 137 | 表示一个 Tab 宽度使用 `2` 个空格;我个人建议 Prettier **不应该覆盖** EditorConfig 的部分,它们包含:`tabWidth`、`useTabs`、`endOfLine`。 138 | 139 | 而一个简单的 Prettier 配置差不多这样: 140 | 141 | ```json 142 | { 143 | // 单行最大长度 144 | "printWidth": 140, 145 | // 语句末尾添加分号 146 | "semi": true, 147 | // 使用单引号而非双引号 148 | "singleQuote": true, 149 | // 尾逗号 150 | "trailingComma": "all" 151 | } 152 | ``` 153 | 154 | 如果你想忽略一些不想美化的文件,例如 Markdown,则 `. prettierignore`: 155 | 156 | ```yaml 157 | *.md 158 | ``` 159 | 160 | 同样如果你期望每次保存文件时自动美化代码,只需要在项目根目录 `.vscode/settings.json` 配置: 161 | 162 | ```json 163 | { 164 | "editor.formatOnSave": true 165 | } 166 | ``` 167 | 168 | ### Prettier 与 TSLint 169 | 170 | [Prettier 配置](https://prettier.io/docs/en/configuration.html#basic-configuration) 项会有部分与 `tslint.json` 项重复,例如:Prettier 的 `printWidth` 与 `tsline.json` 的 `max-line-length`,对于 Prettier 以她为优先,反之使用 `ng lint` 会以 `tslint.json` 优先。 171 | 172 | 这对我们来说有些困惑,TSLint 包含了一些代码”美化性"(例如:`max-line-length`),事实上,这更应该是 Prettier 的专长([所有配置](https://prettier.io/docs/en/options.html)都跟代码美化相关)。 173 | 174 | **tslint-config-prettier** 175 | 176 | 好在 [tslint-config-prettier](https://github.com/prettier/tslint-config-prettier) 帮助清理这些可能会产生冲突规则的解决方案: 177 | 178 | ```json 179 | "extends": [ 180 | "tslint:recommended", 181 | "tslint-config-prettier" 182 | ] 183 | ``` 184 | 185 | 将 `prettier` 提供一种检查机制: 186 | 187 | ```bash 188 | tslint-config-prettier-check ./tslint.json 189 | ``` 190 | 191 | 你会发现默认的 Angular 项目中会有 `max-line-length`、`object-literal-key-quotes`、`quotemark` 三项是冲突的,我们可以关掉 `tsline.json` 这三项的配置,让 Prettier 来代替。 192 | 193 | ```json 194 | { 195 | "rules": { 196 | "max-line-length": [false, 140], 197 | "object-literal-key-quotes": [false, "as-needed"], 198 | "quotemark": [false, "single"] 199 | } 200 | } 201 | ``` 202 | 203 | ### HTML 204 | 205 | Prettier 默认会自动识别 Angular 项目并使用其引擎,当然也包含对 `template` 或 `templateUrl` 两种写法。 206 | 207 | **printWidth** 208 | 209 | 会决定一段 HTML 超出长度范围后属性会自动换行: 210 | 211 | ```html 212 |

213 | Share Component 214 |

215 | ``` 216 | 217 | 变成: 218 | 219 | ```html 220 |

227 | Share Component 228 |

229 | ``` 230 | 231 | **htmlWhitespaceSensitivity** 232 | 233 | 空白敏感这一点同 Angular 的 `preserveWhitespaces` 类似,如果你的 Angular 项目配置了 `preserveWhitespaces: false` 则无须理会;反之设定不同的参数会影响美化的效果,若项目对空白敏感有需求可以设定为 `strict` 会强制清除空白,例如: 234 | 235 | ```html 236 |

Share Component

244 | ``` 245 | 246 | ### Less 247 | 248 | 不管哪种 CSS 预处理器都可以使用 [stylelint](https://stylelint.io/) 作为代码检查工具,安装 [stylelint](https://www.npmjs.com/package/stylelint)、[stylelint-config-standard](https://www.npmjs.com/package/stylelint-config-standard) 并在根目录 `.stylelintrc` 配置: 249 | 250 | ```json 251 | { 252 | "extends": [ "stylelint-config-standard" ], 253 | "plugins": [ ], 254 | "rules": { }, 255 | "ignoreFiles": [ "src/assets/**/*" ] 256 | } 257 | ``` 258 | 259 | 在 `package.json` 定义一条: 260 | 261 | ```json 262 | { 263 | "scripts": { 264 | "lint:style": "stylelint 'src/**/*.less' --syntax less" 265 | } 266 | } 267 | ``` 268 | 269 | 若缺少 `;` 会被收到 `Missed semicolon` 错误: 270 | 271 | ```less 272 | :host { 273 | width: 100px; 274 | height: 100px 275 | display: block; 276 | } 277 | ``` 278 | 279 | ``` 280 | src/app/app.component.less 281 | 3:11 ✖ Missed semicolon CssSyntaxError 282 | ``` 283 | 284 | > stylelint 有自己的一套[规则](https://github.com/stylelint/stylelint/blob/master/docs/user-guide/rules.md),而 [stylelint-config-standard](https://www.npmjs.com/package/stylelint-config-standard) 只是官方提供一种[默认规则](https://github.com/stylelint/stylelint-config-standard/blob/master/index.js)。 285 | 286 | #### Prettier 287 | 288 | 前面提到 Prettier 也支持 Less,需要额外安装依赖包 [prettier-stylelint](https://github.com/hugomrdias/prettier-stylelint) ,并修改 `.stylelintrc` 配置: 289 | 290 | ```json 291 | { 292 | "extends": [ 293 | "stylelint-config-standard", 294 | "stylelint-config-prettier" 295 | ], 296 | "plugins": [ ], 297 | "rules": { 298 | "selector-type-no-unknown": [ 299 | true, 300 | { 301 | "ignoreTypes": [ 302 | "/^app-/" 303 | ] 304 | } 305 | ], 306 | "selector-pseudo-element-no-unknown": [ 307 | true, 308 | { 309 | "ignorePseudoElements": [ 310 | "ng-deep" 311 | ] 312 | } 313 | ] 314 | }, 315 | "ignoreFiles": [ "src/assets/**/*" ] 316 | } 317 | ``` 318 | 319 | 但是依然无法生效,由于在 VSCode 下面 [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) 扩展默认并没有开启它,在 `.vscode/settings.json` 增加: 320 | 321 | ```json 322 | { 323 | "prettier.stylelintIntegration": true 324 | } 325 | ``` 326 | 327 | 加上之前已经开启保存时自动修复功能,同样也适用 Less 即保存时根据 `.stylelintrc` 配置修复及美化。 328 | 329 | #### 一些有趣的插件 330 | 331 | **stylelint-config-rational-order** 332 | 333 | Css 语言同一类型的相关属性可能会很多,而将这些相关属性进行分组对于维护非常有帮助,安装 [stylelint-order](https://www.npmjs.com/package/stylelint-order)、[stylelint-config-rational-order](https://www.npmjs.com/package/stylelint-config-rational-order),并修改 `.stylelintrc` 配置: 334 | 335 | ```json 336 | { 337 | "extends": [ 338 | "stylelint-config-standard", 339 | "stylelint-config-rational-order", 340 | "stylelint-config-prettier" 341 | ], 342 | "plugins": [ 343 | "stylelint-order" 344 | ], 345 | "rules": { }, 346 | "ignoreFiles": [ "src/assets/**/*" ] 347 | } 348 | ``` 349 | 350 | **stylelint-declaration-block-no-ignored-properties** 351 | 352 | 当设置 `display: inline` 内联时,此时再写 `width: 100px` 是无意义的,而该组件可以自动移除这种无效属性。 353 | 354 | ```json 355 | { 356 | "plugins": [ 357 | "stylelint-declaration-block-no-ignored-properties" 358 | ], 359 | "rules": { 360 | "plugin/declaration-block-no-ignored-properties": true, 361 | } 362 | } 363 | ``` 364 | 365 | ## 智能点 366 | 367 | 至此,涉及 Angular 所需要的代码风格运用已全部完结,这些检查我们都大多数是依靠 VSCode 编辑器的扩展辅助完成。 368 | 369 | 事实上,我们只需要增加几个命令来对整个项目进行检查,确保整个项目的代码能按所配置的风格执行。 370 | 371 | ### 命令行 372 | 373 | ```json 374 | { 375 | "scripts": { 376 | "lint": "npm run lint:ts && npm run lint:style", 377 | "lint:ts": "tslint -p tsconfig.app.json -c tslint.json 'src/**/*.ts' --fix", 378 | "lint:style": "stylelint 'src/**/*.less' --syntax less --fix" 379 | } 380 | } 381 | ``` 382 | 383 | > `tslint`、`stylelint` 命令行对应的参数说明,都可以通过上述提供官网找得到。 384 | 385 | 运行 `npm run lint` 可以对整个项目进行检查及修复;若有包含 CI 可以直接使用它。 386 | 387 | ### Git 388 | 389 | 如果可以将这一过程在向源码仓库提交代码时进行检查的话,可以在向代码仓储提交前就发现问题,需要安装 [husky](https://www.npmjs.com/package/husky)、[lint-staged](https://www.npmjs.com/package/lint-staged),并且修改 `package.json`: 390 | 391 | ```json 392 | { 393 | "lint-staged": { 394 | "src/**/*.ts": [ 395 | "npm run lint:ts", 396 | "git add" 397 | ], 398 | "src/**/*.less": [ 399 | "npm run lint:style", 400 | "git add" 401 | ] 402 | }, 403 | "husky": { 404 | "hooks": { 405 | "pre-commit": "lint-staged" 406 | } 407 | } 408 | } 409 | ``` 410 | 411 | 当向源码仓库 Commit 时会自动先执行命令行才会 `git add`。 412 | 413 | ## 总结 414 | 415 | 当我写完最后一节时,Angular8 发布了正式版;所以我把原本准备的样板项目 [ng-code-style-boilerplate](https://github.com/cipchk/ng-code-style-boilerplate)([f526a0c](https://github.com/cipchk/ng-code-style-boilerplate/commit/f526a0c)) 切换成 Angular8,但完全适用 Angular7.x 版本。 416 | 417 | [Angular 风格指南](https://angular.io/guide/styleguide)([中文版](https://angular.cn/guide/styleguide))对于喜爱 Angular 是必读、常读的文章,它指引团队更友好的编写代码,个人良好编码风格可能律己,但无法律人,而这种风格指南可以减少无差别的团队沟通。 418 | 419 | > 本文虽然以 Angular 的角度出发,但大部分内容同样适用 React、Vue 等。 420 | 421 | (完) 422 | --------------------------------------------------------------------------------