diff --git a/package.json b/package.json index 5aa7ccd..5903df8 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "dev": "vite", "build": "vite build", - "generate:data": "node scripts/generate-data-manifest.mjs", + "generate:data": "tsx scripts/generate-data-manifest.ts", "preview": "vite preview" }, "dependencies": { @@ -15,6 +15,7 @@ "@vitejs/plugin-vue": "^2.3.4", "vite": "^2.9.16", "typescript": "^4.9.5", - "vue-tsc": "^1.0.24" + "vue-tsc": "^1.0.24", + "tsx": "^4.7.0" } } diff --git a/scripts/generate-data-manifest.mjs b/scripts/generate-data-manifest.mjs deleted file mode 100644 index 33a9fa9..0000000 --- a/scripts/generate-data-manifest.mjs +++ /dev/null @@ -1,68 +0,0 @@ -import fs from 'fs' -import path from 'path' - -const projectRoot = process.cwd() -const dataRoot = path.join(projectRoot, 'public', 'Data') -const outputPath = path.join(projectRoot, 'public', 'data.json') - -const isDirectory = (target) => { - try { - return fs.statSync(target).isDirectory() - } catch { - return false - } -} - -const listDirectories = (target) => { - if (!isDirectory(target)) return [] - return fs.readdirSync(target).filter((entry) => isDirectory(path.join(target, entry))) -} - -const buildManifest = () => { - const games = [] - const gameNames = listDirectories(dataRoot) - gameNames.forEach((gameName) => { - const gamePath = path.join(dataRoot, gameName) - const categories = [] - const categoryNames = listDirectories(gamePath) - categoryNames.forEach((categoryName) => { - const songsDir = path.join(gamePath, categoryName, 'Songs') - const answersDir = path.join(gamePath, categoryName, 'Answers') - const clues = [] - for (let i = 1; i <= 5; i += 1) { - const songPath = path.join('Data', gameName, categoryName, 'Songs', `${i}.mp3`) - const answerPath = path.join('Data', gameName, categoryName, 'Answers', `${i}.mp3`) - const songExists = fs.existsSync(path.join(dataRoot, gameName, categoryName, 'Songs', `${i}.mp3`)) - const answerExists = fs.existsSync( - path.join(dataRoot, gameName, categoryName, 'Answers', `${i}.mp3`) - ) - clues.push({ - number: i, - points: i * 100, - song: songExists ? `/${songPath.replace(/\\/g, '/')}` : null, - answer: answerExists ? `/${answerPath.replace(/\\/g, '/')}` : null - }) - } - categories.push({ - name: categoryName, - clues - }) - }) - games.push({ - name: gameName, - categories - }) - }) - return games -} - -const main = () => { - if (!isDirectory(dataRoot)) { - fs.writeFileSync(outputPath, JSON.stringify([], null, 2)) - return - } - const manifest = buildManifest() - fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2)) -} - -main() diff --git a/scripts/generate-data-manifest.ts b/scripts/generate-data-manifest.ts new file mode 100644 index 0000000..1310b6e --- /dev/null +++ b/scripts/generate-data-manifest.ts @@ -0,0 +1,105 @@ +import fs from 'fs' +import path from 'path' + +type Clue = { + number: number + points: number + song: string | null + answer: string | null +} + +type Category = { + name: string + clues: Clue[] +} + +type Game = { + name: string + categories: Category[] +} + +const projectRoot = process.cwd() +const publicRoot = path.join(projectRoot, 'public') +const dataRoot = path.join(publicRoot, 'Data') +const outputPath = path.join(publicRoot, 'data.json') + +const isDirectory = (target: string) => { + try { + return fs.statSync(target).isDirectory() + } catch { + return false + } +} + +const listDirectories = (target: string) => { + if (!isDirectory(target)) return [] + return fs.readdirSync(target).filter((entry) => isDirectory(path.join(target, entry))) +} + +const findSubdirCaseInsensitive = (baseDir: string, desiredName: string) => { + if (!isDirectory(baseDir)) return null + const entries = fs.readdirSync(baseDir) + const match = entries.find( + (entry) => entry.toLowerCase() === desiredName.toLowerCase() && isDirectory(path.join(baseDir, entry)) + ) + return match ? path.join(baseDir, match) : null +} + +const findFileCaseInsensitive = (baseDir: string, fileName: string) => { + if (!isDirectory(baseDir)) return null + const entries = fs.readdirSync(baseDir) + const match = entries.find((entry) => entry.toLowerCase() === fileName.toLowerCase()) + return match ? path.join(baseDir, match) : null +} + +const buildManifest = (): Game[] => { + const games: Game[] = [] + const gameNames = listDirectories(dataRoot) + gameNames.forEach((gameName) => { + const gamePath = path.join(dataRoot, gameName) + const categories: Category[] = [] + const categoryNames = listDirectories(gamePath) + categoryNames.forEach((categoryName) => { + const categoryPath = path.join(gamePath, categoryName) + const songsDir = findSubdirCaseInsensitive(categoryPath, 'Songs') + const answersDir = findSubdirCaseInsensitive(categoryPath, 'Answers') + const clues: Clue[] = [] + for (let i = 1; i <= 5; i += 1) { + const songFile = songsDir ? findFileCaseInsensitive(songsDir, `${i}.mp3`) : null + const answerFile = answersDir ? findFileCaseInsensitive(answersDir, `${i}.mp3`) : null + const songRelPath = songFile + ? `/${path.relative(publicRoot, songFile).replace(/\\/g, '/')}` + : null + const answerRelPath = answerFile + ? `/${path.relative(publicRoot, answerFile).replace(/\\/g, '/')}` + : null + clues.push({ + number: i, + points: i * 100, + song: songRelPath, + answer: answerRelPath + }) + } + categories.push({ + name: categoryName, + clues + }) + }) + games.push({ + name: gameName, + categories + }) + }) + return games +} + +const main = () => { + if (!isDirectory(dataRoot)) { + fs.writeFileSync(outputPath, JSON.stringify([], null, 2)) + return + } + const manifest = buildManifest() + fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2)) +} + +main()