feat: introduce Gatekeeper application, Directus utilities, and monorepo configuration for linting, testing, and husky hooks.
Some checks failed
Code Quality / lint-and-build (push) Failing after 52s
Release Packages / release (push) Failing after 32s

This commit is contained in:
2026-02-01 21:23:34 +01:00
parent c2a0ba88c0
commit 83b4ea8807
51 changed files with 3150 additions and 282 deletions

View File

@@ -12,7 +12,8 @@
"scripts": {
"build": "tsup src/index.ts --format esm --target es2020",
"start": "node dist/index.js",
"dev": "tsup src/index.ts --format esm --watch --target es2020"
"dev": "tsup src/index.ts --format esm --watch --target es2020",
"test": "vitest run"
},
"dependencies": {
"commander": "^11.0.0",

View File

@@ -0,0 +1,7 @@
import { describe, it, expect } from "vitest";
describe("cli", () => {
it("should have a working environment", () => {
expect(true).toBe(true);
});
});

View File

@@ -44,6 +44,7 @@ program
react: "^19.0.0",
"react-dom": "^19.0.0",
"@mintel/next-utils": "workspace:*",
"@directus/sdk": "^21.0.0",
},
devDependencies: {
"@types/node": "^20.0.0",
@@ -53,6 +54,7 @@ program
"@mintel/tsconfig": "workspace:*",
"@mintel/eslint-config": "workspace:*",
"@mintel/next-config": "workspace:*",
"@mintel/husky-config": "workspace:*",
},
};
await fs.writeJson(path.join(fullPath, "package.json"), pkgJson, {
@@ -96,7 +98,17 @@ export default nextConfig;
`;
await fs.writeFile(
path.join(fullPath, "eslint.config.mjs"),
eslintConfig
eslintConfig,
);
// Create Husky/Linting configs
await fs.writeFile(
path.join(fullPath, "commitlint.config.js"),
`export { default } from "@mintel/husky-config/commitlint";\n`,
);
await fs.writeFile(
path.join(fullPath, ".lintstagedrc.js"),
`export { default } from "@mintel/husky-config/lint-staged";\n`,
);
// Create env validation script
@@ -111,7 +123,7 @@ try {
} catch (error) {
process.exit(1);
}
`
`,
);
// Create basic src structure
@@ -129,7 +141,7 @@ export default createMintelMiddleware({
export const config = {
matcher: ["/((?!api|_next|_vercel|health|.*\\\\..*).*)", "/", "/(de|en)/:path*"]
};
`
`,
);
// Create i18n/request.ts
@@ -143,21 +155,29 @@ export default createMintelI18nRequestConfig(
"en",
(locale) => import(\`../../messages/\${locale}.json\`)
);
`
`,
);
// Create messages directory
await fs.ensureDir(path.join(fullPath, "messages"));
await fs.writeJson(path.join(fullPath, "messages/en.json"), {
Index: {
title: "Welcome"
}
}, { spaces: 2 });
await fs.writeJson(path.join(fullPath, "messages/de.json"), {
Index: {
title: "Willkommen"
}
}, { spaces: 2 });
await fs.writeJson(
path.join(fullPath, "messages/en.json"),
{
Index: {
title: "Welcome",
},
},
{ spaces: 2 },
);
await fs.writeJson(
path.join(fullPath, "messages/de.json"),
{
Index: {
title: "Willkommen",
},
},
{ spaces: 2 },
);
// Create instrumentation.ts
await fs.writeFile(
@@ -171,7 +191,7 @@ export async function register() {
}
export const onRequestError = Sentry.captureRequestError;
`
`,
);
await fs.writeFile(
@@ -192,11 +212,11 @@ export default function RootLayout({
}) {
return (
<html lang={locale}>
<body>{children}</body>
<body className="antialiased">{children}</body>
</html>
);
}
`
`,
);
await fs.writeFile(
@@ -212,7 +232,7 @@ export default function Home() {
</main>
);
}
`
`,
);
// Copy infra templates
@@ -220,21 +240,75 @@ export default function Home() {
if (await fs.pathExists(infraPath)) {
await fs.copy(
path.join(infraPath, "docker/Dockerfile.nextjs"),
path.join(fullPath, "Dockerfile")
path.join(fullPath, "Dockerfile"),
);
await fs.copy(
path.join(infraPath, "docker/docker-compose.template.yml"),
path.join(fullPath, "docker-compose.yml")
path.join(fullPath, "docker-compose.yml"),
);
await fs.ensureDir(path.join(fullPath, ".gitea/workflows"));
await fs.copy(
path.join(infraPath, "gitea/deploy-action.yml"),
path.join(fullPath, ".gitea/workflows/deploy.yml")
path.join(fullPath, ".gitea/workflows/deploy.yml"),
);
// Create Directus structure
await fs.ensureDir(path.join(fullPath, "directus/uploads"));
await fs.ensureDir(path.join(fullPath, "directus/extensions"));
await fs.writeFile(
path.join(fullPath, "directus/uploads/.gitkeep"),
"",
);
await fs.writeFile(
path.join(fullPath, "directus/extensions/.gitkeep"),
"",
);
// Create .env.example
const envExample = `# Project
PROJECT_NAME=${projectName}
PROJECT_COLOR=#82ed20
# Authentication
GATEKEEPER_PASSWORD=mintel
AUTH_COOKIE_NAME=mintel_gatekeeper_session
# Host Config (Local)
TRAEFIK_HOST=\`${projectName}.localhost\`
DIRECTUS_HOST=\`cms.${projectName}.localhost\`
# Next.js
NEXT_PUBLIC_BASE_URL=http://${projectName}.localhost
# Directus
DIRECTUS_URL=http://cms.${projectName}.localhost
DIRECTUS_KEY=$(openssl rand -hex 32 2>/dev/null || echo "mintel-key")
DIRECTUS_SECRET=$(openssl rand -hex 32 2>/dev/null || echo "mintel-secret")
DIRECTUS_ADMIN_EMAIL=admin@mintel.me
DIRECTUS_ADMIN_PASSWORD=mintel-admin-pass
DIRECTUS_DB_NAME=directus
DIRECTUS_DB_USER=directus
DIRECTUS_DB_PASSWORD=mintel-db-pass
# Sentry / Glitchtip
SENTRY_DSN=
# Analytics (Umami)
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
NEXT_PUBLIC_UMAMI_SCRIPT_URL=https://analytics.infra.mintel.me/script.js
`;
await fs.writeFile(path.join(fullPath, ".env.example"), envExample);
// Copy premium templates (globals.css, lib/directus.ts, scripts/setup-directus.ts)
const templatePath = path.join(infraPath, "templates/website");
if (await fs.pathExists(templatePath)) {
console.log(chalk.blue("Applying premium templates..."));
await fs.copy(templatePath, fullPath, { overwrite: true });
}
}
console.log(
chalk.green(`Successfully initialized ${projectName} at ${fullPath}`)
chalk.green(`Successfully initialized ${projectName} at ${fullPath}`),
);
console.log(chalk.yellow("\nNext steps:"));
console.log(chalk.cyan("1. pnpm install"));