Skip to content

Commit

Permalink
Split processMembersAndSetMeta function into helper functions (Qisk…
Browse files Browse the repository at this point in the history
…it#954)

## Summary

This PR splits the `processMembersAndSetMeta` function from the
`processHtml.ts` script into several helper functions to process each
`apiType`.

## Changes

These changes are done to the `map()` used to create the `replacement`
variable (line 291). The map used to have all the logic to process each
child depending on its `apiType`. Now the map starts by early returning
in the case we are not processing a `<dt>` tag and calling to a new
helper function to process an individual child.

Before the `map()` was setting the metadata, the refactor moved the code
outside of it, given that the metadata doesn't depend on the elements
iterated.

The new helper function is called `processMember` and starts by
preparing the GitHub source link, removing the `apiType` from the
`em.property` selector, and calling other new helper functions to
process each of our available `apiType`.

All the process `apiType` functions have the same code as before but
`processAttribute`, which has been refactored to find the name, type,
and default value of an attribute in a more concise way
  • Loading branch information
arnaucasau authored Mar 5, 2024
1 parent 8a2e97c commit a09d4bf
Showing 1 changed file with 158 additions and 100 deletions.
258 changes: 158 additions & 100 deletions scripts/lib/api/processHtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,127 +279,185 @@ export function processMembersAndSetMeta(
}

const $dl = $(dl);
const id = $dl.find("dt").attr("id") || "";
const apiType = getApiType($dl);
const priorApiType = meta.apiType;

if (!priorApiType) {
meta.apiType = apiType;
meta.apiName = id;
}

const replacement = $dl
.children()
.toArray()
.map((child) => {
const $child = $(child);
const id = $dl.find("dt").attr("id") || "";
const apiType = getApiType($dl);

const github = prepareGitHubLink($child, apiType === "method");
const githubSourceLink = prepareGitHubLink(
$child,
apiType === "method",
);

if (child.name !== "dt" || !apiType) {
return `<div>${$child.html()}</div>`;
}
return processMember(
$,
$main,
$child,
$dl,
priorApiType,
apiType,
id,
githubSourceLink,
);
})
.join("\n");

const priorApiType = meta.apiType;
if (!priorApiType) {
meta.apiType = apiType;
meta.apiName = id;
}
$dl.replaceWith(`<div>${replacement}</div>`);
}
}

if (apiType == "class") {
findByText($, $main, "em.property", "class").remove();
return `<span class="target" id="${id}"/><p><code>${$child.html()}</code>${github}</p>`;
}
function processMember(
$: CheerioAPI,
$main: Cheerio<any>,
$child: Cheerio<any>,
$dl: Cheerio<any>,
priorApiType: string | undefined,
apiType: string,
id: string,
githubSourceLink: string,
) {
findByText($, $main, "em.property", apiType).remove();

if (apiType == "class") {
return `<span class="target" id="${id}"/><p><code>${$child.html()}</code>${githubSourceLink}</p>`;
}

if (apiType == "property") {
if (!priorApiType && id) {
$dl.siblings("h1").text(getLastPartFromFullIdentifier(id));
}
if (apiType == "property") {
return processProperty($child, $dl, priorApiType, id, githubSourceLink);
}

findByText($, $main, "em.property", "property").remove();
const signature = $child.find("em").text()?.replace(/^:\s+/, "");
if (signature.trim().length === 0) return;
return `<span class="target" id='${id}'/><p><code>${signature}</code>${github}</p>`;
}
if (apiType == "method") {
return processMethod($, $child, $dl, priorApiType, id, githubSourceLink);
}

if (apiType == "method") {
if (id) {
if (!priorApiType) {
$dl.siblings("h1").text(getLastPartFromFullIdentifier(id));
} else if (!$child.attr("id")) {
// Overload methods have more than one <dt> tag, but only the first one
// contains an id.
return `<p><code>${$child.html()}</code>${github}</p>`;
} else {
// Inline methods
$(`<h3>${getLastPartFromFullIdentifier(id)}</h3>`).insertBefore(
$dl,
);
}
}

findByText($, $main, "em.property", "method").remove();
return `<span class="target" id='${id}'/><p><code>${$child.html()}</code>${github}</p>`;
}
if (apiType == "attribute") {
return processAttribute($child, $dl, priorApiType, id, githubSourceLink);
}

if (apiType == "attribute") {
if (!priorApiType) {
if (id) {
$dl.siblings("h1").text(getLastPartFromFullIdentifier(id));
}

findByText($, $main, "em.property", "attribute").remove();
const signature = $child.find("em").text()?.replace(/^:\s+/, "");
if (signature.trim().length === 0) return;
return `<span class="target" id='${id}'/><p><code>${signature}</code>${github}</p>`;
}

// Else, the attribute is embedded on the class
const text = $child.text();

// Index of the default value of the attribute
const equalIndex = text.indexOf("=");
// Index of the attribute's type. The type should be
// found before the default value
const colonIndex = text.slice(0, equalIndex).indexOf(":");

let name = text;
let type: string | undefined;
let value: string | undefined;
if (colonIndex > 0 && equalIndex > 0) {
name = text.substring(0, colonIndex);
type = text.substring(colonIndex + 1, equalIndex);
value = text.substring(equalIndex);
} else if (colonIndex > 0) {
name = text.substring(0, colonIndex);
type = text.substring(colonIndex + 1);
} else if (equalIndex > 0) {
name = text.substring(0, equalIndex);
value = text.substring(equalIndex);
}
const output = [`<span class="target" id='${id}'/><h3>${name}</h3>`];
if (type) {
output.push(`<p><code>${type}</code></p>`);
}
if (value) {
output.push(`<p><code>${value}</code></p>`);
}
return output.join("\n");
}
if (apiType === "function" || apiType === "exception") {
return processFunctionOrException($child, $dl, id, githubSourceLink);
}

if (apiType === "function" || apiType === "exception") {
findByText($, $main, "em.property", apiType).remove();
const descriptionHtml = `<span class="target" id="${id}"/><p><code>${$child.html()}</code>${github}</p>`;
throw new Error(`Unhandled Python type: ${apiType}`);
}

const pageHeading = $dl.siblings("h1").text();
if (id.endsWith(pageHeading) && pageHeading != "") {
// Page is already dedicated to apiType; no heading needed
return descriptionHtml;
}
function processProperty(
$child: Cheerio<any>,
$dl: Cheerio<any>,
priorApiType: string | undefined,
id: string,
githubSourceLink: string,
) {
if (!priorApiType && id) {
$dl.siblings("h1").text(getLastPartFromFullIdentifier(id));
}

const apiName = id.split(".").slice(-1)[0];
return `<h3>${apiName}</h3>${descriptionHtml}`;
}
const signature = $child.find("em").text()?.replace(/^:\s+/, "");
if (signature.trim().length === 0) return;
return `<span class="target" id='${id}'/><p><code>${signature}</code>${githubSourceLink}</p>`;
}

throw new Error(`Unhandled Python type: ${apiType}`);
})
.join("\n");
function processMethod(
$: CheerioAPI,
$child: Cheerio<any>,
$dl: Cheerio<any>,
priorApiType: string | undefined,
id: string,
githubSourceLink: string,
) {
if (id) {
if (!priorApiType) {
$dl.siblings("h1").text(getLastPartFromFullIdentifier(id));
} else if (!$child.attr("id")) {
// Overload methods have more than one <dt> tag, but only the first one
// contains an id.
return `<p><code>${$child.html()}</code>${githubSourceLink}</p>`;
} else {
// Inline methods
$(`<h3>${getLastPartFromFullIdentifier(id)}</h3>`).insertBefore($dl);
}
}

$dl.replaceWith(`<div>${replacement}</div>`);
return `<span class="target" id='${id}'/><p><code>${$child.html()}</code>${githubSourceLink}</p>`;
}

function processAttribute(
$child: Cheerio<any>,
$dl: Cheerio<any>,
priorApiType: string | undefined,
id: string,
githubSourceLink: string,
) {
if (!priorApiType) {
if (id) {
$dl.siblings("h1").text(getLastPartFromFullIdentifier(id));
}

const signature = $child.find("em").text()?.replace(/^:\s+/, "");
if (signature.trim().length === 0) return;
return `<span class="target" id='${id}'/><p><code>${signature}</code>${githubSourceLink}</p>`;
}

// Else, the attribute is embedded on the class
const text = $child.text();

// Index of the default value of the attribute
let equalIndex = text.indexOf("=");
if (equalIndex == -1) {
equalIndex = text.length;
}
// Index of the attribute's type. The type should be
// found before the default value
let colonIndex = text.slice(0, equalIndex).indexOf(":");
if (colonIndex == -1) {
colonIndex = text.length;
}

// The attributes have the following shape: name [: type] [= value]
const name = text.slice(0, Math.min(colonIndex, equalIndex)).trim();
const type = text
.slice(Math.min(colonIndex + 1, equalIndex), equalIndex)
.trim();
const value = text.slice(equalIndex, text.length).trim();

const output = [`<span class="target" id='${id}'/><h3>${name}</h3>`];
if (type) {
output.push(`<p><code>${type}</code></p>`);
}
if (value) {
output.push(`<p><code>${value}</code></p>`);
}
return output.join("\n");
}

function processFunctionOrException(
$child: Cheerio<any>,
$dl: Cheerio<any>,
id: string,
githubSourceLink: string,
) {
const descriptionHtml = `<span class="target" id="${id}"/><p><code>${$child.html()}</code>${githubSourceLink}</p>`;

const pageHeading = $dl.siblings("h1").text();
if (id.endsWith(pageHeading) && pageHeading != "") {
// Page is already dedicated to apiType; no heading needed
return descriptionHtml;
}

const apiName = id.split(".").slice(-1)[0];
return `<h3>${apiName}</h3>${descriptionHtml}`;
}

/**
Expand Down

0 comments on commit a09d4bf

Please sign in to comment.