From 363027187d4893fb45402615fa054dd30d3fde34 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Mon, 8 Jul 2024 02:03:47 +0200 Subject: [PATCH] feat(build): integrate generation of manifest file in build process --- .gitignore | 4 +- .prettierignore | 1 + build/env.ts | 22 +++++++ build/manifestPlugin.ts | 82 ++++++++++++++++++++++++ package.json | 5 +- pnpm-lock.yaml | 135 ++++++++++++++++++++++++++++++++++++++++ tsconfig.node.json | 2 +- vite.config.ts | 3 +- 8 files changed, 249 insertions(+), 5 deletions(-) create mode 100644 build/env.ts create mode 100644 build/manifestPlugin.ts diff --git a/.gitignore b/.gitignore index 332d5ae..ae4f4a4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ .pnp.js # Production -/build +/dist # Misc .DS_Store @@ -29,3 +29,5 @@ yarn-error.log* # Project .notes +# Generated +/src/manifest.ts diff --git a/.prettierignore b/.prettierignore index a0ece49..fc3f55d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ .env.example .idea pnpm-lock.yaml +dist diff --git a/build/env.ts b/build/env.ts new file mode 100644 index 0000000..b9c601d --- /dev/null +++ b/build/env.ts @@ -0,0 +1,22 @@ +import { z } from "zod"; +import { configDotenv } from "dotenv"; + +configDotenv(); + +const schema = z.object({ + GITHUB_PAT: z.string().min(1), + GITHUB_USERNAME: z.string().min(1), +}); + +const result = schema.safeParse(process.env); +if (result.success === false) { + console.error("❌ Invalid environment variables"); + console.error( + result.error.errors + .map((error) => `- ${error.path.join(".")}: ${error.message}`) + .join("\n"), + ); + process.exit(1); +} + +export const env = result.data; diff --git a/build/manifestPlugin.ts b/build/manifestPlugin.ts new file mode 100644 index 0000000..fab125d --- /dev/null +++ b/build/manifestPlugin.ts @@ -0,0 +1,82 @@ +import { Plugin } from "vite"; +import { readFile, writeFile } from "fs/promises"; +import { spawnSync } from "child_process"; +import { Octokit } from "@octokit/rest"; +import { env } from "./env"; + +export const manifest = (): Plugin => ({ + name: "generate-pages-plugin", + buildStart: async () => { + const octokit = new Octokit({ auth: env.GITHUB_PAT }); + + const { data: manifestRepo } = await octokit.repos.get({ + owner: env.GITHUB_USERNAME, + repo: env.GITHUB_USERNAME, + }); + try { + const storedUpdatedAt = ( + await readFile("./node_modules/.cache/manifest") + ).toString(); + if (storedUpdatedAt === manifestRepo.updated_at) return; + } catch {} + + await writeFile("./node_modules/.cache/manifest", manifestRepo.updated_at); + + const getRepoFileContent = async (repo: string, path: string) => { + const { data: file } = await octokit.repos.getContent({ + owner: env.GITHUB_USERNAME, + repo, + path, + }); + + if (Array.isArray(file) || file.type !== "file") throw new Error(""); + + return Buffer.from(file.content, "base64").toString("utf8"); + }; + + const manifest = JSON.parse( + await getRepoFileContent(env.GITHUB_USERNAME, "manifest.json"), + ) as { + files: string[]; + projects: string[]; + }; + + const projects: Array<{ + name: string; + content: string; + language: string | null; + url: string; + private: boolean; + }> = []; + for (const project of manifest.projects) { + const { data: repo } = await octokit.repos.get({ + owner: env.GITHUB_USERNAME, + repo: project, + }); + const content = await getRepoFileContent(project, "README.md"); + + projects.push({ + name: project, + content, + language: repo.language, + url: repo.url, + private: repo.private, + }); + } + + const code = ` + const projects = ${JSON.stringify(projects, null, 2)}; + + const projectsMap = Object.fromEntries(projects.map(project => [project.name, project])); + + export const manifest = { + projects, + projectsMap + }; + `; + + await writeFile("./src/manifest.ts", code); + + spawnSync("prettier", ["--write", "./src/manifest.ts"]); + }, +}); diff --git a/package.json b/package.json index 91691ee..0863b4e 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "devDependencies": { "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "@esbuild-plugins/node-modules-polyfill": "^0.2.2", + "@octokit/rest": "^21.0.0", "@types/eslint": "^8.44.7", "@types/node": "^18.17.0", "@types/react": "^18.2.37", @@ -31,6 +32,7 @@ "@vitejs/plugin-react-swc": "^3.5.0", "autoprefixer": "^10.4.14", "clsx": "^2.1.0", + "dotenv": "^16.4.5", "eslint": "^8.54.0", "eslint-config-next": "^14.0.4", "eslint-plugin-react-hooks": "^4.6.0", @@ -46,6 +48,5 @@ }, "ct3aMetadata": { "initVersion": "7.25.1" - }, - "packageManager": "pnpm@9.1.2" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 40fa67e..c5bd543 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,6 +36,9 @@ importers: '@esbuild-plugins/node-modules-polyfill': specifier: ^0.2.2 version: 0.2.2(esbuild@0.19.12) + '@octokit/rest': + specifier: ^21.0.0 + version: 21.0.0 '@types/eslint': specifier: ^8.44.7 version: 8.56.1 @@ -63,6 +66,9 @@ importers: clsx: specifier: ^2.1.0 version: 2.1.0 + dotenv: + specifier: ^16.4.5 + version: 16.4.5 eslint: specifier: ^8.54.0 version: 8.56.0 @@ -328,6 +334,58 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@octokit/auth-token@5.1.1': + resolution: {integrity: sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==} + engines: {node: '>= 18'} + + '@octokit/core@6.1.2': + resolution: {integrity: sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==} + engines: {node: '>= 18'} + + '@octokit/endpoint@10.1.1': + resolution: {integrity: sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==} + engines: {node: '>= 18'} + + '@octokit/graphql@8.1.1': + resolution: {integrity: sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==} + engines: {node: '>= 18'} + + '@octokit/openapi-types@22.2.0': + resolution: {integrity: sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==} + + '@octokit/plugin-paginate-rest@11.3.3': + resolution: {integrity: sha512-o4WRoOJZlKqEEgj+i9CpcmnByvtzoUYC6I8PD2SA95M+BJ2x8h7oLcVOg9qcowWXBOdcTRsMZiwvM3EyLm9AfA==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-request-log@5.3.0': + resolution: {integrity: sha512-FiGcyjdtYPlr03ExBk/0ysIlEFIFGJQAVoPPMxL19B24bVSEiZQnVGBunNtaAF1YnvE/EFoDpXmITtRnyCiypQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@13.2.4': + resolution: {integrity: sha512-gusyAVgTrPiuXOdfqOySMDztQHv6928PQ3E4dqVGEtOvRXAKRbJR4b1zQyniIT9waqaWk/UDaoJ2dyPr7Bk7Iw==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/request-error@6.1.1': + resolution: {integrity: sha512-1mw1gqT3fR/WFvnoVpY/zUM2o/XkMs/2AszUUG9I69xn0JFLv6PGkPhNk5lbfvROs79wiS0bqiJNxfCZcRJJdg==} + engines: {node: '>= 18'} + + '@octokit/request@9.1.1': + resolution: {integrity: sha512-pyAguc0p+f+GbQho0uNetNQMmLG1e80WjkIaqqgUkihqUp0boRU6nKItXO4VWnr+nbZiLGEyy4TeKRwqaLvYgw==} + engines: {node: '>= 18'} + + '@octokit/rest@21.0.0': + resolution: {integrity: sha512-XudXXOmiIjivdjNZ+fN71NLrnDM00sxSZlhqmPR3v0dVoJwyP628tSlc12xqn8nX3N0965583RBw5GPo6r8u4Q==} + engines: {node: '>= 18'} + + '@octokit/types@13.5.0': + resolution: {integrity: sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -707,6 +765,9 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + before-after-hook@3.0.2: + resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} + binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -846,6 +907,10 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1967,6 +2032,9 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + universal-user-agent@7.0.2: + resolution: {integrity: sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==} + update-browserslist-db@1.0.13: resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true @@ -2225,6 +2293,67 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.16.0 + '@octokit/auth-token@5.1.1': {} + + '@octokit/core@6.1.2': + dependencies: + '@octokit/auth-token': 5.1.1 + '@octokit/graphql': 8.1.1 + '@octokit/request': 9.1.1 + '@octokit/request-error': 6.1.1 + '@octokit/types': 13.5.0 + before-after-hook: 3.0.2 + universal-user-agent: 7.0.2 + + '@octokit/endpoint@10.1.1': + dependencies: + '@octokit/types': 13.5.0 + universal-user-agent: 7.0.2 + + '@octokit/graphql@8.1.1': + dependencies: + '@octokit/request': 9.1.1 + '@octokit/types': 13.5.0 + universal-user-agent: 7.0.2 + + '@octokit/openapi-types@22.2.0': {} + + '@octokit/plugin-paginate-rest@11.3.3(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + '@octokit/types': 13.5.0 + + '@octokit/plugin-request-log@5.3.0(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + + '@octokit/plugin-rest-endpoint-methods@13.2.4(@octokit/core@6.1.2)': + dependencies: + '@octokit/core': 6.1.2 + '@octokit/types': 13.5.0 + + '@octokit/request-error@6.1.1': + dependencies: + '@octokit/types': 13.5.0 + + '@octokit/request@9.1.1': + dependencies: + '@octokit/endpoint': 10.1.1 + '@octokit/request-error': 6.1.1 + '@octokit/types': 13.5.0 + universal-user-agent: 7.0.2 + + '@octokit/rest@21.0.0': + dependencies: + '@octokit/core': 6.1.2 + '@octokit/plugin-paginate-rest': 11.3.3(@octokit/core@6.1.2) + '@octokit/plugin-request-log': 5.3.0(@octokit/core@6.1.2) + '@octokit/plugin-rest-endpoint-methods': 13.2.4(@octokit/core@6.1.2) + + '@octokit/types@13.5.0': + dependencies: + '@octokit/openapi-types': 22.2.0 + '@pkgjs/parseargs@0.11.0': optional: true @@ -2598,6 +2727,8 @@ snapshots: base64-js@1.5.1: {} + before-after-hook@3.0.2: {} + binary-extensions@2.2.0: {} brace-expansion@1.1.11: @@ -2726,6 +2857,8 @@ snapshots: dependencies: esutils: 2.0.3 + dotenv@16.4.5: {} + eastasianwidth@0.2.0: {} electron-to-chromium@1.4.627: {} @@ -4033,6 +4166,8 @@ snapshots: undici-types@5.26.5: {} + universal-user-agent@7.0.2: {} + update-browserslist-db@1.0.13(browserslist@4.22.2): dependencies: browserslist: 4.22.2 diff --git a/tsconfig.node.json b/tsconfig.node.json index f4dc8ce..7ee8e2f 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -6,5 +6,5 @@ "moduleResolution": "bundler", "allowSyntheticDefaultImports": true }, - "include": ["vite.config.ts", "tailwind.config.ts"] + "include": ["build", "vite.config.ts", "tailwind.config.ts"] } diff --git a/vite.config.ts b/vite.config.ts index be6f7eb..cf842f5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,9 +4,10 @@ import tsconfigPaths from "vite-tsconfig-paths"; import rollupNodePolyfillsPlugin from "rollup-plugin-polyfill-node"; import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill"; import { NodeModulesPolyfillPlugin } from "@esbuild-plugins/node-modules-polyfill"; +import { manifest } from "./build/manifestPlugin"; const config = defineConfig({ - plugins: [tsconfigPaths(), react(), rollupNodePolyfillsPlugin()], + plugins: [manifest(), tsconfigPaths(), react(), rollupNodePolyfillsPlugin()], resolve: { alias: { events: "rollup-plugin-node-polyfills/polyfills/events",