From 3237a5b0f7da7ff48f8fd1785b26bc99d15415db Mon Sep 17 00:00:00 2001 From: Okiki Date: Fri, 5 Jan 2024 09:04:38 +0000 Subject: [PATCH] fix: fix spotty chatgpt responses Signed-off-by: Okiki --- mod.ts | 5 +- process.ts | 144 ++++++++++++++++++++++++++++++++ static/.well-known/openapi.yaml | 15 ++-- vendor/twoslash.ts | 2 +- 4 files changed, 153 insertions(+), 13 deletions(-) create mode 100644 process.ts diff --git a/mod.ts b/mod.ts index 95314ec..6389c54 100644 --- a/mod.ts +++ b/mod.ts @@ -134,6 +134,7 @@ router `POST: /twoslash --> FormData Body={"code":"import { hasTransferables } from \"transferables\"","extension":"ts"}`, ], }; + context.response.type = "application/json"; return; } @@ -227,7 +228,7 @@ app.use(async (context, next) => { // If the client accepts JSON, send the error details as a JSON object if (context.request.accepts("json")) { context.response.body = { message, status, stack }; - context.response.type = "json"; + context.response.type = "application/json"; } else { // Otherwise, send the error details as plain text context.response.body = `${status} ${message}\n\n${stack ?? ""}`; @@ -240,7 +241,7 @@ app.use(async (context, next) => { // If the client accepts JSON, send the error details as a JSON object if (context.request.accepts("json")) { context.response.body = { message, code, description, recommendation, title, stack }; - context.response.type = "json"; + context.response.type = "application/json"; } else { // Otherwise, send the error details as plain text context.response.body = `${message}\n\n${stack ?? ""}`; diff --git a/process.ts b/process.ts new file mode 100644 index 0000000..8ac746f --- /dev/null +++ b/process.ts @@ -0,0 +1,144 @@ +import { Typescript } from "./deps.ts" + +/** + * Determines whether escape sequences in a token of a specific kind should be preserved. + * Useful for maintaining the integrity of literals where escape characters are meaningful. + * + * @example + * // Simple: In string literals like `"Hello\nWorld"`, the `\n` should be preserved. + * shouldPreserveEscapes(ts.SyntaxKind.StringLiteral); + * + * @example + * // Complex: In JSX text, escape sequences are part of the content and should not be altered. + * shouldPreserveEscapes(ts.SyntaxKind.JsxText); + * + * @param kind - The kind of the token being analyzed. + * @returns True if escape sequences should be preserved; otherwise, false. + */ +export function shouldPreserveEscapes(kind: Typescript.SyntaxKind, ts = Typescript): boolean { + return [ + ts.SyntaxKind.StringLiteral, + ts.SyntaxKind.JsxText, + ts.SyntaxKind.JsxTextAllWhiteSpaces, + ts.SyntaxKind.RegularExpressionLiteral, + ts.SyntaxKind.NoSubstitutionTemplateLiteral, + ts.SyntaxKind.TemplateHead, + ts.SyntaxKind.TemplateMiddle, + ts.SyntaxKind.TemplateTail, + // Additional kinds can be added as needed. + ].includes(kind); +} + +/** + * Processes the text of a token, applying or preserving escape sequences based on the token kind. + * This function handles different contexts like strings, regexes, and templates. + * + * @example + * // Simple: Convert escape sequences in a regular code line. + * processToken('console.log("Line1\\nLine2");', ts.SyntaxKind.StringLiteral); + * + * @example + * // Advanced: Leave a regex pattern unchanged. + * processToken('/\\d+/', ts.SyntaxKind.RegularExpressionLiteral); + * + * @param text - The text of the token. + * @param kind - The kind of the token. + * @returns The processed text. + */ +export function processToken(text: string, kind: Typescript.SyntaxKind, ts = Typescript): string { + if (shouldPreserveEscapes(kind, ts)) { + // Special handling for regex literals to preserve their pattern. + if (kind === ts.SyntaxKind.RegularExpressionLiteral) { + return processRegex(text); + } + return text; + } + + // Unescape common escape sequences for other kinds of tokens. + return unescapeSequences(text); +} + +/** + * Replaces escape sequences in a given text with their corresponding characters. + * This function is commonly used for non-literal parts of the code. + * + * @example + * // Simple: Replace basic escape sequences. + * unescapeSequences("Line1\\nLine2"); + * + * @example + * // Complex: Handle Unicode escapes. + * unescapeSequences("\\u0041\\u0042\\u0043"); + * + * @param text - The text to be processed. + * @returns The text with escape sequences replaced. + */ +export function unescapeSequences(text: string): string { + const result = text + .replace(/\\f/g, '\f') + .replace(/\\n/g, '\n') + .replace(/\\r/g, '\r') + .replace(/\\t/g, '\t') + .replace(/\\v/g, '\v') + .replace(/\\0/g, '\0') + .replace(/\\x([0-9A-Fa-f]{2})/g, (_, h) => String.fromCharCode(parseInt(h, 16))) + .replace(/\\u([0-9A-Fa-f]{4})/g, (_, h) => String.fromCharCode(parseInt(h, 16))) + .replace(/\\u{([0-9A-Fa-f]{1,6})}/g, (_, h) => String.fromCodePoint(parseInt(h, 16))); + console.log({ text, result, test: /\n/g.test(text) }) + return result +} + +/** + * Processes regex literals, ensuring that escape sequences are handled correctly. + * This function is designed to preserve the pattern of the regex. + * + * @example + * // Simple: Keep a basic regex pattern unchanged. + * processRegex('/\\d+/'); + * + * @example + * // Advanced: Complex regex patterns remain intact. + * processRegex('/^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$/'); + * + * @param regex - The regex literal text. + * @returns The processed regex. + */ +export function processRegex(regex: string): string { + // Add any specific processing for regex literals if necessary. + // For now, it returns the regex as-is. + return regex; +} + +/** + * Processes TypeScript source code to handle escape sequences. + * It scans the code, processes each token, and reconstructs the code with escape sequences handled. + * + * @example + * // Simple: Process a single line of code. + * processCode('let example = "Line1\\nLine2";'); + * + * @example + * // Complex: Process a block of code with various contexts. + * processCode(`function greet() { console.log("Hello\\nWorld"); } /\\d+/.test("123");`); + * + * @param code - The TypeScript source code. + * @returns The processed code. + */ +export function processCode(code: string, ts = Typescript): string { + let output = ''; + const scanner = ts.createScanner( + ts.ScriptTarget.Latest, + false, + ts.LanguageVariant.Standard, + code + ); + + let token = scanner.scan(); + while (token !== ts.SyntaxKind.EndOfFileToken) { + const text = scanner.getTokenText(); + output += processToken(text, token); + token = scanner.scan(); + } + + return output; +} \ No newline at end of file diff --git a/static/.well-known/openapi.yaml b/static/.well-known/openapi.yaml index 142f90e..53036f5 100644 --- a/static/.well-known/openapi.yaml +++ b/static/.well-known/openapi.yaml @@ -64,27 +64,22 @@ paths: less. Accepts TwoSlashOptions as query parameters and returns TwoSlashReturn JSON. parameters: - - name: options - in: query - schema: - type: object - $ref: '#/components/schemas/TwoSlashOptions' - - name: option - in: query - schema: - type: object - $ref: '#/components/schemas/TwoSlashOptions' - name: code in: query + description: The TypeScript, JavaScript, TSX, or JSX code to analyze. + required: true schema: type: string $ref: '#/components/schemas/Code' - name: extension + description: The extension of the code file. Can be 'typescript', 'javascript', 'json', 'jsn', 'ts', 'js', 'tsx', or 'jsx'. in: query + required: true schema: type: string $ref: '#/components/schemas/Extension' - name: ext + description: The extension of the code file. Can be 'typescript', 'javascript', 'json', 'jsn', 'ts', 'js', 'tsx', or 'jsx'. in: query schema: type: string diff --git a/vendor/twoslash.ts b/vendor/twoslash.ts index 44eec02..8af1bad 100644 --- a/vendor/twoslash.ts +++ b/vendor/twoslash.ts @@ -672,7 +672,7 @@ export async function twoslasher(code: string, extension: string, options: TwoSl const defaultCompilerOptions = { strict: true, - target: ts.ScriptTarget.ES2022, + target: ts.ScriptTarget.Latest, allowJs: true, ...(options.defaultCompilerOptions ?? {}), } -- 2.51.2