{"version":3,"file":"parser_executables.js","sourceRoot":"","sources":["../../src/connect/parser_executables.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod'\nimport { exitWithError, logger } from '../common/logging'\nimport {\n CodeConnectCustomExecutableParserConfig,\n CodeConnectExecutableParserConfig,\n CodeConnectExecutableParser,\n CodeConnectParser,\n} from './project'\nimport {\n ParserExecutableMessages,\n ParserRequestPayload,\n ParseResponsePayload,\n CreateResponsePayload,\n} from './parser_executable_types'\nimport { spawn } from 'cross-spawn'\nimport { getSwiftParserDir } from '../parser_scripts/get_swift_parser_dir'\nimport fs from 'fs'\nimport path from 'path'\nimport {\n getGradleWrapperExecutablePath,\n getGradleWrapperPath,\n} from '../parser_scripts/get_gradlew_path'\nimport { getComposeErrorSuggestion } from '../parser_scripts/compose_errors'\n\nconst temporaryInputFilePath = 'tmp/figma-code-connect-parser-io.json.tmp'\nconst temporaryOutputDirectoryPath = 'tmp/parser-output'\n\ntype ParserInfo = {\n command: (\n cwd: string,\n config: CodeConnectExecutableParserConfig | CodeConnectCustomExecutableParserConfig,\n mode: ParserRequestPayload['mode'],\n ) => Promise\n temporaryInputFilePath?: string\n temporaryOutputDirectoryPath?: string\n}\n\nconst FIRST_PARTY_PARSERS: Record = {\n swift: {\n command: async (cwd, config) => {\n return `swift run --package-path ${await getSwiftParserDir(\n cwd,\n (config as any).xcodeprojPath,\n (config as any).swiftPackagePath,\n (config as any).sourcePackagesPath,\n )} figma-swift`\n },\n },\n compose: {\n command: async (cwd, config, mode) => {\n const gradlewPath = await getGradleWrapperPath(cwd, (config as any).gradleWrapperPath)\n const gradleExecutableInvocation = getGradleWrapperExecutablePath(gradlewPath)\n const verboseFlags = (config as any).verbose ? ' --stacktrace' : ''\n if (mode === 'CREATE') {\n return `${gradleExecutableInvocation} -p ${gradlewPath} createCodeConnect -PfilePath=${temporaryInputFilePath}${verboseFlags} -PoutputDir=${temporaryOutputDirectoryPath}`\n } else {\n return `${gradleExecutableInvocation} -p ${gradlewPath} parseCodeConnect -PfilePath=${temporaryInputFilePath}${verboseFlags} -PoutputDir=${temporaryOutputDirectoryPath}`\n }\n },\n temporaryInputFilePath: temporaryInputFilePath,\n temporaryOutputDirectoryPath: temporaryOutputDirectoryPath,\n },\n custom: {\n command: async (cwd, config) => {\n if (!('parserCommand' in config)) {\n exitWithError(\n 'No `parserCommand` specified in config. A command is required when using the `custom` parser.',\n )\n }\n logger.info('Using custom parser command: ' + config.parserCommand)\n return config.parserCommand\n },\n },\n __unit_test__: {\n command: async () => 'node parser/unit_test_parser.js',\n },\n}\n\nfunction getParser(config: CodeConnectExecutableParserConfig): ParserInfo | never {\n const parser = FIRST_PARTY_PARSERS[config.parser]\n\n if (!parser) {\n exitWithError(\n `Invalid parser specified: \"${config.parser}\". Valid parsers are: ${Object.keys(\n FIRST_PARTY_PARSERS,\n ).join(', ')}.`,\n )\n }\n\n return parser\n}\n\nexport async function callParser(\n config: CodeConnectExecutableParserConfig,\n payload: ParserRequestPayload,\n cwd: string,\n) {\n return new Promise(async (resolve, reject) => {\n try {\n const parser = getParser(config)\n const configWithVerbose = {\n ...config,\n verbose: (payload as any).verbose,\n }\n\n const command = await parser.command(cwd, configWithVerbose, payload.mode)\n\n // Create temporary input file if it exists\n if (parser.temporaryInputFilePath) {\n fs.mkdirSync(path.dirname(parser.temporaryInputFilePath), { recursive: true })\n fs.writeFileSync(parser.temporaryInputFilePath, JSON.stringify(payload))\n }\n\n // Create temporary output directory if it exists\n if (parser.temporaryOutputDirectoryPath) {\n fs.mkdirSync(parser.temporaryOutputDirectoryPath, { recursive: true })\n }\n\n logger.debug(`Running parser: ${command}`)\n const commandSplit = command.split(' ')\n\n const child = spawn(commandSplit[0], commandSplit.slice(1), {\n cwd,\n })\n\n let stdout = ''\n let stderr = ''\n\n child.stdout.on('data', (data) => {\n stdout += data.toString()\n })\n\n // This handles any stderr output from the parser.\n //\n // Parsers should not generally write to stderr, and should instead return\n // an array of messages at the end of execution, but there are cases where\n // you might want to log output immediately rather than at the end of the\n // run - e.g. if the parser can take a while to compile first time, you\n // might want to inform the user immediately that it is compiling.\n //\n // To log output, the parser should write a JSON object to stderr with the\n // same structure as the `messages` response object, e.g. `{ \"level\":\n // \"INFO\", \"message\": \"Compiling parser...\" }`.\n //\n // Non-JSON output will be logged as debug messages, as this is likely to\n // be e.g. compiler output which the user doesn't need to see ordinarily.\n child.stderr.on('data', (data) => {\n const message = data.toString()\n const trimmedMessage = message.trim()\n try {\n const parsed = JSON.parse(trimmedMessage)\n handleMessages([parsed])\n } catch (e) {\n stderr += message\n logger.debug(trimmedMessage)\n }\n })\n\n child.on('close', (code) => {\n if (code !== 0) {\n const errorSuggestion = determineErrorSuggestionFromStderr(stderr, config.parser)\n if (errorSuggestion) {\n reject(new Error(`Parser exited with code ${code}: ${errorSuggestion}`))\n } else {\n reject(new Error(`Parser exited with code ${code}`))\n }\n } else {\n // List the files in the temporary output directory if it exists and log them\n if (parser.temporaryOutputDirectoryPath) {\n try {\n const outputDir = parser.temporaryOutputDirectoryPath\n if (fs.existsSync(outputDir)) {\n const files = fs.readdirSync(outputDir)\n logger.debug(\n `Files in temporary output directory (${outputDir}): ${files.length > 0 ? files.join(', ') : '[empty]'}`,\n )\n\n // Combine all JSON files in the output directory into a single JSON object.\n // For PARSE responses: Each file has a \"docs\" array and a \"messages\" array.\n // For CREATE responses: Each file has a \"createdFiles\" array and a \"messages\" array.\n // The combined output will have a single array of the appropriate type and a single \"messages\" array.\n\n // Filter for .json files only\n const jsonFiles = files.filter((f) => f.endsWith('.json'))\n let allMessages: z.infer = []\n const uniqueDocsMap = new Map<\n string,\n z.infer['docs'][number]\n >()\n let allCreatedFiles: z.infer['createdFiles'] = []\n\n for (const file of jsonFiles) {\n const filePath = path.join(outputDir, file)\n try {\n const content = fs.readFileSync(filePath, 'utf8')\n const parsed = JSON.parse(content)\n if (Array.isArray(parsed.docs)) {\n // Deduplicate docs based on figmaNode + template\n // The template contains the actual code example and is unique per component.\n // This allows multiple different code components to map to the same Figma node,\n // while preventing the same component from being duplicated in multi-module projects.\n for (const doc of parsed.docs) {\n const dedupeKey = JSON.stringify({\n figmaNode: doc.figmaNode,\n template: doc.template,\n })\n if (!uniqueDocsMap.has(dedupeKey)) {\n uniqueDocsMap.set(dedupeKey, doc)\n }\n }\n }\n if (Array.isArray(parsed.createdFiles)) {\n allCreatedFiles = allCreatedFiles.concat(parsed.createdFiles)\n }\n if (Array.isArray(parsed.messages)) {\n allMessages = allMessages.concat(parsed.messages)\n }\n } catch (err) {\n logger.warn(`Failed to parse output file ${file}: ${err}`)\n }\n }\n\n const allDocs = Array.from(uniqueDocsMap.values())\n\n // Return the appropriate response based on the mode\n if (payload.mode === 'CREATE') {\n resolve({\n createdFiles: allCreatedFiles,\n messages: allMessages,\n })\n } else {\n resolve({\n docs: allDocs,\n messages: allMessages,\n })\n }\n } else {\n logger.debug(`Temporary output directory (${outputDir}) does not exist.`)\n }\n } catch (err) {\n logger.warn(`Failed to list files in temporary output directory: ${err}`)\n }\n } else {\n // Assume the output is in the stdout\n resolve(JSON.parse(stdout))\n }\n }\n\n if (parser.temporaryInputFilePath) {\n // Retain temp file and directory when verbose mode is enabled\n if (!(payload as any).verbose) {\n fs.unlinkSync(parser.temporaryInputFilePath)\n // Delete parent directory if empty after removing temp file\n const parentDir = path.dirname(parser.temporaryInputFilePath)\n if (fs.readdirSync(parentDir).length === 0) {\n fs.rmdirSync(parentDir)\n }\n }\n }\n })\n\n child.on('error', (e) => {\n reject(e)\n })\n if (!parser.temporaryInputFilePath) {\n child.stdin.write(JSON.stringify(payload))\n child.stdin.end()\n }\n } catch (e) {\n if ((payload as any).verbose) {\n console.trace(e)\n\n // Don't say to enable verbose if the user has already enabled it.\n exitWithError(`Error calling parser: ${e}.`)\n } else {\n exitWithError(\n `Error calling parser: ${e}. Try re-running the command with --verbose for more information.`,\n )\n }\n }\n })\n}\n\nexport function handleMessages(messages: z.infer) {\n let hasErrors = false\n\n messages.forEach((message) => {\n switch (message.level) {\n case 'DEBUG':\n logger.debug(message.message)\n break\n case 'INFO':\n logger.info(message.message)\n break\n case 'WARN':\n logger.warn(message.message)\n break\n case 'ERROR':\n logger.error(message.message)\n hasErrors = true\n break\n }\n })\n\n return { hasErrors }\n}\n\n// This function is used to determine if there is a suggestion for the error based on the output\n// to stderr. Certain parsers return the same error code for different types of errors such as\n// errors from invoking the gradle wrapper for Compose.\n// In the future we should consider exposing a different API for having the parser return a suggestion directly.\nfunction determineErrorSuggestionFromStderr(\n stderr: string,\n parser: CodeConnectParser,\n): string | null {\n if (parser === 'compose') {\n return getComposeErrorSuggestion(stderr)\n }\n return null\n}\n"]}