Depois de muita discussão chegamos a uma decisão! Tornar o código da base em uma biblioteca. Dessa forma o projeto fica um pouco menor e as atualizações são facilitadas
Comando para executar a CLI
npx constatic@latestMudanças
Agora ao gerar um novo projeto usando a CLI, a biblioteca @constatic/base virá como uma dependência.
A pasta src/discord/base foi removida!
As estruturas e funções que existiam nessa pasta agora estão todas na biblioteca @constatic/base, mas uma coisa boa é que a migração é muito fácil e não deve quebrar muitas coisas.
Migração 1.4 -> ^1.5
Quase tudo que vinha do atalho #base que apontava para src/discord/base/index.ts agora deve vir de @constatic/base:
A função bootstrap:
import { env } from "#env";
import { bootstrap } from "#base";
import { bootstrap } from "@constatic/base";
await bootstrap({ meta: import.meta });
await bootstrap({ meta: import.meta, env }); Se tornou necessário importar o objeto env do arquivo src/env.ts para que a função validateEnv possa ser executada, assim validando e transformando as variaveis de ambiente.
A função setupCreators:
import { setupCreators } from "#base";
import { setupCreators } from "@constatic/base";
export const { createCommand, createEvent, createResponder } = setupCreators();O atalho #base ainda existe, porém agora ele aponta para src/discord/index que é o local onde estamos usando a função setupCreators. Então nos seus comandos, eventos e responders, as importações das funções createCommand, createEvent e createResponder ainda são válidas.
É assim que deve ficar no package.json
{
// ...
"imports": {
// ...
"#base": [
"./build/discord/base/index.js"
"./build/discord/index.js"
],
// ...
},
}E no tsconfig.json:
{
"compilerOptions": {
// ...
"paths": {
"#base": ["./src/discord/base/index.ts"],
"#base": ["./src/discord/index.ts"],
// ...
}
},
// ...
}O enum ResponderType agora está na biblioteca, então você deve alterar a importação dele nos arquivos onde você cria responders:
import { createResponder, ResponderType } from "#base";
import { createResponder } from "#base";
import { ResponderType } from "@constatic/base";
import { createDate } from "@magicyan/discord";
import { time } from "discord.js";
import { z } from "zod";
const schema = z.object({
date: z.transform(createDate),
});
createResponder({
customId: "remind/:date",
types: [ResponderType.Button],
parse: schema.parse, cache: "cached",
async run(interaction, { date }) {
await interaction.reply({
flags: ["Ephemeral"],
content: `⏳ You run ping command ${time(date, "R")}`,
});
},
});A função validateEnv que agora é importada da biblioteca, se tornou assíncrona (requirindo await) e aceita o standard schema (Isso significa que você pode passar um schema de qualquer lib que use o standard schema, como zod, arktype, valibot, yup):
import "./constants.js";
import { validateEnv } from "#base";
import { validateEnv } from "@constatic/base";
import { z } from "zod";
export const env = await validateEnv(z.looseObject({
export const env = await validateEnv(z.object({
BOT_TOKEN: z.string("Discord Bot Token is required").min(1),
WEBHOOK_LOGS_URL: z.url().optional(),
GUILD_ID: z.string().optional()
}));Se vocês notarem acima, no topo do arquivo é feita uma importação do arquivo ./src/constants.js. O novo arquivo constants.ts em src carrega e cria o objeto global constants para que você possa usar em qualquer lugar do seu código:
import constantsJson from "../constants.json" with { type: "json" };
declare global {
const constants: typeof constantsJson;
}
Object.assign(globalThis, Object.freeze({
constants: constantsJson
}));Antes ele ficava internamente nos arquivos da pasta src/discord/base, mas isso não é mais possível, é inviável colocar isso em uma biblioteca e não teríamos o autocomplete das propriedades pela inferencia de tipos.
Então é importante prestar atenção em como esse arquivo está sendo executado
O arquivo src/index.ts importa o arquivo src/env.ts e o arquivo src/env.ts importa o arquivo src/constants.ts:
constants.ts -> env.ts -> index.ts
Só assim o objeto global constants pode existir.
A biblioteca também exporta um arquivo json com configurações do tsconfig recomendadas para o projeto, você pode usar para estender o tsconfig.json:
{
"extends": "@constatic/base/tsconfig",
"compilerOptions": {
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"strict": true,
"lib": ["ESNext"],
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noImplicitOverride": true,
"noEmitOnError": true,
"outDir": "./build",
"rootDir": "./src",
"paths": {
"#env": ["./src/env.ts"],
"#base": ["./src/discord/index.ts"],
"#functions": ["./src/functions/index.ts"],
"#database": ["./src/database/index.ts"],
"#server": ["./src/server/index.ts"],
"#menus": ["./src/menus/index.ts"],
"#tools": ["./src/tools/index.ts"],
"#lib/*": ["./src/lib/*"],
"#shared/*": ["./src/shared/*"],
"#types/*": ["./src/@types/*"],
"#emojis": ["./emojis.json"],
}
},
"include": ["src"],
"exclude": ["node_modules"]
}Conclusão
Essas mudanças chegaram para trazer mais praticidade na hora de fazer atualizações e também para tornar o projeto mais organizado. Reporte qualquer problema que encontrar no discord de suporte: Zunder Community
Novidades, atualizações, alterações, dicas e muito mais será postado neste blog! Se você quiser ser notificado sempre que uma nova postagem for publicada, entre no discord Zunder Community ou Siga @rinckodev no twitter/x
Jan 2, 2026