Skip to content

Commit 97d537f

Browse files
authored
Add script to inspect compat key notes (web-platform-dx#3294)
1 parent 74c9d0e commit 97d537f

File tree

5 files changed

+290
-20
lines changed

5 files changed

+290
-20
lines changed

index.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,9 @@ import path from 'path';
44
import { Temporal } from '@js-temporal/polyfill';
55
import { fdir } from 'fdir';
66
import YAML from 'yaml';
7+
import { convertMarkdown } from "./text";
78
import { FeatureData, GroupData, SnapshotData, WebFeaturesData } from './types';
89

9-
import { toString as hastTreeToString } from 'hast-util-to-string';
10-
import rehypeStringify from 'rehype-stringify';
11-
import remarkParse from 'remark-parse';
12-
import remarkRehype from 'remark-rehype';
13-
import { unified } from 'unified';
14-
1510
import { BASELINE_LOW_TO_HIGH_DURATION, coreBrowserSet, parseRangedDateString } from 'compute-baseline';
1611
import { Compat } from 'compute-baseline/browser-compat-data';
1712

@@ -121,20 +116,6 @@ function* identifiers(value) {
121116
}
122117
}
123118

124-
function convertMarkdown(markdown: string) {
125-
const mdTree = unified().use(remarkParse).parse(markdown);
126-
const htmlTree = unified().use(remarkRehype).runSync(mdTree);
127-
const text = hastTreeToString(htmlTree);
128-
129-
let html = unified().use(rehypeStringify).stringify(htmlTree);
130-
// Remove leading <p> and trailing </p> if there is only one of each in the
131-
// description. (If there are multiple paragraphs, let them be.)
132-
if (html.lastIndexOf('<p>') === 0 && html.indexOf('</p>') === html.length - 4) {
133-
html = html.substring(3, html.length - 4);
134-
}
135-
136-
return { text, html };
137-
}
138119

139120
// Map from BCD keys/paths to web-features identifiers.
140121
const bcdToFeatureId: Map<string, string> = new Map();

package-lock.json

Lines changed: 123 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@
4848
"caniuse-lite": "^1.0.30001737",
4949
"cheerio": "^1.1.2",
5050
"diff": "^8.0.2",
51+
"escape-html": "^1.0.3",
5152
"eslint-plugin-new-with-error": "^5.0.0",
5253
"fast-json-stable-stringify": "^2.1.0",
5354
"fdir": "^6.5.0",
5455
"hast-util-to-string": "^3.0.1",
5556
"prettier": "^3.6.2",
57+
"rehype-parse": "^9.0.1",
5658
"rehype-stringify": "^10.0.1",
5759
"remark-parse": "^11.0.0",
5860
"remark-rehype": "^11.1.2",

scripts/inspect-feature.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { coreBrowserSet, getStatus } from "compute-baseline";
2+
import escapeHtml from "escape-html";
3+
import fs from "node:fs";
4+
import path from "node:path";
5+
import { fileURLToPath } from "node:url";
6+
import YAML from "yaml";
7+
import yargs from "yargs";
8+
import { features } from "..";
9+
import { defaultCompat } from "../packages/compute-baseline/dist/browser-compat-data/compat";
10+
import { feature } from "../packages/compute-baseline/dist/browser-compat-data/feature";
11+
import { SupportStatement } from "../packages/compute-baseline/dist/browser-compat-data/supportStatements";
12+
import { convertHTML } from "../text";
13+
import { FeatureData } from "../types";
14+
15+
interface Args {
16+
paths: string[];
17+
verbose: number;
18+
}
19+
20+
const argv = yargs(process.argv.slice(2))
21+
.scriptName("dist")
22+
.usage("$0 [paths..]", "Inspect a .yml or .dist file", (yargs) =>
23+
yargs.positional("paths", {
24+
describe: "Directories or files to inspect.",
25+
default: ["features/a.yml"],
26+
}),
27+
)
28+
.option("verbose", {
29+
alias: "v",
30+
describe: "Show more detail",
31+
type: "count",
32+
default: 0,
33+
defaultDescription: "warn",
34+
}).argv as Args;
35+
36+
function main(): void {
37+
for (const filePath of argv.paths) {
38+
const { name, ext, dir } = path.parse(filePath);
39+
const id = ext === ".dist" ? path.basename(name, ".yml") : name;
40+
41+
const yamlFile = path.join(dir, `${id}.yml`);
42+
const distFile = `${yamlFile}.dist`;
43+
44+
const feature = features[id];
45+
if (!feature) {
46+
throw new Error(`No feature found for ID ${id}`);
47+
}
48+
49+
const { compat_features } = feature;
50+
51+
console.log(`${escapeHtml(feature.name)} (${yamlFile}, ${distFile})\n`);
52+
53+
// Iterate over the compat keys in the order given by the dist file
54+
const yml = YAML.parse(
55+
fs.readFileSync(distFile, { encoding: "utf-8" }),
56+
) as Partial<FeatureData>;
57+
let compatKeys: string[];
58+
if (yml.compat_features?.length > 0) {
59+
compatKeys = yml.compat_features;
60+
} else if (compat_features && compat_features.length > 0) {
61+
compatKeys = compat_features;
62+
} else {
63+
compatKeys = [];
64+
}
65+
66+
for (const compatKey of compatKeys) {
67+
const status = getStatus(id, compatKey);
68+
console.log(
69+
`- ${compatKey}${status.baseline ? ` (${status.baseline})` : ""}`,
70+
);
71+
for (const [browser, notes] of getNotes(compatKey).entries()) {
72+
console.log(` - ${browser}`);
73+
for (const note of notes) {
74+
console.log(` - ${convertHTML(note).text}`);
75+
}
76+
}
77+
}
78+
}
79+
}
80+
81+
function getNotes(compatKey: string): Map<string, string[]> {
82+
const browserNotes = new Map<string, string[]>();
83+
const f = feature(compatKey);
84+
for (const browserKey of coreBrowserSet) {
85+
const statements = f
86+
.supportStatements(defaultCompat.browser(browserKey))
87+
.filter(isEligibleStatement);
88+
for (const statement of statements) {
89+
const notes = Array.isArray(statement.data.notes!)
90+
? statement.data.notes
91+
: [statement.data.notes];
92+
for (const note of notes) {
93+
browserNotes.set(statement.browser.id, [
94+
...(browserNotes.get(statement.browser.id) ?? []),
95+
`${statement.partial_implementation ? `[partial_implementation] ` : ""}${statement.version_added}+: ${note}`,
96+
]);
97+
}
98+
}
99+
}
100+
return browserNotes;
101+
}
102+
103+
function isEligibleStatement(statement: SupportStatement): boolean {
104+
if (statement.version_added === false) {
105+
return false;
106+
}
107+
if (statement.version_removed) {
108+
return false;
109+
}
110+
if (statement.flags.length) {
111+
return false;
112+
}
113+
if (statement.data.alternative_name) {
114+
return false;
115+
}
116+
if (statement.data.prefix) {
117+
return false;
118+
}
119+
const { notes } = statement.data;
120+
if (notes === undefined) {
121+
return false;
122+
}
123+
if (notes.length === 0) {
124+
return false;
125+
}
126+
return true;
127+
}
128+
129+
if (import.meta.url.startsWith("file:")) {
130+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
131+
main();
132+
}
133+
}

text.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { toString as hastTreeToString } from "hast-util-to-string";
2+
import rehypeParse from "rehype-parse";
3+
import rehypeStringify from "rehype-stringify";
4+
import remarkParse from "remark-parse";
5+
import remarkRehype from "remark-rehype";
6+
import { unified } from "unified";
7+
8+
export function convertMarkdown(markdown: string) {
9+
const mdTree = unified().use(remarkParse).parse(markdown);
10+
const htmlTree = unified().use(remarkRehype).runSync(mdTree);
11+
const text = hastTreeToString(htmlTree);
12+
13+
let html = unified().use(rehypeStringify).stringify(htmlTree);
14+
// Remove leading <p> and trailing </p> if there is only one of each in the
15+
// description. (If there are multiple paragraphs, let them be.)
16+
if (
17+
html.lastIndexOf("<p>") === 0 &&
18+
html.indexOf("</p>") === html.length - 4
19+
) {
20+
html = html.substring(3, html.length - 4);
21+
}
22+
23+
return { text, html };
24+
}
25+
26+
export function convertHTML(html: string) {
27+
const htmlTree = unified().use(rehypeParse).parse(html);
28+
const text = hastTreeToString(htmlTree);
29+
30+
return { text };
31+
}

0 commit comments

Comments
 (0)