azure-sdk-for-js/.scripts
Wei Dong a9b002a0c5
use latest automation tool (#19052)
2021-12-09 11:21:26 +08:00
..
automation_generate.sh
automation_init.sh
checkEverything.ts
clean-autopr.ts
commandLine.ts
common.ts
generateDataplaneList.ts
gulp.ts
latest.ts
local.ts
logger.ts
packages.ts
readme.ts
version.ts

readme.ts

/**
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for
 * license information.
 */

import { promises as fs } from "fs";
import * as glob from "glob";
import * as yaml from "js-yaml";
import * as path from "path";
import { SdkType } from "./commandLine";
import { contains, pathExists, startsWith } from "./common";
import { Logger } from "./logger";

const _logger = Logger.get();

interface ReadmeSettings {
    "nodejs": {
        "azure-arm": boolean;
        "license-header": string;
        "payload-flattening-threshold": number;
        "package-name": string;
        "output-folder": string;
        "generate-license-txt": boolean | undefined;
        "generate-package-json": boolean | undefined;
        "generate-readme-md": boolean | undefined;
        "generate-metadata": boolean | undefined;
    } | undefined;
}

export async function getPackageFolderPathFromPackageArgument(packageName: string, azureRestAPISpecsRoot: string, azureSDKForJSRepoRoot: string): Promise<string | undefined> {
    let packageFolderPath: string | undefined;

    if (!packageName) {
        _logger.logError(`No --package specified.`);
    } else {
        const typeScriptReadmeFilePaths: string[] = findReadmeTypeScriptMdFilePaths(azureRestAPISpecsRoot);
        _logger.logTrace(`Found ${typeScriptReadmeFilePaths.length} readmes in ${azureRestAPISpecsRoot}`)

        let foundPackage = false;

        for (let i = 0; i < typeScriptReadmeFilePaths.length; ++i) {
            const typeScriptReadmeFilePath: string = typeScriptReadmeFilePaths[i];

            const typeScriptReadmeFileContents: string = await fs.readFile(typeScriptReadmeFilePath, 'utf8');
            const packageNames: string[] = getPackageNamesFromReadmeTypeScriptMdFileContents(typeScriptReadmeFileContents);
            _logger.logTrace(`Found [${packageNames}] package names`);

            if (contains(packageNames, packageName)) {
                foundPackage = true;
                packageFolderPath = getAbsolutePackageFolderPathFromReadmeFileContents(azureSDKForJSRepoRoot, typeScriptReadmeFileContents);
            }
        }

        if (!foundPackage) {
            _logger.logError(`No package found with the name "${packageName}".`);
        }
    }

    return packageFolderPath;
}

export async function getYamlSection(buffer: Buffer, sectionBeginning: string, sectionEnd: string): Promise<Buffer> {
    const beginningIndex = buffer.indexOf(sectionBeginning);
    const trimmedBuffer = buffer.slice(beginningIndex + (sectionBeginning.length));

    const endIndex = trimmedBuffer.indexOf(sectionEnd, 3);
    const sectionBuffer = trimmedBuffer.slice(0, endIndex);

    return sectionBuffer;
}

export async function doesReadmeMdFileSpecifiesTypescriptSdk(readmeMdPath: string): Promise<boolean> {
    const readmeMdBuffer = await fs.readFile(readmeMdPath);
    const sectionBuffer = await getYamlSection(readmeMdBuffer, "``` yaml $(swagger-to-sdk)", "```");

    if (sectionBuffer.includes("azure-sdk-for-js")) {
        return true;
    }

    return false;
}

export async function copyExistingNodeJsReadme(sdkPath: string): Promise<string> {
    const nodeJsReadmePath = path.resolve(sdkPath, "readme.nodejs.md");
    if (!(await pathExists(nodeJsReadmePath))) {
        return Promise.reject(`${nodeJsReadmePath} doesn't exists`)
    }

    const typescriptReadmePath = path.resolve(sdkPath, "readme.typescript.md");
    _logger.logDebug(`Copying ${nodeJsReadmePath} to ${typescriptReadmePath}`)

    if (await pathExists(typescriptReadmePath)) {
        return Promise.reject(`${typescriptReadmePath} file already exists`)
    }

    await fs.copyFile(nodeJsReadmePath, typescriptReadmePath);
    return typescriptReadmePath;
}

export async function getSinglePackageName(typescriptReadmePath: string): Promise<string> {
    const readmeBuffer: Buffer = await fs.readFile(typescriptReadmePath);
    const yamlSectionBuffer = await getYamlSection(readmeBuffer, "``` yaml $(typescript)", "```");
    const yamlSectionText = yamlSectionBuffer.toString();
    const yamlSection:any = yaml.safeLoad(yamlSectionText);
    return yamlSection["typescript"]["package-name"];
}

async function updatePackageName(settings: ReadmeSettings, sdkType: SdkType): Promise<ReadmeSettings> {
    if (settings.nodejs) {
        let packageName: string = settings.nodejs["package-name"]
        if (packageName.startsWith("azure-")) {
            packageName = packageName.replace("azure-", "");
        }

        if (sdkType == SdkType.ResourceManager && !packageName.startsWith("arm-")) {
            packageName = `arm-${packageName}`
        }

        settings.nodejs["package-name"] = `"@azure/${packageName}"`
    }
    return settings;
}

async function updateMetadataFields(settings: ReadmeSettings): Promise<ReadmeSettings> {
    if (settings.nodejs) {
        settings.nodejs["generate-metadata"] = true;
        delete settings.nodejs["generate-license-txt"]
        delete settings.nodejs["generate-package-json"]
        delete settings.nodejs["generate-readme-md"];
    }

    return settings;
}

function stripExtraQuotes(text: string): string {
    return text.replace(/'/g, "");
}

async function updateOutputFolder(settings: ReadmeSettings): Promise<ReadmeSettings> {
    if (settings.nodejs) {
        const outputName: string = settings.nodejs["package-name"].replace(/"/g, "");
        settings.nodejs["output-folder"] = `"$(typescript-sdks-folder)/packages/${outputName}"`;
    }
    return settings;
}

async function updateYamlSection(sectionText: string, sdkType: SdkType): Promise<string> {
    const section: any = yaml.safeLoad(sectionText);
    await updatePackageName(section, sdkType);
    await updateMetadataFields(section);
    await updateOutputFolder(section);
    section["typescript"] = section.nodejs;
    delete section.nodejs;

    return yaml.safeDump(section).trim();
}

export async function updateTypeScriptReadmeFile(typescriptReadmePath: string, sdkType: SdkType): Promise<string> {
    const readmeBuffer: Buffer = await fs.readFile(typescriptReadmePath);
    let outputReadme: string = readmeBuffer.toString();

    const yamlSection = await getYamlSection(readmeBuffer, "``` yaml $(nodejs)", "```");
    const sectionText = yamlSection.toString().trim();
    let updatedYamlSection = await updateYamlSection(sectionText, sdkType);
    updatedYamlSection = stripExtraQuotes(updatedYamlSection);

    outputReadme = outputReadme.replace(sectionText, updatedYamlSection);
    outputReadme = outputReadme.replace("azure-sdk-for-node", "azure-sdk-for-js");
    outputReadme = outputReadme.replace("Node.js", "TypeScript");
    outputReadme = outputReadme.replace("$(nodejs)", "$(typescript)");
    outputReadme = outputReadme.replace("nodejs", "typescript");
    outputReadme = outputReadme.replace("Node", "TypeScript");
    outputReadme = outputReadme.replace("node", "typescript");

    return outputReadme;
}

export async function updateMainReadmeFile(readmeFilePath: string) {
    const readmeBuffer: Buffer = await fs.readFile(readmeFilePath);
    let outputReadme: string = readmeBuffer.toString();

    const yamlSection = await getYamlSection(readmeBuffer, "``` yaml $(swagger-to-sdk)", "```");
    const sectionText = yamlSection.toString().trim();

    let lines = sectionText.split("\r\n");
    let nodeLineIndex = lines.findIndex(el => el.includes("- repo: azure-sdk-for-node"));

    if (nodeLineIndex == -1) {
        lines.push("  - repo: azure-sdk-for-node");
        nodeLineIndex = lines.length - 1;
    }

    const nodeLine = lines[nodeLineIndex];
    lines.splice(nodeLineIndex, 0, nodeLine.replace("node", "js"));
    const updatedYamlSection = lines.join("\r\n");

    outputReadme = outputReadme.replace(sectionText, updatedYamlSection);
    return outputReadme;
}

export function getPackageNamesFromReadmeTypeScriptMdFileContents(readmeTypeScriptMdFileContents: string): string[] {
    const packageNamePattern: RegExp = /package-name: (\S*)/g;
    const matches: string[] = readmeTypeScriptMdFileContents.match(packageNamePattern) || [];
    _logger.logTrace(`"package-name" matches: ${JSON.stringify(matches)}`);

    for (let i = 0; i < matches.length; ++i) {
        matches[i] = matches[i].substring("package-name: ".length);
    }

    const trimmedMatches = matches.map(match => match.replace(/\"/g, ""));
    _logger.logTrace(`"package-name" matches trimmed: ${JSON.stringify(trimmedMatches)}`);
    return trimmedMatches;
}

export function findReadmeTypeScriptMdFilePaths(azureRestAPISpecsRoot: string): string[] {
    _logger.logDebug(`Looking for "readme.typescript.md" files in "${azureRestAPISpecsRoot}"...`);

    const specificationFolderPath: string = path.resolve(azureRestAPISpecsRoot, 'specification');
    const readmeTypeScriptMdFilePaths: string[] = glob.sync(
        '**/readme.typescript.md',
        { absolute: true, cwd: specificationFolderPath }
    );
    if (readmeTypeScriptMdFilePaths) {
        for (let i = 0; i < readmeTypeScriptMdFilePaths.length; ++i) {
            const readmeTypeScriptMdFilePath: string = readmeTypeScriptMdFilePaths[i];
            _logger.logTrace(`  Found "${readmeTypeScriptMdFilePath}".`);

            if (readmeTypeScriptMdFilePath && !startsWith(readmeTypeScriptMdFilePath, specificationFolderPath)) {
                const resolvedReadmeTypeScriptMdFilePath: string = path.resolve(specificationFolderPath, readmeTypeScriptMdFilePath);
                _logger.logTrace(`    Updating to "${resolvedReadmeTypeScriptMdFilePath}".`);
                readmeTypeScriptMdFilePaths[i] = resolvedReadmeTypeScriptMdFilePath;
            }
        }
    }
    return readmeTypeScriptMdFilePaths;
}

export function getOutputFolderFromReadmeTypeScriptMdFileContents(readmeTypeScriptMdFileContents: string): string | undefined {
    const regExpMatch: RegExpMatchArray | null = readmeTypeScriptMdFileContents.match(/output-folder: (\S*)/);
    return regExpMatch && regExpMatch.length >= 1 ? regExpMatch[1].replace(/\"/g, "") : undefined;
}

export function getAbsolutePackageFolderPathFromReadmeFileContents(
    azureSDKForJSRepoRoot: string,
    typeScriptReadmeFileContents: string,
): string | undefined {
    const outputFolderPath: string | undefined = getOutputFolderFromReadmeTypeScriptMdFileContents(typeScriptReadmeFileContents);
    return !outputFolderPath ? outputFolderPath : outputFolderPath.replace("$(typescript-sdks-folder)", azureSDKForJSRepoRoot);
}