diff --git a/src/angular-cli.ts b/src/angular-cli.ts index 0a167d4..437bd86 100644 --- a/src/angular-cli.ts +++ b/src/angular-cli.ts @@ -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); @@ -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(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) { @@ -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); } } diff --git a/src/configuration-manager.ts b/src/configuration-manager.ts index 87f306c..ef66685 100644 --- a/src/configuration-manager.ts +++ b/src/configuration-manager.ts @@ -15,7 +15,7 @@ export class ConfigurationManager { private async readConfigFile(): Promise { 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; diff --git a/src/ng-module-utils.ts b/src/ng-module-utils.ts new file mode 100644 index 0000000..a575cf9 --- /dev/null +++ b/src/ng-module-utils.ts @@ -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 +}; diff --git a/test/extension.test.ts b/test/extension.test.ts index babd048..cd8f719 100644 --- a/test/extension.test.ts +++ b/test/extension.test.ts @@ -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); @@ -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 () => {