Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API generation script uses MDX components #1026

Merged
merged 29 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
59bce17
API generation script uses Function MDX component
arnaucasau Mar 13, 2024
06b72cb
add short name and exceptions
arnaucasau Mar 13, 2024
66227d1
Add all MDX components: class, propery, method, attribute, function, …
arnaucasau Mar 19, 2024
2dba29e
remove h3 and fix signatures
arnaucasau Mar 19, 2024
f50931e
fix method component
arnaucasau Mar 20, 2024
902f7ea
remove backticks and add copyright
arnaucasau Mar 20, 2024
d8a2b95
add back <h3>s
arnaucasau Mar 20, 2024
096e98b
fix method h3
arnaucasau Mar 20, 2024
388accb
incorporate feedback
arnaucasau Mar 21, 2024
e662550
fix signature
arnaucasau Mar 21, 2024
ee08ec2
fix UpperCase
arnaucasau Mar 21, 2024
f966534
remove undefined early return
arnaucasau Mar 22, 2024
ce842a6
change attributeType to attributeTypeHint
arnaucasau Mar 22, 2024
e91d5fd
Merge branch 'main' into AC/generate-Function-component
arnaucasau Mar 25, 2024
4039c0c
fix tests and move github links to prepareProps()
arnaucasau Mar 26, 2024
4c1fe88
Fix inlining
arnaucasau Mar 26, 2024
2ead845
rename tree variable
arnaucasau Mar 26, 2024
a728ebb
escape quotes and remove the unnecessary loop
arnaucasau Mar 27, 2024
f7ed13f
Merge branch 'main' into AC/generate-Function-component
arnaucasau Mar 27, 2024
aceb889
fix snapshot
arnaucasau Mar 27, 2024
2d7c177
Merge branch 'AC/generate-Function-component' of https://github.com/a…
arnaucasau Mar 27, 2024
d3b8195
fix snapshot and move tagName
arnaucasau Mar 27, 2024
8ed2097
fix tests
arnaucasau Mar 27, 2024
03e2fe1
make id optional
arnaucasau Mar 28, 2024
7ceb9ff
fix tests
arnaucasau Mar 28, 2024
3c46a3b
add isDedicatedPage prop and fix historical versions
arnaucasau Apr 2, 2024
92b29d6
fix tests
arnaucasau Apr 2, 2024
5b1c16d
Update scripts/lib/api/mergeClassMembers.ts
arnaucasau Apr 3, 2024
7f67604
early return
arnaucasau Apr 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 129 additions & 157 deletions scripts/lib/api/__snapshots__/conversionPipeline.test.ts.snap

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion scripts/lib/api/generateApiComponents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ describe("createOpeningTag()", () => {
name='run'
attributeTypeHint='undefined'
attributeValue='undefined'
isDedicatedPage='undefined'
github='undefined'
signature='Estimator.run(circuits, observables, parameter_values=None, **kwargs)'
extraSignatures='[]'
Expand All @@ -100,9 +101,10 @@ describe("createOpeningTag()", () => {
name='run'
attributeTypeHint='undefined'
attributeValue='undefined'
isDedicatedPage='undefined'
github='undefined'
signature='Estimator.run(circuits, observables, parameter_values=None, **kwargs)'
extraSignatures='[${APOSTROPHE_HEX_CODE}Estimator.run(circuits, observables, parameter_values=None, **kwargs)${APOSTROPHE_HEX_CODE}, ${APOSTROPHE_HEX_CODE}Estimator.run(circuits, observables, parameter_values=None, **kwargs)${APOSTROPHE_HEX_CODE}]'
extraSignatures='["Estimator.run(circuits, observables, parameter_values=None, **kwargs)", "Estimator.run(circuits, observables, parameter_values=None, **kwargs)"]'
>
`);
});
Expand All @@ -121,6 +123,7 @@ describe("createOpeningTag()", () => {
name='instance'
attributeTypeHint='str | None'
attributeValue='None'
isDedicatedPage='undefined'
github='undefined'
signature=''
extraSignatures='[]'
Expand All @@ -139,6 +142,7 @@ describe("createOpeningTag()", () => {
name='undefined'
attributeTypeHint='undefined'
attributeValue='undefined'
isDedicatedPage='undefined'
github='undefined'
signature=''
extraSignatures='[]'
Expand Down
243 changes: 235 additions & 8 deletions scripts/lib/api/generateApiComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,253 @@
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.

import { CheerioAPI, Cheerio } from "cheerio";
import { CheerioAPI, Cheerio, Element } from "cheerio";
import { unified } from "unified";
import rehypeParse from "rehype-parse";
import rehypeRemark from "rehype-remark";
import remarkStringify from "remark-stringify";

import { APOSTROPHE_HEX_CODE } from "../stringUtils";
import { ApiType } from "./Metadata";
import {
getLastPartFromFullIdentifier,
APOSTROPHE_HEX_CODE,
} from "../stringUtils";

export type ComponentProps = {
id: string;
id?: string;
name?: string;
attributeTypeHint?: string;
attributeValue?: string;
githubSourceLink?: string;
rawSignature?: string;
extraRawSignatures?: string[];
isDedicatedPage?: boolean;
};

const APITYPE_TO_TAG: Record<string, string> = {
class: "class",
exception: "class",
attribute: "attribute",
property: "attribute",
function: "function",
method: "function",
};

export async function processMdxComponent(
$: CheerioAPI,
$main: Cheerio<any>,
signatures: Cheerio<Element>[],
$dl: Cheerio<any>,
priorApiType: ApiType | undefined,
apiType: ApiType,
id: string,
): Promise<[string, string]> {
const tagName = APITYPE_TO_TAG[apiType];

const $firstSignature = signatures.shift()!;
const componentProps = prepareProps(
$,
$main,
$firstSignature,
$dl,
priorApiType,
apiType,
id,
);

const extraProps = signatures.flatMap(
($overloadedSignature) =>
prepareProps($, $main, $overloadedSignature, $dl, apiType, apiType, id) ??
[],
);
addExtraSignatures(componentProps, extraProps);

return [await createOpeningTag(tagName, componentProps), `</${tagName}>`];
}

// ------------------------------------------------------------------
// Prepare props for MDX components
// ------------------------------------------------------------------

function prepareProps(
$: CheerioAPI,
$main: Cheerio<any>,
$child: Cheerio<Element>,
$dl: Cheerio<any>,
priorApiType: ApiType | undefined,
apiType: ApiType,
id: string,
): ComponentProps {
const preparePropsPerApiType: Record<string, () => ComponentProps> = {
class: () => prepareClassProps($child, githubSourceLink, id),
property: () =>
preparePropertyProps($child, $dl, priorApiType, githubSourceLink, id),
method: () =>
prepareMethodProps($, $child, $dl, priorApiType, githubSourceLink, id),
attribute: () =>
prepareAttributeProps($, $child, $dl, priorApiType, githubSourceLink, id),
function: () =>
prepareFunctionOrExceptionProps($, $child, $dl, id, githubSourceLink),
exception: () =>
prepareFunctionOrExceptionProps($, $child, $dl, id, githubSourceLink),
};

const githubSourceLink = prepareGitHubLink($child, apiType === "method");
findByText($, $main, "em.property", apiType).remove();

if (!(apiType in preparePropsPerApiType)) {
throw new Error(`Unhandled Python type: ${apiType}`);
}

return preparePropsPerApiType[apiType]();
}

function prepareClassProps(
$child: Cheerio<any>,
githubSourceLink: string | undefined,
id: string,
): ComponentProps {
return {
id,
rawSignature: $child.html()!,
githubSourceLink,
};
}

function preparePropertyProps(
$child: Cheerio<any>,
$dl: Cheerio<any>,
priorApiType: string | undefined,
githubSourceLink: string | undefined,
id: string,
): ComponentProps {
const rawSignature = $child.find("em").text()?.replace(/^:\s+/, "");
const props = {
id,
name: getLastPartFromFullIdentifier(id),
rawSignature,
githubSourceLink,
};

if (!priorApiType && id) {
$dl.siblings("h1").text(getLastPartFromFullIdentifier(id));
return {
...props,
isDedicatedPage: true,
};
}

return props;
}

function prepareMethodProps(
$: CheerioAPI,
$child: Cheerio<any>,
$dl: Cheerio<any>,
priorApiType: string | undefined,
githubSourceLink: string | undefined,
id: string,
): ComponentProps {
const props = {
id,
name: getLastPartFromFullIdentifier(id),
rawSignature: $child.html()!,
githubSourceLink,
};

if (id) {
if (!priorApiType) {
$dl.siblings("h1").text(getLastPartFromFullIdentifier(id));
return {
...props,
isDedicatedPage: true,
};
} else if ($child.attr("id")) {
$(`<h3>${getLastPartFromFullIdentifier(id)}</h3>`).insertBefore($dl);
}
}
return props;
}

function prepareAttributeProps(
$: CheerioAPI,
$child: Cheerio<any>,
$dl: Cheerio<any>,
priorApiType: string | undefined,
githubSourceLink: string | undefined,
id: string,
): ComponentProps {
if (!priorApiType) {
if (id) {
$dl.siblings("h1").text(getLastPartFromFullIdentifier(id));
}
const rawSignature = $child.find("em").text()?.replace(/^:\s+/, "");
return {
id,
rawSignature,
githubSourceLink,
isDedicatedPage: true,
};
}

// 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;
}

$(`<h3>${getLastPartFromFullIdentifier(id)}</h3>`).insertBefore($dl);
// The attributes have the following shape: name [: type] [= value]
const name = text.slice(0, Math.min(colonIndex, equalIndex)).trim();
const attributeTypeHint = text
.slice(Math.min(colonIndex + 1, equalIndex), equalIndex)
.trim();
const attributeValue = text.slice(equalIndex, text.length).trim();

return {
id,
name,
attributeTypeHint,
attributeValue,
};
}

function prepareFunctionOrExceptionProps(
$: CheerioAPI,
$child: Cheerio<any>,
$dl: Cheerio<any>,
id: string,
githubSourceLink: string | undefined,
): ComponentProps {
const props = {
id,
name: getLastPartFromFullIdentifier(id),
rawSignature: $child.html()!,
githubSourceLink,
};

const pageHeading = $dl.siblings("h1").text();
if (id.endsWith(pageHeading) && pageHeading != "") {
// Page is already dedicated to apiType; no heading needed
return {
...props,
isDedicatedPage: true,
};
}
$(`<h3>${getLastPartFromFullIdentifier(id)}</h3>`).insertBefore($dl);

return props;
}

// ------------------------------------------------------------------
// Generate MDX components
// ------------------------------------------------------------------
Expand All @@ -52,18 +281,15 @@ export async function createOpeningTag(
const signature = await htmlSignatureToMd(props.rawSignature!);
const extraSignatures: string[] = [];
for (const sig of props.extraRawSignatures ?? []) {
extraSignatures.push(
`${APOSTROPHE_HEX_CODE}${await htmlSignatureToMd(
sig!,
)}${APOSTROPHE_HEX_CODE}`,
);
extraSignatures.push(`"${await htmlSignatureToMd(sig!)}"`);
Eric-Arellano marked this conversation as resolved.
Show resolved Hide resolved
}

return `<${tagName}
id='${props.id}'
name='${props.name}'
attributeTypeHint='${attributeTypeHint}'
attributeValue='${attributeValue}'
isDedicatedPage='${props.isDedicatedPage}'
github='${props.githubSourceLink}'
signature='${signature}'
extraSignatures='[${extraSignatures.join(", ")}]'
Expand Down Expand Up @@ -139,6 +365,7 @@ export async function htmlSignatureToMd(
return String(file)
.replaceAll("\n", "")
.replaceAll("'", APOSTROPHE_HEX_CODE)
.replaceAll('"', '\\"')
.replace(/^`/, "")
.replace(/`$/, "");
}
Loading
Loading