Skip to content

Commit

Permalink
⚒ script for running API Extractor in Github Actions (react-hook-form…
Browse files Browse the repository at this point in the history
…#7407)

* Implement script for running API Extractor in CI

* Make it obvious what the failure reason is

* Speed up api-extractor workflow

* Color FAILURE REASON red
  • Loading branch information
felixschorer authored Jan 1, 2022
1 parent 6129230 commit 9174754
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/api-extrator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
uses: bahmutov/npm-install@v1

- name: Build
run: yarn build
run: yarn build:esm

- name: API Extractor
run: yarn api-extractor:ci
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"cypress:open": "cypress open",
"api-extractor": "api-extractor run --local",
"api-extractor:build": "yarn build:esm && yarn api-extractor",
"api-extractor:ci": "api-extractor run",
"api-extractor:ci": "node scripts/apiExtractor.js",
"postversion": "git push && git push origin v$npm_package_version",
"prepublishOnly": "yarn && yarn lint:fix && yarn type && yarn test && yarn build",
"bundlesize": "yarn build:modern && bundlesize",
Expand Down
127 changes: 127 additions & 0 deletions scripts/apiExtractor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const { Extractor, ExtractorConfig } = require('@microsoft/api-extractor');
const fs = require('fs');
const path = require('path');

/**
* Command which developers need to run to generate an updated API report.
*/
const API_EXTRACTOR_YARN_COMMAND = 'yarn api-extractor:build';

const config = loadExtractorConfig();
checkReportMatchesApi(config);
checkNoErrorsAndWarnings(config);
checkLineEndings(config);

/**
* Runs the API Extractor to check if the API report matches the API.
* Exits with status code 1 if it does not match.
* @param config {ExtractorConfig}
*/
function checkReportMatchesApi(config) {
const result = Extractor.invoke(config, {
localBuild: false, // validate report, fail on warnings or errors
messageCallback: (message) => {
// suppress all error or warnings
message.handled = true;
},
});

if (result.apiReportChanged) {
exit(
`The API Extractor report does not match the exported API.`,
`Please run \`${API_EXTRACTOR_YARN_COMMAND}\` to generate an`,
`updated report and commit it.`,
);
}
}

/**
* Runs the API Extractor.
* Exits with status code 1 if API Extractor reports any warnings or errors.
* @param config {ExtractorConfig}
*/
function checkNoErrorsAndWarnings(config) {
const result = Extractor.invoke(config, {
localBuild: false, // validate report, fail on warnings or errors,
});

if (!result.succeeded) {
exit(
`API Extractor completed with ${result.errorCount} errors and`,
`${result.warningCount} warnings.`,
);
}
}

/**
* Checks the line endings of the API extractor report.
* Exits with status code 1 if the line endings don't match the
* API Extractor config.
* @param config {ExtractorConfig}
*/
function checkLineEndings(config) {
const report = fs.readFileSync(config.reportFilePath);

const LF = '\n';
const CRLF = '\r\n';

const containsLf = report.includes(LF);
const containsCrLf = report.includes(CRLF);

const relativeReportPath = path.relative(
process.cwd(),
config.reportFilePath,
);

if (config.newlineKind === LF && containsCrLf) {
exit(
`${relativeReportPath} contains CRLF.`,
`Please convert its line endings to LF.`,
);
}
if (config.newlineKind === CRLF && containsLf && !containsCrLf) {
exit(
`${relativeReportPath} contains LF.`,
`Please convert its line endings to CRLF.`,
);
}
}

/**
* Finds and loads the API Extractor config relative to the
* current working directory.
* @return {ExtractorConfig}
*/
function loadExtractorConfig() {
const rawConfig = ExtractorConfig.tryLoadForFolder({
startingFolder: process.cwd(),
});
if (!rawConfig) {
exit(
`No API Extractor config could be found for the`,
`current working directory.`,
);
}
return ExtractorConfig.prepare(rawConfig);
}

/**
* Surrounds the message with control characters to display red text on a
* terminal.
* See {@link https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html}
* @param message {string}
*/
function red(message) {
return `\u001b[31m${message}\u001b[0m`;
}

/**
* Prints a failure reason and exits the process with status code 1.
* @param message {string}
*/
function exit(...message) {
/* eslint-disable-next-line no-console */
console.log(`${red('FAILURE REASON')} ${message.join(' ')}`);
process.exit(1);
}

0 comments on commit 9174754

Please sign in to comment.