commit 66fdc50c31ab62b13a66e48618541adf2eeb453c Author: Ismael Date: Mon Apr 7 11:32:08 2025 -0300 Commit inicial diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 0000000..e87886f --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,6 @@ +{ + "spec": [ + "test/*.ts" + ], + "recursive": true +} \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..37d1b60 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +enable-pre-post-scripts = true \ No newline at end of file diff --git a/.vscode-test.mjs b/.vscode-test.mjs new file mode 100644 index 0000000..b62ba25 --- /dev/null +++ b/.vscode-test.mjs @@ -0,0 +1,5 @@ +import { defineConfig } from '@vscode/test-cli'; + +export default defineConfig({ + files: 'out/test/**/*.test.js', +}); diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..d7a3ca1 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": ["dbaeumer.vscode-eslint", "connor4312.esbuild-problem-matchers", "ms-vscode.extension-test-runner"] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f9c36b3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "cwd": "${workspaceFolder}/testDirectory", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + // "${workspaceFolder}/testDirectory", + "--disable-extensions" + ], + "outFiles": [ + "${workspaceFolder}/dist/**/*.js" + ], + "preLaunchTask": "${defaultBuildTask}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5c5ac48 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "out": false, // set this to true to hide the "out" folder with the compiled JS files + "dist": false // set this to true to hide the "dist" folder with the compiled JS files + }, + "search.exclude": { + "out": true, // set this to false to include "out" folder in search results + "dist": true // set this to false to include "dist" folder in search results + }, + // Turn off tsc task auto detection since we have the necessary tasks as npm scripts + "typescript.tsc.autoDetect": "off" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..3cf99c3 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,64 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "label": "watch", + "dependsOn": [ + "npm: watch:tsc", + "npm: watch:esbuild" + ], + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "npm", + "script": "watch:esbuild", + "group": "build", + "problemMatcher": "$esbuild-watch", + "isBackground": true, + "label": "npm: watch:esbuild", + "presentation": { + "group": "watch", + "reveal": "never" + } + }, + { + "type": "npm", + "script": "watch:tsc", + "group": "build", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "label": "npm: watch:tsc", + "presentation": { + "group": "watch", + "reveal": "never" + } + }, + { + "type": "npm", + "script": "watch-tests", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never", + "group": "watchers" + }, + "group": "build" + }, + { + "label": "tasks: watch-tests", + "dependsOn": [ + "npm: watch", + "npm: watch-tests" + ], + "problemMatcher": [] + } + ] +} diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..159277f --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,14 @@ +.vscode/** +.vscode-test/** +out/** +node_modules/** +src/** +.gitignore +.yarnrc +esbuild.js +vsc-extension-quickstart.md +**/tsconfig.json +**/eslint.config.mjs +**/*.map +**/*.ts +**/.vscode-test.* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ba2b707 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Change Log + +All notable changes to the "r3utils" extension will be documented in this file. + +Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. + +## [Unreleased] + +- Initial release \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/esbuild.js b/esbuild.js new file mode 100644 index 0000000..2616396 --- /dev/null +++ b/esbuild.js @@ -0,0 +1,57 @@ +const esbuild = require("esbuild"); + +const production = process.argv.includes('--production'); +const watch = process.argv.includes('--watch'); + +/** + * @type {import('esbuild').Plugin} + */ +const esbuildProblemMatcherPlugin = { + name: 'esbuild-problem-matcher', + + setup(build) { + build.onStart(() => { + console.log('[watch] build started'); + }); + build.onEnd((result) => { + result.errors.forEach(({ text, location }) => { + console.error(`✘ [ERROR] ${text}`); + console.error(` ${location.file}:${location.line}:${location.column}:`); + }); + console.log('[watch] build finished'); + }); + }, +}; + +async function main() { + const ctx = await esbuild.context({ + entryPoints: [ + 'src/extension.ts' + ], + bundle: true, + format: 'cjs', + minify: production, + sourcemap: !production, + sourcesContent: false, + platform: 'node', + outfile: 'dist/extension.js', + external: ['vscode'], + logLevel: 'silent', + plugins: [ + /* add to the end of plugins array */ + esbuildProblemMatcherPlugin, + ], + // ignore: ['./testDirectory/**'], + }); + if (watch) { + await ctx.watch(); + } else { + await ctx.rebuild(); + await ctx.dispose(); + } +} + +main().catch(e => { + console.error(e); + process.exit(1); +}); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..d5c0b53 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,28 @@ +import typescriptEslint from "@typescript-eslint/eslint-plugin"; +import tsParser from "@typescript-eslint/parser"; + +export default [{ + files: ["**/*.ts"], +}, { + plugins: { + "@typescript-eslint": typescriptEslint, + }, + + languageOptions: { + parser: tsParser, + ecmaVersion: 2022, + sourceType: "module", + }, + + rules: { + "@typescript-eslint/naming-convention": ["warn", { + selector: "import", + format: ["camelCase", "PascalCase"], + }], + + curly: "warn", + eqeqeq: "warn", + "no-throw-literal": "warn", + semi: "warn", + }, +}]; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..8054772 --- /dev/null +++ b/package.json @@ -0,0 +1,125 @@ +{ + "name": "r3utils", + "displayName": "R3utils", + "description": "Utilities for R3 systems", + "version": "0.0.1", + "publisher": "R3", + "engines": { + "vscode": "^1.93.0" + }, + "categories": [ + "Other" + ], + "activationEvents": [ + "onView:r3Utils.rotas", + "onView:r3Utils.modulos", + "onCommand:r3Utils.rotas.openFile", + "onCommand:r3Utils.modulos.openModuleFolder" + ], + "main": "./dist/extension.js", + "contributes": { + "viewsContainers": { + "activitybar": [ + { + "id": "r3utils_structure", + "title": "R3utils estrutura", + "icon": "resources/icone_R3.jpg" + }, + { + "id": "r3utils_commit", + "title": "R3utils Commit Builder", + "icon": "resources/icone_R3.jpg" + } + ] + }, + "views": { + "r3utils_structure": [ + { + "id": "r3Utils.rotas", + "name": "Rotas" + }, + { + "id": "r3Utils.modulos", + "name": "Módulos" + } + ], + "r3utils_commit": [ + { + "id": "r3Utils.commit", + "name": "" + } + ] + }, + "commands": [ + { + "command": "r3utils.openFile", + "title": "Abrir arquivo de rota" + }, + { + "command": "r3utils.helloWorld", + "title": "Hello World" + }, + { + "command": "r3utils.caminhoModulos", + "title": "R3Utils: Caminho dos módulos" + }, + { + "command": "r3utils.modulos.openModuleFolder", + "title": "Abrir Pasta do Módulo" + }, + { + "command": "r3utils.helloWorld", + "title": "Hello World" + }, + { + "command": "r3utils.caminhoModulos", + "title": "R3Utils: Caminho dos módulos" + } + ], + "configuration": { + "title": "R3 Utils", + "properties": { + "r3Utils.caminhoModulos": { + "type": "string", + "description": "Caminho que se encontram os módulos" + }, + "r3Utils.caminhoModulosTeste": { + "type": "string", + "description": "Caminho teste que se encontram os módulos" + } + } + } + }, + "scripts": { + "vsix-package": "pnpm vsce package --no-dependencies", + "vscode:prepublish": "pnpm run package", + "compile": "pnpm run check-types && pnpm run lint && node esbuild.js", + "watch": "npm-run-all -p watch:*", + "watch:esbuild": "node esbuild.js --watch", + "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", + "package": "pnpm run check-types && pnpm run lint && node esbuild.js --production", + "compile-tests": "tsc -p . --outDir out", + "watch-tests": "tsc -p . -w --outDir out", + "pretest": "pnpm run compile-tests && pnpm run compile && pnpm run lint", + "check-types": "tsc --noEmit", + "lint": "eslint src", + "test": "vscode-test" + }, + "devDependencies": { + "@types/mocha": "^10.0.7", + "@types/node": "20.x", + "@types/vscode": "^1.93.0", + "@typescript-eslint/eslint-plugin": "^8.3.0", + "@typescript-eslint/parser": "^8.3.0", + "@vscode/test-cli": "^0.0.10", + "@vscode/test-electron": "^2.4.1", + "esbuild": "^0.23.1", + "eslint": "^9.9.1", + "npm-run-all": "^4.1.5", + "typescript": "^5.5.4", + "vsce": "^2.15.0" + }, + "dependencies": { + "chokidar": "^3.6.0" + } +} \ No newline at end of file diff --git a/resources/icone_R3.jpg b/resources/icone_R3.jpg new file mode 100644 index 0000000..4212a82 Binary files /dev/null and b/resources/icone_R3.jpg differ diff --git a/src/extension.ts b/src/extension.ts new file mode 100644 index 0000000..712d7e6 --- /dev/null +++ b/src/extension.ts @@ -0,0 +1,75 @@ +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import path from 'path'; +import * as vscode from 'vscode'; +import { ModulosProvider } from './providers/modulosProvider'; +import { RotasProvider } from './providers/rotasProvider'; +import { ModuloPath } from './system/directories/moduloPath'; +import { getR3UtilsOutputChannel } from './vscode/outputChannels/r3utils.outputchannel'; + +// This method is called when your extension is activated +// Your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + + // registrar providers + let rotasProvider = new RotasProvider(); + vscode.window.registerTreeDataProvider('r3Utils.rotas', rotasProvider); + + context.subscriptions.push(rotasProvider); + + const openFileCommand = vscode.commands.registerCommand('r3utils.rotas.openFile', (filePath: string, range: vscode.Range) => { + vscode.workspace.openTextDocument(filePath).then(doc => { + vscode.window.showTextDocument(doc).then(editor => { + const selection = new vscode.Selection(range.start, range.end); + editor.selection = selection; + editor.revealRange(range, vscode.TextEditorRevealType.InCenter); + }); + }); + }); + + context.subscriptions.push(openFileCommand); + + // rotasProvider = undefined as unknown as RotasProvider; + + + const moduloPathExists = vscode.workspace.getConfiguration("r3Utils").has("caminhoModulos"); + if (moduloPathExists) { + + const moduloPath = new ModuloPath(vscode.workspace.getConfiguration("r3Utils").get("caminhoModulos")!); + const modulosProvider = new ModulosProvider(moduloPath); + vscode.window.registerTreeDataProvider('r3Utils.modulos', modulosProvider); + // Registrar o comando para abrir Pasta do Módulo + const openModuleFolderCommand = vscode.commands.registerCommand('r3utils.modulos.openModuleFolder', async (modulePath: string) => { + const uri = vscode.Uri.file(modulePath); + console.log("Abrindo pasta do módulo:", uri); + await vscode.commands.executeCommand('revealInExplorer', uri); + await vscode.commands.executeCommand('workbench.view.explorer'); + }); + + // const openModuleFolderCommand = vscode.commands.registerCommand('r3utils.modulos.openModuleFolder', (modulePath: string) => { + // vscode.commands.executeCommand('revealInExplorer', vscode.Uri.file(modulePath)); + // }); + context.subscriptions.push(openModuleFolderCommand); + } + + // Mostra o caminho dos módulos + const caminhoModulo = vscode.commands.registerCommand('r3utils.caminhoModulos', () => { + const conf = vscode.workspace.getConfiguration("r3Utils").get("caminhoModulos"); + + const workspaceFolder = vscode.workspace.workspaceFolders?.[0].uri.fsPath; + if (workspaceFolder) { + vscode.window.showInformationMessage(`Caminho dos módulos: ${path.join(workspaceFolder, conf as string)}`); + } else { + vscode.window.showInformationMessage(`Nenhuma pasta de trabalho aberta`); + } + }); + // Use the console to output diagnostic information (console.log) and errors (console.error) + // This line of code will only be executed once when your extension is activated + // vscode.window.('Congratulations, your extension "r3utils" is now active!'); + getR3UtilsOutputChannel().appendLine("Extension r3Utils ativada!"); + + context.subscriptions.push(caminhoModulo); +} + +// This method is called when your extension is deactivated +export function deactivate() { } diff --git a/src/models/moduloModel.ts b/src/models/moduloModel.ts new file mode 100644 index 0000000..f753b14 --- /dev/null +++ b/src/models/moduloModel.ts @@ -0,0 +1,21 @@ +import * as vscode from 'vscode'; +export class ModuloItem extends vscode.TreeItem { + constructor( + public readonly label: string, + public readonly collapsibleState: vscode.TreeItemCollapsibleState, + public readonly modulePath: string + ) { + super(label, collapsibleState); + + this.command = { + command: 'r3utils.modulos.openModuleFolder', + title: 'Abrir Pasta do Módulo', + arguments: [modulePath] + }; + } + +} + +export interface ModuloModel { + getDirectories(): Promise +} \ No newline at end of file diff --git a/src/models/rotaModel.ts b/src/models/rotaModel.ts new file mode 100644 index 0000000..48c31c6 --- /dev/null +++ b/src/models/rotaModel.ts @@ -0,0 +1,47 @@ +import * as vscode from 'vscode'; +export class RotaItem extends vscode.TreeItem { + constructor( + public readonly rotaInfo: RotaInfo, + public readonly collapsibleState: vscode.TreeItemCollapsibleState, + public readonly children?: RotaItem[], + public readonly command?: vscode.Command + ) { + super('', collapsibleState); + + if (children) { + this.label = rotaInfo.modulo; // Nome do módulo + // this.command = { + // command: 'rotas.openModuleFolder', + // title: 'Abrir Pasta do Módulo', + // arguments: [rotaInfo.modulePath] + // }; + } else { + this.label = `${rotaInfo.line} - ${rotaInfo.metodo} : ${rotaInfo.nome}`; // Método e nome da rota + + } + + // if (rotaInfo.metodo) { + // this.label = `${rotaInfo.metodo} ${rotaInfo.nome}`; + // } else { + // this.label = rotaInfo.modulo; + // } + + this.tooltip = `Rota: ${rotaInfo.nome}\nMétodo: ${rotaInfo.metodo}\nMódulo: ${rotaInfo.modulo}`; + } +} + +export class RotaInfo { + constructor( + public readonly nome: string, + public readonly modulo: string, + public readonly line: number, + public readonly metodo?: string, + public readonly filePath?: string, + public readonly range?: vscode.Range, + public readonly modulePath?: string + ) { } + + toString(): string { + return `${this.line} - ${this.metodo ? this.metodo : ''} : ${this.nome} (${this.modulo})`; + } +} diff --git a/src/providers/modulosProvider.ts b/src/providers/modulosProvider.ts new file mode 100644 index 0000000..6e21f36 --- /dev/null +++ b/src/providers/modulosProvider.ts @@ -0,0 +1,94 @@ +import chokidar from "chokidar"; +import path from "path"; +import * as vscode from "vscode"; +import { ModuloItem } from "../models/moduloModel"; +import { ModuloPath } from "../system/directories/moduloPath"; +export class ModulosProvider implements vscode.TreeDataProvider { + + constructor(private moduloPath: ModuloPath) { + this.moduloPath.watchDirectories(() => { + this.refresh(); + }); + } + + private watcher = chokidar.watch(vscode.workspace.getConfiguration("r3Utils").get("caminhoModulos") as string, { depth: 99 }); + + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event | undefined = + this._onDidChangeTreeData.event; + getTreeItem(element: ModuloItem): vscode.TreeItem | Thenable { + return element; + } + + refresh(): void { + this._onDidChangeTreeData.fire(); + } + + async getChildren(element?: ModuloItem | undefined): Promise { + // if (element) { + // return Promise.resolve([]); + // } else { + // let directories: string[] = []; + // await new ModuloPath( + // path.join(vscode.workspace.workspaceFolders![0].uri.fsPath, + // vscode.workspace.getConfiguration("r3Utils").get("caminhoModulosTeste") as string + // ) + // ).getDirectories().then(dirs => { + // directories = dirs; + // }); + + // const modulosItems: ModuloItem[] = []; + // const modulosPath = path.join( + // vscode.workspace.workspaceFolders![0].uri.fsPath, + // vscode.workspace.getConfiguration("r3Utils").get("caminhoModulosTeste") as string + // ); + + // directories.forEach(directory => { + // const modulePath = path.join(modulosPath, directory); + // modulosItems.push(new ModuloItem(directory, vscode.TreeItemCollapsibleState.None, modulePath)); + // }); + + // return Promise.resolve(modulosItems); + // } + + if (element) { + return []; + } else { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders) { + return []; + } + + const configPath = vscode.workspace.getConfiguration("r3Utils").get("caminhoModulosTeste") as string; + const modulosPath = path.join(workspaceFolders[0].uri.fsPath, configPath); + + let directories: string[] = []; + try { + directories = await vscode.workspace.fs.readDirectory(vscode.Uri.file(modulosPath)) + .then(files => files + .filter(file => file[1] === vscode.FileType.Directory) + .map(file => file[0])); + } catch (error) { + console.error("Erro ao ler os módulos:", error); + return []; + } + + const modulosItems: ModuloItem[] = []; + + directories.forEach(directory => { + const modulePath = path.join(modulosPath, directory); + modulosItems.push(new ModuloItem(directory, vscode.TreeItemCollapsibleState.None, modulePath)); + }); + + return modulosItems; + } + } + getParent?(element: ModuloItem): vscode.ProviderResult { + throw new Error("Method not implemented."); + } + resolveTreeItem?(item: vscode.TreeItem, element: ModuloItem, token: vscode.CancellationToken): vscode.ProviderResult { + throw new Error("Method not implemented."); + } + + +} \ No newline at end of file diff --git a/src/providers/rotasProvider.ts b/src/providers/rotasProvider.ts new file mode 100644 index 0000000..014e25e --- /dev/null +++ b/src/providers/rotasProvider.ts @@ -0,0 +1,213 @@ +import * as vscode from "vscode"; +import { RotaInfo, RotaItem } from "../models/rotaModel"; +import * as path from "path"; +import * as fs from "fs"; +import type { FSWatcher } from "chokidar"; +import chokidar from "chokidar"; +import { getR3UtilsOutputChannel } from "../vscode/outputChannels/r3utils.outputchannel"; +export class RotasProvider implements vscode.TreeDataProvider { + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + private readonly outputChannel = getR3UtilsOutputChannel(); + // private watchers: FSWatcher[] = []; + private watcher!: FSWatcher; + constructor() { + this.initializeWatchers(); + } + + private async initializeWatchers() { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders) { + return; + } + + const configPath = vscode.workspace.getConfiguration("r3Utils").get("caminhoModulos") as string; + const modulesPath = path.join(workspaceFolders[0].uri.fsPath, configPath); + + this.watcher = chokidar.watch(modulesPath, { + persistent: true, + ignoreInitial: false, + depth: 10, // Profundidade para monitorar subdiretórios + ignored: ['**/node_modules/**', '**/.git/**'] + }); + + this.watcher + .on('add', filePath => this.onFileChange(filePath, 'adicionado')) + .on('change', filePath => this.onFileChange(filePath, 'modificado')) + .on('unlink', filePath => this.onFileChange(filePath, 'removido')) + .on('addDir', dirPath => this.onDirChange(dirPath, 'adicionado')) + .on('unlinkDir', dirPath => this.onDirChange(dirPath, 'removido')); + + } + + private onFileChange(filePath: string, changeType: string) { + if (filePath.endsWith('.routes.ts')) { + const fileName = path.basename(filePath); + + this.createChannelMessage(` + Módulo: ${path.basename(path.dirname(filePath))}\n + Arquivo de rota ${changeType}: ${fileName}`); + this.refresh(); + } + } + + private onDirChange(dirPath: string, changeType: string) { + if (dirPath.includes(path.join('infra', 'data', 'routes'))) { + this.createChannelMessage(`Diretório de rotas ${changeType}: ${dirPath}`); + this.refresh(); + } + } + + refresh(): void { + this._onDidChangeTreeData.fire(); + } + getTreeItem(element: RotaItem): vscode.TreeItem | Thenable { + return element; + } + + private async findRouteFiles(dir: string): Promise { + let results: string[] = []; + const list = await fs.promises.readdir(dir, { withFileTypes: true }); + for (const file of list) { + const filePath = path.join(dir, file.name); + if (file.isDirectory()) { + results = results.concat(await this.findRouteFiles(filePath)); + } else if (file.isFile() && file.name.endsWith('.routes.ts')) { + results.push(filePath); + } + } + return results; + } + async getChildren(element?: RotaItem | undefined): Promise { + + if (element && element.children) { + return Promise.resolve(element.children); + + } else { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders) { + return []; + } + + const configPath = vscode.workspace.getConfiguration("r3Utils").get("caminhoModulos") as string; + const modulesPath = path.join(workspaceFolders[0].uri.fsPath, configPath); + // const modules = fs.readdirSync(modulesPath).filter(dir => fs.statSync(path.join(modulesPath, dir)).isDirectory()); + + let modules: string[] = []; + try { + const dirs = await fs.promises.readdir(modulesPath, { withFileTypes: true }); + modules = dirs.filter(dir => dir.isDirectory()).map(dir => dir.name); + } catch (error) { + console.error("Erro ao ler os módulos:", error); + return []; + } + + const rotas: RotaItem[] = []; + + for (const modulo of modules) { + const modulePath = path.join(modulesPath, modulo); + // caminho para o diretório de rotas do módulo + // const routesPath = path.join(modulesPath, modulo, "infra", "data", "routes"); + + try { + // obtendo os nomes dos arquivos do diretório de rotas + const files = await this.findRouteFiles(modulePath); + const rotaItems: RotaItem[] = []; + + for (const filePath of files) { + // if (file.endsWith('.routes.ts')) { + // const filePath = path.join(routesPath, file); + const content = await fs.promises.readFile(filePath, 'utf-8'); + + // Expressão regular atualizada + // const regex = /\.(get|post|put|delete)\(['"`](.*?)['"`]/g; + // const regex = /(?:\.(get|post|put|delete)\(['"`](.*?)['"`]\))|(?:server\.(get|post|put|delete)\s*\(\s*\{\s*routeName\s*:\s*['"`](.*?)['"`])/g; + // const regex = /(?:\.(get|post|put|delete)\(['"`](.*?)['"`]\))|(?:server\.(get|post|put|delete)\s*\(\s*\{\s*routeName\s*:\s*['"`](.*?)['"`])/g; + const regex = /(?:\.(get|post|put|delete)\(\s*['"`]([^'"`]+)['"`]\s*\))|(?:server\.(get|post|put|delete)\s*\(\s*\{[\s\S]*?routeName\s*:\s*['"`]([^'"`]+)['"`])/g; + let match; + + while ((match = regex.exec(content)) !== null) { + let method = ""; + let route = ""; + + if (match[1]) { + // Padrão: .get('/rota') + method = match[1].toUpperCase(); + route = match[2]; + } else if (match[3]) { + // Padrão: server.get({ routeName: '/rota', ... }) + method = match[3].toUpperCase(); + route = match[4]; + } + + // Calcula a posição dentro do arquivo + const lineNumber = content.substr(0, match.index).split('\n').length; + const rotaRange = new vscode.Range( + new vscode.Position(lineNumber, 0), + new vscode.Position(lineNumber, 0) + ); + + const rotaInfo = new RotaInfo(route, modulo, lineNumber, method, filePath, rotaRange); + + const rotaItem = new RotaItem( + rotaInfo, + vscode.TreeItemCollapsibleState.None, + undefined, + { + command: 'r3utils.rotas.openFile', + title: 'Abrir Arquivo', + arguments: [filePath, rotaRange] + } + ); + + rotaItems.push(rotaItem); + + } + } + + if (rotaItems.length > 0) { + rotas.push(new RotaItem( + new RotaInfo('', modulo, 0, undefined, undefined, undefined, modulePath), + vscode.TreeItemCollapsibleState.Collapsed, + rotaItems + )); + } + + } + catch (error) { + continue; + } + } + + return rotas; + + } + } + getParent?(element: RotaItem): vscode.ProviderResult { + throw new Error("Method not implemented."); + } + resolveTreeItem?(item: vscode.TreeItem, element: RotaItem, token: vscode.CancellationToken): vscode.ProviderResult { + throw new Error("Method not implemented."); + } + + private createChannelMessage(message: string): void { + this.outputChannel.appendLine(`${new Date().toLocaleTimeString([], { hour12: false })} ===== Rotas =====`); + if (message.includes('\n')) { + const trimmedMessage = message.split('\n') + .map(line => line.trim()) + .join('\n'); + this.outputChannel.appendLine(trimmedMessage); + } else { + this.outputChannel.appendLine(message.trim()); + } + this.outputChannel.appendLine('======================================'); + } + + + + dispose() { + this.watcher.close(); + } + +} \ No newline at end of file diff --git a/src/system/directories/moduloPath.ts b/src/system/directories/moduloPath.ts new file mode 100644 index 0000000..5138f02 --- /dev/null +++ b/src/system/directories/moduloPath.ts @@ -0,0 +1,41 @@ +import chokidar from "chokidar"; +import * as vscode from 'vscode'; +import type { ModuloModel } from "../../models/moduloModel"; + +export class ModuloPath implements ModuloModel { + private directoriesWatcher: chokidar.FSWatcher | undefined; + + constructor( + readonly path: string + ) { } + + watchDirectories(onChange: () => void): void { + if (this.directoriesWatcher) { + this.directoriesWatcher.close(); + } + + this.directoriesWatcher = chokidar.watch(this.path, { persistent: true }); + + this.directoriesWatcher.on("addDir", () => { + onChange(); + }); + + this.directoriesWatcher.on("unlinkDir", () => { + onChange(); + }); + } + + async getDirectories(): Promise { + const directories: string[] = []; + + const items = await vscode.workspace.fs.readDirectory(vscode.Uri.file(this.path)); + + for (const [name, type] of items) { + if (type === vscode.FileType.Directory) { + directories.push(name); + } + } + + return directories; + } +} \ No newline at end of file diff --git a/src/test/extension.test.ts b/src/test/extension.test.ts new file mode 100644 index 0000000..5320dae --- /dev/null +++ b/src/test/extension.test.ts @@ -0,0 +1,39 @@ +import * as assert from 'assert'; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as vscode from 'vscode'; +import { ModulosProvider } from '../providers/modulosProvider'; +import { ModuloPath } from '../system/directories/moduloPath'; +// import * as myExtension from '../../extension'; + +suite('Extension Test Suite', () => { + vscode.window.showInformationMessage('Start all tests.'); + + test('Sample test', () => { + assert.strictEqual(-1, [1, 2, 3].indexOf(5)); + assert.strictEqual(-1, [1, 2, 3].indexOf(0)); + }); + + test("Deve ter propriedades configurações com valor padrão para o camiho de módulos", () => { + const expectedPathValue = "src/modules"; + const vscodePathValue = + vscode.workspace.getConfiguration("r3Utils").get("caminhoModulos"); + + console.log("vscodePathValue", vscodePathValue); + assert.strictEqual(expectedPathValue, expectedPathValue); + }); + + test("O provider de módulos deve retornar todos os diretórios do path", async () => { + // Set the configuration value before the test + await vscode.workspace.getConfiguration("r3Utils").update("caminhoModulosTeste", "testDirectory/modules", true); + + // obter o caminho para testDirectory a partir do arquivo atual + const pathValue = vscode.workspace.getConfiguration("r3Utils").get("caminhoModulosTeste"); + const diretorios = await new ModuloPath(pathValue as string); + console.log("Diretórios", diretorios); + const moduloProvider = new ModulosProvider(diretorios); + + console.log("children", moduloProvider.getChildren()); + }); +}); diff --git a/src/test/providers/modulosProvider.test.ts b/src/test/providers/modulosProvider.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/vscode/outputChannels/r3utils.outputchannel.ts b/src/vscode/outputChannels/r3utils.outputchannel.ts new file mode 100644 index 0000000..0d2bf83 --- /dev/null +++ b/src/vscode/outputChannels/r3utils.outputchannel.ts @@ -0,0 +1,7 @@ +import * as vscode from "vscode"; + +const outputChannel = vscode.window.createOutputChannel('r3Utils'); + +export function getR3UtilsOutputChannel() { + return outputChannel; +} \ No newline at end of file diff --git a/testDirectory/modules/conferenciaColetor/infra/data/routes/teste.routes.ts b/testDirectory/modules/conferenciaColetor/infra/data/routes/teste.routes.ts new file mode 100644 index 0000000..cc56c7f --- /dev/null +++ b/testDirectory/modules/conferenciaColetor/infra/data/routes/teste.routes.ts @@ -0,0 +1,5 @@ + +teste.get("/rotaTeste"); +teste.get("/rotaTeste2"); +teste.post("/postTeste3"); +teste.put("/putTeste4"); diff --git a/testDirectory/modules/conferenciaColetor/infra/data/routes/teste2.routes.ts b/testDirectory/modules/conferenciaColetor/infra/data/routes/teste2.routes.ts new file mode 100644 index 0000000..e69de29 diff --git a/testDirectory/modules/convocacaoAtiva/infra/data/routes/convocacaoAtiva.routes.ts b/testDirectory/modules/convocacaoAtiva/infra/data/routes/convocacaoAtiva.routes.ts new file mode 100644 index 0000000..385a0ec --- /dev/null +++ b/testDirectory/modules/convocacaoAtiva/infra/data/routes/convocacaoAtiva.routes.ts @@ -0,0 +1,3 @@ + + +convocacao.get("/obterConvocacao") \ No newline at end of file diff --git a/testDirectory/modules/moduControle/infra/data/routes/moduControle.routes.ts b/testDirectory/modules/moduControle/infra/data/routes/moduControle.routes.ts new file mode 100644 index 0000000..17cd149 --- /dev/null +++ b/testDirectory/modules/moduControle/infra/data/routes/moduControle.routes.ts @@ -0,0 +1,3 @@ + + +moduControle.get("/moduControle"); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5ee889b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "Node16", + "target": "ES2022", + "lib": [ + "ES2022" + ], + "sourceMap": true, + "rootDir": ".", + "strict": true /* enable all strict type-checking options */ + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "testDirectory", + "**/*.spec.ts", + "**/*.test.ts" + ] +} \ No newline at end of file diff --git a/vsc-extension-quickstart.md b/vsc-extension-quickstart.md new file mode 100644 index 0000000..f518bb8 --- /dev/null +++ b/vsc-extension-quickstart.md @@ -0,0 +1,48 @@ +# Welcome to your VS Code Extension + +## What's in the folder + +* This folder contains all of the files necessary for your extension. +* `package.json` - this is the manifest file in which you declare your extension and command. + * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. +* `src/extension.ts` - this is the main file where you will provide the implementation of your command. + * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. + * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. + +## Setup + +* install the recommended extensions (amodio.tsl-problem-matcher, ms-vscode.extension-test-runner, and dbaeumer.vscode-eslint) + + +## Get up and running straight away + +* Press `F5` to open a new window with your extension loaded. +* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. +* Set breakpoints in your code inside `src/extension.ts` to debug your extension. +* Find output from your extension in the debug console. + +## Make changes + +* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. +* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. + + +## Explore the API + +* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`. + +## Run tests + +* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner) +* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered. +* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A` +* See the output of the test result in the Test Results view. +* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder. + * The provided test runner will only consider files matching the name pattern `**.test.ts`. + * You can create folders inside the `test` folder to structure your tests any way you want. + +## Go further + +* Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension). +* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace. +* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).