Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 9 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
cat <<EOF > .gitignore
# Dependencies
/node_modules
/.pnp
node_modules/
.pnp
.pnp.js
.yarn/install-state.gz

# Production
/build
/dist
/.out
build/
dist/
.out/

# Misc
.DS_Store
Expand All @@ -27,12 +28,13 @@ lerna-debug.log*
.env.production.local

# IDEs and editors
/.vscode/*
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.sublime-workspace

# TypeScript
*.tsbuildinfo
*.tsbuildinfo
EOF
11 changes: 7 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

148 changes: 102 additions & 46 deletions packages/cli/dist/commands/config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
// packages/cli/src/commands/config.ts
import chalk from 'chalk';
import Configstore from 'configstore';
import inquirer from 'inquirer';
import fs from 'fs/promises';
import path from 'path';
import { t } from '@stackcode/i18n';
const globalConfig = new Configstore('@stackcode/cli');
/**
* Handles the non-interactive command logic based on provided arguments.
* This function is easily testable in isolation.
* @param argv - The arguments object from yargs.
*/
export async function handleNonInteractiveMode(argv) {
switch (argv.action) {
case 'set':
if (!argv.key || !argv.value) {
console.error(chalk.red(t('config.error.missing_set_args')));
return;
}
globalConfig.set(argv.key, argv.value);
console.log(chalk.green(t('config.success.set', { key: argv.key, value: argv.value })));
break;
// Futuras implementaΓ§Γ΅es nΓ£o-interativas (get, delete, etc.) podem ser adicionadas aqui.
default:
console.error(chalk.yellow(t('config.error.invalid_action', { action: argv.action || 'unknown' })));
break;
}
}
/**
* Finds the project root by looking for a package.json file.
*/
const findProjectRoot = async (startPath) => {
let currentPath = startPath;
while (currentPath !== path.parse(currentPath).root) {
Expand All @@ -16,57 +42,87 @@ const findProjectRoot = async (startPath) => {
}
return null;
};
const globalConfig = new Configstore('@stackcode/cli');
export const getConfigCommand = () => ({
command: 'config',
describe: t('config.command_description'),
builder: {},
handler: async () => {
const { choice } = await inquirer.prompt([
/**
* Runs the fully interactive configuration session using inquirer.
*/
export async function runInteractiveMode() {
const { choice } = await inquirer.prompt([
{
type: 'list', name: 'choice', message: t('config.prompt.main'),
choices: [
{ name: t('config.prompt.select_lang'), value: 'lang' },
{ name: t('config.prompt.toggle_validation'), value: 'commitValidation' },
],
}
]);
if (choice === 'lang') {
const { lang } = await inquirer.prompt([
{
type: 'list', name: 'choice', message: t('config.prompt.main'),
choices: [
{ name: t('config.prompt.select_lang'), value: 'lang' },
{ name: t('config.prompt.toggle_validation'), value: 'commitValidation' },
],
type: 'list', name: 'lang', message: t('config.prompt.select_lang'),
choices: [{ name: 'English', value: 'en' }, { name: 'PortuguΓͺs', value: 'pt' }],
}
]);
if (choice === 'lang') {
const { lang } = await inquirer.prompt([
{
type: 'list', name: 'lang', message: t('config.prompt.select_lang'),
choices: [{ name: 'English', value: 'en' }, { name: 'PortuguΓͺs', value: 'pt' }],
}
]);
globalConfig.set('lang', lang);
console.log(chalk.green(t('config.success.set', { key: 'lang', value: lang })));
globalConfig.set('lang', lang);
console.log(chalk.green(t('config.success.set', { key: 'lang', value: lang })));
}
else if (choice === 'commitValidation') {
const projectRoot = await findProjectRoot(process.cwd());
if (!projectRoot) {
console.error(chalk.red(t('config.error.not_in_project')));
return;
}
else if (choice === 'commitValidation') {
const projectRoot = await findProjectRoot(process.cwd());
if (!projectRoot) {
console.error(chalk.red(t('config.error.not_in_project')));
return;
}
const localConfigPath = path.join(projectRoot, '.stackcoderc.json');
try {
await fs.access(localConfigPath);
}
catch {
console.error(chalk.red(t('config.error.not_in_project')));
return;
const localConfigPath = path.join(projectRoot, '.stackcoderc.json');
try {
await fs.access(localConfigPath);
}
catch {
console.error(chalk.red(t('config.error.no_stackcoderc')));
return;
}
const { enable } = await inquirer.prompt([
{
type: 'confirm', name: 'enable', message: t('config.prompt.toggle_validation'),
default: true,
}
const { enable } = await inquirer.prompt([
{
type: 'confirm', name: 'enable', message: t('config.prompt.toggle_validation'),
default: true,
}
]);
const localConfigContent = await fs.readFile(localConfigPath, 'utf-8');
const localConfig = JSON.parse(localConfigContent);
localConfig.features.commitValidation = enable;
await fs.writeFile(localConfigPath, JSON.stringify(localConfig, null, 2));
const status = enable ? t('config.status.enabled') : t('config.status.disabled');
console.log(chalk.green(t('config.success.set_validation', { status })));
]);
const localConfigContent = await fs.readFile(localConfigPath, 'utf-8');
const localConfig = JSON.parse(localConfigContent);
localConfig.features.commitValidation = enable;
await fs.writeFile(localConfigPath, JSON.stringify(localConfig, null, 2));
const status = enable ? t('config.status.enabled') : t('config.status.disabled');
console.log(chalk.green(t('config.success.set_validation', { status })));
}
}
/**
* Defines the 'config' command, its arguments, and the handler logic.
*/
export const getConfigCommand = () => ({
command: 'config [action] [key] [value]',
describe: t('config.command_description'),
builder: (yargs) => {
return yargs
.positional('action', {
describe: t('config.args.action_description'),
type: 'string',
choices: ['set'], // Apenas 'set' estΓ‘ implementado no modo nΓ£o-interativo por enquanto
})
.positional('key', {
describe: t('config.args.key_description'),
type: 'string',
})
.positional('value', {
describe: t('config.args.value_description'),
type: 'string',
});
},
handler: async (argv) => {
// The handler is now an orchestrator.
const isInteractive = !argv.action;
if (isInteractive) {
await runInteractiveMode();
}
else {
await handleNonInteractiveMode(argv);
}
},
});
2 changes: 1 addition & 1 deletion packages/cli/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async function main() {
const locale = getLocale();
yargs(hideBin(process.argv))
.scriptName("stackcode")
.version('1.0.0')
.version('1.0.3')
.alias('h', 'help')
.alias('v', 'version')
.strict()
Expand Down
17 changes: 13 additions & 4 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,17 @@
"scripts": {
"clean": "rm -rf dist tsconfig.tsbuildinfo",
"build": "tsc && chmod +x dist/index.js",
"test": "echo \"βœ… No tests for this package\""
"test": "vitest run"
},
"keywords": ["cli", "scaffolding", "conventional-commits", "gitflow", "automation", "devops", "typescript"],
"keywords": [
"cli",
"scaffolding",
"conventional-commits",
"gitflow",
"automation",
"devops",
"typescript"
],
"author": "Yago Borba",
"license": "MIT",
"homepage": "https://github.com/YagoBorba/StackCode#readme",
Expand Down Expand Up @@ -42,6 +50,7 @@
"@types/open": "^6.1.0",
"@types/semver": "^7.7.0",
"@types/yargs": "^17.0.32",
"typescript": "^5.5.2"
"typescript": "^5.5.2",
"vitest": "^3.2.4"
}
}
}
Loading