Skip to content

Commit 9176daf

Browse files
authored
feat(checker): add verbosity toggle (#225)
1 parent b7e7d8e commit 9176daf

File tree

6 files changed

+84
-14
lines changed

6 files changed

+84
-14
lines changed

docs/content/en/index.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ This module configures [`html-validate`](https://html-validate.org/) to automati
7575

7676
<alert>Consider not enabling this if you are using TailwindCSS, as prettier will struggle to cope with parsing the size of your HTML in development mode.</alert>
7777

78+
- `logLevel` sets the verbosity to one of `verbose`, `warning` or `error`. It defaults to `verbose` in dev, and `warning` when generating.
79+
80+
<alert>You can use this configuration option to turn off console logging for the `No HTML validation errors found for ...` message.</alert>
81+
7882
- `failOnError` will throw an error after running `nuxt generate` if there are any validation errors with the generated pages.
7983

8084
<alert>Useful in continuous integration.</alert>
@@ -89,6 +93,7 @@ This module configures [`html-validate`](https://html-validate.org/) to automati
8993
{
9094
htmlValidator: {
9195
usePrettier: false,
96+
logLevel: 'verbose',
9297
failOnError: false,
9398
options: {
9499
extends: [

src/config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,16 @@ export const defaultHtmlValidateConfig: ConfigData = {
2222
}
2323
}
2424

25+
export type LogLevel = 'verbose' | 'warning' | 'error'
26+
2527
export interface ModuleOptions {
2628
usePrettier?: boolean
29+
logLevel?: LogLevel
2730
failOnError?: boolean
2831
options?: ConfigData
2932
}
3033

31-
export const DEFAULTS: Required<ModuleOptions> = {
34+
export const DEFAULTS: Required<Omit<ModuleOptions, 'logLevel'>> & { logLevel?: LogLevel } = {
3235
usePrettier: false,
3336
failOnError: false,
3437
options: defaultHtmlValidateConfig

src/module.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ export default defineNuxtModule<ModuleOptions>({
1414
nuxt: '^2.0.0 || ^3.0.0-rc.7'
1515
}
1616
},
17-
defaults: DEFAULTS,
18-
async setup (_options, nuxt) {
17+
defaults: nuxt => ({
18+
...DEFAULTS,
19+
logLevel: nuxt.options.dev ? 'verbose' : 'warning'
20+
}),
21+
async setup (moduleOptions, nuxt) {
1922
logger.info(`Using ${chalk.bold('html-validate')} to validate server-rendered HTML`)
2023

21-
const { usePrettier, failOnError, options } = _options as Required<ModuleOptions>
24+
const { usePrettier, failOnError, options, logLevel } = moduleOptions as Required<ModuleOptions>
2225
if ((nuxt.options as any).htmlValidator?.options?.extends) {
2326
options.extends = (nuxt.options as any).htmlValidator.options.extends
2427
}
@@ -34,15 +37,15 @@ export default defineNuxtModule<ModuleOptions>({
3437
config.plugins = config.plugins || []
3538
config.plugins.push(fileURLToPath(new URL('./runtime/nitro', import.meta.url)))
3639
config.virtual = config.virtual || {}
37-
config.virtual['#html-validator-config'] = `export default ${JSON.stringify(_options)}`
40+
config.virtual['#html-validator-config'] = `export default ${JSON.stringify(moduleOptions)}`
3841
})
3942
}
4043

4144
if (!nuxt.options.dev || isNuxt2()) {
4245
const validatorPath = fileURLToPath(new URL('./runtime/validator', import.meta.url))
4346
const { useChecker, getValidator } = await import(resolveModule(validatorPath))
4447
const validator = getValidator(options)
45-
const { checkHTML, invalidPages } = useChecker(validator, usePrettier)
48+
const { checkHTML, invalidPages } = useChecker(validator, usePrettier, logLevel)
4649

4750
if (failOnError) {
4851
const errorIfNeeded = () => {

src/runtime/nitro.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import config from '#html-validator-config'
55

66
export default <NitroAppPlugin> function (nitro) {
77
const validator = getValidator(config.options)
8-
const { checkHTML } = useChecker(validator, config.usePrettier)
8+
const { checkHTML } = useChecker(validator, config.usePrettier, config.logLevel)
99

1010
nitro.hooks.hook('render:response', (response: RenderResponse, { event }) => {
1111
if (typeof response.body === 'string' && (response.headers['Content-Type'] || response.headers['content-type'])?.includes('html')) {

src/runtime/validator.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import chalk from 'chalk'
22
import { ConfigData, HtmlValidate, formatterFactory } from 'html-validate'
3+
import type { LogLevel } from '../config'
34

45
export const getValidator = (options: ConfigData = {}) => {
56
return new HtmlValidate(options)
67
}
78

89
export const useChecker = (
910
validator: HtmlValidate,
10-
usePrettier = false
11+
usePrettier = false,
12+
logLevel: LogLevel = 'verbose'
1113
) => {
1214
const invalidPages: string[] = []
1315

@@ -27,7 +29,7 @@ export const useChecker = (
2729
html = typeof html === 'string' ? html.replace(/ ?data-v-[a-z0-9]+\b/g, '') : html
2830
const { valid, results } = validator.validateString(html)
2931

30-
if (valid && !results.length) {
32+
if (valid && !results.length && logLevel === 'verbose') {
3133
return console.log(
3234
`No HTML validation errors found for ${chalk.bold(url)}`
3335
)
@@ -39,12 +41,18 @@ export const useChecker = (
3941
const formatter = couldFormat ? formatterFactory('codeframe') : await import('@html-validate/stylish').then(r => r.default?.default ?? r.default ?? r)
4042

4143
const formattedResult = formatter?.(results)
42-
const reporter = valid ? console.warn : console.error
43-
44-
reporter([
44+
const message = [
4545
`HTML validation errors found for ${chalk.bold(url)}`,
4646
formattedResult
47-
].join('\n'))
47+
].join('\n')
48+
49+
if (valid) {
50+
if (logLevel === 'verbose' || logLevel === 'warning') {
51+
console.warn(message)
52+
}
53+
} else {
54+
console.error(message)
55+
}
4856
}
4957

5058
return { checkHTML, invalidPages }

test/checker.test.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import chalk from 'chalk'
12
import { describe, it, expect, vi, afterEach } from 'vitest'
3+
24
import { useChecker } from '../src/runtime/validator'
35

46
vi.mock('prettier', () => ({
@@ -14,6 +16,7 @@ vi.mock('html-validate')
1416

1517
vi.spyOn(console, 'error')
1618
vi.spyOn(console, 'log')
19+
vi.spyOn(console, 'warn')
1720

1821
describe('useChecker', () => {
1922
afterEach(() => {
@@ -29,7 +32,7 @@ describe('useChecker', () => {
2932
expect(console.error).toHaveBeenCalled()
3033
})
3134

32-
it('prints an error message when invalid html is provided', async () => {
35+
it('prints an error when invalid html is provided', async () => {
3336
const mockValidator = vi.fn().mockImplementation(() => ({ valid: false, results: [] }))
3437
const { checkHTML: checker } = useChecker({ validateString: mockValidator } as any, false)
3538

@@ -38,6 +41,36 @@ describe('useChecker', () => {
3841
expect(console.error).toHaveBeenCalled()
3942
})
4043

44+
it('prints a warning when invalid html is provided and log level is set to verbose', async () => {
45+
const mockValidator = vi.fn().mockImplementation(() => ({ valid: true, results: [{ messages: [{ message: '' }] }] }))
46+
const { checkHTML: checker } = useChecker({ validateString: mockValidator } as any, false, 'verbose')
47+
48+
await checker('https://test.com/', '<a>Link</a>')
49+
expect(mockValidator).toHaveBeenCalled()
50+
expect(console.warn).toHaveBeenCalled()
51+
expect(console.error).not.toHaveBeenCalled()
52+
})
53+
54+
it('prints a warning when invalid html is provided and log level is set to warning', async () => {
55+
const mockValidator = vi.fn().mockImplementation(() => ({ valid: true, results: [] }))
56+
const { checkHTML: checker } = useChecker({ validateString: mockValidator } as any, false, 'warning')
57+
58+
await checker('https://test.com/', '<a>Link</a>')
59+
expect(mockValidator).toHaveBeenCalled()
60+
expect(console.warn).toHaveBeenCalled()
61+
expect(console.error).not.toHaveBeenCalled()
62+
})
63+
64+
it('prints no warning when invalid html is provided and log level is set to error', async () => {
65+
const mockValidator = vi.fn().mockImplementation(() => ({ valid: true, results: [] }))
66+
const { checkHTML: checker } = useChecker({ validateString: mockValidator } as any, false, 'error')
67+
68+
await checker('https://test.com/', '<a>Link</a>')
69+
expect(mockValidator).toHaveBeenCalled()
70+
expect(console.warn).not.toHaveBeenCalled()
71+
expect(console.error).not.toHaveBeenCalled()
72+
})
73+
4174
it('records urls when invalid html is provided', async () => {
4275
const mockValidator = vi.fn().mockImplementation(() => ({ valid: false, results: [] }))
4376
const { checkHTML: checker, invalidPages } = useChecker({ validateString: mockValidator } as any, false)
@@ -84,4 +117,22 @@ describe('useChecker', () => {
84117
const validate = await import('html-validate')
85118
expect(validate.formatterFactory).not.toHaveBeenCalledWith('codeframe')
86119
})
120+
121+
it('logs valid output', async () => {
122+
const mockValidator = vi.fn().mockImplementation(() => ({ valid: true, results: [] }))
123+
const { checkHTML: checker } = useChecker({ validateString: mockValidator } as any, false, 'verbose')
124+
125+
await checker('https://test.com/', Symbol as any)
126+
expect(console.log).toHaveBeenCalledWith(
127+
`No HTML validation errors found for ${chalk.bold('https://test.com/')}`
128+
)
129+
})
130+
131+
it('does not log valid output when logging on level warning', async () => {
132+
const mockValidator = vi.fn().mockImplementation(() => ({ valid: true, results: [] }))
133+
const { checkHTML: checker } = useChecker({ validateString: mockValidator } as any, false, 'warning')
134+
135+
await checker('https://test.com/', Symbol as any)
136+
expect(console.log).not.toHaveBeenCalled()
137+
})
87138
})

0 commit comments

Comments
 (0)