UNPKG

shipthis

Version:

ShipThis manages building and uploading your Godot games to the App Store and Google Play.

240 lines (219 loc) 8.95 kB
import fs__default from 'node:fs'; import path__default from 'node:path'; import { Args, Flags } from '@oclif/core'; import { q as ejs } from '../../baseGameCommand-8VL7xe-O.js'; import 'axios'; import 'crypto-js'; import 'uuid'; import { B as BaseCommand } from '../../baseCommand-CTn3KGH3.js'; import 'luxon'; import 'react/jsx-runtime'; import 'ink'; import 'ink-spinner'; import 'node:crypto'; import 'node:readline'; import 'node:url'; import 'readline-sync'; import 'isomorphic-git'; import CustomHelp from '../../utils/help.js'; import '@tanstack/react-query'; import 'react'; import 'fast-glob'; import 'yazl'; import 'socket.io-client'; import 'fullscreen-ink'; import 'string-length'; import 'strip-ansi'; import 'open'; import '@inkjs/ui'; import 'marked'; import 'marked-terminal'; import 'qrcode'; import 'fs'; import 'path'; import 'chalk'; import '@expo/apple-utils/build/index.js'; import 'deepmerge'; import 'ini'; const ROOT_TOPIC_NAME = "shipthis"; const ROOT_TOPIC_DESCRIPTION = "Root topic"; const ROOT_TOPIC_FILENAME = "README.md"; const TOPIC_TEMPLATE = ` # Topic: \`<%= topic.name.replaceAll(":", " ") %>\` <%= topic.description || "" %> <% if (subTopics && subTopics.length > 0) { -%> ## Topics <% subTopics.forEach(subTopic => { -%> - [<%= subTopic.topic.name %>](<%= subTopic.filePath %>) <% }) -%> <% } -%> <% if (commands && commands.length > 0) { -%> ## Commands <% commands.forEach(readmeCommand => { -%> - [<%= readmeCommand.command.id %>](<%= readmeCommand.filePath %>) <% }) -%> <% } -%> `.trim(); const TOPIC_TEMPLATE_INCLUDE = ` # Topic: \`<%= topic.name.replaceAll(":", " ") %>\` <%= topic.description || "" %> <% if (subTopics && subTopics.length > 0) { -%> ## Topics <% subTopics.forEach(subTopic => { %> <%- subTopic.rendered %> <% }) -%> <% } -%> <% if (commands && commands.length > 0) { -%> ## Commands <% commands.forEach(readmeCommand => { %> <%- readmeCommand.renderedForInclude %> <% }) -%> <% } -%> `.trim(); const COMMAND_TEMPLATE = ` # Command: \`<%= command.id %>\` ## Description <%= command.description || "" %> ## Help Output \`\`\`help <%- helpOutput %> \`\`\` `.trim(); const COMMAND_TEMPLATE_INCLUDE = ` ### \`<%= command.id %>\` #### Description <%= command.description || "" %> #### Help Output \`\`\`help <%- helpOutput %> \`\`\` `.trim(); function getTopicTree(topics, commands, separateFileDepth) { const commandIds = new Set(commands.map((command) => command.id)); const nonInternalTopics = topics.filter((topic) => topic.name !== "internal"); const nonCommandTopics = nonInternalTopics.filter((topic) => !commandIds.has(topic.name)); const topicTree = { [ROOT_TOPIC_NAME]: { commands: [], filePath: ROOT_TOPIC_FILENAME, includeTopicsAndCommands: separateFileDepth === 0, subTopics: [], topic: { description: ROOT_TOPIC_DESCRIPTION, name: ROOT_TOPIC_NAME } } }; const topicsByName = Object.fromEntries(topics.map((topic) => [topic.name, topic])); for (const topic of nonCommandTopics) { const topicPath = topic.name.split(":"); let currentParent = topicTree[ROOT_TOPIC_NAME]; for (let i = 0; i < topicPath.length; i++) { const name = topicPath.slice(0, i + 1).join(":"); const subTopic = currentParent.subTopics.find((subTopic2) => subTopic2.topic.name === name); if (subTopic) { currentParent = subTopic; } else { const currentDepth = i + 1; const includeTopicsAndCommands = currentParent.includeTopicsAndCommands || currentDepth >= separateFileDepth; const newSubTopic = { commands: [], filePath: `${path__default.join(...name.split(":"))}.md`, includeTopicsAndCommands, subTopics: [], topic: topicsByName[name] }; currentParent.subTopics.push(newSubTopic); currentParent = newSubTopic; } } } for (const command of commands) { const commandPath = command.id.split(":"); if (commandPath[0] === "internal") continue; let currentParent = topicTree[ROOT_TOPIC_NAME]; for (let i = 0; i < commandPath.length - 1; i++) { const name = commandPath.slice(0, i + 1).join(":"); const subTopic = currentParent.subTopics.find((subTopic2) => subTopic2.topic.name === name); if (!subTopic) throw new Error("Could not find topic for command: " + command.id); currentParent = subTopic; } currentParent.commands.push({ command, filePath: `${path__default.join(...command.id.split(":"))}.md` }); } return topicTree; } function renderTopic(readmeTopic, config) { const renderedSubTopics = readmeTopic.subTopics.map((subTopic) => renderTopic(subTopic, config)); const renderedCommands = readmeTopic.commands.map((readmeCommand) => renderCommand(readmeCommand, config)); const topicTemplate = readmeTopic.includeTopicsAndCommands ? TOPIC_TEMPLATE_INCLUDE : TOPIC_TEMPLATE; const rendered = ejs.render(topicTemplate, { commands: renderedCommands, subTopics: renderedSubTopics, topic: readmeTopic.topic }); return { ...readmeTopic, commands: renderedCommands, rendered, subTopics: renderedSubTopics }; } function renderCommand(readmeCommand, config) { const columns = Number.parseInt(process.env.COLUMNS, 10) || 120; const help = new CustomHelp(config, { maxWidth: columns, stripAnsi: true }); const helpOutput = help.exposedFormatCommand(readmeCommand.command); const renderedForInclude = ejs.render(COMMAND_TEMPLATE_INCLUDE, { command: readmeCommand.command, helpOutput }); const renderedForFile = ejs.render(COMMAND_TEMPLATE, { command: readmeCommand.command, helpOutput }); return { ...readmeCommand, renderedForFile, renderedForInclude }; } function writeTopic(topic, outputDir, dryRun, overWrite, only) { if (!topic.rendered) throw new Error(`Topic ${topic.topic.name} has not been rendered`); const makeFolderAndSave = (filePath2, rendered) => { const exists = fs__default.existsSync(filePath2); const outputList = exists ? writeOutput.overwritten : writeOutput.created; outputList.push(filePath2); const doWrite = !exists || overWrite; if (!doWrite || dryRun) return; const folder = path__default.dirname(filePath2); fs__default.mkdirSync(folder, { recursive: true }); fs__default.writeFileSync(filePath2, rendered); }; const skipFile = (filePath2) => only && !filePath2.match(only); const writeOutput = { created: [], overwritten: [] }; const filePath = path__default.join(outputDir, topic.filePath); if (!skipFile(filePath)) makeFolderAndSave(filePath, topic.rendered); for (const subTopic of topic.subTopics) { const subWriteOutput = writeTopic(subTopic, outputDir, dryRun, overWrite, only); writeOutput.created.push(...subWriteOutput.created); writeOutput.overwritten.push(...subWriteOutput.overwritten); } for (const command of topic.commands) { if (!command.renderedForFile) throw new Error(`Command ${command.command.id} has not been rendered`); const filePath2 = path__default.join(outputDir, command.filePath); if (!skipFile(filePath2)) makeFolderAndSave(filePath2, command.renderedForFile); } return writeOutput; } class InternalReadme extends BaseCommand { static args = { outputDir: Args.string({ description: "The directory where the readme files will be written", required: true }) }; static description = "Generate the readme files for the commands"; static examples = ["<%= config.bin %> <%= command.id %>"]; static flags = { depth: Flags.integer({ char: "d", description: "The depth of the topic tree to render as separate files" }), // By default do nothing notDryRun: Flags.boolean({ char: "n", description: "Set to actually write the files (will not overwrite)" }), only: Flags.string({ char: "l", description: "Glob pattern - will only write the files which match" }), overWrite: Flags.boolean({ char: "o", description: "Overwrite existing files" }) }; async run() { const { outputDir } = this.args; const { depth, notDryRun, only, overWrite } = this.flags; const dryRun = !notDryRun; const { commands, topics } = this.config; const topicTree = getTopicTree(topics, commands, depth || 0); const renderedTopicTree = renderTopic(topicTree[ROOT_TOPIC_NAME], this.config); if (dryRun) console.log("Dry-run mode: No files will be written."); const writeOutput = writeTopic(renderedTopicTree, outputDir, dryRun, overWrite, only); if (writeOutput.created.length > 0) console.log(dryRun ? "Would create the following files:" : "Created the following files:"); for (const file of writeOutput.created) console.log(`- ${file}`); if (writeOutput.overwritten.length > 0) console.log(notDryRun && overWrite ? "Overwritten the following files:" : "Would overwrite the following files:"); for (const file of writeOutput.overwritten) console.log(`- ${file}`); } } export { InternalReadme as default };