@omlet/cli
Version:
Omlet (https://omlet.dev) is a component analytics tool that uses a CLI to scan your codebase to detect components and their usage. Get real usage insights from customizable charts to measure adoption across all projects and identify opportunities to impr
160 lines (159 loc) • 6.15 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.init = exports.CliHookError = exports.HookContext = exports.HookComponent = void 0;
const upath_1 = __importDefault(require("upath"));
const error_1 = require("../error");
const fileUtils_1 = require("../fileUtils");
class HookComponent {
constructor(context, component) {
this.context = context;
this.component = component;
this._props = null;
this._htmlElements = null;
this._children = null;
}
get name() {
return this.component.name;
}
get id() {
return this.component.id;
}
get createdAt() {
return Object.freeze(this.component.created_at);
}
get updatedAt() {
return Object.freeze(this.component.updated_at);
}
get packageName() {
return this.component.source.source.package_name;
}
get filePath() {
return this.component.source.source.path;
}
get props() {
if (this._props === null) {
this._props = Object.freeze(this.component.props.map(p => Object.freeze(p)));
}
return this._props;
}
get htmlElementsUsed() {
if (this._htmlElements === null) {
this._htmlElements = Object.freeze(this.component.html_elements);
}
return this._htmlElements;
}
get children() {
if (this._children === null) {
this._children = Object.freeze(this.component.dependencies.map(d => this.context.getComponent(d.to.id)).filter(c => c !== null));
}
return this._children;
}
get parents() {
return this.context.findParentComponents(this.component.id);
}
get metadata() {
return Object.freeze(this.context.getComponentMetadata(this.id));
}
setMetadata(name, value) {
this.context.setComponentMetadata(this.id, name, value);
}
}
exports.HookComponent = HookComponent;
class HookContext {
constructor(analysisData, hookModule) {
this.hookModule = hookModule;
this.componentMap = new Map();
this.reverseDependencyMap = new Map();
for (const sourceComponent of analysisData.components) {
this.componentMap.set(sourceComponent.id, Object.preventExtensions(new HookComponent(this, sourceComponent)));
for (const dependency of sourceComponent.dependencies) {
if (!this.reverseDependencyMap.has(dependency.to.id)) {
this.reverseDependencyMap.set(dependency.to.id, []);
}
this.reverseDependencyMap.get(dependency.to.id).push(sourceComponent.id);
}
}
this.components = Array.from(this.componentMap.values());
this.componentMetadata = new Map();
}
getComponent(id) {
return this.componentMap.get(id) || null;
}
findParentComponents(id) {
var _a;
return ((_a = this.reverseDependencyMap.get(id)) !== null && _a !== void 0 ? _a : []).map(id => this.getComponent(id));
}
setComponentMetadata(id, name, value) {
const valueType = typeof value;
if (valueType !== "number" && valueType !== "string" && valueType !== "boolean" && !(value instanceof Date)) {
throw new CliHookError(`Setting metadata failed. Invalid value type: ${valueType}. Only string, number, boolean or Date are allowed.`);
}
if (value instanceof Date && Number.isNaN(value.getTime())) {
throw new CliHookError("Setting metadata failed. Invalid Date value.");
}
if (!this.componentMetadata.has(id)) {
this.componentMetadata.set(id, {});
}
this.componentMetadata.get(id)[name] = value;
}
getComponentMetadata(id) {
var _a;
return (_a = this.componentMetadata.get(id)) !== null && _a !== void 0 ? _a : null;
}
async afterScan() {
try {
await this.hookModule.afterScan(Object.freeze([...this.components]));
}
catch (err) {
if (err instanceof CliHookError) {
throw err;
}
throw new CliHookError("Error running afterScan hook", err);
}
}
}
exports.HookContext = HookContext;
class CliHookError extends error_1.CliError {
constructor(message, reason) {
super(message, {
context: { reason },
});
this.name = this.constructor.name;
this.reason = reason;
}
}
exports.CliHookError = CliHookError;
async function init(hookScriptPath, analysisData) {
const absScriptPath = upath_1.default.resolve(process.cwd(), hookScriptPath);
if (!await (0, fileUtils_1.pathExists)(absScriptPath)) {
throw new CliHookError(`Hook script could not be found at ${absScriptPath}`);
}
const hookModule = (await Promise.resolve().then(() => __importStar(require(absScriptPath))));
if (typeof hookModule.afterScan !== "function") {
throw new CliHookError(`The script (${absScriptPath}) has no exported function called afterScan`);
}
return new HookContext(analysisData, hookModule);
}
exports.init = init;