Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(lazyload): move 'root' provider to provideMatomo #98

Merged
merged 5 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"options": {
"main": "projects/ngx-matomo-client/test.ts",
"tsConfig": "projects/ngx-matomo-client/tsconfig.spec.json",
"karmaConfig": "projects/ngx-matomo-client/karma.conf.js"
"karmaConfig": "projects/ngx-matomo-client/karma.conf.js",
"codeCoverageExclude": ["projects/ngx-matomo-client/core/testing/**"]
}
},
"lint": {
Expand Down
6 changes: 3 additions & 3 deletions projects/demo/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { MatomoConfiguration, MatomoRouterModule, MatomoModule } from 'ngx-matomo-client';
import { MatomoModule, MatomoRouterModule } from 'ngx-matomo-client';
import { AppComponent } from './app.component';

describe('AppComponent', () => {
Expand All @@ -12,8 +12,8 @@ describe('AppComponent', () => {
MatomoModule.forRoot({
trackerUrl: '',
siteId: '',
} as MatomoConfiguration),
MatomoRouterModule,
}),
MatomoRouterModule.forRoot(),
],
declarations: [AppComponent],
schemas: [NO_ERRORS_SCHEMA],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Component, LOCALE_ID } from '@angular/core';
import { fakeAsync, flush, TestBed } from '@angular/core/testing';
import { By, DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { provideMatomo } from '../providers';
import { provideTestingTracker } from '../testing/testing-tracker';
import {
ASYNC_INTERNAL_MATOMO_CONFIGURATION,
INTERNAL_MATOMO_CONFIGURATION,
InternalMatomoConfiguration,
MATOMO_CONFIGURATION,
MatomoConfiguration,
} from '../tracker/configuration';
import { MatomoInitializerService } from '../tracker/matomo-initializer.service';
import { MatomoOptOutFormComponent } from './matomo-opt-out-form.component';

@Component({
Expand Down Expand Up @@ -102,18 +101,14 @@ describe('MatomoOptOutFormComponent', () => {
HostWithoutLocaleComponent,
],
providers: [
{
provide: MATOMO_CONFIGURATION,
useValue: { siteId: 1, trackerUrl: 'http://localhost' } as MatomoConfiguration,
},
provideMatomo({ siteId: 1, trackerUrl: 'http://localhost' }),
provideTestingTracker(),
{
provide: LOCALE_ID,
useValue: 'en',
},
],
}).compileComponents();

TestBed.inject(MatomoInitializerService).initialize();
});

it('should create', async () => {
Expand Down
4 changes: 4 additions & 0 deletions projects/ngx-matomo-client/core/private-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ export {
isAutoConfigurationMode as ɵisAutoConfigurationMode,
} from './tracker/configuration';
export { InternalMatomoTracker as ɵInternalMatomoTracker } from './tracker/internal-matomo-tracker.service';
export {
provideTestingTracker as ɵprovideTestingTracker,
MatomoTestingTracker as ɵMatomoTestingTracker,
} from './testing/testing-tracker';
export { createMatomoFeature as ɵcreateMatomoFeature } from './providers';
4 changes: 2 additions & 2 deletions projects/ngx-matomo-client/core/providers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ describe('providers', () => {
const config: MatomoConfiguration = { trackerUrl: 'my-tracker', siteId: 42 };

await setUp([
provideMatomo(config),
{
provide: MatomoInitializerService,
useValue: fakeInitializer,
},
provideMatomo(config),
]);

expect(TestBed.inject(MatomoTracker)).toEqual(jasmine.any(MatomoTracker));
Expand All @@ -40,6 +40,7 @@ describe('providers', () => {
const trackerUrlToken = new InjectionToken<string>('trackerUrl');

await setUp([
provideMatomo(() => ({ trackerUrl: TestBed.inject(trackerUrlToken), siteId: 42 })),
{
provide: MatomoInitializerService,
useValue: fakeInitializer,
Expand All @@ -48,7 +49,6 @@ describe('providers', () => {
provide: trackerUrlToken,
useValue: trackerUrl,
},
provideMatomo(() => ({ trackerUrl: TestBed.inject(trackerUrlToken), siteId: 42 })),
]);

expect(TestBed.inject(MatomoTracker)).toEqual(jasmine.any(MatomoTracker));
Expand Down
43 changes: 41 additions & 2 deletions projects/ngx-matomo-client/core/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,26 @@ import {
makeEnvironmentProviders,
Provider,
} from '@angular/core';
import { MATOMO_CONFIGURATION, MatomoConfiguration } from './tracker/configuration';
import { MatomoInitializerService } from './tracker/matomo-initializer.service';
import {
ASYNC_INTERNAL_MATOMO_CONFIGURATION,
createDeferredInternalMatomoConfiguration,
createInternalMatomoConfiguration,
DEFERRED_INTERNAL_MATOMO_CONFIGURATION,
INTERNAL_MATOMO_CONFIGURATION,
MATOMO_CONFIGURATION,
MatomoConfiguration,
} from './tracker/configuration';
import {
createInternalMatomoTracker,
InternalMatomoTracker,
} from './tracker/internal-matomo-tracker.service';
import {
createMatomoInitializer,
MatomoInitializerService,
} from './tracker/matomo-initializer.service';
import { MatomoTracker } from './tracker/matomo-tracker.service';
import { MATOMO_SCRIPT_FACTORY, MatomoScriptFactory } from './tracker/script-factory';
import { ScriptInjector } from './utils/script-injector';

const PRIVATE_MATOMO_PROVIDERS = Symbol('MATOMO_PROVIDERS');
const PRIVATE_MATOMO_CHECKS = Symbol('MATOMO_CHECKS');
Expand Down Expand Up @@ -74,6 +91,28 @@ export function provideMatomo(
...features: MatomoFeature[]
): EnvironmentProviders {
const providers: Provider[] = [
MatomoTracker,
ScriptInjector,
{
provide: InternalMatomoTracker,
useFactory: createInternalMatomoTracker,
},
{
provide: MatomoInitializerService,
useFactory: createMatomoInitializer,
},
{
provide: INTERNAL_MATOMO_CONFIGURATION,
useFactory: createInternalMatomoConfiguration,
},
{
provide: DEFERRED_INTERNAL_MATOMO_CONFIGURATION,
useFactory: createDeferredInternalMatomoConfiguration,
},
{
provide: ASYNC_INTERNAL_MATOMO_CONFIGURATION,
useFactory: () => inject(DEFERRED_INTERNAL_MATOMO_CONFIGURATION).configuration,
},
{
provide: ENVIRONMENT_INITIALIZER,
multi: true,
Expand Down
59 changes: 59 additions & 0 deletions projects/ngx-matomo-client/core/testing/testing-tracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ApplicationInitStatus, inject, Injectable, Provider } from '@angular/core';
import {
InternalMatomoTracker,
InternalMatomoTrackerType,
} from '../tracker/internal-matomo-tracker.service';
import { PrefixedType } from '../utils/types';

export function provideTestingTracker(): Provider[] {
return [
MatomoTestingTracker,
{
provide: InternalMatomoTracker,
useExisting: MatomoTestingTracker,
},
];
}

@Injectable()
export class MatomoTestingTracker<MATOMO = unknown, PREFIX extends string = ''>
implements InternalMatomoTrackerType
{
private readonly initStatus = inject(ApplicationInitStatus);

/** Get list of all calls until initialization */
callsOnInit: unknown[][] = [];
/** Get list of all calls after initialization */
callsAfterInit: unknown[][] = [];

/** Get a copy of all calls since application startup */
get calls(): unknown[] {
return [...this.callsOnInit, ...this.callsAfterInit];
}

countCallsAfterInit(command: string): number {
return this.callsAfterInit.filter(call => call[0] === command).length;
}

reset() {
this.callsOnInit = [];
this.callsAfterInit = [];
}

/** Asynchronously call provided method name on matomo tracker instance */
async get<K extends keyof PrefixedType<MATOMO, PREFIX>>(_: K): Promise<never> {
return Promise.reject('MatomoTracker is disabled');
}

push(arg: unknown[]): void {
if (this.initStatus.done) {
this.callsAfterInit.push(arg);
} else {
this.callsOnInit.push(arg);
}
}

async pushFn<T>(_: (matomo: PrefixedType<MATOMO, PREFIX>) => T): Promise<T> {
return Promise.reject('MatomoTracker is disabled');
}
}
90 changes: 41 additions & 49 deletions projects/ngx-matomo-client/core/tracker/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,69 +20,61 @@ export const MATOMO_CONFIGURATION = new InjectionToken<MatomoConfiguration>('MAT
*/
export const INTERNAL_MATOMO_CONFIGURATION = new InjectionToken<InternalMatomoConfiguration>(
'INTERNAL_MATOMO_CONFIGURATION',
{
factory(): InternalMatomoConfiguration {
const { mode, requireConsent, ...restConfig } = requireNonNull(
inject(MATOMO_CONFIGURATION, { optional: true }),
CONFIG_NOT_FOUND,
);

return {
mode: mode ? coerceInitializationMode(mode) : undefined,
disabled: false,
enableLinkTracking: true,
trackAppInitialLoad: !inject(MATOMO_ROUTER_ENABLED),
requireConsent: requireConsent ? coerceConsentRequirement(requireConsent) : 'none',
enableJSErrorTracking: false,
runOutsideAngularZone: false,
disableCampaignParameters: false,
acceptDoNotTrack: false,
...restConfig,
};
},
},
);

export function createInternalMatomoConfiguration(): InternalMatomoConfiguration {
const { mode, requireConsent, ...restConfig } = requireNonNull(
inject(MATOMO_CONFIGURATION, { optional: true }),
CONFIG_NOT_FOUND,
);

return {
mode: mode ? coerceInitializationMode(mode) : undefined,
disabled: false,
enableLinkTracking: true,
trackAppInitialLoad: !inject(MATOMO_ROUTER_ENABLED),
requireConsent: requireConsent ? coerceConsentRequirement(requireConsent) : 'none',
enableJSErrorTracking: false,
runOutsideAngularZone: false,
disableCampaignParameters: false,
acceptDoNotTrack: false,
...restConfig,
};
}

/**
* For internal use only. Injection token for deferred {@link InternalMatomoConfiguration}.
*
*/
export const DEFERRED_INTERNAL_MATOMO_CONFIGURATION =
new InjectionToken<DeferredInternalMatomoConfiguration>(
'DEFERRED_INTERNAL_MATOMO_CONFIGURATION',
{
factory: () => {
const base = inject(INTERNAL_MATOMO_CONFIGURATION);
let resolveFn: ((configuration: InternalMatomoConfiguration) => void) | undefined;
const configuration = new Promise<InternalMatomoConfiguration>(
resolve => (resolveFn = resolve),
);

return {
configuration,
markReady(configuration) {
requireNonNull(
resolveFn,
'resolveFn',
)({
...base,
...configuration,
} as InternalMatomoConfiguration);
},
};
},
new InjectionToken<DeferredInternalMatomoConfiguration>('DEFERRED_INTERNAL_MATOMO_CONFIGURATION');

export function createDeferredInternalMatomoConfiguration(): DeferredInternalMatomoConfiguration {
const base = inject(INTERNAL_MATOMO_CONFIGURATION);
let resolveFn: ((configuration: InternalMatomoConfiguration) => void) | undefined;
const configuration = new Promise<InternalMatomoConfiguration>(resolve => (resolveFn = resolve));

return {
configuration,
markReady(configuration) {
requireNonNull(
resolveFn,
'resolveFn',
)({
...base,
...configuration,
});
},
);
};
}

/**
* For internal use only. Injection token for fully loaded async {@link InternalMatomoConfiguration}.
*
*/
export const ASYNC_INTERNAL_MATOMO_CONFIGURATION = new InjectionToken<
Promise<InternalMatomoConfiguration>
>('ASYNC_INTERNAL_MATOMO_CONFIGURATION', {
factory: () => inject(DEFERRED_INTERNAL_MATOMO_CONFIGURATION).configuration,
});
>('ASYNC_INTERNAL_MATOMO_CONFIGURATION');

/**
* For internal use only. Module configuration merged with default values.
Expand All @@ -97,7 +89,7 @@ export type InternalMatomoConfiguration = Omit<MatomoConfiguration, 'mode' | 're
export interface DeferredInternalMatomoConfiguration {
readonly configuration: Promise<InternalMatomoConfiguration>;

markReady(configuration: AutoMatomoConfiguration<'auto' | 'deferred'>): void;
markReady(configuration: InternalMatomoConfiguration): void;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function trimTrailingUndefinedElements<T>(array: T[]): T[] {
return trimmed;
}

type InternalMatomoTrackerType = Pick<
export type InternalMatomoTrackerType = Pick<
InternalMatomoTracker<unknown, string>,
'get' | 'push' | 'pushFn'
>;
Expand All @@ -30,10 +30,7 @@ export function createInternalMatomoTracker(): InternalMatomoTrackerType {
return disabled || !isBrowser ? new NoopMatomoTracker() : new InternalMatomoTracker();
}

@Injectable({
providedIn: 'root',
useFactory: createInternalMatomoTracker,
})
@Injectable()
export class InternalMatomoTracker<MATOMO, PREFIX extends string = ''> {
private readonly ngZone = inject(NgZone);
private readonly config = inject(INTERNAL_MATOMO_CONFIGURATION);
Expand Down Expand Up @@ -70,6 +67,7 @@ export class InternalMatomoTracker<MATOMO, PREFIX extends string = ''> {
}
}

@Injectable()
export class NoopMatomoTracker<MATOMO = unknown, PREFIX extends string = ''>
implements InternalMatomoTrackerType
{
Expand Down
Loading
Loading