diff --git a/CHANGELOG.md b/CHANGELOG.md index 95eeead..313b550 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log (@egomobile/documentation) +## 0.7.0 + +- add `appDependsOn()` and `moduleDependsOn()` function +- add `existsIn` props to `DependencyItemWithInfo` and `ISerializableDependencyItemWithInfo` based items +- fix documentation + ## 0.6.0 - add optional `entities` prop to `IDependencyInformationEntity` diff --git a/package-lock.json b/package-lock.json index 6c093fa..6140e6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@egomobile/documentation", - "version": "0.6.0", + "version": "0.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@egomobile/documentation", - "version": "0.6.0", + "version": "0.7.0", "license": "LGPL-3.0", "devDependencies": { "@egomobile/tsconfig": "^5.0.0", diff --git a/package.json b/package.json index 8356d5f..644ff45 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@egomobile/documentation", - "version": "0.6.0", + "version": "0.7.0", "description": "Tools for documenting (TypeScript) code.", "main": "lib/index.js", "engines": { diff --git a/src/decorators/DependsOn.ts b/src/decorators/DependsOn.ts index 6428194..5d828f5 100644 --- a/src/decorators/DependsOn.ts +++ b/src/decorators/DependsOn.ts @@ -15,8 +15,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see . -import type { IDependencyInformation, IParameterDependencyItem, IPropertyDependencyItem, IMethodDependencyItem, DependencyInfoResolver, DependencyInfoCollectionArg } from "../types"; -import type { ClassPropKey, CreateDependsOnHelpersFunc, Nilable, Optional } from "../types/internal"; +import type { IDependencyInformation, IParameterDependencyItem, IPropertyDependencyItem, IMethodDependencyItem, DependencyInfoResolver, DependencyInfoCollectionArg, IStackInfo } from "../types"; +import type { ClassPropKey, CreateDependsOnHelpersFunc, Nilable, Nullable, Optional } from "../types/internal"; /** * Adds meta for a class, property, method or parameter @@ -100,6 +100,9 @@ export function DependsOn( dependenciesOrResolver?: Nilable ): any { const createDependsOnHelpers: CreateDependsOnHelpersFunc = require("../utils/internal").createDependsOnHelpers; + const tryGetStackInfo = require("../utils/internal").tryGetStackInfo; + + const stackInfo: Nullable = tryGetStackInfo(); const { addItem @@ -117,7 +120,10 @@ export function DependsOn( "type": "method" }; - addItem(newMethodItem); + addItem({ + "existsIn": stackInfo, + "item": newMethodItem + }); }; const addAsParameter = () => { @@ -131,7 +137,10 @@ export function DependsOn( "type": "parameter" }; - addItem(newParameterItem); + addItem({ + "existsIn": stackInfo, + "item": newParameterItem + }); }; const addAsProperty = () => { @@ -143,7 +152,10 @@ export function DependsOn( "type": "property" }; - addItem(newPropertyItem); + addItem({ + "existsIn": stackInfo, + "item": newPropertyItem + }); }; const addWith3Args = () => { @@ -176,7 +188,8 @@ export function DependsOn( classDependsOn( target, infoOrResolver, - dependenciesOrResolver + dependenciesOrResolver, + stackInfo ); } } diff --git a/src/functions/dependsOn.ts b/src/functions/dependsOn.ts index 983e113..d0f4028 100644 --- a/src/functions/dependsOn.ts +++ b/src/functions/dependsOn.ts @@ -13,9 +13,60 @@ // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see . -import type { DependencyInfoCollectionArg, DependencyInfoResolver, IClassDependencyItem, IDependencyInformation, IFunctionDependencyItem } from "../types"; +import type { DependencyInfoCollectionArg, DependencyInfoResolver, IClassDependencyItem, IDependencyInformation, IFunctionDependencyItem, IModuleDependencyItem, IStackInfo } from "../types"; import type { Constructor, CreateDependsOnHelpersFunc, Func, Nilable } from "../types/internal"; +/** + * Adds dependency information for current app. + * + * @example + * ``` + * import { + * appDependsOn, + * getDependencies + * } from "@egomobile/documentation" + * + * appDependsOn( + * { + * // your information here... + * } + * ) + * + * console.log( + * getDependencies() + * ) + * ``` + * + * @param {IDependencyInformation|DependencyInfoResolver} infoOrResolver The dependency information or the function that resolves it. + * @param {Nilable} [dependenciesOrResolver] The custom collection for the dependency info items or the function that resolves it. + * @param {Nilable} [stackInfo] Custom stack information. + * + * @throws ReferenceError No CommonJS based main module found. + */ +export function appDependsOn( + infoOrResolver: IDependencyInformation | DependencyInfoResolver, + dependenciesOrResolver?: Nilable, + stackInfo?: Nilable +) { + const mainModule = require.main; + if (!mainModule) { + throw new ReferenceError("no main module found"); + } + + const tryGetStackInfo = require("../utils/internal").tryGetStackInfo; + + if (!stackInfo) { + stackInfo = tryGetStackInfo(); + } + + moduleDependsOn( + mainModule, + infoOrResolver, + dependenciesOrResolver, + stackInfo + ); +} + /** * Adds dependency information for a class. * @@ -31,7 +82,7 @@ import type { Constructor, CreateDependsOnHelpersFunc, Func, Nilable } from "../ * } * * classDependsOn( - * MyClass + * MyClass, * { * // your information here... * } @@ -45,14 +96,21 @@ import type { Constructor, CreateDependsOnHelpersFunc, Func, Nilable } from "../ * @param {Constructor} classConstructor The class constructor. * @param {IDependencyInformation|DependencyInfoResolver} infoOrResolver The dependency information or the function that resolves it. * @param {Nilable} [dependenciesOrResolver] The custom collection for the dependency info items or the function that resolves it. + * @param {Nilable} [stackInfo] Custom stack information. */ export function classDependsOn( classConstructor: Constructor, infoOrResolver: IDependencyInformation | DependencyInfoResolver, - dependenciesOrResolver?: Nilable + dependenciesOrResolver?: Nilable, + stackInfo?: Nilable ) { const createDependsOnHelpers: CreateDependsOnHelpersFunc = require("../utils/internal").createDependsOnHelpers; + const tryGetStackInfo = require("../utils/internal").tryGetStackInfo; + + if (!stackInfo) { + stackInfo = tryGetStackInfo(); + } const { addItem @@ -63,7 +121,10 @@ export function classDependsOn( "type": "class" }; - addItem(newClassItem); + addItem({ + "existsIn": stackInfo ?? null, + "item": newClassItem + }); } /** @@ -81,7 +142,7 @@ export function classDependsOn( * } * * functionDependsOn( - * myFunction + * myFunction, * { * // your information here... * } @@ -95,14 +156,21 @@ export function classDependsOn( * @param {Func} func The function. * @param {IDependencyInformation|DependencyInfoResolver} infoOrResolver The dependency information or the function that resolves it. * @param {Nilable} [dependenciesOrResolver] The custom collection for the dependency info items or the function that resolves it. + * @param {Nilable} [stackInfo] Custom stack information. */ export function functionDependsOn( func: Func, infoOrResolver: IDependencyInformation | DependencyInfoResolver, - dependenciesOrResolver?: Nilable + dependenciesOrResolver?: Nilable, + stackInfo?: Nilable ) { const createDependsOnHelpers: CreateDependsOnHelpersFunc = require("../utils/internal").createDependsOnHelpers; + const tryGetStackInfo = require("../utils/internal").tryGetStackInfo; + + if (!stackInfo) { + stackInfo = tryGetStackInfo(); + } const { addItem @@ -113,5 +181,64 @@ export function functionDependsOn( "type": "function" }; - addItem(newFunctionItem); + addItem({ + "existsIn": stackInfo ?? null, + "item": newFunctionItem + }); +} + +/** + * Adds dependency information for a module. + * + * @example + * ``` + * import { + * moduleDependsOn, + * getDependencies + * } from "@egomobile/documentation" + * + * moduleDependsOn( + * require.main, + * { + * // your information here... + * } + * ) + * + * console.log( + * getDependencies() + * ) + * ``` + * + * @param {any} module The module. + * @param {IDependencyInformation|DependencyInfoResolver} infoOrResolver The dependency information or the function that resolves it. + * @param {Nilable} [dependenciesOrResolver] The custom collection for the dependency info items or the function that resolves it. + * @param {Nilable} [stackInfo] Custom stack information. + */ +export function moduleDependsOn( + module: any, + infoOrResolver: IDependencyInformation | DependencyInfoResolver, + dependenciesOrResolver?: Nilable, + stackInfo?: Nilable +) { + const createDependsOnHelpers: CreateDependsOnHelpersFunc = + require("../utils/internal").createDependsOnHelpers; + const tryGetStackInfo = require("../utils/internal").tryGetStackInfo; + + if (!stackInfo) { + stackInfo = tryGetStackInfo(); + } + + const { + addItem + } = createDependsOnHelpers(infoOrResolver, dependenciesOrResolver); + + const newModuleItem: IModuleDependencyItem = { + module, + "type": "module" + }; + + addItem({ + "existsIn": stackInfo ?? null, + "item": newModuleItem + }); } diff --git a/src/types/dependencies.ts b/src/types/dependencies.ts index 00ea08a..62d2a62 100644 --- a/src/types/dependencies.ts +++ b/src/types/dependencies.ts @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see . -import type { IWithClassNameProp, IWithClassOrInstanceProp, IWithEffectsProps, IWithIsStaticProp, IWithReferencesProp, IWithRemarksProp, IWithTypeProp } from "."; +import type { IStackInfo, IWithClassNameProp, IWithClassOrInstanceProp, IWithEffectsProps, IWithIsStaticProp, IWithReferencesProp, IWithRemarksProp, IWithTypeProp } from "."; import type { ClassPropKey, Collection, Constructor, Nilable, Nullable, Optional, ReferenceValue } from "./internal"; /** @@ -48,6 +48,7 @@ export type DependencyItem = IClassDependencyItem | IFunctionDependencyItem | IMethodDependencyItem | + IModuleDependencyItem | IParameterDependencyItem | IPropertyDependencyItem; @@ -60,6 +61,13 @@ export type DependencyItemCollectionResolver = () => Collection; /** * The underlying information. */ @@ -172,6 +180,20 @@ export interface IMethodDependencyItem extends IDependencyItem, Partial; /** * The underlying information. */ @@ -256,6 +282,20 @@ export interface ISerializableMethodDependencyItemWithInfo extends ISerializable type: "method"; } +/** + * A serizable version of an `IModuleDependencyItem`. + */ +export interface ISerializableModuleDependencyItemWithInfo extends ISerializableDependencyItemWithInfo { + /** + * The name/path of the module. + */ + name: string; + /** + * The type. + */ + type: "module"; +} + /** * A serizable version of an `IParameterDependencyItem`. */ @@ -295,5 +335,6 @@ export type SerializableDependencyItem = ISerializableClassDependencyItemWithInfo | ISerializableFunctionDependencyItemWithInfo | ISerializableMethodDependencyItemWithInfo | + ISerializableModuleDependencyItemWithInfo | ISerializableParameterDependencyItemWithInfo | ISerializablePropertyDependencyItemWithInfo; diff --git a/src/types/index.ts b/src/types/index.ts index eeb4503..6df884a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -15,6 +15,20 @@ import type { ClassOrInstance, Nilable, Nullable } from "./internal"; +/** + * Stack information. + */ +export interface IStackInfo { + /** + * Path to file, if available. + */ + file: Nullable; + /** + * Line of code inside `file`, if available. + */ + line: Nullable; +} + /** * An object with a `className` property. */ diff --git a/src/types/internal.ts b/src/types/internal.ts index 6ea6cf6..9e0f08d 100644 --- a/src/types/internal.ts +++ b/src/types/internal.ts @@ -13,6 +13,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see . +import type { IStackInfo } from "."; import type { DependencyInfoArg, DependencyInfoCollectionArg, DependencyItem, DependencyItemWithInfo, IDependencyInformation } from "./dependencies"; export type ClassOrInstance = T | Constructor; @@ -31,7 +32,7 @@ export type CreateDependsOnHelpersFunc = infoOrResolver: DependencyInfoArg, dependenciesOrResolver: Nilable ) => { - addItem: (item: DependencyItem) => void; + addItem: (options: IAddDependencyItemOptions) => void; getDepsCollection: () => Collection; getInfo: () => IDependencyInformation; }; @@ -41,6 +42,11 @@ export type Func = (...args: any[]) => any; export type GetClassNameFunc = (classOrInstance: ClassOrInstance) => Nullable; +export interface IAddDependencyItemOptions { + existsIn: Nullable; + item: DependencyItem; +} + export interface IArrayLike { push(item: T): any; } diff --git a/src/utils/index.ts b/src/utils/index.ts index 356a257..c99691e 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see . -import type { DependencyItemWithInfo, ISerializableClassDependencyItemWithInfo, ISerializableFunctionDependencyItemWithInfo, ISerializableMethodDependencyItemWithInfo, ISerializableParameterDependencyItemWithInfo, ISerializablePropertyDependencyItemWithInfo, SerializableDependencyItem } from "../types"; +import type { DependencyItemWithInfo, ISerializableClassDependencyItemWithInfo, ISerializableFunctionDependencyItemWithInfo, ISerializableMethodDependencyItemWithInfo, ISerializableModuleDependencyItemWithInfo, ISerializableParameterDependencyItemWithInfo, ISerializablePropertyDependencyItemWithInfo, SerializableDependencyItem } from "../types"; import type { GetClassNameFunc, IsConstructorFunc, List } from "../types/internal"; /** @@ -30,9 +30,12 @@ export function serializeDependencies(list: List): Seria const serializedItems: SerializableDependencyItem[] = []; for (const item of list) { + const existsIn = item.existsIn || null; + let newSerializedItem: SerializableDependencyItem; if (item.type === "class") { const newClassItem: ISerializableClassDependencyItemWithInfo = { + existsIn, "info": item.info, "name": item.constructor.name, "type": item.type @@ -41,19 +44,21 @@ export function serializeDependencies(list: List): Seria newSerializedItem = newClassItem; } else if (item.type === "method") { - const newClassItem: ISerializableMethodDependencyItemWithInfo = { + const newMethodItem: ISerializableMethodDependencyItemWithInfo = { "className": getClassName(item.classOrInstance), + existsIn, "info": item.info, "isStatic": isConstructor(item.classOrInstance), "name": String(item.key), "type": item.type }; - newSerializedItem = newClassItem; + newSerializedItem = newMethodItem; } else if (item.type === "parameter") { - const newClassItem: ISerializableParameterDependencyItemWithInfo = { + const newParameterItem: ISerializableParameterDependencyItemWithInfo = { "className": getClassName(item.classOrInstance), + existsIn, "info": item.info, "index": item.index, "isStatic": isConstructor(item.classOrInstance), @@ -61,27 +66,39 @@ export function serializeDependencies(list: List): Seria "type": item.type }; - newSerializedItem = newClassItem; + newSerializedItem = newParameterItem; } else if (item.type === "property") { - const newClassItem: ISerializablePropertyDependencyItemWithInfo = { + const newPropertyItem: ISerializablePropertyDependencyItemWithInfo = { "className": getClassName(item.classOrInstance), + existsIn, "info": item.info, "isStatic": isConstructor(item.classOrInstance), "name": String(item.key), "type": item.type }; - newSerializedItem = newClassItem; + newSerializedItem = newPropertyItem; } - else { - const newClassItem: ISerializableFunctionDependencyItemWithInfo = { + else if (item.type === "function") { + const newFunctionItem: ISerializableFunctionDependencyItemWithInfo = { + existsIn, "info": item.info, "name": item.key, "type": item.type }; - newSerializedItem = newClassItem; + newSerializedItem = newFunctionItem; + } + else { + const newModuleItem: ISerializableModuleDependencyItemWithInfo = { + existsIn, + "info": item.info, + "name": String(item.module?.filename ?? ""), + "type": item.type + }; + + newSerializedItem = newModuleItem; } serializedItems.push( diff --git a/src/utils/internal.ts b/src/utils/internal.ts index 16f04a4..0d7898e 100644 --- a/src/utils/internal.ts +++ b/src/utils/internal.ts @@ -13,8 +13,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see . -import type { DependencyInfoArg, DependencyInfoCollectionArg, DependencyItem, DependencyItemWithInfo } from "../types"; -import type { ClassOrInstance, Collection, Constructor, Nilable, Nullable } from "../types/internal"; +import type { DependencyInfoArg, DependencyInfoCollectionArg, DependencyItem, DependencyItemWithInfo, IStackInfo } from "../types"; +import type { ClassOrInstance, Collection, Constructor, IAddDependencyItemOptions, Nilable, Nullable } from "../types/internal"; export function createDependsOnHelpers( infoOrResolver: DependencyInfoArg, @@ -39,12 +39,18 @@ export function createDependsOnHelpers( } : getDependencies; - const addItem = (item: DependencyItem) => { + const addItem = (options: IAddDependencyItemOptions) => { + const { + existsIn, + item + } = options; + const deps = getDepsCollection(); const itemToAdd: DependencyItemWithInfo = { ...item, + existsIn, "info": getInfo(item) }; @@ -93,3 +99,33 @@ export function isConstructor(classOrInstance: ClassOrInsta export function isNil(val: unknown): val is (null | undefined) { return typeof val === "undefined" || val === null; } + +export function tryGetStackInfo(): Nullable { + try { + const stackTrace = new Error().stack; + + if (stackTrace) { + const stackTraceLines = stackTrace.split("\n"); + const callerLine = stackTraceLines[2]; + if (callerLine) { + const fileAndLineRegex = /\(?([^\s]+):(\d+):\d+\)?$/i; + const match = fileAndLineRegex.exec(callerLine); + + if (match && match.length > 2) { + const file = match[1].trim(); + const line = parseInt(match[2].trim() || ""); + + return { + "file": file || null, + "line": Number.isNaN(line) ? null : line + }; + } + } + } + + return null; + } + catch { + return false; + } +}