diff --git a/action.yml b/action.yml index 279606f..bbbbb8b 100644 --- a/action.yml +++ b/action.yml @@ -22,6 +22,9 @@ inputs: VALIDATE_MARKUP: description: Validate markup default: true + VALIDATE_WEBIDL: + description: Validate Web IDL + default: true GH_PAGES_BRANCH: description: Provide a branch name to deploy to GitHub pages. GH_PAGES_BUILD_OVERRIDE: @@ -110,6 +113,16 @@ runs: INPUTS_VALIDATE_MARKUP: ${{ fromJson(steps.prepare.outputs.validate).markup }} OUTPUTS_BUILD: ${{ toJson(fromJson(steps.build.outputs.w3c)) }} + - name: Validate Web IDL + run: | + echo "::group::Validate Web IDL" + node --enable-source-maps ${{ github.action_path }}/src/validate-webidl.js + echo "::endgroup::" + shell: bash + env: + INPUTS_VALIDATE_WEBIDL: ${{ fromJson(steps.prepare.outputs.validate).webidl }} + OUTPUTS_BUILD: ${{ toJson(fromJson(steps.build.outputs.w3c)) }} + - name: Deploy to GitHub pages run: | echo "::group::Deploy to GitHub pages" diff --git a/docs/examples.md b/docs/examples.md index b7ab85c..5ec239f 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -20,7 +20,7 @@ jobs: ### Selectively enable/disable validators -By default, both hyperlink and markup validators are enabled. +By default, both markup and Web IDL validators are enabled. ```yaml # Create a file called .github/workflows/auto-publish.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v2 - uses: w3c/spec-prod@v2 with: - VALIDATE_LINKS: false + VALIDATE_WEBIDL: false VALIDATE_MARKUP: true ``` diff --git a/docs/options.md b/docs/options.md index 98952de..9eacd12 100644 --- a/docs/options.md +++ b/docs/options.md @@ -98,6 +98,18 @@ The Action will try to make use of metadata/config from previously published ver # respec -s index.html?specStatus=WD&shortName=my-custom-shortname… -o OUTPUT ``` +## `VALIDATE_WEBIDL` + +Whether or not to validate the Web IDL that the spec may define. + +Spec authoring tools may already include some level of Web IDL validation but that validation may be restricted to detecting syntax errors. The action also checks additional constraints defined in [Web IDL](https://heycam.github.io/webidl/) such as usage of dictionaries as function parameters or attributes. The action will automatically skip validation if the spec does not define any Web IDL. + +Note that the Web IDL validation is restricted to the spec at hand and cannot validate that references to IDL constructs defined in other specs are valid. As such, there may remain IDL errors that can only be detected by tools that look at all specs in combination such as [Webref](https://github.com/w3c/webref)). + +**Possible values:** true, false + +**Default:** true + ## `VALIDATE_LINKS` Whether or not to check for broken hyperlinks. diff --git a/src/prepare-validate.ts b/src/prepare-validate.ts index bab6ded..2136b08 100644 --- a/src/prepare-validate.ts +++ b/src/prepare-validate.ts @@ -4,5 +4,6 @@ import { Inputs } from "./prepare.js"; export function validation(inputs: Inputs) { const links = yesOrNo(inputs.VALIDATE_LINKS) || false; const markup = yesOrNo(inputs.VALIDATE_MARKUP) || false; - return { links, markup }; + const webidl = yesOrNo(inputs.VALIDATE_WEBIDL) || false; + return { links, markup, webidl }; } diff --git a/src/prepare.ts b/src/prepare.ts index 9ac9c57..0d40d6b 100644 --- a/src/prepare.ts +++ b/src/prepare.ts @@ -21,6 +21,7 @@ export interface Inputs { BUILD_FAIL_ON: string; VALIDATE_LINKS: string; VALIDATE_MARKUP: string; + VALIDATE_WEBIDL: string; GH_PAGES_BRANCH: string; GH_PAGES_TOKEN: string; GH_PAGES_BUILD_OVERRIDE: string; diff --git a/src/validate-webidl.ts b/src/validate-webidl.ts new file mode 100644 index 0000000..c90fbfa --- /dev/null +++ b/src/validate-webidl.ts @@ -0,0 +1,50 @@ +import { env, exit, install, yesOrNo } from "./utils.js"; +import { BuildResult } from "./build.js"; +type Input = Pick; + +if (module === require.main) { + if (yesOrNo(env("INPUTS_VALIDATE_WEBIDL")) === false) { + exit("Skipped", 0); + } + + const input: Input = JSON.parse(env("OUTPUTS_BUILD")); + main(input).catch(err => exit(err.message || "Failed", err.code)); +} + +export default async function main({ dest, file }: Input) { + console.log(`Validating Web IDL defined in ${file}...`); + await install("reffy"); + const { crawlList } = require("reffy/src/cli/crawl-specs"); + + const fileurl = new URL(file, `file://${dest}/`).href; + const results = await crawlList( + [{ url: fileurl, nightly: { url: fileurl } }], + { modules: ["idl"] }, + ); + + const idl = results[0]?.idl?.idl; + if (!idl) { + exit("No Web IDL found in spec, skipped validation", 0); + } + + await install("webidl2"); + const { parse, validate } = require("webidl2"); + let errors: { message: string }[] = []; + try { + const tree = parse(idl); + errors = validate(tree); + } catch (error) { + errors = [error]; + } + if (!errors.length) { + exit("✅ Looks good! No Web IDL validation errors!", 0); + } else { + console.group("Invalid Web IDL detected:"); + for (const error of errors) { + console.log(error.message); + console.log(""); + } + console.groupEnd(); + exit("❌ Invalid Web IDL detected... please fix the issues above."); + } +} diff --git a/test/index.test.ts b/test/index.test.ts index bc8a376..ce7eb76 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -52,6 +52,7 @@ Promise.resolve() .then(run(require("./build.test.js").default)) .then(run(require("./validate-links.test.js").default)) .then(run(require("./validate-markup.test.js").default)) + .then(run(require("./validate-webidl.test.js").default)) .then(run(require("./deploy-gh-pages.test.js").default)) .then(() => { console.log(); diff --git a/test/validate-webidl.test.ts b/test/validate-webidl.test.ts new file mode 100644 index 0000000..ab23692 --- /dev/null +++ b/test/validate-webidl.test.ts @@ -0,0 +1,14 @@ +import main from "../src/validate-webidl.js"; +import { Outputs } from "./index.test.js"; + +export default async function validateWebIdl(outputs: Outputs) { + const { webidl: shouldValidate = false } = outputs?.prepare?.validate || {}; + if (shouldValidate === false) { + return; + } + + const { dest = process.cwd() + ".common", file = "index.html" } = + outputs?.build?.w3c || {}; + + return await main({ dest, file }); +}