Skip to content

Commit

Permalink
Merge branch 'fix-category-descriptions-for-project-merge'
Browse files Browse the repository at this point in the history
  • Loading branch information
krisztianb committed Oct 25, 2024
2 parents 4f8fe25 + 0a14683 commit 120293a
Show file tree
Hide file tree
Showing 17 changed files with 201 additions and 143 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [6.0.3] - 2024-10-25
### Fixed
- Category descriptions and group descriptions were missing when merging into the project root.

## [6.0.2] - 2024-10-13
### Fixed
- Category descriptions and group descriptions were missing in the generated documentation when TypeDoc was run
Expand Down Expand Up @@ -88,7 +92,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

First release

[unreleased]: https://github.com/krisztianb/typedoc-plugin-merge-modules/compare/v6.0.2...HEAD
[unreleased]: https://github.com/krisztianb/typedoc-plugin-merge-modules/compare/v6.0.3...HEAD
[6.0.3]: https://github.com/krisztianb/typedoc-plugin-merge-modules/releases/tag/v6.0.3
[6.0.2]: https://github.com/krisztianb/typedoc-plugin-merge-modules/releases/tag/v6.0.2
[6.0.1]: https://github.com/krisztianb/typedoc-plugin-merge-modules/releases/tag/v6.0.1
[6.0.0]: https://github.com/krisztianb/typedoc-plugin-merge-modules/releases/tag/v6.0.0
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typedoc-plugin-merge-modules",
"version": "6.0.2",
"version": "6.0.3",
"description": "Plugin for TypeDoc that merges the content of modules.",
"author": {
"name": "Krisztián Balla",
Expand Down
137 changes: 58 additions & 79 deletions src/merger/module_bundle.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
/** @module merger */
import { Comment, DeclarationReflection, DocumentReflection, ProjectReflection, ReflectionKind } from "typedoc";
import {
addDeclarationReflectionToTarget,
addDocumentReflectionToTarget,
getNameFromDescriptionTag,
removeDeclarationReflectionFromModule,
removeDocumentReflectionFromModule,
moveDeclarationReflectionToTarget,
moveDocumentReflectionToTarget,
removeTagFromCommentsOf,
} from "../utils";

Expand Down Expand Up @@ -42,33 +39,37 @@ export class ModuleBundle {
}

/**
* Merges the modules of the bundle into one module.
* Merges the modules of the bundle into one module (or the given target override).
* @param categorizationHasAlreadyHappened Defines if TypeDoc has already categorized the
* reflections in the modules of the bundle.
* @param targetOverride A module to target, taking precendence over the bundle's search.
*/
public merge(categorizationHasAlreadyHappened: boolean): void {
public merge(
categorizationHasAlreadyHappened: boolean,
targetOverride?: DeclarationReflection | ProjectReflection,
): void {
// get target module
const targetModule = this.getTargetModule();
removeTagFromCommentsOf(targetModule, targetModuleCommentTag);
const mergeTarget = targetOverride ?? this.getTargetModule();
removeTagFromCommentsOf(mergeTarget, targetModuleCommentTag);

this.mergeChildrenAndDocumentsIntoTargetModule(targetModule);
this.moveChildrenAndDocumentsIntoTarget(mergeTarget);

if (categorizationHasAlreadyHappened) {
// In this case TypeDoc has already categorized and grouped the reflections.
// Therefore we must merge the content all categories and groups into the target module.
this.mergeCategoriesIntoTargetModule(targetModule);
this.mergeGroupsIntoTargetModule(targetModule);
// Therefore we must copy the content of all categories and groups into the target.
this.moveCategoriesIntoTarget(mergeTarget);
this.moveGroupsIntoTarget(mergeTarget);
} else {
// In this case we must copy the category descriptions into the target module because TypeDoc will look
// In this case we must copy the category descriptions into the target because TypeDoc will look
// for them there when it categorizes and groups the reflections.
// If we don't do this then the category and group descriptions would be missing in the docs.
this.copyCategoryDescriptionTagsIntoTargetModule(targetModule);
this.copyGroupDescriptionTagsIntoTargetModule(targetModule);
// If we don't do this then the category and group descriptions will be missing in the docs.
this.copyCategoryDescriptionTagsIntoTarget(mergeTarget);
this.copyGroupDescriptionTagsIntoTarget(mergeTarget);
}

// remove rest modules
this.modules.forEach((module) => {
if (module !== targetModule) {
if (module !== mergeTarget) {
delete module.children;
this.project.removeReflection(module);
}
Expand Down Expand Up @@ -101,61 +102,39 @@ export class ModuleBundle {
return this.modules[0];
}

private mergeChildrenAndDocumentsIntoTargetModule(targetModule: DeclarationReflection): void {
/**
* Moves all children and documents into the given target.
* @param target The target into which the children and documents should be moved.
*/
private moveChildrenAndDocumentsIntoTarget(target: DeclarationReflection | ProjectReflection): void {
for (const mod of this.modules) {
// Here we create a copy because the next loop modifies the collection
const reflections = [...(mod.childrenIncludingDocuments ?? [])];

for (const ref of reflections) {
// Drop aliases (= ReflectionKind.Reference)
if (ref instanceof DeclarationReflection && !ref.kindOf(ReflectionKind.Reference)) {
this.moveDeclarationReflectionToTargetModule(ref, targetModule);
moveDeclarationReflectionToTarget(ref, target);
} else if (ref instanceof DocumentReflection) {
this.moveDocumentReflectionToTargetModule(ref, targetModule);
moveDocumentReflectionToTarget(ref, target);
}
}
}
}

/**
* Moves a declaration reflection to the given target module.
* @param ref The declaration reflection that should be moved.
* @param targetModule The target module into which the declaration reflection should be moved.
*/
// eslint-disable-next-line class-methods-use-this
private moveDeclarationReflectionToTargetModule(
ref: DeclarationReflection,
targetModule: DeclarationReflection,
): void {
removeDeclarationReflectionFromModule(ref);
addDeclarationReflectionToTarget(ref, targetModule);
}

/**
* Moves a document reflection to the given target module.
* @param ref The document reflection that should be moved.
* @param targetModule The target module into which the document reflection should be moved.
* @throws {Error} If the given reflection is not within a module.
*/
// eslint-disable-next-line class-methods-use-this
private moveDocumentReflectionToTargetModule(ref: DocumentReflection, targetModule: DeclarationReflection): void {
removeDocumentReflectionFromModule(ref);
addDocumentReflectionToTarget(ref, targetModule);
}

/**
* Merges the children from all modules' categories into the corresponding category of the given target module.
* @param targetModule The target module into whoes categories the children should be merged.
* Moves the children from all modules' categories into the corresponding category of the given target.
* @param target The target into whose categories the children should be moved.
*/
private mergeCategoriesIntoTargetModule(targetModule: DeclarationReflection): void {
// merge categories
private moveCategoriesIntoTarget(target: DeclarationReflection | ProjectReflection): void {
// move categories
this.modules.forEach((module) => {
if (module !== targetModule) {
if (module !== target) {
module.categories?.forEach((category) => {
const existingTargetCategory = targetModule.categories?.find((c) => c.title === category.title);
const existingTargetCategory = target.categories?.find((c) => c.title === category.title);

if (!existingTargetCategory) {
targetModule.categories = [...(targetModule.categories ?? []), category];
target.categories = [...(target.categories ?? []), category];
} else {
existingTargetCategory.children = existingTargetCategory.children.concat(category.children);

Expand All @@ -168,7 +147,7 @@ export class ModuleBundle {
});

// sort categories
targetModule.categories?.forEach((category) => {
target.categories?.forEach((category) => {
category.children.sort((a, b) => {
if (a.name > b.name) {
return 1;
Expand All @@ -180,51 +159,51 @@ export class ModuleBundle {
});
}
/**
* Copies the category description comment tags into the the given target module.
* @param targetModule The target module into which the category descriptions are merged.
* Copies the category description comment tags into the the given target.
* @param target The target into which the category descriptions are copied.
*/
private copyCategoryDescriptionTagsIntoTargetModule(targetModule: DeclarationReflection): void {
private copyCategoryDescriptionTagsIntoTarget(target: DeclarationReflection | ProjectReflection): void {
this.modules.forEach((module) => {
if (module !== targetModule) {
if (module !== target) {
const categoryDescriptionsOfModule =
module.comment?.blockTags.filter((bt) => bt.tag === "@categoryDescription") ?? [];

if (categoryDescriptionsOfModule.length === 0) {
return; // nothing to copy
}

if (!targetModule.comment) {
targetModule.comment = new Comment([], []);
if (!target.comment) {
target.comment = new Comment([], []);
}

categoryDescriptionsOfModule.forEach((categoryDescription) => {
const targetModuleAlreadyHasThisCategoryDescriptionsTag = targetModule.comment?.blockTags.find(
const targetModuleAlreadyHasThisCategoryDescriptionsTag = target.comment?.blockTags.find(
(bt) =>
bt.tag === "@categoryDescription" &&
getNameFromDescriptionTag(bt) === getNameFromDescriptionTag(categoryDescription),
);

if (!targetModuleAlreadyHasThisCategoryDescriptionsTag) {
targetModule.comment?.blockTags.push(categoryDescription);
target.comment?.blockTags.push(categoryDescription);
}
});
}
});
}

/**
* Merges the children from all modules' groups into the corresponding group of the given target module.
* @param targetModule The target module into whoes groups the children should be merged.
* Moves the children from all modules' groups into the corresponding group of the given target.
* @param target The target into whose groups the children should be moved.
*/
private mergeGroupsIntoTargetModule(targetModule: DeclarationReflection): void {
// merge groups
private moveGroupsIntoTarget(target: DeclarationReflection | ProjectReflection): void {
// move groups
this.modules.forEach((module) => {
if (module !== targetModule) {
if (module !== target) {
module.groups?.forEach((group) => {
const existingTargetGroup = targetModule.groups?.find((g) => g.title === group.title);
const existingTargetGroup = target.groups?.find((g) => g.title === group.title);

if (!existingTargetGroup) {
targetModule.groups = [...(targetModule.groups ?? []), group];
target.groups = [...(target.groups ?? []), group];
} else {
existingTargetGroup.children = existingTargetGroup.children.concat(group.children);

Expand All @@ -237,7 +216,7 @@ export class ModuleBundle {
});

// sort groups
targetModule.groups?.forEach((group) => {
target.groups?.forEach((group) => {
group.children.sort((a, b) => {
if (a.name > b.name) {
return 1;
Expand All @@ -250,32 +229,32 @@ export class ModuleBundle {
}

/**
* Copies the group description comment tags into the the given target module.
* @param targetModule The target module into which the group descriptions are merged.
* Copies the group description comment tags into the the given target.
* @param target The target into which the group descriptions are copied.
*/
private copyGroupDescriptionTagsIntoTargetModule(targetModule: DeclarationReflection): void {
private copyGroupDescriptionTagsIntoTarget(target: DeclarationReflection | ProjectReflection): void {
this.modules.forEach((module) => {
if (module !== targetModule) {
if (module !== target) {
const groupDescriptionsOfModule =
module.comment?.blockTags.filter((bt) => bt.tag === "@groupDescription") ?? [];

if (groupDescriptionsOfModule.length === 0) {
return; // nothing to copy
}

if (!targetModule.comment) {
targetModule.comment = new Comment([], []);
if (!target.comment) {
target.comment = new Comment([], []);
}

groupDescriptionsOfModule.forEach((groupDescription) => {
const targetModuleAlreadyHasThisGroupDescriptionsTag = targetModule.comment?.blockTags.find(
const targetModuleAlreadyHasThisGroupDescriptionsTag = target.comment?.blockTags.find(
(bt) =>
bt.tag === "@groupDescription" &&
getNameFromDescriptionTag(bt) === getNameFromDescriptionTag(groupDescription),
);

if (!targetModuleAlreadyHasThisGroupDescriptionsTag) {
targetModule.comment?.blockTags.push(groupDescription);
target.comment?.blockTags.push(groupDescription);
}
});
}
Expand Down
1 change: 0 additions & 1 deletion src/merger/module_category_merger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/** @module merger */
import { DeclarationReflection } from "typedoc";
import { ModuleMerger } from "./module_merger";

Expand Down
74 changes: 15 additions & 59 deletions src/merger/project_merger.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { DeclarationReflection, DocumentReflection, ProjectReflection, ReflectionKind } from "typedoc";
import {
addDeclarationReflectionToTarget,
addDocumentReflectionToTarget,
getModulesFrom,
removeDeclarationReflectionFromModule,
removeDocumentReflectionFromModule,
} from "../utils";
import { ProjectReflection } from "typedoc";
import { Plugin } from "../plugin";
import { getModulesFrom } from "../utils";
import { ModuleBundle } from "./module_bundle";

/**
* Merger that moves the content of all modules into the project root.
Expand All @@ -14,12 +10,17 @@ export class ProjectMerger {
/** The project whose modules are merged. */
private readonly project: ProjectReflection;

/** The plugin which is using this merger. */
private readonly plugin: Plugin;

/**
* Creates a new merger instance.
* @param project The project whose modules are merged.
* @param plugin The plugin which is using this merger.
*/
public constructor(project: ProjectReflection) {
public constructor(project: ProjectReflection, plugin: Plugin) {
this.project = project;
this.plugin = plugin;
}

/**
Expand All @@ -29,56 +30,11 @@ export class ProjectMerger {
// In monorepo project each project is also a module => Recursively collect all modules
const allModules = getModulesFrom(this.project);

if (allModules.length > 0) {
this.removeModulesFromProject();

for (const module of allModules) {
// Here we create a copy because the next loop modifies the collection
const reflections = [...(module.childrenIncludingDocuments ?? [])];

for (const ref of reflections) {
// Drop aliases (= ReflectionKind.Reference) and modules
if (
ref instanceof DeclarationReflection &&
!ref.kindOf([ReflectionKind.Reference, ReflectionKind.Module])
) {
this.moveDeclarationReflectionToProject(ref);
} else if (ref instanceof DocumentReflection) {
this.moveDocumentReflectionFromToProject(ref);
}
}
}
}
}

/**
* Removes all modules from the project reflection. Doesn't touch the project documents.
*/
private removeModulesFromProject(): void {
this.project.children = [];
this.project.children.forEach((child) => this.project.removeReflection(child));
// Create a module bundle for all the modules
const bundle = new ModuleBundle(this.project);
allModules.forEach((module) => bundle.add(module));

// keep project documents that are included with the TypeDoc config parameter "projectDocuments"
this.project.childrenIncludingDocuments =
this.project.childrenIncludingDocuments?.filter((item) => item instanceof DocumentReflection) ?? [];
}

/**
* Moves a declaration reflection to the project root.
* @param ref The declaration reflection that should be moved.
*/
private moveDeclarationReflectionToProject(ref: DeclarationReflection): void {
removeDeclarationReflectionFromModule(ref);
addDeclarationReflectionToTarget(ref, this.project);
}

/**
* Moves a document reflection to the project root.
* @param ref The document reflection that should be moved.
* @throws {Error} If the given reflection is not within a module.
*/
private moveDocumentReflectionFromToProject(ref: DocumentReflection): void {
removeDocumentReflectionFromModule(ref);
addDocumentReflectionToTarget(ref, this.project);
// Merge the bundle into the project
bundle.merge(this.plugin.runsAfterCategorization, this.project);
}
}
Loading

0 comments on commit 120293a

Please sign in to comment.