Skip to content

Commit

Permalink
New ngmodule parser closes #50
Browse files Browse the repository at this point in the history
  • Loading branch information
ialexivy committed Aug 8, 2020
1 parent 142fc69 commit 503b741
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 78 deletions.
87 changes: 13 additions & 74 deletions src/angular-cli.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { window, workspace, TextEditor, commands, Uri, WorkspaceEdit } from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
import { IConfig } from './models/config';
import { IPath } from './models/path';
import { ResourceType } from './enums/resource-type';
import { FileContents } from './file-contents';
import { toUpperCase } from './formatting';
import { createFiles, createFolder } from './ioutil';
import { IConfig } from './models/config';
import { IFiles } from './models/file';
import { IPath } from './models/path';
import { addToImport, addToType } from './ng-module-utils';
import { promisify } from './promisify';
import { toCamelCase, toUpperCase } from './formatting';
import { createFiles, createFolder } from './ioutil';
import { TemplateType } from './enums/template-type';
import { resources } from './resources';
import { ResourceType } from './enums/resource-type';
import { OptionType } from './enums/option-type';

const fsWriteFile = promisify(fs.writeFile);
const fsReaddir = promisify(fs.readdir);
Expand Down Expand Up @@ -53,69 +51,12 @@ export class AngularCli {
const typeUpper = toUpperCase(type);
const fileNameUpper = toUpperCase(fileName);

const lastImportInx = data.lastIndexOf('import ');
const endOfLastImportInx = data.indexOf('\n', lastImportInx);
const fileLength = data.length;
return data.substring(0, endOfLastImportInx) + `\nimport { ${fileNameUpper}${typeUpper} } from '${relativePath}/${fileName}.${type}';` + data.substring(endOfLastImportInx, fileLength);
}


private parseNgModule(data: string) {
const startPattern = '@NgModule({';
const endPattern = '})';
const startIndex = data.indexOf(startPattern) + startPattern.length;
const endIndex = data.indexOf(endPattern, startIndex);
const ngModuleStr = data
.substring(startIndex, endIndex)
.replace('{', '')
.replace('}', '')
.split(' ')
.join('');

const before = data.substring(0, startIndex - startPattern.length);
const after = data.substring(endIndex + endPattern.length, data.length);

const ngModuleTokens = ngModuleStr.split('],');

const ngModuleTokenPairs = ngModuleTokens.map((t) => {
const [key, val] = t.split(':');

const values = val.replace('[', '')
.replace(']', '')
.split(',')
.map(item => item.trim())
.filter(item => item !== '');

return [key.trim(), values] as [string, string[]];
});

const ngModuleMap = new Map<string, string[]>(ngModuleTokenPairs);

return {
data,
before,
after,
ngModuleMap,
toString: () => {
const obj = {};
ngModuleMap.forEach((value, key, map) => {
obj[key] = value;
});

const moduleStr = JSON.stringify(obj, null, 3).split(`"`).join('');
return before + `@NgModule(${moduleStr})` + after;
},
};
return addToImport(data, `import { ${fileNameUpper}${typeUpper} } from '${relativePath}/${fileName}.${type}';`);
}

private addToArray(ngModule, fileName: string, type: string, prop: string) {
private addToArray(content, fileName: string, type: string, prop: string) {
const item = `${toUpperCase(fileName)}${toUpperCase(type)}`;
if (ngModule.ngModuleMap.has(prop)) {
const items = ngModule.ngModuleMap.get(prop);
items.push(item);
} else {
ngModule.ngModuleMap.set(prop, [item]);
}
return addToType(content, prop, item);
}

private getRelativePath(dst: string, src: string) {
Expand Down Expand Up @@ -152,16 +93,14 @@ export class AngularCli {

// relativePath
const relativePath = this.getRelativePath(module, loc.dirPath);
const content = this.addToImport(data, loc.fileName, type, relativePath);

const ngModule = this.parseNgModule(content);
let content = this.addToImport(data, loc.fileName, type, relativePath);

this.addToArray(ngModule, loc.fileName, type, 'declarations');
content = this.addToArray(content, loc.fileName, type, 'declarations');
if (exports) {
this.addToArray(ngModule, loc.fileName, type, 'exports');
content = this.addToArray(content, loc.fileName, type, 'exports');
}

await fsWriteFile(module, ngModule.toString());
await fsWriteFile(module, content);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/configuration-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class ConfigurationManager {
private async readConfigFile(): Promise<Object> {
const files = await workspace.findFiles('{**/.angular-cli.json,**/angular.json}', '', 1);
const [ws] = workspace.workspaceFolders;
this.currentRootPath = ws?.uri?.path;
this.currentRootPath = ws && ws.uri && ws.uri.path;
if (files.length > 0) {
const [{ 'fsPath': filePath }] = files;

Expand Down
80 changes: 80 additions & 0 deletions src/ng-module-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const tokenizer = (st: string) => {
let start = 0;
let pos = 0;
const arr = [];
while (pos != st.length) {
if (st[pos] == ",") {
while (st[pos] != "\n" && pos != st.length) {
pos++;
}

arr.push(st.substring(start, pos));
start = pos;
}
pos++;
}

if (start != pos) {
arr.push(st.substring(start, pos));
}

return arr.filter(x => x.trim().length > 0);
}

const addToType = (data: string, type: string, str: string) => {
const stInx = data.indexOf(`@NgModule(`);
const typeIndex = data.indexOf(type, stInx);

if (typeIndex == -1) {
const startIndex = data.lastIndexOf("]");
const tmpl = `
${type}: [
"${str}"
]`;

return data.substring(0, startIndex + 1) + "," + tmpl + data.substring(startIndex + 1, data.length);
}

const startIndex = data.indexOf("[", typeIndex);
const endIndex = data.indexOf("]", startIndex);

const area = data.substring(startIndex + 1, endIndex);
const lines = tokenizer(area);

let last = lines.length > 0 ? lines.pop().trimEnd() : "";
let commentsIndex = last.indexOf("//");
let injectIndex = (commentsIndex == -1) ? last.length - 1 : commentsIndex - 1;

if (commentsIndex != -1) {
while (last[injectIndex] == " ") {
injectIndex--;
}
}

if (last[injectIndex] != "," && last.length > 0) {
last = last.substring(0, injectIndex + 1) + "," + last.substring(injectIndex + 1, last.length);
}
// add last line
lines.push(last);

// inject new token
lines.push("\n " + str);

const newArea = "[\t" + lines.join('') + "\n ]";

const newStr = data.substring(0, startIndex) + newArea + data.substring(endIndex + 1, data.length);

return newStr;
}

const addToImport = (data: string, str: string) => {
const lastImportInx = data.lastIndexOf('import ');
const endOfLastImportInx = data.indexOf('\n', lastImportInx);
const fileLength = data.length;
return data.substring(0, endOfLastImportInx) + `\n${str}` + data.substring(endOfLastImportInx, fileLength);
}

export {
addToType,
addToImport
};
5 changes: 2 additions & 3 deletions test/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe('Extension Tests:', () => {
// tslint:disable-next-line:max-line-length
const beforeModuleContent = `import { BrowserModule } from \'@angular/platform-browser\';\nimport { NgModule } from \'@angular/core\';\nimport { RouterModule, Routes } from \'@angular/router\';\n\nimport { AppComponent } from \'./app.component\';\nimport { AdminSettingsComponent } from \'./admin-settings/admin-settings.component\';\nimport { AdminLandingComponent } from \'./admin-landing/admin-landing.component\';\nimport { AdminSettingsGradeMarksComponent } from \'./admin-settings-grade-marks/admin-settings-grade-marks.component\';\n\nconst appRoutes: Routes = [\n { path: \'settings/main\', component: AdminSettingsComponent },\n { path: \'settings/grade-marks\', component: AdminSettingsGradeMarksComponent },\n { path: \'\', component: AdminLandingComponent }\n];\n\n@NgModule({\n declarations: [\n AppComponent,\n AdminSettingsComponent,\n AdminLandingComponent,\n AdminSettingsGradeMarksComponent\n ],\n imports: [\n RouterModule.forRoot(appRoutes),\n BrowserModule\n ],\n providers: [\n RouterModule\n ],\n bootstrap: [\n AppComponent\n ]\n})\nexport class AppModule {}\n`;
// tslint:disable-next-line:max-line-length
const afterModuleContent = `import { BrowserModule } from \'@angular/platform-browser\';\nimport { NgModule } from \'@angular/core\';\nimport { RouterModule, Routes } from \'@angular/router\';\n\nimport { AppComponent } from \'./app.component\';\nimport { AdminSettingsComponent } from \'./admin-settings/admin-settings.component\';\nimport { AdminLandingComponent } from \'./admin-landing/admin-landing.component\';\nimport { AdminSettingsGradeMarksComponent } from \'./admin-settings-grade-marks/admin-settings-grade-marks.component\';\nimport { JeffTestComponent } from \'./JeffTest/JeffTest.component\';\n\nconst appRoutes: Routes = [\n { path: \'settings/main\', component: AdminSettingsComponent },\n { path: \'settings/grade-marks\', component: AdminSettingsGradeMarksComponent },\n { path: \'\', component: AdminLandingComponent }\n];\n\n@NgModule({\n declarations: [\n AppComponent,\n AdminSettingsComponent,\n AdminLandingComponent,\n AdminSettingsGradeMarksComponent,\n JeffTestComponent\n ],\n imports: [\n RouterModule.forRoot(appRoutes),\n BrowserModule\n ],\n providers: [\n RouterModule\n ],\n bootstrap: [\n AppComponent\n ]\n})\nexport class AppModule {}\n`;
const afterModuleContent = "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\n\nimport { AppComponent } from './app.component';\nimport { AdminSettingsComponent } from './admin-settings/admin-settings.component';\nimport { AdminLandingComponent } from './admin-landing/admin-landing.component';\nimport { AdminSettingsGradeMarksComponent } from './admin-settings-grade-marks/admin-settings-grade-marks.component';\nimport { JeffTestComponent } from './JeffTest/JeffTest.component';\n\nconst appRoutes: Routes = [\n { path: 'settings/main', component: AdminSettingsComponent },\n { path: 'settings/grade-marks', component: AdminSettingsGradeMarksComponent },\n { path: '', component: AdminLandingComponent }\n];\n\n@NgModule({\n declarations: [\t\n AppComponent,\n AdminSettingsComponent,\n AdminLandingComponent,\n AdminSettingsGradeMarksComponent,\n JeffTestComponent\n ],\n imports: [\n RouterModule.forRoot(appRoutes),\n BrowserModule\n ],\n providers: [\n RouterModule\n ],\n bootstrap: [\n AppComponent\n ]\n})\nexport class AppModule {}\n";
const moduleLocation = Object.assign({}, { fullPath: path.join(testPath, 'my-module'), fileName: 'my-module', dirName: '', dirPath: testPath, rootPath: __dirname, params: [] });
config.defaults.component.module = 'my-module';
await angularCli.generateResources(ResourceType.Module, moduleLocation, config);
Expand Down Expand Up @@ -441,13 +441,12 @@ describe('Extension Tests:', () => {
const files = getAllFiles(srcpath).filter(f => f.endsWith('.js'));
files.forEach((file) => {
const fileContent = fs.readFileSync(file, 'utf-8');
const lines = fileContent.split('\r\n').filter(f => f.includes('require('));
const lines = fileContent.split('\r\n').filter(f => f.includes('require(') && !f.includes('__esModule'));
const requireLines = lines.map(line => regex.exec(line)[1]);
requireLines.forEach((line) => {
expect(line).to.be.eql(line.toLocaleLowerCase());
});
});

});

it('Should contain templates', async () => {
Expand Down

0 comments on commit 503b741

Please sign in to comment.