nativescript
Version:
Command-line interface for building NativeScript projects
235 lines • 12.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HelpService = void 0;
const path = require("path");
const os_1 = require("os");
const marked_1 = require("marked");
const yok_1 = require("../yok");
const _ = require("lodash");
class HelpService {
get newLineRegex() {
return /\r?\n/g;
}
get pathToStylesCss() {
return path.join(this.$staticConfig.HTML_COMMON_HELPERS_DIR, "styles.css");
}
get pathToBasicPage() {
return path.join(this.$staticConfig.HTML_COMMON_HELPERS_DIR, "basic-page.html");
}
get pathToBasicPageForExtensions() {
return path.join(this.$staticConfig.HTML_COMMON_HELPERS_DIR, "basic-extensions-page.html");
}
get pathToIndexHtml() {
return path.join(this.$staticConfig.HTML_PAGES_DIR, "index.html");
}
constructor($logger, $injector, $errors, $fs, $staticConfig, $extensibilityService, $microTemplateService, $opener) {
this.$logger = $logger;
this.$injector = $injector;
this.$errors = $errors;
this.$fs = $fs;
this.$staticConfig = $staticConfig;
this.$extensibilityService = $extensibilityService;
this.$microTemplateService = $microTemplateService;
this.$opener = $opener;
this.pathToImages = this.$staticConfig.HTML_CLI_HELPERS_DIR;
this.pathToHtmlPages = this.$staticConfig.HTML_PAGES_DIR;
this.pathToManPages = this.$staticConfig.MAN_PAGES_DIR;
}
async openHelpForCommandInBrowser(commandData) {
const { commandName } = commandData;
const htmlPage = (await this.convertCommandNameToFileName(commandData)) +
HelpService.HTML_FILE_EXTENSION;
this.$logger.trace("Opening help for command '%s'. FileName is '%s'.", commandName, htmlPage);
this.$fs.ensureDirectoryExists(this.pathToHtmlPages);
if (!this.tryOpeningSelectedPage(htmlPage)) {
// HTML pages may have been skipped on post-install, lets generate them.
this.$logger.trace("Required HTML file '%s' is missing. Let's try generating HTML files and see if we'll find it.", htmlPage);
await this.generateHtmlPages();
if (!this.tryOpeningSelectedPage(htmlPage)) {
this.$errors.fail("Unable to find help for '%s'", commandName);
}
}
}
async generateHtmlPages() {
const mdFiles = this.$fs.enumerateFilesInDirectorySync(this.pathToManPages);
const basicHtmlPage = this.$fs.readText(this.pathToBasicPage);
await Promise.all(_.map(mdFiles, (markdownFile) => {
const htmlPageGenerationData = {
basicHtmlPage,
pathToMdFile: markdownFile,
pathToMdPages: this.pathToManPages,
pathToHtmlPages: this.pathToHtmlPages,
};
return this.createHtmlPage(htmlPageGenerationData);
}));
const installedExtensionsData = this.$extensibilityService.getInstalledExtensionsData();
const basicHtmlPageForExtensions = this.$fs.readText(this.pathToBasicPageForExtensions);
for (const extensionData of installedExtensionsData) {
const docsDir = extensionData.docs;
if (docsDir) {
this.$logger.trace(`Start generation of html help content for extension ${extensionData.extensionName}`);
if (!this.$fs.exists(docsDir)) {
this.$logger.warn(`Unable to generate html help pages for extension ${extensionData.extensionName} as the docs directory ${docsDir} does not exist.`);
continue;
}
const htmlDirFullPath = HelpService.getHtmlDirFullPath(docsDir);
this.$fs.ensureDirectoryExists(htmlDirFullPath);
const extensionMdFiles = this.$fs.enumerateFilesInDirectorySync(docsDir);
try {
await Promise.all(_.map(extensionMdFiles, (markdownFile) => {
const htmlPageGenerationData = {
basicHtmlPage: basicHtmlPageForExtensions,
pathToMdFile: markdownFile,
pathToMdPages: docsDir,
pathToHtmlPages: htmlDirFullPath,
extensionName: extensionData.extensionName,
};
return this.createHtmlPage(htmlPageGenerationData);
}));
}
catch (err) {
this.$logger.warn(`Unable to generate html help for extension ${extensionData.extensionName}. Error is: ${err.message}`);
}
this.$logger.trace(`Finished generation of html help content for extension ${extensionData.extensionName}`);
}
}
this.$logger.trace("Finished generating HTML files.");
}
async showCommandLineHelp(commandData) {
const help = await this.getCommandLineHelpForCommand(commandData);
this.$logger.printMarkdown(help);
}
/**
* Gets the help content for a specific command that should be shown on the terminal.
* @param {string} commandName Name of the command for which to read the help.
* @returns {Promise<string>} Help content of the command parsed with all terminal rules applied (stripped content that should be shown only for html help).
*/
async getCommandLineHelpForCommand(commandData) {
const helpText = await this.readMdFileForCommand(commandData);
const commandLineHelp = (await this.$microTemplateService.parseContent(helpText, { isHtml: false }))
.replace(/ /g, " ")
.replace(HelpService.MARKDOWN_LINK_REGEX, "$1")
.replace(HelpService.SPAN_REGEX, (matchingSubstring, textBeforeSpan, textInsideSpan, index, fullString) => {
return textBeforeSpan + textInsideSpan.replace(this.newLineRegex, "");
})
.replace(HelpService.NEW_LINE_REGEX, os_1.EOL);
return commandLineHelp;
}
// This method should return Promise in order to generate all html pages simultaneously.
async createHtmlPage(htmlPageGenerationData) {
const { basicHtmlPage, pathToMdFile, pathToMdPages, pathToHtmlPages, extensionName, } = htmlPageGenerationData;
const mdFileName = path.basename(pathToMdFile);
const htmlFileName = mdFileName.replace(HelpService.MARKDOWN_FILE_EXTENSION, HelpService.HTML_FILE_EXTENSION);
this.$logger.trace("Generating '%s' help topic.", htmlFileName);
const helpText = this.$fs.readText(pathToMdFile);
const outputText = await this.$microTemplateService.parseContent(helpText, {
isHtml: true,
});
const htmlText = await (0, marked_1.marked)(outputText);
const filePath = pathToMdFile
.replace(path.basename(pathToMdPages), path.basename(pathToHtmlPages))
.replace(mdFileName, htmlFileName);
this.$logger.trace("HTML file path for '%s' man page is: '%s'.", mdFileName, filePath);
let outputHtml = basicHtmlPage
.replace(HelpService.MAN_PAGE_NAME_REGEX, mdFileName.replace(HelpService.MARKDOWN_FILE_EXTENSION, ""))
.replace(HelpService.HTML_COMMAND_HELP_REGEX, htmlText)
.replace(HelpService.RELATIVE_PATH_TO_STYLES_CSS_REGEX, path.relative(path.dirname(filePath), this.pathToStylesCss))
.replace(HelpService.RELATIVE_PATH_TO_IMAGES_REGEX, path.relative(path.dirname(filePath), this.pathToImages))
.replace(HelpService.RELATIVE_PATH_TO_INDEX_REGEX, path.relative(path.dirname(filePath), this.pathToIndexHtml));
if (extensionName) {
outputHtml = outputHtml.replace(HelpService.EXTENSION_NAME_REGEX, extensionName);
}
this.$fs.writeFile(filePath, outputHtml);
this.$logger.trace("Finished writing file '%s'.", filePath);
}
async convertCommandNameToFileName(commandData) {
let { commandName } = commandData;
const defaultCommandMatch = commandName && commandName.match(/([\w-]+?)\|\*/);
if (defaultCommandMatch) {
this.$logger.trace("Default command found. Replace current command name '%s' with '%s'.", commandName, defaultCommandMatch[1]);
commandName = defaultCommandMatch[1];
}
const availableCommands = this.$injector
.getRegisteredCommandsNames(true)
.sort();
this.$logger.trace("List of registered commands: %s", availableCommands.join(", "));
if (commandName && !_.includes(availableCommands, commandName)) {
await this.throwMissingCommandError(commandData);
}
return (commandName && commandName.replace(/\|/g, "-")) || "start";
}
async throwMissingCommandError(commandData) {
const commandName = commandData.commandName;
const commandInfo = {
inputStrings: [commandName, ...commandData.commandArguments],
commandDelimiter: "|" /* CommandsDelimiters.HierarchicalCommand */,
defaultCommandDelimiter: "|*" /* CommandsDelimiters.DefaultHierarchicalCommand */,
};
const extensionData = await this.$extensibilityService.getExtensionNameWhereCommandIsRegistered(commandInfo);
if (extensionData) {
this.$errors.fail(extensionData.installationMessage);
}
this.$errors.fail("Unknown command '%s'. Try '$ %s help' for a full list of supported commands.", commandName, this.$staticConfig.CLIENT_NAME.toLowerCase());
}
static getHtmlDirFullPath(docsDir) {
return path.join(path.dirname(docsDir), "html");
}
getHelpFile(searchedFileName, dirToCheck, getFullPathAction) {
const fileList = this.$fs.enumerateFilesInDirectorySync(dirToCheck);
let fileToOpen = _.find(fileList, (file) => path.basename(file) === searchedFileName);
if (!fileToOpen) {
fileToOpen = this.getHelpFileFromExtensions(searchedFileName, getFullPathAction);
}
return fileToOpen;
}
getHelpFileFromExtensions(searchedFileName, getFullPathAction) {
const installedExtensionsData = this.$extensibilityService.getInstalledExtensionsData();
for (const extensionData of installedExtensionsData) {
const docsDir = extensionData.docs;
if (docsDir) {
const fullPath = getFullPathAction
? getFullPathAction(docsDir)
: docsDir;
const fileToOpen = this.$fs.exists(fullPath) &&
_.find(this.$fs.enumerateFilesInDirectorySync(fullPath), (file) => path.basename(file) === searchedFileName);
if (fileToOpen) {
return fileToOpen;
}
}
}
}
tryOpeningSelectedPage(htmlPage) {
const pageToOpen = this.getHelpFile(htmlPage, this.pathToHtmlPages, HelpService.getHtmlDirFullPath);
if (pageToOpen) {
this.$logger.trace("Found page to open: '%s'", pageToOpen);
this.$opener.open(pageToOpen, "");
return true;
}
this.$logger.trace("Unable to find file: '%s'", htmlPage);
return false;
}
async readMdFileForCommand(commandData) {
const mdFileName = (await this.convertCommandNameToFileName(commandData)) +
HelpService.MARKDOWN_FILE_EXTENSION;
this.$logger.trace("Reading help for command '%s'. FileName is '%s'.", commandData.commandName, mdFileName);
const markdownFile = this.getHelpFile(mdFileName, this.pathToManPages);
if (markdownFile) {
return this.$fs.readText(markdownFile);
}
await this.throwMissingCommandError(commandData);
}
}
exports.HelpService = HelpService;
HelpService.MARKDOWN_FILE_EXTENSION = ".md";
HelpService.HTML_FILE_EXTENSION = ".html";
HelpService.MAN_PAGE_NAME_REGEX = / @/g;
HelpService.HTML_COMMAND_HELP_REGEX = / @/g;
HelpService.RELATIVE_PATH_TO_STYLES_CSS_REGEX = / @/g;
HelpService.RELATIVE_PATH_TO_IMAGES_REGEX = / @/g;
HelpService.RELATIVE_PATH_TO_INDEX_REGEX = / @/g;
HelpService.EXTENSION_NAME_REGEX = / @/g;
HelpService.MARKDOWN_LINK_REGEX = /\[([\w \-\`\<\>\*\:\\]+?)\]\([\s\S]+?\)/g;
HelpService.SPAN_REGEX = /([\s\S]*?)(?:\r?\n)?<span.*?>([\s\S]*?)<\/span>(?:\r?\n)*/g;
HelpService.NEW_LINE_REGEX = /<\/?\s*?br\s*?\/?>/g; // <br>, <br > <br/> <br />
yok_1.injector.register("helpService", HelpService);
//# sourceMappingURL=help-service.js.map