{"version":3,"file":"judgeByStaticAnalysis.cjs","names":["fs","path","findLanguageDefinitionByPath","removeCommentsInSourceCode","DecisionCode"],"sources":["../../src/helpers/judgeByStaticAnalysis.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { DecisionCode } from '../types/decisionCode.js';\nimport type { ProblemMarkdownFrontMatter } from '../types/problem.js';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\nimport { findLanguageDefinitionByPath } from './findLanguageDefinitionByPath.js';\nimport { removeCommentsInSourceCode } from './removeCommentsInSourceCode.js';\n\nexport async function judgeByStaticAnalysis(\n  cwd: string,\n  problemMarkdownFrontMatterLike: Pick<\n    ProblemMarkdownFrontMatter,\n    'forbiddenRegExpsInCode' | 'forbiddenTextsInCode' | 'requiredRegExpsInCode' | 'requiredSubmissionFilePaths'\n  >\n): Promise<Pick<TestCaseResult, 'decisionCode' | 'feedbackMarkdown'> | undefined> {\n  const filePathSet = new Set<string>();\n  const sourceCodeWithoutCommentFiles: { path: string; data: string }[] = [];\n\n  for (const dirent of await fs.promises.readdir(cwd, { withFileTypes: true, recursive: true })) {\n    if (!dirent.isFile()) continue;\n\n    const relativePath = path.relative(cwd, path.join(dirent.parentPath, dirent.name));\n    filePathSet.add(relativePath);\n\n    const text = await fs.promises.readFile(path.join(dirent.parentPath, dirent.name), 'utf8');\n    const isBinary = text.includes('\\uFFFD');\n    if (isBinary) continue;\n\n    const languageDefinition = findLanguageDefinitionByPath(dirent.name);\n    if (!languageDefinition) continue;\n\n    sourceCodeWithoutCommentFiles.push({\n      path: dirent.name,\n      data: languageDefinition.grammer ? removeCommentsInSourceCode(languageDefinition.grammer, text) : text,\n    });\n  }\n\n  if (problemMarkdownFrontMatterLike.requiredSubmissionFilePaths) {\n    const missingFilePaths = problemMarkdownFrontMatterLike.requiredSubmissionFilePaths\n      .filter((p) => !filePathSet.has(p))\n      .toSorted();\n\n    if (missingFilePaths.length > 0) {\n      return {\n        decisionCode: DecisionCode.MISSING_REQUIRED_SUBMISSION_FILE_ERROR,\n        feedbackMarkdown: `ファイルが不足しています。\n次のファイルを追加してから再度提出してください。\n\n${missingFilePaths.map((p) => `- \\`${p}\\``).join('\\n')}\n`,\n      };\n    }\n  }\n\n  const forbiddenFounds: { pattern: string; path: string; match: string }[] = [];\n\n  for (const file of sourceCodeWithoutCommentFiles) {\n    for (const pattern of problemMarkdownFrontMatterLike.forbiddenRegExpsInCode ?? []) {\n      const re = new RegExp(pattern, 'g');\n      const mathces = file.data.matchAll(re);\n      for (const match of mathces) forbiddenFounds.push({ pattern: re.toString(), path: file.path, match: match[0] });\n    }\n    for (const pattern of problemMarkdownFrontMatterLike.forbiddenTextsInCode ?? []) {\n      let p = 0;\n      while (p < file.data.length) {\n        const index = file.data.indexOf(pattern, p);\n        if (index === -1) break;\n        forbiddenFounds.push({ pattern, path: file.path, match: pattern });\n        p = index + pattern.length;\n      }\n    }\n  }\n\n  if (forbiddenFounds.length > 0) {\n    return {\n      decisionCode: DecisionCode.FORBIDDEN_PATTERNS_IN_CODE_ERROR,\n      feedbackMarkdown: `ソースコード中に禁止された文字列が含まれています。\nソースコードを修正してから再度提出してください。\n\n| ファイル | 禁止パターン | 文字列 |\n| -------- | ------------ | ------ |\n${forbiddenFounds.map((f) => `| \\`${f.path}\\` | \\`${f.pattern}\\` | \\`${f.match}\\` |`).join('\\n')}\n`,\n    };\n  }\n\n  const missingRequiredPatterns: string[] = [];\n\n  for (const pattern of problemMarkdownFrontMatterLike.requiredRegExpsInCode ?? []) {\n    const re = new RegExp(pattern);\n    const isFound = sourceCodeWithoutCommentFiles.some((f) => re.test(f.data));\n    if (!isFound) missingRequiredPatterns.push(re.toString());\n  }\n\n  if (missingRequiredPatterns.length > 0) {\n    return {\n      decisionCode: DecisionCode.REQUIRED_PATTERNS_IN_CODE_ERROR,\n      feedbackMarkdown: `ソースコード中に必要な文字列が含まれていません。\nソースコードを修正してから再度提出してください。\n\n${missingRequiredPatterns.map((p) => `- \\`${p}\\``).join('\\n')}\n`,\n    };\n  }\n\n  return;\n}\n"],"mappings":"6RAUA,eAAsB,EACpB,EACA,EAIgF,CAChF,IAAM,EAAc,IAAI,IAClB,EAAkE,CAAC,EAEzE,IAAK,IAAM,KAAU,MAAMA,EAAAA,QAAG,SAAS,QAAQ,EAAK,CAAE,cAAe,GAAM,UAAW,EAAK,CAAC,EAAG,CAC7F,GAAI,CAAC,EAAO,OAAO,EAAG,SAEtB,IAAM,EAAeC,EAAAA,QAAK,SAAS,EAAKA,EAAAA,QAAK,KAAK,EAAO,WAAY,EAAO,IAAI,CAAC,EACjF,EAAY,IAAI,CAAY,EAE5B,IAAM,EAAO,MAAMD,EAAAA,QAAG,SAAS,SAASC,EAAAA,QAAK,KAAK,EAAO,WAAY,EAAO,IAAI,EAAG,MAAM,EAEzF,GADiB,EAAK,SAAS,GACpB,EAAG,SAEd,IAAM,EAAqBC,EAAAA,6BAA6B,EAAO,IAAI,EAC9D,GAEL,EAA8B,KAAK,CACjC,KAAM,EAAO,KACb,KAAM,EAAmB,QAAUC,EAAAA,2BAA2B,EAAmB,QAAS,CAAI,EAAI,CACpG,CAAC,CACH,CAEA,GAAI,EAA+B,4BAA6B,CAC9D,IAAM,EAAmB,EAA+B,4BACrD,OAAQ,GAAM,CAAC,EAAY,IAAI,CAAC,CAAC,CAAC,CAClC,SAAS,EAEZ,GAAI,EAAiB,OAAS,EAC5B,MAAO,CACL,aAAcC,EAAAA,aAAa,uCAC3B,iBAAkB;;;EAGxB,EAAiB,IAAK,GAAM,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK;CAAI,EAAE;CAEjD,CAEJ,CAEA,IAAM,EAAsE,CAAC,EAE7E,IAAK,IAAM,KAAQ,EAA+B,CAChD,IAAK,IAAM,KAAW,EAA+B,wBAA0B,CAAC,EAAG,CACjF,IAAM,EAAK,IAAI,OAAO,EAAS,GAAG,EAC5B,EAAU,EAAK,KAAK,SAAS,CAAE,EACrC,IAAK,IAAM,KAAS,EAAS,EAAgB,KAAK,CAAE,QAAS,EAAG,SAAS,EAAG,KAAM,EAAK,KAAM,MAAO,EAAM,EAAG,CAAC,CAChH,CACA,IAAK,IAAM,KAAW,EAA+B,sBAAwB,CAAC,EAAG,CAC/E,IAAI,EAAI,EACR,KAAO,EAAI,EAAK,KAAK,QAAQ,CAC3B,IAAM,EAAQ,EAAK,KAAK,QAAQ,EAAS,CAAC,EAC1C,GAAI,IAAU,GAAI,MAClB,EAAgB,KAAK,CAAE,UAAS,KAAM,EAAK,KAAM,MAAO,CAAQ,CAAC,EACjE,EAAI,EAAQ,EAAQ,MACtB,CACF,CACF,CAEA,GAAI,EAAgB,OAAS,EAC3B,MAAO,CACL,aAAcA,EAAAA,aAAa,iCAC3B,iBAAkB;;;;;EAKtB,EAAgB,IAAK,GAAM,OAAO,EAAE,KAAK,SAAS,EAAE,QAAQ,SAAS,EAAE,MAAM,KAAK,CAAC,CAAC,KAAK;CAAI,EAAE;CAE7F,EAGF,IAAM,EAAoC,CAAC,EAE3C,IAAK,IAAM,KAAW,EAA+B,uBAAyB,CAAC,EAAG,CAChF,IAAM,EAAK,IAAI,OAAO,CAAO,EACb,EAA8B,KAAM,GAAM,EAAG,KAAK,EAAE,IAAI,CAC7D,GAAG,EAAwB,KAAK,EAAG,SAAS,CAAC,CAC1D,CAEA,GAAI,EAAwB,OAAS,EACnC,MAAO,CACL,aAAcA,EAAAA,aAAa,gCAC3B,iBAAkB;;;EAGtB,EAAwB,IAAK,GAAM,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK;CAAI,EAAE;CAE1D,CAIJ"}