UNPKG

ngx-matomo-client

Version:

Matomo (fka. Piwik) client for Angular applications

261 lines 12.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ngAdd = ngAdd; const schematics_1 = require("@angular-devkit/schematics"); const tasks_1 = require("@angular-devkit/schematics/tasks"); const ts = require("typescript"); const typescript_1 = require("typescript"); const schematics_utils_1 = require("../schematics-utils"); const utils_1 = require("../utils"); const version_1 = require("../version"); function hasAngularRouterDependency(host) { return (0, utils_1.getPackageJsonDependency)(host, '@angular/router') != null; } function checkRequiredRouterDependency(host) { if (!hasAngularRouterDependency(host)) { throw new schematics_1.SchematicsException(`You chose to automatically track page view, but this requires @angular/router as a dependency.\n` + `You can run "ng add @angular/router" to add it to your application.`); } } function addPackageJsonDependencies(options) { return (host, context) => { (0, utils_1.addPackageJsonDependency)(host, { type: utils_1.NodeDependencyType.Default, name: 'ngx-matomo-client', version: version_1.version, }); if (!options.skipLegacyPackageMigration) { (0, utils_1.removePackageJsonDependency)(host, '@ngx-matomo/tracker'); (0, utils_1.removePackageJsonDependency)(host, '@ngx-matomo/router'); } if (options.router) { checkRequiredRouterDependency(host); } context.addTask(new tasks_1.NodePackageInstallTask()); return host; }; } function buildTrackerConfig(options, context, modulePath) { const trackerUrl = (0, schematics_utils_1.escapeLiteral)(options.serverUrl || ''); const siteId = (0, schematics_utils_1.escapeLiteral)(options.siteId || ''); const scriptUrl = (0, schematics_utils_1.escapeLiteral)(options.scriptUrl || ''); const embeddedMode = !!scriptUrl && !trackerUrl && !siteId; let config; if (embeddedMode) { config = `{ scriptUrl: '${scriptUrl}' }`; } else { if (scriptUrl) { config = `{ trackerUrl: '${trackerUrl}', siteId: '${siteId}', scriptUrl: '${scriptUrl}' }`; } else { config = `{ trackerUrl: '${trackerUrl}', siteId: '${siteId}' }`; } if (!trackerUrl || !siteId) { context.logger.warn('Configuration properties "siteId" and "trackerUrl" are usually required. ' + 'You will need to manually update your configuration in "' + modulePath + "'. "); } } return config; } function applyChanges(host, modulePath, changes) { const recorder = host.beginUpdate(modulePath); (0, utils_1.applyToUpdateRecorder)(recorder, changes); host.commitUpdate(recorder); } function isLegacyImportPath(importPath) { return importPath.startsWith('@ngx-matomo/'); } function isRelevantImportPath(importPath) { return (importPath === 'ngx-matomo-client' || importPath.startsWith('ngx-matomo-client/') || isLegacyImportPath(importPath)); } function findRelevantImports(source, predicate) { const allImports = (0, utils_1.findNodes)(source, ts.SyntaxKind.ImportDeclaration); return allImports.filter(node => ts.isStringLiteral(node.moduleSpecifier) && predicate(node.moduleSpecifier.text)); } function hasLegacyModuleDeclaration(source) { const result = (0, utils_1.getDecoratorMetadata)(source, 'NgModule', '@angular/core'); const node = result[0]; if (!node || !ts.isObjectLiteralExpression(node)) { return false; } const matchingProperties = (0, utils_1.getMetadataField)(node, 'imports'); if (!matchingProperties) { return false; } const assignment = matchingProperties[0]; if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) { return false; } const arrLiteral = assignment.initializer; return arrLiteral.elements .filter(el => el.kind === ts.SyntaxKind.CallExpression) .some(el => el.getText().startsWith('Matomo')); } function getImportEntryPoints(host, options) { const useSecondaryEntryPoint = !hasAngularRouterDependency(host) && !options.router; const core = useSecondaryEntryPoint ? 'ngx-matomo-client/core' : 'ngx-matomo-client'; const router = useSecondaryEntryPoint ? 'ngx-matomo-client/router' : 'ngx-matomo-client'; return { core, router }; } function addImportsToNgModule(options, context) { return (host) => { var _a; const modulePath = options.module; if (options.skipImport || !modulePath) { return host; } const changes = []; const source = (0, schematics_utils_1.readIntoSourceFile)(host, modulePath); const trackerConfig = buildTrackerConfig(options, context, modulePath); const entryPoints = getImportEntryPoints(host, options); // If some Matomo imports are already present, use the legacy setup using // NgModule. // // If no import is present, use the new providers-style setup. // // Maybe in the future a schematics can be provided to migrate legacy NgModule // setup to new providers-style setup. const imports = findRelevantImports(source, isRelevantImportPath); let mainModuleIdentifier = 'MatomoModule'; let routerModuleIdentifier = 'MatomoRouterModule'; let asteriskAlias; let mainModuleImported = false; let routerModuleImported = false; // Loop in reverse order and migrate if needed for (let i = imports.length - 1; i >= 0; i--) { const statement = imports[i]; if (!((_a = statement.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings)) { // Such statement should not exist because this lib has no side effect return; } const bindings = statement.importClause.namedBindings; if (ts.isNamespaceImport(bindings)) { asteriskAlias = bindings.name.text; if (!mainModuleImported) { mainModuleIdentifier = `${asteriskAlias}.MatomoModule`; } if (!routerModuleImported) { routerModuleIdentifier = `${asteriskAlias}.MatomoRouterModule`; } mainModuleImported = true; routerModuleImported = true; } else { for (const specifier of bindings.elements) { switch (specifier.name.text) { case 'MatomoModule': case 'NgxMatomoModule': case 'NgxMatomoTrackerModule': mainModuleImported = true; mainModuleIdentifier = specifier.name.text; break; case 'MatomoRouterModule': case 'NgxMatomoRouterModule': routerModuleImported = true; routerModuleIdentifier = specifier.name.text; break; } } } } if (hasLegacyModuleDeclaration(source)) { context.logger.info('Your configuration is using classic configuration with NgModule imports. ' + 'While this is still fully supported, you may want to take a look at the new NgModule-free setup using provideMatomo() (see README > Installation)'); if (!mainModuleImported) { changes.push((0, utils_1.insertImport)(source, modulePath, mainModuleIdentifier, entryPoints.core)); } changes.push(...(0, utils_1.addSymbolToNgModuleMetadata)(source, modulePath, 'imports', `${mainModuleIdentifier}.forRoot(${trackerConfig})`)); if (options.router) { if (!routerModuleImported) { changes.push((0, utils_1.insertImport)(source, modulePath, routerModuleIdentifier, entryPoints.router)); } changes.push(...(0, utils_1.addSymbolToNgModuleMetadata)(source, modulePath, 'imports', routerModuleIdentifier)); } } else { const provideMatomoArgs = [trackerConfig]; changes.push((0, utils_1.insertImport)(source, modulePath, 'provideMatomo', entryPoints.core)); if (options.router) { changes.push((0, utils_1.insertImport)(source, modulePath, 'withRouter', entryPoints.router)); provideMatomoArgs.push(`withRouter()`); } changes.push(...(0, utils_1.addSymbolToNgModuleMetadata)(source, modulePath, 'providers', `provideMatomo(${provideMatomoArgs.join(', ')})`)); } applyChanges(host, modulePath, changes); return host; }; } function migrateAllLegacyImports(options, context) { return (host) => { if (!options.skipLegacyPackageMigration) { context.logger.info('Migrating imports from legacy @ngx-matomo/* packages...'); host.visit(path => { // if (options.module && path.endsWith(options.module) && !options.skipImport) { // context.logger.info('Skip migration of "' + path + '"'); // // If this is the main module path, handle migration directly in // // `addImportsToNgModule` // return; // } if (!options.path || path.startsWith(options.path)) { const file = (0, schematics_utils_1.readIntoSourceFile)(host, path); const entryPoints = getImportEntryPoints(host, options); const changes = []; file.forEachChild(node => { if (node.kind === typescript_1.SyntaxKind.ImportDeclaration) { const statement = node; const moduleSpecifier = statement.moduleSpecifier; const fullText = moduleSpecifier.getFullText(); const text = moduleSpecifier.text; const pos = moduleSpecifier.pos; if (text === '@ngx-matomo/tracker') { // Be sure to keep any original spacings (contained in getFullText() only) const newFullText = fullText.replace(text, entryPoints.core); changes.push(new utils_1.ReplaceChange(path, pos, fullText, newFullText)); } if (text === '@ngx-matomo/router') { // Be sure to keep any original spacings (contained in getFullText() only) const newFullText = fullText.replace(text, entryPoints.router); changes.push(new utils_1.ReplaceChange(path, pos, fullText, newFullText)); } } }); applyChanges(host, path, changes); } }); } return host; }; } function ngAdd(options) { return (host, context) => __awaiter(this, void 0, void 0, function* () { if (options.path === undefined) { options.path = yield (0, utils_1.createDefaultPath)(host, options.project); } options.module = (0, utils_1.findModuleFromOptions)(host, { skipImport: options.skipImport, path: options.path, module: options.module, name: '', }); return (0, schematics_1.chain)([ addPackageJsonDependencies(options), addImportsToNgModule(options, context), migrateAllLegacyImports(options, context), ]); }); } //# sourceMappingURL=index.js.map