Skip to content

Commit

Permalink
feat: dynamic commands and config
Browse files Browse the repository at this point in the history
  • Loading branch information
Gorniv committed Feb 3, 2021
1 parent dfd719c commit 7142dee
Show file tree
Hide file tree
Showing 20 changed files with 347 additions and 654 deletions.
18 changes: 16 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vscode-flutter-files",
"displayName": "[FF] Flutter Files",
"description": "Quickly scaffold flutter bloc file templates",
"version": "2.2.0",
"version": "3.0.0",
"icon": "assets/icon.png",
"publisher": "gornivv",
"author": {
Expand All @@ -27,6 +27,11 @@
"type": "object",
"title": "Flutter Files menu option configuration",
"properties": {
"flutter-files.menu.dynamic": {
"type": "boolean",
"default": true,
"description": "Shows or hides the menu item."
},
"flutter-files.menu.bigpack": {
"type": "boolean",
"default": true,
Expand Down Expand Up @@ -90,6 +95,10 @@
}
},
"commands": [
{
"command": "extension.addFlutter2_Dynamic",
"title": "[FF] New with dynamic config"
},
{
"command": "extension.addFlutter2BigPack",
"title": "[FF] New Big Pack Bloc"
Expand Down Expand Up @@ -141,6 +150,11 @@
],
"menus": {
"explorer/context": [
{
"when": "config.flutter-files.menu.dynamic",
"command": "extension.addFlutter2_Dynamic",
"group": "0Flutter Pack"
},
{
"when": "config.flutter-files.menu.bigpack",
"command": "extension.addFlutter2BigPack",
Expand Down Expand Up @@ -209,7 +223,7 @@
"compile": "npm run templates && webpack --mode none",
"watch": "npm run templates && webpack --mode none",
"test-compile": "tsc -p ./",
"templates": "copyfiles templates/**/*.tmpl templates/**/*.tmpl ./out/",
"templates": "copyfiles templates/**/*.tmpl templates/**/*.tmpl ./out/ && copyfiles templates/**/*.json templates/**/*.json ./out/",
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "npm run compile && node ./node_modules/vscode/bin/test",
"format:check": "prettier --write --config ./.prettierrc --list-different \"src/**/*{.ts,.json}\"",
Expand Down
228 changes: 23 additions & 205 deletions src/angular-cli.ts
Original file line number Diff line number Diff line change
@@ -1,201 +1,34 @@
import { window, workspace, TextEditor, commands, Uri, WorkspaceEdit } from 'vscode';
import { window } from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
import { IConfig } from './models/config';
import { IPath } from './models/path';
import { FileContents } from './file-contents';
import { IFiles } from './models/file';
import { promisify } from './promisify';
import { toCamelCase, toUpperCase } from './formatting';
import { toUpperCase } from './formatting';
import { createFiles, createFolder } from './ioutil';
import { TemplateType } from './enums/template-type';
import { resources } from './resources';
import { ResourcesDynamic } from './resources';
import { ResourceType } from './enums/resource-type';
import { OptionType } from './enums/option-type';
import { ConfigElement } from './config-ext';

const fsWriteFile = promisify(fs.writeFile);
const fsReaddir = promisify(fs.readdir);
const fsStat = promisify(fs.stat);
const fsReadFile = promisify(fs.readFile);

export class AngularCli {
constructor(private readonly fc = new FileContents()) {
}

private async findModulePathRecursive(
dir: string,
fileList: any[] | string[],
optionalFilterFunction: { (name: string): boolean; (arg0: string): boolean },
) {
if (!fileList) {
console.error("Variable 'fileList' is undefined or NULL.");
return;
}
const files: string[] = await fsReaddir(dir);
for (const i in files) {
if (!files.hasOwnProperty(i)) {
continue;
}
const name = path.join(dir, files[i]);
const stat: fs.Stats = await fsStat(name);

if (stat.isDirectory()) {
await this.findModulePathRecursive(name, fileList, optionalFilterFunction);
} else {
if (optionalFilterFunction && optionalFilterFunction(name) !== true) {
continue;
}
fileList.push(name);
}
}
}

private addToImport(data: string, fileName: string, type: string, relativePath: string) {
const typeUpper = toUpperCase(type);
const fileNameUpper = toUpperCase(fileName);

const lastImportInx = data.lastIndexOf('import ');
const endOfLastImportInx = data.indexOf('\n', lastImportInx);
const fileLength = data.length;
return (
// tslint:disable-next-line:prefer-template
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(',')
// tslint:disable-next-line:ter-arrow-parens
.map((item) => item.trim())
// tslint:disable-next-line:ter-arrow-parens
.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: any = {};
ngModuleMap.forEach((value, key, map) => {
obj[key] = value;
});

const moduleStr = JSON.stringify(obj, null, 3)
.split('"')
.join('');
// tslint:disable-next-line:prefer-template
return before + `@NgModule(${moduleStr})` + after;
},
};
}

private addToArray(
ngModule: {
data?: string;
before?: string;
after?: string;
ngModuleMap: any;
toString?: () => string;
},
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]);
}
}

private getRelativePath(dst: string, src: string) {
const modulePath = path.parse(dst).dir;
// tslint:disable-next-line:prefer-template
return '.' + src.replace(modulePath, '').replace(/\\/g, '/');
}
constructor(private readonly fc = new FileContents()) {}

private async addDeclarationsToModule(
async generateResources(
name: ResourceType,
loc: IPath,
type: string,
module: string,
exports: boolean = false,
config: IConfig,
configExt: ConfigElement,
) {
const condition = (name: string) =>
module ? name.includes(`${module}.module.ts`) : name.includes('.module.ts');

const moduleFiles: never[] | [any] = [];
await this.findModulePathRecursive(loc.rootPath, moduleFiles, condition);

// at least one module is there
if (moduleFiles.length > 0) {
moduleFiles.sort((a: string, b: string) => path.dirname(a).length - path.dirname(b).length);

// find closest module
let [module] = moduleFiles;
let minDistance = Infinity;

for (const moduleFile of moduleFiles) {
const moduleDirPath = path.parse(moduleFile).dir;
const locPath = loc.dirPath.replace(loc.dirName, '');

const distance = Math.abs(locPath.length - moduleDirPath.length);
if (distance < minDistance) {
minDistance = distance;
module = moduleFile;
}
}

const data: string = await fsReadFile(module, 'utf8');

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

const ngModule = this.parseNgModule(content);

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

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

async generateResources(name: ResourceType, loc: IPath, config: IConfig) {
const resource = resources.get(name);
const resource = !!loc.command
? ResourcesDynamic.resourcesCommand(loc.command)
: ResourcesDynamic.resourcesDynamic(configExt).get(name);

loc.dirName = resource.hasOwnProperty('locDirName')
? resource.locDirName(loc, config)
Expand All @@ -204,64 +37,48 @@ export class AngularCli {
? resource.locDirPath(loc, config)
: loc.dirPath;

if (
resource.hasOwnProperty('declaration') &&
resource.declaration &&
!config.defaults[name].skipImport
) {
await this.addDeclarationsToModule(
loc,
resource.declaration,
config.defaults[name].module,
config.defaults[name].export,
);
}

// tslint:disable-next-line:ter-arrow-parens
if (resource.hasOwnProperty('createFolder') && resource.createFolder(config)) {
await createFolder(loc);
}

const filesASync: Promise<IFiles>[] = resource.files
// tslint:disable-next-line:ter-arrow-parens
.filter((file) => (file.condition ? file.condition(config, loc.params) : true))
// tslint:disable-next-line:ter-arrow-parens
.filter((file) => file.name(config) !== 'index.dart')
.filter((file) => file !== 'index')
.map(async (file) => {
try {
const fileName: string = file.name(config);
const fileName: string = `${file}.dart`;
const newName: string = path.join(
loc.dirPath,
fileName.startsWith('_') ? `${loc.fileName}${fileName}` : `${loc.fileName}_${fileName}`,
);
const result: IFiles = {
name: newName,
content: await this.fc.getTemplateContent(file.type, config, loc.fileName, loc.params, loc),
content: await this.fc.getTemplateContent(file, config, loc.fileName, loc),
};
return result;
} catch (ex) {
console.log(ex);
await window.showErrorMessage(`Error: ${ex}`);
}
});

const files = await Promise.all(filesASync);

let files = await Promise.all(filesASync);
files = files.filter((c) => c.content != '');
await createFiles(loc, files);

const filesIndex: Promise<IFiles>[] = resource.files
// tslint:disable-next-line:ter-arrow-parens
.filter((file) => (file.condition ? file.condition(config, loc.params) : true))
// .filter((file) => (file.condition ? file.condition(config, []) : true))
// tslint:disable-next-line:ter-arrow-parens
.filter((file) => file.name(config) === 'index.dart')
.filter((file) => file === 'index')
.map(async (file) => {
try {
const fileName: string = file.name(config);
const fileName: string = `${file}.dart`;
const files: string[] = await fsReaddir(loc.dirPath);
let contentStr = '';
// tslint:disable-next-line:ter-arrow-parens
for (const file of files.filter((c) => c.toLowerCase().includes('.dart'))) {
if (file === 'index.dart') {
if (file === 'index') {
continue;
}
contentStr += `export '${file}';\r\n`;
Expand All @@ -276,7 +93,8 @@ export class AngularCli {
await window.showErrorMessage(`Error: ${ex}`);
}
});
const indexFiles = await Promise.all(filesIndex);
let indexFiles = await Promise.all(filesIndex);
indexFiles = indexFiles.filter((c) => c.content != '');
await createFiles(loc, indexFiles);
}
}
1 change: 1 addition & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ICommand } from './models/command';
import { CommandType } from './enums/command-type';

export const commandsMap = new Map<CommandType, ICommand>([
[CommandType.Dynamic, { fileName: 'you_awesome', resource: ResourceType.Dynamic }],
[CommandType.BigPack, { fileName: 'you_awesome', resource: ResourceType.BigPack }],
[CommandType.SmallPack, { fileName: 'you_awesome', resource: ResourceType.SmallPack }],
[CommandType.Bloc, { fileName: 'you_awesome', resource: ResourceType.Bloc }],
Expand Down
17 changes: 17 additions & 0 deletions src/config-ext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

export interface ConfigExt {
name: string;
configs: ConfigElement[];
}

export interface ConfigElement {
name: string;
commands: Command[];
}

export interface Command {
name: string;
key?: string;
templates: string[];
files: string[];
}
Loading

0 comments on commit 7142dee

Please sign in to comment.