diff --git a/.eslintignore b/.eslintignore index 3b37b3fb..2b7f193b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,12 +1 @@ -/.idea/ -/coverage/ -/dist/ -/docs/ -/node_modules/ - -karma.conf.js -*.e2e-spec.ts -*.spec.ts -*.js - -/projects/micro-dash/ +**/*.dts-spec.ts diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..612410d8 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": "./projects/ng-dev/src/eslint-config.json", + "overrides": [ + { + "files": ["*.ts"], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { "type": "attribute", "prefix": "s", "style": "camelCase" } + ], + "@angular-eslint/component-selector": [ + "error", + { "type": "element", "prefix": "s", "style": "kebab-case" } + ] + } + } + ] +} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 523992ee..8342a261 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,10 +20,6 @@ jobs: - run: yarn test --no-watch --no-progress --browsers=ChromeHeadlessCI - - run: sudo apt-get update - - run: sudo apt-get install google-chrome-stable - - run: yarn e2e integration --protractorConfig=projects/integration/e2e/protractor-ci.conf.js - lint: runs-on: ubuntu-latest steps: @@ -41,5 +37,5 @@ jobs: - run: yarn build-libs - - run: yarn eslint + - run: yarn ng lint --quiet - run: yarn ts-node scripts/dtslint-all diff --git a/.idea/s-libs.iml b/.idea/s-libs.iml index 0cc4742e..8c356fc0 100644 --- a/.idea/s-libs.iml +++ b/.idea/s-libs.iml @@ -12,6 +12,7 @@ + diff --git a/TODO.md b/TODO.md index 119d1746..970bddb4 100644 --- a/TODO.md +++ b/TODO.md @@ -5,6 +5,7 @@ - Check for cool new TS features to use. - Open issue request about enabling ivy for ng-dev? +- Re-enable eslint rules to ban types in micro-dash. Figure it out. - landing page to link to all API docs - coveralls - help may be here, to combine multiple coverage runs into one report: https://github.com/angular/angular-cli/issues/11268 diff --git a/angular.json b/angular.json index e455e1f6..f5f658de 100644 --- a/angular.json +++ b/angular.json @@ -3,7 +3,8 @@ "version": 1, "cli": { "packageManager": "yarn", - "analytics": "d2657102-efac-4510-9c45-87ca8fcdb63c" + "analytics": "d2657102-efac-4510-9c45-87ca8fcdb63c", + "defaultCollection": "@angular-eslint/schematics" }, "newProjectRoot": "projects", "projects": { @@ -35,6 +36,15 @@ "tsConfig": "projects/app-state/tsconfig.spec.json", "karmaConfig": "projects/app-state/karma.conf.js" } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/app-state/**/*.ts", + "projects/app-state/**/*.html" + ] + } } } }, @@ -64,7 +74,9 @@ "projects/integration/src/favicon.ico", "projects/integration/src/assets" ], - "styles": ["projects/integration/src/styles.scss"], + "styles": [ + "projects/integration/src/styles.scss" + ], "scripts": [], "vendorChunk": true, "extractLicenses": false, @@ -135,7 +147,9 @@ "projects/integration/src/favicon.ico", "projects/integration/src/assets" ], - "styles": ["projects/integration/src/styles.scss"], + "styles": [ + "projects/integration/src/styles.scss" + ], "scripts": [ "node_modules/rxjs/bundles/rxjs.umd.js", "node_modules/@angular/core/bundles/core.umd.js", @@ -149,6 +163,15 @@ "dist/ng-app-state/bundles/ng-app-state.umd.js" ] } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/integration/**/*.ts", + "projects/integration/**/*.html" + ] + } } } }, @@ -180,6 +203,15 @@ "tsConfig": "projects/js-core/tsconfig.spec.json", "karmaConfig": "projects/js-core/karma.conf.js" } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/js-core/**/*.ts", + "projects/js-core/**/*.html" + ] + } } } }, @@ -211,6 +243,15 @@ "tsConfig": "projects/micro-dash/tsconfig.spec.json", "karmaConfig": "projects/micro-dash/karma.conf.js" } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/micro-dash/**/*.ts", + "projects/micro-dash/**/*.html" + ] + } } } }, @@ -272,6 +313,15 @@ "options": { "browserTarget": "micro-dash-sizes:build" } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/micro-dash-sizes/**/*.ts", + "projects/micro-dash-sizes/**/*.html" + ] + } } } }, @@ -303,6 +353,15 @@ "tsConfig": "projects/ng-app-state/tsconfig.spec.json", "karmaConfig": "projects/ng-app-state/karma.conf.js" } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/ng-app-state/**/*.ts", + "projects/ng-app-state/**/*.html" + ] + } } } }, @@ -335,6 +394,15 @@ "karmaConfig": "projects/ng-core/karma.conf.js", "polyfills": "projects/ng-core/src/test-polyfills.ts" } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/ng-core/**/*.ts", + "projects/ng-core/**/*.html" + ] + } } } }, @@ -367,6 +435,15 @@ "karmaConfig": "projects/ng-dev/karma.conf.js", "polyfills": "projects/ng-dev/src/test-polyfills.ts" } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/ng-dev/**/*.ts", + "projects/ng-dev/**/*.html" + ] + } } } }, @@ -399,6 +476,15 @@ "karmaConfig": "projects/rxjs-core/karma.conf.js", "polyfills": "projects/rxjs-core/src/test-polyfills.ts" } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": [ + "projects/rxjs-core/**/*.ts", + "projects/rxjs-core/**/*.html" + ] + } } } } diff --git a/package.json b/package.json index 7ca663e0..1ade66b3 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "e2e": "ng e2e", "build-libs": "yarn ts-node scripts/build-libs", "dtslint": "dtslint --expectOnly --localTs node_modules/typescript/lib", - "eslint": "eslint . --ext .ts,.tsx", "prettier-all": "prettier --write \"**/*.{html,js,json,md,scss,ts,yml}\"", "docs": "yarn ts-node scripts/docs-libs", "git-publish": "standard-version --commit-all", @@ -29,7 +28,6 @@ "@angular/router": "~12.0.0", "@typescript-eslint/eslint-plugin": "^4.23.0", "@typescript-eslint/parser": "^4.23.0", - "eslint": "^7.26.0", "rxjs": "~6.6.0", "tslib": "^2.1.0", "utility-types": "~3.10.0", @@ -37,6 +35,11 @@ }, "devDependencies": { "@angular-devkit/build-angular": "~12.0.0", + "@angular-eslint/builder": "12.0.0", + "@angular-eslint/eslint-plugin": "12.0.0", + "@angular-eslint/eslint-plugin-template": "12.0.0", + "@angular-eslint/schematics": "12.0.0", + "@angular-eslint/template-parser": "12.0.0", "@angular/cdk": "~12.0.0", "@angular/cli": "~12.0.0", "@angular/compiler-cli": "~12.0.0", @@ -45,7 +48,10 @@ "@types/lodash-es": "^4.17.3", "@types/node": "^12.11.1", "@types/prettier": "^2.0.1", + "@typescript-eslint/eslint-plugin": "4.23.0", + "@typescript-eslint/parser": "4.23.0", "dtslint": "^4.0.6", + "eslint": "^7.26.0", "expect-type": "^0.11.0", "glob": "^7.1.6", "jasmine-core": "~3.7.0", diff --git a/projects/app-state/.eslintrc.json b/projects/app-state/.eslintrc.json new file mode 100644 index 00000000..f0b051a0 --- /dev/null +++ b/projects/app-state/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": [ + "projects/app-state/tsconfig.lib.json", + "projects/app-state/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": {} + }, + { "files": ["*.html"], "rules": {} } + ] +} diff --git a/projects/app-state/src/lib/child-store.spec.ts b/projects/app-state/src/lib/child-store.spec.ts index f7d55e38..cc1f1c5d 100644 --- a/projects/app-state/src/lib/child-store.spec.ts +++ b/projects/app-state/src/lib/child-store.spec.ts @@ -80,7 +80,7 @@ describe('ChildStore', () => { }); it('gets the new subvalue even when it has a later subscriber (production bug)', () => { - let expectedValue: InnerState | undefined; + let expectedValue: InnerState | undefined = undefined; store.$.subscribe(() => { expect(store('optional').state()).toBe(expectedValue); }); diff --git a/projects/app-state/src/lib/root-store.spec.ts b/projects/app-state/src/lib/root-store.spec.ts index 70a0ef75..9eeec764 100644 --- a/projects/app-state/src/lib/root-store.spec.ts +++ b/projects/app-state/src/lib/root-store.spec.ts @@ -94,7 +94,7 @@ describe('RootStore', () => { it("doesn't have that infinite loop with 2 stores (production bug)", () => { // https://github.com/simontonsoftware/ng-app-state/issues/28 - const store2 = new RootStore<{}>({}); + const store2 = new RootStore>({}); store.$.subscribe(() => { store2.batch(noop); }); diff --git a/projects/app-state/src/lib/store.spec.ts b/projects/app-state/src/lib/store.spec.ts index 294662e8..0d1bce89 100644 --- a/projects/app-state/src/lib/store.spec.ts +++ b/projects/app-state/src/lib/store.spec.ts @@ -123,8 +123,8 @@ describe('Store', () => { }); // https://github.com/simontonsoftware/ng-app-state/issues/13 - it('does not emit stale values in the middle of propogating a change (production bug)', () => { - let log: jasmine.Spy | undefined; + it('does not emit stale values in the middle of propagating a change (production bug)', () => { + let log: jasmine.Spy | undefined = undefined; store.$.subscribe(() => { store('optional').$.subscribe(log); }); diff --git a/projects/app-state/src/lib/store.ts b/projects/app-state/src/lib/store.ts index 601846dc..5e18054a 100644 --- a/projects/app-state/src/lib/store.ts +++ b/projects/app-state/src/lib/store.ts @@ -7,7 +7,6 @@ import { ChildStore, RootStore } from './index'; type GetSlice = (attr: K) => Store; export interface Store extends GetSlice { - // tslint:disable:callable-types /** * Select a slice of the store to operate on. For example `store('currentUser')` will return a new `Store` that represents the `currentUser` property. */ diff --git a/projects/integration/.eslintrc.json b/projects/integration/.eslintrc.json new file mode 100644 index 00000000..708f25dc --- /dev/null +++ b/projects/integration/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": [ + "projects/integration/tsconfig.app.json", + "projects/integration/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": {} + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/projects/integration/e2e/.eslintrc.json b/projects/integration/e2e/.eslintrc.json new file mode 100644 index 00000000..c105b599 --- /dev/null +++ b/projects/integration/e2e/.eslintrc.json @@ -0,0 +1 @@ +{ "ignorePatterns": ["**/*"] } diff --git a/projects/integration/src/app/api-tests/ng-app-state.spec.ts b/projects/integration/src/app/api-tests/ng-app-state.spec.ts index 6c60f707..6eb9e81d 100644 --- a/projects/integration/src/app/api-tests/ng-app-state.spec.ts +++ b/projects/integration/src/app/api-tests/ng-app-state.spec.ts @@ -8,7 +8,7 @@ import { import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { RootStore } from '@s-libs/app-state'; -import { keys } from '@s-libs/micro-dash'; +import { keys, noop } from "@s-libs/micro-dash"; import * as ngAppState from '@s-libs/ng-app-state'; import { NasModelModule } from '@s-libs/ng-app-state'; @@ -78,7 +78,7 @@ describe('ng-app-state', () => { { extraDirectives = [] as Array>, template = '', - beforeCreate = () => {}, + beforeCreate = noop, } = {}, ): S { if (template) { diff --git a/projects/js-core/.eslintrc.json b/projects/js-core/.eslintrc.json new file mode 100644 index 00000000..f62ca78e --- /dev/null +++ b/projects/js-core/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": [ + "projects/js-core/tsconfig.lib.json", + "projects/js-core/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": {} + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/projects/js-core/src/lib/assert.spec.ts b/projects/js-core/src/lib/assert.spec.ts index daf624b2..c2192961 100644 --- a/projects/js-core/src/lib/assert.spec.ts +++ b/projects/js-core/src/lib/assert.spec.ts @@ -1,3 +1,4 @@ +import { getArguments } from '../test-helpers/test-utils'; import { assert } from './assert'; describe('assert()', () => { @@ -11,10 +12,7 @@ describe('assert()', () => { }); it('does not throw an error when truthy', () => { - // tslint:disable-next-line:only-arrow-functions - const args = (function (..._: any[]): IArguments { - return arguments; - })([1, 2, 3]); + const args = getArguments(1, 2, 3); expect(() => assert(args)).not.toThrow(); expect(() => assert([1, 2, 3])).not.toThrow(); diff --git a/projects/js-core/src/lib/functions/callable-object.ts b/projects/js-core/src/lib/functions/callable-object.ts index d00f2428..89522a79 100644 --- a/projects/js-core/src/lib/functions/callable-object.ts +++ b/projects/js-core/src/lib/functions/callable-object.ts @@ -1,6 +1,5 @@ /** @hidden */ export interface CallableObject any> { - // tslint:disable-next-line:callable-types (...args: Parameters): ReturnType; } diff --git a/projects/js-core/src/lib/functions/wrap-function.spec.ts b/projects/js-core/src/lib/functions/wrap-function.spec.ts index 64a3c700..e64b9d97 100644 --- a/projects/js-core/src/lib/functions/wrap-function.spec.ts +++ b/projects/js-core/src/lib/functions/wrap-function.spec.ts @@ -20,15 +20,14 @@ describe('wrapFunction()', () => { beforeEach(() => { original = jasmine.createSpy().and.returnValue(toReturn); before = jasmine.createSpy(); - around = jasmine - .createSpy() - .and.callFake(function ( - this: any, - orig: Function, - ...args: any[] - ): [any, symbol] { - return [orig.call(aroundContext, aroundArg, ...args), aroundReturn]; - }); + around = jasmine.createSpy().and.callFake(function ( + this: any, + // eslint-disable-next-line @typescript-eslint/ban-types + orig: Function, + ...args: any[] + ): [any, symbol] { + return [orig.call(aroundContext, aroundArg, ...args), aroundReturn]; + }); transform = jasmine.createSpy().and.returnValue(transformed); after = jasmine.createSpy(); }); diff --git a/projects/js-core/src/lib/functions/wrap-function.ts b/projects/js-core/src/lib/functions/wrap-function.ts index e17fb342..ee099cbe 100644 --- a/projects/js-core/src/lib/functions/wrap-function.ts +++ b/projects/js-core/src/lib/functions/wrap-function.ts @@ -48,6 +48,7 @@ export function wrapFunction( /** @hidden */ function callHook( + // eslint-disable-next-line @typescript-eslint/ban-types hook: Function | undefined, context: any, args: any[], diff --git a/projects/js-core/src/lib/objects/constructor.spec.ts b/projects/js-core/src/lib/objects/constructor.spec.ts new file mode 100644 index 00000000..c38564d7 --- /dev/null +++ b/projects/js-core/src/lib/objects/constructor.spec.ts @@ -0,0 +1,17 @@ +import { Constructor } from './constructor'; +import { expectTypeOf } from 'expect-type'; + +describe('Constructor', () => { + it('can be used for a mixin pattern', () => { + expect().nothing(); + + function mixInSomething(Base: B) { + return class extends Base { + something = true; + }; + } + class DateSomething extends mixInSomething(Date) {} + expectTypeOf().toMatchTypeOf(); + expectTypeOf().toMatchTypeOf<{ something: boolean }>(); + }); +}); diff --git a/projects/js-core/src/lib/objects/constructor.ts b/projects/js-core/src/lib/objects/constructor.ts index 20367bcf..c4922c1c 100644 --- a/projects/js-core/src/lib/objects/constructor.ts +++ b/projects/js-core/src/lib/objects/constructor.ts @@ -1,4 +1,5 @@ /** * Taken from the [typescript docs for mixins](https://www.typescriptlang.org/docs/handbook/mixins.html). */ +// eslint-disable-next-line @typescript-eslint/ban-types -- this comes directly from TS docs (at the link above) export type Constructor = new (...args: any[]) => T; diff --git a/projects/js-core/src/lib/objects/create-builder.spec.ts b/projects/js-core/src/lib/objects/create-builder.spec.ts index eadccc05..da3524b0 100644 --- a/projects/js-core/src/lib/objects/create-builder.spec.ts +++ b/projects/js-core/src/lib/objects/create-builder.spec.ts @@ -28,7 +28,7 @@ describe('createBuilder()', () => { describe('buildDefaults argument', () => { it('is supplied seq and options', () => { - const build = createBuilder((seq, options) => ({ + const build = createBuilder((seq, options) => ({ id: seq, text: JSON.stringify(options), })); @@ -47,7 +47,7 @@ describe('createBuilder()', () => { }); it('is supplied the object to be returned, seq, and options', () => { - const build = createBuilder( + const build = createBuilder( (seq) => ({ id: seq, text: 'hi' }), (obj, seq, options) => { obj.text = JSON.stringify({ origText: obj.text, seq, options }); @@ -82,7 +82,6 @@ describe('createBuilder()', () => { function shiftCharacters(text: string, distance: number): string { return text.replace(/[a-z]/gi, (c) => { - // tslint:disable:no-bitwise const code = c.charCodeAt(0); const a = code & 96; return String.fromCharCode(((code - a + distance + 129) % 26) - ~a); diff --git a/projects/js-core/src/lib/objects/create-builder.ts b/projects/js-core/src/lib/objects/create-builder.ts index be1f0136..2b3372ca 100644 --- a/projects/js-core/src/lib/objects/create-builder.ts +++ b/projects/js-core/src/lib/objects/create-builder.ts @@ -28,16 +28,14 @@ let seq = 0; * buildMessage({ text: "abc" }, { cypherDistance: 3 }); // { id: 3, text: "def" } * ``` */ -export function createBuilder( +export function createBuilder>( buildDefaults: (seq: number, options: Partial) => T, afterBuild?: (obj: T, seq: number, options: Partial) => void, ): (attributes?: Partial, options?: Partial) => T { return (attributes?: Partial, options: Partial = {}): T => { ++seq; const obj = Object.assign(buildDefaults(seq, options), attributes); - if (afterBuild) { - afterBuild(obj, seq, options); - } + afterBuild?.(obj, seq, options); return obj; }; } diff --git a/projects/js-core/src/lib/objects/map-as-keys.spec.ts b/projects/js-core/src/lib/objects/map-as-keys.spec.ts index c9f61797..2f7a4ecf 100644 --- a/projects/js-core/src/lib/objects/map-as-keys.spec.ts +++ b/projects/js-core/src/lib/objects/map-as-keys.spec.ts @@ -1,4 +1,5 @@ import { expectCallsAndReset } from '@s-libs/ng-dev'; +import { expectTypeOf } from 'expect-type'; import { mapAsKeys } from './map-as-keys'; describe('mapAsKeys()', () => { @@ -19,7 +20,9 @@ describe('mapAsKeys()', () => { expect(mapAsKeys({}, iteratee)).toEqual({}); expect(mapAsKeys([], iteratee)).toEqual({}); expect(mapAsKeys(null as [] | null, iteratee)).toEqual({}); - expect(mapAsKeys(undefined as {} | undefined, iteratee)).toEqual({}); + expect( + mapAsKeys(undefined as Record | undefined, iteratee), + ).toEqual({}); }); it('provides the right iteratee arguments', () => { @@ -31,4 +34,63 @@ describe('mapAsKeys()', () => { mapAsKeys({ a: 1, b: 2 }, spy); expectCallsAndReset(spy, [1, 'a'], [2, 'b']); }); + + it('has fancy typing', () => { + expect().nothing(); + + type A = number[]; + type AorU = A | undefined; + type AorN = A | null; + const a = [] as A; + const aOrU = a as AorU; + const aOrN = a as AorN; + expectTypeOf(mapAsKeys(a, () => 'a')).toEqualTypeOf<{ + [x: number]: string; + }>(); + expectTypeOf(mapAsKeys(aOrN, () => 'a')).toEqualTypeOf< + { [x: number]: string } | Record + >(); + expectTypeOf(mapAsKeys(aOrU, () => 'a')).toEqualTypeOf< + { [x: number]: string } | Record + >(); + + type B = Array<'a' | 2>; + type BorU = B | undefined; + type BorN = B | null; + const b = [] as B; + const bOrU = b as BorU; + const bOrN = b as BorN; + expectTypeOf(mapAsKeys(b, () => 'a')).toEqualTypeOf<{ + a: string; + 2: string; + }>(); + expectTypeOf(mapAsKeys(bOrN, () => 0)).toEqualTypeOf< + { a: number; 2: number } | Record + >(); + expectTypeOf(mapAsKeys(bOrU, (): 2 => 2)).toEqualTypeOf< + { a: 2; 2: 2 } | Record + >(); + + interface O { + a: string; + b: number; + } + type OorU = O | undefined; + type OorN = O | null; + const o = {} as O; + const oOrU = o as OorU; + const oOrN = o as OorN; + expectTypeOf(mapAsKeys(o, () => true)).toEqualTypeOf<{ + [x: string]: boolean; + [x: number]: boolean; + }>(); + expectTypeOf(mapAsKeys(oOrU, () => true)).toEqualTypeOf<{ + [x: string]: boolean; + [x: number]: boolean; + }>(); + expectTypeOf(mapAsKeys(oOrN, () => true)).toEqualTypeOf<{ + [x: string]: boolean; + [x: number]: boolean; + }>(); + }); }); diff --git a/projects/js-core/src/lib/objects/map-as-keys.ts b/projects/js-core/src/lib/objects/map-as-keys.ts index d2e3902a..cbf9ac7d 100644 --- a/projects/js-core/src/lib/objects/map-as-keys.ts +++ b/projects/js-core/src/lib/objects/map-as-keys.ts @@ -20,7 +20,7 @@ export function mapAsKeys( export function mapAsKeys( array: K[] | Nil, iteratee: ArrayIteratee, -): { [k in K]: V } | {}; +): { [k in K]: V } | Record; export function mapAsKeys, V>( object: T, iteratee: ObjectIteratee, @@ -28,7 +28,7 @@ export function mapAsKeys, V>( export function mapAsKeys, V>( object: T | Nil, iteratee: ObjectIteratee, -): { [k in T[keyof T]]: V } | {}; +): { [k in T[keyof T]]: V } | Record; export function mapAsKeys(collection: any, iteratee: any): any { return mapToObject(collection, (value, keyOrIndex) => [ diff --git a/projects/js-core/src/lib/predicates/is-defined.spec.ts b/projects/js-core/src/lib/predicates/is-defined.spec.ts index 072793fe..72519c57 100644 --- a/projects/js-core/src/lib/predicates/is-defined.spec.ts +++ b/projects/js-core/src/lib/predicates/is-defined.spec.ts @@ -1,10 +1,7 @@ +import { getArguments } from '../../test-helpers/test-utils'; import { isDefined } from './is-defined'; describe('isDefined()', () => { - function getArguments(): IArguments { - return arguments; - } - it('works', () => { // falsey values expect(isDefined(undefined)).toBe(false); diff --git a/projects/js-core/src/lib/predicates/is-equal-at-depth.ts b/projects/js-core/src/lib/predicates/is-equal-at-depth.ts index 63e4acf8..dd3bbc43 100644 --- a/projects/js-core/src/lib/predicates/is-equal-at-depth.ts +++ b/projects/js-core/src/lib/predicates/is-equal-at-depth.ts @@ -36,7 +36,7 @@ export function isEqualAtDepth(depth: number, value: any, other: any): boolean { /** @hidden */ function hasSameValues(depth: number, value: any, other: any): boolean { for (const key of keys(value)) { - if (!other.hasOwnProperty(key)) { + if (!Object.prototype.hasOwnProperty.call(other, key)) { return false; } } diff --git a/projects/js-core/src/lib/predicates/is-falsy.spec.ts b/projects/js-core/src/lib/predicates/is-falsy.spec.ts index 85dfe6c5..7c49c11c 100644 --- a/projects/js-core/src/lib/predicates/is-falsy.spec.ts +++ b/projects/js-core/src/lib/predicates/is-falsy.spec.ts @@ -1,3 +1,4 @@ +import { getArguments } from '../../test-helpers/test-utils'; import { isFalsy } from './is-falsy'; describe('isFalsy', () => { @@ -13,9 +14,8 @@ describe('isFalsy', () => { expect(isFalsy(NaN)).toBe(true); }); - // tslint:disable-next-line:only-arrow-functions - it('returns `false` for truthy values', function (): void { - expect(isFalsy(arguments)).toBe(false); + it('returns `false` for truthy values', function () { + expect(isFalsy(getArguments())).toBe(false); expect(isFalsy([1, 2, 3])).toBe(false); expect(isFalsy(true)).toBe(false); expect(isFalsy(new Date())).toBe(false); diff --git a/projects/js-core/src/lib/predicates/is-promise-like.spec.ts b/projects/js-core/src/lib/predicates/is-promise-like.spec.ts index 55b653d6..6b59fe82 100644 --- a/projects/js-core/src/lib/predicates/is-promise-like.spec.ts +++ b/projects/js-core/src/lib/predicates/is-promise-like.spec.ts @@ -1,10 +1,7 @@ +import { getArguments } from '../../test-helpers/test-utils'; import { isPromiseLike } from './is-promise-like'; describe('isPromiseLike()', () => { - function getArguments(): IArguments { - return arguments; - } - it('works', () => { expect(isPromiseLike(Promise.resolve('hi'))).toBe(true); expect(isPromiseLike(Promise.reject('bye'))).toBe(true); diff --git a/projects/js-core/src/lib/predicates/is-truthy.spec.ts b/projects/js-core/src/lib/predicates/is-truthy.spec.ts index 761d85ec..5ce2c5fc 100644 --- a/projects/js-core/src/lib/predicates/is-truthy.spec.ts +++ b/projects/js-core/src/lib/predicates/is-truthy.spec.ts @@ -1,9 +1,9 @@ +import { getArguments } from '../../test-helpers/test-utils'; import { isTruthy } from './is-truthy'; describe('isTruthy', () => { - // tslint:disable-next-line:only-arrow-functions - it('returns `true` for truthy values', function (): void { - expect(isTruthy(arguments)).toBe(true); + it('returns `true` for truthy values', function () { + expect(isTruthy(getArguments())).toBe(true); expect(isTruthy([1, 2, 3])).toBe(true); expect(isTruthy(true)).toBe(true); expect(isTruthy(new Date())).toBe(true); diff --git a/projects/js-core/src/test-helpers/test-utils.ts b/projects/js-core/src/test-helpers/test-utils.ts new file mode 100644 index 00000000..a702eae0 --- /dev/null +++ b/projects/js-core/src/test-helpers/test-utils.ts @@ -0,0 +1,4 @@ +export function getArguments(..._args: any[]): IArguments { + // eslint-disable-next-line prefer-rest-params + return arguments; +} diff --git a/projects/js-core/src/typing-tests/map-as-keys.dts-spec.ts b/projects/js-core/src/typing-tests/map-as-keys.dts-spec.ts deleted file mode 100644 index 64d3e42a..00000000 --- a/projects/js-core/src/typing-tests/map-as-keys.dts-spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { mapAsKeys } from '../public-api'; - -type A = number[]; -type AorU = A | undefined; -type AorN = A | null; - -const a = [] as A; -const aOrU = a as AorU; -const aOrN = a as AorN; - -// $ExpectType { [x: number]: string; } -mapAsKeys(a, () => 'a'); -// $ExpectType {} | { [x: number]: string; } -mapAsKeys(aOrN, () => 'a'); -// $ExpectType {} | { [x: number]: string; } -mapAsKeys(aOrU, () => 'a'); - -interface O { - a: string; - b: number; -} -type OorU = O | undefined; -type OorN = O | null; - -const o = {} as O; -const oOrU = o as OorU; -const oOrN = o as OorN; - -mapAsKeys({ a: 'foo', b: 'bar' }, (_item, key) => key.toUpperCase()); - -// $ExpectType { [x: string]: boolean; [x: number]: boolean; } -mapAsKeys(o, () => true); -// $ExpectType {} | { [x: string]: boolean; [x: number]: boolean; } -mapAsKeys(oOrU, () => true); -// $ExpectType {} | { [x: string]: boolean; [x: number]: boolean; } -mapAsKeys(oOrN, () => true); diff --git a/projects/micro-dash-sizes/.eslintrc.json b/projects/micro-dash-sizes/.eslintrc.json new file mode 100644 index 00000000..66288be9 --- /dev/null +++ b/projects/micro-dash-sizes/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["projects/micro-dash-sizes/tsconfig.app.json"], + "createDefaultProgram": true + }, + "rules": { + // We use empty functions a lot in the tiny size files + "@typescript-eslint/no-empty-function": "off" + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/projects/micro-dash-sizes/calc-sizes.ts b/projects/micro-dash-sizes/calc-sizes.ts index 8208f2f0..021754cd 100644 --- a/projects/micro-dash-sizes/calc-sizes.ts +++ b/projects/micro-dash-sizes/calc-sizes.ts @@ -16,6 +16,7 @@ const sourceDir = path.join(rootDir, 'projects', 'micro-dash', 'src', 'lib'); run(); async function run(): Promise { + // eslint-disable-next-line no-constant-condition while (true) { const base = await getFileBaseInput(); const library = await getLibraryInput(); @@ -122,7 +123,6 @@ async function inspect(): Promise { } function updateComment(inputPath: string, summary: string): void { - // tslint:disable-next-line:no-non-null-assertion const lib = summary.match(/ - (.*):/)![1]; const relativePath = path.relative(appDir, inputPath); diff --git a/projects/micro-dash/.eslintrc.json b/projects/micro-dash/.eslintrc.json new file mode 100644 index 00000000..32d6eb6f --- /dev/null +++ b/projects/micro-dash/.eslintrc.json @@ -0,0 +1,38 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": [ + "projects/micro-dash/tsconfig.lib.json", + "projects/micro-dash/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": { + // It would be nice to remove these and fix up the code. Or at least put more thought into it before declaring these exceptions final. + "@typescript-eslint/ban-types": [ + "error", + { + "types": { "Function": false, "object": false, "{}": false }, + "extendDefaults": true + } + ], + + // micro-dash tests do weird things to make sure the library works for users when they do weird things. + "@typescript-eslint/no-empty-function": "off", + "no-sparse-arrays": "off", + "prefer-rest-params": "off", + + // for this project, we knowingly violate some standards for the sake of smaller bundle size + "complexity": "off", + "max-depth": "off", + "max-lines-per-function": "off", + "no-prototype-builtins": "off" + } + }, + { "files": ["*.html"], "rules": {} } + ] +} diff --git a/projects/micro-dash/src/lib/collection/size.ts b/projects/micro-dash/src/lib/collection/size.ts index 4ae9d979..5ff1099f 100644 --- a/projects/micro-dash/src/lib/collection/size.ts +++ b/projects/micro-dash/src/lib/collection/size.ts @@ -9,7 +9,6 @@ import { keys } from '../object'; */ export function size(collection: object): number; -// tslint:disable-next-line:unified-signatures export function size(collection: readonly any[] | string): number; export function size(collection: object | readonly any[] | string): number { diff --git a/projects/micro-dash/src/lib/function/partial.spec.ts b/projects/micro-dash/src/lib/function/partial.spec.ts index c13db253..7b05360d 100644 --- a/projects/micro-dash/src/lib/function/partial.spec.ts +++ b/projects/micro-dash/src/lib/function/partial.spec.ts @@ -38,7 +38,6 @@ describe('partial()', () => { }); it('works when there are no partially applied arguments and the created function is invoked without additional arguments', () => { - // tslint:disable-next-line:only-arrow-functions const fn = function (): number { return arguments.length; }; diff --git a/projects/micro-dash/src/lib/lang/is-boolean.spec.ts b/projects/micro-dash/src/lib/lang/is-boolean.spec.ts index f83e7505..c444161e 100644 --- a/projects/micro-dash/src/lib/lang/is-boolean.spec.ts +++ b/projects/micro-dash/src/lib/lang/is-boolean.spec.ts @@ -10,8 +10,7 @@ describe('isBoolean()', () => { expect(isBoolean(false)).toBe(true); }); - // tslint:disable-next-line:only-arrow-functions - it('should return `false` for non-booleans', function (): void { + it('should return `false` for non-booleans', function () { expect(isBoolean(null)).toBe(false); expect(isBoolean(undefined)).toBe(false); expect(isBoolean(0)).toBe(false); diff --git a/projects/micro-dash/src/lib/lang/is-function.spec.ts b/projects/micro-dash/src/lib/lang/is-function.spec.ts index 802806b6..f89b0552 100644 --- a/projects/micro-dash/src/lib/lang/is-function.spec.ts +++ b/projects/micro-dash/src/lib/lang/is-function.spec.ts @@ -33,8 +33,7 @@ describe('isFunction()', () => { expect(isFunction(DataView)).toBe(true); }); - // tslint:disable-next-line:only-arrow-functions - it('should return `false` for non-functions', function (): void { + it('should return `false` for non-functions', function () { expect(isFunction(undefined)).toBe(false); expect(isFunction(null)).toBe(false); expect(isFunction(false)).toBe(false); diff --git a/projects/micro-dash/src/lib/lang/is-number.spec.ts b/projects/micro-dash/src/lib/lang/is-number.spec.ts index 8d5ba82e..0e25ebbe 100644 --- a/projects/micro-dash/src/lib/lang/is-number.spec.ts +++ b/projects/micro-dash/src/lib/lang/is-number.spec.ts @@ -10,8 +10,7 @@ describe('isNumber()', () => { expect(isNumber(NaN)).toBe(true); }); - // tslint:disable-next-line:only-arrow-functions - it('should return `false` for non-numbers', function (): void { + it('should return `false` for non-numbers', function () { expect(isNumber(null)).toBe(false); expect(isNumber(undefined)).toBe(false); expect(isNumber(false)).toBe(false); diff --git a/projects/micro-dash/src/lib/lang/is-reg-exp.spec.ts b/projects/micro-dash/src/lib/lang/is-reg-exp.spec.ts index 8ba1916e..94c8eb43 100644 --- a/projects/micro-dash/src/lib/lang/is-reg-exp.spec.ts +++ b/projects/micro-dash/src/lib/lang/is-reg-exp.spec.ts @@ -17,8 +17,7 @@ describe('isRegExp()', () => { expect(isRegExp(RegExp('x'))).toBe(true); }); - // tslint:disable-next-line:only-arrow-functions - it('should return `false` for non-regexes', function (): void { + it('should return `false` for non-regexes', function () { expect(isRegExp(null)).toBe(false); expect(isRegExp(undefined)).toBe(false); expect(isRegExp(false)).toBe(false); diff --git a/projects/micro-dash/src/lib/lang/is-string.spec.ts b/projects/micro-dash/src/lib/lang/is-string.spec.ts index 15c905fb..1621f7a1 100644 --- a/projects/micro-dash/src/lib/lang/is-string.spec.ts +++ b/projects/micro-dash/src/lib/lang/is-string.spec.ts @@ -66,8 +66,7 @@ describe('isString()', () => { expect(isString('a')).toBe(true); }); - // tslint:disable-next-line:only-arrow-functions - it('should return `false` for non-strings', function (): void { + it('should return `false` for non-strings', function () { expect(isString(null)).toBe(false); expect(isString(undefined)).toBe(false); expect(isString(false)).toBe(false); diff --git a/projects/micro-dash/src/lib/lang/is-undefined.spec.ts b/projects/micro-dash/src/lib/lang/is-undefined.spec.ts index f565b1b9..88d03bb2 100644 --- a/projects/micro-dash/src/lib/lang/is-undefined.spec.ts +++ b/projects/micro-dash/src/lib/lang/is-undefined.spec.ts @@ -5,8 +5,7 @@ describe('isUndefined', () => { expect(isUndefined(undefined)).toBe(true); }); - // tslint:disable-next-line:only-arrow-functions - it('should return `false` for non `undefined` values', function (): void { + it('should return `false` for non `undefined` values', function () { // falsey values expect(isUndefined(null)).toBe(false); expect(isUndefined(false)).toBe(false); diff --git a/projects/micro-dash/src/lib/math/extreme-utils.ts b/projects/micro-dash/src/lib/math/extreme-utils.ts index f81ee2ae..6e766545 100644 --- a/projects/micro-dash/src/lib/math/extreme-utils.ts +++ b/projects/micro-dash/src/lib/math/extreme-utils.ts @@ -19,6 +19,5 @@ export function findExtreme( currentCriterion = candidateCriterion; } }); - // tslint:disable-next-line:no-non-null-assertion return current!; } diff --git a/projects/micro-dash/src/lib/math/round.ts b/projects/micro-dash/src/lib/math/round.ts index 69968008..cce774a2 100644 --- a/projects/micro-dash/src/lib/math/round.ts +++ b/projects/micro-dash/src/lib/math/round.ts @@ -14,7 +14,6 @@ * - Lodash: 1,826 bytes * - Micro-dash: 111 bytes */ -// tslint:disable-next-line:variable-name export function round(number: number, precision = 0): number { const factor = 10 ** Math.trunc(precision); return Math.round(number * factor) / factor; diff --git a/projects/micro-dash/src/lib/number/clamp.ts b/projects/micro-dash/src/lib/number/clamp.ts index f1b80fa4..5013c55e 100644 --- a/projects/micro-dash/src/lib/number/clamp.ts +++ b/projects/micro-dash/src/lib/number/clamp.ts @@ -9,7 +9,6 @@ * - Lodash: 1,367 bytes * - Micro-dash: 119 bytes */ -// tslint:disable-next-line:variable-name export function clamp(number: number, lower: number, upper: number): number { return Math.min(upper, Math.max(lower, number)); } diff --git a/projects/micro-dash/src/lib/number/in-range.ts b/projects/micro-dash/src/lib/number/in-range.ts index 63d2c2c9..2fadad56 100644 --- a/projects/micro-dash/src/lib/number/in-range.ts +++ b/projects/micro-dash/src/lib/number/in-range.ts @@ -9,7 +9,6 @@ * - Lodash: 1,396 bytes * - Micro-dash: 127 bytes */ -// tslint:disable-next-line:variable-name export function inRange(number: number, start: number, end: number): boolean { return number >= Math.min(start, end) && number < Math.max(start, end); } diff --git a/projects/micro-dash/src/lib/object/keys.spec.ts b/projects/micro-dash/src/lib/object/keys.spec.ts index b0bc6abc..0dd5fea2 100644 --- a/projects/micro-dash/src/lib/object/keys.spec.ts +++ b/projects/micro-dash/src/lib/object/keys.spec.ts @@ -2,8 +2,7 @@ import { expectTypeOf } from 'expect-type'; import { keys } from './keys'; describe('keys()', () => { - // tslint:disable-next-line:only-arrow-functions - it('makes no special accommodations for `arguments` objects (unlike lodash)', function (): void { + it('makes no special accommodations for `arguments` objects (unlike lodash)', function () { expect(keys(arguments).sort()).toEqual(['callee', 'length']); }); @@ -78,7 +77,6 @@ describe('keys()', () => { }); it('should return keys for custom properties on `arguments` objects', () => { - // tslint:disable-next-line:only-arrow-functions const args: any = (function (..._: any[]): IArguments { return arguments; })(1, 2, 3); @@ -88,7 +86,6 @@ describe('keys()', () => { it('should not include inherited string keyed properties of `arguments` objects', () => { (Object.prototype as any).a = 1; - // tslint:disable-next-line:only-arrow-functions const args: any = (function (..._: any[]): IArguments { return arguments; })(1, 2, 3); diff --git a/projects/micro-dash/src/lib/string/camel-case.ts b/projects/micro-dash/src/lib/string/camel-case.ts index f8ce5079..5c8bb4ba 100644 --- a/projects/micro-dash/src/lib/string/camel-case.ts +++ b/projects/micro-dash/src/lib/string/camel-case.ts @@ -11,7 +11,6 @@ import { words } from './words'; * - Lodash: 6,097 bytes * - Micro-dash: 344 bytes */ -// tslint:disable-next-line:variable-name export function camelCase(string: string): string { return words(string) .map((w, i) => (i ? capitalize(w) : w.toLowerCase())) diff --git a/projects/micro-dash/src/lib/string/capitalize.ts b/projects/micro-dash/src/lib/string/capitalize.ts index e939970b..a00ea51e 100644 --- a/projects/micro-dash/src/lib/string/capitalize.ts +++ b/projects/micro-dash/src/lib/string/capitalize.ts @@ -10,7 +10,6 @@ import { upperFirst } from './upper-first'; * - Lodash: 1,937 bytes * - Micro-dash: 76 bytes */ -// tslint:disable-next-line:variable-name export function capitalize(string: string): string { return upperFirst(string.toLowerCase()); } diff --git a/projects/micro-dash/src/lib/string/kebab-case.ts b/projects/micro-dash/src/lib/string/kebab-case.ts index c1765c39..b4892f3c 100644 --- a/projects/micro-dash/src/lib/string/kebab-case.ts +++ b/projects/micro-dash/src/lib/string/kebab-case.ts @@ -8,7 +8,6 @@ import { words } from './words'; * - Lodash: 5,154 bytes * - Micro-dash: 254 bytes */ -// tslint:disable-next-line:variable-name export function kebabCase(string: string): string { return words(string).map(toLower).join('-'); } diff --git a/projects/micro-dash/src/lib/string/lower-first.ts b/projects/micro-dash/src/lib/string/lower-first.ts index db4a089d..1076b290 100644 --- a/projects/micro-dash/src/lib/string/lower-first.ts +++ b/projects/micro-dash/src/lib/string/lower-first.ts @@ -7,7 +7,6 @@ import { Nil } from '../interfaces'; * - Lodash: 1,940 bytes * - Micro-dash: 102 bytes */ -// tslint:disable-next-line:variable-name export function lowerFirst(string: string | Nil): string { return string ? string.charAt(0).toLowerCase() + string.slice(1) : ''; } diff --git a/projects/micro-dash/src/lib/string/repeat.ts b/projects/micro-dash/src/lib/string/repeat.ts index 2673545d..a12b3069 100644 --- a/projects/micro-dash/src/lib/string/repeat.ts +++ b/projects/micro-dash/src/lib/string/repeat.ts @@ -8,8 +8,6 @@ * - Lodash: 2,344 bytes * - Micro-dash: 96 bytes */ -// tslint:disable-next-line:variable-name export function repeat(string: string, n: number): string { - // tslint:disable-next-line:no-bitwise return n < 0 ? '' : new Array(n | 0).fill(string).join(''); } diff --git a/projects/micro-dash/src/lib/string/snake-case.ts b/projects/micro-dash/src/lib/string/snake-case.ts index 9080ca8b..0b98ff60 100644 --- a/projects/micro-dash/src/lib/string/snake-case.ts +++ b/projects/micro-dash/src/lib/string/snake-case.ts @@ -8,7 +8,6 @@ import { words } from './words'; * - Lodash: 5,171 bytes * - Micro-dash: 268 bytes */ -// tslint:disable-next-line:variable-name export function snakeCase(string: string): string { return words(string).map(toLower).join('_'); } diff --git a/projects/micro-dash/src/lib/string/to-lower.ts b/projects/micro-dash/src/lib/string/to-lower.ts index 31164217..6b634bc9 100644 --- a/projects/micro-dash/src/lib/string/to-lower.ts +++ b/projects/micro-dash/src/lib/string/to-lower.ts @@ -8,7 +8,6 @@ * - Lodash: 996 bytes * - Micro-dash: 27 bytes */ -// tslint:disable-next-line:variable-name export function toLower(string: string): string { return string.toLowerCase(); } diff --git a/projects/micro-dash/src/lib/string/upper-first.ts b/projects/micro-dash/src/lib/string/upper-first.ts index 4a37886b..5f4a331f 100644 --- a/projects/micro-dash/src/lib/string/upper-first.ts +++ b/projects/micro-dash/src/lib/string/upper-first.ts @@ -7,7 +7,6 @@ import { Nil } from '../interfaces'; * - Lodash: 1,940 bytes * - Micro-dash: 102 bytes */ -// tslint:disable-next-line:variable-name export function upperFirst(string: string | Nil): string { return string ? string.charAt(0).toUpperCase() + string.slice(1) : ''; } diff --git a/projects/micro-dash/src/lib/string/words.ts b/projects/micro-dash/src/lib/string/words.ts index 0ffc0c0b..be131351 100644 --- a/projects/micro-dash/src/lib/string/words.ts +++ b/projects/micro-dash/src/lib/string/words.ts @@ -8,7 +8,6 @@ * - Lodash: 2,283 bytes * - Micro-dash: 187 bytes */ -// tslint:disable-next-line:variable-name export function words(string: string): string[] { return ( string diff --git a/projects/micro-dash/src/lib/util/range.ts b/projects/micro-dash/src/lib/util/range.ts index 5694eecf..c5c0716a 100644 --- a/projects/micro-dash/src/lib/util/range.ts +++ b/projects/micro-dash/src/lib/util/range.ts @@ -15,7 +15,6 @@ import { times } from './times'; */ export function range(end: number): number[]; -// tslint:disable-next-line:unified-signatures export function range(start: number, end: number, step?: number): number[]; export function range(start: number, end?: number, step?: number): number[] { diff --git a/projects/ng-app-state/.eslintrc.json b/projects/ng-app-state/.eslintrc.json new file mode 100644 index 00000000..6daf976d --- /dev/null +++ b/projects/ng-app-state/.eslintrc.json @@ -0,0 +1,30 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": [ + "projects/ng-app-state/tsconfig.lib.json", + "projects/ng-app-state/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { "type": "attribute", "prefix": "nas", "style": "camelCase" } + ], + "@angular-eslint/component-selector": [ + "error", + { "type": "element", "prefix": "nas", "style": "kebab-case" } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/projects/ng-app-state/src/lib/nas-model.directive.spec.ts b/projects/ng-app-state/src/lib/nas-model.directive.spec.ts index f828ccdf..9f2603a9 100644 --- a/projects/ng-app-state/src/lib/nas-model.directive.spec.ts +++ b/projects/ng-app-state/src/lib/nas-model.directive.spec.ts @@ -9,6 +9,7 @@ import { } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; +import { noop } from "@s-libs/micro-dash"; import { CityComponent, citySelectWithCustomCompareFnTemplate, @@ -57,7 +58,7 @@ function initTest( { extraDirectives = [] as Array>, template = '', - beforeCreate = () => {}, + beforeCreate = noop, } = {}, ): S { if (template) { diff --git a/projects/ng-app-state/src/lib/value-accessors/checkbox-value-accessor.directive.ts b/projects/ng-app-state/src/lib/value-accessors/checkbox-value-accessor.directive.ts index a9c6354f..bb8feade 100644 --- a/projects/ng-app-state/src/lib/value-accessors/checkbox-value-accessor.directive.ts +++ b/projects/ng-app-state/src/lib/value-accessors/checkbox-value-accessor.directive.ts @@ -7,7 +7,7 @@ import { /** @hidden */ @Directive({ selector: 'input[type=checkbox][nasModel]', - // tslint:disable-next-line:no-host-metadata-property + // eslint-disable-next-line @angular-eslint/no-host-metadata-property host: { '(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()', diff --git a/projects/ng-app-state/src/lib/value-accessors/input-value-accessor.directive.ts b/projects/ng-app-state/src/lib/value-accessors/input-value-accessor.directive.ts index b4e9d963..bfdb9fb5 100644 --- a/projects/ng-app-state/src/lib/value-accessors/input-value-accessor.directive.ts +++ b/projects/ng-app-state/src/lib/value-accessors/input-value-accessor.directive.ts @@ -5,7 +5,7 @@ import { DefaultValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Directive({ selector: 'input:not([type=checkbox]):not([type=number]):not([type=radio]):not([type=range])[nasModel],textarea[nasModel]', - // tslint:disable-next-line:no-host-metadata-property + // eslint-disable-next-line @angular-eslint/no-host-metadata-property host: { '(input)': '$any(this)._handleInput($event.target.value)', '(blur)': 'onTouched()', diff --git a/projects/ng-app-state/src/lib/value-accessors/select-multiple-value-accessor.directive.ts b/projects/ng-app-state/src/lib/value-accessors/select-multiple-value-accessor.directive.ts index 0b0658e8..418bb742 100644 --- a/projects/ng-app-state/src/lib/value-accessors/select-multiple-value-accessor.directive.ts +++ b/projects/ng-app-state/src/lib/value-accessors/select-multiple-value-accessor.directive.ts @@ -7,7 +7,7 @@ import { /** @hidden */ @Directive({ selector: 'select[multiple][nasModel]', - // tslint:disable-next-line:no-host-metadata-property + // eslint-disable-next-line @angular-eslint/no-host-metadata-property host: { '(change)': 'onChange($event.target)', '(blur)': 'onTouched()' }, providers: [ { diff --git a/projects/ng-app-state/src/lib/value-accessors/select-value-accessor.directive.ts b/projects/ng-app-state/src/lib/value-accessors/select-value-accessor.directive.ts index a3f4ad34..aef6fdb8 100644 --- a/projects/ng-app-state/src/lib/value-accessors/select-value-accessor.directive.ts +++ b/projects/ng-app-state/src/lib/value-accessors/select-value-accessor.directive.ts @@ -4,7 +4,7 @@ import { NG_VALUE_ACCESSOR, SelectControlValueAccessor } from '@angular/forms'; /** @hidden */ @Directive({ selector: 'select:not([multiple])[nasModel]', - // tslint:disable-next-line:no-host-metadata-property + // eslint-disable-next-line @angular-eslint/no-host-metadata-property host: { '(change)': 'onChange($event.target.value)', '(blur)': 'onTouched()', diff --git a/projects/ng-app-state/src/test-helpers/helper-components.ts b/projects/ng-app-state/src/test-helpers/helper-components.ts index 71663a68..f4c3c3f5 100644 --- a/projects/ng-app-state/src/test-helpers/helper-components.ts +++ b/projects/ng-app-state/src/test-helpers/helper-components.ts @@ -1,6 +1,7 @@ import { Component, Injectable } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { RootStore } from '@s-libs/app-state'; +import { noop } from '@s-libs/micro-dash'; class StoreComponent { compareFn: (o1: any, o2: any) => boolean = (o1: any, o2: any) => @@ -205,12 +206,12 @@ export class NameComponent extends StoreComponent { ], }) export class InnerNameComponent implements ControlValueAccessor { - // prettier-ignore model!: string; disabled = false; - // prettier-ignore changeFn!: (value: any) => void; + registerOnTouched = noop; + writeValue(value: any): void { this.model = value; } @@ -219,8 +220,6 @@ export class InnerNameComponent implements ControlValueAccessor { this.changeFn = fn; } - registerOnTouched(): void {} - setDisabledState(disabled: boolean): void { this.disabled = disabled; } diff --git a/projects/ng-core/.eslintrc.json b/projects/ng-core/.eslintrc.json new file mode 100644 index 00000000..431c7ada --- /dev/null +++ b/projects/ng-core/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": [ + "projects/ng-core/tsconfig.lib.json", + "projects/ng-core/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": {} + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/projects/ng-core/src/lib/directive-superclass.spec.ts b/projects/ng-core/src/lib/directive-superclass.spec.ts index e4da5c68..1be4ff0f 100644 --- a/projects/ng-core/src/lib/directive-superclass.spec.ts +++ b/projects/ng-core/src/lib/directive-superclass.spec.ts @@ -175,7 +175,7 @@ describe('DirectiveSuperclass', () => { // https://github.com/simontonsoftware/s-ng-utils/issues/10 it('emits `undefined` for unspecified inputs', () => { @Component({ template: '' }) - class TestDirective extends DirectiveSuperclass { + class TestComponent extends DirectiveSuperclass { @Input() unspecified?: string; @Input() specified?: string; emittedValue? = 'initial value'; @@ -188,7 +188,7 @@ describe('DirectiveSuperclass', () => { } } - const ctx2 = new ComponentContext(TestDirective); + const ctx2 = new ComponentContext(TestComponent); ctx2.assignInputs({ specified: 'a value' }); ctx2.run(() => { const testDirective = ctx2.getComponentInstance(); @@ -199,7 +199,7 @@ describe('DirectiveSuperclass', () => { // https://github.com/simontonsoftware/s-libs/issues/14 it('does not emit until ngOnChanges is called', () => { @Component({ template: '' }) - class TestDirective extends DirectiveSuperclass implements OnChanges { + class TestComponent extends DirectiveSuperclass implements OnChanges { @Input() myInput?: string; stage = 'before ngOnChanges'; emittedDuring?: string; @@ -219,7 +219,7 @@ describe('DirectiveSuperclass', () => { } } - const ctx2 = new ComponentContext(TestDirective); + const ctx2 = new ComponentContext(TestComponent); ctx2.run(() => { const testDirective = ctx2.getComponentInstance(); expect(testDirective.emittedDuring).toBe('after ngOnChanges'); @@ -257,7 +257,8 @@ describe('DirectiveSuperclass', () => { @Component({ template: `{{ boundValue.name }}` }) class InputBindingComponent extends DirectiveSuperclass - implements OnInit { + implements OnInit + { @Input() inputValue!: { name: string }; boundValue!: { name: string }; diff --git a/projects/ng-core/src/lib/directive-superclass.ts b/projects/ng-core/src/lib/directive-superclass.ts index 854e0ed8..9b202ba0 100644 --- a/projects/ng-core/src/lib/directive-superclass.ts +++ b/projects/ng-core/src/lib/directive-superclass.ts @@ -49,10 +49,11 @@ import { InjectableSuperclass } from './injectable-superclass'; */ // maybe this won't need the fake selector after https://github.com/angular/angular/issues/36427 @Directive({ selector: '[sDirectiveSuperclass]' }) -// tslint:disable-next-line:directive-class-suffix +// eslint-disable-next-line @angular-eslint/directive-class-suffix export abstract class DirectiveSuperclass extends InjectableSuperclass - implements OnChanges { + implements OnChanges +{ /** * Emits the set of `@Input()` property names that change during each call to `ngOnChanges()`. */ diff --git a/projects/ng-core/src/lib/injectable-superclass.ts b/projects/ng-core/src/lib/injectable-superclass.ts index 3ea42b33..14e85369 100644 --- a/projects/ng-core/src/lib/injectable-superclass.ts +++ b/projects/ng-core/src/lib/injectable-superclass.ts @@ -14,7 +14,6 @@ import { Subject } from 'rxjs'; * } * ``` */ -// tslint:disable-next-line:typedef export function mixInInjectableSuperclass(Base: B) { return class extends mixInSubscriptionManager(Base) implements OnDestroy { #destructionSubject = new Subject(); diff --git a/projects/ng-dev/.eslintrc.json b/projects/ng-dev/.eslintrc.json new file mode 100644 index 00000000..07dee1b3 --- /dev/null +++ b/projects/ng-dev/.eslintrc.json @@ -0,0 +1,38 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": [ + "projects/ng-dev/tsconfig.lib.json", + "projects/ng-dev/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": ["s", "app"], + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": ["s", "app"], + "style": "kebab-case" + } + ] + } + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/projects/ng-dev/README.md b/projects/ng-dev/README.md index 588497b1..f4ec672e 100644 --- a/projects/ng-dev/README.md +++ b/projects/ng-dev/README.md @@ -8,51 +8,16 @@ To quickly see what is available, see the [api documentation](https://simontonso yarn add -D @s-libs/ng-dev @s-libs/ng-core @s-libs/rxjs-core @s-libs/js-core @s-libs/micro-dash @angular/cdk ``` -## TSLint Config - -This library comes with a predefined `tslint.json` that tracks the angular cli's config with these changes that we have found useful: - -- Disables rules that conflict with Prettier (via [tslint-config-prettier](https://github.com/prettier/tslint-config-prettier)) -- Allows using the `Function` type. Some of our libraries deal a lot with utilities that operate on functions, and using this type is very handy. -- Downgrades [no-non-null-assertion](https://palantir.github.io/tslint/rules/no-non-null-assertion/) to a warning. While we believe using `!` should be avoided when reasonable, we find that sometimes it just makes sense. -- Allows prefixing variables with `_`. This is useful e.g. when overriding a method in a way that does not use all its parameters. We use typescript's "noUnusedParameters" option, which gives an error with unused parameters unless their names are prefixed with `_`. +## ESLint Config -To use it, change your `tslint.json` to: +This library comes with some default config you can use for ESLint. It to use it, first install [Angular ESLint](https://github.com/angular-eslint/angular-eslint) following their instructions for your situation. Make sure it is working with their minimal config, then change `.eslintrc.json` in your root directory to this: ```json -{ "extends": "@s-libs/ng-dev/tslint" } +{ "extends": "@s-libs/ng-dev/eslint-config.json" } ``` -## ESLint Config - -This library comes with configuration to lint code complexity and length using ESLint. Eventually we expect the Angular CLI to switch to ESLint because TSLint is deprecated. After that this should require less configuration to activate, but for now it requires all these steps: - -1. Add a file `.eslintrc.js` to your project root like this: - - ```js - module.exports = require("@s-libs/ng-dev/.eslintrc"); - ``` - -1. Add a file `.eslintignore` to your project root like this (and tweak to fit your needs): - - ``` - /.idea/ - /coverage/ - /dist/ - /docs/ - /node_modules/ - karma.conf.js - *.spec.ts - ``` - -1. Modify your `package.json`'s lint script to include ESLint: - ``` - "lint": "ng lint && eslint . --ext .js,.jsx,.ts,.tsx", - ``` - -## Upgrading from the non-S-Libs version - -If you are upgrading from the loose version of [`s-ng-dev-utils`](https://github.com/simontonsoftware/s-ng-dev-utils), there are a couple changes to be aware of. In addition to the general guidlines [here](https://github.com/simontonsoftware/s-libs/#upgrading-from-the-loose-packages): +This will give you all the recommended, community-standard rules from ESLint, "eslint:recommended", +@typescript-eslint, and @angular-eslint/recommended, plus these additions and changes: -- If you use the provided TSLint config, open `tslint.json` and replace `s-ng-dev-utils/tslint` with `@s-libs/ng-dev/tslint`. -- If you use the provided ESLint config, open `.eslintrc.js` and replace `s-ng-dev-utils/.eslintrc` with `@s-libs/ng-dev/.eslintrc`. +- code complexity rules to keep your functions and files focused and readable +- relaxed rules around `ban-types` that Simonton Software has found unuseful diff --git a/.eslintrc.js b/projects/ng-dev/deprecated-config/.eslintrc.js similarity index 100% rename from .eslintrc.js rename to projects/ng-dev/deprecated-config/.eslintrc.js diff --git a/projects/ng-dev/deprecated-config/tslint.angularcli.json b/projects/ng-dev/deprecated-config/tslint.angularcli.json new file mode 100644 index 00000000..91a3e7c5 --- /dev/null +++ b/projects/ng-dev/deprecated-config/tslint.angularcli.json @@ -0,0 +1,96 @@ +{ + "extends": "tslint:recommended", + "rules": { + "align": { "options": ["parameters", "statements"] }, + "array-type": false, + "arrow-return-shorthand": true, + "curly": true, + "deprecation": { "severity": "warning" }, + "component-class-suffix": true, + "contextual-lifecycle": true, + "directive-class-suffix": true, + "directive-selector": [true, "attribute", "app", "camelCase"], + "component-selector": [true, "element", "app", "kebab-case"], + "eofline": true, + "import-blacklist": [true, "rxjs/Rx"], + "import-spacing": true, + "indent": { "options": ["spaces"] }, + "max-classes-per-file": false, + "max-line-length": [true, 140], + "member-ordering": [ + true, + { + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } + ], + "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], + "no-empty": false, + "no-inferrable-types": [true, "ignore-params"], + "no-non-null-assertion": true, + "no-redundant-jsdoc": true, + "no-switch-case-fall-through": true, + "no-var-requires": false, + "object-literal-key-quotes": [true, "as-needed"], + "quotemark": [true, "single"], + "semicolon": { "options": ["always"] }, + "space-before-function-paren": { + "options": { + "anonymous": "never", + "asyncArrow": "always", + "constructor": "never", + "method": "never", + "named": "never" + } + }, + "typedef": [true, "call-signature"], + "typedef-whitespace": { + "options": [ + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + }, + { + "call-signature": "onespace", + "index-signature": "onespace", + "parameter": "onespace", + "property-declaration": "onespace", + "variable-declaration": "onespace" + } + ] + }, + "variable-name": { + "options": ["ban-keywords", "check-format", "allow-pascal-case"] + }, + "whitespace": { + "options": [ + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type", + "check-typecast" + ] + }, + "no-conflicting-lifecycle": true, + "no-host-metadata-property": true, + "no-input-rename": true, + "no-inputs-metadata-property": true, + "no-output-native": true, + "no-output-on-prefix": true, + "no-output-rename": true, + "no-outputs-metadata-property": true, + "template-banana-in-box": true, + "template-no-negated-async": true, + "use-lifecycle-interface": true, + "use-pipe-transform-interface": true + }, + "rulesDirectory": ["codelyzer"] +} diff --git a/projects/ng-dev/deprecated-config/tslint.json b/projects/ng-dev/deprecated-config/tslint.json new file mode 100644 index 00000000..1e27f2bb --- /dev/null +++ b/projects/ng-dev/deprecated-config/tslint.json @@ -0,0 +1,25 @@ +{ + "extends": ["./tslint.angularcli.json", "tslint-config-prettier"], + "rules": { + // the same as tslint:recommended, but without "Function" + "ban-types": { + "options": [ + ["Object", "Avoid using the `Object` type. Did you mean `object`?"], + ["Boolean", "Avoid using the `Boolean` type. Did you mean `boolean`?"], + ["Number", "Avoid using the `Number` type. Did you mean `number`?"], + ["String", "Avoid using the `String` type. Did you mean `string`?"], + ["Symbol", "Avoid using the `Symbol` type. Did you mean `symbol`?"] + ] + }, + "no-non-null-assertion": { "severity": "warning" }, + // the same as tslint:recommended, but with "allow-leading-underscore" + "variable-name": { + "options": [ + "ban-keywords", + "check-format", + "allow-pascal-case", + "allow-leading-underscore" + ] + } + } +} diff --git a/projects/ng-dev/package.json b/projects/ng-dev/package.json index d6304d8d..c9cbe320 100644 --- a/projects/ng-dev/package.json +++ b/projects/ng-dev/package.json @@ -20,9 +20,6 @@ "jasmine-core": "^3.6.0" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^4.8.0", - "@typescript-eslint/parser": "^4.6.0", - "eslint": "^7.14.0", "tslib": "^2.1.0" } } diff --git a/projects/ng-dev/src/eslint-config.json b/projects/ng-dev/src/eslint-config.json new file mode 100644 index 00000000..300d8749 --- /dev/null +++ b/projects/ng-dev/src/eslint-config.json @@ -0,0 +1,52 @@ +{ + "root": true, + "ignorePatterns": ["projects/**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": ["tsconfig.json"], + "createDefaultProgram": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@angular-eslint/recommended", + "plugin:@angular-eslint/template/process-inline-templates" + ], + "rules": { + "complexity": ["error", { "max": 5 }], + "max-depth": ["error", { "max": 3 }], + "max-lines": [ + "error", + { "max": 200, "skipBlankLines": true, "skipComments": true } + ], + "max-lines-per-function": [ + "error", + { "max": 25, "skipBlankLines": true, "skipComments": true } + ], + "max-nested-callbacks": ["error", { "max": 2 }], + + // I have not found good alternatives to `object` for the cases I've used it + "@typescript-eslint/ban-types": [ + "error", + { "types": { "object": false }, "extendDefaults": true } + ] + }, + "env": { "browser": true, "jasmine": true } + }, + { + "files": ["*.spec.ts"], + "rules": { + "max-lines": "off", + "max-lines-per-function": "off", + "max-nested-callbacks": "off" + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@angular-eslint/template/recommended"], + "rules": {} + } + ] +} diff --git a/projects/ng-dev/src/lib/angular-context/angular-context.ts b/projects/ng-dev/src/lib/angular-context/angular-context.ts index 3e2700df..edb73ffb 100644 --- a/projects/ng-dev/src/lib/angular-context/angular-context.ts +++ b/projects/ng-dev/src/lib/angular-context/angular-context.ts @@ -168,6 +168,7 @@ export class AngularContext { /** * This is a hook for subclasses to override. It is called during `run()`, before the `test()` callback. This implementation does nothing, but if you override this it is still recommended to call `super.init()` in case this implementation does something in the future. */ + // eslint-disable-next-line @typescript-eslint/no-empty-function protected init(): void {} /** @hidden */ diff --git a/projects/ng-dev/src/lib/component-context/component-context.spec.ts b/projects/ng-dev/src/lib/component-context/component-context.spec.ts index 29d8f421..f3eb16d8 100644 --- a/projects/ng-dev/src/lib/component-context/component-context.spec.ts +++ b/projects/ng-dev/src/lib/component-context/component-context.spec.ts @@ -101,7 +101,7 @@ describe('ComponentContext', () => { @Component({ template: '' }) class NonInputComponent { nonInput?: string; - // tslint:disable-next-line:no-input-rename + // eslint-disable-next-line @angular-eslint/no-input-rename @Input('nonInput') letsTryToTrickIt?: string; } diff --git a/projects/ng-dev/src/lib/component-context/create-dynamic-wrapper.spec.ts b/projects/ng-dev/src/lib/component-context/create-dynamic-wrapper.spec.ts index b1166643..ffa3a4e1 100644 --- a/projects/ng-dev/src/lib/component-context/create-dynamic-wrapper.spec.ts +++ b/projects/ng-dev/src/lib/component-context/create-dynamic-wrapper.spec.ts @@ -33,7 +33,7 @@ describe('createDynamicWrapper()', () => { }); it('can handle components whose selectors are not tag names', () => { - // tslint:disable-next-line:component-selector + // eslint-disable-next-line @angular-eslint/component-selector @Component({ selector: '[myAttribute]', template: 'the template' }) class AttributeSelectorComponent {} const ctx = new ComponentContext(AttributeSelectorComponent); @@ -45,7 +45,7 @@ describe('createDynamicWrapper()', () => { it('can handle renamed inputs', () => { @Component({ template: '{{ propertyName }}' }) class RenamedInputComponent { - // tslint:disable-next-line:no-input-rename + // eslint-disable-next-line @angular-eslint/no-input-rename @Input('bindingName') propertyName?: string; } @@ -131,7 +131,6 @@ describe('createDynamicWrapper()', () => { class NotAComponent {} expect(() => { - // tslint:disable-next-line:no-unused-expression new ComponentContext(NotAComponent); }).toThrowError('That does not appear to be a component'); }); diff --git a/projects/ng-dev/src/lib/log-timers.spec.ts b/projects/ng-dev/src/lib/log-timers.spec.ts index 4b793fb0..999ae075 100644 --- a/projects/ng-dev/src/lib/log-timers.spec.ts +++ b/projects/ng-dev/src/lib/log-timers.spec.ts @@ -1,4 +1,5 @@ import { discardPeriodicTasks, fakeAsync, tick } from '@angular/core/testing'; +import { noop } from '@s-libs/micro-dash'; import { expectSingleCallAndReset } from '../public-api'; import { logTimers } from './log-timers'; @@ -22,8 +23,10 @@ describe('logTimers()', () => { tick(1000); stopLogging(); + // eslint-disable-next-line @typescript-eslint/no-empty-function function myFunction(): void {} + // eslint-disable-next-line @typescript-eslint/no-empty-function function myDelayedFunction(): void {} })); @@ -46,8 +49,10 @@ describe('logTimers()', () => { discardPeriodicTasks(); stopLogging(); + // eslint-disable-next-line @typescript-eslint/no-empty-function function myFunction(): void {} + // eslint-disable-next-line @typescript-eslint/no-empty-function function myDelayedFunction(): void {} })); @@ -57,10 +62,8 @@ describe('logTimers()', () => { const stopLogging = logTimers(); stopLogging(); - setTimeout(myFunction); - setInterval(myFunction); + setTimeout(noop); + setInterval(noop); expect(logSpy).not.toHaveBeenCalled(); - - function myFunction(): void {} }); }); diff --git a/projects/ng-dev/src/lib/precompile-for-tests.spec.ts b/projects/ng-dev/src/lib/precompile-for-tests.spec.ts index 544589de..9011de78 100644 --- a/projects/ng-dev/src/lib/precompile-for-tests.spec.ts +++ b/projects/ng-dev/src/lib/precompile-for-tests.spec.ts @@ -53,7 +53,6 @@ class SecondaryModule {} class MainModule {} describe('precompileForTests()', () => { - // tslint:disable-next-line:deprecation precompileForTests([MainModule]); async function initComponent(): Promise> { diff --git a/projects/ng-dev/src/lib/spies/async-method-controller.spec.ts b/projects/ng-dev/src/lib/spies/async-method-controller.spec.ts index 70239f84..af58e1f1 100644 --- a/projects/ng-dev/src/lib/spies/async-method-controller.spec.ts +++ b/projects/ng-dev/src/lib/spies/async-method-controller.spec.ts @@ -6,7 +6,6 @@ import CallInfo = jasmine.CallInfo; describe('AsyncMethodController', () => { describe('constructor', () => { it('allows the controlled method to be called immediately', () => { - // tslint:disable-next-line:no-unused-expression new AsyncMethodController(navigator.clipboard, 'readText'); expect(() => { diff --git a/projects/rxjs-core/.eslintrc.json b/projects/rxjs-core/.eslintrc.json new file mode 100644 index 00000000..58305de8 --- /dev/null +++ b/projects/rxjs-core/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": "../../.eslintrc.json", + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "parserOptions": { + "project": [ + "projects/rxjs-core/tsconfig.lib.json", + "projects/rxjs-core/tsconfig.spec.json" + ], + "createDefaultProgram": true + }, + "rules": {} + }, + { + "files": ["*.html"], + "rules": {} + } + ] +} diff --git a/projects/rxjs-core/src/lib/create-operator-function.spec.ts b/projects/rxjs-core/src/lib/create-operator-function.spec.ts index 30adb76c..555f10ed 100644 --- a/projects/rxjs-core/src/lib/create-operator-function.spec.ts +++ b/projects/rxjs-core/src/lib/create-operator-function.spec.ts @@ -1,3 +1,4 @@ +import { noop } from '@s-libs/micro-dash'; import { marbleTest } from '@s-libs/ng-dev'; import { OperatorFunction, Subject } from 'rxjs'; import { @@ -20,7 +21,8 @@ function map(fn: (input: I) => O): OperatorFunction { }); } -function noop(): OperatorFunction { +function nonOp(): OperatorFunction { + // eslint-disable-next-line @typescript-eslint/no-empty-function return createOperatorFunction(() => {}); } @@ -51,9 +53,9 @@ describe('createOperatorFunction()', () => { 'allows preventing values, error and completion', marbleTest(({ hot, expectObservable }) => { const operatorFunction = createOperatorFunction((subscriber) => { - subscriber.next = () => {}; - subscriber.error = () => {}; - subscriber.complete = () => {}; + subscriber.next = noop; + subscriber.error = noop; + subscriber.complete = noop; }); const source1 = hot('-a-|').pipe(operatorFunction); @@ -79,12 +81,12 @@ describe('createOperatorFunction()', () => { }); it('passes along values by default', async () => { - await expectPipeResult([1, 2, 3], noop(), [1, 2, 3]); + await expectPipeResult([1, 2, 3], nonOp(), [1, 2, 3]); }); - it('passes along unsubscribes by default', testUnsubscribePropagation(noop)); + it('passes along unsubscribes by default', testUnsubscribePropagation(nonOp)); - it('passes along errors by default', testErrorPropagation(noop)); + it('passes along errors by default', testErrorPropagation(nonOp)); - it('passes along completion by default', testCompletionPropagation(noop)); + it('passes along completion by default', testCompletionPropagation(nonOp)); }); diff --git a/projects/rxjs-core/src/lib/devtools/actions.ts b/projects/rxjs-core/src/lib/devtools/actions.ts index 61e3670b..b57c76ba 100644 --- a/projects/rxjs-core/src/lib/devtools/actions.ts +++ b/projects/rxjs-core/src/lib/devtools/actions.ts @@ -1,7 +1,5 @@ // Copied from https://github.com/reduxjs/redux/blob/b3165d0cbd0d8f48142284807d764160df26d6fa/src/types/actions.ts, to avoid requiring `redux` as an installed dependency just for this typing. -// tslint:disable - /** * An *action* is a plain object that represents an intention to change the * state. Actions are the only way to get data into the store. Any data, diff --git a/projects/rxjs-core/src/lib/devtools/enhancer-options.ts b/projects/rxjs-core/src/lib/devtools/enhancer-options.ts index 1730e455..03c58497 100644 --- a/projects/rxjs-core/src/lib/devtools/enhancer-options.ts +++ b/projects/rxjs-core/src/lib/devtools/enhancer-options.ts @@ -2,7 +2,7 @@ import { Action, ActionCreator } from './actions'; // copied from https://github.com/zalmoxisus/redux-devtools-extension/blob/3df4d939b548b8ec891c73dabe2ffdd5dd2a8119/npm-package/index.d.ts, to avoid required `redux-devtools-extension` as an installed dependency just for this typing. -// tslint:disable +/* eslint-disable */ /** * Copied from `redux-devtools-extension`, to avoid requiring it as an installed dependency just for this interface. diff --git a/projects/rxjs-core/src/lib/subscription-manager.ts b/projects/rxjs-core/src/lib/subscription-manager.ts index c626c76e..a80dff4d 100644 --- a/projects/rxjs-core/src/lib/subscription-manager.ts +++ b/projects/rxjs-core/src/lib/subscription-manager.ts @@ -13,7 +13,6 @@ import { Observable, Subscription, Unsubscribable } from 'rxjs'; * ``` */ /* eslint-disable max-lines-per-function */ -// tslint:disable-next-line:typedef export function mixInSubscriptionManager(Base: B) { return class extends Base implements Unsubscribable { #subscriptions = new Subscription(); diff --git a/projects/rxjs-core/src/test-helpers/misc-helpers.ts b/projects/rxjs-core/src/test-helpers/misc-helpers.ts index f4c4913c..4b94c992 100644 --- a/projects/rxjs-core/src/test-helpers/misc-helpers.ts +++ b/projects/rxjs-core/src/test-helpers/misc-helpers.ts @@ -27,7 +27,6 @@ export function testUserFunctionError( return marbleTest(({ hot, expectObservable, expectSubscriptions }) => { const thrower = () => { // this is the error TestScheduler expects when it sees "#" - // tslint:disable-next-line:no-string-throw throw 'error'; }; const source = hot('-1-', { 1: upstreamValue }); diff --git a/scripts/copy-extra-dist-files.ts b/scripts/copy-extra-dist-files.ts new file mode 100644 index 00000000..865c4c18 --- /dev/null +++ b/scripts/copy-extra-dist-files.ts @@ -0,0 +1,3 @@ +import { copyEslintConfig } from './shared'; + +copyEslintConfig(); diff --git a/scripts/publish-libs.ts b/scripts/publish-libs.ts index 1a4900f1..747ac693 100644 --- a/scripts/publish-libs.ts +++ b/scripts/publish-libs.ts @@ -1,20 +1,10 @@ -import { copyFileSync } from 'fs'; -import { getInput, libraries, runCommand } from './shared'; - -const ngDevExtraFiles = [ - 'tslint.angularcli.json', - 'tslint.json', - '.eslintrc.js', -]; +import { copyEslintConfig, getInput, libraries, runCommand } from './shared'; async function run(): Promise { - const extraArgs = process.argv.slice(2).join(' '); - - for (const file of ngDevExtraFiles) { - copyFileSync(file, `dist/ng-dev/${file}`); - } + copyEslintConfig(); const otp = await getInput('Enter OTP: '); + const extraArgs = process.argv.slice(2).join(' '); for (const project of libraries) { runCommand( `npm publish --access public --otp ${otp} ${extraArgs} dist/${project}`, diff --git a/scripts/shared.ts b/scripts/shared.ts index ef0470d4..0c00670d 100644 --- a/scripts/shared.ts +++ b/scripts/shared.ts @@ -1,4 +1,5 @@ import { execSync } from 'child_process'; +import { copyFileSync } from 'fs'; import { createInterface } from 'readline'; // in dependency order @@ -25,3 +26,22 @@ export function getInput(text: string): Promise { }); }); } + +export function copyEslintConfig() { + copyFileSync( + 'projects/ng-dev/src/eslint-config.json', + 'dist/ng-dev/eslint-config.json', + ); + copyFileSync( + 'projects/ng-dev/deprecated-config/.eslintrc.js', + 'dist/ng-dev/.eslintrc.js', + ); + copyFileSync( + 'projects/ng-dev/deprecated-config/tslint.angularcli.json', + 'dist/ng-dev/tslint.angularcli.json', + ); + copyFileSync( + 'projects/ng-dev/deprecated-config/tslint.json', + 'dist/ng-dev/tslint.json', + ); +} diff --git a/yarn.lock b/yarn.lock index 61fe57be..44bd225e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -121,6 +121,45 @@ ora "5.4.0" rxjs "6.6.7" +"@angular-eslint/builder@12.0.0": + version "12.0.0" + resolved "https://registry.npmjs.org/@angular-eslint/builder/-/builder-12.0.0.tgz#b6cf118b8918fd6440599b1765f2bd5d90bbc6e7" + integrity sha512-gvvXQDXXi0gsWZ25KyMqF/1b3AaX+CJbpVgTPqxJdEx4euvmG/m3993ynmpf+Kc+F+aI2O9W4TUbDbbLWoCjIA== + +"@angular-eslint/eslint-plugin-template@12.0.0": + version "12.0.0" + resolved "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-12.0.0.tgz#eac9df5a04b3c0a5dc14a67aa580f5164f892f93" + integrity sha512-RF8PwN2A3U4ihd7sKYUM8wgPj46M30reziLl8CPPhN3H5Hn46nksmKmHRbPNakH2gW0Ba7NIxy+ocqUy0fQpcQ== + dependencies: + "@typescript-eslint/experimental-utils" "4.23.0" + aria-query "^4.2.2" + axobject-query "^2.2.0" + +"@angular-eslint/eslint-plugin@12.0.0": + version "12.0.0" + resolved "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-12.0.0.tgz#47663c1b1101bcf07fb0f714f8287b3de3b3d7f7" + integrity sha512-osdJdMu8bYFv9WGhC04AwRcbeKq4sxCQnShV7NiF0xkgNG9KqDaStytVhPjJFn2Ja1QhfiTGlcFFk4D/9aruog== + dependencies: + "@typescript-eslint/experimental-utils" "4.23.0" + +"@angular-eslint/schematics@12.0.0": + version "12.0.0" + resolved "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-12.0.0.tgz#cd0d7f688304141fb039966a0c441bb1908c2f5f" + integrity sha512-AwYEVuAQLJfyIF5vxBL98a67ecF9U25pQSIg0xCY6DeDpIaGdORr4yg2rGYy8fTlzDQo6BctKZQOTiVX3Y3uew== + dependencies: + "@angular-eslint/eslint-plugin" "12.0.0" + "@angular-eslint/eslint-plugin-template" "12.0.0" + ignore "5.1.8" + strip-json-comments "3.1.1" + tmp "0.2.1" + +"@angular-eslint/template-parser@12.0.0": + version "12.0.0" + resolved "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-12.0.0.tgz#5b2ea31d4a9338cfe4a52689de36c8b00b94f286" + integrity sha512-gl5ansA2a8LY6TEjhe0k8NiQJJdEQPjjqpysO1Rpt3NWUYQkFMt+1+AnUyokHB1TU3/11dHRUjVWXj+pMtTIAA== + dependencies: + eslint-scope "^5.1.0" + "@angular/animations@~12.0.0": version "12.0.0" resolved "https://registry.npmjs.org/@angular/animations/-/animations-12.0.0.tgz#5f845b1a58ffb6f3ea6103edf0756ac65320b725" @@ -1085,7 +1124,15 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/runtime@7.14.0", "@babel/runtime@^7.8.4": +"@babel/runtime-corejs3@^7.10.2": + version "7.14.0" + resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.0.tgz#6bf5fbc0b961f8e3202888cb2cd0fb7a0a9a3f66" + integrity sha512-0R0HTZWHLk6G8jIk0FtoX+AatCtKnswS98VhXwGImFc759PJRp4Tru0PQYZofyijTFUr+gT8Mu7sgXVJLQ0ceg== + dependencies: + core-js-pure "^3.0.0" + regenerator-runtime "^0.13.4" + +"@babel/runtime@7.14.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.8.4": version "7.14.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6" integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA== @@ -1908,6 +1955,14 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +aria-query@^4.2.2: + version "4.2.2" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" + integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== + dependencies: + "@babel/runtime" "^7.10.2" + "@babel/runtime-corejs3" "^7.10.2" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -2052,6 +2107,11 @@ aws4@^1.8.0: resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axobject-query@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" + integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== + babel-code-frame@^6.22.0: version "6.26.0" resolved "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -2988,6 +3048,11 @@ core-js-compat@^3.9.0, core-js-compat@^3.9.1: browserslist "^4.16.6" semver "7.0.0" +core-js-pure@^3.0.0: + version "3.12.1" + resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz#934da8b9b7221e2a2443dc71dfa5bd77a7ea00b8" + integrity sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ== + core-js@3.12.0: version "3.12.0" resolved "https://registry.npmjs.org/core-js/-/core-js-3.12.0.tgz#62bac86f7d7f087d40dba3e90a211c2c3c8559ea" @@ -3737,7 +3802,7 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -eslint-scope@^5.0.0, eslint-scope@^5.1.1: +eslint-scope@^5.0.0, eslint-scope@^5.1.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -4768,16 +4833,16 @@ ignore-walk@^3.0.3: dependencies: minimatch "^3.0.4" +ignore@5.1.8, ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + ignore@^4.0.6: version "4.0.6" resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.4: - version "5.1.8" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== - image-size@~0.5.0: version "0.5.5" resolved "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" @@ -8826,16 +8891,16 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-json-comments@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - style-loader@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c"