@exercode/problem-utils 4.3.2 → 4.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/checkAllProblems.cjs +1 -1
- package/dist/cli/checkAllProblems.cjs.map +1 -1
- package/dist/cli/checkAllProblems.js +1 -1
- package/dist/cli/checkAllProblems.js.map +1 -1
- package/dist/cli/exercodeProblem.cjs +1 -1
- package/dist/cli/exercodeProblem.cjs.map +1 -1
- package/dist/cli/exercodeProblem.js +1 -1
- package/dist/cli/exercodeProblem.js.map +1 -1
- package/dist/helpers/guiRecording.cjs +1 -1
- package/dist/helpers/guiRecording.cjs.map +1 -1
- package/dist/helpers/guiRecording.js +1 -1
- package/dist/helpers/guiRecording.js.map +1 -1
- package/dist/helpers/javaJudge.cjs +1 -1
- package/dist/helpers/javaJudge.cjs.map +1 -1
- package/dist/helpers/javaJudge.js +1 -1
- package/dist/helpers/javaJudge.js.map +1 -1
- package/dist/helpers/parseCsv.cjs +2 -2
- package/dist/helpers/parseCsv.cjs.map +1 -1
- package/dist/helpers/parseCsv.js +2 -2
- package/dist/helpers/parseCsv.js.map +1 -1
- package/dist/helpers/removeCommentsInSourceCode.cjs +4 -4
- package/dist/helpers/removeCommentsInSourceCode.cjs.map +1 -1
- package/dist/helpers/removeCommentsInSourceCode.js +4 -4
- package/dist/helpers/removeCommentsInSourceCode.js.map +1 -1
- package/dist/helpers/runHarnessProcess.cjs +1 -1
- package/dist/helpers/runHarnessProcess.cjs.map +1 -1
- package/dist/helpers/runHarnessProcess.js +1 -1
- package/dist/helpers/runHarnessProcess.js.map +1 -1
- package/dist/helpers/spawnWithLimits.cjs +1 -1
- package/dist/helpers/spawnWithLimits.cjs.map +1 -1
- package/dist/helpers/spawnWithLimits.js +1 -1
- package/dist/helpers/spawnWithLimits.js.map +1 -1
- package/dist/helpers/startHttpServer.cjs +1 -1
- package/dist/helpers/startHttpServer.cjs.map +1 -1
- package/dist/helpers/startHttpServer.js +1 -1
- package/dist/helpers/startHttpServer.js.map +1 -1
- package/dist/learningMaterial/validateCourse.cjs +1 -1
- package/dist/learningMaterial/validateCourse.cjs.map +1 -1
- package/dist/learningMaterial/validateCourse.js +1 -1
- package/dist/learningMaterial/validateCourse.js.map +1 -1
- package/dist/learningMaterial/validateProblem.cjs +1 -1
- package/dist/learningMaterial/validateProblem.cjs.map +1 -1
- package/dist/learningMaterial/validateProblem.js +1 -1
- package/dist/learningMaterial/validateProblem.js.map +1 -1
- package/dist/presets/guiCommand.cjs +1 -1
- package/dist/presets/guiCommand.cjs.map +1 -1
- package/dist/presets/guiCommand.js +1 -1
- package/dist/presets/guiCommand.js.map +1 -1
- package/package.json +7 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"javaJudge.js","names":["fs","path","MAX_OUTPUT_LENGTH","resolveJavaRelativePath","sourceCode","sourcePath","packageMatch","fallbackRelativePath","listSubmissionFiles","filePaths","collectSubmissionFiles","rootDir","currentDir","relativeDir","entries","entry","entryRelativePath","readOutputFiles","filePath","parsed","values","z","outputFileSchema","result","value","truncateOutput"],"sources":["../../src/helpers/javaJudge.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\ntype OutputFile = NonNullable<TestCaseResult['outputFiles']>[number];\nexport const MAX_OUTPUT_LENGTH = 50_000;\n\nexport async function resolveJavaRelativePath(sourcePath: string, fallbackRelativePath: string): Promise<string> {\n const sourceCode = await fs.promises.readFile(sourcePath, 'utf8');\n const packageMatch = /^\\s*package\\s+([a-zA-Z0-9_.]+)\\s*;/m.exec(sourceCode);\n if (!packageMatch?.[1]) {\n return fallbackRelativePath;\n }\n\n return path.join(packageMatch[1].replaceAll('.', '/'), path.basename(fallbackRelativePath));\n}\n\nexport async function listSubmissionFiles(rootDir: string): Promise<string[]> {\n const filePaths: string[] = [];\n await collectSubmissionFiles(rootDir, '', filePaths);\n return filePaths.toSorted();\n}\n\nasync function collectSubmissionFiles(rootDir: string, relativeDir: string, filePaths: string[]): Promise<void> {\n const currentDir = relativeDir ? path.join(rootDir, relativeDir) : rootDir;\n const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const entryRelativePath = relativeDir ? path.join(relativeDir, entry.name) : entry.name;\n if (entry.isDirectory()) {\n await collectSubmissionFiles(rootDir, entryRelativePath, filePaths);\n continue;\n }\n if (entry.isFile()) {\n filePaths.push(entryRelativePath);\n }\n }\n}\n\nexport function readOutputFiles(filePath: string): OutputFile[] | undefined {\n if (!fs.existsSync(filePath)) {\n return undefined;\n }\n\n // Malformed evaluator JSON remains a harness error (JUDGE_NOT_AVAILABLE), not missing screenshots.\n const parsed: unknown = JSON.parse(fs.readFileSync(filePath, 'utf8'));\n const values = z.array(z.unknown()).safeParse(parsed);\n if (!values.success) return undefined;\n const outputFileSchema = z.object({\n path: z.string(),\n data: z.string(),\n encoding: z.literal('base64').optional(),\n });\n return values.data.flatMap((value) => {\n const result = outputFileSchema.safeParse(value);\n return result.success ? [result.data] : [];\n });\n}\n\nexport function truncateOutput(value: string): string {\n return value.slice(0, MAX_OUTPUT_LENGTH);\n}\n"],"mappings":"wEAMA,MAAaE,EAAoB,IAEjC,eAAsBC,EAAwB,EAAoB,EAA+C,CAC/G,IAAMC,EAAa,MAAMJ,EAAG,SAAS,SAASK,EAAY,MAAM,EAC1DC,EAAe,sCAAsC,KAAKF,CAAU,EAK1E,OAJKE,IAAe,GAIbL,EAAK,KAAKK,EAAa,EAAE,CAAC,WAAW,IAAK,GAAG,EAAGL,EAAK,SAASM,CAAoB,CAAC,EAHjFA,CAIX,CAEA,eAAsBC,EAAoB,EAAoC,CAC5E,IAAMC,EAAsB,CAAC,EAE7B,OADA,MAAMC,EAAuBC,EAAS,GAAIF,CAAS,EAC5CA,EAAU,SAAS,CAC5B,CAEA,eAAeC,EAAuB,EAAiB,EAAqB,EAAoC,CAC9G,IAAME,EAAaC,EAAcZ,EAAK,KAAKU,EAASE,CAAW,EAAIF,EAC7DG,EAAU,MAAMd,EAAG,SAAS,QAAQY,EAAY,CAAE,cAAe,EAAK,CAAC,EAC7E,IAAK,IAAMG,KAASD,EAAS,CAC3B,IAAME,EAAoBH,EAAcZ,EAAK,KAAKY,EAAaE,EAAM,IAAI,EAAIA,EAAM,
|
|
1
|
+
{"version":3,"file":"javaJudge.js","names":["fs","path","MAX_OUTPUT_LENGTH","resolveJavaRelativePath","sourceCode","sourcePath","packageMatch","fallbackRelativePath","listSubmissionFiles","filePaths","collectSubmissionFiles","rootDir","currentDir","relativeDir","entries","entry","entryRelativePath","readOutputFiles","filePath","parsed","values","z","outputFileSchema","result","value","truncateOutput"],"sources":["../../src/helpers/javaJudge.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport type { TestCaseResult } from '../types/testCaseResult.js';\n\ntype OutputFile = NonNullable<TestCaseResult['outputFiles']>[number];\nexport const MAX_OUTPUT_LENGTH = 50_000;\n\nexport async function resolveJavaRelativePath(sourcePath: string, fallbackRelativePath: string): Promise<string> {\n const sourceCode = await fs.promises.readFile(sourcePath, 'utf8');\n const packageMatch = /^\\s*package\\s+([a-zA-Z0-9_.]+)\\s*;/m.exec(sourceCode);\n if (!packageMatch?.[1]) {\n return fallbackRelativePath;\n }\n\n return path.join(packageMatch[1].replaceAll('.', '/'), path.basename(fallbackRelativePath));\n}\n\nexport async function listSubmissionFiles(rootDir: string): Promise<string[]> {\n const filePaths: string[] = [];\n await collectSubmissionFiles(rootDir, '', filePaths);\n return filePaths.toSorted();\n}\n\nasync function collectSubmissionFiles(rootDir: string, relativeDir: string, filePaths: string[]): Promise<void> {\n const currentDir = relativeDir ? path.join(rootDir, relativeDir) : rootDir;\n const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const entryRelativePath = relativeDir ? path.join(relativeDir, entry.name) : entry.name;\n if (entry.isDirectory()) {\n await collectSubmissionFiles(rootDir, entryRelativePath, filePaths);\n continue;\n }\n if (entry.isFile()) {\n filePaths.push(entryRelativePath);\n }\n }\n}\n\nexport function readOutputFiles(filePath: string): OutputFile[] | undefined {\n if (!fs.existsSync(filePath)) {\n return undefined;\n }\n\n // Malformed evaluator JSON remains a harness error (JUDGE_NOT_AVAILABLE), not missing screenshots.\n const parsed: unknown = JSON.parse(fs.readFileSync(filePath, 'utf8'));\n const values = z.array(z.unknown()).safeParse(parsed);\n if (!values.success) return undefined;\n const outputFileSchema = z.object({\n path: z.string(),\n data: z.string(),\n encoding: z.literal('base64').optional(),\n });\n return values.data.flatMap((value) => {\n const result = outputFileSchema.safeParse(value);\n return result.success ? [result.data] : [];\n });\n}\n\nexport function truncateOutput(value: string): string {\n return value.slice(0, MAX_OUTPUT_LENGTH);\n}\n"],"mappings":"wEAMA,MAAaE,EAAoB,IAEjC,eAAsBC,EAAwB,EAAoB,EAA+C,CAC/G,IAAMC,EAAa,MAAMJ,EAAG,SAAS,SAASK,EAAY,MAAM,EAC1DC,EAAe,sCAAsC,KAAKF,CAAU,EAK1E,OAJKE,IAAe,GAIbL,EAAK,KAAKK,EAAa,EAAE,CAAC,WAAW,IAAK,GAAG,EAAGL,EAAK,SAASM,CAAoB,CAAC,EAHjFA,CAIX,CAEA,eAAsBC,EAAoB,EAAoC,CAC5E,IAAMC,EAAsB,CAAC,EAE7B,OADA,MAAMC,EAAuBC,EAAS,GAAIF,CAAS,EAC5CA,EAAU,SAAS,CAC5B,CAEA,eAAeC,EAAuB,EAAiB,EAAqB,EAAoC,CAC9G,IAAME,EAAaC,EAAcZ,EAAK,KAAKU,EAASE,CAAW,EAAIF,EAC7DG,EAAU,MAAMd,EAAG,SAAS,QAAQY,EAAY,CAAE,cAAe,EAAK,CAAC,EAC7E,IAAK,IAAMG,KAASD,EAAS,CAC3B,IAAME,EAAoBH,EAAcZ,EAAK,KAAKY,EAAaE,EAAM,IAAI,EAAIA,EAAM,KAC/EA,EAAM,YAAY,EACpB,MAAML,EAAuBC,EAASK,EAAmBP,CAAS,EAGhEM,EAAM,OAAO,GACfN,EAAU,KAAKO,CAAiB,CAEpC,CACF,CAEA,SAAgBC,EAAgB,EAA4C,CAC1E,GAAI,CAACjB,EAAG,WAAWkB,CAAQ,EACzB,OAIF,IAAMC,EAAkB,KAAK,MAAMnB,EAAG,aAAakB,EAAU,MAAM,CAAC,EAC9DE,EAASC,EAAE,MAAMA,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAUF,CAAM,EACpD,GAAI,CAACC,EAAO,QAAS,OACrB,IAAME,EAAmBD,EAAE,OAAO,CAChC,KAAMA,EAAE,OAAO,EACf,KAAMA,EAAE,OAAO,EACf,SAAUA,EAAE,QAAQ,QAAQ,CAAC,CAAC,SAAS,CACzC,CAAC,EACD,OAAOD,EAAO,KAAK,QAAS,GAAU,CACpC,IAAMG,EAASD,EAAiB,UAAUE,CAAK,EAC/C,OAAOD,EAAO,QAAU,CAACA,EAAO,IAAI,EAAI,CAAC,CAC3C,CAAC,CACH,CAEA,SAAgBE,EAAe,EAAuB,CACpD,OAAOD,EAAM,MAAM,EAAGtB,CAAiB,CACzC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";var e=class extends Error{reason;lineNumber;constructor(e,t){super(`${e.replaceAll(`_`,` `)} on line ${t}`),this.name=`CsvParseError`,this.reason=e,this.lineNumber=t}};function t(e){return n(e).map(e=>e.cells)}function n(t){let n=[],r=[],i=``,a=`start`,o=!1,s=1,c=1,l=0,u=t.startsWith(``)?t.slice(1):t,d=()=>{r.push(i),i=``,a=`start`};for(;l<u.length;){let t=u[l];if(a===`quoted`){if(t===`"`){if(u[l+1]===`"`){i+=`"`,l+=2;continue}a=`closed`}else(t===`
|
|
2
2
|
`||t===`\r`&&u[l+1]!==`
|
|
3
|
-
`)&&s++,i+=t;l
|
|
3
|
+
`)&&s++,i+=t;l++}else{if(o=!0,t===`,`||t===`
|
|
4
4
|
`||t===`\r`)d(),t!==`,`&&(n.push({cells:r,lineNumber:c}),r=[],o=!1,t===`\r`&&u[l+1]===`
|
|
5
|
-
`&&l++,s++,c=s);else if(t===`"`){if(a!==`start`)throw new e(`unexpected_quote`,s);a=`quoted`}else if(a===`closed`)throw new e(`text_after_quoted_field`,s);else a=`unquoted`,i+=t;l++}if(a===`quoted`)throw new e(`unterminated_quote`,c);return o&&(d(),n.push({cells:r,lineNumber:c})),n}exports.CsvParseError=e,exports.parseCsv=t,exports.parseCsvRecords=n;
|
|
5
|
+
`&&l++,s++,c=s);else if(t===`"`){if(a!==`start`)throw new e(`unexpected_quote`,s);a=`quoted`}else if(a===`closed`)throw new e(`text_after_quoted_field`,s);else a=`unquoted`,i+=t;l++}}if(a===`quoted`)throw new e(`unterminated_quote`,c);return o&&(d(),n.push({cells:r,lineNumber:c})),n}exports.CsvParseError=e,exports.parseCsv=t,exports.parseCsvRecords=n;
|
|
6
6
|
//# sourceMappingURL=parseCsv.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseCsv.cjs","names":["CsvParseError","reason","lineNumber","parseCsv","parseCsvRecords","text","record","records","cells","field","fieldState","hasField","recordLineNumber","index","content","endField","char"],"sources":["../../src/helpers/parseCsv.ts"],"sourcesContent":["export type CsvParseErrorReason = 'unterminated_quote' | 'unexpected_quote' | 'text_after_quoted_field';\n\nexport class CsvParseError extends Error {\n readonly reason: CsvParseErrorReason;\n /** 1-based physical line where the error was detected (the record's start line for an unterminated quote). */\n readonly lineNumber: number;\n\n constructor(reason: CsvParseErrorReason, lineNumber: number) {\n super(`${reason.replaceAll('_', ' ')} on line ${lineNumber}`);\n this.name = 'CsvParseError';\n this.reason = reason;\n this.lineNumber = lineNumber;\n }\n}\n\nexport interface CsvRecord {\n cells: string[];\n /** 1-based physical line on which the record starts (quoted fields may span several lines). */\n lineNumber: number;\n}\n\n/**\n * Parse RFC 4180 style CSV text (double-quoted fields, `\"\"` escapes, LF, CRLF, or CR line ends) into rows.\n * A trailing line end does not produce an extra row.\n *\n * @throws {CsvParseError} when a quoted field is not closed, or a quote appears anywhere but around a whole field.\n */\nexport function parseCsv(text: string): string[][] {\n return parseCsvRecords(text).map((record) => record.cells);\n}\n\n/** Like {@link parseCsv}, but also reports the physical line each record starts on. */\nexport function parseCsvRecords(text: string): CsvRecord[] {\n const records: CsvRecord[] = [];\n let cells: string[] = [];\n let field = '';\n let fieldState: 'start' | 'unquoted' | 'quoted' | 'closed' = 'start';\n let hasField = false;\n let lineNumber = 1;\n let recordLineNumber = 1;\n let index = 0;\n const content = text.startsWith('\\uFEFF') ? text.slice(1) : text;\n\n const endField = (): void => {\n cells.push(field);\n field = '';\n fieldState = 'start';\n };\n\n while (index < content.length) {\n const char = content[index] as string;\n if (fieldState === 'quoted') {\n if (char === '\"') {\n if (content[index + 1] === '\"') {\n field += '\"';\n index += 2;\n continue;\n }\n fieldState = 'closed';\n } else {\n if (char === '\\n' || (char === '\\r' && content[index + 1] !== '\\n')) lineNumber++;\n field += char;\n }\n index++;\n continue;\n }\n\n hasField = true;\n if (char === ',' || char === '\\n' || char === '\\r') {\n endField();\n if (char !== ',') {\n records.push({ cells, lineNumber: recordLineNumber });\n cells = [];\n hasField = false;\n if (char === '\\r' && content[index + 1] === '\\n') index++;\n lineNumber++;\n recordLineNumber = lineNumber;\n }\n } else if (char === '\"') {\n if (fieldState !== 'start') throw new CsvParseError('unexpected_quote', lineNumber);\n fieldState = 'quoted';\n } else if (fieldState === 'closed') {\n throw new CsvParseError('text_after_quoted_field', lineNumber);\n } else {\n fieldState = 'unquoted';\n field += char;\n }\n index++;\n }\n\n if (fieldState === 'quoted') throw new CsvParseError('unterminated_quote', recordLineNumber);\n if (hasField) {\n endField();\n records.push({ cells, lineNumber: recordLineNumber });\n }\n return records;\n}\n"],"mappings":"aAEA,IAAaA,EAAb,cAAmC,KAAM,CACvC,OAEA,WAEA,YAAY,EAA6B,EAAoB,CAC3D,MAAM,GAAGC,EAAO,WAAW,IAAK,GAAG,EAAE,WAAWC,GAAY,EAC5D,KAAK,KAAO,gBACZ,KAAK,OAASD,EACd,KAAK,WAAaC,CACpB,CACF,EAcA,SAAgBC,EAAS,EAA0B,CACjD,OAAOC,EAAgBC,CAAI,CAAC,CAAC,IAAK,GAAWC,EAAO,KAAK,CAC3D,CAGA,SAAgBF,EAAgB,EAA2B,CACzD,IAAMG,EAAuB,CAAC,EAC1BC,EAAkB,CAAC,EACnBC,EAAQ,GACRC,EAAyD,QACzDC,EAAW,GACXT,EAAa,EACbU,EAAmB,EACnBC,EAAQ,EACNC,EAAUT,EAAK,WAAW,GAAQ,EAAIA,EAAK,MAAM,CAAC,EAAIA,EAEtDU,MAAuB,CAC3BP,EAAM,KAAKC,CAAK,EAChB,EAAQ,GACR,EAAa,OACf,EAEA,KAAOI,EAAQC,EAAQ,QAAQ,CAC7B,IAAME,EAAOF,EAAQD,GACrB,GAAIH,IAAe,SAAU,CAC3B,GAAIM,IAAS,IAAK,CAChB,GAAIF,EAAQD,EAAQ,KAAO,IAAK,CAC9B,GAAS,IACT,GAAS,EACT,QACF,CACA,EAAa,QACf,MACMG,IAAS;GAASA,IAAS,MAAQF,EAAQD,EAAQ,KAAO;IAAO,IACrE,GAASG,EAEX,
|
|
1
|
+
{"version":3,"file":"parseCsv.cjs","names":["CsvParseError","reason","lineNumber","parseCsv","parseCsvRecords","text","record","records","cells","field","fieldState","hasField","recordLineNumber","index","content","endField","char"],"sources":["../../src/helpers/parseCsv.ts"],"sourcesContent":["export type CsvParseErrorReason = 'unterminated_quote' | 'unexpected_quote' | 'text_after_quoted_field';\n\nexport class CsvParseError extends Error {\n readonly reason: CsvParseErrorReason;\n /** 1-based physical line where the error was detected (the record's start line for an unterminated quote). */\n readonly lineNumber: number;\n\n constructor(reason: CsvParseErrorReason, lineNumber: number) {\n super(`${reason.replaceAll('_', ' ')} on line ${lineNumber}`);\n this.name = 'CsvParseError';\n this.reason = reason;\n this.lineNumber = lineNumber;\n }\n}\n\nexport interface CsvRecord {\n cells: string[];\n /** 1-based physical line on which the record starts (quoted fields may span several lines). */\n lineNumber: number;\n}\n\n/**\n * Parse RFC 4180 style CSV text (double-quoted fields, `\"\"` escapes, LF, CRLF, or CR line ends) into rows.\n * A trailing line end does not produce an extra row.\n *\n * @throws {CsvParseError} when a quoted field is not closed, or a quote appears anywhere but around a whole field.\n */\nexport function parseCsv(text: string): string[][] {\n return parseCsvRecords(text).map((record) => record.cells);\n}\n\n/** Like {@link parseCsv}, but also reports the physical line each record starts on. */\nexport function parseCsvRecords(text: string): CsvRecord[] {\n const records: CsvRecord[] = [];\n let cells: string[] = [];\n let field = '';\n let fieldState: 'start' | 'unquoted' | 'quoted' | 'closed' = 'start';\n let hasField = false;\n let lineNumber = 1;\n let recordLineNumber = 1;\n let index = 0;\n const content = text.startsWith('\\uFEFF') ? text.slice(1) : text;\n\n const endField = (): void => {\n cells.push(field);\n field = '';\n fieldState = 'start';\n };\n\n while (index < content.length) {\n const char = content[index] as string;\n if (fieldState === 'quoted') {\n if (char === '\"') {\n if (content[index + 1] === '\"') {\n field += '\"';\n index += 2;\n continue;\n }\n fieldState = 'closed';\n } else {\n if (char === '\\n' || (char === '\\r' && content[index + 1] !== '\\n')) lineNumber++;\n field += char;\n }\n index++;\n continue;\n }\n\n hasField = true;\n if (char === ',' || char === '\\n' || char === '\\r') {\n endField();\n if (char !== ',') {\n records.push({ cells, lineNumber: recordLineNumber });\n cells = [];\n hasField = false;\n if (char === '\\r' && content[index + 1] === '\\n') index++;\n lineNumber++;\n recordLineNumber = lineNumber;\n }\n } else if (char === '\"') {\n if (fieldState !== 'start') throw new CsvParseError('unexpected_quote', lineNumber);\n fieldState = 'quoted';\n } else if (fieldState === 'closed') {\n throw new CsvParseError('text_after_quoted_field', lineNumber);\n } else {\n fieldState = 'unquoted';\n field += char;\n }\n index++;\n }\n\n if (fieldState === 'quoted') throw new CsvParseError('unterminated_quote', recordLineNumber);\n if (hasField) {\n endField();\n records.push({ cells, lineNumber: recordLineNumber });\n }\n return records;\n}\n"],"mappings":"aAEA,IAAaA,EAAb,cAAmC,KAAM,CACvC,OAEA,WAEA,YAAY,EAA6B,EAAoB,CAC3D,MAAM,GAAGC,EAAO,WAAW,IAAK,GAAG,EAAE,WAAWC,GAAY,EAC5D,KAAK,KAAO,gBACZ,KAAK,OAASD,EACd,KAAK,WAAaC,CACpB,CACF,EAcA,SAAgBC,EAAS,EAA0B,CACjD,OAAOC,EAAgBC,CAAI,CAAC,CAAC,IAAK,GAAWC,EAAO,KAAK,CAC3D,CAGA,SAAgBF,EAAgB,EAA2B,CACzD,IAAMG,EAAuB,CAAC,EAC1BC,EAAkB,CAAC,EACnBC,EAAQ,GACRC,EAAyD,QACzDC,EAAW,GACXT,EAAa,EACbU,EAAmB,EACnBC,EAAQ,EACNC,EAAUT,EAAK,WAAW,GAAQ,EAAIA,EAAK,MAAM,CAAC,EAAIA,EAEtDU,MAAuB,CAC3BP,EAAM,KAAKC,CAAK,EAChB,EAAQ,GACR,EAAa,OACf,EAEA,KAAOI,EAAQC,EAAQ,QAAQ,CAC7B,IAAME,EAAOF,EAAQD,GACrB,GAAIH,IAAe,SAAU,CAC3B,GAAIM,IAAS,IAAK,CAChB,GAAIF,EAAQD,EAAQ,KAAO,IAAK,CAC9B,GAAS,IACT,GAAS,EACT,QACF,CACA,EAAa,QACf,MACMG,IAAS;GAASA,IAAS,MAAQF,EAAQD,EAAQ,KAAO;IAAO,IACrE,GAASG,EAEX,GAEF,KAEA,CACA,GADA,EAAW,GACPA,IAAS,KAAOA,IAAS;GAAQA,IAAS,KAC5CD,EAAS,EACLC,IAAS,MACXT,EAAQ,KAAK,CAAE,QAAO,WAAYK,CAAiB,CAAC,EACpD,EAAQ,CAAC,EACT,EAAW,GACPI,IAAS,MAAQF,EAAQD,EAAQ,KAAO;GAAM,IAClD,IACA,EAAmBX,QAEhB,GAAIc,IAAS,IAAK,CACvB,GAAIN,IAAe,QAAS,MAAM,IAAIV,EAAc,mBAAoBE,CAAU,EAClF,EAAa,QACf,MAAO,GAAIQ,IAAe,SACxB,MAAM,IAAIV,EAAc,0BAA2BE,CAAU,MAE7D,GAAa,WACb,GAASc,EAEX,GApBW,CAqBb,CAEA,GAAIN,IAAe,SAAU,MAAM,IAAIV,EAAc,qBAAsBY,CAAgB,EAK3F,OAJID,IACFI,EAAS,EACTR,EAAQ,KAAK,CAAE,QAAO,WAAYK,CAAiB,CAAC,GAE/CL,CACT"}
|
package/dist/helpers/parseCsv.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var e=class extends Error{reason;lineNumber;constructor(e,t){super(`${e.replaceAll(`_`,` `)} on line ${t}`),this.name=`CsvParseError`,this.reason=e,this.lineNumber=t}};function t(e){return n(e).map(e=>e.cells)}function n(t){let n=[],r=[],i=``,a=`start`,o=!1,s=1,c=1,l=0,u=t.startsWith(``)?t.slice(1):t,d=()=>{r.push(i),i=``,a=`start`};for(;l<u.length;){let t=u[l];if(a===`quoted`){if(t===`"`){if(u[l+1]===`"`){i+=`"`,l+=2;continue}a=`closed`}else(t===`
|
|
2
2
|
`||t===`\r`&&u[l+1]!==`
|
|
3
|
-
`)&&s++,i+=t;l
|
|
3
|
+
`)&&s++,i+=t;l++}else{if(o=!0,t===`,`||t===`
|
|
4
4
|
`||t===`\r`)d(),t!==`,`&&(n.push({cells:r,lineNumber:c}),r=[],o=!1,t===`\r`&&u[l+1]===`
|
|
5
|
-
`&&l++,s++,c=s);else if(t===`"`){if(a!==`start`)throw new e(`unexpected_quote`,s);a=`quoted`}else if(a===`closed`)throw new e(`text_after_quoted_field`,s);else a=`unquoted`,i+=t;l++}if(a===`quoted`)throw new e(`unterminated_quote`,c);return o&&(d(),n.push({cells:r,lineNumber:c})),n}export{e as CsvParseError,t as parseCsv,n as parseCsvRecords};
|
|
5
|
+
`&&l++,s++,c=s);else if(t===`"`){if(a!==`start`)throw new e(`unexpected_quote`,s);a=`quoted`}else if(a===`closed`)throw new e(`text_after_quoted_field`,s);else a=`unquoted`,i+=t;l++}}if(a===`quoted`)throw new e(`unterminated_quote`,c);return o&&(d(),n.push({cells:r,lineNumber:c})),n}export{e as CsvParseError,t as parseCsv,n as parseCsvRecords};
|
|
6
6
|
//# sourceMappingURL=parseCsv.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseCsv.js","names":["CsvParseError","reason","lineNumber","parseCsv","parseCsvRecords","text","record","records","cells","field","fieldState","hasField","recordLineNumber","index","content","endField","char"],"sources":["../../src/helpers/parseCsv.ts"],"sourcesContent":["export type CsvParseErrorReason = 'unterminated_quote' | 'unexpected_quote' | 'text_after_quoted_field';\n\nexport class CsvParseError extends Error {\n readonly reason: CsvParseErrorReason;\n /** 1-based physical line where the error was detected (the record's start line for an unterminated quote). */\n readonly lineNumber: number;\n\n constructor(reason: CsvParseErrorReason, lineNumber: number) {\n super(`${reason.replaceAll('_', ' ')} on line ${lineNumber}`);\n this.name = 'CsvParseError';\n this.reason = reason;\n this.lineNumber = lineNumber;\n }\n}\n\nexport interface CsvRecord {\n cells: string[];\n /** 1-based physical line on which the record starts (quoted fields may span several lines). */\n lineNumber: number;\n}\n\n/**\n * Parse RFC 4180 style CSV text (double-quoted fields, `\"\"` escapes, LF, CRLF, or CR line ends) into rows.\n * A trailing line end does not produce an extra row.\n *\n * @throws {CsvParseError} when a quoted field is not closed, or a quote appears anywhere but around a whole field.\n */\nexport function parseCsv(text: string): string[][] {\n return parseCsvRecords(text).map((record) => record.cells);\n}\n\n/** Like {@link parseCsv}, but also reports the physical line each record starts on. */\nexport function parseCsvRecords(text: string): CsvRecord[] {\n const records: CsvRecord[] = [];\n let cells: string[] = [];\n let field = '';\n let fieldState: 'start' | 'unquoted' | 'quoted' | 'closed' = 'start';\n let hasField = false;\n let lineNumber = 1;\n let recordLineNumber = 1;\n let index = 0;\n const content = text.startsWith('\\uFEFF') ? text.slice(1) : text;\n\n const endField = (): void => {\n cells.push(field);\n field = '';\n fieldState = 'start';\n };\n\n while (index < content.length) {\n const char = content[index] as string;\n if (fieldState === 'quoted') {\n if (char === '\"') {\n if (content[index + 1] === '\"') {\n field += '\"';\n index += 2;\n continue;\n }\n fieldState = 'closed';\n } else {\n if (char === '\\n' || (char === '\\r' && content[index + 1] !== '\\n')) lineNumber++;\n field += char;\n }\n index++;\n continue;\n }\n\n hasField = true;\n if (char === ',' || char === '\\n' || char === '\\r') {\n endField();\n if (char !== ',') {\n records.push({ cells, lineNumber: recordLineNumber });\n cells = [];\n hasField = false;\n if (char === '\\r' && content[index + 1] === '\\n') index++;\n lineNumber++;\n recordLineNumber = lineNumber;\n }\n } else if (char === '\"') {\n if (fieldState !== 'start') throw new CsvParseError('unexpected_quote', lineNumber);\n fieldState = 'quoted';\n } else if (fieldState === 'closed') {\n throw new CsvParseError('text_after_quoted_field', lineNumber);\n } else {\n fieldState = 'unquoted';\n field += char;\n }\n index++;\n }\n\n if (fieldState === 'quoted') throw new CsvParseError('unterminated_quote', recordLineNumber);\n if (hasField) {\n endField();\n records.push({ cells, lineNumber: recordLineNumber });\n }\n return records;\n}\n"],"mappings":"AAEA,IAAaA,EAAb,cAAmC,KAAM,CACvC,OAEA,WAEA,YAAY,EAA6B,EAAoB,CAC3D,MAAM,GAAGC,EAAO,WAAW,IAAK,GAAG,EAAE,WAAWC,GAAY,EAC5D,KAAK,KAAO,gBACZ,KAAK,OAASD,EACd,KAAK,WAAaC,CACpB,CACF,EAcA,SAAgBC,EAAS,EAA0B,CACjD,OAAOC,EAAgBC,CAAI,CAAC,CAAC,IAAK,GAAWC,EAAO,KAAK,CAC3D,CAGA,SAAgBF,EAAgB,EAA2B,CACzD,IAAMG,EAAuB,CAAC,EAC1BC,EAAkB,CAAC,EACnBC,EAAQ,GACRC,EAAyD,QACzDC,EAAW,GACXT,EAAa,EACbU,EAAmB,EACnBC,EAAQ,EACNC,EAAUT,EAAK,WAAW,GAAQ,EAAIA,EAAK,MAAM,CAAC,EAAIA,EAEtDU,MAAuB,CAC3BP,EAAM,KAAKC,CAAK,EAChB,EAAQ,GACR,EAAa,OACf,EAEA,KAAOI,EAAQC,EAAQ,QAAQ,CAC7B,IAAME,EAAOF,EAAQD,GACrB,GAAIH,IAAe,SAAU,CAC3B,GAAIM,IAAS,IAAK,CAChB,GAAIF,EAAQD,EAAQ,KAAO,IAAK,CAC9B,GAAS,IACT,GAAS,EACT,QACF,CACA,EAAa,QACf,MACMG,IAAS;GAASA,IAAS,MAAQF,EAAQD,EAAQ,KAAO;IAAO,IACrE,GAASG,EAEX,
|
|
1
|
+
{"version":3,"file":"parseCsv.js","names":["CsvParseError","reason","lineNumber","parseCsv","parseCsvRecords","text","record","records","cells","field","fieldState","hasField","recordLineNumber","index","content","endField","char"],"sources":["../../src/helpers/parseCsv.ts"],"sourcesContent":["export type CsvParseErrorReason = 'unterminated_quote' | 'unexpected_quote' | 'text_after_quoted_field';\n\nexport class CsvParseError extends Error {\n readonly reason: CsvParseErrorReason;\n /** 1-based physical line where the error was detected (the record's start line for an unterminated quote). */\n readonly lineNumber: number;\n\n constructor(reason: CsvParseErrorReason, lineNumber: number) {\n super(`${reason.replaceAll('_', ' ')} on line ${lineNumber}`);\n this.name = 'CsvParseError';\n this.reason = reason;\n this.lineNumber = lineNumber;\n }\n}\n\nexport interface CsvRecord {\n cells: string[];\n /** 1-based physical line on which the record starts (quoted fields may span several lines). */\n lineNumber: number;\n}\n\n/**\n * Parse RFC 4180 style CSV text (double-quoted fields, `\"\"` escapes, LF, CRLF, or CR line ends) into rows.\n * A trailing line end does not produce an extra row.\n *\n * @throws {CsvParseError} when a quoted field is not closed, or a quote appears anywhere but around a whole field.\n */\nexport function parseCsv(text: string): string[][] {\n return parseCsvRecords(text).map((record) => record.cells);\n}\n\n/** Like {@link parseCsv}, but also reports the physical line each record starts on. */\nexport function parseCsvRecords(text: string): CsvRecord[] {\n const records: CsvRecord[] = [];\n let cells: string[] = [];\n let field = '';\n let fieldState: 'start' | 'unquoted' | 'quoted' | 'closed' = 'start';\n let hasField = false;\n let lineNumber = 1;\n let recordLineNumber = 1;\n let index = 0;\n const content = text.startsWith('\\uFEFF') ? text.slice(1) : text;\n\n const endField = (): void => {\n cells.push(field);\n field = '';\n fieldState = 'start';\n };\n\n while (index < content.length) {\n const char = content[index] as string;\n if (fieldState === 'quoted') {\n if (char === '\"') {\n if (content[index + 1] === '\"') {\n field += '\"';\n index += 2;\n continue;\n }\n fieldState = 'closed';\n } else {\n if (char === '\\n' || (char === '\\r' && content[index + 1] !== '\\n')) lineNumber++;\n field += char;\n }\n index++;\n continue;\n }\n\n hasField = true;\n if (char === ',' || char === '\\n' || char === '\\r') {\n endField();\n if (char !== ',') {\n records.push({ cells, lineNumber: recordLineNumber });\n cells = [];\n hasField = false;\n if (char === '\\r' && content[index + 1] === '\\n') index++;\n lineNumber++;\n recordLineNumber = lineNumber;\n }\n } else if (char === '\"') {\n if (fieldState !== 'start') throw new CsvParseError('unexpected_quote', lineNumber);\n fieldState = 'quoted';\n } else if (fieldState === 'closed') {\n throw new CsvParseError('text_after_quoted_field', lineNumber);\n } else {\n fieldState = 'unquoted';\n field += char;\n }\n index++;\n }\n\n if (fieldState === 'quoted') throw new CsvParseError('unterminated_quote', recordLineNumber);\n if (hasField) {\n endField();\n records.push({ cells, lineNumber: recordLineNumber });\n }\n return records;\n}\n"],"mappings":"AAEA,IAAaA,EAAb,cAAmC,KAAM,CACvC,OAEA,WAEA,YAAY,EAA6B,EAAoB,CAC3D,MAAM,GAAGC,EAAO,WAAW,IAAK,GAAG,EAAE,WAAWC,GAAY,EAC5D,KAAK,KAAO,gBACZ,KAAK,OAASD,EACd,KAAK,WAAaC,CACpB,CACF,EAcA,SAAgBC,EAAS,EAA0B,CACjD,OAAOC,EAAgBC,CAAI,CAAC,CAAC,IAAK,GAAWC,EAAO,KAAK,CAC3D,CAGA,SAAgBF,EAAgB,EAA2B,CACzD,IAAMG,EAAuB,CAAC,EAC1BC,EAAkB,CAAC,EACnBC,EAAQ,GACRC,EAAyD,QACzDC,EAAW,GACXT,EAAa,EACbU,EAAmB,EACnBC,EAAQ,EACNC,EAAUT,EAAK,WAAW,GAAQ,EAAIA,EAAK,MAAM,CAAC,EAAIA,EAEtDU,MAAuB,CAC3BP,EAAM,KAAKC,CAAK,EAChB,EAAQ,GACR,EAAa,OACf,EAEA,KAAOI,EAAQC,EAAQ,QAAQ,CAC7B,IAAME,EAAOF,EAAQD,GACrB,GAAIH,IAAe,SAAU,CAC3B,GAAIM,IAAS,IAAK,CAChB,GAAIF,EAAQD,EAAQ,KAAO,IAAK,CAC9B,GAAS,IACT,GAAS,EACT,QACF,CACA,EAAa,QACf,MACMG,IAAS;GAASA,IAAS,MAAQF,EAAQD,EAAQ,KAAO;IAAO,IACrE,GAASG,EAEX,GAEF,KAEA,CACA,GADA,EAAW,GACPA,IAAS,KAAOA,IAAS;GAAQA,IAAS,KAC5CD,EAAS,EACLC,IAAS,MACXT,EAAQ,KAAK,CAAE,QAAO,WAAYK,CAAiB,CAAC,EACpD,EAAQ,CAAC,EACT,EAAW,GACPI,IAAS,MAAQF,EAAQD,EAAQ,KAAO;GAAM,IAClD,IACA,EAAmBX,QAEhB,GAAIc,IAAS,IAAK,CACvB,GAAIN,IAAe,QAAS,MAAM,IAAIV,EAAc,mBAAoBE,CAAU,EAClF,EAAa,QACf,MAAO,GAAIQ,IAAe,SACxB,MAAM,IAAIV,EAAc,0BAA2BE,CAAU,MAE7D,GAAa,WACb,GAASc,EAEX,GApBW,CAqBb,CAEA,GAAIN,IAAe,SAAU,MAAM,IAAIV,EAAc,qBAAsBY,CAAgB,EAK3F,OAJID,IACFI,EAAS,EACTR,EAAQ,KAAK,CAAE,QAAO,WAAYK,CAAiB,CAAC,GAE/CL,CACT"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"use strict";function e(e,t){return n(e,t,{removeStrings:!1})}function t(e,t){return n(e,t,{removeStrings:!0})}function n(e,t,n,a=r(e)){if(!e.comments?.length&&(!n.removeStrings||!e.strings?.length))return t;let s=[],c=0;for(;c<t.length;){let r;for(let e of a){let a=e.openRegExp;a.lastIndex=c;let o=a.exec(t);o&&i(o,e,r,n)&&(r={grammar:e,match:o})}if(r){let i=r.match.index;if(!r.grammar.isComment&&n.removeStrings&&(i=d(t,r.match.index)),s.push(t.slice(c,i)),r.grammar.isComment){let e=r.grammar.closeRegExp;e.lastIndex=r.match.index+r.match[0].length;let n=e.exec(t);c=n?n.index+n[0].length:t.length}else{c=o(t,r.match.index,r.grammar);let i=t.slice(r.match.index,c);n.removeStrings?s.push(p(e,a,r.grammar,t,r.match.index,i)):s.push(i)}}else s.push(t.slice(c)),c=t.length}return s.join(``)}function r(e){return[...e.comments?.map(e=>({closeRegExp:a(e.close??/(?=\n)/g),isComment:!0,openRegExp:a(e.open)}))??[],...e.strings?.map(e=>({closeRegExp:a(e.close),isComment:!1,openRegExp:a(e.open),templateSyntax:e.templateSyntax}))??[]]}function i(e,t,n,r){return!n||e.index<n.match.index?!0:e.index>n.match.index?!1:r.removeStrings&&n.grammar.isComment&&!t.isComment}function a(e){return new RegExp(e,e.flags.includes(`g`)?e.flags:`${e.flags}g`)}function o(e,t,n){if(n.templateSyntax===`kotlin`)return s(e,t);if(e[t]==="`")return c(e,t);if(_(e,t))return l(e,t);let r=n.closeRegExp;r.lastIndex=t+1;let i=r.exec(e);return i?i.index+i[0].length:e.length}function s(e,t){let n=u(e,t),r=n.length===3,i=t+n.length;for(;i<e.length;){if(e.startsWith(n,i))return i+n.length
|
|
2
|
-
`)}function h(e){return e!==void 0&&/[\p{L}_]/u.test(e)}function g(e){return e!==void 0&&/[\p{L}\p{N}_]/u.test(e)}function _(e,t){return f(e,t)?.toLowerCase().includes(`f`)??!1}function v(e,t,r){let i=[],a=1;for(;a<r.length-1;)
|
|
3
|
-
`)}function y(e,t,r){let i=u(r,0).length,a=[],o=i;for(;o<r.length-i;)
|
|
4
|
-
`)}function b(e,t,n,r){let i=1,a=t;for(;a<e.length;){let o=e[a];if(o===`"`&&r.supportsKotlinStrings)
|
|
1
|
+
"use strict";function e(e,t){return n(e,t,{removeStrings:!1})}function t(e,t){return n(e,t,{removeStrings:!0})}function n(e,t,n,a=r(e)){if(!e.comments?.length&&(!n.removeStrings||!e.strings?.length))return t;let s=[],c=0;for(;c<t.length;){let r;for(let e of a){let a=e.openRegExp;a.lastIndex=c;let o=a.exec(t);o&&i(o,e,r,n)&&(r={grammar:e,match:o})}if(r){let i=r.match.index;if(!r.grammar.isComment&&n.removeStrings&&(i=d(t,r.match.index)),s.push(t.slice(c,i)),r.grammar.isComment){let e=r.grammar.closeRegExp;e.lastIndex=r.match.index+r.match[0].length;let n=e.exec(t);c=n?n.index+n[0].length:t.length}else{c=o(t,r.match.index,r.grammar);let i=t.slice(r.match.index,c);n.removeStrings?s.push(p(e,a,r.grammar,t,r.match.index,i)):s.push(i)}}else s.push(t.slice(c)),c=t.length}return s.join(``)}function r(e){return[...e.comments?.map(e=>({closeRegExp:a(e.close??/(?=\n)/g),isComment:!0,openRegExp:a(e.open)}))??[],...e.strings?.map(e=>({closeRegExp:a(e.close),isComment:!1,openRegExp:a(e.open),templateSyntax:e.templateSyntax}))??[]]}function i(e,t,n,r){return!n||e.index<n.match.index?!0:e.index>n.match.index?!1:r.removeStrings&&n.grammar.isComment&&!t.isComment}function a(e){return new RegExp(e,e.flags.includes(`g`)?e.flags:`${e.flags}g`)}function o(e,t,n){if(n.templateSyntax===`kotlin`)return s(e,t);if(e[t]==="`")return c(e,t);if(_(e,t))return l(e,t);let r=n.closeRegExp;r.lastIndex=t+1;let i=r.exec(e);return i?i.index+i[0].length:e.length}function s(e,t){let n=u(e,t),r=n.length===3,i=t+n.length;for(;i<e.length;){if(e.startsWith(n,i))return i+n.length;!r&&e[i]===`\\`?i+=2:e[i]===`$`&&e[i+1]===`{`?i=b(e,i+2,`}`,{slashStartsComments:!0,slashStartsRegExp:!1,supportsKotlinStrings:!0}).endIndex+1:i+=1}return e.length}function c(e,t){let n=t+1;for(;n<e.length;)if(e[n]===`\\`)n+=2;else{if(e[n]==="`")return n+1;e[n]===`$`&&e[n+1]===`{`?n=b(e,n+2,`}`,{slashStartsRegExp:!0}).endIndex+1:n+=1}return e.length}function l(e,t){let n=u(e,t),r=t+n.length;for(;r<e.length;){if(e.startsWith(n,r))return r+n.length;e[r]===`{`&&e[r+1]===`{`?r+=2:e[r]===`{`?r=b(e,r+1,`}`,{slashStartsRegExp:!1}).endIndex+1:r+=e[r]===`\\`?2:1}return e.length}function u(e,t){let n=e.slice(t,t+3);return n===`'''`||n===`"""`?n:e[t]??``}function d(e,t){let n=f(e,t);return n?t-n.length:t}function f(e,t){if(e[t]!==`"`&&e[t]!==`'`)return;let n=t-1;for(;n>=0&&/[A-Za-z]/.test(e[n]??``);)--n;let r=e.slice(n+1,t);if(r&&/^[bfru]+$/i.test(r)&&!(n>=0&&/[\dA-Za-z_]/.test(e[n]??``)))return r}function p(e,t,n,r,i,a){return n.templateSyntax===`kotlin`?m(e,t,a):a.startsWith("`")?v(e,t,a):_(r,i)?y(e,t,a):``}function m(e,t,r){let i=u(r,0).length,a=i===3,o=[],s=i;for(;s<r.length-i;)if(!a&&r[s]===`\\`)s+=2;else if(r[s]!==`$`)s+=1;else if(r[s+1]===`{`){let i=b(r,s+2,`}`,{slashStartsComments:!0,slashStartsRegExp:!1,supportsKotlinStrings:!0});o.push(n(e,i.content,{removeStrings:!0},t)),s=i.endIndex+1}else if(h(r[s+1])){let e=s+2;for(;g(r[e]);)e+=1;o.push(r.slice(s+1,e)),s=e}else s+=1;return o.join(`
|
|
2
|
+
`)}function h(e){return e!==void 0&&/[\p{L}_]/u.test(e)}function g(e){return e!==void 0&&/[\p{L}\p{N}_]/u.test(e)}function _(e,t){return f(e,t)?.toLowerCase().includes(`f`)??!1}function v(e,t,r){let i=[],a=1;for(;a<r.length-1;)if(r[a]===`\\`)a+=2;else if(r[a]===`$`&&r[a+1]===`{`){let o=b(r,a+2,`}`,{slashStartsRegExp:!0});i.push(n(e,o.content,{removeStrings:!0},t)),a=o.endIndex+1}else a+=1;return i.join(`
|
|
3
|
+
`)}function y(e,t,r){let i=u(r,0).length,a=[],o=i;for(;o<r.length-i;)if(r[o]===`{`&&r[o+1]===`{`)o+=2;else if(r[o]===`{`){let i=b(r,o+1,`}`,{slashStartsRegExp:!1});a.push(n(e,i.content,{removeStrings:!0},t)),o=i.endIndex+1}else o+=r[o]===`\\`?2:1;return a.join(`
|
|
4
|
+
`)}function b(e,t,n,r){let i=1,a=t;for(;a<e.length;){let o=e[a];if(o===`"`&&r.supportsKotlinStrings)a=s(e,a);else if(o===`"`||o===`'`||o==="`")a=x(e,a);else if((r.slashStartsComments||r.slashStartsRegExp)&&e[a]===`/`&&e[a+1]===`/`)a=C(e,a);else if((r.slashStartsComments||r.slashStartsRegExp)&&e[a]===`/`&&e[a+1]===`*`)a=w(e,a);else if(r.slashStartsRegExp&&o===`/`)a=S(e,a);else{if(o===`{`&&(i+=1),o===n&&(--i,i===0))return{content:e.slice(t,a),endIndex:a};a+=1}}return{content:e.slice(t),endIndex:e.length}}function x(e,t){let n=u(e,t),r=t+n.length;for(;r<e.length;)if(e[r]===`\\`)r+=2;else{if(e.startsWith(n,r))return r+n.length;r+=1}return r}function S(e,t){let n=t+1,r=!1;for(;n<e.length;)if(e[n]===`\\`)n+=2;else{if(e[n]===`[`&&(r=!0),e[n]===`]`&&(r=!1),e[n]===`/`&&!r){for(n+=1;/[A-Za-z]/.test(e[n]??``);)n+=1;return n}if(e[n]===`
|
|
5
5
|
`)return t+1;n+=1}return t+1}function C(e,t){let n=e.indexOf(`
|
|
6
6
|
`,t+2);return n===-1?e.length:n}function w(e,t){let n=e.indexOf(`*/`,t+2);return n===-1?e.length:n+2}exports.removeCommentsAndStringsInSourceCode=t,exports.removeCommentsInSourceCode=e;
|
|
7
7
|
//# sourceMappingURL=removeCommentsInSourceCode.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"removeCommentsInSourceCode.cjs","names":["removeCommentsInSourceCode","removeCommentsAndMaybeStringsInSourceCode","grammar","sourceCode","removeCommentsAndStringsInSourceCode","compileCommentOrStringGrammars","options","newSourceCodeSlices","lastIndex","first","commentOrStringGrammer","commentOrStringGrammars","startRegExp","match","shouldPreferMatch","stringPrefixStartIndex","getPythonStringPrefixStartIndex","closeRegExp","findStringEndIndex","stringLiteral","preserveStringInterpolationExpressions","makeGlobalRegExp","v","regExp","findKotlinStringEndIndex","stringStartIndex","findJavaScriptTemplateEndIndex","hasPythonFStringPrefix","findPythonFStringEndIndex","quote","getStringQuote","isRawString","index","readBalancedExpression","threeCharacterQuote","prefix","getPythonStringPrefix","stringGrammar","preserveKotlinStringTemplateExpressions","preserveJavaScriptTemplateExpressions","preservePythonFStringExpressions","quoteLength","expressions","expression","isKotlinIdentifierStart","endIndex","isKotlinIdentifierPart","character","depth","startIndex","char","skipQuotedSpan","skipJavaScriptSingleLineComment","skipJavaScriptBlockComment","skipJavaScriptRegExpLiteral","closeChar","inCharacterClass","newLineIndex"],"sources":["../../src/helpers/removeCommentsInSourceCode.ts"],"sourcesContent":["import type { LanguageDefinition } from '../types/language.js';\n\nexport type SourceCodeGrammar = NonNullable<LanguageDefinition['grammer']>;\n\n/** Removes comments from source code according to the provided language grammar. */\nexport function removeCommentsInSourceCode(grammar: SourceCodeGrammar, sourceCode: string): string {\n return removeCommentsAndMaybeStringsInSourceCode(grammar, sourceCode, { removeStrings: false });\n}\n\n/** Removes comments and string literal contents while preserving executable interpolation expressions. */\nexport function removeCommentsAndStringsInSourceCode(grammar: SourceCodeGrammar, sourceCode: string): string {\n return removeCommentsAndMaybeStringsInSourceCode(grammar, sourceCode, { removeStrings: true });\n}\n\ninterface CommentOrStringGrammar {\n closeRegExp: RegExp;\n isComment: boolean;\n openRegExp: RegExp;\n templateSyntax?: 'kotlin';\n}\n\ninterface BalancedExpressionOptions {\n slashStartsComments?: boolean;\n slashStartsRegExp: boolean;\n supportsKotlinStrings?: boolean;\n}\n\nfunction removeCommentsAndMaybeStringsInSourceCode(\n grammar: SourceCodeGrammar,\n sourceCode: string,\n options: { removeStrings: boolean },\n commentOrStringGrammars = compileCommentOrStringGrammars(grammar)\n): string {\n if (!grammar.comments?.length && (!options.removeStrings || !grammar.strings?.length)) return sourceCode;\n\n const newSourceCodeSlices: string[] = [];\n\n let lastIndex = 0;\n\n while (lastIndex < sourceCode.length) {\n let first: { grammar: CommentOrStringGrammar; match: RegExpExecArray } | undefined;\n\n for (const commentOrStringGrammer of commentOrStringGrammars) {\n const startRegExp = commentOrStringGrammer.openRegExp;\n startRegExp.lastIndex = lastIndex;\n\n const match = startRegExp.exec(sourceCode);\n\n if (match && shouldPreferMatch(match, commentOrStringGrammer, first, options)) {\n first = { grammar: commentOrStringGrammer, match };\n }\n }\n\n if (first) {\n let stringPrefixStartIndex = first.match.index;\n if (!first.grammar.isComment && options.removeStrings) {\n stringPrefixStartIndex = getPythonStringPrefixStartIndex(sourceCode, first.match.index);\n }\n newSourceCodeSlices.push(sourceCode.slice(lastIndex, stringPrefixStartIndex));\n\n if (!first.grammar.isComment) {\n lastIndex = findStringEndIndex(sourceCode, first.match.index, first.grammar);\n const stringLiteral = sourceCode.slice(first.match.index, lastIndex);\n if (options.removeStrings) {\n newSourceCodeSlices.push(\n preserveStringInterpolationExpressions(\n grammar,\n commentOrStringGrammars,\n first.grammar,\n sourceCode,\n first.match.index,\n stringLiteral\n )\n );\n } else {\n newSourceCodeSlices.push(stringLiteral);\n }\n } else {\n const closeRegExp = first.grammar.closeRegExp;\n closeRegExp.lastIndex = first.match.index + first.match[0].length;\n\n const match = closeRegExp.exec(sourceCode);\n\n lastIndex = match ? match.index + match[0].length : sourceCode.length;\n }\n } else {\n newSourceCodeSlices.push(sourceCode.slice(lastIndex));\n lastIndex = sourceCode.length;\n }\n }\n\n return newSourceCodeSlices.join('');\n}\n\nfunction compileCommentOrStringGrammars(grammar: SourceCodeGrammar): readonly CommentOrStringGrammar[] {\n return [\n ...(grammar.comments?.map((v) => ({\n closeRegExp: makeGlobalRegExp(v.close ?? /(?=\\n)/g),\n isComment: true,\n openRegExp: makeGlobalRegExp(v.open),\n })) ?? []),\n ...(grammar.strings?.map((v) => ({\n closeRegExp: makeGlobalRegExp(v.close),\n isComment: false,\n openRegExp: makeGlobalRegExp(v.open),\n templateSyntax: v.templateSyntax,\n })) ?? []),\n ] satisfies CommentOrStringGrammar[];\n}\n\nfunction shouldPreferMatch(\n match: RegExpExecArray,\n grammar: CommentOrStringGrammar,\n first: { grammar: CommentOrStringGrammar; match: RegExpExecArray } | undefined,\n options: { removeStrings: boolean }\n): boolean {\n if (!first) return true;\n if (match.index < first.match.index) return true;\n if (match.index > first.match.index) return false;\n return options.removeStrings && first.grammar.isComment && !grammar.isComment;\n}\n\nfunction makeGlobalRegExp(regExp: RegExp): RegExp {\n return new RegExp(regExp, regExp.flags.includes('g') ? regExp.flags : `${regExp.flags}g`);\n}\n\nfunction findStringEndIndex(sourceCode: string, stringStartIndex: number, grammar: CommentOrStringGrammar): number {\n if (grammar.templateSyntax === 'kotlin') return findKotlinStringEndIndex(sourceCode, stringStartIndex);\n if (sourceCode[stringStartIndex] === '`') return findJavaScriptTemplateEndIndex(sourceCode, stringStartIndex);\n if (hasPythonFStringPrefix(sourceCode, stringStartIndex))\n return findPythonFStringEndIndex(sourceCode, stringStartIndex);\n\n const closeRegExp = grammar.closeRegExp;\n closeRegExp.lastIndex = stringStartIndex + 1;\n const match = closeRegExp.exec(sourceCode);\n return match ? match.index + match[0].length : sourceCode.length;\n}\n\nfunction findKotlinStringEndIndex(sourceCode: string, stringStartIndex: number): number {\n const quote = getStringQuote(sourceCode, stringStartIndex);\n const isRawString = quote.length === 3;\n let index = stringStartIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n if (!isRawString && sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '$' && sourceCode[index + 1] === '{') {\n index =\n readBalancedExpression(sourceCode, index + 2, '}', {\n slashStartsComments: true,\n slashStartsRegExp: false,\n supportsKotlinStrings: true,\n }).endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return sourceCode.length;\n}\n\nfunction findJavaScriptTemplateEndIndex(sourceCode: string, stringStartIndex: number): number {\n let index = stringStartIndex + 1;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '`') return index + 1;\n if (sourceCode[index] === '$' && sourceCode[index + 1] === '{') {\n index = readBalancedExpression(sourceCode, index + 2, '}', { slashStartsRegExp: true }).endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return sourceCode.length;\n}\n\nfunction findPythonFStringEndIndex(sourceCode: string, stringStartIndex: number): number {\n const quote = getStringQuote(sourceCode, stringStartIndex);\n let index = stringStartIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n if (sourceCode[index] === '{' && sourceCode[index + 1] === '{') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '{') {\n index = readBalancedExpression(sourceCode, index + 1, '}', { slashStartsRegExp: false }).endIndex + 1;\n continue;\n }\n index += sourceCode[index] === '\\\\' ? 2 : 1;\n }\n\n return sourceCode.length;\n}\n\nfunction getStringQuote(sourceCode: string, stringStartIndex: number): string {\n const threeCharacterQuote = sourceCode.slice(stringStartIndex, stringStartIndex + 3);\n if (threeCharacterQuote === \"'''\" || threeCharacterQuote === '\"\"\"') return threeCharacterQuote;\n return sourceCode[stringStartIndex] ?? '';\n}\n\nfunction getPythonStringPrefixStartIndex(sourceCode: string, stringStartIndex: number): number {\n const prefix = getPythonStringPrefix(sourceCode, stringStartIndex);\n return prefix ? stringStartIndex - prefix.length : stringStartIndex;\n}\n\nfunction getPythonStringPrefix(sourceCode: string, stringStartIndex: number): string | undefined {\n if (sourceCode[stringStartIndex] !== '\"' && sourceCode[stringStartIndex] !== \"'\") return undefined;\n let index = stringStartIndex - 1;\n while (index >= 0 && /[A-Za-z]/.test(sourceCode[index] ?? '')) index -= 1;\n\n const prefix = sourceCode.slice(index + 1, stringStartIndex);\n if (!prefix || !/^[bfru]+$/i.test(prefix)) return undefined;\n if (index >= 0 && /[\\dA-Za-z_]/.test(sourceCode[index] ?? '')) return undefined;\n\n return prefix;\n}\n\nfunction preserveStringInterpolationExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringGrammar: CommentOrStringGrammar,\n sourceCode: string,\n stringStartIndex: number,\n stringLiteral: string\n): string {\n if (stringGrammar.templateSyntax === 'kotlin')\n return preserveKotlinStringTemplateExpressions(grammar, commentOrStringGrammars, stringLiteral);\n if (stringLiteral.startsWith('`'))\n return preserveJavaScriptTemplateExpressions(grammar, commentOrStringGrammars, stringLiteral);\n if (hasPythonFStringPrefix(sourceCode, stringStartIndex))\n return preservePythonFStringExpressions(grammar, commentOrStringGrammars, stringLiteral);\n return '';\n}\n\nfunction preserveKotlinStringTemplateExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const quoteLength = getStringQuote(stringLiteral, 0).length;\n const isRawString = quoteLength === 3;\n const expressions: string[] = [];\n let index = quoteLength;\n\n while (index < stringLiteral.length - quoteLength) {\n if (!isRawString && stringLiteral[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] !== '$') {\n index += 1;\n continue;\n }\n if (stringLiteral[index + 1] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 2, '}', {\n slashStartsComments: true,\n slashStartsRegExp: false,\n supportsKotlinStrings: true,\n });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n if (isKotlinIdentifierStart(stringLiteral[index + 1])) {\n let endIndex = index + 2;\n while (isKotlinIdentifierPart(stringLiteral[endIndex])) endIndex += 1;\n expressions.push(stringLiteral.slice(index + 1, endIndex));\n index = endIndex;\n continue;\n }\n index += 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction isKotlinIdentifierStart(character: string | undefined): boolean {\n return character !== undefined && /[\\p{L}_]/u.test(character);\n}\n\nfunction isKotlinIdentifierPart(character: string | undefined): boolean {\n return character !== undefined && /[\\p{L}\\p{N}_]/u.test(character);\n}\n\nfunction hasPythonFStringPrefix(sourceCode: string, stringStartIndex: number): boolean {\n return getPythonStringPrefix(sourceCode, stringStartIndex)?.toLowerCase().includes('f') ?? false;\n}\n\nfunction preserveJavaScriptTemplateExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const expressions: string[] = [];\n let index = 1;\n\n while (index < stringLiteral.length - 1) {\n if (stringLiteral[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] === '$' && stringLiteral[index + 1] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 2, '}', { slashStartsRegExp: true });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction preservePythonFStringExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const quoteLength = getStringQuote(stringLiteral, 0).length;\n const expressions: string[] = [];\n let index = quoteLength;\n\n while (index < stringLiteral.length - quoteLength) {\n if (stringLiteral[index] === '{' && stringLiteral[index + 1] === '{') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 1, '}', { slashStartsRegExp: false });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n index += stringLiteral[index] === '\\\\' ? 2 : 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction readBalancedExpression(\n sourceCode: string,\n startIndex: number,\n closeChar: string,\n options: BalancedExpressionOptions\n): { content: string; endIndex: number } {\n let depth = 1;\n let index = startIndex;\n\n while (index < sourceCode.length) {\n const char = sourceCode[index];\n if (char === '\"' && options.supportsKotlinStrings) {\n index = findKotlinStringEndIndex(sourceCode, index);\n continue;\n }\n if (char === '\"' || char === \"'\" || char === '`') {\n index = skipQuotedSpan(sourceCode, index);\n continue;\n }\n if (\n (options.slashStartsComments || options.slashStartsRegExp) &&\n sourceCode[index] === '/' &&\n sourceCode[index + 1] === '/'\n ) {\n index = skipJavaScriptSingleLineComment(sourceCode, index);\n continue;\n }\n if (\n (options.slashStartsComments || options.slashStartsRegExp) &&\n sourceCode[index] === '/' &&\n sourceCode[index + 1] === '*'\n ) {\n index = skipJavaScriptBlockComment(sourceCode, index);\n continue;\n }\n if (options.slashStartsRegExp && char === '/') {\n index = skipJavaScriptRegExpLiteral(sourceCode, index);\n continue;\n }\n if (char === '{') depth += 1;\n if (char === closeChar) {\n depth -= 1;\n if (depth === 0) return { content: sourceCode.slice(startIndex, index), endIndex: index };\n }\n index += 1;\n }\n\n return { content: sourceCode.slice(startIndex), endIndex: sourceCode.length };\n}\n\nfunction skipQuotedSpan(sourceCode: string, startIndex: number): number {\n const quote = getStringQuote(sourceCode, startIndex);\n let index = startIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n index += 1;\n }\n\n return index;\n}\n\nfunction skipJavaScriptRegExpLiteral(sourceCode: string, startIndex: number): number {\n let index = startIndex + 1;\n let inCharacterClass = false;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '[') inCharacterClass = true;\n if (sourceCode[index] === ']') inCharacterClass = false;\n if (sourceCode[index] === '/' && !inCharacterClass) {\n index += 1;\n while (/[A-Za-z]/.test(sourceCode[index] ?? '')) index += 1;\n return index;\n }\n if (sourceCode[index] === '\\n') return startIndex + 1;\n index += 1;\n }\n\n return startIndex + 1;\n}\n\nfunction skipJavaScriptSingleLineComment(sourceCode: string, startIndex: number): number {\n const newLineIndex = sourceCode.indexOf('\\n', startIndex + 2);\n return newLineIndex === -1 ? sourceCode.length : newLineIndex;\n}\n\nfunction skipJavaScriptBlockComment(sourceCode: string, startIndex: number): number {\n const endIndex = sourceCode.indexOf('*/', startIndex + 2);\n return endIndex === -1 ? sourceCode.length : endIndex + 2;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"removeCommentsInSourceCode.cjs","names":["removeCommentsInSourceCode","removeCommentsAndMaybeStringsInSourceCode","grammar","sourceCode","removeCommentsAndStringsInSourceCode","compileCommentOrStringGrammars","options","newSourceCodeSlices","lastIndex","first","commentOrStringGrammer","commentOrStringGrammars","startRegExp","match","shouldPreferMatch","stringPrefixStartIndex","getPythonStringPrefixStartIndex","closeRegExp","findStringEndIndex","stringLiteral","preserveStringInterpolationExpressions","makeGlobalRegExp","v","regExp","findKotlinStringEndIndex","stringStartIndex","findJavaScriptTemplateEndIndex","hasPythonFStringPrefix","findPythonFStringEndIndex","quote","getStringQuote","isRawString","index","readBalancedExpression","threeCharacterQuote","prefix","getPythonStringPrefix","stringGrammar","preserveKotlinStringTemplateExpressions","preserveJavaScriptTemplateExpressions","preservePythonFStringExpressions","quoteLength","expressions","expression","isKotlinIdentifierStart","endIndex","isKotlinIdentifierPart","character","depth","startIndex","char","skipQuotedSpan","skipJavaScriptSingleLineComment","skipJavaScriptBlockComment","skipJavaScriptRegExpLiteral","closeChar","inCharacterClass","newLineIndex"],"sources":["../../src/helpers/removeCommentsInSourceCode.ts"],"sourcesContent":["import type { LanguageDefinition } from '../types/language.js';\n\nexport type SourceCodeGrammar = NonNullable<LanguageDefinition['grammer']>;\n\n/** Removes comments from source code according to the provided language grammar. */\nexport function removeCommentsInSourceCode(grammar: SourceCodeGrammar, sourceCode: string): string {\n return removeCommentsAndMaybeStringsInSourceCode(grammar, sourceCode, { removeStrings: false });\n}\n\n/** Removes comments and string literal contents while preserving executable interpolation expressions. */\nexport function removeCommentsAndStringsInSourceCode(grammar: SourceCodeGrammar, sourceCode: string): string {\n return removeCommentsAndMaybeStringsInSourceCode(grammar, sourceCode, { removeStrings: true });\n}\n\ninterface CommentOrStringGrammar {\n closeRegExp: RegExp;\n isComment: boolean;\n openRegExp: RegExp;\n templateSyntax?: 'kotlin';\n}\n\ninterface BalancedExpressionOptions {\n slashStartsComments?: boolean;\n slashStartsRegExp: boolean;\n supportsKotlinStrings?: boolean;\n}\n\nfunction removeCommentsAndMaybeStringsInSourceCode(\n grammar: SourceCodeGrammar,\n sourceCode: string,\n options: { removeStrings: boolean },\n commentOrStringGrammars = compileCommentOrStringGrammars(grammar)\n): string {\n if (!grammar.comments?.length && (!options.removeStrings || !grammar.strings?.length)) return sourceCode;\n\n const newSourceCodeSlices: string[] = [];\n\n let lastIndex = 0;\n\n while (lastIndex < sourceCode.length) {\n let first: { grammar: CommentOrStringGrammar; match: RegExpExecArray } | undefined;\n\n for (const commentOrStringGrammer of commentOrStringGrammars) {\n const startRegExp = commentOrStringGrammer.openRegExp;\n startRegExp.lastIndex = lastIndex;\n\n const match = startRegExp.exec(sourceCode);\n\n if (match && shouldPreferMatch(match, commentOrStringGrammer, first, options)) {\n first = { grammar: commentOrStringGrammer, match };\n }\n }\n\n if (first) {\n let stringPrefixStartIndex = first.match.index;\n if (!first.grammar.isComment && options.removeStrings) {\n stringPrefixStartIndex = getPythonStringPrefixStartIndex(sourceCode, first.match.index);\n }\n newSourceCodeSlices.push(sourceCode.slice(lastIndex, stringPrefixStartIndex));\n\n if (!first.grammar.isComment) {\n lastIndex = findStringEndIndex(sourceCode, first.match.index, first.grammar);\n const stringLiteral = sourceCode.slice(first.match.index, lastIndex);\n if (options.removeStrings) {\n newSourceCodeSlices.push(\n preserveStringInterpolationExpressions(\n grammar,\n commentOrStringGrammars,\n first.grammar,\n sourceCode,\n first.match.index,\n stringLiteral\n )\n );\n } else {\n newSourceCodeSlices.push(stringLiteral);\n }\n } else {\n const closeRegExp = first.grammar.closeRegExp;\n closeRegExp.lastIndex = first.match.index + first.match[0].length;\n\n const match = closeRegExp.exec(sourceCode);\n\n lastIndex = match ? match.index + match[0].length : sourceCode.length;\n }\n } else {\n newSourceCodeSlices.push(sourceCode.slice(lastIndex));\n lastIndex = sourceCode.length;\n }\n }\n\n return newSourceCodeSlices.join('');\n}\n\nfunction compileCommentOrStringGrammars(grammar: SourceCodeGrammar): readonly CommentOrStringGrammar[] {\n return [\n ...(grammar.comments?.map((v) => ({\n closeRegExp: makeGlobalRegExp(v.close ?? /(?=\\n)/g),\n isComment: true,\n openRegExp: makeGlobalRegExp(v.open),\n })) ?? []),\n ...(grammar.strings?.map((v) => ({\n closeRegExp: makeGlobalRegExp(v.close),\n isComment: false,\n openRegExp: makeGlobalRegExp(v.open),\n templateSyntax: v.templateSyntax,\n })) ?? []),\n ] satisfies CommentOrStringGrammar[];\n}\n\nfunction shouldPreferMatch(\n match: RegExpExecArray,\n grammar: CommentOrStringGrammar,\n first: { grammar: CommentOrStringGrammar; match: RegExpExecArray } | undefined,\n options: { removeStrings: boolean }\n): boolean {\n if (!first) return true;\n if (match.index < first.match.index) return true;\n if (match.index > first.match.index) return false;\n return options.removeStrings && first.grammar.isComment && !grammar.isComment;\n}\n\nfunction makeGlobalRegExp(regExp: RegExp): RegExp {\n return new RegExp(regExp, regExp.flags.includes('g') ? regExp.flags : `${regExp.flags}g`);\n}\n\nfunction findStringEndIndex(sourceCode: string, stringStartIndex: number, grammar: CommentOrStringGrammar): number {\n if (grammar.templateSyntax === 'kotlin') return findKotlinStringEndIndex(sourceCode, stringStartIndex);\n if (sourceCode[stringStartIndex] === '`') return findJavaScriptTemplateEndIndex(sourceCode, stringStartIndex);\n if (hasPythonFStringPrefix(sourceCode, stringStartIndex))\n return findPythonFStringEndIndex(sourceCode, stringStartIndex);\n\n const closeRegExp = grammar.closeRegExp;\n closeRegExp.lastIndex = stringStartIndex + 1;\n const match = closeRegExp.exec(sourceCode);\n return match ? match.index + match[0].length : sourceCode.length;\n}\n\nfunction findKotlinStringEndIndex(sourceCode: string, stringStartIndex: number): number {\n const quote = getStringQuote(sourceCode, stringStartIndex);\n const isRawString = quote.length === 3;\n let index = stringStartIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n if (!isRawString && sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '$' && sourceCode[index + 1] === '{') {\n index =\n readBalancedExpression(sourceCode, index + 2, '}', {\n slashStartsComments: true,\n slashStartsRegExp: false,\n supportsKotlinStrings: true,\n }).endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return sourceCode.length;\n}\n\nfunction findJavaScriptTemplateEndIndex(sourceCode: string, stringStartIndex: number): number {\n let index = stringStartIndex + 1;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '`') return index + 1;\n if (sourceCode[index] === '$' && sourceCode[index + 1] === '{') {\n index = readBalancedExpression(sourceCode, index + 2, '}', { slashStartsRegExp: true }).endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return sourceCode.length;\n}\n\nfunction findPythonFStringEndIndex(sourceCode: string, stringStartIndex: number): number {\n const quote = getStringQuote(sourceCode, stringStartIndex);\n let index = stringStartIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n if (sourceCode[index] === '{' && sourceCode[index + 1] === '{') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '{') {\n index = readBalancedExpression(sourceCode, index + 1, '}', { slashStartsRegExp: false }).endIndex + 1;\n continue;\n }\n index += sourceCode[index] === '\\\\' ? 2 : 1;\n }\n\n return sourceCode.length;\n}\n\nfunction getStringQuote(sourceCode: string, stringStartIndex: number): string {\n const threeCharacterQuote = sourceCode.slice(stringStartIndex, stringStartIndex + 3);\n if (threeCharacterQuote === \"'''\" || threeCharacterQuote === '\"\"\"') return threeCharacterQuote;\n return sourceCode[stringStartIndex] ?? '';\n}\n\nfunction getPythonStringPrefixStartIndex(sourceCode: string, stringStartIndex: number): number {\n const prefix = getPythonStringPrefix(sourceCode, stringStartIndex);\n return prefix ? stringStartIndex - prefix.length : stringStartIndex;\n}\n\nfunction getPythonStringPrefix(sourceCode: string, stringStartIndex: number): string | undefined {\n if (sourceCode[stringStartIndex] !== '\"' && sourceCode[stringStartIndex] !== \"'\") return undefined;\n let index = stringStartIndex - 1;\n while (index >= 0 && /[A-Za-z]/.test(sourceCode[index] ?? '')) index -= 1;\n\n const prefix = sourceCode.slice(index + 1, stringStartIndex);\n if (!prefix || !/^[bfru]+$/i.test(prefix)) return undefined;\n if (index >= 0 && /[\\dA-Za-z_]/.test(sourceCode[index] ?? '')) return undefined;\n\n return prefix;\n}\n\nfunction preserveStringInterpolationExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringGrammar: CommentOrStringGrammar,\n sourceCode: string,\n stringStartIndex: number,\n stringLiteral: string\n): string {\n if (stringGrammar.templateSyntax === 'kotlin')\n return preserveKotlinStringTemplateExpressions(grammar, commentOrStringGrammars, stringLiteral);\n if (stringLiteral.startsWith('`'))\n return preserveJavaScriptTemplateExpressions(grammar, commentOrStringGrammars, stringLiteral);\n if (hasPythonFStringPrefix(sourceCode, stringStartIndex))\n return preservePythonFStringExpressions(grammar, commentOrStringGrammars, stringLiteral);\n return '';\n}\n\nfunction preserveKotlinStringTemplateExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const quoteLength = getStringQuote(stringLiteral, 0).length;\n const isRawString = quoteLength === 3;\n const expressions: string[] = [];\n let index = quoteLength;\n\n while (index < stringLiteral.length - quoteLength) {\n if (!isRawString && stringLiteral[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] !== '$') {\n index += 1;\n continue;\n }\n if (stringLiteral[index + 1] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 2, '}', {\n slashStartsComments: true,\n slashStartsRegExp: false,\n supportsKotlinStrings: true,\n });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n if (isKotlinIdentifierStart(stringLiteral[index + 1])) {\n let endIndex = index + 2;\n while (isKotlinIdentifierPart(stringLiteral[endIndex])) endIndex += 1;\n expressions.push(stringLiteral.slice(index + 1, endIndex));\n index = endIndex;\n continue;\n }\n index += 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction isKotlinIdentifierStart(character: string | undefined): boolean {\n return character !== undefined && /[\\p{L}_]/u.test(character);\n}\n\nfunction isKotlinIdentifierPart(character: string | undefined): boolean {\n return character !== undefined && /[\\p{L}\\p{N}_]/u.test(character);\n}\n\nfunction hasPythonFStringPrefix(sourceCode: string, stringStartIndex: number): boolean {\n return getPythonStringPrefix(sourceCode, stringStartIndex)?.toLowerCase().includes('f') ?? false;\n}\n\nfunction preserveJavaScriptTemplateExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const expressions: string[] = [];\n let index = 1;\n\n while (index < stringLiteral.length - 1) {\n if (stringLiteral[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] === '$' && stringLiteral[index + 1] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 2, '}', { slashStartsRegExp: true });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction preservePythonFStringExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const quoteLength = getStringQuote(stringLiteral, 0).length;\n const expressions: string[] = [];\n let index = quoteLength;\n\n while (index < stringLiteral.length - quoteLength) {\n if (stringLiteral[index] === '{' && stringLiteral[index + 1] === '{') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 1, '}', { slashStartsRegExp: false });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n index += stringLiteral[index] === '\\\\' ? 2 : 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction readBalancedExpression(\n sourceCode: string,\n startIndex: number,\n closeChar: string,\n options: BalancedExpressionOptions\n): { content: string; endIndex: number } {\n let depth = 1;\n let index = startIndex;\n\n while (index < sourceCode.length) {\n const char = sourceCode[index];\n if (char === '\"' && options.supportsKotlinStrings) {\n index = findKotlinStringEndIndex(sourceCode, index);\n continue;\n }\n if (char === '\"' || char === \"'\" || char === '`') {\n index = skipQuotedSpan(sourceCode, index);\n continue;\n }\n if (\n (options.slashStartsComments || options.slashStartsRegExp) &&\n sourceCode[index] === '/' &&\n sourceCode[index + 1] === '/'\n ) {\n index = skipJavaScriptSingleLineComment(sourceCode, index);\n continue;\n }\n if (\n (options.slashStartsComments || options.slashStartsRegExp) &&\n sourceCode[index] === '/' &&\n sourceCode[index + 1] === '*'\n ) {\n index = skipJavaScriptBlockComment(sourceCode, index);\n continue;\n }\n if (options.slashStartsRegExp && char === '/') {\n index = skipJavaScriptRegExpLiteral(sourceCode, index);\n continue;\n }\n if (char === '{') depth += 1;\n if (char === closeChar) {\n depth -= 1;\n if (depth === 0) return { content: sourceCode.slice(startIndex, index), endIndex: index };\n }\n index += 1;\n }\n\n return { content: sourceCode.slice(startIndex), endIndex: sourceCode.length };\n}\n\nfunction skipQuotedSpan(sourceCode: string, startIndex: number): number {\n const quote = getStringQuote(sourceCode, startIndex);\n let index = startIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n index += 1;\n }\n\n return index;\n}\n\nfunction skipJavaScriptRegExpLiteral(sourceCode: string, startIndex: number): number {\n let index = startIndex + 1;\n let inCharacterClass = false;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '[') inCharacterClass = true;\n if (sourceCode[index] === ']') inCharacterClass = false;\n if (sourceCode[index] === '/' && !inCharacterClass) {\n index += 1;\n while (/[A-Za-z]/.test(sourceCode[index] ?? '')) index += 1;\n return index;\n }\n if (sourceCode[index] === '\\n') return startIndex + 1;\n index += 1;\n }\n\n return startIndex + 1;\n}\n\nfunction skipJavaScriptSingleLineComment(sourceCode: string, startIndex: number): number {\n const newLineIndex = sourceCode.indexOf('\\n', startIndex + 2);\n return newLineIndex === -1 ? sourceCode.length : newLineIndex;\n}\n\nfunction skipJavaScriptBlockComment(sourceCode: string, startIndex: number): number {\n const endIndex = sourceCode.indexOf('*/', startIndex + 2);\n return endIndex === -1 ? sourceCode.length : endIndex + 2;\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
function e(e,t){return n(e,t,{removeStrings:!1})}function t(e,t){return n(e,t,{removeStrings:!0})}function n(e,t,n,a=r(e)){if(!e.comments?.length&&(!n.removeStrings||!e.strings?.length))return t;let s=[],c=0;for(;c<t.length;){let r;for(let e of a){let a=e.openRegExp;a.lastIndex=c;let o=a.exec(t);o&&i(o,e,r,n)&&(r={grammar:e,match:o})}if(r){let i=r.match.index;if(!r.grammar.isComment&&n.removeStrings&&(i=d(t,r.match.index)),s.push(t.slice(c,i)),r.grammar.isComment){let e=r.grammar.closeRegExp;e.lastIndex=r.match.index+r.match[0].length;let n=e.exec(t);c=n?n.index+n[0].length:t.length}else{c=o(t,r.match.index,r.grammar);let i=t.slice(r.match.index,c);n.removeStrings?s.push(p(e,a,r.grammar,t,r.match.index,i)):s.push(i)}}else s.push(t.slice(c)),c=t.length}return s.join(``)}function r(e){return[...e.comments?.map(e=>({closeRegExp:a(e.close??/(?=\n)/g),isComment:!0,openRegExp:a(e.open)}))??[],...e.strings?.map(e=>({closeRegExp:a(e.close),isComment:!1,openRegExp:a(e.open),templateSyntax:e.templateSyntax}))??[]]}function i(e,t,n,r){return!n||e.index<n.match.index?!0:e.index>n.match.index?!1:r.removeStrings&&n.grammar.isComment&&!t.isComment}function a(e){return new RegExp(e,e.flags.includes(`g`)?e.flags:`${e.flags}g`)}function o(e,t,n){if(n.templateSyntax===`kotlin`)return s(e,t);if(e[t]==="`")return c(e,t);if(_(e,t))return l(e,t);let r=n.closeRegExp;r.lastIndex=t+1;let i=r.exec(e);return i?i.index+i[0].length:e.length}function s(e,t){let n=u(e,t),r=n.length===3,i=t+n.length;for(;i<e.length;){if(e.startsWith(n,i))return i+n.length
|
|
2
|
-
`)}function h(e){return e!==void 0&&/[\p{L}_]/u.test(e)}function g(e){return e!==void 0&&/[\p{L}\p{N}_]/u.test(e)}function _(e,t){return f(e,t)?.toLowerCase().includes(`f`)??!1}function v(e,t,r){let i=[],a=1;for(;a<r.length-1;)
|
|
3
|
-
`)}function y(e,t,r){let i=u(r,0).length,a=[],o=i;for(;o<r.length-i;)
|
|
4
|
-
`)}function b(e,t,n,r){let i=1,a=t;for(;a<e.length;){let o=e[a];if(o===`"`&&r.supportsKotlinStrings)
|
|
1
|
+
function e(e,t){return n(e,t,{removeStrings:!1})}function t(e,t){return n(e,t,{removeStrings:!0})}function n(e,t,n,a=r(e)){if(!e.comments?.length&&(!n.removeStrings||!e.strings?.length))return t;let s=[],c=0;for(;c<t.length;){let r;for(let e of a){let a=e.openRegExp;a.lastIndex=c;let o=a.exec(t);o&&i(o,e,r,n)&&(r={grammar:e,match:o})}if(r){let i=r.match.index;if(!r.grammar.isComment&&n.removeStrings&&(i=d(t,r.match.index)),s.push(t.slice(c,i)),r.grammar.isComment){let e=r.grammar.closeRegExp;e.lastIndex=r.match.index+r.match[0].length;let n=e.exec(t);c=n?n.index+n[0].length:t.length}else{c=o(t,r.match.index,r.grammar);let i=t.slice(r.match.index,c);n.removeStrings?s.push(p(e,a,r.grammar,t,r.match.index,i)):s.push(i)}}else s.push(t.slice(c)),c=t.length}return s.join(``)}function r(e){return[...e.comments?.map(e=>({closeRegExp:a(e.close??/(?=\n)/g),isComment:!0,openRegExp:a(e.open)}))??[],...e.strings?.map(e=>({closeRegExp:a(e.close),isComment:!1,openRegExp:a(e.open),templateSyntax:e.templateSyntax}))??[]]}function i(e,t,n,r){return!n||e.index<n.match.index?!0:e.index>n.match.index?!1:r.removeStrings&&n.grammar.isComment&&!t.isComment}function a(e){return new RegExp(e,e.flags.includes(`g`)?e.flags:`${e.flags}g`)}function o(e,t,n){if(n.templateSyntax===`kotlin`)return s(e,t);if(e[t]==="`")return c(e,t);if(_(e,t))return l(e,t);let r=n.closeRegExp;r.lastIndex=t+1;let i=r.exec(e);return i?i.index+i[0].length:e.length}function s(e,t){let n=u(e,t),r=n.length===3,i=t+n.length;for(;i<e.length;){if(e.startsWith(n,i))return i+n.length;!r&&e[i]===`\\`?i+=2:e[i]===`$`&&e[i+1]===`{`?i=b(e,i+2,`}`,{slashStartsComments:!0,slashStartsRegExp:!1,supportsKotlinStrings:!0}).endIndex+1:i+=1}return e.length}function c(e,t){let n=t+1;for(;n<e.length;)if(e[n]===`\\`)n+=2;else{if(e[n]==="`")return n+1;e[n]===`$`&&e[n+1]===`{`?n=b(e,n+2,`}`,{slashStartsRegExp:!0}).endIndex+1:n+=1}return e.length}function l(e,t){let n=u(e,t),r=t+n.length;for(;r<e.length;){if(e.startsWith(n,r))return r+n.length;e[r]===`{`&&e[r+1]===`{`?r+=2:e[r]===`{`?r=b(e,r+1,`}`,{slashStartsRegExp:!1}).endIndex+1:r+=e[r]===`\\`?2:1}return e.length}function u(e,t){let n=e.slice(t,t+3);return n===`'''`||n===`"""`?n:e[t]??``}function d(e,t){let n=f(e,t);return n?t-n.length:t}function f(e,t){if(e[t]!==`"`&&e[t]!==`'`)return;let n=t-1;for(;n>=0&&/[A-Za-z]/.test(e[n]??``);)--n;let r=e.slice(n+1,t);if(r&&/^[bfru]+$/i.test(r)&&!(n>=0&&/[\dA-Za-z_]/.test(e[n]??``)))return r}function p(e,t,n,r,i,a){return n.templateSyntax===`kotlin`?m(e,t,a):a.startsWith("`")?v(e,t,a):_(r,i)?y(e,t,a):``}function m(e,t,r){let i=u(r,0).length,a=i===3,o=[],s=i;for(;s<r.length-i;)if(!a&&r[s]===`\\`)s+=2;else if(r[s]!==`$`)s+=1;else if(r[s+1]===`{`){let i=b(r,s+2,`}`,{slashStartsComments:!0,slashStartsRegExp:!1,supportsKotlinStrings:!0});o.push(n(e,i.content,{removeStrings:!0},t)),s=i.endIndex+1}else if(h(r[s+1])){let e=s+2;for(;g(r[e]);)e+=1;o.push(r.slice(s+1,e)),s=e}else s+=1;return o.join(`
|
|
2
|
+
`)}function h(e){return e!==void 0&&/[\p{L}_]/u.test(e)}function g(e){return e!==void 0&&/[\p{L}\p{N}_]/u.test(e)}function _(e,t){return f(e,t)?.toLowerCase().includes(`f`)??!1}function v(e,t,r){let i=[],a=1;for(;a<r.length-1;)if(r[a]===`\\`)a+=2;else if(r[a]===`$`&&r[a+1]===`{`){let o=b(r,a+2,`}`,{slashStartsRegExp:!0});i.push(n(e,o.content,{removeStrings:!0},t)),a=o.endIndex+1}else a+=1;return i.join(`
|
|
3
|
+
`)}function y(e,t,r){let i=u(r,0).length,a=[],o=i;for(;o<r.length-i;)if(r[o]===`{`&&r[o+1]===`{`)o+=2;else if(r[o]===`{`){let i=b(r,o+1,`}`,{slashStartsRegExp:!1});a.push(n(e,i.content,{removeStrings:!0},t)),o=i.endIndex+1}else o+=r[o]===`\\`?2:1;return a.join(`
|
|
4
|
+
`)}function b(e,t,n,r){let i=1,a=t;for(;a<e.length;){let o=e[a];if(o===`"`&&r.supportsKotlinStrings)a=s(e,a);else if(o===`"`||o===`'`||o==="`")a=x(e,a);else if((r.slashStartsComments||r.slashStartsRegExp)&&e[a]===`/`&&e[a+1]===`/`)a=C(e,a);else if((r.slashStartsComments||r.slashStartsRegExp)&&e[a]===`/`&&e[a+1]===`*`)a=w(e,a);else if(r.slashStartsRegExp&&o===`/`)a=S(e,a);else{if(o===`{`&&(i+=1),o===n&&(--i,i===0))return{content:e.slice(t,a),endIndex:a};a+=1}}return{content:e.slice(t),endIndex:e.length}}function x(e,t){let n=u(e,t),r=t+n.length;for(;r<e.length;)if(e[r]===`\\`)r+=2;else{if(e.startsWith(n,r))return r+n.length;r+=1}return r}function S(e,t){let n=t+1,r=!1;for(;n<e.length;)if(e[n]===`\\`)n+=2;else{if(e[n]===`[`&&(r=!0),e[n]===`]`&&(r=!1),e[n]===`/`&&!r){for(n+=1;/[A-Za-z]/.test(e[n]??``);)n+=1;return n}if(e[n]===`
|
|
5
5
|
`)return t+1;n+=1}return t+1}function C(e,t){let n=e.indexOf(`
|
|
6
6
|
`,t+2);return n===-1?e.length:n}function w(e,t){let n=e.indexOf(`*/`,t+2);return n===-1?e.length:n+2}export{t as removeCommentsAndStringsInSourceCode,e as removeCommentsInSourceCode};
|
|
7
7
|
//# sourceMappingURL=removeCommentsInSourceCode.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"removeCommentsInSourceCode.js","names":["removeCommentsInSourceCode","removeCommentsAndMaybeStringsInSourceCode","grammar","sourceCode","removeCommentsAndStringsInSourceCode","compileCommentOrStringGrammars","options","newSourceCodeSlices","lastIndex","first","commentOrStringGrammer","commentOrStringGrammars","startRegExp","match","shouldPreferMatch","stringPrefixStartIndex","getPythonStringPrefixStartIndex","closeRegExp","findStringEndIndex","stringLiteral","preserveStringInterpolationExpressions","makeGlobalRegExp","v","regExp","findKotlinStringEndIndex","stringStartIndex","findJavaScriptTemplateEndIndex","hasPythonFStringPrefix","findPythonFStringEndIndex","quote","getStringQuote","isRawString","index","readBalancedExpression","threeCharacterQuote","prefix","getPythonStringPrefix","stringGrammar","preserveKotlinStringTemplateExpressions","preserveJavaScriptTemplateExpressions","preservePythonFStringExpressions","quoteLength","expressions","expression","isKotlinIdentifierStart","endIndex","isKotlinIdentifierPart","character","depth","startIndex","char","skipQuotedSpan","skipJavaScriptSingleLineComment","skipJavaScriptBlockComment","skipJavaScriptRegExpLiteral","closeChar","inCharacterClass","newLineIndex"],"sources":["../../src/helpers/removeCommentsInSourceCode.ts"],"sourcesContent":["import type { LanguageDefinition } from '../types/language.js';\n\nexport type SourceCodeGrammar = NonNullable<LanguageDefinition['grammer']>;\n\n/** Removes comments from source code according to the provided language grammar. */\nexport function removeCommentsInSourceCode(grammar: SourceCodeGrammar, sourceCode: string): string {\n return removeCommentsAndMaybeStringsInSourceCode(grammar, sourceCode, { removeStrings: false });\n}\n\n/** Removes comments and string literal contents while preserving executable interpolation expressions. */\nexport function removeCommentsAndStringsInSourceCode(grammar: SourceCodeGrammar, sourceCode: string): string {\n return removeCommentsAndMaybeStringsInSourceCode(grammar, sourceCode, { removeStrings: true });\n}\n\ninterface CommentOrStringGrammar {\n closeRegExp: RegExp;\n isComment: boolean;\n openRegExp: RegExp;\n templateSyntax?: 'kotlin';\n}\n\ninterface BalancedExpressionOptions {\n slashStartsComments?: boolean;\n slashStartsRegExp: boolean;\n supportsKotlinStrings?: boolean;\n}\n\nfunction removeCommentsAndMaybeStringsInSourceCode(\n grammar: SourceCodeGrammar,\n sourceCode: string,\n options: { removeStrings: boolean },\n commentOrStringGrammars = compileCommentOrStringGrammars(grammar)\n): string {\n if (!grammar.comments?.length && (!options.removeStrings || !grammar.strings?.length)) return sourceCode;\n\n const newSourceCodeSlices: string[] = [];\n\n let lastIndex = 0;\n\n while (lastIndex < sourceCode.length) {\n let first: { grammar: CommentOrStringGrammar; match: RegExpExecArray } | undefined;\n\n for (const commentOrStringGrammer of commentOrStringGrammars) {\n const startRegExp = commentOrStringGrammer.openRegExp;\n startRegExp.lastIndex = lastIndex;\n\n const match = startRegExp.exec(sourceCode);\n\n if (match && shouldPreferMatch(match, commentOrStringGrammer, first, options)) {\n first = { grammar: commentOrStringGrammer, match };\n }\n }\n\n if (first) {\n let stringPrefixStartIndex = first.match.index;\n if (!first.grammar.isComment && options.removeStrings) {\n stringPrefixStartIndex = getPythonStringPrefixStartIndex(sourceCode, first.match.index);\n }\n newSourceCodeSlices.push(sourceCode.slice(lastIndex, stringPrefixStartIndex));\n\n if (!first.grammar.isComment) {\n lastIndex = findStringEndIndex(sourceCode, first.match.index, first.grammar);\n const stringLiteral = sourceCode.slice(first.match.index, lastIndex);\n if (options.removeStrings) {\n newSourceCodeSlices.push(\n preserveStringInterpolationExpressions(\n grammar,\n commentOrStringGrammars,\n first.grammar,\n sourceCode,\n first.match.index,\n stringLiteral\n )\n );\n } else {\n newSourceCodeSlices.push(stringLiteral);\n }\n } else {\n const closeRegExp = first.grammar.closeRegExp;\n closeRegExp.lastIndex = first.match.index + first.match[0].length;\n\n const match = closeRegExp.exec(sourceCode);\n\n lastIndex = match ? match.index + match[0].length : sourceCode.length;\n }\n } else {\n newSourceCodeSlices.push(sourceCode.slice(lastIndex));\n lastIndex = sourceCode.length;\n }\n }\n\n return newSourceCodeSlices.join('');\n}\n\nfunction compileCommentOrStringGrammars(grammar: SourceCodeGrammar): readonly CommentOrStringGrammar[] {\n return [\n ...(grammar.comments?.map((v) => ({\n closeRegExp: makeGlobalRegExp(v.close ?? /(?=\\n)/g),\n isComment: true,\n openRegExp: makeGlobalRegExp(v.open),\n })) ?? []),\n ...(grammar.strings?.map((v) => ({\n closeRegExp: makeGlobalRegExp(v.close),\n isComment: false,\n openRegExp: makeGlobalRegExp(v.open),\n templateSyntax: v.templateSyntax,\n })) ?? []),\n ] satisfies CommentOrStringGrammar[];\n}\n\nfunction shouldPreferMatch(\n match: RegExpExecArray,\n grammar: CommentOrStringGrammar,\n first: { grammar: CommentOrStringGrammar; match: RegExpExecArray } | undefined,\n options: { removeStrings: boolean }\n): boolean {\n if (!first) return true;\n if (match.index < first.match.index) return true;\n if (match.index > first.match.index) return false;\n return options.removeStrings && first.grammar.isComment && !grammar.isComment;\n}\n\nfunction makeGlobalRegExp(regExp: RegExp): RegExp {\n return new RegExp(regExp, regExp.flags.includes('g') ? regExp.flags : `${regExp.flags}g`);\n}\n\nfunction findStringEndIndex(sourceCode: string, stringStartIndex: number, grammar: CommentOrStringGrammar): number {\n if (grammar.templateSyntax === 'kotlin') return findKotlinStringEndIndex(sourceCode, stringStartIndex);\n if (sourceCode[stringStartIndex] === '`') return findJavaScriptTemplateEndIndex(sourceCode, stringStartIndex);\n if (hasPythonFStringPrefix(sourceCode, stringStartIndex))\n return findPythonFStringEndIndex(sourceCode, stringStartIndex);\n\n const closeRegExp = grammar.closeRegExp;\n closeRegExp.lastIndex = stringStartIndex + 1;\n const match = closeRegExp.exec(sourceCode);\n return match ? match.index + match[0].length : sourceCode.length;\n}\n\nfunction findKotlinStringEndIndex(sourceCode: string, stringStartIndex: number): number {\n const quote = getStringQuote(sourceCode, stringStartIndex);\n const isRawString = quote.length === 3;\n let index = stringStartIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n if (!isRawString && sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '$' && sourceCode[index + 1] === '{') {\n index =\n readBalancedExpression(sourceCode, index + 2, '}', {\n slashStartsComments: true,\n slashStartsRegExp: false,\n supportsKotlinStrings: true,\n }).endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return sourceCode.length;\n}\n\nfunction findJavaScriptTemplateEndIndex(sourceCode: string, stringStartIndex: number): number {\n let index = stringStartIndex + 1;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '`') return index + 1;\n if (sourceCode[index] === '$' && sourceCode[index + 1] === '{') {\n index = readBalancedExpression(sourceCode, index + 2, '}', { slashStartsRegExp: true }).endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return sourceCode.length;\n}\n\nfunction findPythonFStringEndIndex(sourceCode: string, stringStartIndex: number): number {\n const quote = getStringQuote(sourceCode, stringStartIndex);\n let index = stringStartIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n if (sourceCode[index] === '{' && sourceCode[index + 1] === '{') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '{') {\n index = readBalancedExpression(sourceCode, index + 1, '}', { slashStartsRegExp: false }).endIndex + 1;\n continue;\n }\n index += sourceCode[index] === '\\\\' ? 2 : 1;\n }\n\n return sourceCode.length;\n}\n\nfunction getStringQuote(sourceCode: string, stringStartIndex: number): string {\n const threeCharacterQuote = sourceCode.slice(stringStartIndex, stringStartIndex + 3);\n if (threeCharacterQuote === \"'''\" || threeCharacterQuote === '\"\"\"') return threeCharacterQuote;\n return sourceCode[stringStartIndex] ?? '';\n}\n\nfunction getPythonStringPrefixStartIndex(sourceCode: string, stringStartIndex: number): number {\n const prefix = getPythonStringPrefix(sourceCode, stringStartIndex);\n return prefix ? stringStartIndex - prefix.length : stringStartIndex;\n}\n\nfunction getPythonStringPrefix(sourceCode: string, stringStartIndex: number): string | undefined {\n if (sourceCode[stringStartIndex] !== '\"' && sourceCode[stringStartIndex] !== \"'\") return undefined;\n let index = stringStartIndex - 1;\n while (index >= 0 && /[A-Za-z]/.test(sourceCode[index] ?? '')) index -= 1;\n\n const prefix = sourceCode.slice(index + 1, stringStartIndex);\n if (!prefix || !/^[bfru]+$/i.test(prefix)) return undefined;\n if (index >= 0 && /[\\dA-Za-z_]/.test(sourceCode[index] ?? '')) return undefined;\n\n return prefix;\n}\n\nfunction preserveStringInterpolationExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringGrammar: CommentOrStringGrammar,\n sourceCode: string,\n stringStartIndex: number,\n stringLiteral: string\n): string {\n if (stringGrammar.templateSyntax === 'kotlin')\n return preserveKotlinStringTemplateExpressions(grammar, commentOrStringGrammars, stringLiteral);\n if (stringLiteral.startsWith('`'))\n return preserveJavaScriptTemplateExpressions(grammar, commentOrStringGrammars, stringLiteral);\n if (hasPythonFStringPrefix(sourceCode, stringStartIndex))\n return preservePythonFStringExpressions(grammar, commentOrStringGrammars, stringLiteral);\n return '';\n}\n\nfunction preserveKotlinStringTemplateExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const quoteLength = getStringQuote(stringLiteral, 0).length;\n const isRawString = quoteLength === 3;\n const expressions: string[] = [];\n let index = quoteLength;\n\n while (index < stringLiteral.length - quoteLength) {\n if (!isRawString && stringLiteral[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] !== '$') {\n index += 1;\n continue;\n }\n if (stringLiteral[index + 1] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 2, '}', {\n slashStartsComments: true,\n slashStartsRegExp: false,\n supportsKotlinStrings: true,\n });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n if (isKotlinIdentifierStart(stringLiteral[index + 1])) {\n let endIndex = index + 2;\n while (isKotlinIdentifierPart(stringLiteral[endIndex])) endIndex += 1;\n expressions.push(stringLiteral.slice(index + 1, endIndex));\n index = endIndex;\n continue;\n }\n index += 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction isKotlinIdentifierStart(character: string | undefined): boolean {\n return character !== undefined && /[\\p{L}_]/u.test(character);\n}\n\nfunction isKotlinIdentifierPart(character: string | undefined): boolean {\n return character !== undefined && /[\\p{L}\\p{N}_]/u.test(character);\n}\n\nfunction hasPythonFStringPrefix(sourceCode: string, stringStartIndex: number): boolean {\n return getPythonStringPrefix(sourceCode, stringStartIndex)?.toLowerCase().includes('f') ?? false;\n}\n\nfunction preserveJavaScriptTemplateExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const expressions: string[] = [];\n let index = 1;\n\n while (index < stringLiteral.length - 1) {\n if (stringLiteral[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] === '$' && stringLiteral[index + 1] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 2, '}', { slashStartsRegExp: true });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction preservePythonFStringExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const quoteLength = getStringQuote(stringLiteral, 0).length;\n const expressions: string[] = [];\n let index = quoteLength;\n\n while (index < stringLiteral.length - quoteLength) {\n if (stringLiteral[index] === '{' && stringLiteral[index + 1] === '{') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 1, '}', { slashStartsRegExp: false });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n index += stringLiteral[index] === '\\\\' ? 2 : 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction readBalancedExpression(\n sourceCode: string,\n startIndex: number,\n closeChar: string,\n options: BalancedExpressionOptions\n): { content: string; endIndex: number } {\n let depth = 1;\n let index = startIndex;\n\n while (index < sourceCode.length) {\n const char = sourceCode[index];\n if (char === '\"' && options.supportsKotlinStrings) {\n index = findKotlinStringEndIndex(sourceCode, index);\n continue;\n }\n if (char === '\"' || char === \"'\" || char === '`') {\n index = skipQuotedSpan(sourceCode, index);\n continue;\n }\n if (\n (options.slashStartsComments || options.slashStartsRegExp) &&\n sourceCode[index] === '/' &&\n sourceCode[index + 1] === '/'\n ) {\n index = skipJavaScriptSingleLineComment(sourceCode, index);\n continue;\n }\n if (\n (options.slashStartsComments || options.slashStartsRegExp) &&\n sourceCode[index] === '/' &&\n sourceCode[index + 1] === '*'\n ) {\n index = skipJavaScriptBlockComment(sourceCode, index);\n continue;\n }\n if (options.slashStartsRegExp && char === '/') {\n index = skipJavaScriptRegExpLiteral(sourceCode, index);\n continue;\n }\n if (char === '{') depth += 1;\n if (char === closeChar) {\n depth -= 1;\n if (depth === 0) return { content: sourceCode.slice(startIndex, index), endIndex: index };\n }\n index += 1;\n }\n\n return { content: sourceCode.slice(startIndex), endIndex: sourceCode.length };\n}\n\nfunction skipQuotedSpan(sourceCode: string, startIndex: number): number {\n const quote = getStringQuote(sourceCode, startIndex);\n let index = startIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n index += 1;\n }\n\n return index;\n}\n\nfunction skipJavaScriptRegExpLiteral(sourceCode: string, startIndex: number): number {\n let index = startIndex + 1;\n let inCharacterClass = false;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '[') inCharacterClass = true;\n if (sourceCode[index] === ']') inCharacterClass = false;\n if (sourceCode[index] === '/' && !inCharacterClass) {\n index += 1;\n while (/[A-Za-z]/.test(sourceCode[index] ?? '')) index += 1;\n return index;\n }\n if (sourceCode[index] === '\\n') return startIndex + 1;\n index += 1;\n }\n\n return startIndex + 1;\n}\n\nfunction skipJavaScriptSingleLineComment(sourceCode: string, startIndex: number): number {\n const newLineIndex = sourceCode.indexOf('\\n', startIndex + 2);\n return newLineIndex === -1 ? sourceCode.length : newLineIndex;\n}\n\nfunction skipJavaScriptBlockComment(sourceCode: string, startIndex: number): number {\n const endIndex = sourceCode.indexOf('*/', startIndex + 2);\n return endIndex === -1 ? sourceCode.length : endIndex + 2;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"removeCommentsInSourceCode.js","names":["removeCommentsInSourceCode","removeCommentsAndMaybeStringsInSourceCode","grammar","sourceCode","removeCommentsAndStringsInSourceCode","compileCommentOrStringGrammars","options","newSourceCodeSlices","lastIndex","first","commentOrStringGrammer","commentOrStringGrammars","startRegExp","match","shouldPreferMatch","stringPrefixStartIndex","getPythonStringPrefixStartIndex","closeRegExp","findStringEndIndex","stringLiteral","preserveStringInterpolationExpressions","makeGlobalRegExp","v","regExp","findKotlinStringEndIndex","stringStartIndex","findJavaScriptTemplateEndIndex","hasPythonFStringPrefix","findPythonFStringEndIndex","quote","getStringQuote","isRawString","index","readBalancedExpression","threeCharacterQuote","prefix","getPythonStringPrefix","stringGrammar","preserveKotlinStringTemplateExpressions","preserveJavaScriptTemplateExpressions","preservePythonFStringExpressions","quoteLength","expressions","expression","isKotlinIdentifierStart","endIndex","isKotlinIdentifierPart","character","depth","startIndex","char","skipQuotedSpan","skipJavaScriptSingleLineComment","skipJavaScriptBlockComment","skipJavaScriptRegExpLiteral","closeChar","inCharacterClass","newLineIndex"],"sources":["../../src/helpers/removeCommentsInSourceCode.ts"],"sourcesContent":["import type { LanguageDefinition } from '../types/language.js';\n\nexport type SourceCodeGrammar = NonNullable<LanguageDefinition['grammer']>;\n\n/** Removes comments from source code according to the provided language grammar. */\nexport function removeCommentsInSourceCode(grammar: SourceCodeGrammar, sourceCode: string): string {\n return removeCommentsAndMaybeStringsInSourceCode(grammar, sourceCode, { removeStrings: false });\n}\n\n/** Removes comments and string literal contents while preserving executable interpolation expressions. */\nexport function removeCommentsAndStringsInSourceCode(grammar: SourceCodeGrammar, sourceCode: string): string {\n return removeCommentsAndMaybeStringsInSourceCode(grammar, sourceCode, { removeStrings: true });\n}\n\ninterface CommentOrStringGrammar {\n closeRegExp: RegExp;\n isComment: boolean;\n openRegExp: RegExp;\n templateSyntax?: 'kotlin';\n}\n\ninterface BalancedExpressionOptions {\n slashStartsComments?: boolean;\n slashStartsRegExp: boolean;\n supportsKotlinStrings?: boolean;\n}\n\nfunction removeCommentsAndMaybeStringsInSourceCode(\n grammar: SourceCodeGrammar,\n sourceCode: string,\n options: { removeStrings: boolean },\n commentOrStringGrammars = compileCommentOrStringGrammars(grammar)\n): string {\n if (!grammar.comments?.length && (!options.removeStrings || !grammar.strings?.length)) return sourceCode;\n\n const newSourceCodeSlices: string[] = [];\n\n let lastIndex = 0;\n\n while (lastIndex < sourceCode.length) {\n let first: { grammar: CommentOrStringGrammar; match: RegExpExecArray } | undefined;\n\n for (const commentOrStringGrammer of commentOrStringGrammars) {\n const startRegExp = commentOrStringGrammer.openRegExp;\n startRegExp.lastIndex = lastIndex;\n\n const match = startRegExp.exec(sourceCode);\n\n if (match && shouldPreferMatch(match, commentOrStringGrammer, first, options)) {\n first = { grammar: commentOrStringGrammer, match };\n }\n }\n\n if (first) {\n let stringPrefixStartIndex = first.match.index;\n if (!first.grammar.isComment && options.removeStrings) {\n stringPrefixStartIndex = getPythonStringPrefixStartIndex(sourceCode, first.match.index);\n }\n newSourceCodeSlices.push(sourceCode.slice(lastIndex, stringPrefixStartIndex));\n\n if (!first.grammar.isComment) {\n lastIndex = findStringEndIndex(sourceCode, first.match.index, first.grammar);\n const stringLiteral = sourceCode.slice(first.match.index, lastIndex);\n if (options.removeStrings) {\n newSourceCodeSlices.push(\n preserveStringInterpolationExpressions(\n grammar,\n commentOrStringGrammars,\n first.grammar,\n sourceCode,\n first.match.index,\n stringLiteral\n )\n );\n } else {\n newSourceCodeSlices.push(stringLiteral);\n }\n } else {\n const closeRegExp = first.grammar.closeRegExp;\n closeRegExp.lastIndex = first.match.index + first.match[0].length;\n\n const match = closeRegExp.exec(sourceCode);\n\n lastIndex = match ? match.index + match[0].length : sourceCode.length;\n }\n } else {\n newSourceCodeSlices.push(sourceCode.slice(lastIndex));\n lastIndex = sourceCode.length;\n }\n }\n\n return newSourceCodeSlices.join('');\n}\n\nfunction compileCommentOrStringGrammars(grammar: SourceCodeGrammar): readonly CommentOrStringGrammar[] {\n return [\n ...(grammar.comments?.map((v) => ({\n closeRegExp: makeGlobalRegExp(v.close ?? /(?=\\n)/g),\n isComment: true,\n openRegExp: makeGlobalRegExp(v.open),\n })) ?? []),\n ...(grammar.strings?.map((v) => ({\n closeRegExp: makeGlobalRegExp(v.close),\n isComment: false,\n openRegExp: makeGlobalRegExp(v.open),\n templateSyntax: v.templateSyntax,\n })) ?? []),\n ] satisfies CommentOrStringGrammar[];\n}\n\nfunction shouldPreferMatch(\n match: RegExpExecArray,\n grammar: CommentOrStringGrammar,\n first: { grammar: CommentOrStringGrammar; match: RegExpExecArray } | undefined,\n options: { removeStrings: boolean }\n): boolean {\n if (!first) return true;\n if (match.index < first.match.index) return true;\n if (match.index > first.match.index) return false;\n return options.removeStrings && first.grammar.isComment && !grammar.isComment;\n}\n\nfunction makeGlobalRegExp(regExp: RegExp): RegExp {\n return new RegExp(regExp, regExp.flags.includes('g') ? regExp.flags : `${regExp.flags}g`);\n}\n\nfunction findStringEndIndex(sourceCode: string, stringStartIndex: number, grammar: CommentOrStringGrammar): number {\n if (grammar.templateSyntax === 'kotlin') return findKotlinStringEndIndex(sourceCode, stringStartIndex);\n if (sourceCode[stringStartIndex] === '`') return findJavaScriptTemplateEndIndex(sourceCode, stringStartIndex);\n if (hasPythonFStringPrefix(sourceCode, stringStartIndex))\n return findPythonFStringEndIndex(sourceCode, stringStartIndex);\n\n const closeRegExp = grammar.closeRegExp;\n closeRegExp.lastIndex = stringStartIndex + 1;\n const match = closeRegExp.exec(sourceCode);\n return match ? match.index + match[0].length : sourceCode.length;\n}\n\nfunction findKotlinStringEndIndex(sourceCode: string, stringStartIndex: number): number {\n const quote = getStringQuote(sourceCode, stringStartIndex);\n const isRawString = quote.length === 3;\n let index = stringStartIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n if (!isRawString && sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '$' && sourceCode[index + 1] === '{') {\n index =\n readBalancedExpression(sourceCode, index + 2, '}', {\n slashStartsComments: true,\n slashStartsRegExp: false,\n supportsKotlinStrings: true,\n }).endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return sourceCode.length;\n}\n\nfunction findJavaScriptTemplateEndIndex(sourceCode: string, stringStartIndex: number): number {\n let index = stringStartIndex + 1;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '`') return index + 1;\n if (sourceCode[index] === '$' && sourceCode[index + 1] === '{') {\n index = readBalancedExpression(sourceCode, index + 2, '}', { slashStartsRegExp: true }).endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return sourceCode.length;\n}\n\nfunction findPythonFStringEndIndex(sourceCode: string, stringStartIndex: number): number {\n const quote = getStringQuote(sourceCode, stringStartIndex);\n let index = stringStartIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n if (sourceCode[index] === '{' && sourceCode[index + 1] === '{') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '{') {\n index = readBalancedExpression(sourceCode, index + 1, '}', { slashStartsRegExp: false }).endIndex + 1;\n continue;\n }\n index += sourceCode[index] === '\\\\' ? 2 : 1;\n }\n\n return sourceCode.length;\n}\n\nfunction getStringQuote(sourceCode: string, stringStartIndex: number): string {\n const threeCharacterQuote = sourceCode.slice(stringStartIndex, stringStartIndex + 3);\n if (threeCharacterQuote === \"'''\" || threeCharacterQuote === '\"\"\"') return threeCharacterQuote;\n return sourceCode[stringStartIndex] ?? '';\n}\n\nfunction getPythonStringPrefixStartIndex(sourceCode: string, stringStartIndex: number): number {\n const prefix = getPythonStringPrefix(sourceCode, stringStartIndex);\n return prefix ? stringStartIndex - prefix.length : stringStartIndex;\n}\n\nfunction getPythonStringPrefix(sourceCode: string, stringStartIndex: number): string | undefined {\n if (sourceCode[stringStartIndex] !== '\"' && sourceCode[stringStartIndex] !== \"'\") return undefined;\n let index = stringStartIndex - 1;\n while (index >= 0 && /[A-Za-z]/.test(sourceCode[index] ?? '')) index -= 1;\n\n const prefix = sourceCode.slice(index + 1, stringStartIndex);\n if (!prefix || !/^[bfru]+$/i.test(prefix)) return undefined;\n if (index >= 0 && /[\\dA-Za-z_]/.test(sourceCode[index] ?? '')) return undefined;\n\n return prefix;\n}\n\nfunction preserveStringInterpolationExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringGrammar: CommentOrStringGrammar,\n sourceCode: string,\n stringStartIndex: number,\n stringLiteral: string\n): string {\n if (stringGrammar.templateSyntax === 'kotlin')\n return preserveKotlinStringTemplateExpressions(grammar, commentOrStringGrammars, stringLiteral);\n if (stringLiteral.startsWith('`'))\n return preserveJavaScriptTemplateExpressions(grammar, commentOrStringGrammars, stringLiteral);\n if (hasPythonFStringPrefix(sourceCode, stringStartIndex))\n return preservePythonFStringExpressions(grammar, commentOrStringGrammars, stringLiteral);\n return '';\n}\n\nfunction preserveKotlinStringTemplateExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const quoteLength = getStringQuote(stringLiteral, 0).length;\n const isRawString = quoteLength === 3;\n const expressions: string[] = [];\n let index = quoteLength;\n\n while (index < stringLiteral.length - quoteLength) {\n if (!isRawString && stringLiteral[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] !== '$') {\n index += 1;\n continue;\n }\n if (stringLiteral[index + 1] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 2, '}', {\n slashStartsComments: true,\n slashStartsRegExp: false,\n supportsKotlinStrings: true,\n });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n if (isKotlinIdentifierStart(stringLiteral[index + 1])) {\n let endIndex = index + 2;\n while (isKotlinIdentifierPart(stringLiteral[endIndex])) endIndex += 1;\n expressions.push(stringLiteral.slice(index + 1, endIndex));\n index = endIndex;\n continue;\n }\n index += 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction isKotlinIdentifierStart(character: string | undefined): boolean {\n return character !== undefined && /[\\p{L}_]/u.test(character);\n}\n\nfunction isKotlinIdentifierPart(character: string | undefined): boolean {\n return character !== undefined && /[\\p{L}\\p{N}_]/u.test(character);\n}\n\nfunction hasPythonFStringPrefix(sourceCode: string, stringStartIndex: number): boolean {\n return getPythonStringPrefix(sourceCode, stringStartIndex)?.toLowerCase().includes('f') ?? false;\n}\n\nfunction preserveJavaScriptTemplateExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const expressions: string[] = [];\n let index = 1;\n\n while (index < stringLiteral.length - 1) {\n if (stringLiteral[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] === '$' && stringLiteral[index + 1] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 2, '}', { slashStartsRegExp: true });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n index += 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction preservePythonFStringExpressions(\n grammar: SourceCodeGrammar,\n commentOrStringGrammars: readonly CommentOrStringGrammar[],\n stringLiteral: string\n): string {\n const quoteLength = getStringQuote(stringLiteral, 0).length;\n const expressions: string[] = [];\n let index = quoteLength;\n\n while (index < stringLiteral.length - quoteLength) {\n if (stringLiteral[index] === '{' && stringLiteral[index + 1] === '{') {\n index += 2;\n continue;\n }\n if (stringLiteral[index] === '{') {\n const expression = readBalancedExpression(stringLiteral, index + 1, '}', { slashStartsRegExp: false });\n expressions.push(\n removeCommentsAndMaybeStringsInSourceCode(\n grammar,\n expression.content,\n { removeStrings: true },\n commentOrStringGrammars\n )\n );\n index = expression.endIndex + 1;\n continue;\n }\n index += stringLiteral[index] === '\\\\' ? 2 : 1;\n }\n\n return expressions.join('\\n');\n}\n\nfunction readBalancedExpression(\n sourceCode: string,\n startIndex: number,\n closeChar: string,\n options: BalancedExpressionOptions\n): { content: string; endIndex: number } {\n let depth = 1;\n let index = startIndex;\n\n while (index < sourceCode.length) {\n const char = sourceCode[index];\n if (char === '\"' && options.supportsKotlinStrings) {\n index = findKotlinStringEndIndex(sourceCode, index);\n continue;\n }\n if (char === '\"' || char === \"'\" || char === '`') {\n index = skipQuotedSpan(sourceCode, index);\n continue;\n }\n if (\n (options.slashStartsComments || options.slashStartsRegExp) &&\n sourceCode[index] === '/' &&\n sourceCode[index + 1] === '/'\n ) {\n index = skipJavaScriptSingleLineComment(sourceCode, index);\n continue;\n }\n if (\n (options.slashStartsComments || options.slashStartsRegExp) &&\n sourceCode[index] === '/' &&\n sourceCode[index + 1] === '*'\n ) {\n index = skipJavaScriptBlockComment(sourceCode, index);\n continue;\n }\n if (options.slashStartsRegExp && char === '/') {\n index = skipJavaScriptRegExpLiteral(sourceCode, index);\n continue;\n }\n if (char === '{') depth += 1;\n if (char === closeChar) {\n depth -= 1;\n if (depth === 0) return { content: sourceCode.slice(startIndex, index), endIndex: index };\n }\n index += 1;\n }\n\n return { content: sourceCode.slice(startIndex), endIndex: sourceCode.length };\n}\n\nfunction skipQuotedSpan(sourceCode: string, startIndex: number): number {\n const quote = getStringQuote(sourceCode, startIndex);\n let index = startIndex + quote.length;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode.startsWith(quote, index)) return index + quote.length;\n index += 1;\n }\n\n return index;\n}\n\nfunction skipJavaScriptRegExpLiteral(sourceCode: string, startIndex: number): number {\n let index = startIndex + 1;\n let inCharacterClass = false;\n\n while (index < sourceCode.length) {\n if (sourceCode[index] === '\\\\') {\n index += 2;\n continue;\n }\n if (sourceCode[index] === '[') inCharacterClass = true;\n if (sourceCode[index] === ']') inCharacterClass = false;\n if (sourceCode[index] === '/' && !inCharacterClass) {\n index += 1;\n while (/[A-Za-z]/.test(sourceCode[index] ?? '')) index += 1;\n return index;\n }\n if (sourceCode[index] === '\\n') return startIndex + 1;\n index += 1;\n }\n\n return startIndex + 1;\n}\n\nfunction skipJavaScriptSingleLineComment(sourceCode: string, startIndex: number): number {\n const newLineIndex = sourceCode.indexOf('\\n', startIndex + 2);\n return newLineIndex === -1 ? sourceCode.length : newLineIndex;\n}\n\nfunction skipJavaScriptBlockComment(sourceCode: string, startIndex: number): number {\n const endIndex = sourceCode.indexOf('*/', startIndex + 2);\n return endIndex === -1 ? sourceCode.length : endIndex + 2;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./temporaryProblemDirCopy.cjs");let n=require("node:child_process");n=e.__toESM(n,1);const r=/* @__PURE__ */ new Set;let i=!1;function a(e,t){return o(),new Promise(i=>{let a=n.default.spawn(process.execPath,e,{cwd:t.cwd,detached:process.platform!==`win32`,env:t.tempRoot?{...t.env,TMPDIR:t.tempRoot}:t.env,stdio:[`ignore`,`pipe`,`pipe`]}),o=a.pid===void 0?void 0:{pid:a.pid,tempRoot:t.tempRoot};o&&r.add(o);let c=[],l=[],u=0,d,f=!1,p=!1,m=e=>{if(d!==void 0)return!1;d=e,a.stdout?.destroy(),a.stderr?.destroy();try{a.pid===void 0?a.kill(`SIGKILL`):s(a.pid)}catch{a.kill(`SIGKILL`)}return!0},h=setTimeout(()=>{f=m(`timed out after ${t.timeoutMs/1e3} seconds`)},t.timeoutMs),g=(e,n)=>{
|
|
1
|
+
"use strict";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./temporaryProblemDirCopy.cjs");let n=require("node:child_process");n=e.__toESM(n,1);const r=/* @__PURE__ */ new Set;let i=!1;function a(e,t){return o(),new Promise(i=>{let a=n.default.spawn(process.execPath,e,{cwd:t.cwd,detached:process.platform!==`win32`,env:t.tempRoot?{...t.env,TMPDIR:t.tempRoot}:t.env,stdio:[`ignore`,`pipe`,`pipe`]}),o=a.pid===void 0?void 0:{pid:a.pid,tempRoot:t.tempRoot};o&&r.add(o);let c=[],l=[],u=0,d,f=!1,p=!1,m=e=>{if(d!==void 0)return!1;d=e,a.stdout?.destroy(),a.stderr?.destroy();try{a.pid===void 0?a.kill(`SIGKILL`):s(a.pid)}catch{a.kill(`SIGKILL`)}return!0},h=setTimeout(()=>{f=m(`timed out after ${t.timeoutMs/1e3} seconds`)},t.timeoutMs),g=(e,n)=>{u+=e.byteLength,u>t.maxOutputBytes?m(`the harness printed more than ${t.maxOutputBytes/1024/1024} MB of output`):n.push(e)};a.stdout?.on(`data`,e=>g(e,c)),a.stderr?.on(`data`,e=>g(e,l));let _=(e,t,n)=>{p||(p=!0,clearTimeout(h),o&&r.delete(o),i({stdout:Buffer.concat(c).toString(`utf8`),stderr:Buffer.concat(l).toString(`utf8`),exitCode:e,signal:t,failureReason:d??(n?`failed to run the harness: ${n.message}`:void 0),timedOut:f}))};a.on(`error`,e=>_(void 0,void 0,e)),a.on(`close`,(e,t)=>_(e??void 0,t??void 0))})}function o(){if(!i){i=!0;for(let e of[`SIGINT`,`SIGTERM`])process.once(e,()=>{for(let e of r){try{s(e.pid)}catch{}e.tempRoot&&t.forciblyRemoveDirectorySync(e.tempRoot)}process.kill(process.pid,e)})}}function s(e){process.platform===`win32`?n.default.spawnSync(`taskkill`,[`/PID`,String(e),`/T`,`/F`],{stdio:`ignore`}):process.kill(-e,`SIGKILL`)}exports.runHarnessProcess=a;
|
|
2
2
|
//# sourceMappingURL=runHarnessProcess.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runHarnessProcess.cjs","names":["child_process","require_runtime","require_temporaryProblemDirCopy","node_child_process","liveHarnessRuns","signalHandlersInstalled","runHarnessProcess","installSignalHandlers","child","commandArgs","options","liveRun","stdoutChunks","stderrChunks","totalOutputBytes","failureReason","timedOut","settled","killProcessGroup","reason","killHarnessTree","timeoutId","appendOutput","chunk","chunks","settle","resolve","spawnError","error","exitCode","signal","pid"],"sources":["../../src/helpers/runHarnessProcess.ts"],"sourcesContent":["import child_process from 'node:child_process';\n\nimport { forciblyRemoveDirectorySync } from './temporaryProblemDirCopy.js';\n\nexport interface HarnessProcessOptions {\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs: number;\n maxOutputBytes: number;\n /** A temporary copy to remove when the check itself is interrupted by a signal. */\n tempRoot?: string;\n}\n\nexport interface HarnessProcessResult {\n stdout: string;\n stderr: string;\n exitCode: number | undefined;\n /** The signal that terminated the harness when it did not exit on its own. */\n signal: NodeJS.Signals | undefined;\n /** Set when the run was killed by this helper (timeout or output cap) or could not be spawned. */\n failureReason: string | undefined;\n timedOut: boolean;\n}\n\ninterface LiveHarnessRun {\n pid: number;\n tempRoot: string | undefined;\n}\n\n// Detached harness groups no longer receive the terminal's SIGINT, so an interrupted run must\n// tear them down (and remove their temp copies) itself before exiting. The handlers stay installed\n// for the process lifetime: once the last run has settled they only iterate an empty set and re-raise\n// the signal, so removing them would change nothing observable.\nconst liveHarnessRuns = new Set<LiveHarnessRun>();\nlet signalHandlersInstalled = false;\n\n/**\n * Run a judge harness with `process.execPath` in its own process group, killing the whole group on\n * timeout or when the output cap is exceeded. The submissions a harness judges through\n * `spawnWithLimits` run in sessions of their own, out of that kill's reach; each wrapper's process\n * group is bounded by its own GNU `timeout` instead, while a descendant that moved to yet another\n * session may survive without stalling the harness.\n */\nexport function runHarnessProcess(\n commandArgs: readonly string[],\n options: HarnessProcessOptions\n): Promise<HarnessProcessResult> {\n installSignalHandlers();\n return new Promise((resolve) => {\n // process.execPath keeps the harness on the same bun executable regardless of PATH.\n const child = child_process.spawn(process.execPath, commandArgs, {\n cwd: options.cwd,\n detached: process.platform !== 'win32',\n // The parent removes this root even when SIGKILL prevents child cleanup.\n env: options.tempRoot ? { ...options.env, TMPDIR: options.tempRoot } : options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const liveRun: LiveHarnessRun | undefined =\n child.pid === undefined ? undefined : { pid: child.pid, tempRoot: options.tempRoot };\n if (liveRun) liveHarnessRuns.add(liveRun);\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let totalOutputBytes = 0;\n let failureReason: string | undefined;\n let timedOut = false;\n let settled = false;\n\n // Returns false when an earlier kill already recorded its reason, so callers can tell whether theirs won.\n const killProcessGroup = (reason: string): boolean => {\n if (failureReason !== undefined) return false;\n failureReason = reason;\n // Stop buffering immediately: OS pipe buffers can keep emitting data after the kill, and\n // destroyed streams also let `close` fire even if a stray grandchild inherited the pipes.\n child.stdout?.destroy();\n child.stderr?.destroy();\n try {\n if (child.pid === undefined) {\n child.kill('SIGKILL');\n } else {\n killHarnessTree(child.pid);\n }\n } catch {\n child.kill('SIGKILL');\n }\n return true;\n };\n const timeoutId = setTimeout(() => {\n timedOut = killProcessGroup(`timed out after ${options.timeoutMs / 1000} seconds`);\n }, options.timeoutMs);\n\n const appendOutput = (chunk: Buffer, chunks: Buffer[]): void => {\n totalOutputBytes += chunk.byteLength;\n if (totalOutputBytes > options.maxOutputBytes) {\n killProcessGroup(`the harness printed more than ${options.maxOutputBytes / 1024 / 1024} MB of output`);\n return;\n }\n chunks.push(chunk);\n };\n child.stdout?.on('data', (chunk: Buffer) => appendOutput(chunk, stdoutChunks));\n child.stderr?.on('data', (chunk: Buffer) => appendOutput(chunk, stderrChunks));\n\n const settle = (exitCode: number | undefined, signal: NodeJS.Signals | undefined, spawnError?: Error): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n if (liveRun) liveHarnessRuns.delete(liveRun);\n resolve({\n stdout: Buffer.concat(stdoutChunks).toString('utf8'),\n stderr: Buffer.concat(stderrChunks).toString('utf8'),\n exitCode,\n signal,\n failureReason: failureReason ?? (spawnError ? `failed to run the harness: ${spawnError.message}` : undefined),\n timedOut,\n });\n };\n child.on('error', (error) => settle(undefined, undefined, error));\n child.on('close', (exitCode, signal) => settle(exitCode ?? undefined, signal ?? undefined));\n });\n}\n\nfunction installSignalHandlers(): void {\n if (signalHandlersInstalled) return;\n signalHandlersInstalled = true;\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, () => {\n for (const liveRun of liveHarnessRuns) {\n try {\n killHarnessTree(liveRun.pid);\n } catch {\n // The tree already exited.\n }\n // Best-effort cleanup while exiting; judged code may have left permission-locked entries.\n if (liveRun.tempRoot) forciblyRemoveDirectorySync(liveRun.tempRoot);\n }\n process.kill(process.pid, signal);\n });\n }\n}\n\n// On Windows the harness is not detached (process groups are unavailable), so killing only the\n// direct child would leave submission grandchildren running; taskkill terminates the whole tree.\nfunction killHarnessTree(pid: number): void {\n if (process.platform === 'win32') {\n child_process.spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });\n } else {\n process.kill(-pid, 'SIGKILL');\n }\n}\n"],"mappings":"kKAiCA,MAAMI,kBAAkB,IAAI,IAC5B,IAAIC,EAA0B,GAS9B,SAAgBC,EACd,EACA,EAC+B,CAE/B,OADAC,EAAsB,EACf,IAAI,QAAS,GAAY,CAE9B,IAAMC,EAAQR,EAAAA,QAAc,MAAM,QAAQ,SAAUS,EAAa,CAC/D,IAAKC,EAAQ,IACb,SAAU,QAAQ,WAAa,QAE/B,IAAKA,EAAQ,SAAW,CAAE,GAAGA,EAAQ,IAAK,OAAQA,EAAQ,QAAS,EAAIA,EAAQ,IAC/E,MAAO,CAAC,SAAU,OAAQ,MAAM,CAClC,CAAC,EACKC,EACJH,EAAM,MAAQ,IAAA,GAAY,IAAA,GAAY,CAAE,IAAKA,EAAM,IAAK,SAAUE,EAAQ,QAAS,EACjFC,GAASP,EAAgB,IAAIO,CAAO,EACxC,IAAMC,EAAyB,CAAC,EAC1BC,EAAyB,CAAC,EAC5BC,EAAmB,EACnBC,EACAC,EAAW,GACXC,EAAU,GAGRC,EAAoB,GAA4B,CACpD,GAAIH,IAAkB,IAAA,GAAW,MAAO,GACxC,EAAgBI,EAGhBX,EAAM,QAAQ,QAAQ,EACtBA,EAAM,QAAQ,QAAQ,EACtB,GAAI,CACEA,EAAM,MAAQ,IAAA,GAChBA,EAAM,KAAK,SAAS,EAEpBY,EAAgBZ,EAAM,GAAG,CAE7B,MAAQ,CACNA,EAAM,KAAK,SAAS,CACtB,CACA,MAAO,EACT,EACMa,EAAY,eAAiB,CACjC,EAAWH,EAAiB,mBAAmBR,EAAQ,UAAY,IAAK,SAAS,CACnF,EAAGA,EAAQ,SAAS,EAEdY,GAAgB,EAAe,IAA2B,
|
|
1
|
+
{"version":3,"file":"runHarnessProcess.cjs","names":["child_process","require_runtime","require_temporaryProblemDirCopy","node_child_process","liveHarnessRuns","signalHandlersInstalled","runHarnessProcess","installSignalHandlers","child","commandArgs","options","liveRun","stdoutChunks","stderrChunks","totalOutputBytes","failureReason","timedOut","settled","killProcessGroup","reason","killHarnessTree","timeoutId","appendOutput","chunk","chunks","settle","resolve","spawnError","error","exitCode","signal","pid"],"sources":["../../src/helpers/runHarnessProcess.ts"],"sourcesContent":["import child_process from 'node:child_process';\n\nimport { forciblyRemoveDirectorySync } from './temporaryProblemDirCopy.js';\n\nexport interface HarnessProcessOptions {\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs: number;\n maxOutputBytes: number;\n /** A temporary copy to remove when the check itself is interrupted by a signal. */\n tempRoot?: string;\n}\n\nexport interface HarnessProcessResult {\n stdout: string;\n stderr: string;\n exitCode: number | undefined;\n /** The signal that terminated the harness when it did not exit on its own. */\n signal: NodeJS.Signals | undefined;\n /** Set when the run was killed by this helper (timeout or output cap) or could not be spawned. */\n failureReason: string | undefined;\n timedOut: boolean;\n}\n\ninterface LiveHarnessRun {\n pid: number;\n tempRoot: string | undefined;\n}\n\n// Detached harness groups no longer receive the terminal's SIGINT, so an interrupted run must\n// tear them down (and remove their temp copies) itself before exiting. The handlers stay installed\n// for the process lifetime: once the last run has settled they only iterate an empty set and re-raise\n// the signal, so removing them would change nothing observable.\nconst liveHarnessRuns = new Set<LiveHarnessRun>();\nlet signalHandlersInstalled = false;\n\n/**\n * Run a judge harness with `process.execPath` in its own process group, killing the whole group on\n * timeout or when the output cap is exceeded. The submissions a harness judges through\n * `spawnWithLimits` run in sessions of their own, out of that kill's reach; each wrapper's process\n * group is bounded by its own GNU `timeout` instead, while a descendant that moved to yet another\n * session may survive without stalling the harness.\n */\nexport function runHarnessProcess(\n commandArgs: readonly string[],\n options: HarnessProcessOptions\n): Promise<HarnessProcessResult> {\n installSignalHandlers();\n return new Promise((resolve) => {\n // process.execPath keeps the harness on the same bun executable regardless of PATH.\n const child = child_process.spawn(process.execPath, commandArgs, {\n cwd: options.cwd,\n detached: process.platform !== 'win32',\n // The parent removes this root even when SIGKILL prevents child cleanup.\n env: options.tempRoot ? { ...options.env, TMPDIR: options.tempRoot } : options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const liveRun: LiveHarnessRun | undefined =\n child.pid === undefined ? undefined : { pid: child.pid, tempRoot: options.tempRoot };\n if (liveRun) liveHarnessRuns.add(liveRun);\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let totalOutputBytes = 0;\n let failureReason: string | undefined;\n let timedOut = false;\n let settled = false;\n\n // Returns false when an earlier kill already recorded its reason, so callers can tell whether theirs won.\n const killProcessGroup = (reason: string): boolean => {\n if (failureReason !== undefined) return false;\n failureReason = reason;\n // Stop buffering immediately: OS pipe buffers can keep emitting data after the kill, and\n // destroyed streams also let `close` fire even if a stray grandchild inherited the pipes.\n child.stdout?.destroy();\n child.stderr?.destroy();\n try {\n if (child.pid === undefined) {\n child.kill('SIGKILL');\n } else {\n killHarnessTree(child.pid);\n }\n } catch {\n child.kill('SIGKILL');\n }\n return true;\n };\n const timeoutId = setTimeout(() => {\n timedOut = killProcessGroup(`timed out after ${options.timeoutMs / 1000} seconds`);\n }, options.timeoutMs);\n\n const appendOutput = (chunk: Buffer, chunks: Buffer[]): void => {\n totalOutputBytes += chunk.byteLength;\n if (totalOutputBytes > options.maxOutputBytes) {\n killProcessGroup(`the harness printed more than ${options.maxOutputBytes / 1024 / 1024} MB of output`);\n return;\n }\n chunks.push(chunk);\n };\n child.stdout?.on('data', (chunk: Buffer) => appendOutput(chunk, stdoutChunks));\n child.stderr?.on('data', (chunk: Buffer) => appendOutput(chunk, stderrChunks));\n\n const settle = (exitCode: number | undefined, signal: NodeJS.Signals | undefined, spawnError?: Error): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n if (liveRun) liveHarnessRuns.delete(liveRun);\n resolve({\n stdout: Buffer.concat(stdoutChunks).toString('utf8'),\n stderr: Buffer.concat(stderrChunks).toString('utf8'),\n exitCode,\n signal,\n failureReason: failureReason ?? (spawnError ? `failed to run the harness: ${spawnError.message}` : undefined),\n timedOut,\n });\n };\n child.on('error', (error) => settle(undefined, undefined, error));\n child.on('close', (exitCode, signal) => settle(exitCode ?? undefined, signal ?? undefined));\n });\n}\n\nfunction installSignalHandlers(): void {\n if (signalHandlersInstalled) return;\n signalHandlersInstalled = true;\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, () => {\n for (const liveRun of liveHarnessRuns) {\n try {\n killHarnessTree(liveRun.pid);\n } catch {\n // The tree already exited.\n }\n // Best-effort cleanup while exiting; judged code may have left permission-locked entries.\n if (liveRun.tempRoot) forciblyRemoveDirectorySync(liveRun.tempRoot);\n }\n process.kill(process.pid, signal);\n });\n }\n}\n\n// On Windows the harness is not detached (process groups are unavailable), so killing only the\n// direct child would leave submission grandchildren running; taskkill terminates the whole tree.\nfunction killHarnessTree(pid: number): void {\n if (process.platform === 'win32') {\n child_process.spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });\n } else {\n process.kill(-pid, 'SIGKILL');\n }\n}\n"],"mappings":"kKAiCA,MAAMI,kBAAkB,IAAI,IAC5B,IAAIC,EAA0B,GAS9B,SAAgBC,EACd,EACA,EAC+B,CAE/B,OADAC,EAAsB,EACf,IAAI,QAAS,GAAY,CAE9B,IAAMC,EAAQR,EAAAA,QAAc,MAAM,QAAQ,SAAUS,EAAa,CAC/D,IAAKC,EAAQ,IACb,SAAU,QAAQ,WAAa,QAE/B,IAAKA,EAAQ,SAAW,CAAE,GAAGA,EAAQ,IAAK,OAAQA,EAAQ,QAAS,EAAIA,EAAQ,IAC/E,MAAO,CAAC,SAAU,OAAQ,MAAM,CAClC,CAAC,EACKC,EACJH,EAAM,MAAQ,IAAA,GAAY,IAAA,GAAY,CAAE,IAAKA,EAAM,IAAK,SAAUE,EAAQ,QAAS,EACjFC,GAASP,EAAgB,IAAIO,CAAO,EACxC,IAAMC,EAAyB,CAAC,EAC1BC,EAAyB,CAAC,EAC5BC,EAAmB,EACnBC,EACAC,EAAW,GACXC,EAAU,GAGRC,EAAoB,GAA4B,CACpD,GAAIH,IAAkB,IAAA,GAAW,MAAO,GACxC,EAAgBI,EAGhBX,EAAM,QAAQ,QAAQ,EACtBA,EAAM,QAAQ,QAAQ,EACtB,GAAI,CACEA,EAAM,MAAQ,IAAA,GAChBA,EAAM,KAAK,SAAS,EAEpBY,EAAgBZ,EAAM,GAAG,CAE7B,MAAQ,CACNA,EAAM,KAAK,SAAS,CACtB,CACA,MAAO,EACT,EACMa,EAAY,eAAiB,CACjC,EAAWH,EAAiB,mBAAmBR,EAAQ,UAAY,IAAK,SAAS,CACnF,EAAGA,EAAQ,SAAS,EAEdY,GAAgB,EAAe,IAA2B,CAC9D,GAAoBC,EAAM,WACtBT,EAAmBJ,EAAQ,eAC7BQ,EAAiB,iCAAiCR,EAAQ,eAAiB,KAAO,KAAK,cAAc,EAGvGc,EAAO,KAAKD,CAAK,CACnB,EACAf,EAAM,QAAQ,GAAG,OAAS,GAAkBc,EAAaC,EAAOX,CAAY,CAAC,EAC7EJ,EAAM,QAAQ,GAAG,OAAS,GAAkBc,EAAaC,EAAOV,CAAY,CAAC,EAE7E,IAAMY,GAAU,EAA8B,EAAoC,IAA6B,CACzGR,IACJ,EAAU,GACV,aAAaI,CAAS,EAClBV,GAASP,EAAgB,OAAOO,CAAO,EAC3Ce,EAAQ,CACN,OAAQ,OAAO,OAAOd,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,OAAQ,OAAO,OAAOC,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,WACA,SACA,cAAeE,IAAkBY,EAAa,8BAA8BA,EAAW,UAAY,IAAA,IACnG,UACF,CAAC,EACH,EACAnB,EAAM,GAAG,QAAU,GAAUiB,EAAO,IAAA,GAAW,IAAA,GAAWG,CAAK,CAAC,EAChEpB,EAAM,GAAG,SAAU,EAAU,IAAWiB,EAAOI,GAAY,IAAA,GAAWC,GAAU,IAAA,EAAS,CAAC,CAC5F,CAAC,CACH,CAEA,SAASvB,GAA8B,CACrC,GAAI,GACJ,GAA0B,GAC1B,IAAK,IAAMuB,IAAU,CAAC,SAAU,SAAS,EACvC,QAAQ,KAAKA,MAAc,CACzB,IAAK,IAAMnB,KAAWP,EAAiB,CACrC,GAAI,CACFgB,EAAgBT,EAAQ,GAAG,CAC7B,MAAQ,CAER,CAEIA,EAAQ,UAAUT,EAAA,4BAA4BS,EAAQ,QAAQ,CACpE,CACA,QAAQ,KAAK,QAAQ,IAAKmB,CAAM,CAClC,CAAC,CAbuB,CAe5B,CAIA,SAASV,EAAgB,EAAmB,CACtC,QAAQ,WAAa,QACvBjB,EAAA,QAAc,UAAU,WAAY,CAAC,OAAQ,OAAO4B,CAAG,EAAG,KAAM,IAAI,EAAG,CAAE,MAAO,QAAS,CAAC,EAE1F,QAAQ,KAAK,CAACA,EAAK,SAAS,CAEhC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{forciblyRemoveDirectorySync as e}from"./temporaryProblemDirCopy.js";import t from"node:child_process";const n=/* @__PURE__ */ new Set;let r=!1;function i(e,r){return a(),new Promise(i=>{let a=t.spawn(process.execPath,e,{cwd:r.cwd,detached:process.platform!==`win32`,env:r.tempRoot?{...r.env,TMPDIR:r.tempRoot}:r.env,stdio:[`ignore`,`pipe`,`pipe`]}),s=a.pid===void 0?void 0:{pid:a.pid,tempRoot:r.tempRoot};s&&n.add(s);let c=[],l=[],u=0,d,f=!1,p=!1,m=e=>{if(d!==void 0)return!1;d=e,a.stdout?.destroy(),a.stderr?.destroy();try{a.pid===void 0?a.kill(`SIGKILL`):o(a.pid)}catch{a.kill(`SIGKILL`)}return!0},h=setTimeout(()=>{f=m(`timed out after ${r.timeoutMs/1e3} seconds`)},r.timeoutMs),g=(e,t)=>{
|
|
1
|
+
import{forciblyRemoveDirectorySync as e}from"./temporaryProblemDirCopy.js";import t from"node:child_process";const n=/* @__PURE__ */ new Set;let r=!1;function i(e,r){return a(),new Promise(i=>{let a=t.spawn(process.execPath,e,{cwd:r.cwd,detached:process.platform!==`win32`,env:r.tempRoot?{...r.env,TMPDIR:r.tempRoot}:r.env,stdio:[`ignore`,`pipe`,`pipe`]}),s=a.pid===void 0?void 0:{pid:a.pid,tempRoot:r.tempRoot};s&&n.add(s);let c=[],l=[],u=0,d,f=!1,p=!1,m=e=>{if(d!==void 0)return!1;d=e,a.stdout?.destroy(),a.stderr?.destroy();try{a.pid===void 0?a.kill(`SIGKILL`):o(a.pid)}catch{a.kill(`SIGKILL`)}return!0},h=setTimeout(()=>{f=m(`timed out after ${r.timeoutMs/1e3} seconds`)},r.timeoutMs),g=(e,t)=>{u+=e.byteLength,u>r.maxOutputBytes?m(`the harness printed more than ${r.maxOutputBytes/1024/1024} MB of output`):t.push(e)};a.stdout?.on(`data`,e=>g(e,c)),a.stderr?.on(`data`,e=>g(e,l));let _=(e,t,r)=>{p||(p=!0,clearTimeout(h),s&&n.delete(s),i({stdout:Buffer.concat(c).toString(`utf8`),stderr:Buffer.concat(l).toString(`utf8`),exitCode:e,signal:t,failureReason:d??(r?`failed to run the harness: ${r.message}`:void 0),timedOut:f}))};a.on(`error`,e=>_(void 0,void 0,e)),a.on(`close`,(e,t)=>_(e??void 0,t??void 0))})}function a(){if(!r){r=!0;for(let t of[`SIGINT`,`SIGTERM`])process.once(t,()=>{for(let t of n){try{o(t.pid)}catch{}t.tempRoot&&e(t.tempRoot)}process.kill(process.pid,t)})}}function o(e){process.platform===`win32`?t.spawnSync(`taskkill`,[`/PID`,String(e),`/T`,`/F`],{stdio:`ignore`}):process.kill(-e,`SIGKILL`)}export{i as runHarnessProcess};
|
|
2
2
|
//# sourceMappingURL=runHarnessProcess.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runHarnessProcess.js","names":["child_process","childProcess","liveHarnessRuns","signalHandlersInstalled","runHarnessProcess","installSignalHandlers","child","commandArgs","options","liveRun","stdoutChunks","stderrChunks","totalOutputBytes","failureReason","timedOut","settled","killProcessGroup","reason","killHarnessTree","timeoutId","appendOutput","chunk","chunks","settle","resolve","spawnError","error","exitCode","signal","forciblyRemoveDirectorySync","pid"],"sources":["../../src/helpers/runHarnessProcess.ts"],"sourcesContent":["import child_process from 'node:child_process';\n\nimport { forciblyRemoveDirectorySync } from './temporaryProblemDirCopy.js';\n\nexport interface HarnessProcessOptions {\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs: number;\n maxOutputBytes: number;\n /** A temporary copy to remove when the check itself is interrupted by a signal. */\n tempRoot?: string;\n}\n\nexport interface HarnessProcessResult {\n stdout: string;\n stderr: string;\n exitCode: number | undefined;\n /** The signal that terminated the harness when it did not exit on its own. */\n signal: NodeJS.Signals | undefined;\n /** Set when the run was killed by this helper (timeout or output cap) or could not be spawned. */\n failureReason: string | undefined;\n timedOut: boolean;\n}\n\ninterface LiveHarnessRun {\n pid: number;\n tempRoot: string | undefined;\n}\n\n// Detached harness groups no longer receive the terminal's SIGINT, so an interrupted run must\n// tear them down (and remove their temp copies) itself before exiting. The handlers stay installed\n// for the process lifetime: once the last run has settled they only iterate an empty set and re-raise\n// the signal, so removing them would change nothing observable.\nconst liveHarnessRuns = new Set<LiveHarnessRun>();\nlet signalHandlersInstalled = false;\n\n/**\n * Run a judge harness with `process.execPath` in its own process group, killing the whole group on\n * timeout or when the output cap is exceeded. The submissions a harness judges through\n * `spawnWithLimits` run in sessions of their own, out of that kill's reach; each wrapper's process\n * group is bounded by its own GNU `timeout` instead, while a descendant that moved to yet another\n * session may survive without stalling the harness.\n */\nexport function runHarnessProcess(\n commandArgs: readonly string[],\n options: HarnessProcessOptions\n): Promise<HarnessProcessResult> {\n installSignalHandlers();\n return new Promise((resolve) => {\n // process.execPath keeps the harness on the same bun executable regardless of PATH.\n const child = child_process.spawn(process.execPath, commandArgs, {\n cwd: options.cwd,\n detached: process.platform !== 'win32',\n // The parent removes this root even when SIGKILL prevents child cleanup.\n env: options.tempRoot ? { ...options.env, TMPDIR: options.tempRoot } : options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const liveRun: LiveHarnessRun | undefined =\n child.pid === undefined ? undefined : { pid: child.pid, tempRoot: options.tempRoot };\n if (liveRun) liveHarnessRuns.add(liveRun);\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let totalOutputBytes = 0;\n let failureReason: string | undefined;\n let timedOut = false;\n let settled = false;\n\n // Returns false when an earlier kill already recorded its reason, so callers can tell whether theirs won.\n const killProcessGroup = (reason: string): boolean => {\n if (failureReason !== undefined) return false;\n failureReason = reason;\n // Stop buffering immediately: OS pipe buffers can keep emitting data after the kill, and\n // destroyed streams also let `close` fire even if a stray grandchild inherited the pipes.\n child.stdout?.destroy();\n child.stderr?.destroy();\n try {\n if (child.pid === undefined) {\n child.kill('SIGKILL');\n } else {\n killHarnessTree(child.pid);\n }\n } catch {\n child.kill('SIGKILL');\n }\n return true;\n };\n const timeoutId = setTimeout(() => {\n timedOut = killProcessGroup(`timed out after ${options.timeoutMs / 1000} seconds`);\n }, options.timeoutMs);\n\n const appendOutput = (chunk: Buffer, chunks: Buffer[]): void => {\n totalOutputBytes += chunk.byteLength;\n if (totalOutputBytes > options.maxOutputBytes) {\n killProcessGroup(`the harness printed more than ${options.maxOutputBytes / 1024 / 1024} MB of output`);\n return;\n }\n chunks.push(chunk);\n };\n child.stdout?.on('data', (chunk: Buffer) => appendOutput(chunk, stdoutChunks));\n child.stderr?.on('data', (chunk: Buffer) => appendOutput(chunk, stderrChunks));\n\n const settle = (exitCode: number | undefined, signal: NodeJS.Signals | undefined, spawnError?: Error): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n if (liveRun) liveHarnessRuns.delete(liveRun);\n resolve({\n stdout: Buffer.concat(stdoutChunks).toString('utf8'),\n stderr: Buffer.concat(stderrChunks).toString('utf8'),\n exitCode,\n signal,\n failureReason: failureReason ?? (spawnError ? `failed to run the harness: ${spawnError.message}` : undefined),\n timedOut,\n });\n };\n child.on('error', (error) => settle(undefined, undefined, error));\n child.on('close', (exitCode, signal) => settle(exitCode ?? undefined, signal ?? undefined));\n });\n}\n\nfunction installSignalHandlers(): void {\n if (signalHandlersInstalled) return;\n signalHandlersInstalled = true;\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, () => {\n for (const liveRun of liveHarnessRuns) {\n try {\n killHarnessTree(liveRun.pid);\n } catch {\n // The tree already exited.\n }\n // Best-effort cleanup while exiting; judged code may have left permission-locked entries.\n if (liveRun.tempRoot) forciblyRemoveDirectorySync(liveRun.tempRoot);\n }\n process.kill(process.pid, signal);\n });\n }\n}\n\n// On Windows the harness is not detached (process groups are unavailable), so killing only the\n// direct child would leave submission grandchildren running; taskkill terminates the whole tree.\nfunction killHarnessTree(pid: number): void {\n if (process.platform === 'win32') {\n child_process.spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });\n } else {\n process.kill(-pid, 'SIGKILL');\n }\n}\n"],"mappings":"6GAiCA,MAAME,kBAAkB,IAAI,IAC5B,IAAIC,EAA0B,GAS9B,SAAgBC,EACd,EACA,EAC+B,CAE/B,OADAC,EAAsB,EACf,IAAI,QAAS,GAAY,CAE9B,IAAMC,EAAQN,EAAc,MAAM,QAAQ,SAAUO,EAAa,CAC/D,IAAKC,EAAQ,IACb,SAAU,QAAQ,WAAa,QAE/B,IAAKA,EAAQ,SAAW,CAAE,GAAGA,EAAQ,IAAK,OAAQA,EAAQ,QAAS,EAAIA,EAAQ,IAC/E,MAAO,CAAC,SAAU,OAAQ,MAAM,CAClC,CAAC,EACKC,EACJH,EAAM,MAAQ,IAAA,GAAY,IAAA,GAAY,CAAE,IAAKA,EAAM,IAAK,SAAUE,EAAQ,QAAS,EACjFC,GAASP,EAAgB,IAAIO,CAAO,EACxC,IAAMC,EAAyB,CAAC,EAC1BC,EAAyB,CAAC,EAC5BC,EAAmB,EACnBC,EACAC,EAAW,GACXC,EAAU,GAGRC,EAAoB,GAA4B,CACpD,GAAIH,IAAkB,IAAA,GAAW,MAAO,GACxC,EAAgBI,EAGhBX,EAAM,QAAQ,QAAQ,EACtBA,EAAM,QAAQ,QAAQ,EACtB,GAAI,CACEA,EAAM,MAAQ,IAAA,GAChBA,EAAM,KAAK,SAAS,EAEpBY,EAAgBZ,EAAM,GAAG,CAE7B,MAAQ,CACNA,EAAM,KAAK,SAAS,CACtB,CACA,MAAO,EACT,EACMa,EAAY,eAAiB,CACjC,EAAWH,EAAiB,mBAAmBR,EAAQ,UAAY,IAAK,SAAS,CACnF,EAAGA,EAAQ,SAAS,EAEdY,GAAgB,EAAe,IAA2B,
|
|
1
|
+
{"version":3,"file":"runHarnessProcess.js","names":["child_process","childProcess","liveHarnessRuns","signalHandlersInstalled","runHarnessProcess","installSignalHandlers","child","commandArgs","options","liveRun","stdoutChunks","stderrChunks","totalOutputBytes","failureReason","timedOut","settled","killProcessGroup","reason","killHarnessTree","timeoutId","appendOutput","chunk","chunks","settle","resolve","spawnError","error","exitCode","signal","forciblyRemoveDirectorySync","pid"],"sources":["../../src/helpers/runHarnessProcess.ts"],"sourcesContent":["import child_process from 'node:child_process';\n\nimport { forciblyRemoveDirectorySync } from './temporaryProblemDirCopy.js';\n\nexport interface HarnessProcessOptions {\n cwd: string;\n env: NodeJS.ProcessEnv;\n timeoutMs: number;\n maxOutputBytes: number;\n /** A temporary copy to remove when the check itself is interrupted by a signal. */\n tempRoot?: string;\n}\n\nexport interface HarnessProcessResult {\n stdout: string;\n stderr: string;\n exitCode: number | undefined;\n /** The signal that terminated the harness when it did not exit on its own. */\n signal: NodeJS.Signals | undefined;\n /** Set when the run was killed by this helper (timeout or output cap) or could not be spawned. */\n failureReason: string | undefined;\n timedOut: boolean;\n}\n\ninterface LiveHarnessRun {\n pid: number;\n tempRoot: string | undefined;\n}\n\n// Detached harness groups no longer receive the terminal's SIGINT, so an interrupted run must\n// tear them down (and remove their temp copies) itself before exiting. The handlers stay installed\n// for the process lifetime: once the last run has settled they only iterate an empty set and re-raise\n// the signal, so removing them would change nothing observable.\nconst liveHarnessRuns = new Set<LiveHarnessRun>();\nlet signalHandlersInstalled = false;\n\n/**\n * Run a judge harness with `process.execPath` in its own process group, killing the whole group on\n * timeout or when the output cap is exceeded. The submissions a harness judges through\n * `spawnWithLimits` run in sessions of their own, out of that kill's reach; each wrapper's process\n * group is bounded by its own GNU `timeout` instead, while a descendant that moved to yet another\n * session may survive without stalling the harness.\n */\nexport function runHarnessProcess(\n commandArgs: readonly string[],\n options: HarnessProcessOptions\n): Promise<HarnessProcessResult> {\n installSignalHandlers();\n return new Promise((resolve) => {\n // process.execPath keeps the harness on the same bun executable regardless of PATH.\n const child = child_process.spawn(process.execPath, commandArgs, {\n cwd: options.cwd,\n detached: process.platform !== 'win32',\n // The parent removes this root even when SIGKILL prevents child cleanup.\n env: options.tempRoot ? { ...options.env, TMPDIR: options.tempRoot } : options.env,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const liveRun: LiveHarnessRun | undefined =\n child.pid === undefined ? undefined : { pid: child.pid, tempRoot: options.tempRoot };\n if (liveRun) liveHarnessRuns.add(liveRun);\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n let totalOutputBytes = 0;\n let failureReason: string | undefined;\n let timedOut = false;\n let settled = false;\n\n // Returns false when an earlier kill already recorded its reason, so callers can tell whether theirs won.\n const killProcessGroup = (reason: string): boolean => {\n if (failureReason !== undefined) return false;\n failureReason = reason;\n // Stop buffering immediately: OS pipe buffers can keep emitting data after the kill, and\n // destroyed streams also let `close` fire even if a stray grandchild inherited the pipes.\n child.stdout?.destroy();\n child.stderr?.destroy();\n try {\n if (child.pid === undefined) {\n child.kill('SIGKILL');\n } else {\n killHarnessTree(child.pid);\n }\n } catch {\n child.kill('SIGKILL');\n }\n return true;\n };\n const timeoutId = setTimeout(() => {\n timedOut = killProcessGroup(`timed out after ${options.timeoutMs / 1000} seconds`);\n }, options.timeoutMs);\n\n const appendOutput = (chunk: Buffer, chunks: Buffer[]): void => {\n totalOutputBytes += chunk.byteLength;\n if (totalOutputBytes > options.maxOutputBytes) {\n killProcessGroup(`the harness printed more than ${options.maxOutputBytes / 1024 / 1024} MB of output`);\n return;\n }\n chunks.push(chunk);\n };\n child.stdout?.on('data', (chunk: Buffer) => appendOutput(chunk, stdoutChunks));\n child.stderr?.on('data', (chunk: Buffer) => appendOutput(chunk, stderrChunks));\n\n const settle = (exitCode: number | undefined, signal: NodeJS.Signals | undefined, spawnError?: Error): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n if (liveRun) liveHarnessRuns.delete(liveRun);\n resolve({\n stdout: Buffer.concat(stdoutChunks).toString('utf8'),\n stderr: Buffer.concat(stderrChunks).toString('utf8'),\n exitCode,\n signal,\n failureReason: failureReason ?? (spawnError ? `failed to run the harness: ${spawnError.message}` : undefined),\n timedOut,\n });\n };\n child.on('error', (error) => settle(undefined, undefined, error));\n child.on('close', (exitCode, signal) => settle(exitCode ?? undefined, signal ?? undefined));\n });\n}\n\nfunction installSignalHandlers(): void {\n if (signalHandlersInstalled) return;\n signalHandlersInstalled = true;\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, () => {\n for (const liveRun of liveHarnessRuns) {\n try {\n killHarnessTree(liveRun.pid);\n } catch {\n // The tree already exited.\n }\n // Best-effort cleanup while exiting; judged code may have left permission-locked entries.\n if (liveRun.tempRoot) forciblyRemoveDirectorySync(liveRun.tempRoot);\n }\n process.kill(process.pid, signal);\n });\n }\n}\n\n// On Windows the harness is not detached (process groups are unavailable), so killing only the\n// direct child would leave submission grandchildren running; taskkill terminates the whole tree.\nfunction killHarnessTree(pid: number): void {\n if (process.platform === 'win32') {\n child_process.spawnSync('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });\n } else {\n process.kill(-pid, 'SIGKILL');\n }\n}\n"],"mappings":"6GAiCA,MAAME,kBAAkB,IAAI,IAC5B,IAAIC,EAA0B,GAS9B,SAAgBC,EACd,EACA,EAC+B,CAE/B,OADAC,EAAsB,EACf,IAAI,QAAS,GAAY,CAE9B,IAAMC,EAAQN,EAAc,MAAM,QAAQ,SAAUO,EAAa,CAC/D,IAAKC,EAAQ,IACb,SAAU,QAAQ,WAAa,QAE/B,IAAKA,EAAQ,SAAW,CAAE,GAAGA,EAAQ,IAAK,OAAQA,EAAQ,QAAS,EAAIA,EAAQ,IAC/E,MAAO,CAAC,SAAU,OAAQ,MAAM,CAClC,CAAC,EACKC,EACJH,EAAM,MAAQ,IAAA,GAAY,IAAA,GAAY,CAAE,IAAKA,EAAM,IAAK,SAAUE,EAAQ,QAAS,EACjFC,GAASP,EAAgB,IAAIO,CAAO,EACxC,IAAMC,EAAyB,CAAC,EAC1BC,EAAyB,CAAC,EAC5BC,EAAmB,EACnBC,EACAC,EAAW,GACXC,EAAU,GAGRC,EAAoB,GAA4B,CACpD,GAAIH,IAAkB,IAAA,GAAW,MAAO,GACxC,EAAgBI,EAGhBX,EAAM,QAAQ,QAAQ,EACtBA,EAAM,QAAQ,QAAQ,EACtB,GAAI,CACEA,EAAM,MAAQ,IAAA,GAChBA,EAAM,KAAK,SAAS,EAEpBY,EAAgBZ,EAAM,GAAG,CAE7B,MAAQ,CACNA,EAAM,KAAK,SAAS,CACtB,CACA,MAAO,EACT,EACMa,EAAY,eAAiB,CACjC,EAAWH,EAAiB,mBAAmBR,EAAQ,UAAY,IAAK,SAAS,CACnF,EAAGA,EAAQ,SAAS,EAEdY,GAAgB,EAAe,IAA2B,CAC9D,GAAoBC,EAAM,WACtBT,EAAmBJ,EAAQ,eAC7BQ,EAAiB,iCAAiCR,EAAQ,eAAiB,KAAO,KAAK,cAAc,EAGvGc,EAAO,KAAKD,CAAK,CACnB,EACAf,EAAM,QAAQ,GAAG,OAAS,GAAkBc,EAAaC,EAAOX,CAAY,CAAC,EAC7EJ,EAAM,QAAQ,GAAG,OAAS,GAAkBc,EAAaC,EAAOV,CAAY,CAAC,EAE7E,IAAMY,GAAU,EAA8B,EAAoC,IAA6B,CACzGR,IACJ,EAAU,GACV,aAAaI,CAAS,EAClBV,GAASP,EAAgB,OAAOO,CAAO,EAC3Ce,EAAQ,CACN,OAAQ,OAAO,OAAOd,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,OAAQ,OAAO,OAAOC,CAAY,CAAC,CAAC,SAAS,MAAM,EACnD,WACA,SACA,cAAeE,IAAkBY,EAAa,8BAA8BA,EAAW,UAAY,IAAA,IACnG,UACF,CAAC,EACH,EACAnB,EAAM,GAAG,QAAU,GAAUiB,EAAO,IAAA,GAAW,IAAA,GAAWG,CAAK,CAAC,EAChEpB,EAAM,GAAG,SAAU,EAAU,IAAWiB,EAAOI,GAAY,IAAA,GAAWC,GAAU,IAAA,EAAS,CAAC,CAC5F,CAAC,CACH,CAEA,SAASvB,GAA8B,CACrC,GAAI,GACJ,GAA0B,GAC1B,IAAK,IAAMuB,IAAU,CAAC,SAAU,SAAS,EACvC,QAAQ,KAAKA,MAAc,CACzB,IAAK,IAAMnB,KAAWP,EAAiB,CACrC,GAAI,CACFgB,EAAgBT,EAAQ,GAAG,CAC7B,MAAQ,CAER,CAEIA,EAAQ,UAAUoB,EAA4BpB,EAAQ,QAAQ,CACpE,CACA,QAAQ,KAAK,QAAQ,IAAKmB,CAAM,CAClC,CAAC,CAbuB,CAe5B,CAIA,SAASV,EAAgB,EAAmB,CACtC,QAAQ,WAAa,QACvBjB,EAAc,UAAU,WAAY,CAAC,OAAQ,OAAO6B,CAAG,EAAG,KAAM,IAAI,EAAG,CAAE,MAAO,QAAS,CAAC,EAE1F,QAAQ,KAAK,CAACA,EAAK,SAAS,CAEhC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("node:fs/promises");n=e.__toESM(n,1);let r=require("node:os");r=e.__toESM(r,1);let i=require("@willbooster/shared-lib"),a=require("node:child_process");a=e.__toESM(a,1);const o=1e3,s=_(),c=s!==void 0,l=/* @__PURE__ */ new Set;process.once(`exit`,()=>{for(let e of l)f(e,`SIGKILL`)});async function u(e,t){let n=s===void 0?void 0:await p(`exercode-time-`);try{let r=process.platform!==`win32`,i=r?[`timeout`,`--foreground`,`-k`,`1`,String(Math.max(t.timeLimitSeconds,.001)),...e]:e,c=s===void 0?i:[...s,`--output=/dev/fd/3`,...i],u=r?t.timeLimitSeconds*1e3+2*o:t.timeLimitSeconds*1e3,p=performance.now(),g=a.default.spawn(c[0],c.slice(1),{cwd:t.cwd,detached:r,env:t.env,stdio:n===void 0?[`pipe`,`pipe`,`pipe`]:[`pipe`,`pipe`,`pipe`,n.fd]});l.add(g);let _=[],v=[],y=0,b=!1,x=!1,S=0,C,w=(e,n)=>{if(y>=t.outputLimitBytes){n.byteLength>0&&(x=!0,f(g,`SIGKILL`));return}let r=t.outputLimitBytes-y,i=n.byteLength>r?n.subarray(0,r):n;e.push(i),y+=i.byteLength,n.byteLength>r&&(x=!0,f(g,`SIGKILL`))};g.stdout.on(`data`,e=>w(_,e)),g.stderr.on(`data`,e=>w(v,e));let T=setTimeout(()=>{b=!0,f(g,`SIGTERM`)},u),E=setTimeout(()=>{b&&f(g,`SIGKILL`)},u+o);E.unref();let{status:D,signal:O}=await new Promise((e,n)=>{let r=!1,i,a,s=(t,o)=>{
|
|
1
|
+
"use strict";const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);let n=require("node:fs/promises");n=e.__toESM(n,1);let r=require("node:os");r=e.__toESM(r,1);let i=require("@willbooster/shared-lib"),a=require("node:child_process");a=e.__toESM(a,1);const o=1e3,s=_(),c=s!==void 0,l=/* @__PURE__ */ new Set;process.once(`exit`,()=>{for(let e of l)f(e,`SIGKILL`)});async function u(e,t){let n=s===void 0?void 0:await p(`exercode-time-`);try{let r=process.platform!==`win32`,i=r?[`timeout`,`--foreground`,`-k`,`1`,String(Math.max(t.timeLimitSeconds,.001)),...e]:e,c=s===void 0?i:[...s,`--output=/dev/fd/3`,...i],u=r?t.timeLimitSeconds*1e3+2*o:t.timeLimitSeconds*1e3,p=performance.now(),g=a.default.spawn(c[0],c.slice(1),{cwd:t.cwd,detached:r,env:t.env,stdio:n===void 0?[`pipe`,`pipe`,`pipe`]:[`pipe`,`pipe`,`pipe`,n.fd]});l.add(g);let _=[],v=[],y=0,b=!1,x=!1,S=0,C,w=(e,n)=>{if(y>=t.outputLimitBytes){n.byteLength>0&&(x=!0,f(g,`SIGKILL`));return}let r=t.outputLimitBytes-y,i=n.byteLength>r?n.subarray(0,r):n;e.push(i),y+=i.byteLength,n.byteLength>r&&(x=!0,f(g,`SIGKILL`))};g.stdout.on(`data`,e=>w(_,e)),g.stderr.on(`data`,e=>w(v,e));let T=setTimeout(()=>{b=!0,f(g,`SIGTERM`)},u),E=setTimeout(()=>{b&&f(g,`SIGKILL`)},u+o);E.unref();let{status:D,signal:O}=await new Promise((e,n)=>{let r=!1,i,a,s=(t,o)=>{r||(r=!0,clearTimeout(a),i?n(i):e({status:t??void 0,signal:o??void 0}))},c=e=>{r||(g.pid===void 0?(C=e.message,s(void 0,void 0)):(i=e,f(g,`SIGKILL`)))};g.on(`error`,c),g.stdin.on(`error`,e=>{e.code!==`EPIPE`&&c(e)}),g.on(`exit`,(e,t)=>{clearTimeout(T),clearTimeout(E),S=(performance.now()-p)/1e3,f(g,`SIGKILL`),a=setTimeout(()=>{g.stdout.destroy(),g.stderr.destroy(),s(e,t)},o)}),g.on(`close`,s),g.stdin.end(t.stdin)}).finally(()=>{clearTimeout(T),clearTimeout(E),l.delete(g)}),k=n===void 0?void 0:h(await m(n,4096));return{stdout:Buffer.concat(_).toString(),stderr:C??Buffer.concat(v).toString(),status:D,signal:O,timeSeconds:k?.timeSeconds||S,cpuTimeSeconds:k?.cpuTimeSeconds??0,memoryBytes:k?.memoryBytes??0,timeCommandMessage:k?.message,timedOut:b||d(D,S,t.timeLimitSeconds),outputLimitExceeded:x}}finally{await n?.close()}}function d(e,t,n){return e===124?t>=n:e===137&&t>=n+o/1e3}function f(e,t){if(e.pid!==void 0)try{if(process.platform===`win32`){e.kill(t);return}process.kill(-e.pid,t)}catch(e){if(!(0,i.hasErrorCode)(e,`ESRCH`)&&!(0,i.hasErrorCode)(e,`EPERM`))throw e}}async function p(e){let i=await n.default.mkdtemp(t.default.join(r.default.tmpdir(),e));try{return await n.default.open(t.default.join(i,`record`),`wx+`,384)}finally{await n.default.rm(i,{recursive:!0,force:!0})}}async function m(e,t){let{size:n}=await e.stat(),r=Math.min(n,t),{buffer:i}=await e.read(Buffer.alloc(r),0,r,n-r);return i.toString()}function h(e){let t=/(?:^|\n)(\d+(?:[.,]\d+)?) (\d+(?:[.,]\d+)?) (\d+(?:[.,]\d+)?) (\d+)\s*$/.exec(e);if(t)return{timeSeconds:g(t[1]),cpuTimeSeconds:g(t[2])+g(t[3]),memoryBytes:Number(t[4])*1024,message:e.slice(0,t.index).trim()||void 0}}function g(e){return Number(e.replace(`,`,`.`))}function _(){let e=r.default.platform()===`darwin`?`gtime`:`/usr/bin/time`,t=a.default.spawnSync(e,[`--version`],{stdio:`ignore`});if(!(t.error||t.status!==0))return[e,`--format`,`%e %U %S %M`]}exports.isTimeCommandAvailable=c,exports.spawnWithLimits=u;
|
|
2
2
|
//# sourceMappingURL=spawnWithLimits.cjs.map
|