Skip to content

Commit f6fc696

Browse files
authored
Migrate source code to TypeScript (#72)
* Install ts-jest * Convert source files to TypeScript * Add tslib dependency * Use TS helpers, stop checking for JS * Use nullish coalescing :)
1 parent 22d948d commit f6fc696

File tree

10 files changed

+21573
-7794
lines changed

10 files changed

+21573
-7794
lines changed

jest.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'node',
5+
}

package-lock.json

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

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"path-type": "^4.0.0",
1313
"picocolors": "^1.0.0",
1414
"puppeteer": "~9.1.1",
15-
"readdirp": "^3.6.0"
15+
"readdirp": "^3.6.0",
16+
"tslib": "^2.3.1"
1617
},
1718
"scripts": {
1819
"build": "tsc",
@@ -38,14 +39,17 @@
3839
"node": ">=12"
3940
},
4041
"devDependencies": {
42+
"@netlify/build": "^26.0.2",
4143
"@netlify/eslint-config-node": "^4.0.4",
44+
"@types/jest": "^27.4.0",
4245
"eslint": "^8.5.0",
4346
"eslint-config-prettier": "^8.3.0",
4447
"husky": "^7.0.4",
4548
"jest": "^27.4.5",
4649
"lint-staged": "^11.2.6",
4750
"prettier": "^2.5.1",
4851
"rimraf": "^3.0.2",
52+
"ts-jest": "^27.1.2",
4953
"typescript": "^4.5.4"
5054
},
5155
"repository": "https://github.com/netlify-labs/netlify-plugin-a11y",

src/config.js

Lines changed: 0 additions & 45 deletions
This file was deleted.

src/config.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import puppeteer from 'puppeteer'
2+
import type { NetlifyPluginOptions } from '@netlify/build'
3+
4+
export type WCAGLevel = 'WCAG2A' | 'WCAG2AA' | 'WCAG2AAA'
5+
6+
type InputT = {
7+
checkPaths?: string[],
8+
ignoreDirectories?: string[],
9+
ignoreElements?: string,
10+
failWithIssues?: boolean,
11+
wcagLevel?: WCAGLevel
12+
}
13+
14+
const DEFAULT_CHECK_PATHS = ['/']
15+
const DEFAULT_FAIL_WITH_ISSUES = true
16+
const DEFAULT_IGNORE_DIRECTORIES: string[] = []
17+
18+
const PA11Y_DEFAULT_WCAG_LEVEL = 'WCAG2AA'
19+
const PA11Y_RUNNERS = ['axe']
20+
const PA11Y_USER_AGENT = 'netlify-plugin-a11y'
21+
22+
export const getConfiguration = async ({
23+
constants: { PUBLISH_DIR },
24+
inputs,
25+
}: Pick<NetlifyPluginOptions, 'constants' | 'inputs'>) => {
26+
const { checkPaths, ignoreDirectories, ignoreElements, failWithIssues, wcagLevel } =
27+
inputs as InputT
28+
return {
29+
checkPaths: checkPaths || DEFAULT_CHECK_PATHS,
30+
failWithIssues: failWithIssues ?? DEFAULT_FAIL_WITH_ISSUES,
31+
ignoreDirectories: ignoreDirectories || DEFAULT_IGNORE_DIRECTORIES,
32+
pa11yOpts: await getPa11yOpts({
33+
hideElements: ignoreElements,
34+
standard: wcagLevel || PA11Y_DEFAULT_WCAG_LEVEL,
35+
}),
36+
publishDir: (PUBLISH_DIR || process.env.PUBLISH_DIR) as string,
37+
}
38+
}
39+
40+
export type Config = ReturnType<typeof getConfiguration>
41+
42+
export const getPa11yOpts = async ({ hideElements, standard }: { hideElements?: string; standard: WCAGLevel }) => {
43+
return {
44+
browser: await puppeteer.launch({ ignoreHTTPSErrors: true }),
45+
hideElements,
46+
runners: PA11Y_RUNNERS,
47+
userAgent: PA11Y_USER_AGENT,
48+
standard,
49+
}
50+
}
51+
52+
export type Pa11yOpts = Awaited<ReturnType<typeof getPa11yOpts>>

src/index.js renamed to src/index.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
// @ts-check
1+
import { getConfiguration } from './config'
2+
import { generateFilePaths, runPa11y } from './pluginCore'
3+
import pico from 'picocolors'
24

3-
const { getConfiguration } = require('./config')
4-
const { generateFilePaths, runPa11y } = require('./pluginCore')
5-
const pico = require('picocolors')
5+
import type { OnPostBuild } from '@netlify/build'
66

77
module.exports = {
8-
async onPostBuild({ constants, inputs, utils: { build } }) {
8+
async onPostBuild({ constants, inputs, utils: { build }, }) {
99
try {
1010
const { publishDir, checkPaths, ignoreDirectories, failWithIssues, pa11yOpts } = await getConfiguration({
1111
constants,
@@ -40,4 +40,6 @@ module.exports = {
4040
build.failBuild(err.message)
4141
}
4242
},
43+
} as {
44+
onPostBuild: OnPostBuild
4345
}

src/pluginCore.js renamed to src/pluginCore.ts

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
1-
// @ts-check
1+
import pa11y from 'pa11y'
2+
import { extname, join } from 'path'
3+
import { isDirectory, isFile } from 'path-type'
4+
import { results as cliReporter } from './reporter'
5+
import readdirp from 'readdirp'
6+
import { StaticServer, SERVER_ADDRESS } from './server'
27

3-
const pa11y = require('pa11y')
4-
const { extname, join } = require('path')
5-
const { isDirectory, isFile } = require('path-type')
6-
const { results: cliReporter } = require('./reporter')
7-
const readdirp = require('readdirp')
8-
const { StaticServer, SERVER_ADDRESS } = require('./server')
8+
import type { NetlifyPluginUtils } from '@netlify/build'
9+
import type { Pa11yOpts } from './config'
910

1011
const EMPTY_ARRAY = []
11-
const ASTERISK = '*'
12+
const ASTERISK = '*';
1213
const HTML_EXT = '.html'
1314
const GLOB_HTML = '*.html'
1415

15-
exports.runPa11y = async function ({ build, htmlFilePaths, pa11yOpts, publishDir }) {
16+
export const runPa11y = async function ({
17+
build,
18+
htmlFilePaths,
19+
pa11yOpts,
20+
publishDir,
21+
}: {
22+
build: NetlifyPluginUtils['build']
23+
htmlFilePaths: string[]
24+
pa11yOpts: Pa11yOpts
25+
publishDir: string
26+
}) {
1627
let issueCount = 0
1728

1829
const staticServer = new StaticServer(publishDir).listen()
1930

2031
const results = await Promise.all(
21-
htmlFilePaths.map(async (/** @type {string} */ filePath) => {
32+
htmlFilePaths.map(async (filePath) => {
2233
try {
2334
const res = await pa11y(join(SERVER_ADDRESS, filePath), pa11yOpts)
2435
if (res.issues.length) {
@@ -41,10 +52,14 @@ exports.runPa11y = async function ({ build, htmlFilePaths, pa11yOpts, publishDir
4152
}
4253
}
4354

44-
exports.generateFilePaths = async function ({
55+
export const generateFilePaths = async function ({
4556
fileAndDirPaths, // array, mix of html and directories
4657
ignoreDirectories,
4758
publishDir,
59+
}: {
60+
fileAndDirPaths: string[]
61+
ignoreDirectories: string[]
62+
publishDir: string
4863
}) {
4964
const directoryFilter =
5065
ignoreDirectories.length === 0
@@ -56,10 +71,11 @@ exports.generateFilePaths = async function ({
5671
const htmlFilePaths = await Promise.all(
5772
fileAndDirPaths.map((fileAndDirPath) => findHtmlFiles(`${publishDir}${fileAndDirPath}`, directoryFilter)),
5873
)
59-
return [].concat(...htmlFilePaths)
74+
75+
return [].concat(...htmlFilePaths) as string[]
6076
}
6177

62-
const findHtmlFiles = async function (fileAndDirPath, directoryFilter) {
78+
const findHtmlFiles = async function (fileAndDirPath: string, directoryFilter: '*' | string[]): Promise<string[]> {
6379
if (await isDirectory(fileAndDirPath)) {
6480
const filePaths = []
6581
const stream = readdirp(fileAndDirPath, {

src/reporter.js renamed to src/reporter.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
*
55
* @see https://github.com/pa11y/pa11y/blob/6.1.1/lib/reporters/cli.js
66
*/
7-
// @ts-check
87

98
'use strict'
109

11-
const { bold, cyan, green, gray, red, underline, yellow } = require('picocolors')
10+
import { bold, cyan, green, gray, red, underline, yellow } from 'picocolors'
1211

1312
// Pa11y version support
1413
const PA11Y_SUPPORTS = '^6.0.0 || ^6.0.0-alpha || ^6.0.0-beta'
@@ -43,7 +42,7 @@ function renderIssue(issue) {
4342
}
4443

4544
// Output formatted results
46-
function renderResults(results) {
45+
function renderResults(results): string {
4746
if (results.issues.length) {
4847
const publicFilePath = results.pageUrl.replace(LOCAL_FILE_PATH_EXP, '')
4948
const totals = {
@@ -91,21 +90,21 @@ function renderBegin() {
9190
}
9291

9392
// Output debug messages
94-
function renderDebug(message) {
93+
function renderDebug(message: string) {
9594
message = `Debug: ${message}`
9695
return cleanWhitespace(`
9796
${start} ${gray(message)}
9897
`)
9998
}
10099

101100
// Output information messages
102-
function renderInfo(message) {
101+
function renderInfo(message: string) {
103102
return cleanWhitespace(`
104103
${start} ${message}
105104
`)
106105
}
107106

108-
function renderError(message) {
107+
function renderError(message: string) {
109108
if (!/^error:/i.test(message)) {
110109
message = `Error: ${message}`
111110
}
@@ -116,19 +115,21 @@ function renderError(message) {
116115

117116
// Clean whitespace from output. This function is used to keep
118117
// the reporter code a little cleaner
119-
function cleanWhitespace(string) {
118+
function cleanWhitespace(string): string {
120119
return string.replace(/\t+|^\t*\n|\n\t*$/g, '')
121120
}
122121

123-
function pluralize(noun, count) {
122+
function pluralize(noun: string, count: number): string {
124123
return count === 1 ? noun : noun + 's'
125124
}
126125

127-
module.exports = {
128-
begin: renderBegin,
129-
debug: renderDebug,
130-
info: renderInfo,
131-
error: renderError,
132-
results: renderResults,
133-
supports: PA11Y_SUPPORTS,
126+
127+
128+
export {
129+
renderBegin as begin,
130+
renderDebug as debug,
131+
renderInfo as info,
132+
renderError as error,
133+
renderResults as results,
134+
PA11Y_SUPPORTS as supports,
134135
}

src/server.js renamed to src/server.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
// @ts-check
2-
const http = require('http')
3-
const fs = require('fs')
4-
const path = require('path')
5-
const MIME_TYPES = require('./mimeTypes.json')
1+
import http, { Server } from 'http'
2+
import fs from 'fs'
3+
import path from 'path'
4+
import MIME_TYPES from './mimeTypes.json'
65

76
const HTML_EXT = '.html'
87

@@ -18,10 +17,8 @@ const SERVER_OPTS = {
1817
const basePath = process.cwd()
1918

2019
class StaticServer {
21-
/**
22-
* @param {string} publishDir
23-
*/
24-
constructor(publishDir) {
20+
instance: Server
21+
constructor(publishDir: string) {
2522
this.instance = http.createServer(function (req, res) {
2623
const ext = path.extname(req.url)
2724
const filepath = ext === HTML_EXT ? path.join(basePath, req.url) : path.join(basePath, publishDir, req.url)
@@ -50,7 +47,7 @@ class StaticServer {
5047
}
5148
}
5249

53-
module.exports = {
50+
export {
5451
StaticServer,
5552
SERVER_ADDRESS,
5653
}

tsconfig.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
"target": "ES2019",
44
"module": "commonjs",
55
"lib": ["ES2019"],
6-
"allowJs": true,
76
"outDir": "./lib",
87
"types": ["jest"],
98
"esModuleInterop": true,
109
"removeComments": true,
10+
"importHelpers": true,
1111
"resolveJsonModule": true,
1212
"skipLibCheck": true /* Skip type checking of declaration files. */,
13-
"forceConsistentCasingInFileNames": true
13+
"forceConsistentCasingInFileNames": true,
1414
},
15-
"include": ["src/**/*.ts", "src/**/*.js"]
15+
"include": ["src/**/*.ts"]
1616
}

0 commit comments

Comments
 (0)