From 7c38a6ea19c63c5ef8ac58b00d77a61d3df3c226 Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Wed, 14 Apr 2021 19:36:55 +0200 Subject: [PATCH 01/13] Prototype spec section from BCD spec_urls --- build/document-extractor.js | 233 ++++++++++++++++++----------- client/src/document/index.tsx | 5 + client/src/document/spec-table.tsx | 32 ++++ kumascript/macros/specs.ejs | 20 +++ 4 files changed, 202 insertions(+), 88 deletions(-) create mode 100644 client/src/document/spec-table.tsx create mode 100644 kumascript/macros/specs.ejs diff --git a/build/document-extractor.js b/build/document-extractor.js index 91f8a1e477ca..34d4a71eed26 100644 --- a/build/document-extractor.js +++ b/build/document-extractor.js @@ -110,16 +110,29 @@ function extractSections($) { * data: {....} * }] * - * At the time of writing (Jan 2020), there is only one single special type of - * section and that's BCD. The idea is we look for a bunch of special sections - * and if all else fails, just leave it as HTML as is. + * Another example is for specification tables. If the input is this: + * + *

Specifications

+ *
...
+ * + * Then, extract the data-bcd-query and return this: + * + * [{ + * type: "specifications", + * value: { + * query: "foo.bar.thing", + * id: "specifications", + * title: "Specifications", + * spec_urls: {....} + * }] */ function addSections($) { const flaws = []; - const countPotentialBCDDataDivs = $.find("div.bc-data").length; - if (countPotentialBCDDataDivs) { - /** If there's exactly 1 BCD table the only section to add is something + const countPotentialSpecialDivs = + $.find("div.bc-data").length + $.find("div.bc-specs").length; + if (countPotentialSpecialDivs) { + /** If there's exactly 1 special table the only section to add is something * like this: * { * "type": "browser_compatibility", @@ -132,8 +145,8 @@ function addSections($) { * * Where the 'title' and 'id' values comes from the

tag (if available). * - * However, if there are **multiple BCD tables**, which is rare, the it - * needs to return something like this: + * However, if there are **multiple special tables**, + * it needs to return something like this: * * [{ * "type": "prose", @@ -154,7 +167,7 @@ function addSections($) { * "content": "Any other stuff before table maybe" * }, */ - if (countPotentialBCDDataDivs > 1) { + if (countPotentialSpecialDivs > 1) { const subSections = []; const section = cheerio .load("
", { @@ -163,19 +176,20 @@ function addSections($) { .eq(0); // Loop over each and every "root element" in the node and keep piling - // them up in a buffer, until you encounter a `div.bc-table` then + // them up in a buffer, until you encounter a `div.bc-data` or `div.bc-specs` then // add that to the stack, clear and repeat. const iterable = [...$[0].childNodes]; let c = 0; - let countBCDDataDivsFound = 0; + let countSpecialDivsFound = 0; iterable.forEach((child) => { if ( child.tagName === "div" && child.attribs && child.attribs.class && - /bc-data/.test(child.attribs.class) + (child.attribs.class.includes("bc-data") || + child.attribs.class.includes("bc-specs")) ) { - countBCDDataDivsFound++; + countSpecialDivsFound++; if (c) { const [proseSections, proseFlaws] = _addSectionProse( section.clone() @@ -186,10 +200,10 @@ function addSections($) { c = 0; // reset the counter } section.append(child); - // XXX That `_addSingleSectionBCD(section.clone())` might return a + // XXX That `_addSingleSpecialSection(section.clone())` might return a // and empty array and that means it failed and we should // bail. - subSections.push(..._addSingleSectionBCD(section.clone())); + subSections.push(..._addSingleSpecialSection(section.clone())); section.empty(); } else { section.append(child); @@ -201,28 +215,29 @@ function addSections($) { subSections.push(...proseSections); flaws.push(...proseFlaws); } - if (countBCDDataDivsFound !== countPotentialBCDDataDivs) { - const leftoverCount = countPotentialBCDDataDivs - countBCDDataDivsFound; - const explanation = `${leftoverCount} 'div.bc-data' element${ + if (countSpecialDivsFound !== countPotentialSpecialDivs) { + const leftoverCount = countPotentialSpecialDivs - countSpecialDivsFound; + const explanation = `${leftoverCount} 'div.bc-data' or 'div.bc-specs' element${ leftoverCount > 1 ? "s" : "" } found but deeply nested.`; flaws.push(explanation); } return [subSections, flaws]; } - const bcdSections = _addSingleSectionBCD($); + const specialSections = _addSingleSpecialSection($); - // The _addSingleSectionBCD() function will have sucked up the

or

- // and the `div.bc-data` to turn it into a BCD section. + // The _addSingleSpecialSection() function will have sucked up the

or

+ // and the `div.bc-data` or `div.bc-specs` to turn it into a special section. // First remove that, then put whatever HTML is left as a prose // section underneath. $.find("div.bc-data, h2, h3").remove(); + $.find("div.bc-specs, h2, h3").remove(); const [proseSections, proseFlaws] = _addSectionProse($); - bcdSections.push(...proseSections); + specialSections.push(...proseSections); flaws.push(...proseFlaws); - if (bcdSections.length) { - return [bcdSections, flaws]; + if (specialSections.length) { + return [specialSections, flaws]; } } @@ -233,7 +248,7 @@ function addSections($) { return [proseSections, flaws]; } -function _addSingleSectionBCD($) { +function _addSingleSpecialSection($) { let id = null; let title = null; let isH3 = false; @@ -251,7 +266,16 @@ function _addSingleSectionBCD($) { } } - const dataQuery = $.find("div.bc-data").attr("id"); + let dataQuery = null; + let specialSectionType = null; + if ($.find("div.bc-data").length) { + specialSectionType = "browser_compatibility"; + dataQuery = $.find("div.bc-data").attr("id"); + } else if ($.find("div.bc-specs").length) { + specialSectionType = "specifications"; + dataQuery = $.find("div.bc-specs").attr("data-bcd-query"); + } + // Some old legacy documents haven't been re-rendered yet, since it // was added, so the `div.bc-data` tag doesn't have a `id="bcd:..."` // attribute. If that's the case, bail and fail back on a regular @@ -266,7 +290,7 @@ function _addSingleSectionBCD($) { if (data === undefined) { return [ { - type: "browser_compatibility", + type: specialSectionType, value: { title, id, @@ -279,77 +303,110 @@ function _addSingleSectionBCD($) { ]; } - // First extract a map of all release data, keyed by (normalized) browser - // name and the versions. - // You'll have a map that looks like this: - // - // 'chrome_android': { - // '28': { - // release_data: '2012-06-01', - // release_notes: '...', - // ... - // - // The reason we extract this to a locally scoped map, is so we can - // use it to augment the `__compat` blocks for the latest version - // when (if known) it was added. - const browserReleaseData = new Map(); - for (const [name, browser] of Object.entries(browsers)) { - const releaseData = new Map(); - for (const [version, data] of Object.entries(browser.releases || [])) { - if (data) { - releaseData.set(version, data); + if (specialSectionType === "browser_compatibility") { + // First extract a map of all release data, keyed by (normalized) browser + // name and the versions. + // You'll have a map that looks like this: + // + // 'chrome_android': { + // '28': { + // release_data: '2012-06-01', + // release_notes: '...', + // ... + // + // The reason we extract this to a locally scoped map, is so we can + // use it to augment the `__compat` blocks for the latest version + // when (if known) it was added. + const browserReleaseData = new Map(); + for (const [name, browser] of Object.entries(browsers)) { + const releaseData = new Map(); + for (const [version, data] of Object.entries(browser.releases || [])) { + if (data) { + releaseData.set(version, data); + } } + browserReleaseData.set(name, releaseData); } - browserReleaseData.set(name, releaseData); - } - for (const [key, compat] of Object.entries(data)) { - let block; - if (key === "__compat") { - block = compat; - } else if (compat.__compat) { - block = compat.__compat; - } - if (block) { - for (let [browser, info] of Object.entries(block.support)) { - // `info` here will be one of the following: - // - a single simple_support_statement: - // { version_added: 42 } - // - an array of simple_support_statements: - // [ { version_added: 42 }, { prefix: '-moz', version_added: 35 } ] - // - // Standardize the first version to an array of one, so we don't have - // to deal with two different forms below - if (!Array.isArray(info)) { - info = [info]; - } - for (const infoEntry of info) { - const added = infoEntry.version_added; - if (browserReleaseData.has(browser)) { - if (browserReleaseData.get(browser).has(added)) { - infoEntry.release_date = browserReleaseData - .get(browser) - .get(added).release_date; + for (const [key, compat] of Object.entries(data)) { + let block; + if (key === "__compat") { + block = compat; + } else if (compat.__compat) { + block = compat.__compat; + } + if (block) { + for (let [browser, info] of Object.entries(block.support)) { + // `info` here will be one of the following: + // - a single simple_support_statement: + // { version_added: 42 } + // - an array of simple_support_statements: + // [ { version_added: 42 }, { prefix: '-moz', version_added: 35 } ] + // + // Standardize the first version to an array of one, so we don't have + // to deal with two different forms below + if (!Array.isArray(info)) { + info = [info]; + } + for (const infoEntry of info) { + const added = infoEntry.version_added; + if (browserReleaseData.has(browser)) { + if (browserReleaseData.get(browser).has(added)) { + infoEntry.release_date = browserReleaseData + .get(browser) + .get(added).release_date; + } } } } } } - } - return [ - { - type: "browser_compatibility", - value: { - title, - id, - isH3, - data, - query, - browsers, + return [ + { + type: "browser_compatibility", + value: { + title, + id, + isH3, + data, + query, + browsers, + }, }, - }, - ]; + ]; + } else if (specialSectionType === "specifications") { + let spec_urls = []; + + for (const [key, compat] of Object.entries(data)) { + let block; + if (key === "__compat") { + block = compat; + } else if (compat.__compat) { + block = compat.__compat; + } + if (block && block.spec_url) { + if (Array.isArray(block.spec_url)) { + spec_urls = block.spec_url; + } else { + spec_urls.push(block.spec_url); + } + } + } + + return [ + { + type: "specifications", + value: { + title, + id, + isH3, + spec_urls, + query, + }, + }, + ]; + } } function _addSectionProse($) { diff --git a/client/src/document/index.tsx b/client/src/document/index.tsx index cc955807cc7c..bd348276f9e9 100644 --- a/client/src/document/index.tsx +++ b/client/src/document/index.tsx @@ -9,6 +9,7 @@ import { Doc } from "./types"; // Ingredients import { Prose, ProseWithHeading } from "./ingredients/prose"; import { LazyBrowserCompatibilityTable } from "./lazy-bcd-table"; +import { SpecificationTable } from "./spec-table"; // Misc // Sub-components @@ -232,6 +233,10 @@ function RenderDocumentBody({ doc }) { {...section.value} /> ); + } else if (section.type === "specifications") { + return ( + + ); } else { console.warn(section); throw new Error(`No idea how to handle a '${section.type}' section`); diff --git a/client/src/document/spec-table.tsx b/client/src/document/spec-table.tsx new file mode 100644 index 000000000000..0aba41cd78d5 --- /dev/null +++ b/client/src/document/spec-table.tsx @@ -0,0 +1,32 @@ +import { DisplayH2, DisplayH3 } from "./ingredients/utils"; + +export function SpecificationTable({ + id, + title, + isH3, + spec_urls, + query, +}: { + id: string; + title: string; + isH3: boolean; + spec_urls: []; + query: string; +}) { + return ( + <> + {title && !isH3 && } + {title && isH3 && } + + + + + + + + + +
Specification
{spec_urls}
+ + ); +} diff --git a/kumascript/macros/specs.ejs b/kumascript/macros/specs.ejs new file mode 100644 index 000000000000..644f01e806a9 --- /dev/null +++ b/kumascript/macros/specs.ejs @@ -0,0 +1,20 @@ +<% +/* +Placeholder to render a specification section with spec_urls from BCD + +Parameters + +$0 – A query string indicating for which feature to retrieve specification URLs for. + +Example calls + +{{specs}} +{{specs("html.element.abbr")}} + +*/ + +var query = $0 || env['browser-compat']; +var output = `
`; +%> + +<%-output%> From a30d53a89e8052f8f3886a42b26e1957d970fb16 Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Fri, 16 Apr 2021 14:20:39 +0200 Subject: [PATCH 02/13] Address some feedback, mostly rename things --- build/document-extractor.js | 14 ++++++-------- client/src/document/spec-table.tsx | 8 +++----- .../macros/{specs.ejs => SpecificationTable.ejs} | 3 +++ 3 files changed, 12 insertions(+), 13 deletions(-) rename kumascript/macros/{specs.ejs => SpecificationTable.ejs} (77%) diff --git a/build/document-extractor.js b/build/document-extractor.js index 34d4a71eed26..a46cc060c437 100644 --- a/build/document-extractor.js +++ b/build/document-extractor.js @@ -123,14 +123,13 @@ function extractSections($) { * query: "foo.bar.thing", * id: "specifications", * title: "Specifications", - * spec_urls: {....} + * specURLs: {....} * }] */ function addSections($) { const flaws = []; - const countPotentialSpecialDivs = - $.find("div.bc-data").length + $.find("div.bc-specs").length; + const countPotentialSpecialDivs = $.find("div.bc-data, div.bc-specs").length; if (countPotentialSpecialDivs) { /** If there's exactly 1 special table the only section to add is something * like this: @@ -376,7 +375,7 @@ function _addSingleSpecialSection($) { }, ]; } else if (specialSectionType === "specifications") { - let spec_urls = []; + let specURLs = []; for (const [key, compat] of Object.entries(data)) { let block; @@ -387,9 +386,9 @@ function _addSingleSpecialSection($) { } if (block && block.spec_url) { if (Array.isArray(block.spec_url)) { - spec_urls = block.spec_url; + specURLs = block.spec_url; } else { - spec_urls.push(block.spec_url); + specURLs.push(block.spec_url); } } } @@ -401,8 +400,7 @@ function _addSingleSpecialSection($) { title, id, isH3, - spec_urls, - query, + specURLs, }, }, ]; diff --git a/client/src/document/spec-table.tsx b/client/src/document/spec-table.tsx index 0aba41cd78d5..63249d9c44e3 100644 --- a/client/src/document/spec-table.tsx +++ b/client/src/document/spec-table.tsx @@ -4,14 +4,12 @@ export function SpecificationTable({ id, title, isH3, - spec_urls, - query, + specURLs, }: { id: string; title: string; isH3: boolean; - spec_urls: []; - query: string; + specURLs: string[]; }) { return ( <> @@ -24,7 +22,7 @@ export function SpecificationTable({ - {spec_urls} + {specURLs} diff --git a/kumascript/macros/specs.ejs b/kumascript/macros/SpecificationTable.ejs similarity index 77% rename from kumascript/macros/specs.ejs rename to kumascript/macros/SpecificationTable.ejs index 644f01e806a9..a35ede55838d 100644 --- a/kumascript/macros/specs.ejs +++ b/kumascript/macros/SpecificationTable.ejs @@ -14,6 +14,9 @@ Example calls */ var query = $0 || env['browser-compat']; +if (!query) { + throw new Error("No first query argument or 'browser-compat' front-matter value passed"); +} var output = `
`; %> From 20e7dfececdb289f54f532700b527b9c494094b0 Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Fri, 16 Apr 2021 14:48:33 +0200 Subject: [PATCH 03/13] Branch out building special sections in their own functions per Ryuno-Ki --- build/document-extractor.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/build/document-extractor.js b/build/document-extractor.js index a46cc060c437..dc0233356034 100644 --- a/build/document-extractor.js +++ b/build/document-extractor.js @@ -303,6 +303,12 @@ function _addSingleSpecialSection($) { } if (specialSectionType === "browser_compatibility") { + return _buildSpecialBCDSection(); + } else if (specialSectionType === "specifications") { + return _buildSpecialSpecSection(); + } + + function _buildSpecialBCDSection() { // First extract a map of all release data, keyed by (normalized) browser // name and the versions. // You'll have a map that looks like this: @@ -374,7 +380,16 @@ function _addSingleSpecialSection($) { }, }, ]; - } else if (specialSectionType === "specifications") { + } + + function _buildSpecialSpecSection() { + // Extract spec_url from a BCD feature. + // Can either be a string or an array of strings. + // Here, specURLs always returns an array of strings + + // TODO We want to display more than just the URL of a spec + // TODO Query https://github.com/w3c/browser-specs for spec name, spec status, etc. + let specURLs = []; for (const [key, compat] of Object.entries(data)) { From de444ec7280fcebf34bd960b5ee34b2b4936395f Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Fri, 16 Apr 2021 15:33:36 +0200 Subject: [PATCH 04/13] Add a KumaScript test --- kumascript/macros/SpecificationTable.ejs | 4 +++- .../tests/macros/SpecificationTable.test.js | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 kumascript/tests/macros/SpecificationTable.test.js diff --git a/kumascript/macros/SpecificationTable.ejs b/kumascript/macros/SpecificationTable.ejs index a35ede55838d..c4da78a08772 100644 --- a/kumascript/macros/SpecificationTable.ejs +++ b/kumascript/macros/SpecificationTable.ejs @@ -17,7 +17,9 @@ var query = $0 || env['browser-compat']; if (!query) { throw new Error("No first query argument or 'browser-compat' front-matter value passed"); } -var output = `
`; +var output = `
+ If you're able to see this, something went wrong on this page. +
`; %> <%-output%> diff --git a/kumascript/tests/macros/SpecificationTable.test.js b/kumascript/tests/macros/SpecificationTable.test.js new file mode 100644 index 000000000000..d88929c8ecc4 --- /dev/null +++ b/kumascript/tests/macros/SpecificationTable.test.js @@ -0,0 +1,24 @@ +const { assert, itMacro, describeMacro, lintHTML } = require("./utils"); + +const jsdom = require("jsdom"); +const { JSDOM } = jsdom; + +describeMacro("SpecificationTable", function () { + itMacro("Outputs a simple div tag", async (macro) => { + const result = await macro.call("api.feature"); + const dom = JSDOM.fragment(result); + assert.equal( + dom.querySelector("div.bc-specs").dataset.bcdQuery, + "api.feature" + ); + assert.equal( + dom.querySelector("div.bc-specs").textContent.trim(), + "If you're able to see this, something went wrong on this page." + ); + }); + + itMacro("Outputs valid HTML", async (macro) => { + const result = await macro.call("api.feature"); + expect(lintHTML(result)).toBeFalsy(); + }); +}); From c1f7fa76fa5d8e36d6e45a0aeb6a60da9be2e01d Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Fri, 16 Apr 2021 16:15:15 +0200 Subject: [PATCH 05/13] Add extraction test --- .../web/spec_table_extraction/index.html | 20 +++++++++++++++++++ testing/tests/index.test.js | 18 +++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 testing/content/files/en-us/web/spec_table_extraction/index.html diff --git a/testing/content/files/en-us/web/spec_table_extraction/index.html b/testing/content/files/en-us/web/spec_table_extraction/index.html new file mode 100644 index 000000000000..2b63ec8d2754 --- /dev/null +++ b/testing/content/files/en-us/web/spec_table_extraction/index.html @@ -0,0 +1,20 @@ +--- +title: Spec table extraction +browser-compat: javascript.operators.optional_chaining +slug: Web/Spec_Table_Extraction +--- + +

Intro

+

Text in Intro

+ +

Specifications

+ +

{{SpecificationTable}}

+ +

Browser compatibility

+ +

{{Compat}}

+ +

See also

+ +

More stuff

diff --git a/testing/tests/index.test.js b/testing/tests/index.test.js index 8ae99db637ea..a74d9c5c2042 100644 --- a/testing/tests/index.test.js +++ b/testing/tests/index.test.js @@ -1016,6 +1016,24 @@ test("bcd table extraction followed by h3", () => { expect(doc.body[4].value.isH3).toBeTruthy(); }); +test("specification table and bcd extraction", () => { + const builtFolder = path.join( + buildRoot, + "en-us", + "docs", + "web", + "spec_table_extraction" + ); + expect(fs.existsSync(builtFolder)).toBeTruthy(); + const jsonFile = path.join(builtFolder, "index.json"); + const { doc } = JSON.parse(fs.readFileSync(jsonFile)); + expect(doc.body[0].type).toBe("prose"); + expect(doc.body[1].type).toBe("specifications"); + expect(doc.body[2].type).toBe("prose"); + expect(doc.body[3].type).toBe("browser_compatibility"); + expect(doc.body[4].type).toBe("prose"); +}); + test("headers within non-root elements is a 'sectioning' flaw", () => { const builtFolder = path.join( buildRoot, From b1d4b8312cb42e6522d30acd38315d2725c5c199 Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Tue, 20 Apr 2021 16:24:59 +0200 Subject: [PATCH 06/13] Add browser-specs data; update tests; update renderer --- build/document-extractor.js | 39 ++++++----- client/src/document/spec-table.tsx | 67 ++++++++++++++----- kumascript/macros/SpecificationTable.ejs | 4 +- package.json | 1 + .../web/spec_table_extraction/index.html | 2 +- testing/tests/index.test.js | 12 ++++ yarn.lock | 5 ++ 7 files changed, 92 insertions(+), 38 deletions(-) diff --git a/build/document-extractor.js b/build/document-extractor.js index dc0233356034..761024b6f2d7 100644 --- a/build/document-extractor.js +++ b/build/document-extractor.js @@ -1,5 +1,6 @@ const cheerio = require("cheerio"); const { packageBCD } = require("./resolve-bcd"); +const specs = require("browser-specs"); /** Extract and mutate the $ if it as a "Quick_Links" section. * But only if it exists. @@ -383,31 +384,34 @@ function _addSingleSpecialSection($) { } function _buildSpecialSpecSection() { - // Extract spec_url from a BCD feature. + // Collect spec_urls from a BCD feature. // Can either be a string or an array of strings. - // Here, specURLs always returns an array of strings - - // TODO We want to display more than just the URL of a spec - // TODO Query https://github.com/w3c/browser-specs for spec name, spec status, etc. - let specURLs = []; for (const [key, compat] of Object.entries(data)) { - let block; - if (key === "__compat") { - block = compat; - } else if (compat.__compat) { - block = compat.__compat; - } - if (block && block.spec_url) { - if (Array.isArray(block.spec_url)) { - specURLs = block.spec_url; + if (key === "__compat" && compat.spec_url) { + if (Array.isArray(compat.spec_url)) { + specURLs = compat.spec_url; } else { - specURLs.push(block.spec_url); + specURLs.push(compat.spec_url); } } } + // Use BCD specURLs to look up more specification data + // from the browser-specs package + let specifications = []; + + specURLs.forEach((url) => { + let spec = specs.find( + (spec) => url.startsWith(spec.url) || url.startsWith(spec.nightly.url) + ); + if (spec) { + spec.bcdSpecificationURL = url; + specifications.push(spec); + } + }); + return [ { type: "specifications", @@ -415,7 +419,8 @@ function _addSingleSpecialSection($) { title, id, isH3, - specURLs, + specifications, + query, }, }, ]; diff --git a/client/src/document/spec-table.tsx b/client/src/document/spec-table.tsx index 63249d9c44e3..201c423c71b0 100644 --- a/client/src/document/spec-table.tsx +++ b/client/src/document/spec-table.tsx @@ -4,27 +4,58 @@ export function SpecificationTable({ id, title, isH3, - specURLs, + specifications, + query, }: { id: string; title: string; isH3: boolean; - specURLs: string[]; + specifications: Array<{ + title: string; + bcdSpecificationURL: string; + shortTitle: string; + }>; + query: string; }) { - return ( - <> - {title && !isH3 && } - {title && isH3 && } - - - - - - - - - -
Specification
{specURLs}
- - ); + if (specifications) { + const rows = specifications.map((spec) => ( + + + {spec.title} + + + )); + + return ( + <> + {title && !isH3 && } + {title && isH3 && } + + + + + + + {rows} +
Specification
+ + ); + } else { + return ( + <> + {title && !isH3 && } + {title && isH3 && } +

+ No specification data found for {query}.
+ Check for problems with this page or + contribute a missing spec_url to{" "} + + mdn/browser-compat-data + + . Also make sure the specification is included in{" "} + w3c/browser-specs. +

+ + ); + } } diff --git a/kumascript/macros/SpecificationTable.ejs b/kumascript/macros/SpecificationTable.ejs index c4da78a08772..db814031924b 100644 --- a/kumascript/macros/SpecificationTable.ejs +++ b/kumascript/macros/SpecificationTable.ejs @@ -8,8 +8,8 @@ $0 – A query string indicating for which feature to retrieve specification URL Example calls -{{specs}} -{{specs("html.element.abbr")}} +{{SpecificationTable}} +{{SpecificationTable("html.element.abbr")}} */ diff --git a/package.json b/package.json index c8ba4f7daf18..281070b3bf14 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@fast-csv/parse": "4.3.6", "@mdn/browser-compat-data": "3.2.4", "accept-language-parser": "1.5.0", + "browser-specs": "^1.34.2", "chalk": "4.1.0", "cheerio": "1.0.0-rc.6", "cli-progress": "^3.9.0", diff --git a/testing/content/files/en-us/web/spec_table_extraction/index.html b/testing/content/files/en-us/web/spec_table_extraction/index.html index 2b63ec8d2754..b25d06def344 100644 --- a/testing/content/files/en-us/web/spec_table_extraction/index.html +++ b/testing/content/files/en-us/web/spec_table_extraction/index.html @@ -1,6 +1,6 @@ --- title: Spec table extraction -browser-compat: javascript.operators.optional_chaining +browser-compat: javascript.builtins.Array.toLocaleString slug: Web/Spec_Table_Extraction --- diff --git a/testing/tests/index.test.js b/testing/tests/index.test.js index a74d9c5c2042..369dbf7e1b97 100644 --- a/testing/tests/index.test.js +++ b/testing/tests/index.test.js @@ -1029,6 +1029,18 @@ test("specification table and bcd extraction", () => { const { doc } = JSON.parse(fs.readFileSync(jsonFile)); expect(doc.body[0].type).toBe("prose"); expect(doc.body[1].type).toBe("specifications"); + expect(doc.body[1].value.specifications[0].url).toBe( + "https://tc39.es/ecma262/" + ); + expect(doc.body[1].value.specifications[0].bcdSpecificationURL).toBe( + "https://tc39.es/ecma262/#sec-array.prototype.tolocalestring" + ); + expect(doc.body[1].value.specifications[1].url).toBe( + "https://tc39.es/ecma402/" + ); + expect(doc.body[1].value.specifications[1].bcdSpecificationURL).toBe( + "https://tc39.es/ecma402/#sup-array.prototype.tolocalestring" + ); expect(doc.body[2].type).toBe("prose"); expect(doc.body[3].type).toBe("browser_compatibility"); expect(doc.body[4].type).toBe("prose"); diff --git a/yarn.lock b/yarn.lock index f9e91ee4b99b..441b76f0895a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5034,6 +5034,11 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== +browser-specs@^1.34.2: + version "1.34.2" + resolved "https://registry.yarnpkg.com/browser-specs/-/browser-specs-1.34.2.tgz#ecd889aedc785b56689d8e1ec7cc1b3e469fb137" + integrity sha512-ph6MN3/BcfMzh+hpLLt8rhxnpB287K4AUyKIV6oBygiXRr1MKPL2nrnAgHA01DzpbDj0CukD5YHc+53QsElrUw== + browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" From 6eba53d051bc1feff5853b358c7d90344f024518 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Wed, 21 Apr 2021 17:29:20 -0400 Subject: [PATCH 07/13] feedback on pr 3518 --- build/document-extractor.js | 75 ++++++++++++++++++++---------- client/src/document/spec-table.tsx | 67 +++++++++++++------------- 2 files changed, 84 insertions(+), 58 deletions(-) diff --git a/build/document-extractor.js b/build/document-extractor.js index 761024b6f2d7..cadf0d11a290 100644 --- a/build/document-extractor.js +++ b/build/document-extractor.js @@ -287,28 +287,44 @@ function _addSingleSpecialSection($) { } const query = dataQuery.replace(/^bcd:/, ""); const { browsers, data } = packageBCD(query); - if (data === undefined) { - return [ - { - type: specialSectionType, - value: { - title, - id, - isH3, - data: null, - query, - browsers: null, - }, - }, - ]; - } if (specialSectionType === "browser_compatibility") { + if (data === undefined) { + return [ + { + type: specialSectionType, + value: { + title, + id, + isH3, + data: null, + query, + browsers: null, + }, + }, + ]; + } return _buildSpecialBCDSection(); } else if (specialSectionType === "specifications") { + if (data === undefined) { + return [ + { + type: specialSectionType, + value: { + title, + id, + isH3, + query, + specifications: [], + }, + }, + ]; + } return _buildSpecialSpecSection(); } + throw new Error(`Unrecognized special section type '${specialSectionType}'`); + function _buildSpecialBCDSection() { // First extract a map of all release data, keyed by (normalized) browser // name and the versions. @@ -400,17 +416,26 @@ function _addSingleSpecialSection($) { // Use BCD specURLs to look up more specification data // from the browser-specs package - let specifications = []; + const specifications = specURLs + .map((specURL) => { + const spec = specs.find( + (spec) => + specURL.startsWith(spec.url) || specURL.startsWith(spec.nightly.url) + ); + if (spec) { + // We only want to return exactly the keys that we will use in the + // client code that renders this in React. + return { + bcdSpecificationURL: specURL, + title: spec.title, + shortTitle: spec.shortTitle, + }; + } + }) + .filter(Boolean); - specURLs.forEach((url) => { - let spec = specs.find( - (spec) => url.startsWith(spec.url) || url.startsWith(spec.nightly.url) - ); - if (spec) { - spec.bcdSpecificationURL = url; - specifications.push(spec); - } - }); + console.log("HERE!!"); + console.log(specifications); return [ { diff --git a/client/src/document/spec-table.tsx b/client/src/document/spec-table.tsx index 201c423c71b0..cc2e8450d7f5 100644 --- a/client/src/document/spec-table.tsx +++ b/client/src/document/spec-table.tsx @@ -17,45 +17,46 @@ export function SpecificationTable({ }>; query: string; }) { - if (specifications) { - const rows = specifications.map((spec) => ( - - - {spec.title} - - - )); + return ( + <> + {title && !isH3 && } + {title && isH3 && } - return ( - <> - {title && !isH3 && } - {title && isH3 && } + {/* XXX We could have a third condition; the specURL worked but yielded + exactly 0 specifications. If that's the case, perhaps the messaging + should be different. */} + {specifications.length > 0 ? ( - {rows} + + {specifications.map((spec) => ( + + + + ))} +
Specification
+ {spec.title} +
- - ); - } else { - return ( - <> - {title && !isH3 && } - {title && isH3 && } -

- No specification data found for {query}.
- Check for problems with this page or - contribute a missing spec_url to{" "} - - mdn/browser-compat-data - - . Also make sure the specification is included in{" "} - w3c/browser-specs. -

- - ); - } + ) : ( +
+

+ No specification data found for {query}.
+ Check for problems with this page or + contribute a missing spec_url to{" "} + + mdn/browser-compat-data + + . Also make sure the specification is included in{" "} + w3c/browser-specs + . +

+
+ )} + + ); } From 4193f3bee5c8e55ca34cf3b65ba9bd78e69d5dac Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Thu, 22 Apr 2021 15:24:42 +0200 Subject: [PATCH 08/13] Remove log calls, fix tests --- build/document-extractor.js | 3 --- client/src/document/spec-table.tsx | 4 +++- testing/tests/index.test.js | 8 +++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/build/document-extractor.js b/build/document-extractor.js index cadf0d11a290..a28b8bbb9f1e 100644 --- a/build/document-extractor.js +++ b/build/document-extractor.js @@ -434,9 +434,6 @@ function _addSingleSpecialSection($) { }) .filter(Boolean); - console.log("HERE!!"); - console.log(specifications); - return [ { type: "specifications", diff --git a/client/src/document/spec-table.tsx b/client/src/document/spec-table.tsx index cc2e8450d7f5..353cad864187 100644 --- a/client/src/document/spec-table.tsx +++ b/client/src/document/spec-table.tsx @@ -36,7 +36,9 @@ export function SpecificationTable({ {specifications.map((spec) => ( - {spec.title} + + {spec.title} ({spec.shortTitle}) + ))} diff --git a/testing/tests/index.test.js b/testing/tests/index.test.js index 3667cf21bfdc..428f7bad74dd 100644 --- a/testing/tests/index.test.js +++ b/testing/tests/index.test.js @@ -1032,14 +1032,12 @@ test("specification table and bcd extraction", () => { const { doc } = JSON.parse(fs.readFileSync(jsonFile)); expect(doc.body[0].type).toBe("prose"); expect(doc.body[1].type).toBe("specifications"); - expect(doc.body[1].value.specifications[0].url).toBe( - "https://tc39.es/ecma262/" - ); + expect(doc.body[1].value.specifications[0].shortTitle).toBe("ECMAScript"); expect(doc.body[1].value.specifications[0].bcdSpecificationURL).toBe( "https://tc39.es/ecma262/#sec-array.prototype.tolocalestring" ); - expect(doc.body[1].value.specifications[1].url).toBe( - "https://tc39.es/ecma402/" + expect(doc.body[1].value.specifications[1].shortTitle).toBe( + "ECMAScript Internationalization API" ); expect(doc.body[1].value.specifications[1].bcdSpecificationURL).toBe( "https://tc39.es/ecma402/#sup-array.prototype.tolocalestring" From 9f31dd13ced59b5c4f0ddfbe66cae9f1c7b6ee4c Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Tue, 27 Apr 2021 15:19:49 +0200 Subject: [PATCH 09/13] Rename SpecificationTable to SpecificationSection/Specifications --- build/document-extractor.js | 4 ++-- client/src/document/index.tsx | 4 ++-- client/src/document/{spec-table.tsx => spec-section.tsx} | 5 ++++- .../macros/{SpecificationTable.ejs => Specifications.ejs} | 4 ++-- .../{SpecificationTable.test.js => Specifications.test.js} | 2 +- .../index.html | 6 +++--- testing/tests/index.test.js | 4 ++-- 7 files changed, 16 insertions(+), 13 deletions(-) rename client/src/document/{spec-table.tsx => spec-section.tsx} (90%) rename kumascript/macros/{SpecificationTable.ejs => Specifications.ejs} (88%) rename kumascript/tests/macros/{SpecificationTable.test.js => Specifications.test.js} (93%) rename testing/content/files/en-us/web/{spec_table_extraction => spec_section_extraction}/index.html (75%) diff --git a/build/document-extractor.js b/build/document-extractor.js index a28b8bbb9f1e..06084aa704b6 100644 --- a/build/document-extractor.js +++ b/build/document-extractor.js @@ -111,7 +111,7 @@ function extractSections($) { * data: {....} * }] * - * Another example is for specification tables. If the input is this: + * Another example is for the specification section. If the input is this: * *

Specifications

*
...
@@ -124,7 +124,7 @@ function extractSections($) { * query: "foo.bar.thing", * id: "specifications", * title: "Specifications", - * specURLs: {....} + * specifications: {....} * }] */ function addSections($) { diff --git a/client/src/document/index.tsx b/client/src/document/index.tsx index bd348276f9e9..c0e540780a94 100644 --- a/client/src/document/index.tsx +++ b/client/src/document/index.tsx @@ -9,7 +9,7 @@ import { Doc } from "./types"; // Ingredients import { Prose, ProseWithHeading } from "./ingredients/prose"; import { LazyBrowserCompatibilityTable } from "./lazy-bcd-table"; -import { SpecificationTable } from "./spec-table"; +import { SpecificationSection } from "./spec-section"; // Misc // Sub-components @@ -235,7 +235,7 @@ function RenderDocumentBody({ doc }) { ); } else if (section.type === "specifications") { return ( - + ); } else { console.warn(section); diff --git a/client/src/document/spec-table.tsx b/client/src/document/spec-section.tsx similarity index 90% rename from client/src/document/spec-table.tsx rename to client/src/document/spec-section.tsx index 353cad864187..5c8c83727a0d 100644 --- a/client/src/document/spec-table.tsx +++ b/client/src/document/spec-section.tsx @@ -1,6 +1,6 @@ import { DisplayH2, DisplayH3 } from "./ingredients/utils"; -export function SpecificationTable({ +export function SpecificationSection({ id, title, isH3, @@ -38,6 +38,8 @@ export function SpecificationTable({ {spec.title} ({spec.shortTitle}) +
{" "} + #{spec.bcdSpecificationURL.split("#")[1]}
@@ -46,6 +48,7 @@ export function SpecificationTable({ ) : (
+

No specification found

No specification data found for {query}.
Check for problems with this page or diff --git a/kumascript/macros/SpecificationTable.ejs b/kumascript/macros/Specifications.ejs similarity index 88% rename from kumascript/macros/SpecificationTable.ejs rename to kumascript/macros/Specifications.ejs index db814031924b..149ae710502f 100644 --- a/kumascript/macros/SpecificationTable.ejs +++ b/kumascript/macros/Specifications.ejs @@ -8,8 +8,8 @@ $0 – A query string indicating for which feature to retrieve specification URL Example calls -{{SpecificationTable}} -{{SpecificationTable("html.element.abbr")}} +{{Specifications}} +{{Specifications("html.element.abbr")}} */ diff --git a/kumascript/tests/macros/SpecificationTable.test.js b/kumascript/tests/macros/Specifications.test.js similarity index 93% rename from kumascript/tests/macros/SpecificationTable.test.js rename to kumascript/tests/macros/Specifications.test.js index d88929c8ecc4..ffff3ed11f6b 100644 --- a/kumascript/tests/macros/SpecificationTable.test.js +++ b/kumascript/tests/macros/Specifications.test.js @@ -3,7 +3,7 @@ const { assert, itMacro, describeMacro, lintHTML } = require("./utils"); const jsdom = require("jsdom"); const { JSDOM } = jsdom; -describeMacro("SpecificationTable", function () { +describeMacro("Specifications", function () { itMacro("Outputs a simple div tag", async (macro) => { const result = await macro.call("api.feature"); const dom = JSDOM.fragment(result); diff --git a/testing/content/files/en-us/web/spec_table_extraction/index.html b/testing/content/files/en-us/web/spec_section_extraction/index.html similarity index 75% rename from testing/content/files/en-us/web/spec_table_extraction/index.html rename to testing/content/files/en-us/web/spec_section_extraction/index.html index b25d06def344..927474afb5ad 100644 --- a/testing/content/files/en-us/web/spec_table_extraction/index.html +++ b/testing/content/files/en-us/web/spec_section_extraction/index.html @@ -1,7 +1,7 @@ --- -title: Spec table extraction +title: Spec section extraction browser-compat: javascript.builtins.Array.toLocaleString -slug: Web/Spec_Table_Extraction +slug: Web/Spec_Section_Extraction ---

Intro

@@ -9,7 +9,7 @@

Intro

Specifications

-

{{SpecificationTable}}

+

{{Specifications}}

Browser compatibility

diff --git a/testing/tests/index.test.js b/testing/tests/index.test.js index 428f7bad74dd..71e637304858 100644 --- a/testing/tests/index.test.js +++ b/testing/tests/index.test.js @@ -1019,13 +1019,13 @@ test("bcd table extraction followed by h3", () => { expect(doc.body[4].value.isH3).toBeTruthy(); }); -test("specification table and bcd extraction", () => { +test("specifications and bcd extraction", () => { const builtFolder = path.join( buildRoot, "en-us", "docs", "web", - "spec_table_extraction" + "spec_section_extraction" ); expect(fs.existsSync(builtFolder)).toBeTruthy(); const jsonFile = path.join(builtFolder, "index.json"); From 450c5b2b26ab8e322c4d9ef2c342532d76bc6854 Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Tue, 27 Apr 2021 15:25:53 +0200 Subject: [PATCH 10/13] Update browser-specs to latest --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index dbb57aed7eb2..594c597dedd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5414,9 +5414,9 @@ browser-process-hrtime@^1.0.0: integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== browser-specs@^1.34.2: - version "1.34.2" - resolved "https://registry.yarnpkg.com/browser-specs/-/browser-specs-1.34.2.tgz#ecd889aedc785b56689d8e1ec7cc1b3e469fb137" - integrity sha512-ph6MN3/BcfMzh+hpLLt8rhxnpB287K4AUyKIV6oBygiXRr1MKPL2nrnAgHA01DzpbDj0CukD5YHc+53QsElrUw== + version "1.35.1" + resolved "https://registry.yarnpkg.com/browser-specs/-/browser-specs-1.35.1.tgz#01c77221940b5d733995248438e869ca5342cc9c" + integrity sha512-y9rMHjHa2kXUOBqovbRHCQAQhCJARiPQYluiO3PBoBl4Wa7f0ukE72+zDBN7+0oYzdRyWngZaUtl2rqCVdZ1Aw== browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" From 780327214565652c89adbfbf41f2c7c5e7644f4b Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Thu, 29 Apr 2021 13:00:26 +0200 Subject: [PATCH 11/13] Add specfications to makeTOC --- build/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/index.js b/build/index.js index 53d67b61d38a..d6fc4522bf0f 100644 --- a/build/index.js +++ b/build/index.js @@ -212,7 +212,8 @@ function makeTOC(doc) { .map((section) => { if ( (section.type === "prose" || - section.type === "browser_compatibility") && + section.type === "browser_compatibility" || + section.type === "specifications") && section.value.id && section.value.title && !section.value.isH3 From 39f79959dfe12b86b676545def8d55b543bfa59c Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Thu, 29 Apr 2021 13:04:37 +0200 Subject: [PATCH 12/13] Move spec-section to ingredients --- client/src/document/index.tsx | 2 +- client/src/document/{ => ingredients}/spec-section.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename client/src/document/{ => ingredients}/spec-section.tsx (96%) diff --git a/client/src/document/index.tsx b/client/src/document/index.tsx index c0e540780a94..27ebb59a79a6 100644 --- a/client/src/document/index.tsx +++ b/client/src/document/index.tsx @@ -9,7 +9,7 @@ import { Doc } from "./types"; // Ingredients import { Prose, ProseWithHeading } from "./ingredients/prose"; import { LazyBrowserCompatibilityTable } from "./lazy-bcd-table"; -import { SpecificationSection } from "./spec-section"; +import { SpecificationSection } from "./ingredients/spec-section"; // Misc // Sub-components diff --git a/client/src/document/spec-section.tsx b/client/src/document/ingredients/spec-section.tsx similarity index 96% rename from client/src/document/spec-section.tsx rename to client/src/document/ingredients/spec-section.tsx index 5c8c83727a0d..33cb026f65c9 100644 --- a/client/src/document/spec-section.tsx +++ b/client/src/document/ingredients/spec-section.tsx @@ -1,4 +1,4 @@ -import { DisplayH2, DisplayH3 } from "./ingredients/utils"; +import { DisplayH2, DisplayH3 } from "./utils"; export function SpecificationSection({ id, From b233777879bd545a18fff8f956db584128508c95 Mon Sep 17 00:00:00 2001 From: Florian Scholz Date: Fri, 30 Apr 2021 11:45:06 +0200 Subject: [PATCH 13/13] Remove comment --- client/src/document/ingredients/spec-section.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/src/document/ingredients/spec-section.tsx b/client/src/document/ingredients/spec-section.tsx index 33cb026f65c9..c1352568281d 100644 --- a/client/src/document/ingredients/spec-section.tsx +++ b/client/src/document/ingredients/spec-section.tsx @@ -22,9 +22,6 @@ export function SpecificationSection({ {title && !isH3 && } {title && isH3 && } - {/* XXX We could have a third condition; the specURL worked but yielded - exactly 0 specifications. If that's the case, perhaps the messaging - should be different. */} {specifications.length > 0 ? (