1
1
mirror of https://github.com/theoludwig/html-w3c-validator.git synced 2025-05-21 23:21:29 +02:00

feat: add html-w3c-validator command

This commit is contained in:
Divlo
2022-01-06 22:34:58 +01:00
parent 39b05c66a7
commit 77cefcd53f
18 changed files with 34634 additions and 449 deletions

View File

@ -1,6 +1,25 @@
import { Command } from 'clipanion'
import path from 'node:path'
import fs from 'node:fs'
// const CURRENT_DIRECTORY = process.cwd()
import { Command } from 'clipanion'
import chalk from 'chalk'
import ora from 'ora'
import validateHTML, { ValidationMessageLocationObject } from 'html-validator'
import { table } from 'table'
import { isExistingPath } from './utils/isExistingPath.js'
const CURRENT_DIRECTORY = process.cwd()
const CONFIG_FILE_NAME = '.html-w3c-validatorrc.json'
interface Config {
urls: string[]
}
interface Error {
url: string
messagesTable: string[][]
}
export class HTMLValidatorCommand extends Command {
static usage = {
@ -9,7 +28,90 @@ export class HTMLValidatorCommand extends Command {
}
async execute(): Promise<number> {
console.log('html-w3c-validator')
return 0
const configPath = path.join(CURRENT_DIRECTORY, CONFIG_FILE_NAME)
try {
if (!(await isExistingPath(configPath))) {
throw new Error(
`No config file found at ${configPath}. Please create ${CONFIG_FILE_NAME}.`
)
}
const configData = await fs.promises.readFile(configPath, {
encoding: 'utf-8'
})
let config: Config = { urls: [] }
let isValidConfig = true
try {
config = JSON.parse(configData)
} catch {
isValidConfig = false
}
isValidConfig = isValidConfig && Array.isArray(config.urls)
if (!isValidConfig) {
throw new Error(
`Invalid config file at ${configPath}. Please check the syntax.`
)
}
const errors: Error[] = []
let isValid = true
for (const url of config.urls) {
const loader = ora(`Validating ${url}`).start()
const result = await validateHTML({
url,
format: 'json',
isLocal: true
})
const isValidHTML = result.messages.length === 0
if (isValidHTML) {
loader.succeed()
} else {
loader.fail()
const messagesTable: string[][] = []
for (const message of result.messages) {
const row: string[] = []
if (message.type === 'error') {
row.push(chalk.red(message.type))
} else {
row.push(chalk.yellow(message.type))
}
row.push(message.message)
const violation = message as ValidationMessageLocationObject
if (violation.extract != null) {
row.push(
`line: ${violation.lastLine}, column: ${violation.firstColumn}-${violation.lastColumn}`
)
}
messagesTable.push(row)
}
errors.push({ url, messagesTable })
isValid = false
}
}
if (!isValid) {
for (const error of errors) {
console.error(`\n${error.url}`)
console.error(table(error.messagesTable))
console.error('------------------------------')
}
console.error()
throw new Error('HTML validation (W3C) failed!')
}
console.log()
console.log(
`${chalk.bold.green('Success:')} HTML validation (W3C) passed! 🎉`
)
return 0
} catch (error) {
if (error instanceof Error) {
console.error(`${chalk.bold.red('Error:')} ${error.message}`)
} else {
console.error(
`${chalk.bold.red('Error:')} HTML validation (W3C) failed!`
)
}
return 1
}
}
}

View File

@ -1,3 +1,7 @@
import path from 'node:path'
import execa from 'execa'
import { cli } from '../cli.js'
import { HTMLValidatorCommand } from '../HTMLValidatorCommand.js'
@ -12,11 +16,11 @@ describe('html-w3c-validator', () => {
})
it('succeeds and validate the html correctly', async () => {
console.log = jest.fn()
const exitCode = await cli.run([], {
stdin: process.stdin
})
expect(console.log).toHaveBeenCalledWith('html-w3c-validator')
const examplePath = path.join(__dirname, '..', '..', 'example')
process.chdir(examplePath)
await execa('rimraf', ['node_modules'])
await execa('npm', ['install'])
const { exitCode } = await execa('npm', ['run', 'test:html-w3c-validator'])
expect(exitCode).toEqual(0)
})
})

1
src/__test__/setup.ts Normal file
View File

@ -0,0 +1 @@
jest.setTimeout(60000)

View File

@ -0,0 +1,29 @@
import fsMock from 'mock-fs'
import { isExistingPath } from '../isExistingPath.js'
describe('utils/isExistingFile', () => {
afterEach(async () => {
fsMock.restore()
})
it('should return true if the file exists', async () => {
fsMock(
{
'/file.txt': ''
},
{ createCwd: false }
)
expect(await isExistingPath('/file.txt')).toBeTruthy()
})
it("should return false if the file doesn't exists", async () => {
fsMock(
{
'/file.txt': ''
},
{ createCwd: false }
)
expect(await isExistingPath('/randomfile.txt')).toBeFalsy()
})
})

View File

@ -0,0 +1,10 @@
import fs from 'node:fs'
export const isExistingPath = async (path: string): Promise<boolean> => {
try {
await fs.promises.access(path, fs.constants.F_OK)
return true
} catch {
return false
}
}