feat(angular): build for angular 12.0 (#23970)

This commit is contained in:
Mike Hartington
2021-10-15 16:54:59 -04:00
committed by GitHub
parent e3996cfbd5
commit 3451a34ad0
67 changed files with 55358 additions and 29616 deletions

View File

@ -101,7 +101,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
command: npm ci
working_directory: /tmp/workspace/angular
- run:
command: sudo npm link
@ -125,7 +125,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
command: npm ci
working_directory: /tmp/workspace/packages/angular-server
- run:
command: npm run build.prod
@ -433,7 +433,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
command: npm ci
working_directory: /tmp/workspace/angular/test/test-app
- run:
command: npm run test

4
angular/.eslintignore Normal file
View File

@ -0,0 +1,4 @@
dist
virtual-scroll
scripts
proxies.ts

30
angular/.eslintrc.json Normal file
View File

@ -0,0 +1,30 @@
{
"root": true,
"ignorePatterns": ["test/**/*", "src/directives/virtual-scroll/**/*"],
"overrides": [
{
"files": ["*.ts"],
"parserOptions": {
"project": ["tsconfig.json"],
"createDefaultProgram": true
},
"extends": [
"@ionic/eslint-config/recommended",
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@typescript-eslint/consistent-type-imports": "off",
"@angular-eslint/component-class-suffix": "off",
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "ion",
"style": "kebab-case"
}
]
}
}
]
}

6
angular/.prettierignore Normal file
View File

@ -0,0 +1,6 @@
dist
virtual-scroll
scripts
test
src/directives/proxies.ts
src/directives/angular-component-lib/utils.ts

15834
angular/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -26,16 +26,14 @@
"scripts": {
"build": "npm run clean && npm run build.ng && npm run build.core && npm run clean-generated",
"build.core": "node scripts/build-core.js",
"build.fesm": "rollup --config ./scripts/rollup.config.js",
"build.link": "npm run build && node scripts/link-copy.js",
"build.ng": "ng-packagr -p package.json",
"build.es2015": "ngc -p tsconfig.json && rollup --config ./scripts/rollup.config.js",
"build.es5": "ngc -p tsconfig.legacy.json && rollup --config ./scripts/rollup.config.legacy.js",
"build.ng": "ng-packagr -p package.json -c tsconfig.json",
"clean": "node scripts/clean.js",
"clean-generated": "node ./scripts/clean-generated.js",
"lint": "npm run lint.ts",
"lint.ts": "tslint --project .",
"lint.fix": "tslint --project . --fix",
"lint": "npm run eslint && npm run prettier -- --check",
"fmt": "npm run eslint -- --fix && npm run prettier -- --write",
"prettier": "prettier \"**/*.ts\"",
"eslint": "eslint . --ext .ts",
"prerelease": "npm run validate && np prerelease --yolo --any-branch --tag next",
"test": "echo 'angular no tests yet'",
"tsc": "tsc -p .",
@ -43,44 +41,53 @@
},
"dependencies": {
"@ionic/core": "6.0.0-rc.0",
"tslib": "^1.9.3"
"jsonc-parser": "^3.0.0",
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": ">=8.2.7",
"@angular/forms": ">=8.2.7",
"@angular/router": ">=8.2.7",
"rxjs": ">=6.2.0",
"zone.js": ">=0.8.26"
"@angular/core": ">=12.0.0",
"@angular/forms": ">=12.0.0",
"@angular/router": ">=12.0.0",
"rxjs": ">=6.6.0",
"zone.js": ">=0.11.0"
},
"devDependencies": {
"@angular-devkit/core": "8.3.17",
"@angular-devkit/schematics": "8.3.17",
"@angular/common": "8.2.13",
"@angular/compiler": "8.2.13",
"@angular/compiler-cli": "8.2.13",
"@angular/core": "8.2.13",
"@angular/forms": "8.2.13",
"@angular/router": "8.2.13",
"@angular-devkit/core": "^12.0.0",
"@angular-devkit/schematics": "^12.0.0",
"@angular-eslint/eslint-plugin": "^12.5.0",
"@angular-eslint/eslint-plugin-template": "^12.5.0",
"@angular-eslint/template-parser": "^12.5.0",
"@angular/common": "^12.0.0",
"@angular/compiler": "^12.0.0",
"@angular/compiler-cli": "^12.0.0",
"@angular/core": "^12.0.0",
"@angular/forms": "^12.0.0",
"@angular/router": "^12.0.0",
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@schematics/angular": "^12.2.9",
"@types/node": "12.12.5",
"@typescript-eslint/eslint-plugin": "^4.32.0",
"@typescript-eslint/parser": "^4.32.0",
"eslint": "^7.32.0",
"eslint-plugin-import": "^2.25.2",
"fs-extra": "^7.0.0",
"glob": "^7.1.4",
"ng-packagr": "^9.1.5",
"rollup": "~1.17.0",
"rollup-plugin-node-resolve": "~5.2.0",
"ng-packagr": "^12.0.0",
"prettier": "^2.4.1",
"rxjs": "^6.6.2",
"tsickle": "^0.39.1",
"tslint": "^5.12.1",
"tslint-ionic-rules": "0.0.21",
"typescript": "3.4.5",
"zone.js": "^0.11.1"
"typescript": "4.2.4",
"typescript-eslint-language-service": "^4.1.5",
"zone.js": "~0.11.4"
},
"prettier": "@ionic/prettier-config",
"schematics": "./schematics/collection.json",
"ngPackage": {
"lib": {
"entryFile": "src/index.ts"
},
"whitelistedNonPeerDependencies": [
"@ionic/core"
"allowedNonPeerDependencies": [
"@ionic/core",
"jsonc-parser"
]
}
}

View File

@ -10,15 +10,13 @@ export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
return (): any => {
const win: IonicWindow | undefined = doc.defaultView as any;
if (win && typeof (window as any) !== 'undefined') {
setupConfig({
...config,
_zoneGate: (h: any) => zone.run(h)
_zoneGate: (h: any) => zone.run(h),
});
const aelFn = '__zone_symbol__addEventListener' in (doc.body as any)
? '__zone_symbol__addEventListener'
: 'addEventListener';
const aelFn =
'__zone_symbol__addEventListener' in (doc.body as any) ? '__zone_symbol__addEventListener' : 'addEventListener';
return applyPolyfills().then(() => {
return defineCustomElements(win, {
@ -31,7 +29,7 @@ export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
},
rel(elm, eventName, cb, opts) {
elm.removeEventListener(eventName, cb, opts);
}
},
});
});
}

View File

@ -1,32 +1,30 @@
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { Directive, HostListener, ElementRef, Injector } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor, setIonicClasses } from './value-accessor';
@Directive({
/* tslint:disable-next-line:directive-selector */
selector: 'ion-checkbox,ion-toggle',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: BooleanValueAccessor,
multi: true
}
]
useExisting: BooleanValueAccessorDirective,
multi: true,
},
],
})
export class BooleanValueAccessor extends ValueAccessor {
export class BooleanValueAccessorDirective extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
writeValue(value: any) {
writeValue(value: any): void {
this.el.nativeElement.checked = this.lastValue = value == null ? false : value;
setIonicClasses(this.el);
}
@HostListener('ionChange', ['$event.target'])
_handleIonChange(el: any) {
_handleIonChange(el: any): void {
this.handleChangeEvent(el, el.checked);
}
}

View File

@ -1,32 +1,30 @@
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { Directive, HostListener, ElementRef, Injector } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@Directive({
/* tslint:disable-next-line:directive-selector */
selector: 'ion-input[type=number]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: NumericValueAccessor,
multi: true
}
]
useExisting: NumericValueAccessorDirective,
multi: true,
},
],
})
export class NumericValueAccessor extends ValueAccessor {
export class NumericValueAccessorDirective extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
@HostListener('ionChange', ['$event.target'])
_handleIonChange(el: any) {
_handleIonChange(el: any): void {
this.handleChangeEvent(el, el.value);
}
registerOnChange(fn: (_: number | null) => void) {
super.registerOnChange(value => {
registerOnChange(fn: (_: number | null) => void): void {
super.registerOnChange((value) => {
fn(value === '' ? null : parseFloat(value));
});
}

View File

@ -1,4 +1,4 @@
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@ -9,19 +9,18 @@ import { ValueAccessor } from './value-accessor';
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: RadioValueAccessor,
multi: true
}
]
useExisting: RadioValueAccessorDirective,
multi: true,
},
],
})
export class RadioValueAccessor extends ValueAccessor {
export class RadioValueAccessorDirective extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
@HostListener('ionSelect', ['$event.target'])
_handleIonSelect(el: any) {
_handleIonSelect(el: any): void {
this.handleChangeEvent(el, el.checked);
}
}

View File

@ -1,4 +1,4 @@
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@ -9,19 +9,18 @@ import { ValueAccessor } from './value-accessor';
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: SelectValueAccessor,
multi: true
}
]
useExisting: SelectValueAccessorDirective,
multi: true,
},
],
})
export class SelectValueAccessor extends ValueAccessor {
export class SelectValueAccessorDirective extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
@HostListener('ionChange', ['$event.target'])
_handleChangeEvent(el: any) {
_handleChangeEvent(el: any): void {
this.handleChangeEvent(el, el.value);
}
}

View File

@ -1,4 +1,4 @@
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@ -9,19 +9,18 @@ import { ValueAccessor } from './value-accessor';
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: TextValueAccessor,
multi: true
}
]
useExisting: TextValueAccessorDirective,
multi: true,
},
],
})
export class TextValueAccessor extends ValueAccessor {
export class TextValueAccessorDirective extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
@HostListener('ionChange', ['$event.target'])
_handleInputEvent(el: any) {
_handleInputEvent(el: any): void {
this.handleChangeEvent(el, el.value);
}
}

View File

@ -1,19 +1,23 @@
import { AfterViewInit, ElementRef, HostListener, Injector, OnDestroy, Type } from '@angular/core';
import { AfterViewInit, ElementRef, Injector, OnDestroy, Directive, HostListener } from '@angular/core';
import { ControlValueAccessor, NgControl } from '@angular/forms';
import { Subscription } from 'rxjs';
import { raf } from '../../util/util';
@Directive()
export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDestroy {
private onChange: (value: any) => void = () => {/**/};
private onTouched: () => void = () => {/**/};
private onChange: (value: any) => void = () => {
/**/
};
private onTouched: () => void = () => {
/**/
};
protected lastValue: any;
private statusChanges?: Subscription;
constructor(protected injector: Injector, protected el: ElementRef) {}
writeValue(value: any) {
writeValue(value: any): void {
/**
* TODO for Ionic 6:
* Change `value == null ? '' : value;`
@ -25,7 +29,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
setIonicClasses(this.el);
}
handleChangeEvent(el: HTMLElement, value: any) {
handleChangeEvent(el: HTMLElement, value: any): void {
if (el === this.el.nativeElement) {
if (value !== this.lastValue) {
this.lastValue = value;
@ -36,38 +40,42 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
}
@HostListener('ionBlur', ['$event.target'])
_handleBlurEvent(el: any) {
_handleBlurEvent(el: any): void {
if (el === this.el.nativeElement) {
this.onTouched();
setIonicClasses(this.el);
}
}
registerOnChange(fn: (value: any) => void) {
registerOnChange(fn: (value: any) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void) {
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean) {
setDisabledState(isDisabled: boolean): void {
this.el.nativeElement.disabled = isDisabled;
}
ngOnDestroy() {
ngOnDestroy(): void {
if (this.statusChanges) {
this.statusChanges.unsubscribe();
}
}
ngAfterViewInit() {
ngAfterViewInit(): void {
let ngControl;
try {
ngControl = this.injector.get<NgControl>(NgControl as Type<NgControl>);
} catch { /* No FormControl or ngModel binding */ }
ngControl = this.injector.get<NgControl>(NgControl);
} catch {
/* No FormControl or ngModel binding */
}
if (!ngControl) { return; }
if (!ngControl) {
return;
}
// Listen for changes in validity, disabled, or pending states
if (ngControl.statusChanges) {
@ -84,13 +92,13 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
* This patches the methods to manually sync
* the classes until this feature is implemented in Angular.
*/
const formControl = ngControl.control;
const formControl = ngControl.control as any;
if (formControl) {
const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'];
methodsToPatch.forEach(method => {
if (formControl[method]) {
methodsToPatch.forEach((method) => {
if (formControl.get(method)) {
const oldFn = formControl[method].bind(formControl);
formControl[method] = (...params) => {
formControl[method] = (...params: any[]) => {
oldFn(...params);
setIonicClasses(this.el);
};
@ -100,7 +108,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
}
}
export const setIonicClasses = (element: ElementRef) => {
export const setIonicClasses = (element: ElementRef): void => {
raf(() => {
const input = element.nativeElement as HTMLElement;
const classes = getClasses(input);
@ -127,16 +135,11 @@ const getClasses = (element: HTMLElement) => {
const setClasses = (element: HTMLElement, classes: string[]) => {
const classList = element.classList;
[
'ion-valid',
'ion-invalid',
'ion-touched',
'ion-untouched',
'ion-dirty',
'ion-pristine'
].forEach(c => classList.remove(c));
['ion-valid', 'ion-invalid', 'ion-touched', 'ion-untouched', 'ion-dirty', 'ion-pristine'].forEach((c) =>
classList.remove(c)
);
classes.forEach(c => classList.add(c));
classes.forEach((c) => classList.add(c));
};
const startsWith = (input: string, search: string): boolean => {

View File

@ -1,4 +1,4 @@
import { Directive, HostListener, Optional } from '@angular/core';
import { Directive, HostListener, Input, Optional } from '@angular/core';
import { AnimationBuilder } from '@ionic/core';
import { Config } from '../../providers/config';
@ -8,11 +8,12 @@ import { IonRouterOutlet } from './ion-router-outlet';
@Directive({
selector: 'ion-back-button',
inputs: ['defaultHref', 'routerAnimation'],
})
export class IonBackButtonDelegate {
export class IonBackButtonDelegateDirective {
@Input()
defaultHref: string | undefined | null;
@Input()
routerAnimation?: AnimationBuilder;
constructor(
@ -25,10 +26,10 @@ export class IonBackButtonDelegate {
* @internal
*/
@HostListener('click', ['$event'])
onClick(ev: Event) {
onClick(ev: Event): void {
const defaultHref = this.defaultHref || this.config.get('backButtonDefaultHref');
if (this.routerOutlet && this.routerOutlet.canGoBack()) {
if (this.routerOutlet?.canGoBack()) {
this.navCtrl.setDirection('back', undefined, undefined, this.routerAnimation);
this.routerOutlet.pop();
ev.preventDefault();

View File

@ -1,11 +1,26 @@
import { Location } from '@angular/common';
import { Attribute, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core';
import { ActivatedRoute, ChildrenOutletContexts, OutletContext, PRIMARY_OUTLET, Router } from '@angular/router';
import {
ComponentFactoryResolver,
ComponentRef,
ElementRef,
Injector,
NgZone,
OnDestroy,
OnInit,
ViewContainerRef,
Attribute,
Directive,
EventEmitter,
Optional,
Output,
SkipSelf,
} from '@angular/core';
import { OutletContext, Router, ActivatedRoute, ChildrenOutletContexts, PRIMARY_OUTLET } from '@angular/router';
import { componentOnReady } from '@ionic/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { Observable, BehaviorSubject } from 'rxjs';
import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
import { AnimationBuilder } from '../../';
import { AnimationBuilder } from '../../ionic-core';
import { Config } from '../../providers/config';
import { NavController } from '../../providers/nav-controller';
@ -15,8 +30,10 @@ import { RouteView, getUrl } from './stack-utils';
@Directive({
selector: 'ion-router-outlet',
exportAs: 'outlet',
inputs: ['animated', 'animation', 'swipeGesture']
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['animated', 'animation', 'swipeGesture'],
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class IonRouterOutlet implements OnDestroy, OnInit {
nativeEl: HTMLIonRouterOutletElement;
@ -37,7 +54,9 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
tabsPrefix: string | undefined;
@Output() stackEvents = new EventEmitter<any>();
// eslint-disable-next-line @angular-eslint/no-output-rename
@Output('activate') activateEvents = new EventEmitter<any>();
// eslint-disable-next-line @angular-eslint/no-output-rename
@Output('deactivate') deactivateEvents = new EventEmitter<any>();
set animation(animation: AnimationBuilder) {
@ -51,11 +70,13 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
set swipeGesture(swipe: boolean) {
this._swipeGesture = swipe;
this.nativeEl.swipeHandler = swipe ? {
this.nativeEl.swipeHandler = swipe
? {
canStart: () => this.stackCtrl.canGoBack(1) && !this.stackCtrl.hasRunningTask(),
onStart: () => this.stackCtrl.startBackTransition(),
onEnd: shouldContinue => this.stackCtrl.endBackTransition(shouldContinue)
} : undefined;
onEnd: (shouldContinue) => this.stackCtrl.endBackTransition(shouldContinue),
}
: undefined;
}
constructor(
@ -93,12 +114,12 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
// If the outlet was not instantiated at the time the route got activated we need to populate
// the outlet when it is initialized (ie inside a NgIf)
const context = this.getContext();
if (context && context.route) {
if (context?.route) {
this.activateWith(context.route, context.resolver || null);
}
}
new Promise(resolve => componentOnReady(this.nativeEl, resolve)).then(() => {
new Promise((resolve) => componentOnReady(this.nativeEl, resolve)).then(() => {
if (this._swipeGesture === undefined) {
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
}
@ -109,7 +130,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
return !!this.activated;
}
get component(): object {
get component(): Record<string, unknown> {
if (!this.activated) {
throw new Error('Outlet is not activated');
}
@ -140,13 +161,15 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
/**
* Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree
*/
attach(_ref: ComponentRef<any>, _activatedRoute: ActivatedRoute) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
attach(_ref: ComponentRef<any>, _activatedRoute: ActivatedRoute): void {
throw new Error('incompatible reuse strategy');
}
deactivate(): void {
if (this.activated) {
if (this.activatedView) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const context = this.getContext()!;
this.activatedView.savedData = new Map(context.children['contexts']);
@ -172,7 +195,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
const contextSnapshot = context.route.snapshot;
this.activatedView.savedExtras.queryParams = contextSnapshot.queryParams;
this.activatedView.savedExtras.fragment = contextSnapshot.fragment;
(this.activatedView.savedExtras.fragment as string | null) = contextSnapshot.fragment;
}
}
const c = this.component;
@ -183,7 +206,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
}
}
activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver | null) {
activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver | null): void {
if (this.isActivated) {
throw new Error('Cannot activate an already activated outlet');
}
@ -196,6 +219,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
const saved = enteringView.savedData;
if (saved) {
// self-restore
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const context = this.getContext()!;
context.children['contexts'] = saved;
}
@ -203,6 +227,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
this.updateActivatedRouteProxy(cmpRef.instance, activatedRoute);
} else {
const snapshot = (activatedRoute as any)._futureSnapshot;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const component = snapshot.routeConfig!.component as any;
resolver = resolver || this.resolver;
@ -230,7 +255,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
}
this.activatedView = enteringView;
this.stackCtrl.setActive(enteringView).then(data => {
this.stackCtrl.setActive(enteringView).then((data) => {
this.navCtrl.setTopOutlet(this);
this.activateEvents.emit(cmpRef.instance);
this.stackEvents.emit(data);
@ -313,11 +338,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
private proxyObservable(component$: Observable<any>, path: string): Observable<any> {
return component$.pipe(
// First wait until the component instance is pushed
filter(component => !!component),
switchMap(component =>
filter((component) => !!component),
switchMap((component) =>
this.currentActivatedRoute$.pipe(
filter(current => current !== null && current.component === component),
switchMap(current => current && (current.activatedRoute as any)[path]),
filter((current) => current !== null && current.component === component),
switchMap((current) => current && (current.activatedRoute as any)[path]),
distinctUntilChanged()
)
)
@ -344,11 +369,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
}
class OutletInjector implements Injector {
constructor(
private route: ActivatedRoute,
private childContexts: ChildrenOutletContexts,
private parent: Injector
) { }
constructor(private route: ActivatedRoute, private childContexts: ChildrenOutletContexts, private parent: Injector) {}
get(token: any, notFoundValue?: any): any {
if (token === ActivatedRoute) {
@ -359,7 +380,6 @@ class OutletInjector implements Injector {
return this.childContexts;
}
// tslint:disable-next-line
return this.parent.get(token, notFoundValue);
}
}

View File

@ -8,13 +8,13 @@ import { StackEvent } from './stack-utils';
@Component({
selector: 'ion-tabs',
template: `
<ng-content select="[slot=top]"></ng-content>
template: ` <ng-content select="[slot=top]"></ng-content>
<div class="tabs-inner">
<ion-router-outlet #outlet tabs="true" (stackEvents)="onPageSelected($event)"></ion-router-outlet>
</div>
<ng-content></ng-content>`,
styles: [`
styles: [
`
:host {
display: flex;
position: absolute;
@ -37,25 +37,24 @@ import { StackEvent } from './stack-utils';
flex: 1;
contain: layout size style;
}`
]
}
`,
],
})
// eslint-disable-next-line @angular-eslint/component-class-suffix
export class IonTabs {
@ViewChild('outlet', { read: IonRouterOutlet, static: false }) outlet: IonRouterOutlet;
@ContentChild(IonTabBar, { static: false }) tabBar: IonTabBar | undefined;
@Output() ionTabsWillChange = new EventEmitter<{ tab: string }>();
@Output() ionTabsDidChange = new EventEmitter<{ tab: string }>();
constructor(
private navCtrl: NavController,
) { }
constructor(private navCtrl: NavController) {}
/**
* @internal
*/
onPageSelected(detail: StackEvent) {
onPageSelected(detail: StackEvent): void {
const stackId = detail.enteringView.stackId;
if (detail.tabSwitch && stackId !== undefined) {
if (this.tabBar) {
@ -87,9 +86,9 @@ export class IonTabs {
* to the default tabRootUrl
*/
@HostListener('ionTabButtonClick', ['$event'])
select(tabOrEvent: string | CustomEvent) {
select(tabOrEvent: string | CustomEvent): Promise<boolean> | undefined {
const isTabString = typeof tabOrEvent === 'string';
const tab = (isTabString) ? tabOrEvent : (tabOrEvent as CustomEvent).detail.tab;
const tab = isTabString ? tabOrEvent : (tabOrEvent as CustomEvent).detail.tab;
const alreadySelected = this.outlet.getActiveStackId() === tab;
const tabRootUrl = `${this.outlet.tabsPrefix}/${tab}`;
@ -108,12 +107,14 @@ export class IonTabs {
const activeView = this.outlet.getLastRouteView(activeStackId);
// If on root tab, do not navigate to root tab again
if (activeView.url === tabRootUrl) { return; }
if (activeView?.url === tabRootUrl) {
return;
}
const rootView = this.outlet.getRootView(tab);
const navigationExtras = rootView && tabRootUrl === rootView.url && rootView.savedExtras;
return this.navCtrl.navigateRoot(tabRootUrl, {
...(navigationExtras),
...navigationExtras,
animated: true,
animationDirection: 'back',
});
@ -123,11 +124,11 @@ export class IonTabs {
* If there is a lastRoute, goto that, otherwise goto the fallback url of the
* selected tab
*/
const url = lastRoute && lastRoute.url || tabRootUrl;
const navigationExtras = lastRoute && lastRoute.savedExtras;
const url = lastRoute?.url || tabRootUrl;
const navigationExtras = lastRoute?.savedExtras;
return this.navCtrl.navigateRoot(url, {
...(navigationExtras),
...navigationExtras,
animated: true,
animationDirection: 'back',
});

View File

@ -1,15 +1,30 @@
import { ComponentFactoryResolver, Directive, ElementRef, Injector, ViewContainerRef } from '@angular/core';
import { ComponentFactoryResolver, ElementRef, Injector, ViewContainerRef, Directive } from '@angular/core';
import { AngularDelegate } from '../../providers/angular-delegate';
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
@ProxyCmp({
inputs: ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'],
methods: ['push', 'insert', 'insertPages', 'pop', 'popTo', 'popToRoot', 'removeIndex', 'setRoot', 'setPages', 'getActive', 'getByIndex', 'canGoBack', 'getPrevious']
methods: [
'push',
'insert',
'insertPages',
'pop',
'popTo',
'popToRoot',
'removeIndex',
'setRoot',
'setPages',
'getActive',
'getByIndex',
'canGoBack',
'getPrevious',
],
})
@Directive({
selector: 'ion-nav'
selector: 'ion-nav',
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class NavDelegate {
protected el: HTMLElement;
constructor(
@ -21,6 +36,6 @@ export class NavDelegate {
) {
this.el = ref.nativeElement;
ref.nativeElement.delegate = angularDelegate.create(resolver, injector, location);
proxyOutputs(this, this.el, ['ionNavDidChange' , 'ionNavWillChange' ]);
proxyOutputs(this, this.el, ['ionNavDidChange', 'ionNavWillChange']);
}
}

View File

@ -19,8 +19,7 @@
* ```
*/
export class NavParams {
constructor(public data: {[key: string]: any} = {}) {}
constructor(public data: { [key: string]: any } = {}) {}
/**
* Get the value of a nav-parameter for the current view

View File

@ -1,5 +1,5 @@
import { LocationStrategy } from '@angular/common';
import { Directive, ElementRef, HostListener, Optional } from '@angular/core';
import { ElementRef, OnChanges, OnDestroy, OnInit, Directive, HostListener, Input, Optional } from '@angular/core';
import { Router, RouterLink } from '@angular/router';
import { AnimationBuilder, RouterDirection } from '@ionic/core';
import { Subscription } from 'rxjs';
@ -8,13 +8,14 @@ import { NavController } from '../../providers/nav-controller';
@Directive({
selector: '[routerLink]',
inputs: ['routerDirection', 'routerAnimation']
})
export class RouterLinkDelegate {
export class RouterLinkDelegateDirective implements OnInit, OnChanges, OnDestroy {
private subscription?: Subscription;
@Input()
routerDirection: RouterDirection = 'forward';
@Input()
routerAnimation?: AnimationBuilder;
constructor(
@ -22,18 +23,18 @@ export class RouterLinkDelegate {
private navCtrl: NavController,
private elementRef: ElementRef,
private router: Router,
@Optional() private routerLink?: RouterLink,
) { }
@Optional() private routerLink?: RouterLink
) {}
ngOnInit() {
ngOnInit(): void {
this.updateTargetUrlAndHref();
}
ngOnChanges(): any {
ngOnChanges(): void {
this.updateTargetUrlAndHref();
}
ngOnDestroy(): any {
ngOnDestroy(): void {
if (this.subscription) {
this.subscription.unsubscribe();
}
@ -50,7 +51,7 @@ export class RouterLinkDelegate {
* @internal
*/
@HostListener('click', ['$event'])
onClick(ev: UIEvent) {
onClick(ev: UIEvent): void {
this.navCtrl.setDirection(this.routerDirection, undefined, undefined, this.routerAnimation);
ev.preventDefault();
}

View File

@ -6,10 +6,18 @@ import { AnimationBuilder, RouterDirection } from '@ionic/core';
import { bindLifecycleEvents } from '../../providers/angular-delegate';
import { NavController } from '../../providers/nav-controller';
import { RouteView, StackEvent, computeStackId, destroyView, getUrl, insertView, isTabSwitch, toSegments } from './stack-utils';
import {
RouteView,
StackEvent,
computeStackId,
destroyView,
getUrl,
insertView,
isTabSwitch,
toSegments,
} from './stack-utils';
export class StackController {
private views: RouteView[] = [];
private runningTask?: Promise<any>;
private skipTransition = false;
@ -30,7 +38,7 @@ export class StackController {
createView(ref: ComponentRef<any>, activatedRoute: ActivatedRoute): RouteView {
const url = getUrl(this.router, activatedRoute);
const element = (ref && ref.location && ref.location.nativeElement) as HTMLElement;
const element = ref?.location?.nativeElement as HTMLElement;
const unlistenEvents = bindLifecycleEvents(this.zone, ref.instance, element);
return {
id: this.nextId++,
@ -44,7 +52,7 @@ export class StackController {
getExistingView(activatedRoute: ActivatedRoute): RouteView | undefined {
const activatedUrlKey = getUrl(this.router, activatedRoute);
const view = this.views.find(vw => vw.url === activatedUrlKey);
const view = this.views.find((vw) => vw.url === activatedUrlKey);
if (view) {
view.ref.changeDetectorRef.reattach();
}
@ -65,17 +73,14 @@ export class StackController {
let currentNavigation;
const router = (this.router as any);
const router = this.router as any;
// Angular >= 7.2.0
if (router.getCurrentNavigation) {
currentNavigation = router.getCurrentNavigation();
// Angular < 7.2.0
} else if (
router.navigations &&
router.navigations.value
) {
} else if (router.navigations?.value) {
currentNavigation = router.navigations.value;
}
@ -86,11 +91,7 @@ export class StackController {
* we remove the last item
* from our views stack
*/
if (
currentNavigation &&
currentNavigation.extras &&
currentNavigation.extras.replaceUrl
) {
if (currentNavigation?.extras?.replaceUrl) {
if (this.views.length > 0) {
this.views.splice(-1, 1);
}
@ -114,12 +115,7 @@ export class StackController {
* provided another animation.
*/
const customAnimation = enteringView.animationBuilder;
if (
animationBuilder === undefined &&
direction === 'back' &&
!tabSwitch &&
customAnimation !== undefined
) {
if (animationBuilder === undefined && direction === 'back' && !tabSwitch && customAnimation !== undefined) {
animationBuilder = customAnimation;
}
@ -148,7 +144,7 @@ export class StackController {
enteringView,
direction,
animation,
tabSwitch
tabSwitch,
}));
});
});
@ -158,7 +154,7 @@ export class StackController {
return this.getStack(stackId).length > deep;
}
pop(deep: number, stackId = this.getActiveStackId()) {
pop(deep: number, stackId = this.getActiveStackId()): Promise<boolean> {
return this.zone.run(() => {
const views = this.getStack(stackId);
if (views.length <= deep) {
@ -170,13 +166,7 @@ export class StackController {
const viewSavedData = view.savedData;
if (viewSavedData) {
const primaryOutlet = viewSavedData.get('primary');
if (
primaryOutlet &&
primaryOutlet.route &&
primaryOutlet.route._routerState &&
primaryOutlet.route._routerState.snapshot &&
primaryOutlet.route._routerState.snapshot.url
) {
if (primaryOutlet?.route?._routerState?.snapshot.url) {
url = primaryOutlet.route._routerState.snapshot.url;
}
}
@ -185,7 +175,7 @@ export class StackController {
});
}
startBackTransition() {
startBackTransition(): Promise<boolean> | Promise<void> {
const leavingView = this.activeView;
if (leavingView) {
const views = this.getStack(leavingView.stackId);
@ -206,7 +196,7 @@ export class StackController {
return Promise.resolve();
}
endBackTransition(shouldComplete: boolean) {
endBackTransition(shouldComplete: boolean): void {
if (shouldComplete) {
this.skipTransition = true;
this.pop(1);
@ -215,7 +205,7 @@ export class StackController {
}
}
getLastUrl(stackId?: string) {
getLastUrl(stackId?: string): RouteView | undefined {
const views = this.getStack(stackId);
return views.length > 0 ? views[views.length - 1] : undefined;
}
@ -223,7 +213,7 @@ export class StackController {
/**
* @internal
*/
getRootUrl(stackId?: string) {
getRootUrl(stackId?: string): RouteView | undefined {
const views = this.getStack(stackId);
return views.length > 0 ? views[0] : undefined;
}
@ -236,7 +226,8 @@ export class StackController {
return this.runningTask !== undefined;
}
destroy() {
destroy(): void {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.containerEl = undefined!;
this.views.forEach(destroyView);
this.activeView = undefined;
@ -244,7 +235,7 @@ export class StackController {
}
private getStack(stackId: string | undefined) {
return this.views.filter(v => v.stackId === stackId);
return this.views.filter((v) => v.stackId === stackId);
}
private insertView(enteringView: RouteView, direction: RouterDirection) {
@ -285,7 +276,7 @@ export class StackController {
direction,
showGoBack,
progressAnimation,
animationBuilder
animationBuilder,
});
}
}
@ -297,15 +288,15 @@ export class StackController {
await this.runningTask;
this.runningTask = undefined;
}
const promise = this.runningTask = task();
promise.finally(() => this.runningTask = undefined);
const promise = (this.runningTask = task());
promise.finally(() => (this.runningTask = undefined));
return promise;
}
}
const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
if (typeof (requestAnimationFrame as any) === 'function') {
return new Promise<any>(resolve => {
return new Promise<void>((resolve) => {
requestAnimationFrame(() => {
cleanup(activeRoute, views, viewsSnapshot, location);
resolve();
@ -316,11 +307,9 @@ const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot:
};
const cleanup = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
viewsSnapshot
.filter(view => !views.includes(view))
.forEach(destroyView);
viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView);
views.forEach(view => {
views.forEach((view) => {
/**
* In the event that a user navigated multiple
* times in rapid succession, we want to make sure

View File

@ -2,7 +2,7 @@ import { ComponentRef } from '@angular/core';
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
import { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core';
export const insertView = (views: RouteView[], view: RouteView, direction: RouterDirection) => {
export const insertView = (views: RouteView[], view: RouteView, direction: RouterDirection): RouteView[] => {
if (direction === 'root') {
return setRoot(views, view);
} else if (direction === 'forward') {
@ -13,7 +13,7 @@ export const insertView = (views: RouteView[], view: RouteView, direction: Route
};
const setRoot = (views: RouteView[], view: RouteView) => {
views = views.filter(v => v.stackId !== view.stackId);
views = views.filter((v) => v.stackId !== view.stackId);
views.push(view);
return views;
};
@ -21,7 +21,7 @@ const setRoot = (views: RouteView[], view: RouteView) => {
const setForward = (views: RouteView[], view: RouteView) => {
const index = views.indexOf(view);
if (index >= 0) {
views = views.filter(v => v.stackId !== view.stackId || v.id <= view.id);
views = views.filter((v) => v.stackId !== view.stackId || v.id <= view.id);
} else {
views.push(view);
}
@ -31,25 +31,25 @@ const setForward = (views: RouteView[], view: RouteView) => {
const setBack = (views: RouteView[], view: RouteView) => {
const index = views.indexOf(view);
if (index >= 0) {
return views.filter(v => v.stackId !== view.stackId || v.id <= view.id);
return views.filter((v) => v.stackId !== view.stackId || v.id <= view.id);
} else {
return setRoot(views, view);
}
};
export const getUrl = (router: Router, activatedRoute: ActivatedRoute) => {
export const getUrl = (router: Router, activatedRoute: ActivatedRoute): string => {
const urlTree = router.createUrlTree(['.'], { relativeTo: activatedRoute });
return router.serializeUrl(urlTree);
};
export const isTabSwitch = (enteringView: RouteView, leavingView: RouteView | undefined) => {
export const isTabSwitch = (enteringView: RouteView, leavingView: RouteView | undefined): boolean => {
if (!leavingView) {
return true;
}
return enteringView.stackId !== leavingView.stackId;
};
export const computeStackId = (prefixUrl: string[] | undefined, url: string) => {
export const computeStackId = (prefixUrl: string[] | undefined, url: string): string | undefined => {
if (!prefixUrl) {
return undefined;
}
@ -65,14 +65,14 @@ export const computeStackId = (prefixUrl: string[] | undefined, url: string) =>
return undefined;
};
export const toSegments = (path: string) => {
export const toSegments = (path: string): string[] => {
return path
.split('/')
.map(s => s.trim())
.filter(s => s !== '');
.map((s) => s.trim())
.filter((s) => s !== '');
};
export const destroyView = (view: RouteView | undefined) => {
export const destroyView = (view: RouteView | undefined): void => {
if (view) {
// TODO lifecycle event
view.ref.destroy();

View File

@ -1,12 +1,66 @@
/* eslint-disable */
/* tslint:disable */
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, NgZone, TemplateRef } from "@angular/core";
import { ProxyCmp, proxyOutputs } from "../angular-component-lib/utils";
import { Components } from "@ionic/core";
export declare interface IonModal extends Components.IonModal {
}
@ProxyCmp({ inputs: ["animated", "backdropBreakpoint", "backdropDismiss", "breakpoints", "cssClass", "enterAnimation", "event", "handle", "initialBreakpoint", "isOpen", "keyboardClose", "leaveAnimation", "mode", "presentingElement", "showBackdrop", "swipeToClose", "translucent", "trigger"], "methods": ["present", "dismiss", "onDidDismiss", "onWillDismiss"] })
@Component({ selector: "ion-modal", changeDetection: ChangeDetectionStrategy.OnPush, template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen"></ng-container>`, inputs: ["animated", "backdropBreakpoint", "backdropDismiss", "breakpoints", "cssClass", "enterAnimation", "event", "handle", "initialBreakpoint", "isOpen", "keyboardClose", "leaveAnimation", "mode", "presentingElement", "showBackdrop", "swipeToClose", "translucent", "trigger"] })
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ContentChild,
ElementRef,
EventEmitter,
NgZone,
TemplateRef,
} from '@angular/core';
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
import { Components } from '@ionic/core';
export declare interface IonModal extends Components.IonModal {}
@ProxyCmp({
inputs: [
'animated',
'backdropBreakpoint',
'backdropDismiss',
'breakpoints',
'cssClass',
'enterAnimation',
'event',
'handle',
'initialBreakpoint',
'isOpen',
'keyboardClose',
'leaveAnimation',
'mode',
'presentingElement',
'showBackdrop',
'swipeToClose',
'translucent',
'trigger',
],
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'],
})
@Component({
selector: 'ion-modal',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen"></ng-container>`,
inputs: [
'animated',
'backdropBreakpoint',
'backdropDismiss',
'breakpoints',
'cssClass',
'enterAnimation',
'event',
'handle',
'initialBreakpoint',
'isOpen',
'keyboardClose',
'leaveAnimation',
'mode',
'presentingElement',
'showBackdrop',
'swipeToClose',
'translucent',
'trigger',
],
})
export class IonModal {
@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;
@ -34,6 +88,15 @@ export class IonModal {
c.detectChanges();
});
proxyOutputs(this, this.el, ["ionModalDidPresent", "ionModalWillPresent", "ionModalWillDismiss", "ionModalDidDismiss", "didPresent", "willPresent", "willDismiss", "didDismiss"]);
proxyOutputs(this, this.el, [
'ionModalDidPresent',
'ionModalWillPresent',
'ionModalWillDismiss',
'ionModalDidDismiss',
'didPresent',
'willPresent',
'willDismiss',
'didDismiss',
]);
}
}

View File

@ -1,12 +1,66 @@
/* eslint-disable */
/* tslint:disable */
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, NgZone, TemplateRef } from "@angular/core";
import { ProxyCmp, proxyOutputs } from "../angular-component-lib/utils";
import { Components } from "@ionic/core";
export declare interface IonPopover extends Components.IonPopover {
}
@ProxyCmp({ inputs: ["alignment", "animated", "arrow", "backdropDismiss", "cssClass", "dismissOnSelect", "enterAnimation", "event", "isOpen", "keyboardClose", "leaveAnimation", "mode", "showBackdrop", "translucent", "trigger", "triggerAction", "reference", "size"], "methods": ["present", "dismiss", "onDidDismiss", "onWillDismiss"] })
@Component({ selector: "ion-popover", changeDetection: ChangeDetectionStrategy.OnPush, template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen"></ng-container>`, inputs: ["alignment", "animated", "arrow", "backdropDismiss", "cssClass", "dismissOnSelect", "enterAnimation", "event", "isOpen", "keyboardClose", "leaveAnimation", "mode", "showBackdrop", "translucent", "trigger", "triggerAction", "reference", "size"] })
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ContentChild,
ElementRef,
EventEmitter,
NgZone,
TemplateRef,
} from '@angular/core';
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
import { Components } from '@ionic/core';
export declare interface IonPopover extends Components.IonPopover {}
@ProxyCmp({
inputs: [
'alignment',
'animated',
'arrow',
'backdropDismiss',
'cssClass',
'dismissOnSelect',
'enterAnimation',
'event',
'isOpen',
'keyboardClose',
'leaveAnimation',
'mode',
'showBackdrop',
'translucent',
'trigger',
'triggerAction',
'reference',
'size',
],
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'],
})
@Component({
selector: 'ion-popover',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen"></ng-container>`,
inputs: [
'alignment',
'animated',
'arrow',
'backdropDismiss',
'cssClass',
'dismissOnSelect',
'enterAnimation',
'event',
'isOpen',
'keyboardClose',
'leaveAnimation',
'mode',
'showBackdrop',
'translucent',
'trigger',
'triggerAction',
'reference',
'size',
],
})
export class IonPopover {
@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;
@ -34,6 +88,15 @@ export class IonPopover {
c.detectChanges();
});
proxyOutputs(this, this.el, ["ionPopoverDidPresent", "ionPopoverWillPresent", "ionPopoverWillDismiss", "ionPopoverDidDismiss", "didPresent", "willPresent", "willDismiss", "didDismiss"]);
proxyOutputs(this, this.el, [
'ionPopoverDidPresent',
'ionPopoverWillPresent',
'ionPopoverWillDismiss',
'ionPopoverDidDismiss',
'didPresent',
'willPresent',
'willDismiss',
'didDismiss',
]);
}
}

View File

@ -205,8 +205,8 @@ export class IonVirtualScroll {
case 'item': return this.itmTmp.templateRef;
case 'header': return this.hdrTmp.templateRef;
case 'footer': return this.ftrTmp.templateRef;
default: throw new Error('template for virtual item was not provided');
}
throw new Error('template for virtual item was not provided');
}
}

View File

@ -1,14 +1,14 @@
// DIRECTIVES
export { BooleanValueAccessor } from './directives/control-value-accessors/boolean-value-accessor';
export { NumericValueAccessor } from './directives/control-value-accessors/numeric-value-accesssor';
export { RadioValueAccessor } from './directives/control-value-accessors/radio-value-accessor';
export { SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
export { TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
export { BooleanValueAccessorDirective as BooleanValueAccessor } from './directives/control-value-accessors/boolean-value-accessor';
export { NumericValueAccessorDirective as NumericValueAccessor } from './directives/control-value-accessors/numeric-value-accesssor';
export { RadioValueAccessorDirective as RadioValueAccessor } from './directives/control-value-accessors/radio-value-accessor';
export { SelectValueAccessorDirective as SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
export { TextValueAccessorDirective as TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
export { IonTabs } from './directives/navigation/ion-tabs';
export { IonBackButtonDelegate } from './directives/navigation/ion-back-button';
export { IonBackButtonDelegateDirective as IonBackButtonDelegate } from './directives/navigation/ion-back-button';
export { NavDelegate } from './directives/navigation/nav-delegate';
export { IonRouterOutlet } from './directives/navigation/ion-router-outlet';
export { RouterLinkDelegate } from './directives/navigation/router-link-delegate';
export { RouterLinkDelegateDirective as RouterLinkDelegate } from './directives/navigation/router-link-delegate';
export { NavParams } from './directives/navigation/nav-params';
export { IonVirtualScroll } from './directives/virtual-scroll/virtual-scroll';
export { VirtualItem } from './directives/virtual-scroll/virtual-item';
@ -55,7 +55,6 @@ export {
getPlatforms,
isPlatform,
getTimeGivenProgression,
// TYPES
Animation,
AnimationBuilder,
@ -68,92 +67,61 @@ export {
GestureConfig,
GestureDetail,
NavComponentWithProps,
SpinnerTypes,
AccordionGroupCustomEvent,
AccordionGroupChangeEventDetail,
BreadcrumbCustomEvent,
BreadcrumbCollapsedClickEventDetail,
ActionSheetOptions,
ActionSheetButton,
AlertOptions,
AlertInput,
AlertTextareaAttributes,
AlertInputAttributes,
AlertButton,
BackButtonEvent,
CheckboxCustomEvent,
CheckboxChangeEventDetail,
DatetimeCustomEvent,
DatetimeChangeEventDetail,
InfiniteScrollCustomEvent,
InputCustomEvent,
InputChangeEventDetail,
ItemReorderEventDetail,
ItemReorderCustomEvent,
ItemSlidingCustomEvent,
IonicSafeString,
LoadingOptions,
MenuCustomEvent,
ModalOptions,
NavCustomEvent,
PickerOptions,
PickerButton,
PickerColumn,
PickerColumnOption,
PlatformConfig,
PopoverOptions,
RadioGroupCustomEvent,
RadioGroupChangeEventDetail,
RefresherCustomEvent,
RefresherEventDetail,
RouterEventDetail,
RouterCustomEvent,
ScrollBaseCustomEvent,
ScrollBaseDetail,
ScrollDetail,
ScrollCustomEvent,
SearchbarCustomEvent,
SearchbarChangeEventDetail,
SegmentChangeEventDetail,
SegmentCustomEvent,
SelectChangeEventDetail,
SelectCustomEvent,
TabsCustomEvent,
TextareaChangeEventDetail,
TextareaCustomEvent,
ToastOptions,
ToastButton,
ToggleChangeEventDetail,
ToggleCustomEvent,
} from '@ionic/core';

21
angular/src/ionic-core.ts Normal file
View File

@ -0,0 +1,21 @@
// Re-exports from ionic/core
// UTILS
export { IonicSafeString, getPlatforms, isPlatform, createAnimation } from '@ionic/core';
// CORE TYPES
export {
Animation,
AnimationBuilder,
AnimationCallbackOptions,
AnimationDirection,
AnimationFill,
AnimationKeyFrames,
AnimationLifecycle,
Gesture,
GestureConfig,
GestureDetail,
mdTransitionAnimation,
iosTransitionAnimation,
NavComponentWithProps,
} from '@ionic/core';

View File

@ -1,21 +1,97 @@
import { CommonModule, DOCUMENT } from '@angular/common';
import { APP_INITIALIZER, ModuleWithProviders, NgModule, NgZone } from '@angular/core';
import { ModuleWithProviders, APP_INITIALIZER, NgModule, NgZone } from '@angular/core';
import { IonicConfig } from '@ionic/core';
import { appInitialize } from './app-initialize';
import { BooleanValueAccessor } from './directives/control-value-accessors/boolean-value-accessor';
import { NumericValueAccessor } from './directives/control-value-accessors/numeric-value-accesssor';
import { RadioValueAccessor } from './directives/control-value-accessors/radio-value-accessor';
import { SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
import { TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
import { IonBackButtonDelegate } from './directives/navigation/ion-back-button';
import { BooleanValueAccessorDirective } from './directives/control-value-accessors/boolean-value-accessor';
import { NumericValueAccessorDirective } from './directives/control-value-accessors/numeric-value-accesssor';
import { RadioValueAccessorDirective } from './directives/control-value-accessors/radio-value-accessor';
import { SelectValueAccessorDirective } from './directives/control-value-accessors/select-value-accessor';
import { TextValueAccessorDirective } from './directives/control-value-accessors/text-value-accessor';
import { IonBackButtonDelegateDirective } from './directives/navigation/ion-back-button';
import { IonRouterOutlet } from './directives/navigation/ion-router-outlet';
import { IonTabs } from './directives/navigation/ion-tabs';
import { NavDelegate } from './directives/navigation/nav-delegate';
import { RouterLinkDelegate } from './directives/navigation/router-link-delegate';
import { RouterLinkDelegateDirective } from './directives/navigation/router-link-delegate';
import { IonModal } from './directives/overlays/modal';
import { IonPopover } from './directives/overlays/popover';
import { IonAccordion, IonAccordionGroup, IonApp, IonAvatar, IonBackButton, IonBackdrop, IonBadge, IonBreadcrumb, IonBreadcrumbs, IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonCheckbox, IonChip, IonCol, IonContent, IonDatetime, IonFab, IonFabButton, IonFabList, IonFooter, IonGrid, IonHeader, IonIcon, IonImg, IonInfiniteScroll, IonInfiniteScrollContent, IonInput, IonItem, IonItemDivider, IonItemGroup, IonItemOption, IonItemOptions, IonItemSliding, IonLabel, IonList, IonListHeader, IonMenu, IonMenuButton, IonMenuToggle, IonNav, IonNavLink, IonNote, IonProgressBar, IonRadio, IonRadioGroup, IonRange, IonRefresher, IonRefresherContent, IonReorder, IonReorderGroup, IonRippleEffect, IonRow, IonSearchbar, IonSegment, IonSegmentButton, IonSelect, IonSelectOption, IonSkeletonText, IonSlide, IonSlides, IonSpinner, IonSplitPane, IonTabBar, IonTabButton, IonText, IonTextarea, IonThumbnail, IonTitle, IonToggle, IonToolbar } from './directives/proxies';
import {
IonAccordion,
IonAccordionGroup,
IonApp,
IonAvatar,
IonBackButton,
IonBackdrop,
IonBadge,
IonBreadcrumb,
IonBreadcrumbs,
IonButton,
IonButtons,
IonCard,
IonCardContent,
IonCardHeader,
IonCardSubtitle,
IonCardTitle,
IonCheckbox,
IonChip,
IonCol,
IonContent,
IonDatetime,
IonFab,
IonFabButton,
IonFabList,
IonFooter,
IonGrid,
IonHeader,
IonIcon,
IonImg,
IonInfiniteScroll,
IonInfiniteScrollContent,
IonInput,
IonItem,
IonItemDivider,
IonItemGroup,
IonItemOption,
IonItemOptions,
IonItemSliding,
IonLabel,
IonList,
IonListHeader,
IonMenu,
IonMenuButton,
IonMenuToggle,
IonNav,
IonNavLink,
IonNote,
IonProgressBar,
IonRadio,
IonRadioGroup,
IonRange,
IonRefresher,
IonRefresherContent,
IonReorder,
IonReorderGroup,
IonRippleEffect,
IonRow,
IonSearchbar,
IonSegment,
IonSegmentButton,
IonSelect,
IonSelectOption,
IonSkeletonText,
IonSlide,
IonSlides,
IonSpinner,
IonSplitPane,
IonTabBar,
IonTabButton,
IonText,
IonTextarea,
IonThumbnail,
IonTitle,
IonToggle,
IonToolbar,
} from './directives/proxies';
import { VirtualFooter } from './directives/virtual-scroll/virtual-footer';
import { VirtualHeader } from './directives/virtual-scroll/virtual-header';
import { VirtualItem } from './directives/virtual-scroll/virtual-item';
@ -108,30 +184,30 @@ const DECLARATIONS = [
IonTabs,
// ngModel accessors
BooleanValueAccessor,
NumericValueAccessor,
RadioValueAccessor,
SelectValueAccessor,
TextValueAccessor,
BooleanValueAccessorDirective,
NumericValueAccessorDirective,
RadioValueAccessorDirective,
SelectValueAccessorDirective,
TextValueAccessorDirective,
// navigation
IonRouterOutlet,
IonBackButtonDelegate,
IonBackButtonDelegateDirective,
NavDelegate,
RouterLinkDelegate,
RouterLinkDelegateDirective,
// virtual scroll
VirtualFooter,
VirtualHeader,
VirtualItem,
IonVirtualScroll
IonVirtualScroll,
];
@NgModule({
declarations: DECLARATIONS,
exports: DECLARATIONS,
providers: [AngularDelegate, ModalController, PopoverController],
imports: [CommonModule]
imports: [CommonModule],
})
export class IonicModule {
static forRoot(config?: IonicConfig): ModuleWithProviders<IonicModule> {
@ -140,19 +216,15 @@ export class IonicModule {
providers: [
{
provide: ConfigToken,
useValue: config
useValue: config,
},
{
provide: APP_INITIALIZER,
useFactory: appInitialize,
multi: true,
deps: [
ConfigToken,
DOCUMENT,
NgZone
]
}
]
deps: [ConfigToken, DOCUMENT, NgZone],
},
],
};
}
}

View File

@ -1,27 +1,37 @@
import { ApplicationRef, ComponentFactoryResolver, Injectable, InjectionToken, Injector, NgZone, ViewContainerRef } from '@angular/core';
import { FrameworkDelegate, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE, LIFECYCLE_WILL_UNLOAD } from '@ionic/core';
import {
ApplicationRef,
ComponentFactoryResolver,
NgZone,
ViewContainerRef,
Injectable,
InjectionToken,
Injector,
} from '@angular/core';
import {
FrameworkDelegate,
LIFECYCLE_DID_ENTER,
LIFECYCLE_DID_LEAVE,
LIFECYCLE_WILL_ENTER,
LIFECYCLE_WILL_LEAVE,
LIFECYCLE_WILL_UNLOAD,
} from '@ionic/core';
import { NavParams } from '../directives/navigation/nav-params';
@Injectable()
export class AngularDelegate {
constructor(
private zone: NgZone,
private appRef: ApplicationRef
) {}
constructor(private zone: NgZone, private appRef: ApplicationRef) {}
create(
resolver: ComponentFactoryResolver,
injector: Injector,
location?: ViewContainerRef,
) {
location?: ViewContainerRef
): AngularFrameworkDelegate {
return new AngularFrameworkDelegate(resolver, injector, location, this.appRef, this.zone);
}
}
export class AngularFrameworkDelegate implements FrameworkDelegate {
private elRefMap = new WeakMap<HTMLElement, any>();
private elEventsMap = new WeakMap<HTMLElement, () => void>();
@ -30,16 +40,24 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
private injector: Injector,
private location: ViewContainerRef | undefined,
private appRef: ApplicationRef,
private zone: NgZone,
private zone: NgZone
) {}
attachViewToDom(container: any, component: any, params?: any, cssClasses?: string[]): Promise<any> {
return this.zone.run(() => {
return new Promise(resolve => {
return new Promise((resolve) => {
const el = attachView(
this.zone, this.resolver, this.injector, this.location, this.appRef,
this.elRefMap, this.elEventsMap,
container, component, params, cssClasses
this.zone,
this.resolver,
this.injector,
this.location,
this.appRef,
this.elRefMap,
this.elEventsMap,
container,
component,
params,
cssClasses
);
resolve(el);
});
@ -48,7 +66,7 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
removeViewFromDom(_container: any, component: any): Promise<void> {
return this.zone.run(() => {
return new Promise(resolve => {
return new Promise((resolve) => {
const componentRef = this.elRefMap.get(component);
if (componentRef) {
componentRef.destroy();
@ -73,14 +91,17 @@ export const attachView = (
appRef: ApplicationRef,
elRefMap: WeakMap<HTMLElement, any>,
elEventsMap: WeakMap<HTMLElement, () => void>,
container: any, component: any, params: any, cssClasses: string[] | undefined
) => {
container: any,
component: any,
params: any,
cssClasses: string[] | undefined
): any => {
const factory = resolver.resolveComponentFactory(component);
const childInjector = Injector.create({
providers: getProviders(params),
parent: injector
parent: injector,
});
const componentRef = (location)
const componentRef = location
? location.createComponent(factory, location.length, childInjector)
: factory.create(childInjector);
@ -111,35 +132,36 @@ const LIFECYCLES = [
LIFECYCLE_DID_ENTER,
LIFECYCLE_WILL_LEAVE,
LIFECYCLE_DID_LEAVE,
LIFECYCLE_WILL_UNLOAD
LIFECYCLE_WILL_UNLOAD,
];
export const bindLifecycleEvents = (zone: NgZone, instance: any, element: HTMLElement) => {
export const bindLifecycleEvents = (zone: NgZone, instance: any, element: HTMLElement): (() => void) => {
return zone.run(() => {
const unregisters = LIFECYCLES
.filter(eventName => typeof instance[eventName] === 'function')
.map(eventName => {
const unregisters = LIFECYCLES.filter((eventName) => typeof instance[eventName] === 'function').map((eventName) => {
const handler = (ev: any) => instance[eventName](ev.detail);
element.addEventListener(eventName, handler);
return () => element.removeEventListener(eventName, handler);
});
return () => unregisters.forEach(fn => fn());
return () => unregisters.forEach((fn) => fn());
});
};
const NavParamsToken = new InjectionToken<any>('NavParamsToken');
const getProviders = (params: {[key: string]: any}) => {
const getProviders = (params: { [key: string]: any }) => {
return [
{
provide: NavParamsToken, useValue: params
provide: NavParamsToken,
useValue: params,
},
{
provide: NavParams, useFactory: provideNavParamsInjectable, deps: [NavParamsToken]
}
provide: NavParams,
useFactory: provideNavParamsInjectable,
deps: [NavParamsToken],
},
];
};
const provideNavParamsInjectable = (params: {[key: string]: any}) => {
const provideNavParamsInjectable = (params: { [key: string]: any }) => {
return new NavParams(params);
};

View File

@ -4,10 +4,9 @@ import { Config as CoreConfig, IonicConfig } from '@ionic/core';
import { IonicWindow } from '../types/interfaces';
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class Config {
get(key: keyof IonicConfig, fallback?: any): any {
const c = getConfig();
if (c) {
@ -38,7 +37,7 @@ export const ConfigToken = new InjectionToken<any>('USERCONFIG');
const getConfig = (): CoreConfig | null => {
if (typeof (window as any) !== 'undefined') {
const Ionic = (window as any as IonicWindow).Ionic;
if (Ionic && Ionic.config) {
if (Ionic?.config) {
return Ionic.config;
}
}

View File

@ -4,12 +4,11 @@ import { Injectable } from '@angular/core';
providedIn: 'root',
})
export class DomController {
/**
* Schedules a task to run during the READ phase of the next frame.
* This task should only read the DOM, but never modify it.
*/
read(cb: RafCallback) {
read(cb: RafCallback): void {
getQueue().read(cb);
}
@ -17,29 +16,29 @@ export class DomController {
* Schedules a task to run during the WRITE phase of the next frame.
* This task should write the DOM, but never READ it.
*/
write(cb: RafCallback) {
write(cb: RafCallback): void {
getQueue().write(cb);
}
}
const getQueue = () => {
const win = typeof (window as any) !== 'undefined' ? window : null as any;
const win = typeof (window as any) !== 'undefined' ? window : (null as any);
if (win != null) {
const Ionic = win.Ionic;
if (Ionic && Ionic.queue) {
if (Ionic?.queue) {
return Ionic.queue;
}
return {
read: (cb: any) => win.requestAnimationFrame(cb),
write: (cb: any) => win.requestAnimationFrame(cb)
write: (cb: any) => win.requestAnimationFrame(cb),
};
}
return {
read: (cb: any) => cb(),
write: (cb: any) => cb()
write: (cb: any) => cb(),
};
};

View File

@ -1,4 +1,4 @@
import { Injectable, NgZone } from '@angular/core';
import { NgZone, Injectable } from '@angular/core';
import { Gesture, GestureConfig, createGesture } from '@ionic/core';
@Injectable({
@ -11,10 +11,10 @@ export class GestureController {
*/
create(opts: GestureConfig, runInsideAngularZone = false): Gesture {
if (runInsideAngularZone) {
Object.getOwnPropertyNames(opts).forEach(key => {
Object.getOwnPropertyNames(opts).forEach((key) => {
if (typeof opts[key] === 'function') {
const fn = opts[key];
opts[key] = (...props) => this.zone.run(() => fn(...props));
opts[key] = (...props: any[]) => this.zone.run(() => fn(...props));
}
});
}

View File

@ -5,13 +5,12 @@ import { menuController } from '@ionic/core';
providedIn: 'root',
})
export class MenuController {
/**
* Programmatically open the Menu.
* @param [menuId] Optionally get the menu by its id, or side.
* @return returns a promise when the menu is fully opened
*/
open(menuId?: string) {
open(menuId?: string): Promise<boolean> {
return menuController.open(menuId);
}
@ -22,7 +21,7 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return returns a promise when the menu is fully closed
*/
close(menuId?: string) {
close(menuId?: string): Promise<boolean> {
return menuController.close(menuId);
}
@ -32,7 +31,7 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return returns a promise when the menu has been toggled
*/
toggle(menuId?: string) {
toggle(menuId?: string): Promise<boolean> {
return menuController.toggle(menuId);
}
@ -44,7 +43,7 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return Returns the instance of the menu, which is useful for chaining.
*/
enable(shouldEnable: boolean, menuId?: string) {
enable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement | undefined> {
return menuController.enable(shouldEnable, menuId);
}
@ -54,7 +53,7 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return Returns the instance of the menu, which is useful for chaining.
*/
swipeGesture(shouldEnable: boolean, menuId?: string) {
swipeGesture(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement | undefined> {
return menuController.swipeGesture(shouldEnable, menuId);
}
@ -63,7 +62,7 @@ export class MenuController {
* @return Returns true if the specified menu is currently open, otherwise false.
* If the menuId is not specified, it returns true if ANY menu is currenly open.
*/
isOpen(menuId?: string) {
isOpen(menuId?: string): Promise<boolean> {
return menuController.isOpen(menuId);
}
@ -71,7 +70,7 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return Returns true if the menu is currently enabled, otherwise false.
*/
isEnabled(menuId?: string) {
isEnabled(menuId?: string): Promise<boolean> {
return menuController.isEnabled(menuId);
}
@ -84,21 +83,21 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return Returns the instance of the menu if found, otherwise `null`.
*/
get(menuId?: string) {
get(menuId?: string): Promise<HTMLIonMenuElement | undefined> {
return menuController.get(menuId);
}
/**
* @return Returns the instance of the menu already opened, otherwise `null`.
*/
getOpen() {
getOpen(): Promise<HTMLIonMenuElement | undefined> {
return menuController.getOpen();
}
/**
* @return Returns an array of all menu instances.
*/
getMenus() {
getMenus(): Promise<HTMLIonMenuElement[]> {
return menuController.getMenus();
}
}

View File

@ -1,4 +1,4 @@
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
import { ComponentFactoryResolver, Injector, Injectable } from '@angular/core';
import { ModalOptions, modalController } from '@ionic/core';
import { OverlayBaseController } from '../util/overlay';
@ -7,11 +7,10 @@ import { AngularDelegate } from './angular-delegate';
@Injectable()
export class ModalController extends OverlayBaseController<ModalOptions, HTMLIonModalElement> {
constructor(
private angularDelegate: AngularDelegate,
private resolver: ComponentFactoryResolver,
private injector: Injector,
private injector: Injector
) {
super(modalController);
}
@ -19,7 +18,7 @@ export class ModalController extends OverlayBaseController<ModalOptions, HTMLIon
create(opts: ModalOptions): Promise<HTMLIonModalElement> {
return super.create({
...opts,
delegate: this.angularDelegate.create(this.resolver, this.injector)
delegate: this.angularDelegate.create(this.resolver, this.injector),
});
}
}

View File

@ -1,6 +1,6 @@
import { Location } from '@angular/common';
import { Injectable, Optional } from '@angular/core';
import { NavigationExtras, NavigationStart, Router, UrlSerializer, UrlTree } from '@angular/router';
import { NavigationExtras, Router, UrlSerializer, UrlTree, NavigationStart } from '@angular/router';
import { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core';
import { IonRouterOutlet } from '../directives/navigation/ion-router-outlet';
@ -19,7 +19,6 @@ export interface NavigationOptions extends NavigationExtras, AnimationOptions {}
providedIn: 'root',
})
export class NavController {
private topOutlet?: IonRouterOutlet;
private direction: 'forward' | 'back' | 'root' | 'auto' = DEFAULT_DIRECTION;
private animated?: NavDirection = DEFAULT_ANIMATED;
@ -32,13 +31,13 @@ export class NavController {
platform: Platform,
private location: Location,
private serializer: UrlSerializer,
@Optional() private router?: Router,
@Optional() private router?: Router
) {
// Subscribe to router events to detect direction
if (router) {
router.events.subscribe(ev => {
router.events.subscribe((ev) => {
if (ev instanceof NavigationStart) {
const id = (ev.restoredState) ? ev.restoredState.navigationId : ev.id;
const id = ev.restoredState ? ev.restoredState.navigationId : ev.id;
this.guessDirection = id < this.lastNavId ? 'back' : 'forward';
this.guessAnimation = !ev.restoredState ? this.guessDirection : undefined;
this.lastNavId = this.guessDirection === 'forward' ? ev.id : id;
@ -47,7 +46,7 @@ export class NavController {
}
// Subscribe to backButton events
platform.backButton.subscribeWithPriority(0, processNextHandler => {
platform.backButton.subscribeWithPriority(0, (processNextHandler) => {
this.pop();
processNextHandler();
});
@ -122,7 +121,7 @@ export class NavController {
* It will use the standard `window.history.back()` under the hood, but featuring a `back` animation
* by default.
*/
back(options: AnimationOptions = { animated: true, animationDirection: 'back' }) {
back(options: AnimationOptions = { animated: true, animationDirection: 'back' }): void {
this.setDirection('back', options.animated, options.animationDirection, options.animation);
return this.location.back();
}
@ -133,7 +132,7 @@ export class NavController {
* It recursively finds the top active `ion-router-outlet` and calls `pop()`.
* This is the recommended way to go back when you are using `ion-router-outlet`.
*/
async pop() {
async pop(): Promise<void> {
let outlet = this.topOutlet;
while (outlet) {
@ -152,7 +151,12 @@ export class NavController {
*
* It's recommended to use `navigateForward()`, `navigateBack()` and `navigateRoot()` instead of `setDirection()`.
*/
setDirection(direction: RouterDirection, animated?: boolean, animationDirection?: 'forward' | 'back', animationBuilder?: AnimationBuilder) {
setDirection(
direction: RouterDirection,
animated?: boolean,
animationDirection?: 'forward' | 'back',
animationBuilder?: AnimationBuilder
): void {
this.direction = direction;
this.animated = getAnimation(direction, animated, animationDirection);
this.animationBuilder = animationBuilder;
@ -161,14 +165,18 @@ export class NavController {
/**
* @internal
*/
setTopOutlet(outlet: IonRouterOutlet) {
setTopOutlet(outlet: IonRouterOutlet): void {
this.topOutlet = outlet;
}
/**
* @internal
*/
consumeTransition() {
consumeTransition(): {
direction: RouterDirection;
animation: NavDirection | undefined;
animationBuilder: AnimationBuilder | undefined;
} {
let direction: RouterDirection = 'root';
let animation: NavDirection | undefined;
const animationBuilder = this.animationBuilder;
@ -187,15 +195,15 @@ export class NavController {
return {
direction,
animation,
animationBuilder
animationBuilder,
};
}
private navigate(url: string | UrlTree | any[], options: NavigationOptions) {
if (Array.isArray(url)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.router!.navigate(url, options);
} else {
/**
* navigateByUrl ignores any properties that
* would change the url, so things like queryParams
@ -217,12 +225,17 @@ export class NavController {
* that do not modify the url, such as `replaceUrl` which is why
* `options` is passed in here.
*/
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.router!.navigateByUrl(urlTree, options);
}
}
}
const getAnimation = (direction: RouterDirection, animated: boolean | undefined, animationDirection: 'forward' | 'back' | undefined): NavDirection | undefined => {
const getAnimation = (
direction: RouterDirection,
animated: boolean | undefined,
animationDirection: 'forward' | 'back' | undefined
): NavDirection | undefined => {
if (animated === false) {
return undefined;
}

View File

@ -1,17 +1,19 @@
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable, NgZone } from '@angular/core';
import { NgZone, Inject, Injectable } from '@angular/core';
import { BackButtonEventDetail, KeyboardEventDetail, Platforms, getPlatforms, isPlatform } from '@ionic/core';
import { Subject, Subscription } from 'rxjs';
import { Subscription, Subject } from 'rxjs';
export interface BackButtonEmitter extends Subject<BackButtonEventDetail> {
subscribeWithPriority(priority: number, callback: (processNextHandler: () => void) => Promise<any> | void): Subscription;
subscribeWithPriority(
priority: number,
callback: (processNextHandler: () => void) => Promise<any> | void
): Subscription;
}
@Injectable({
providedIn: 'root',
})
export class Platform {
private _readyPromise: Promise<string>;
private win: any;
@ -57,9 +59,9 @@ export class Platform {
constructor(@Inject(DOCUMENT) private doc: any, zone: NgZone) {
zone.run(() => {
this.win = doc.defaultView;
this.backButton.subscribeWithPriority = function(priority, callback) {
return this.subscribe(ev => {
return ev.register(priority, processNextHandler => zone.run(() => callback(processNextHandler)));
this.backButton.subscribeWithPriority = function (priority, callback) {
return this.subscribe((ev) => {
return ev.register(priority, (processNextHandler) => zone.run(() => callback(processNextHandler)));
});
};
@ -71,12 +73,19 @@ export class Platform {
proxyEvent(this.keyboardDidHide, this.win, 'ionKeyboardDidHide');
let readyResolve: (value: string) => void;
this._readyPromise = new Promise(res => { readyResolve = res; });
if (this.win && this.win['cordova']) {
doc.addEventListener('deviceready', () => {
this._readyPromise = new Promise((res) => {
readyResolve = res;
});
if (this.win?.['cordova']) {
doc.addEventListener(
'deviceready',
() => {
readyResolve('cordova');
}, { once: true });
},
{ once: true }
);
} else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
readyResolve!('dom');
}
});
@ -213,25 +222,25 @@ export class Platform {
* Returns `true` if the app is in portrait mode.
*/
isPortrait(): boolean {
return this.win.matchMedia && this.win.matchMedia('(orientation: portrait)').matches;
return this.win.matchMedia?.('(orientation: portrait)').matches;
}
testUserAgent(expression: string): boolean {
const nav = this.win.navigator;
return !!(nav && nav.userAgent && nav.userAgent.indexOf(expression) >= 0);
return !!(nav?.userAgent && nav.userAgent.indexOf(expression) >= 0);
}
/**
* Get the current url.
*/
url() {
url(): string {
return this.win.location.href;
}
/**
* Gets the width of the platform's viewport using `window.innerWidth`.
*/
width() {
width(): number {
return this.win.innerWidth;
}
@ -244,17 +253,17 @@ export class Platform {
}
const readQueryParam = (url: string, key: string) => {
key = key.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
key = key.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
const regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
const results = regex.exec(url);
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null;
};
const proxyEvent = <T>(emitter: Subject<T>, el: EventTarget, eventName: string) => {
if ((el as any)) {
if (el as any) {
el.addEventListener(eventName, (ev: Event | undefined | null) => {
// ?? cordova might emit "null" events
emitter.next(ev != null ? (ev as any).detail as T : undefined);
emitter.next(ev != null ? ((ev as any).detail as T) : undefined);
});
}
};

View File

@ -1,4 +1,4 @@
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
import { ComponentFactoryResolver, Injector, Injectable } from '@angular/core';
import { PopoverOptions, popoverController } from '@ionic/core';
import { OverlayBaseController } from '../util/overlay';
@ -7,11 +7,10 @@ import { AngularDelegate } from './angular-delegate';
@Injectable()
export class PopoverController extends OverlayBaseController<PopoverOptions, HTMLIonPopoverElement> {
constructor(
private angularDelegate: AngularDelegate,
private resolver: ComponentFactoryResolver,
private injector: Injector,
private injector: Injector
) {
super(popoverController);
}
@ -19,7 +18,7 @@ export class PopoverController extends OverlayBaseController<PopoverOptions, HTM
create(opts: PopoverOptions): Promise<HTMLIonPopoverElement> {
return super.create({
...opts,
delegate: this.angularDelegate.create(this.resolver, this.injector)
delegate: this.angularDelegate.create(this.resolver, this.injector),
});
}
}

View File

@ -1,8 +1,21 @@
import { join, Path } from '@angular-devkit/core';
import { apply, chain, mergeWith, move, Rule, SchematicContext, SchematicsException, template, Tree, url } from '@angular-devkit/schematics';
import { Path, join } from '@angular-devkit/core';
import {
Rule,
SchematicContext,
Tree,
apply,
chain,
mergeWith,
move,
SchematicsException,
template,
url,
} from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { getWorkspace } from '@schematics/angular/utility/workspace';
import { addModuleImportToRootModule } from './../utils/ast';
import { addArchitectBuilder, addAsset, addStyle, getDefaultAngularAppName, getWorkspace, WorkspaceProject, WorkspaceSchema } from './../utils/config';
import { addArchitectBuilder, addAsset, addStyle, getDefaultAngularAppName } from './../utils/config';
import { addPackageToPackageJson } from './../utils/package';
import { Schema as IonAddOptions } from './schema';
@ -15,24 +28,14 @@ function addIonicAngularToPackageJson(): Rule {
function addIonicAngularToolkitToPackageJson(): Rule {
return (host: Tree) => {
addPackageToPackageJson(
host,
'devDependencies',
'@ionic/angular-toolkit',
'latest'
);
addPackageToPackageJson(host, 'devDependencies', '@ionic/angular-toolkit', 'latest');
return host;
};
}
function addIonicAngularModuleToAppModule(projectSourceRoot: Path): Rule {
return (host: Tree) => {
addModuleImportToRootModule(
host,
projectSourceRoot,
'IonicModule.forRoot()',
'@ionic/angular'
);
addModuleImportToRootModule(host, projectSourceRoot, 'IonicModule.forRoot()', '@ionic/angular');
return host;
};
}
@ -50,10 +53,10 @@ function addIonicStyles(projectName: string, projectSourceRoot: Path): Rule {
'node_modules/@ionic/angular/css/text-alignment.css',
'node_modules/@ionic/angular/css/text-transformation.css',
'node_modules/@ionic/angular/css/flex-utils.css',
`${projectSourceRoot}/theme/variables.css`
]
`${projectSourceRoot}/theme/variables.css`,
];
ionicStyles.forEach(entry => {
ionicStyles.forEach((entry) => {
addStyle(host, projectName, entry);
});
return host;
@ -65,7 +68,7 @@ function addIonicons(projectName: string): Rule {
const ioniconsGlob = {
glob: '**/*.svg',
input: 'node_modules/ionicons/dist/ionicons/svg',
output: './svg'
output: './svg',
};
addAsset(host, projectName, 'build', ioniconsGlob);
addAsset(host, projectName, 'test', ioniconsGlob);
@ -79,25 +82,25 @@ function addIonicBuilder(projectName: string): Rule {
builder: '@ionic/angular-toolkit:cordova-serve',
options: {
cordovaBuildTarget: `${projectName}:ionic-cordova-build`,
devServerTarget: `${projectName}:serve`
devServerTarget: `${projectName}:serve`,
},
configurations: {
production: {
cordovaBuildTarget: `${projectName}:ionic-cordova-build:production`,
devServerTarget: `${projectName}:serve:production`
}
}
devServerTarget: `${projectName}:serve:production`,
},
},
});
addArchitectBuilder(host, projectName, 'ionic-cordova-build', {
builder: '@ionic/angular-toolkit:cordova-build',
options: {
browserTarget: `${projectName}:build`
browserTarget: `${projectName}:build`,
},
configurations: {
production: {
browserTarget: `${projectName}:build:production`
}
}
browserTarget: `${projectName}:build:production`,
},
},
});
return host;
};
@ -110,22 +113,18 @@ function installNodeDeps() {
}
export default function ngAdd(options: IonAddOptions): Rule {
return (host: Tree) => {
const workspace: WorkspaceSchema = getWorkspace(host);
return async (host: Tree) => {
const workspace = await getWorkspace(host);
if (!options.project) {
options.project = getDefaultAngularAppName(workspace);
}
const project: WorkspaceProject = workspace.projects[options.project];
if (project.projectType !== 'application') {
throw new SchematicsException(
`Ionic Add requires a project type of "application".`
);
const project = workspace.projects.get(options.project);
if (!project || project.extensions.projectType !== 'application') {
throw new SchematicsException(`Ionic Add requires a project type of "application".`);
}
const sourcePath: Path = join(project.sourceRoot as Path);
const rootTemplateSource = apply(url('./files/root'), [
template({ ...options }),
move(sourcePath)
]);
const rootTemplateSource = apply(url('./files/root'), [template({ ...options }), move(sourcePath)]);
return chain([
// @ionic/angular
addIonicAngularToPackageJson(),
@ -136,7 +135,7 @@ export default function ngAdd(options: IonAddOptions): Rule {
addIonicons(options.project),
mergeWith(rootTemplateSource),
// install freshly added dependencies
installNodeDeps()
installNodeDeps(),
]);
};
}

View File

@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/schema",
"id": "ionicNgAdd",
"$id": "ionicNgAdd",
"title": "Ionic Add options",
"type": "object",
"properties": {

View File

@ -1,6 +1,7 @@
import { SchematicsException, Tree } from '@angular-devkit/schematics';
import { normalize } from '@angular-devkit/core';
import { Tree, SchematicsException } from '@angular-devkit/schematics';
import * as ts from 'typescript';
import { addImportToModule } from './devkit-utils/ast-utils';
import { InsertChange } from './devkit-utils/change';
@ -13,12 +14,7 @@ export function getSourceFile(host: Tree, path: string): ts.SourceFile {
throw new SchematicsException(`Could not find file for path: ${path}`);
}
const content = buffer.toString();
const source = ts.createSourceFile(
path,
content,
ts.ScriptTarget.Latest,
true
);
const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
return source;
}
@ -30,13 +26,8 @@ export function addModuleImportToRootModule(
projectSourceRoot: string,
moduleName: string,
importSrc: string
) {
addModuleImportToModule(
host,
normalize(`${projectSourceRoot}/app/app.module.ts`),
moduleName,
importSrc
);
): void {
addModuleImportToModule(host, normalize(`${projectSourceRoot}/app/app.module.ts`), moduleName, importSrc);
}
/**
@ -46,17 +37,12 @@ export function addModuleImportToRootModule(
* @param moduleName name of module to import
* @param src src location to import
*/
export function addModuleImportToModule(
host: Tree,
modulePath: string,
moduleName: string,
src: string
) {
export function addModuleImportToModule(host: Tree, modulePath: string, moduleName: string, src: string): void {
const moduleSource = getSourceFile(host, modulePath);
const changes = addImportToModule(moduleSource, modulePath, moduleName, src);
const recorder = host.beginUpdate(modulePath);
changes.forEach(change => {
changes.forEach((change) => {
if (change instanceof InsertChange) {
recorder.insertLeft(change.pos, change.toAdd);
}

View File

@ -1,18 +1,19 @@
import { SchematicsException, Tree } from '@angular-devkit/schematics';
import { experimental, parseJson, JsonParseMode } from '@angular-devkit/core';
import { WorkspaceDefinition } from '@angular-devkit/core/src/workspace';
import { Tree, SchematicsException } from '@angular-devkit/schematics';
import { parse } from 'jsonc-parser';
const CONFIG_PATH = 'angular.json';
export function readConfig(host: Tree) {
const sourceText = host.read(CONFIG_PATH)!.toString('utf-8');
export function readConfig(host: Tree): any {
const sourceText = host.read(CONFIG_PATH)?.toString('utf-8');
return JSON.parse(sourceText);
}
export function writeConfig(host: Tree, config: JSON) {
export function writeConfig(host: Tree, config: JSON): void {
host.overwrite(CONFIG_PATH, JSON.stringify(config, null, 2));
}
function isAngularBrowserProject(projectConfig: any) {
function isAngularBrowserProject(projectConfig: any): boolean {
if (projectConfig.projectType === 'application') {
const buildConfig = projectConfig.architect.build;
return buildConfig.builder === '@angular-devkit/build-angular:browser';
@ -36,6 +37,7 @@ export function getDefaultAngularAppName(config: any): string {
}
export function getAngularAppConfig(config: any, projectName: string): any | never {
// eslint-disable-next-line no-prototype-builtins
if (!config.projects.hasOwnProperty(projectName)) {
throw new SchematicsException(`Could not find project: ${projectName}`);
}
@ -53,40 +55,50 @@ export function getAngularAppConfig(config: any, projectName: string): any | nev
}
}
export function addStyle(host: Tree, projectName: string, stylePath: string) {
export function addStyle(host: Tree, projectName: string, stylePath: string): void {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config, projectName);
appConfig.architect.build.options.styles.push({
input: stylePath
input: stylePath,
});
writeConfig(host, config);
}
export function addAsset(host: Tree, projectName: string, architect: string, asset: string | {glob: string; input: string; output: string}) {
export function addAsset(
host: Tree,
projectName: string,
architect: string,
asset: string | { glob: string; input: string; output: string }
): void {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config, projectName);
appConfig.architect[architect].options.assets.push(asset);
const target = appConfig.architect[architect];
if (target) {
target.options.assets.push(asset);
writeConfig(host, config);
}
}
export function addArchitectBuilder(host: Tree, projectName: string, builderName: string, builderOpts: any): void | never {
export function addArchitectBuilder(
host: Tree,
projectName: string,
builderName: string,
builderOpts: any
): void | never {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config, projectName);
appConfig.architect[builderName] = builderOpts;
writeConfig(host, config);
}
export type WorkspaceSchema = experimental.workspace.WorkspaceSchema;
export type WorkspaceProject = experimental.workspace.WorkspaceProject;
export function getWorkspacePath(host: Tree): string {
const possibleFiles = ['/angular.json', '/.angular.json'];
const path = possibleFiles.filter(path => host.exists(path))[0];
const path = possibleFiles.filter((path) => host.exists(path))[0];
return path;
}
export function getWorkspace(host: Tree): WorkspaceSchema {
export function getWorkspace(host: Tree): WorkspaceDefinition {
const path = getWorkspacePath(host);
const configBuffer = host.read(path);
if (configBuffer === null) {
@ -94,5 +106,5 @@ export function getWorkspace(host: Tree): WorkspaceSchema {
}
const content = configBuffer.toString();
return (parseJson(content, JsonParseMode.Loose) as {}) as WorkspaceSchema;
return parse(content) as WorkspaceDefinition;
}

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import { Change, InsertChange, NoopChange } from './change';
import { Change, InsertChange, NoopChange } from './change';
/**
* Add Import `import { symbolName } from fileName` if the import doesn't exit
@ -18,26 +18,32 @@ import { Change, InsertChange, NoopChange } from './change';
* @param isDefault (if true, import follows style for importing default exports)
* @return Change
*/
export function insertImport(source: ts.SourceFile, fileToEdit: string, symbolName: string,
fileName: string, isDefault = false): Change {
export function insertImport(
source: ts.SourceFile,
fileToEdit: string,
symbolName: string,
fileName: string,
isDefault = false
): Change {
const rootNode = source;
const allImports = findNodes(rootNode, ts.SyntaxKind.ImportDeclaration);
// get nodes that map to import statements from the file fileName
const relevantImports = allImports.filter(node => {
const relevantImports = allImports.filter((node) => {
// StringLiteral of the ImportDeclaration is the import file (fileName in this case).
const importFiles = node.getChildren()
.filter(child => child.kind === ts.SyntaxKind.StringLiteral)
.map(n => (n as ts.StringLiteral).text);
const importFiles = node
.getChildren()
.filter((child) => child.kind === ts.SyntaxKind.StringLiteral)
.map((n) => (n as ts.StringLiteral).text);
return importFiles.filter(file => file === fileName).length === 1;
return importFiles.filter((file) => file === fileName).length === 1;
});
if (relevantImports.length > 0) {
let importsAsterisk = false;
// imports from import file
const imports: ts.Node[] = [];
relevantImports.forEach(n => {
relevantImports.forEach((n) => {
Array.prototype.push.apply(imports, findNodes(n, ts.SyntaxKind.Identifier));
if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) {
importsAsterisk = true;
@ -49,7 +55,7 @@ export function insertImport(source: ts.SourceFile, fileToEdit: string, symbolNa
return new NoopChange();
}
const importTextNodes = imports.filter(n => (n as ts.Identifier).text === symbolName);
const importTextNodes = imports.filter((n) => (n as ts.Identifier).text === symbolName);
// insert import if it's not there
if (importTextNodes.length === 0) {
@ -64,8 +70,9 @@ export function insertImport(source: ts.SourceFile, fileToEdit: string, symbolNa
}
// no such import declaration exists
const useStrict = findNodes(rootNode, ts.SyntaxKind.StringLiteral)
.filter((n: ts.StringLiteral) => n.text === 'use strict');
const useStrict = findNodes(rootNode, ts.SyntaxKind.StringLiteral).filter(
(n: ts.StringLiteral) => n.text === 'use strict'
);
let fallbackPos = 0;
if (useStrict.length > 0) {
fallbackPos = useStrict[0].end;
@ -75,19 +82,12 @@ export function insertImport(source: ts.SourceFile, fileToEdit: string, symbolNa
// if there are no imports or 'use strict' statement, insert import at beginning of file
const insertAtBeginning = allImports.length === 0 && useStrict.length === 0;
const separator = insertAtBeginning ? '' : ';\n';
const toInsert = `${separator}import ${open}${symbolName}${close}` +
` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
const toInsert =
`${separator}import ${open}${symbolName}${close}` + ` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
return insertAfterLastOccurrence(
allImports,
toInsert,
fileToEdit,
fallbackPos,
ts.SyntaxKind.StringLiteral,
);
return insertAfterLastOccurrence(allImports, toInsert, fileToEdit, fallbackPos, ts.SyntaxKind.StringLiteral);
}
/**
* Find all nodes from the AST in the subtree of node of SyntaxKind kind.
* @param node
@ -107,7 +107,7 @@ export function findNodes(node: ts.Node, kind: ts.SyntaxKind, max = Infinity): t
}
if (max > 0) {
for (const child of node.getChildren()) {
findNodes(child, kind, max).forEach(node => {
findNodes(child, kind, max).forEach((node) => {
if (max > 0) {
arr.push(node);
}
@ -123,7 +123,6 @@ export function findNodes(node: ts.Node, kind: ts.SyntaxKind, max = Infinity): t
return arr;
}
/**
* Get all the nodes from a source.
* @param sourceFile The source file object.
@ -154,14 +153,13 @@ export function findNode(node: ts.Node, kind: ts.SyntaxKind, text: string): ts.N
}
let foundNode: ts.Node | null = null;
ts.forEachChild(node, childNode => {
ts.forEachChild(node, (childNode) => {
foundNode = foundNode || findNode(childNode, kind, text);
});
return foundNode;
}
/**
* Helper for sorting nodes.
* @return function to sort nodes in increasing order of position in sourceFile
@ -170,7 +168,6 @@ function nodesByPosition(first: ts.Node, second: ts.Node): number {
return first.getStart() - second.getStart();
}
/**
* Insert `toInsert` after the last occurence of `ts.SyntaxKind[nodes[i].kind]`
* or after the last of occurence of `syntaxKind` if the last occurence is a sub child
@ -184,11 +181,13 @@ function nodesByPosition(first: ts.Node, second: ts.Node): number {
* @return Change instance
* @throw Error if toInsert is first occurence but fall back is not set
*/
export function insertAfterLastOccurrence(nodes: ts.Node[],
export function insertAfterLastOccurrence(
nodes: ts.Node[],
toInsert: string,
file: string,
fallbackPos: number,
syntaxKind?: ts.SyntaxKind): Change {
syntaxKind?: ts.SyntaxKind
): Change {
// sort() has a side effect, so make a copy so that we won't overwrite the parent's object.
let lastItem = [...nodes].sort(nodesByPosition).pop();
if (!lastItem) {
@ -205,7 +204,6 @@ export function insertAfterLastOccurrence(nodes: ts.Node[],
return new InsertChange(file, lastItemPosition, toInsert);
}
export function getContentOfKeyLiteral(_source: ts.SourceFile, node: ts.Node): string | null {
if (node.kind == ts.SyntaxKind.Identifier) {
return (node as ts.Identifier).text;
@ -216,9 +214,11 @@ export function getContentOfKeyLiteral(_source: ts.SourceFile, node: ts.Node): s
}
}
function _angularImportsFromNode(node: ts.ImportDeclaration,
_sourceFile: ts.SourceFile): {[name: string]: string} {
function _angularImportsFromNode(
node: ts.ImportDeclaration,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_sourceFile: ts.SourceFile
): { [name: string]: string } {
const ms = node.moduleSpecifier;
let modulePath: string;
switch (ms.kind) {
@ -249,8 +249,8 @@ function _angularImportsFromNode(node: ts.ImportDeclaration,
const namedImports = nb as ts.NamedImports;
return namedImports.elements
.map((is: ts.ImportSpecifier) => is.propertyName ? is.propertyName.text : is.name.text)
.reduce((acc: {[name: string]: string}, curr: string) => {
.map((is: ts.ImportSpecifier) => (is.propertyName ? is.propertyName.text : is.name.text))
.reduce((acc: { [name: string]: string }, curr: string) => {
acc[curr] = modulePath;
return acc;
@ -265,13 +265,10 @@ function _angularImportsFromNode(node: ts.ImportDeclaration,
}
}
export function getDecoratorMetadata(source: ts.SourceFile, identifier: string,
module: string): ts.Node[] {
const angularImports: {[name: string]: string}
= findNodes(source, ts.SyntaxKind.ImportDeclaration)
export function getDecoratorMetadata(source: ts.SourceFile, identifier: string, module: string): ts.Node[] {
const angularImports: { [name: string]: string } = findNodes(source, ts.SyntaxKind.ImportDeclaration)
.map((node: ts.ImportDeclaration) => _angularImportsFromNode(node, source))
.reduce((acc: {[name: string]: string}, current: {[name: string]: string}) => {
.reduce((acc: { [name: string]: string }, current: { [name: string]: string }) => {
for (const key of Object.keys(current)) {
acc[key] = current[key];
}
@ -280,17 +277,17 @@ export function getDecoratorMetadata(source: ts.SourceFile, identifier: string,
}, {});
return getSourceNodes(source)
.filter(node => {
return node.kind == ts.SyntaxKind.Decorator
&& (node as ts.Decorator).expression.kind == ts.SyntaxKind.CallExpression;
.filter((node) => {
return (
node.kind == ts.SyntaxKind.Decorator && (node as ts.Decorator).expression.kind == ts.SyntaxKind.CallExpression
);
})
.map(node => (node as ts.Decorator).expression as ts.CallExpression)
.filter(expr => {
.map((node) => (node as ts.Decorator).expression as ts.CallExpression)
.filter((expr) => {
if (expr.expression.kind == ts.SyntaxKind.Identifier) {
const id = expr.expression as ts.Identifier;
return id.getFullText(source) == identifier
&& angularImports[id.getFullText(source)] === module;
return id.getFullText(source) == identifier && angularImports[id.getFullText(source)] === module;
} else if (expr.expression.kind == ts.SyntaxKind.PropertyAccessExpression) {
// This covers foo.NgModule when importing * as foo.
const paExpr = expr.expression as ts.PropertyAccessExpression;
@ -302,17 +299,16 @@ export function getDecoratorMetadata(source: ts.SourceFile, identifier: string,
const id = paExpr.name.text;
const moduleId = (paExpr.expression as ts.Identifier).getText(source);
return id === identifier && (angularImports[moduleId + '.'] === module);
return id === identifier && angularImports[moduleId + '.'] === module;
}
return false;
})
.filter(expr => expr.arguments[0]
&& expr.arguments[0].kind == ts.SyntaxKind.ObjectLiteralExpression)
.map(expr => expr.arguments[0] as ts.ObjectLiteralExpression);
.filter((expr) => expr.arguments[0] && expr.arguments[0].kind == ts.SyntaxKind.ObjectLiteralExpression)
.map((expr) => expr.arguments[0] as ts.ObjectLiteralExpression);
}
function findClassDeclarationParent(node: ts.Node): ts.ClassDeclaration|undefined {
function findClassDeclarationParent(node: ts.Node): ts.ClassDeclaration | undefined {
if (ts.isClassDeclaration(node)) {
return node;
}
@ -326,7 +322,7 @@ function findClassDeclarationParent(node: ts.Node): ts.ClassDeclaration|undefine
* @param source source file containing one or more @NgModule
* @returns the name of the first @NgModule, or `undefined` if none is found
*/
export function getFirstNgModuleName(source: ts.SourceFile): string|undefined {
export function getFirstNgModuleName(source: ts.SourceFile): string | undefined {
// First, find the @NgModule decorators.
const ngModulesMetadata = getDecoratorMetadata(source, 'NgModule', '@angular/core');
if (ngModulesMetadata.length === 0) {
@ -349,7 +345,7 @@ export function addSymbolToNgModuleMetadata(
ngModulePath: string,
metadataField: string,
symbolName: string,
importPath: string | null = null,
importPath: string | null = null
): Change[] {
const nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core');
let node: any = nodes[0]; // tslint:disable-line:no-any
@ -360,9 +356,8 @@ export function addSymbolToNgModuleMetadata(
}
// Get all the children property assignment of object literals.
const matchingProperties: ts.ObjectLiteralElement[] =
(node as ts.ObjectLiteralExpression).properties
.filter(prop => prop.kind == ts.SyntaxKind.PropertyAssignment)
const matchingProperties: ts.ObjectLiteralElement[] = (node as ts.ObjectLiteralExpression).properties
.filter((prop) => prop.kind == ts.SyntaxKind.PropertyAssignment)
// Filter out every fields that's not "metadataField". Also handles string literals
// (but not expressions).
.filter((prop: ts.PropertyAssignment) => {
@ -432,8 +427,9 @@ export function addSymbolToNgModuleMetadata(
}
if (Array.isArray(node)) {
const nodeArray = node as {} as Array<ts.Node>;
const symbolsArray = nodeArray.map(node => node.getText());
// eslint-disable-next-line @typescript-eslint/ban-types
const nodeArray = node as {} as ts.Node[];
const symbolsArray = nodeArray.map((node) => node.getText());
if (symbolsArray.includes(symbolName)) {
return [];
}
@ -488,81 +484,93 @@ export function addSymbolToNgModuleMetadata(
* Custom function to insert a declaration (component, pipe, directive)
* into NgModule declarations. It also imports the component.
*/
export function addDeclarationToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
return addSymbolToNgModuleMetadata(
source, modulePath, 'declarations', classifiedName, importPath);
export function addDeclarationToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'declarations', classifiedName, importPath);
}
/**
* Custom function to insert an NgModule into NgModule imports. It also imports the module.
*/
export function addImportToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
export function addImportToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'imports', classifiedName, importPath);
}
/**
* Custom function to insert a provider into NgModule. It also imports it.
*/
export function addProviderToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
export function addProviderToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'providers', classifiedName, importPath);
}
/**
* Custom function to insert an export into NgModule. It also imports it.
*/
export function addExportToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
export function addExportToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'exports', classifiedName, importPath);
}
/**
* Custom function to insert an export into NgModule. It also imports it.
*/
export function addBootstrapToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
export function addBootstrapToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'bootstrap', classifiedName, importPath);
}
/**
* Custom function to insert an entryComponent into NgModule. It also imports it.
*/
export function addEntryComponentToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
return addSymbolToNgModuleMetadata(
source, modulePath,
'entryComponents', classifiedName, importPath,
);
export function addEntryComponentToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'entryComponents', classifiedName, importPath);
}
/**
* Determine if an import already exists.
*/
export function isImported(source: ts.SourceFile,
classifiedName: string,
importPath: string): boolean {
export function isImported(source: ts.SourceFile, classifiedName: string, importPath: string): boolean {
const allNodes = getSourceNodes(source);
const matchingNodes = allNodes
.filter(node => node.kind === ts.SyntaxKind.ImportDeclaration)
.filter((node) => node.kind === ts.SyntaxKind.ImportDeclaration)
.filter((imp: ts.ImportDeclaration) => imp.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral)
.filter((imp: ts.ImportDeclaration) => {
return (<ts.StringLiteral> imp.moduleSpecifier).text === importPath;
return (imp.moduleSpecifier as ts.StringLiteral).text === importPath;
})
.filter((imp: ts.ImportDeclaration) => {
if (!imp.importClause) {
return false;
}
const nodes = findNodes(imp.importClause, ts.SyntaxKind.ImportSpecifier)
.filter(n => n.getText() === classifiedName);
const nodes = findNodes(imp.importClause, ts.SyntaxKind.ImportSpecifier).filter(
(n) => n.getText() === classifiedName
);
return nodes.length > 0;
});

View File

@ -10,7 +10,6 @@ export interface Host {
read(path: string): Promise<string>;
}
export interface Change {
apply(host: Host): Promise<void>;
@ -26,7 +25,6 @@ export interface Change {
readonly description: string;
}
/**
* An operation that does nothing.
*/
@ -34,15 +32,15 @@ export class NoopChange implements Change {
description = 'No operation.';
order = Infinity;
path = null;
apply() { return Promise.resolve(); }
apply(): Promise<void> {
return Promise.resolve();
}
}
/**
* Will add text to the source code.
*/
export class InsertChange implements Change {
order: number;
description: string;
@ -57,8 +55,8 @@ export class InsertChange implements Change {
/**
* This method does not insert spaces if there is none in the original string.
*/
apply(host: Host) {
return host.read(this.path).then(content => {
apply(host: Host): Promise<void> {
return host.read(this.path).then((content) => {
const prefix = content.substring(0, this.pos);
const suffix = content.substring(this.pos);
@ -71,7 +69,6 @@ export class InsertChange implements Change {
* Will remove text from the source code.
*/
export class RemoveChange implements Change {
order: number;
description: string;
@ -84,7 +81,7 @@ export class RemoveChange implements Change {
}
apply(host: Host): Promise<void> {
return host.read(this.path).then(content => {
return host.read(this.path).then((content) => {
const prefix = content.substring(0, this.pos);
const suffix = content.substring(this.pos + this.toRemove.length);
@ -101,8 +98,7 @@ export class ReplaceChange implements Change {
order: number;
description: string;
constructor(public path: string, private pos: number, private oldText: string,
private newText: string) {
constructor(public path: string, private pos: number, private oldText: string, private newText: string) {
if (pos < 0) {
throw new Error('Negative positions are invalid');
}
@ -111,7 +107,7 @@ export class ReplaceChange implements Change {
}
apply(host: Host): Promise<void> {
return host.read(this.path).then(content => {
return host.read(this.path).then((content) => {
const prefix = content.substring(0, this.pos);
const suffix = content.substring(this.pos + this.oldText.length);
const text = content.substring(this.pos, this.pos + this.oldText.length);

View File

@ -3,5 +3,3 @@
These are utility files copied over from `@angular-devkit`.
They are not exported so they need to be manually copied over.
Please do not edit directly.

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import { findNodes, insertAfterLastOccurrence } from './ast-utils';
import { Change, NoopChange } from './change';
@ -30,25 +31,22 @@ export function insertImport(
const allImports = findNodes(rootNode, ts.SyntaxKind.ImportDeclaration);
// get nodes that map to import statements from the file fileName
const relevantImports = allImports.filter(node => {
const relevantImports = allImports.filter((node) => {
// StringLiteral of the ImportDeclaration is the import file (fileName in this case).
const importFiles = node
.getChildren()
.filter(child => child.kind === ts.SyntaxKind.StringLiteral)
.map(n => (n as ts.StringLiteral).text);
.filter((child) => child.kind === ts.SyntaxKind.StringLiteral)
.map((n) => (n as ts.StringLiteral).text);
return importFiles.filter(file => file === fileName).length === 1;
return importFiles.filter((file) => file === fileName).length === 1;
});
if (relevantImports.length > 0) {
let importsAsterisk = false;
// imports from import file
const imports: ts.Node[] = [];
relevantImports.forEach(n => {
Array.prototype.push.apply(
imports,
findNodes(n, ts.SyntaxKind.Identifier)
);
relevantImports.forEach((n) => {
Array.prototype.push.apply(imports, findNodes(n, ts.SyntaxKind.Identifier));
if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) {
importsAsterisk = true;
}
@ -59,25 +57,15 @@ export function insertImport(
return new NoopChange();
}
const importTextNodes = imports.filter(
n => (n as ts.Identifier).text === symbolName
);
const importTextNodes = imports.filter((n) => (n as ts.Identifier).text === symbolName);
// insert import if it's not there
if (importTextNodes.length === 0) {
const fallbackPos =
findNodes(
relevantImports[0],
ts.SyntaxKind.CloseBraceToken
)[0].getStart() ||
findNodes(relevantImports[0], ts.SyntaxKind.CloseBraceToken)[0].getStart() ||
findNodes(relevantImports[0], ts.SyntaxKind.FromKeyword)[0].getStart();
return insertAfterLastOccurrence(
imports,
`, ${symbolName}`,
fileToEdit,
fallbackPos
);
return insertAfterLastOccurrence(imports, `, ${symbolName}`, fileToEdit, fallbackPos);
}
return new NoopChange();
@ -97,14 +85,7 @@ export function insertImport(
const insertAtBeginning = allImports.length === 0 && useStrict.length === 0;
const separator = insertAtBeginning ? '' : ';\n';
const toInsert =
`${separator}import ${open}${symbolName}${close}` +
` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
`${separator}import ${open}${symbolName}${close}` + ` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
return insertAfterLastOccurrence(
allImports,
toInsert,
fileToEdit,
fallbackPos,
ts.SyntaxKind.StringLiteral
);
return insertAfterLastOccurrence(allImports, toInsert, fileToEdit, fallbackPos, ts.SyntaxKind.StringLiteral);
}

View File

@ -1,11 +1,11 @@
import {Tree} from '@angular-devkit/schematics';
import { Tree } from '@angular-devkit/schematics';
/**
* Adds a package to the package.json
*/
export function addPackageToPackageJson(host: Tree, type: string, pkg: string, version: string) {
export function addPackageToPackageJson(host: Tree, type: string, pkg: string, version: string): Tree {
if (host.exists('package.json')) {
const sourceText = host.read('package.json')!.toString('utf-8');
const sourceText = host.read('package.json')?.toString('utf-8');
const json = JSON.parse(sourceText);
if (!json[type]) {
json[type] = {};

View File

@ -1,4 +1,3 @@
export interface IonicGlobal {
config?: any;
asyncQueue?: boolean;

View File

@ -1,27 +1,31 @@
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';
export class IonicRouteStrategy implements RouteReuseStrategy {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
shouldDetach(_route: ActivatedRouteSnapshot): boolean {
return false;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
shouldAttach(_route: ActivatedRouteSnapshot): boolean {
return false;
}
store(_route: ActivatedRouteSnapshot, _detachedTree: DetachedRouteHandle): void {
store(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_route: ActivatedRouteSnapshot,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_detachedTree: DetachedRouteHandle
): void {
return;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
retrieve(_route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
return null;
}
shouldReuseRoute(
future: ActivatedRouteSnapshot,
curr: ActivatedRouteSnapshot
): boolean {
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
if (future.routeConfig !== curr.routeConfig) {
return false;
}

View File

@ -1,4 +1,3 @@
interface ControllerShape<Opts, HTMLElm> {
create(options: Opts): Promise<HTMLElm>;
dismiss(data?: any, role?: string, id?: string): Promise<boolean>;
@ -11,7 +10,7 @@ export class OverlayBaseController<Opts, Overlay> implements ControllerShape<Opt
/**
* Creates a new overlay
*/
create(opts?: Opts) {
create(opts?: Opts): Promise<Overlay> {
// TODO: next major release opts is not optional
return this.ctrl.create((opts || {}) as any);
}
@ -19,14 +18,14 @@ export class OverlayBaseController<Opts, Overlay> implements ControllerShape<Opt
/**
* When `id` is not provided, it dismisses the top overlay.
*/
dismiss(data?: any, role?: string, id?: string) {
dismiss(data?: any, role?: string, id?: string): Promise<boolean> {
return this.ctrl.dismiss(data, role, id);
}
/**
* Returns the top overlay.
*/
getTop() {
getTop(): Promise<Overlay | undefined> {
return this.ctrl.getTop();
}
}

View File

@ -1,8 +1,7 @@
declare const __zone_symbol__requestAnimationFrame: any;
declare const requestAnimationFrame: any;
export const raf = (h: any) => {
export const raf = (h: any): any => {
if (typeof __zone_symbol__requestAnimationFrame === 'function') {
return __zone_symbol__requestAnimationFrame(h);
}

View File

@ -0,0 +1,51 @@
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"tsconfig.json",
"e2e/tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/component-selector": [
"error",
{
"prefix": "app",
"style": "kebab-case",
"type": "element"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"prefix": "app",
"style": "camelCase",
"type": "attribute"
}
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}

View File

@ -33,7 +33,9 @@
"output": "./svg"
}
],
"styles": ["src/styles.css"],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
@ -47,7 +49,6 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"progress": false,
@ -86,10 +87,12 @@
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"builder": "@angular-eslint/builder:lint",
"options": {
"tsConfig": ["tsconfig.app.json", "tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
},
"server": {
@ -137,5 +140,8 @@
}
}
},
"defaultProject": "test-app"
"defaultProject": "test-app",
"cli": {
"defaultCollection": "@angular-eslint/schematics"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
"start": "npm run sync && ng serve",
"sync:build": "sh scripts/build-ionic.sh",
"sync": "sh scripts/sync.sh",
"build": "npm run sync && ng build --prod --no-progress",
"build": "npm run sync && ng build --configuration production --no-progress",
"lint": "ng lint",
"postinstall": "npm run sync && ngcc",
"serve:ssr": "node dist/test-app/server/main.js",
@ -20,40 +20,47 @@
"test.watch": "concurrently \"npm run start\" \"wait-on http-get://localhost:4200 && npm run cy.open\" --kill-others --success first"
},
"dependencies": {
"@angular/animations": "^11.2.11",
"@angular/common": "^11.2.11",
"@angular/compiler": "^11.2.11",
"@angular/core": "^11.2.11",
"@angular/forms": "^11.2.11",
"@angular/platform-browser": "^11.2.11",
"@angular/platform-browser-dynamic": "^11.2.11",
"@angular/platform-server": "^11.2.11",
"@angular/router": "^11.2.11",
"@angular/animations": "^12.2.8",
"@angular/common": "^12.2.8",
"@angular/compiler": "^12.2.8",
"@angular/core": "^12.2.8",
"@angular/forms": "^12.2.8",
"@angular/platform-browser": "^12.2.8",
"@angular/platform-browser-dynamic": "^12.2.8",
"@angular/platform-server": "^12.2.8",
"@angular/router": "^12.2.8",
"@ionic/angular": "^5.3.1",
"@ionic/angular-server": "^5.3.1",
"@nguniversal/express-engine": "^11.2.1",
"@nguniversal/express-engine": "^12.1.1",
"angular-in-memory-web-api": "^0.11.0",
"core-js": "^2.6.11",
"express": "^4.15.2",
"rxjs": "^6.5.5",
"tslib": "^2.0.0",
"zone.js": "^0.10.3"
"typescript-eslint-language-service": "^4.1.5",
"zone.js": "^0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.1102.10",
"@angular/cli": "^11.2.10",
"@angular/compiler-cli": "^11.2.11",
"@angular/language-service": "^11.2.11",
"@nguniversal/builders": "^11.2.1",
"@angular-devkit/build-angular": "^12.2.8",
"@angular-eslint/builder": "12.5.0",
"@angular-eslint/eslint-plugin": "12.5.0",
"@angular-eslint/eslint-plugin-template": "12.5.0",
"@angular-eslint/schematics": "12.5.0",
"@angular-eslint/template-parser": "12.5.0",
"@angular/cli": "^12.2.8",
"@angular/compiler-cli": "^12.2.8",
"@angular/language-service": "^12.2.8",
"@nguniversal/builders": "^12.1.1",
"@types/express": "^4.17.7",
"@types/node": "^12.12.54",
"codelyzer": "^6.0.1",
"@typescript-eslint/eslint-plugin": "4.28.2",
"@typescript-eslint/parser": "4.28.2",
"concurrently": "^6.0.0",
"cypress": "^6.7.1",
"eslint": "^7.26.0",
"ts-loader": "^6.2.2",
"ts-node": "^8.3.0",
"tslint": "~6.1.0",
"typescript": "^4.0.7",
"typescript": "^4.3.5",
"wait-on": "^5.2.1",
"webpack-cli": "^3.3.12"
}

View File

@ -9,6 +9,7 @@ export class RouterLinkPage2Component implements OnInit {
constructor() { }
ngOnInit() {
console.log('ngOnInit')
}
}

View File

@ -9,6 +9,7 @@ export class RouterLinkPage3Component implements OnInit {
constructor() { }
ngOnInit() {
console.log('ngOnInit')
}
}

View File

@ -1,11 +1,11 @@
import { ActivatedRoute } from '@angular/router';
import { Component, Input } from '@angular/core';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-tabs-tab1-nested',
templateUrl: './tabs-tab1-nested.component.html',
})
export class TabsTab1NestedComponent {
export class TabsTab1NestedComponent implements OnInit {
id = '';
constructor(
private route: ActivatedRoute,

View File

@ -1,10 +1,10 @@
import { Component, NgZone } from '@angular/core';
import { Component, NgZone, OnInit } from '@angular/core';
@Component({
selector: 'app-tabs-tab2',
templateUrl: './tabs-tab2.component.html',
})
export class TabsTab2Component {
export class TabsTab2Component implements OnInit {
title = 'ERROR';
segment = 'two';
changed = 'false';

View File

@ -16,6 +16,11 @@
"lib": [
"es2018",
"dom"
],
"plugins": [
{
"name": "typescript-eslint-language-service"
}
]
}
}

View File

@ -1,133 +0,0 @@
{
"extends": "tslint:recommended",
"rulesDirectory": ["codelyzer"],
"rules": {
"align": {
"options": [
"parameters",
"statements"
]
},
"array-type": false,
"arrow-parens": false,
"arrow-return-shorthand": true,
"deprecation": {
"severity": "warn"
},
"import-blacklist": [
true,
"rxjs/Rx"
],
"curly": true,
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"eofline": true,
"member-access": false,
"import-spacing": true,
"indent": {
"options": [
"spaces"
]
},
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"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"],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [true, "single"],
"trailing-comma": false,
"no-output-on-prefix": true,
"no-inputs-metadata-property": true,
"no-inputs-metadata-property": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-output-rename": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"one-variable-per-declaration": false,
"component-class-suffix": [
true,
"Page",
"Component"
],
"semicolon": {
"options": [
"always"
]
},
"space-before-function-paren": {
"options": {
"anonymous": "never",
"asyncArrow": "always",
"constructor": "never",
"method": "never",
"named": "never"
}
},
"directive-class-suffix": true,
"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"
}
]
},
"directive-selector": [true, "attribute", "app", "camelCase"],
"component-selector": [
true,
"element",
"app",
"page",
"kebab-case"
],
"variable-name": {
"options": [
"ban-keywords",
"check-format",
"allow-pascal-case"
]
},
"whitespace": {
"options": [
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type",
"check-typecast"
]
}
}
}

View File

@ -2,12 +2,10 @@
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"strictMetadataEmit": true,
"flatModuleOutFile": "core.js",
"flatModuleId": "@ionic/angular",
"skipTemplateCodegen": true,
"fullTemplateTypeCheck": false,
"enableResourceInlining": true,
"enableIvy": false
"compilationMode": "partial"
},
"compilerOptions": {
"alwaysStrict": true,
@ -36,7 +34,12 @@
"paths": {
"@ionic/core/hydrate": ["../core/hydrate"],
"@ionic/core": ["../core"]
},
"plugins": [
{
"name": "typescript-eslint-language-service"
}
]
},
"exclude": ["node_modules", "src/schematics"],
"files": ["src/index.ts"]

View File

@ -1,8 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"target": "es5",
"declarationDir": "build/es5",
"outDir": "build/es5"
}
}

View File

@ -1,10 +1,7 @@
{
"extends": ["tslint-ionic-rules/strict"],
"linterOptions": {
"exclude": [
"**/*.spec.ts",
"**/*.spec.tsx"
]
"exclude": ["**/*.spec.ts", "**/*.spec.tsx"]
},
"rules": {
"no-conditional-assignment": false,

View File

@ -15037,7 +15037,8 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@stencil/react-output-target/-/react-output-target-0.1.0.tgz",
"integrity": "sha512-NWeN2S43dwWDIousfojzGXIMkJJhfcdS1JxpwgE7IOqy4tZ+nqlDLPhM6tXvZ3eq4rJm8bkF+3/WbPJNR9xR7Q==",
"dev": true
"dev": true,
"requires": {}
},
"@stencil/sass": {
"version": "1.3.2",
@ -15049,7 +15050,8 @@
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.5.1.tgz",
"integrity": "sha512-E9HeuUf4DjHO8VFd+faR6T+/JwLtU/2kIA000YTTv0ARPUXuxr/3+U3YMBRPCVFPC5n2jsFxU3E9rTmVH1MGyg==",
"dev": true
"dev": true,
"requires": {}
},
"@stylelint/postcss-css-in-js": {
"version": "0.37.2",

View File

@ -308,6 +308,7 @@ export interface Gesture {
}
export interface GestureConfig {
[index: string]: any;
el: Node;
disableScroll?: boolean;

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@
"homepage": "https://ionicframework.com/",
"scripts": {
"test": "echo 'angular no tests yet'",
"build": "ng-packagr -p package.json",
"build": "ng-packagr -p package.json -c tsconfig.json",
"build.prod": "npm run clean && npm run build",
"clean": "rm -rf ./dist",
"lint": "npm run lint.ts",
@ -35,25 +35,23 @@
}
},
"peerDependencies": {
"@angular/core": ">=8.2.7",
"@angular/platform-server": ">=8.2.7",
"@angular/core": ">=12.0.0",
"@angular/platform-server": ">=12.0.0",
"@ionic/angular": "*",
"rxjs": ">=6.2.0",
"zone.js": ">=0.8.26"
"rxjs": ">=6.6.0",
"zone.js": ">=0.11.0"
},
"devDependencies": {
"@angular/animations": "8.2.13",
"@angular/common": "8.2.13",
"@angular/compiler": "8.2.13",
"@angular/compiler-cli": "8.2.13",
"@angular/core": "8.2.13",
"@angular/platform-browser": "8.2.13",
"@angular/platform-server": "8.2.13",
"@angular/animations": "^12.0.0",
"@angular/common": "^12.0.0",
"@angular/compiler": "^12.0.0",
"@angular/compiler-cli": "^12.0.0",
"@angular/core": "^12.0.0",
"@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.2.10",
"@angular/platform-server": "^12.0.0",
"@ionic/core": "6.0.0-rc.0",
"ng-packagr": "5.7.1",
"tslint": "^5.12.1",
"tslint-ionic-rules": "0.0.21",
"typescript": "3.4.5",
"typescript-tslint-plugin": "^0.5.5"
"ng-packagr": "^12.0.0",
"typescript": "4.2.4"
}
}

View File

@ -3,20 +3,16 @@
"compilerOptions": {
"module": "commonjs",
"rootDir": "src",
"outDir": "dist",
"plugins": [
{ "name": "typescript-tslint-plugin" }
]
"outDir": "dist"
},
"files": [
"src/ionic-server-module.ts"
],
"files": ["src/ionic-server-module.ts"],
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"strictMetadataEmit" : true,
"strictMetadataEmit": true,
"flatModuleOutFile": "./index.js",
"flatModuleId": "@ionic/angular-server",
"skipTemplateCodegen": true,
"fullTemplateTypeCheck": false
"fullTemplateTypeCheck": false,
"compilationMode": "partial"
}
}