Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adiciona uma Interface de Linha de Comando (CLI) para criar novos conceitos #112

Open
wants to merge 47 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
d70d8ea
chore: add package.json with libraries and scripts
rwietter Oct 20, 2023
22dc589
chore: add the log tree generated by npm
rwietter Oct 20, 2023
256c03c
chore: add tsconfig for typescript
rwietter Oct 20, 2023
de600c5
chore: add webpack to build bundle CLI
rwietter Oct 20, 2023
3840546
chore: update package.json with correctly informations about repository
rwietter Oct 20, 2023
c086407
chore: ignore node_modules
rwietter Oct 20, 2023
5918ca8
feat(cli): add functionality for creating new terms in diciotech
rwietter Oct 20, 2023
5ad5907
build(cli): bundle ready to run with Node.js, packaged with webpack
rwietter Oct 20, 2023
aeccfb1
Merge remote-tracking branch 'upstream/main' into feat/script-new-term
rwietter Oct 20, 2023
1efa625
chore(tsconfig): update include types
rwietter Oct 20, 2023
5d0bd64
feat(cli): add types for questions and terms
rwietter Oct 20, 2023
830776a
chore: remove watch in dev script
rwietter Oct 21, 2023
196abf6
refactor(cli/types): add interfaces
rwietter Oct 21, 2023
dfab617
refactor(cli/config): resolve cards_pt-br path
rwietter Oct 21, 2023
b84c922
refactor(cli/utils): add dash prompt styles
rwietter Oct 21, 2023
7cb6228
refactor(cli/core): add rules of domain application
rwietter Oct 21, 2023
4ce049e
refactor(cli/core): add prompts and file system
rwietter Oct 21, 2023
46123ce
refactor(cli/commands): create runner for prompt
rwietter Oct 21, 2023
03896d5
refactor(cli/commands): remove questions interface
rwietter Oct 21, 2023
1891eae
refactor(cli/commands): update entry point with new dependencies
rwietter Oct 21, 2023
04af6ba
build(cli): create update build bundle
rwietter Oct 21, 2023
df11de0
refactor: replace objective-questions with subjective-questions
rwietter Oct 21, 2023
c4885b3
chore: remove enquirer and add clack
rwietter Oct 27, 2023
690c65a
feat: update types
rwietter Oct 27, 2023
b7267f9
feat: update domain rules
rwietter Oct 27, 2023
da38239
feat: add path to the code file where code snippets for new terms wil…
rwietter Oct 27, 2023
3ea95d8
feat: add merge, clack cancel and parse-terms
rwietter Oct 27, 2023
49b6c31
feat: replace enquirer prompts with clack prompts
rwietter Oct 27, 2023
d67695f
feat: file to add code snippet
rwietter Oct 27, 2023
44414a7
refactor: move new-term to commands
rwietter Oct 27, 2023
64dde14
feat: create the command to generate a new concept based on the Clack…
rwietter Oct 27, 2023
caee84a
build: generate bundle to run with node
rwietter Oct 27, 2023
4f6fc03
fix: remove unused code and files
rwietter Oct 27, 2023
1cd2544
fix: word in title validation
rwietter Oct 27, 2023
e9d9ac6
build: generate bundle to run with node
rwietter Oct 27, 2023
c796465
docs(CONTRIBUTING.md): adicionado a seção de adicionar uma nova tag e…
freitaschz Oct 17, 2023
a2dee6d
docs: correção de pontuação
freitaschz Oct 18, 2023
0277b4a
feat: alterações em textos
freitaschz Oct 19, 2023
9f59bb2
docs(README): reduzido quantidade máxima na listagem de pessoas contr…
freitaschz Oct 19, 2023
1570962
docs: alterado termos e frases em busca de palavras mais neutras e co…
freitaschz Oct 25, 2023
d039b9b
Merge branch 'main' into feat/script-new-term
rwietter Oct 28, 2023
d86e298
chore: move webpack and tsconfig settings to cli
rwietter Oct 28, 2023
fd7deeb
fix: rename variable
rwietter Oct 28, 2023
8935860
Merge branch 'main' into feat/script-new-term
rwietter Nov 7, 2023
7e5f08c
Merge branch 'levxyca:main' into feat/script-new-term
rwietter Jan 2, 2024
8e7123f
Merge branch 'levxyca:main' into feat/script-new-term
rwietter Jul 1, 2024
576319c
docs(cli/README.md): add documentation for CLI usage
rwietter Jul 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,7 @@ $RECYCLE.BIN/
# Windows shortcuts
*.lnk

# Node.js
node_modules/

# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux
75 changes: 75 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Diciotech Command Line Interface (CLI)

A **CLI do Diciotech** permite que você crie novos termos de forma interativa através de um terminal. Esta ferramenta foi desenvolvida usando o framework Node.js. Portanto, é necessário ter o Node.js instalado em sua máquina para utilizar a CLI.

## Sumário

- [Diciotech Command Line Interface (CLI)](#diciotech-command-line-interface-cli)
- [Sumário](#sumário)
- [Introdução](#introdução)
- [Instalação](#instalação)
- [Clonando o Repositório](#clonando-o-repositório)
- [Criando um novo termo](#criando-um-novo-termo)
- [Criando um novo termo com código](#criando-um-novo-termo-com-código)
- [Visualização](#visualização)
- [Contribuição](#contribuição)

## Introdução

A CLI do Diciotech é projetada para facilitar a criação e a gestão de termos de maneira eficiente. Este documento guiará você pelo processo de instalação, configuração e uso da ferramenta, garantindo que você possa contribuir com novos termos para o repositório do Diciotech.

## Instalação

Para começar, você precisa instalar o Node.js. Acesse o guia de instalação para diversos sistemas operacionais na [documentação oficial do Node.js](https://nodejs.org/en/download/) e instale uma versão a partir da `18.17.1`.

## Clonando o Repositório

Após instalar o Node.js, você precisa fazer um fork do projeto. Consulte o guia para [criar um fork de um repositório](https://docs.github.com/pt/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo).

Feito isso, você pode clonar o fork do diciotech que está em seu perfil do GitHub e começar a contribuir com novos termos. Siga os passos abaixo para clonar o fork:

1. Por exemplo, para clonar o fork, execute o seguinte comando em seu terminal (substitua `<my-user>` pelo seu nome de usuário do GitHub):
```bash
git clone https://github.com/<my-user>/diciotech.git
```

2. Navegue até o diretório do projeto:
```bash
cd diciotech
```

3. Crie uma nova branch para adicionar o termo:
```bash
git checkout -b my-branch-name # ou git switch -c my-branch-name
```

## Criando um novo termo

Para criar um novo termo, execute o comando `npm run new` em seu terminal. A CLI fará uma série de perguntas:

1. Nome do termo que você deseja criar.
2. Definição que descreve o termo.
3. Categoria(s) do termo (tecla `Space` para selecionar).
4. Visualização prévia do termo antes de criá-lo.
5. Confirmação para gravar o termo.

Após responder às perguntas, o termo será criado. Em seguida, você pode fazer um pull request para o repositório do [Diciotech](https://github.com/levxyca/diciotech.git). Consulte o guia [CONTRIBUTING](https://github.com/levxyca/diciotech/blob/main/CONTRIBUTING.md) para mais detalhes sobre como contribuir.

## Criando um novo termo com código

Para criar um novo termo com um exemplo de código:

1. Localize o arquivo **code** na raiz do projeto.
2. Abra o arquivo e adicione o código que deseja incluir, devidamente indentado.
3. Execute o comando `npm run new` no seu terminal e siga os passos mencionados anteriormente.
4. Quando a CLI perguntar se você deseja visualizar o termo, selecione `Sim` para ver o termo com o código que você adicionou.

## Visualização

<img src="https://i.imgur.com/RXHCynd.png" width="100%" />

## Contribuição

Quer contribuir para o Diciotech? Confira nosso guia de contribuição para aprender como você pode ajudar a melhorar e expandir o projeto. Seu feedback e suas contribuições são muito bem-vindos!

Para mais informações, consulte o [guia de contribuição](https://github.com/levxyca/diciotech/blob/main/CONTRIBUTING.md).
1 change: 1 addition & 0 deletions cli/build/bundle.js

Large diffs are not rendered by default.

140 changes: 140 additions & 0 deletions cli/commands/new-term.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { intro, outro } from '@clack/prompts';
import crypto from "node:crypto";
import c from 'ansi-colors';

import { subjectivePromptQuestions } from "../core/questions";
import { availableTags } from "../core/available-tags";

import { multiSelect } from "../io/prompts/multiselect";
import { subjectiveQuestion } from "../io/prompts/text";
import { readFileContents } from "../io/read-file-sync";
import { confirmPrompt } from "../io/prompts/confirm";
import { writeToFile } from "../io/write-to-file";
import { notePrompt } from "../io/prompts/note";

import { cardsPath } from "../config/cards_pt-br-path";
import { codePath } from "../config/code-path";

import { parseJSONTerms } from "../utils/parse-terms";
import { merge } from "../utils/merge-item";

import { Term } from "../types";
import fs from "node:fs";

/**
* Prompts the user for the term's information.
*/
export const createNewTerm = async (): Promise<Term> => {
intro(c.bold.bgCyanBright.black(" Olá, boas-vindas ao Diciotech CLI! "))

fs.stat(codePath, (err) => {
if (err) {
fs.writeFileSync(codePath, "")
}
})

notePrompt(
c.cyanBright(`Antes de começar, precisamos saber se o seu conceito ${c.bold.yellow("possui algum trecho de código")}.\nSe sim, você pode ${c.bold.yellow("escrever o código no arquivo")} ${c.bold.magenta(codePath)}.\nCaso contrário, ${c.bold.yellow("apenas ignore isso e continue")}.`),
c.bold.bgYellow.black(" Importante ")
)

const [promptTitle, promptDescription] = subjectivePromptQuestions

const title = await subjectiveQuestion({
message: c.cyanBright(promptTitle.message),
validation: promptTitle.validation,
})
const description = await subjectiveQuestion({
message: c.cyanBright(promptDescription.message),
validation: promptDescription.validation,
})

const tags = await multiSelect(
"Selecione a(s) (tag)s relacionada(s) ao conceito",
availableTags,
(tags: symbol | string[]) => {
if (typeof tags !== 'symbol' && tags.length < 1) {
outro(c.bgRed.black(" Você precisa selecionar pelo menos uma tag. Tente novamente. "))
process.exit(0);
}
}
)

const code = readFileContents(codePath)

const card = {
id: crypto.randomBytes(16).toString("hex"),
title: title.toString(),
description: description.toString(),
tags: Array.isArray(tags) ? tags as string[] : [],
content: {
code: code?.trim(),
},
};

const shouldVisualizeTerm = await confirmPrompt(
c.cyanBright(`Deseja ${c.bold("visualizar")} o novo termo ?`)
);

const truncatedDescription = card.description.length > 60 ? `${card.description.slice(0, 60)}...` : card.description

if (shouldVisualizeTerm) {
notePrompt(
JSON.stringify({
title: card.title,
description: truncatedDescription,
tags: card.tags,
content: {
code: card.content.code.split("\n").filter(line => line !== "")
}
}, null, 2),
c.bold.bgCyanBright.black(" Termo ")
)
}

const shouldContinue = await confirmPrompt(
c.cyanBright(`Show! Agora vamos gravar o conceito. ${c.bold("Deseja continuar")} ?`)
)

if (!shouldContinue) {
outro(c.bgYellow.black(` Tudo bem! Se quiser tentar novamente, é só rodar o comando ${c.bold('npm run new')}. `))
process.exit(0);
}

return card;
};

/**
* Compounds all the steps to write a new term to a file.
*/
const compose = (
newTerm: Term,
filepath: string
) =>
writeToFile(
filepath,
JSON.stringify({
cards: merge(parseJSONTerms(readFileContents(filepath)), newTerm)
}, null, 2)
);

export const newTerm = async () => {
try {
const newTerm = await createNewTerm();
compose(newTerm, cardsPath);

writeToFile(codePath, ""); // clear code file

notePrompt(c.cyanBright(`Se você quiser adicionar mais termos, é só rodar o comando ${c.bold('npm run new')}`), c.bold.bgGreen.black(" Feito "));
outro(c.green(`Tudo certo! você pode abrir um ${c.bold("Pull Request")} em <${c.bold("https://github.com/levxyca/diciotech/pulls")}>`))
} catch (error: unknown) {
notePrompt(
c.italic.red(`- Ocorreu um erro ao adicionar o novo termo.\n- Tente novamente seguindo as instruções.\n- Caso o erro persista, abra uma issue em https://github.com/levxyca/diciotech/issues.`),
c.bold.bgRedBright.black(" Eita! Algo deu errado :( ")
)

if (error instanceof Error) {
outro(c.red(`❌ ${error.message}`));
}
}
};
10 changes: 10 additions & 0 deletions cli/config/cards_pt-br-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import path from "node:path";

export const cardsPath = path.join(
__dirname,
"..",
"..",
"assets",
"data",
"cards_pt-br.json"
);
3 changes: 3 additions & 0 deletions cli/config/code-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import path from "path";

export const codePath = path.join(__dirname, "..", "..", "code");
14 changes: 14 additions & 0 deletions cli/core/available-tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { AvailableTags } from "../types";

export const availableTags: AvailableTags[] = [
{ value: "Back-end", label: "Back-end" },
{ value: "Biblioteca", label: "Biblioteca" },
{ value: "Conceito", label: "Conceito", hint: "recommended" },
{ value: "Design", label: "Design" },
{ value: "Ferramenta", label: "Ferramenta" },
{ value: "Framework", label: "Framework" },
{ value: "Front-end", label: "Front-end" },
{ value: "Mobile", label: "Mobile" },
{ value: "Paradigma", label: "Paradigma" },
{ value: "Versionamento", label: "Versionamento" },
];
21 changes: 21 additions & 0 deletions cli/core/prompt-validations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const validations = {
title: (input: string) => {
const raw = input.trim();

if (raw.trim().length < 1) {
return `O título precisa ter pelo menos 1 caractere. Você digitou ${raw.length} caracteres.`
}
},
description: (input: string) => {
const raw = input.trim();

if (raw.length < 10) {
return `A descrição precisa ter pelo menos 10 caracteres. Você digitou ${raw.length} caracteres.`;
}
},
tags: (input: string[]) => {
if (input.length < 1) {
return `Você precisa selecionar pelo menos uma tag. Você selecionou ${input.length} opções.`
}
},
};
18 changes: 18 additions & 0 deletions cli/core/questions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { validations } from "./prompt-validations";

export const inputBase = {
required: true,
};

export const subjectivePromptQuestions = [
{
message: "Digite o nome do conceito",
validation: validations.title,
required: true,
},
{
...inputBase,
message: "Digite a descrição do conceito",
validation: validations.description
},
];
5 changes: 5 additions & 0 deletions cli/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { newTerm } from "./commands/new-term";

const main = () => newTerm();

main();
18 changes: 18 additions & 0 deletions cli/io/prompts/confirm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { confirm } from "@clack/prompts";

import { handleCancelation } from "../../utils/handle-cancel";

/**
* Generic multiselect prompt. Prompts the user for multiple options.
*/
export const confirmPrompt = async (message: string): Promise<symbol | boolean> => {
const shouldContinue = await confirm({
message: message,
active: 'Sim',
inactive: 'Não',
initialValue: true,
});

handleCancelation(shouldContinue); // handle the user cancelation
return shouldContinue;
};
26 changes: 26 additions & 0 deletions cli/io/prompts/multiselect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import c from "ansi-colors";
import { multiselect } from "@clack/prompts";

import { handleCancelation } from "../../utils/handle-cancel";

import { AvailableTags } from "../../types";


/**
* Generic multiselect prompt. Prompts the user for multiple options.
*/
export const multiSelect = async (
message: string,
availableTags: AvailableTags[],
cb: Function
): Promise<symbol | unknown[] | void> => {
const tags = await multiselect({
message: c.cyanBright(message),
options: availableTags,
required: true,
});

cb(tags); // callback to validate the tags
handleCancelation(tags); // handle the user cancelation
return tags;
};
6 changes: 6 additions & 0 deletions cli/io/prompts/note.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { note as prompt } from "@clack/prompts"

export const notePrompt = (message: string, title: string) => {
const note = prompt(message, title)
return note
}
21 changes: 21 additions & 0 deletions cli/io/prompts/text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

import { text } from "@clack/prompts";

import { handleCancelation } from "../../utils/handle-cancel";

import { SubjectiveQuestion } from "../../types";

interface QuestionProps {
message: string;
validation: (value: string) => void | string;
}

export const subjectiveQuestion = async (props: QuestionProps): Promise<SubjectiveQuestion> => {
const prompt = await text({
message: props.message,
validate: props.validation
});

handleCancelation(prompt);
return prompt;
};
6 changes: 6 additions & 0 deletions cli/io/read-file-sync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import fs from 'node:fs';

/**
* Reads the content of a file.
*/
export const readFileContents = (path: string) => fs.readFileSync(path).toString();
9 changes: 9 additions & 0 deletions cli/io/write-to-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import fs from 'node:fs';

/**
* Writes updated terms to a file.
*/
export const writeToFile = (path: string, content: string): void => {
fs.writeFileSync(path, content);
return;
};
Loading