shipthis
Version:
ShipThis manages building and uploading your Godot games to the App Store and Google Play.
240 lines (219 loc) • 8.95 kB
JavaScript
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 };