207 lines
6.3 KiB
TypeScript
207 lines
6.3 KiB
TypeScript
// This code is a Qiskit project.
|
|
//
|
|
// (C) Copyright IBM 2023.
|
|
//
|
|
// This code is licensed under the Apache License, Version 2.0. You may
|
|
// obtain a copy of this license in the LICENSE file in the root directory
|
|
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
|
|
//
|
|
// Any modifications or derivative works of this code must retain this
|
|
// copyright notice, and modified files need to carry a notice indicating
|
|
// that they have been altered from the originals.
|
|
|
|
import fs from "fs/promises";
|
|
|
|
import yargs from "yargs/yargs";
|
|
import { hideBin } from "yargs/helpers";
|
|
import grayMatter from "gray-matter";
|
|
import { globby } from "globby";
|
|
|
|
const ALLOWED_VIOLATIONS: Set<string> = new Set([...qiskitLegacyIgnores()]);
|
|
|
|
interface Arguments {
|
|
[x: string]: unknown;
|
|
apis: boolean;
|
|
translations: boolean;
|
|
}
|
|
|
|
const readArgs = (): Arguments => {
|
|
return yargs(hideBin(process.argv))
|
|
.version(false)
|
|
.option("apis", {
|
|
type: "boolean",
|
|
default: false,
|
|
description: "Check the API docs?",
|
|
})
|
|
.option("translations", {
|
|
type: "boolean",
|
|
default: false,
|
|
description: "Check the translations?",
|
|
})
|
|
.parseSync();
|
|
};
|
|
|
|
const readMetadata = async (filePath: string): Promise<Record<string, any>> => {
|
|
const ext = filePath.split(".").pop();
|
|
if (ext === "md" || ext === "mdx") {
|
|
const content = await fs.readFile(filePath, "utf-8");
|
|
return grayMatter(content).data;
|
|
} else if (ext === "ipynb") {
|
|
const content = await fs.readFile(filePath, "utf-8");
|
|
return JSON.parse(content).metadata;
|
|
} else {
|
|
throw new Error(`Unknown extension for ${filePath}: ${ext}`);
|
|
}
|
|
};
|
|
|
|
const isValidMetadata = (
|
|
metadata: Record<string, any>,
|
|
filePath: string,
|
|
): boolean =>
|
|
metadata.title &&
|
|
metadata.description &&
|
|
(filePath.startsWith("docs/api/") ||
|
|
(metadata.title != metadata.description &&
|
|
metadata.description.length <= 160 &&
|
|
metadata.description.length >= 50));
|
|
|
|
const main = async (): Promise<void> => {
|
|
const args = readArgs();
|
|
const [mdFiles, notebookFiles] = await determineFiles(args);
|
|
|
|
const mdErrors = [];
|
|
for (const file of mdFiles) {
|
|
if (ALLOWED_VIOLATIONS.has(file)) continue;
|
|
|
|
const metadata = await readMetadata(file);
|
|
if (!isValidMetadata(metadata, file)) {
|
|
mdErrors.push(file);
|
|
}
|
|
}
|
|
|
|
const notebookErrors = [];
|
|
for (const file of notebookFiles) {
|
|
if (ALLOWED_VIOLATIONS.has(file)) continue;
|
|
|
|
const metadata = await readMetadata(file);
|
|
if (!isValidMetadata(metadata, file)) {
|
|
notebookErrors.push(file);
|
|
}
|
|
}
|
|
|
|
handleErrors(mdErrors, notebookErrors);
|
|
};
|
|
|
|
async function determineFiles(args: Arguments): Promise<[string[], string[]]> {
|
|
const mdGlobs = ["docs/**/*.mdx"];
|
|
const notebookGlobs = ["docs/**/*.ipynb"];
|
|
if (!args.apis) {
|
|
const apiIgnore = `!docs/api/**/*`;
|
|
mdGlobs.push(apiIgnore);
|
|
notebookGlobs.push(apiIgnore);
|
|
}
|
|
if (args.translations) {
|
|
mdGlobs.push("translations/**/*.{md,mdx}");
|
|
notebookGlobs.push("translations/**/*.ipynb");
|
|
}
|
|
return [await globby(mdGlobs), await globby(notebookGlobs)];
|
|
}
|
|
|
|
function handleErrors(mdErrors: string[], notebookErrors: string[]): void {
|
|
if (mdErrors.length > 0) {
|
|
console.error(`
|
|
Invalid markdown file metadata. Every .mdx file should start with a metadata block like this:
|
|
|
|
---
|
|
title: Representing quantum computers
|
|
description: Learn about coupling maps, basis gates, and backend errors for transpiling
|
|
---
|
|
|
|
The title should be the page title: it's used for browser tabs and the top line of search results. The description should describe the page
|
|
in at least 50 but no more than 160 characters, ideally using some keywords. The description is what
|
|
shows up as the text in search results. See https://github.com/Qiskit/documentation/issues/131 for some tips.
|
|
|
|
Please fix these files:\n\n${mdErrors.join("\n")}
|
|
`);
|
|
}
|
|
if (notebookErrors.length > 0) {
|
|
console.error(`
|
|
Invalid Jupyter notebook metadata. Every .ipynb file needs to
|
|
set 'title' and 'description' in the file metadata. You need to
|
|
manually add this metadata. Furthermore, the length of the description
|
|
must be at least 50 but no more than 160 characters.
|
|
|
|
For example, if using VSCode, open up the file with
|
|
the "Open With..." option and then "Text Editor".
|
|
|
|
Once the file is open in text-mode, scroll down to the bottom of
|
|
the file for the top-level key "metadata". Be careful that this
|
|
is the metadata for the entire file and not a single code block.
|
|
You should see in the "metadata" section other entries like
|
|
"language_info" and "nbconvert_exporter".
|
|
|
|
Finally, add new keys in the "metadata" section for "title" and "description".
|
|
The title should be the page title: it's used for browser tabs
|
|
and the top line of search results. The description should describe the page
|
|
in at least 50 but no more than 160 characters, ideally using some keywords. The description is what
|
|
shows up as the text in search results. See
|
|
https://github.com/Qiskit/documentation/issues/131 for some tips.
|
|
|
|
For example:
|
|
|
|
"metadata": {
|
|
"description": "Get started using Qiskit with IBM Quantum hardware in this Hello World example",
|
|
"title": "Hello world",
|
|
"celltoolbar": "Raw Cell Format",
|
|
"kernelspec": { ...
|
|
}
|
|
|
|
Please fix these files:\n\n${notebookErrors.join("\n")}
|
|
`);
|
|
}
|
|
if (mdErrors.length > 0 || notebookErrors.length > 0) {
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
function qiskitLegacyIgnores(): string[] {
|
|
const versions = [
|
|
"0.19/",
|
|
"0.24/",
|
|
"0.25/",
|
|
"0.26/",
|
|
"0.27/",
|
|
"0.28/",
|
|
"0.29/",
|
|
"0.30/",
|
|
"0.31/",
|
|
"0.32/",
|
|
"0.33/",
|
|
"0.35/",
|
|
"0.36/",
|
|
"0.37/",
|
|
"0.38/",
|
|
"0.39/",
|
|
"0.40/",
|
|
"0.41/",
|
|
"0.42/",
|
|
"0.43/",
|
|
"0.44/",
|
|
"0.45/",
|
|
"0.46/",
|
|
];
|
|
return [
|
|
...versions.flatMap((vers) => [
|
|
`docs/api/qiskit/${vers}aer.mdx`,
|
|
`docs/api/qiskit/${vers}aqua.mdx`,
|
|
`docs/api/qiskit/${vers}ibmq-provider.mdx`,
|
|
`docs/api/qiskit/${vers}ibmq_jupyter.mdx`,
|
|
`docs/api/qiskit/${vers}ibmq_visualization.mdx`,
|
|
`docs/api/qiskit/${vers}parallel.mdx`,
|
|
`docs/api/qiskit/${vers}transpiler_builtin_plugins.mdx`,
|
|
]),
|
|
];
|
|
}
|
|
|
|
main().then(() => process.exit());
|