ngx-matomo-client
Version:
Matomo (fka. Piwik) client for Angular applications
261 lines • 12.7 kB
JavaScript
;
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