From 3f948c223718bcfda46bdb486f65f29587ecfa04 Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Fri, 8 May 2026 11:00:50 -0700 Subject: [PATCH 1/7] Add LLM content generator with raw markdown serving Generate llms.txt, llms-full.txt, and per-page raw markdown files (/{slug}/index.md) so LLMs and users can access source markdown alongside rendered pages. The generator runs as a GitHub Action during deploy and locally via `npm run start:local`. Co-Authored-By: Claude Opus 4.6 --- .../actions/generate-llm-content/action.yml | 33 + .../generate-llm-content/package-lock.json | 984 ++++++++++++++++++ .../actions/generate-llm-content/package.json | 21 + .../actions/generate-llm-content/src/index.ts | 237 +++++ .../src/templateRenderer.ts | 22 + .../src/templates/llms-full.txt.hbs | 10 + .../src/templates/llms.txt.hbs | 19 + .../generate-llm-content/tsconfig.json | 15 + .github/workflows/deploy.yaml | 5 + .gitignore | 6 + .nvmrc | 1 + package.json | 3 +- 12 files changed, 1355 insertions(+), 1 deletion(-) create mode 100644 .github/actions/generate-llm-content/action.yml create mode 100644 .github/actions/generate-llm-content/package-lock.json create mode 100644 .github/actions/generate-llm-content/package.json create mode 100644 .github/actions/generate-llm-content/src/index.ts create mode 100644 .github/actions/generate-llm-content/src/templateRenderer.ts create mode 100644 .github/actions/generate-llm-content/src/templates/llms-full.txt.hbs create mode 100644 .github/actions/generate-llm-content/src/templates/llms.txt.hbs create mode 100644 .github/actions/generate-llm-content/tsconfig.json create mode 100644 .nvmrc diff --git a/.github/actions/generate-llm-content/action.yml b/.github/actions/generate-llm-content/action.yml new file mode 100644 index 0000000..0d91eb1 --- /dev/null +++ b/.github/actions/generate-llm-content/action.yml @@ -0,0 +1,33 @@ +name: "Generate LLM Content" +description: "Generates llms.txt and clean .md files from Luau documentation" + +inputs: + output_directory: + description: "Directory to write output files (llms.txt, llms-full.txt, llm/**)" + required: true + default: "./public" + base_url: + description: "Base URL for generated links (e.g., https://luau.org)" + required: true + +outputs: + file_count: + description: "Number of .md files generated" + value: ${{ steps.generate.outputs.file_count }} + +runs: + using: "composite" + steps: + - name: Install action dependencies + shell: bash + working-directory: ${{ github.action_path }} + run: npm ci --quiet + + - name: Generate LLM content + id: generate + shell: bash + working-directory: ${{ github.action_path }} + env: + OUTPUT_DIR: ${{ inputs.output_directory }} + BASE_URL: ${{ inputs.base_url }} + run: npx tsx src/index.ts diff --git a/.github/actions/generate-llm-content/package-lock.json b/.github/actions/generate-llm-content/package-lock.json new file mode 100644 index 0000000..894a759 --- /dev/null +++ b/.github/actions/generate-llm-content/package-lock.json @@ -0,0 +1,984 @@ +{ + "name": "luau-generate-llm-content", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "luau-generate-llm-content", + "version": "1.0.0", + "dependencies": { + "glob": "^11.0.0", + "gray-matter": "^4.0.3", + "handlebars": "^4.7.8" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "22.19.18", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/@types/node/-/node-22.19.18.tgz", + "integrity": "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "11.3.6", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://artifactory.rbx.com/artifactory/api/npm/npm-all/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + } + } +} diff --git a/.github/actions/generate-llm-content/package.json b/.github/actions/generate-llm-content/package.json new file mode 100644 index 0000000..f2a60dc --- /dev/null +++ b/.github/actions/generate-llm-content/package.json @@ -0,0 +1,21 @@ +{ + "name": "luau-generate-llm-content", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "start": "tsx src/index.ts", + "start:local": "OUTPUT_DIR=public BASE_URL=http://localhost:4321 tsx src/index.ts", + "generate": "tsx src/index.ts" + }, + "dependencies": { + "glob": "^11.0.0", + "gray-matter": "^4.0.3", + "handlebars": "^4.7.8" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0" + } +} diff --git a/.github/actions/generate-llm-content/src/index.ts b/.github/actions/generate-llm-content/src/index.ts new file mode 100644 index 0000000..10941d7 --- /dev/null +++ b/.github/actions/generate-llm-content/src/index.ts @@ -0,0 +1,237 @@ +import fs from "node:fs"; +import path from "node:path"; +import { glob } from "glob"; +import matter from "gray-matter"; +import { renderTemplate } from "./templateRenderer.js"; + +interface Config { + contentDir: string; + outputDir: string; + baseUrl: string; +} + +interface Section { + dir: string; + title: string; +} + +const SECTIONS: Section[] = [ + { dir: "getting-started", title: "Getting Started" }, + { dir: "guides", title: "Advanced Users" }, + { dir: "types", title: "Type System" }, + { dir: "reference", title: "Reference" }, +]; + +interface DocFile { + relativePath: string; + section: string; + sectionTitle: string; + title: string; + description?: string; + order: number; + body: string; + slug: string; +} + +function loadConfig(): Config { + const actionDir = path.dirname(new URL(import.meta.url).pathname); + // actionDir is .github/actions/generate-llm-content/src — repo root is 4 levels up + const repoRoot = path.resolve(actionDir, "../../../.."); + + const outputDir = process.env.OUTPUT_DIR + ? path.resolve(repoRoot, process.env.OUTPUT_DIR) + : path.join(repoRoot, "public"); + + const baseUrl = (process.env.BASE_URL || "https://luau.org").replace( + /\/$/, + "", + ); + + const contentDir = path.join(repoRoot, "src", "content", "docs"); + + return { contentDir, outputDir, baseUrl }; +} + +function discoverFiles(contentDir: string): string[] { + return glob.sync("**/*.md", { cwd: contentDir }); +} + +function parseFile(relativePath: string, contentDir: string): DocFile | null { + const fullPath = path.join(contentDir, relativePath); + const raw = fs.readFileSync(fullPath, "utf-8"); + const { data, content } = matter(raw); + + const dir = relativePath.split("/")[0]; + const section = SECTIONS.find((s) => s.dir === dir); + if (!section) return null; + + const slug = data.slug || relativePath.replace(/\.md$/, ""); + + return { + relativePath, + section: section.dir, + sectionTitle: section.title, + title: data.title || slug, + description: data.description, + order: data.sidebar?.order ?? 999, + body: content.trim(), + slug, + }; +} + +function groupAndSort(docs: DocFile[]): Map { + const groups = new Map(); + + for (const section of SECTIONS) { + groups.set(section.title, []); + } + + for (const doc of docs) { + const group = groups.get(doc.sectionTitle); + if (group) group.push(doc); + } + + for (const [, files] of groups) { + files.sort((a, b) => a.order - b.order); + } + + return groups; +} + +function generateLlmsTxt( + groups: Map, + config: Config, +): string { + const sections = []; + for (const [title, docs] of groups) { + if (docs.length === 0) continue; + sections.push({ + title, + entries: docs.map((doc) => ({ + title: doc.title, + url: `${config.baseUrl}/${doc.slug}/index.md`, + description: doc.description, + })), + }); + } + + return renderTemplate("llms.txt.hbs", { baseUrl: config.baseUrl, sections }); +} + +function generateLlmsFullTxt( + groups: Map, + config: Config, +): string { + const documents = []; + for (const [, docs] of groups) { + for (const doc of docs) { + documents.push({ + url: `${config.baseUrl}/${doc.slug}/index.md`, + escapedTitle: doc.title.replace(/"/g, """), + markdown: doc.body, + }); + } + } + + return renderTemplate("llms-full.txt.hbs", { documents }); +} + +function ensureDir(filePath: string): void { + const dir = path.dirname(filePath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +} + +function writeRawMdFiles(docs: DocFile[], config: Config): void { + for (const doc of docs) { + const rawPath = path.join(config.contentDir, doc.relativePath); + const raw = fs.readFileSync(rawPath, "utf-8"); + const outPath = path.join(config.outputDir, doc.slug, "index.md"); + ensureDir(outPath); + fs.writeFileSync(outPath, raw, "utf-8"); + } +} + +function writeCleanMdFiles(docs: DocFile[], config: Config): void { + for (const doc of docs) { + const outPath = path.join(config.outputDir, "llm", `${doc.slug}.md`); + ensureDir(outPath); + + const siteUrl = `${config.baseUrl}/${doc.slug}`; + const lines = [ + "---", + `title: "${doc.title.replace(/"/g, '\\"')}"`, + `url: ${siteUrl}`, + ]; + if (doc.description) { + lines.push(`description: "${doc.description.replace(/"/g, '\\"')}"`); + } + lines.push("---", "", ""); + + fs.writeFileSync(outPath, lines.join("\n") + doc.body + "\n", "utf-8"); + } +} + +function main() { + const config = loadConfig(); + + console.log("=== Luau LLM Content Generator ==="); + console.log(`Content dir: ${config.contentDir}`); + console.log(`Output dir: ${config.outputDir}`); + console.log(`Base URL: ${config.baseUrl}`); + console.log(""); + + // 1. Discover + const files = discoverFiles(config.contentDir); + console.log(`Discovered ${files.length} markdown files`); + + // 2. Parse + const docs: DocFile[] = []; + for (const file of files) { + const parsed = parseFile(file, config.contentDir); + if (parsed) docs.push(parsed); + } + console.log(`Parsed ${docs.length} documentation files`); + + // 3. Group and sort + const groups = groupAndSort(docs); + + // 4. Write llms.txt + const llmsTxt = generateLlmsTxt(groups, config); + const llmsTxtPath = path.join(config.outputDir, "llms.txt"); + ensureDir(llmsTxtPath); + fs.writeFileSync(llmsTxtPath, llmsTxt, "utf-8"); + console.log( + `Written: llms.txt (${(Buffer.byteLength(llmsTxt) / 1024).toFixed(1)} KB)`, + ); + + // 5. Write llms-full.txt + const llmsFullTxt = generateLlmsFullTxt(groups, config); + const llmsFullTxtPath = path.join(config.outputDir, "llms-full.txt"); + fs.writeFileSync(llmsFullTxtPath, llmsFullTxt, "utf-8"); + console.log( + `Written: llms-full.txt (${(Buffer.byteLength(llmsFullTxt) / 1024).toFixed(1)} KB)`, + ); + + // 6. Write individual .md files + writeCleanMdFiles(docs, config); + console.log(`Written: ${docs.length} files under llm/`); + + // 7. Write raw markdown files at /{slug}/index.md + writeRawMdFiles(docs, config); + console.log(`Written: ${docs.length} raw markdown files at {slug}/index.md`); + + // Output for GitHub Actions + if (process.env.GITHUB_OUTPUT) { + fs.appendFileSync( + process.env.GITHUB_OUTPUT, + `file_count=${docs.length}\n`, + ); + } + + console.log(""); + console.log("=== Done ==="); +} + +main(); diff --git a/.github/actions/generate-llm-content/src/templateRenderer.ts b/.github/actions/generate-llm-content/src/templateRenderer.ts new file mode 100644 index 0000000..a858bc8 --- /dev/null +++ b/.github/actions/generate-llm-content/src/templateRenderer.ts @@ -0,0 +1,22 @@ +import Handlebars from "handlebars"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const TEMPLATES_DIR = path.join(__dirname, "templates"); +const cache = new Map(); + +export function renderTemplate( + name: string, + data: Record, +): string { + if (!cache.has(name)) { + const templatePath = path.join(TEMPLATES_DIR, name); + const raw = fs.readFileSync(templatePath, "utf-8"); + cache.set(name, Handlebars.compile(raw, { noEscape: true })); + } + return cache.get(name)!(data).trim() + "\n"; +} diff --git a/.github/actions/generate-llm-content/src/templates/llms-full.txt.hbs b/.github/actions/generate-llm-content/src/templates/llms-full.txt.hbs new file mode 100644 index 0000000..14986c7 --- /dev/null +++ b/.github/actions/generate-llm-content/src/templates/llms-full.txt.hbs @@ -0,0 +1,10 @@ +# Luau Programming Language — Full Content + +> This file contains the full content of all Luau documentation pages, suitable for use as LLM context. + +{{#each documents}} + +{{markdown}} + + +{{/each}} diff --git a/.github/actions/generate-llm-content/src/templates/llms.txt.hbs b/.github/actions/generate-llm-content/src/templates/llms.txt.hbs new file mode 100644 index 0000000..d0d4ab6 --- /dev/null +++ b/.github/actions/generate-llm-content/src/templates/llms.txt.hbs @@ -0,0 +1,19 @@ +# Luau Programming Language + +> Luau is a small, fast, and embeddable programming language derived from Lua with a gradual type system. + +Luau (lowercase u, /ˈlu.aʊ/) is a programming language derived from Lua 5.1, designed for performance, safety, and embeddability. It features a state-of-the-art gradual type system, a fast interpreter with an optional JIT compiler, and is used as the scripting language for the Roblox platform. + +- Full content (single file): {{baseUrl}}/llms-full.txt +- This index: {{baseUrl}}/llms.txt + +--- + +{{#each sections}} +## {{title}} + +{{#each entries}} +- [{{title}}]({{url}}){{#if description}}: {{description}}{{/if}} +{{/each}} + +{{/each}} diff --git a/.github/actions/generate-llm-content/tsconfig.json b/.github/actions/generate-llm-content/tsconfig.json new file mode 100644 index 0000000..f9201b1 --- /dev/null +++ b/.github/actions/generate-llm-content/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*.ts"] +} diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 9aea3c5..ed18684 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -15,6 +15,11 @@ jobs: steps: - name: Checkout your repository using git uses: actions/checkout@v5 + - name: Generate LLM content + uses: ./.github/actions/generate-llm-content + with: + output_directory: ./public + base_url: https://luau.org - name: Install, build, and upload your site uses: luau-lang/astro-deploy@v6 diff --git a/.gitignore b/.gitignore index 6240da8..b68e2ab 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,9 @@ pnpm-debug.log* # macOS-specific files .DS_Store + +# generated LLM content (built by .github/actions/generate-llm-content) +public/llms.txt +public/llms-full.txt +public/llm/ +public/**/index.md diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..13f4d2c --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v24.13.0 \ No newline at end of file diff --git a/package.json b/package.json index 7d7d81c..abbca47 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "start": "astro dev", "build": "astro build", "preview": "astro preview", - "astro": "astro" + "astro": "astro", + "generate:llms": "cd .github/actions/generate-llm-content && npx tsx src/index.ts" }, "dependencies": { "@astrojs/rss": "^4.0.14", From 9939e121ac80762ed37ca627b168fe2bb80bf2c4 Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Fri, 8 May 2026 11:01:29 -0700 Subject: [PATCH 2/7] add the claude command --- .claude/commands/start-local.md | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .claude/commands/start-local.md diff --git a/.claude/commands/start-local.md b/.claude/commands/start-local.md new file mode 100644 index 0000000..e9cc3b8 --- /dev/null +++ b/.claude/commands/start-local.md @@ -0,0 +1,63 @@ +Start the Luau documentation site locally with LLM content generation. + +## What to do + +Run the following steps **sequentially**. Stop and report if any step fails. + +### 1. Prerequisites check + +Verify that `node` (v22+) is available on PATH: + +```bash +node --version +``` + +### 2. Install dependencies + +Install the site dependencies if not already present: + +```bash +ls node_modules/.bin/astro 2>/dev/null || npm install +``` + +Install the LLM action dependencies if not already present: + +```bash +ls .github/actions/generate-llm-content/node_modules/.bin/tsx 2>/dev/null || (cd .github/actions/generate-llm-content && npm install) +``` + +### 3. Generate LLM content + +Run the generator to produce `llms.txt`, `llms-full.txt`, and `llm/**/*.md` into `public/`: + +```bash +cd .github/actions/generate-llm-content && npm run start:local +``` + +Confirm the output exists: + +```bash +ls public/llms.txt public/llms-full.txt +find public/llm -name '*.md' | wc -l +``` + +### 4. Start the dev server + +Start the Astro dev server: + +```bash +npm run dev +``` + +This serves the site at `http://localhost:4321`. The LLM files are available at: + +- http://localhost:4321/llms.txt +- http://localhost:4321/llms-full.txt +- http://localhost:4321/llm/types/basic-types.md + +### 5. Report + +Print a summary: +- Number of LLM docs generated +- Dev server URL +- Confirm `llms.txt` is accessible via the dev server From b5da0645a3cf71d47df5b704a561612f9fd7b2e2 Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Fri, 8 May 2026 11:23:45 -0700 Subject: [PATCH 3/7] Generate robots.txt and sitemap.xml in the LLM content action Both files use the BASE_URL env var so they point to localhost in development and https://luau.org in production. Co-Authored-By: Claude Opus 4.6 --- .../actions/generate-llm-content/src/index.ts | 40 +++++++++++++++++++ .gitignore | 4 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/.github/actions/generate-llm-content/src/index.ts b/.github/actions/generate-llm-content/src/index.ts index 10941d7..7f6824c 100644 --- a/.github/actions/generate-llm-content/src/index.ts +++ b/.github/actions/generate-llm-content/src/index.ts @@ -173,6 +173,36 @@ function writeCleanMdFiles(docs: DocFile[], config: Config): void { } } +function generateRobotsTxt(config: Config): string { + return [ + "User-agent: *", + "Allow: /", + "", + `Sitemap: ${config.baseUrl}/sitemap.xml`, + "", + `# LLM content`, + `# Index: ${config.baseUrl}/llms.txt`, + `# Full: ${config.baseUrl}/llms-full.txt`, + "", + ].join("\n"); +} + +function generateSitemapXml(docs: DocFile[], config: Config): string { + const today = new Date().toISOString().split("T")[0]; + const urls = docs.map( + (doc) => + ` \n ${config.baseUrl}/${doc.slug}\n ${today}\n `, + ); + + return [ + '', + '', + ...urls, + "", + "", + ].join("\n"); +} + function main() { const config = loadConfig(); @@ -222,6 +252,16 @@ function main() { writeRawMdFiles(docs, config); console.log(`Written: ${docs.length} raw markdown files at {slug}/index.md`); + // 8. Write robots.txt + const robotsTxt = generateRobotsTxt(config); + fs.writeFileSync(path.join(config.outputDir, "robots.txt"), robotsTxt, "utf-8"); + console.log("Written: robots.txt"); + + // 9. Write sitemap.xml + const sitemapXml = generateSitemapXml(docs, config); + fs.writeFileSync(path.join(config.outputDir, "sitemap.xml"), sitemapXml, "utf-8"); + console.log(`Written: sitemap.xml (${docs.length} URLs)`); + // Output for GitHub Actions if (process.env.GITHUB_OUTPUT) { fs.appendFileSync( diff --git a/.gitignore b/.gitignore index b68e2ab..2aea725 100644 --- a/.gitignore +++ b/.gitignore @@ -20,8 +20,10 @@ pnpm-debug.log* # macOS-specific files .DS_Store -# generated LLM content (built by .github/actions/generate-llm-content) +# generated content (built by .github/actions/generate-llm-content) public/llms.txt public/llms-full.txt public/llm/ public/**/index.md +public/robots.txt +public/sitemap.xml From 764ff0116d7130e62105e15cdde115cce88cc2bb Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Fri, 8 May 2026 11:30:24 -0700 Subject: [PATCH 4/7] Use Handlebars templates for robots.txt and sitemap.xml Consistent with the existing llms.txt and llms-full.txt templates. Co-Authored-By: Claude Opus 4.6 --- .../actions/generate-llm-content/src/index.ts | 29 ++++--------------- .../src/templates/robots.txt.hbs | 8 +++++ .../src/templates/sitemap.xml.hbs | 9 ++++++ 3 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 .github/actions/generate-llm-content/src/templates/robots.txt.hbs create mode 100644 .github/actions/generate-llm-content/src/templates/sitemap.xml.hbs diff --git a/.github/actions/generate-llm-content/src/index.ts b/.github/actions/generate-llm-content/src/index.ts index 7f6824c..a28d8ed 100644 --- a/.github/actions/generate-llm-content/src/index.ts +++ b/.github/actions/generate-llm-content/src/index.ts @@ -174,33 +174,16 @@ function writeCleanMdFiles(docs: DocFile[], config: Config): void { } function generateRobotsTxt(config: Config): string { - return [ - "User-agent: *", - "Allow: /", - "", - `Sitemap: ${config.baseUrl}/sitemap.xml`, - "", - `# LLM content`, - `# Index: ${config.baseUrl}/llms.txt`, - `# Full: ${config.baseUrl}/llms-full.txt`, - "", - ].join("\n"); + return renderTemplate("robots.txt.hbs", { baseUrl: config.baseUrl }); } function generateSitemapXml(docs: DocFile[], config: Config): string { const today = new Date().toISOString().split("T")[0]; - const urls = docs.map( - (doc) => - ` \n ${config.baseUrl}/${doc.slug}\n ${today}\n `, - ); - - return [ - '', - '', - ...urls, - "", - "", - ].join("\n"); + const urls = docs.map((doc) => ({ + loc: `${config.baseUrl}/${doc.slug}`, + lastmod: today, + })); + return renderTemplate("sitemap.xml.hbs", { urls }); } function main() { diff --git a/.github/actions/generate-llm-content/src/templates/robots.txt.hbs b/.github/actions/generate-llm-content/src/templates/robots.txt.hbs new file mode 100644 index 0000000..3f4c5b1 --- /dev/null +++ b/.github/actions/generate-llm-content/src/templates/robots.txt.hbs @@ -0,0 +1,8 @@ +User-agent: * +Allow: / + +Sitemap: {{baseUrl}}/sitemap.xml + +# LLM content +# Index: {{baseUrl}}/llms.txt +# Full: {{baseUrl}}/llms-full.txt diff --git a/.github/actions/generate-llm-content/src/templates/sitemap.xml.hbs b/.github/actions/generate-llm-content/src/templates/sitemap.xml.hbs new file mode 100644 index 0000000..ae5b313 --- /dev/null +++ b/.github/actions/generate-llm-content/src/templates/sitemap.xml.hbs @@ -0,0 +1,9 @@ + + +{{#each urls}} + + {{loc}} + {{lastmod}} + +{{/each}} + From 79475024060b2be955ae3cef835e2643bac99648 Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Fri, 8 May 2026 11:38:52 -0700 Subject: [PATCH 5/7] Remove robots.txt and sitemap.xml from generator Starlight already generates sitemap.xml at build time via @astrojs/sitemap. Use a static robots.txt in public/ instead of generating it. Co-Authored-By: Claude Opus 4.6 --- .../actions/generate-llm-content/src/index.ts | 21 ------------------- .../src/templates/robots.txt.hbs | 8 ------- .../src/templates/sitemap.xml.hbs | 9 -------- .gitignore | 2 -- public/robots.txt | 8 +++++++ 5 files changed, 8 insertions(+), 40 deletions(-) delete mode 100644 .github/actions/generate-llm-content/src/templates/robots.txt.hbs delete mode 100644 .github/actions/generate-llm-content/src/templates/sitemap.xml.hbs create mode 100644 public/robots.txt diff --git a/.github/actions/generate-llm-content/src/index.ts b/.github/actions/generate-llm-content/src/index.ts index a28d8ed..5108ab8 100644 --- a/.github/actions/generate-llm-content/src/index.ts +++ b/.github/actions/generate-llm-content/src/index.ts @@ -173,18 +173,6 @@ function writeCleanMdFiles(docs: DocFile[], config: Config): void { } } -function generateRobotsTxt(config: Config): string { - return renderTemplate("robots.txt.hbs", { baseUrl: config.baseUrl }); -} - -function generateSitemapXml(docs: DocFile[], config: Config): string { - const today = new Date().toISOString().split("T")[0]; - const urls = docs.map((doc) => ({ - loc: `${config.baseUrl}/${doc.slug}`, - lastmod: today, - })); - return renderTemplate("sitemap.xml.hbs", { urls }); -} function main() { const config = loadConfig(); @@ -235,15 +223,6 @@ function main() { writeRawMdFiles(docs, config); console.log(`Written: ${docs.length} raw markdown files at {slug}/index.md`); - // 8. Write robots.txt - const robotsTxt = generateRobotsTxt(config); - fs.writeFileSync(path.join(config.outputDir, "robots.txt"), robotsTxt, "utf-8"); - console.log("Written: robots.txt"); - - // 9. Write sitemap.xml - const sitemapXml = generateSitemapXml(docs, config); - fs.writeFileSync(path.join(config.outputDir, "sitemap.xml"), sitemapXml, "utf-8"); - console.log(`Written: sitemap.xml (${docs.length} URLs)`); // Output for GitHub Actions if (process.env.GITHUB_OUTPUT) { diff --git a/.github/actions/generate-llm-content/src/templates/robots.txt.hbs b/.github/actions/generate-llm-content/src/templates/robots.txt.hbs deleted file mode 100644 index 3f4c5b1..0000000 --- a/.github/actions/generate-llm-content/src/templates/robots.txt.hbs +++ /dev/null @@ -1,8 +0,0 @@ -User-agent: * -Allow: / - -Sitemap: {{baseUrl}}/sitemap.xml - -# LLM content -# Index: {{baseUrl}}/llms.txt -# Full: {{baseUrl}}/llms-full.txt diff --git a/.github/actions/generate-llm-content/src/templates/sitemap.xml.hbs b/.github/actions/generate-llm-content/src/templates/sitemap.xml.hbs deleted file mode 100644 index ae5b313..0000000 --- a/.github/actions/generate-llm-content/src/templates/sitemap.xml.hbs +++ /dev/null @@ -1,9 +0,0 @@ - - -{{#each urls}} - - {{loc}} - {{lastmod}} - -{{/each}} - diff --git a/.gitignore b/.gitignore index 2aea725..34931f1 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,3 @@ public/llms.txt public/llms-full.txt public/llm/ public/**/index.md -public/robots.txt -public/sitemap.xml diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..a23c2fe --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,8 @@ +User-agent: * +Allow: / + +Sitemap: https://luau.org/sitemap.xml + +# LLM content +# Index: https://luau.org/llms.txt +# Full: https://luau.org/llms-full.txt From 5415933dec34f4712c74c70bf387902774acdbd6 Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Fri, 8 May 2026 11:41:20 -0700 Subject: [PATCH 6/7] Fix sitemap path in robots.txt to match Astro's output Astro/Starlight generates sitemap-index.xml, not sitemap.xml. Co-Authored-By: Claude Opus 4.6 --- public/robots.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/robots.txt b/public/robots.txt index a23c2fe..2306d46 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,7 +1,7 @@ User-agent: * Allow: / -Sitemap: https://luau.org/sitemap.xml +Sitemap: https://luau.org/sitemap-index.xml # LLM content # Index: https://luau.org/llms.txt From acb6db3d03c666e91ee8a479a9437999d95b4978 Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Fri, 8 May 2026 12:00:16 -0700 Subject: [PATCH 7/7] Add setup-node step before LLM content generation The action needs Node.js available for npm ci and tsx. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/deploy.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index ed18684..2fedb42 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -15,6 +15,10 @@ jobs: steps: - name: Checkout your repository using git uses: actions/checkout@v5 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc - name: Generate LLM content uses: ./.github/actions/generate-llm-content with: