Skip to content

Commit abe3073

Browse files
committed
Added new command odulesToMarkdown.
1 parent 5fba2f5 commit abe3073

File tree

6 files changed

+175
-6
lines changed

6 files changed

+175
-6
lines changed

package.json

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"displayName": "AngularTools",
55
"description": "AngularTools is a collection of tools for exploring an Angular project, help you with documenting, reverse engineering a project or help when refactoring.",
66
"icon": "images/logo.png",
7-
"version": "1.2.2",
7+
"version": "1.3.0",
88
"repository": "https://github.com/CoderAllan/vscode-angulartools",
99
"author": {
1010
"name": "Allan Simonsen",
@@ -26,7 +26,8 @@
2626
"activationEvents": [
2727
"onCommand:angulartools.componentHierarchyDgml",
2828
"onCommand:angulartools.listAllImports",
29-
"onCommand:angulartools.projectDirectoryStructure",
29+
"onCommand:angulartools.projectDirectoryStructure",
30+
"onCommand:angulartools.modulesToMarkdown",
3031
"onCommand:angulartools.packageJsonToMarkdown",
3132
"onCommand:angulartools.showComponentHierarchy",
3233
"onCommand:angulartools.componentHierarchyMarkdown"
@@ -41,10 +42,14 @@
4142
{
4243
"command": "angulartools.projectDirectoryStructure",
4344
"title": "AngularTools: Show the project directory structure"
44-
},
45+
},
46+
{
47+
"command": "angulartools.modulesToMarkdown",
48+
"title": "AngularTools: Generate a Markdown file of all modules in the current project."
49+
},
4550
{
4651
"command": "angulartools.packageJsonToMarkdown",
47-
"title": "AngularTools: Generate Markdown file from package.json files in the workspace."
52+
"title": "AngularTools: Generate a Markdown file from package.json files in the workspace."
4853
},
4954
{
5055
"command": "angulartools.componentHierarchyDgml",
@@ -145,6 +150,11 @@
145150
"type": "string",
146151
"default": "ComponentHierarchy.md",
147152
"description": "The default name used when saving the component hierarchy to a markdown file."
153+
},
154+
"angularTools.modulesToMarkdownFilename": {
155+
"type": "string",
156+
"default": "Modules.md",
157+
"description": "The default name used when saving the project module to a markdown file."
148158
}
149159
}
150160
}

src/arrayUtils.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import path = require("path");
22

33
export class ArrayUtils {
4-
4+
55
public static arrayToMarkdown(array: string[] | undefined): string {
66
if (array === undefined || array.length === 0) {
77
return '';
@@ -17,7 +17,7 @@ export class ArrayUtils {
1717
}
1818
}
1919
}
20-
20+
2121
public static sortStrings(stringA: string, stringB: string): number {
2222
stringA = path.basename(stringA).toUpperCase();
2323
stringB = path.basename(stringB).toUpperCase();
@@ -29,4 +29,12 @@ export class ArrayUtils {
2929
}
3030
return 0;
3131
}
32+
33+
public static arrayLength(array: string[]): number {
34+
if (typeof (array) === 'string') {
35+
return 1;
36+
} else {
37+
return array.length;
38+
}
39+
}
3240
}

src/commands/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './componentHierarchyDgml';
22
export * from './listAllImports';
3+
export * from './modulesToMarkdown';
34
export * from './packageJsonToMarkdown';
45
export * from './projectDirectoryStructure';
56
export * from './showComponentHierarchy';

src/commands/modulesToMarkdown.ts

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import * as fs from 'fs';
2+
import * as vscode from 'vscode';
3+
import * as path from 'path';
4+
import { ArrayUtils, Config, FileSystemUtils } from '@src';
5+
6+
export interface INgModule {
7+
imports: string[];
8+
exports: string[];
9+
declarations: string[];
10+
entryComponents: string[];
11+
providers: string[];
12+
bootstrap: string[];
13+
}
14+
15+
export class NgModule implements INgModule {
16+
constructor(private data: INgModule) {
17+
this.imports = data.imports;
18+
this.exports = data.exports;
19+
this.declarations = data.declarations;
20+
this.entryComponents = data.entryComponents;
21+
this.providers = data.providers;
22+
this.bootstrap = data.bootstrap;
23+
}
24+
public imports: string[] = [];
25+
public exports: string[] = [];
26+
public declarations: string[] = [];
27+
public entryComponents: string[] = [];
28+
public providers: string[] = [];
29+
public bootstrap: string[] = [];
30+
public filename: string = '';
31+
public moduleName: string = '';
32+
public moduleContents: string = '';
33+
public moduleStats(): number[] {
34+
return [
35+
this.declarations === undefined ? 0 : ArrayUtils.arrayLength(this.declarations),
36+
this.imports === undefined ? 0 : ArrayUtils.arrayLength(this.imports),
37+
this.exports === undefined ? 0 : ArrayUtils.arrayLength(this.exports),
38+
this.bootstrap === undefined ? 0 : ArrayUtils.arrayLength(this.bootstrap),
39+
this.providers === undefined ? 0 : ArrayUtils.arrayLength(this.providers),
40+
this.entryComponents === undefined ? 0 : ArrayUtils.arrayLength(this.entryComponents),
41+
];
42+
}
43+
}
44+
45+
export class ModulesToMarkdown {
46+
private config = new Config();
47+
public static get commandName(): string { return 'modulesToMarkdown'; }
48+
49+
public execute() {
50+
const fsUtils = new FileSystemUtils();
51+
var workspaceDirectory: string = fsUtils.getWorkspaceFolder();
52+
const filenames = fsUtils.listFiles(workspaceDirectory, this.config.excludeDirectories, this.isTypescriptFile);
53+
let markdownContent = '# Modules\n\n';
54+
const errors: string[] = [];
55+
const modules: NgModule[] = [];
56+
filenames.sort(ArrayUtils.sortStrings).forEach(filename => {
57+
const module = this.readModule(filename, errors);
58+
if (module !== undefined) {
59+
modules.push(module);
60+
}
61+
});
62+
markdownContent = markdownContent +
63+
'## Modules in workspace\n\n' +
64+
'| Module | Declarations | Imports | Exports | Bootstrap | Providers | Entry points |\n' +
65+
'| ---| --- | --- | --- | --- | --- | --- |\n';
66+
let modulesMarkdown: string = '';
67+
modules.forEach(module => {
68+
markdownContent = markdownContent + '| ' + module.moduleName + ' | ' + module.moduleStats().join(' | ') + ' |\n';
69+
modulesMarkdown = modulesMarkdown + this.generateModuleMarkdown(module);
70+
});
71+
markdownContent = markdownContent + '\n' +modulesMarkdown;
72+
if (errors.length > 0) {
73+
this.showErrors(errors);
74+
}
75+
fsUtils.writeFileAndOpen(path.join(workspaceDirectory, this.config.modulesToMarkdownFilename), markdownContent);
76+
}
77+
78+
private isTypescriptFile(filename: string): boolean {
79+
return filename.endsWith('.ts') && !filename.endsWith('index.ts');
80+
}
81+
82+
private readModule(filename: string, errors: string[]): NgModule | undefined {
83+
const fileContents = fs.readFileSync(filename);
84+
const regex: RegExp = /@NgModule\s*\(\s*(\{.+?\})\s*\)\s*export\s+class\s+(\w+)\s*\{/ims;
85+
var match = regex.exec(fileContents.toString());
86+
if (match !== null) {
87+
const moduleName = match[2];
88+
const moduleContents = this.convertNgModuleToParsableJson(match[1]);
89+
try {
90+
const module: NgModule = new NgModule(JSON.parse(moduleContents));
91+
module.filename = filename;
92+
module.moduleName = moduleName;
93+
module.moduleContents = moduleContents;
94+
return module;
95+
} catch (ex) {
96+
errors.push(`ModuleName: ${moduleName}\nFilename: ${filename}\nException: ${ex}\n${match[1]}\n`);
97+
return undefined;
98+
}
99+
}
100+
}
101+
102+
private convertNgModuleToParsableJson(moduleContents: string): string {
103+
moduleContents = moduleContents.replace(/\s*?\/\/.*$/igm, () => ''); // Remove comments
104+
moduleContents = moduleContents.replace(/\{\s*provide:\s*(.+?)\s*,\s*use\w+:\s*(.+?)\s*(,\s.*?)*\s*?\}[\s\}]*/igms, (str, provided, provider) => `"${provided.replace(/['"]/gms, '')} provided by ${provider.replace(/[\{\} ']/igms, '').replace(':', '=')}"`); // format providers ;
105+
moduleContents = moduleContents.replace("'", "\""); // Single quotes to double-quotes
106+
moduleContents = moduleContents.replace(/\s*?(\w+)[,\r\n]\s*?/igms, (str, identifier) => `"${identifier}",`); // quotes around array items
107+
moduleContents = moduleContents.replace(/(\w+\.\w+\(\))[,\r\n]/igms, (str, identifier) => `"${identifier}",`); // quotes around array items
108+
moduleContents = moduleContents.replace(/\[\s*([\w_\(\)\.]+?)\s*\]/igms, (str, identifier) => `"${identifier}"`); // quotes around array items
109+
moduleContents = moduleContents.replace(/(\w+\.\w+\().*?(\))[,\r\n]/igms, (str, identifier, idEnd) => `"${identifier}...${idEnd}",`); // quotes around array items
110+
moduleContents = moduleContents.replace(/("\s*),(\s+\])/igms, (str, quote, arrayEnd) => quote + arrayEnd); // Remove illegal empty array ending
111+
moduleContents = moduleContents.replace(/(\w+)\s*:/g, (str, identifier) => `"${identifier}":`); // quotes around identifiers
112+
return moduleContents;
113+
}
114+
115+
private generateModuleMarkdown(module: NgModule): string {
116+
let markdown = `## ${module.moduleName}\n\n`;
117+
markdown = markdown +
118+
'Filename: ' + module.filename + '\n\n' +
119+
'| Section | Classes, service, modules |\n' +
120+
'| ---- |:-----------|\n' +
121+
'| Declarations | ' + ArrayUtils.arrayToMarkdown(module.declarations) + ' |\n' +
122+
'| Imports | ' + ArrayUtils.arrayToMarkdown(module.imports) + ' |\n' +
123+
'| Exports | ' + ArrayUtils.arrayToMarkdown(module.exports) + ' |\n' +
124+
'| Bootstrap | ' + ArrayUtils.arrayToMarkdown(module.bootstrap) + ' |\n' +
125+
'| Providers | ' + ArrayUtils.arrayToMarkdown(module.providers) + ' |\n' +
126+
'| Entry components | ' + ArrayUtils.arrayToMarkdown(module.entryComponents) + ' |\n' +
127+
'\n';
128+
129+
return markdown;
130+
}
131+
132+
private showErrors(errors: string[]) {
133+
const angularToolsOutput = vscode.window.createOutputChannel(this.config.angularToolsOutputChannel);
134+
angularToolsOutput.clear();
135+
angularToolsOutput.appendLine(`Parsing of ${errors.length > 1 ? 'some' : 'one'} of the modules failed.\n`);
136+
angularToolsOutput.appendLine('Below is a list of the errors.');
137+
angularToolsOutput.appendLine(errors.join('\n'));
138+
angularToolsOutput.show();
139+
}
140+
}

src/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,7 @@ export class Config {
4646

4747
// ComponentHierarchyMarkdown
4848
public get componentHierarchyMarkdownFilename(): string { return this.getSetting<string>('componentHierarchyMarkdownFilename', 'ComponentHierarchy.md'); }
49+
50+
// ModulesMarkdown
51+
public get modulesToMarkdownFilename(): string {return this.getSetting<string>('modulesToMarkdownFilename', 'Modules.md'); }
4952
}

src/extension.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ComponentHierarchyDgml,
77
ComponentHierarchyMarkdown,
88
ListAllImports,
9+
ModulesToMarkdown,
910
PackageJsonToMarkdown,
1011
ProjectDirectoryStructure,
1112
ShowComponentHierarchy,
@@ -27,6 +28,12 @@ export function activate(context: vscode.ExtensionContext) {
2728
});
2829
context.subscriptions.push(projectDirectoryStructureDisposable);
2930

31+
const modulesToMarkdownDisposable = vscode.commands.registerCommand(`${cmdPrefix}.${ModulesToMarkdown.commandName}`, () => {
32+
const command = new ModulesToMarkdown();
33+
command.execute();
34+
});
35+
context.subscriptions.push(modulesToMarkdownDisposable);
36+
3037
const packageJsonToMarkdownDisposable = vscode.commands.registerCommand(`${cmdPrefix}.${PackageJsonToMarkdown.commandName}`, () => {
3138
const command = new PackageJsonToMarkdown();
3239
command.execute();

0 commit comments

Comments
 (0)