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

21 Commits

Author SHA1 Message Date
7dafa2401b fix: update dependencies to latest 2023-12-26 22:43:05 +01:00
02a5010340 chore: better Prettier config for easier reviews 2023-10-23 23:21:05 +02:00
e3cd809e56 fix: only show error messages related to configured severities 2023-08-09 17:04:03 +02:00
284b9fb057 test: add automated tests 2023-08-09 16:51:34 +02:00
1ae47aa197 feat: add --current-working-directory option 2023-08-09 15:36:05 +02:00
d4424036c5 fix: update dependencies to latest 2023-08-09 15:01:28 +02:00
82e32f26ff chore: simplify .swcrc config 2023-07-22 12:28:45 +02:00
5abdb8a1f1 style: fix prettier issues
All checks were successful
Build / build (push) Successful in 48s
Lint / lint (push) Successful in 44s
Test / test (push) Successful in 40s
2023-07-18 23:53:21 +02:00
aab0c3f502 build(deps): update latest 2023-07-18 23:17:31 +02:00
5fa068b9f8 chore: remove unneeded @types/tap dependency 2023-07-02 15:33:41 +02:00
c7bf82135b chore: enable source maps for easier debugging 2023-07-02 15:23:04 +02:00
eb970f1823 BREAKING CHANGE: change default validator severities: "warning" and "error" 2023-07-02 13:07:45 +02:00
29203985e4 feat: ability to specify the severity of the validator
fixes #4
2023-07-02 13:05:33 +02:00
a8841b24fd perf: ignore test files in published package (reduce package size) 2023-07-02 12:27:18 +02:00
bda82b39a3 fix: update author - Théo LUDWIG 2023-07-02 12:22:39 +02:00
f7a4edc46a build(deps): update latest 2023-07-02 12:19:45 +02:00
67806866ee refactor: usage of node:test instead of tap 2023-07-02 12:18:21 +02:00
3d24ffe971 feat: add npm package provenance
Ref: https://github.blog/2023-04-19-introducing-npm-package-provenance/
2023-05-13 16:21:58 +02:00
e818556c5b build(deps): update latest 2023-05-13 16:19:21 +02:00
2cb7967bc7 build(deps): update latest 2023-04-02 21:27:42 +02:00
943b3d1fd6 fix: update dependencies to latest 2023-01-10 22:15:14 +01:00
56 changed files with 6624 additions and 19713 deletions

View File

@ -1,8 +1,8 @@
--- ---
name: '🐛 Bug Report' name: "🐛 Bug Report"
about: 'Report an unexpected problem or unintended behavior.' about: "Report an unexpected problem or unintended behavior."
title: '[Bug]' title: "[Bug]"
labels: 'bug' labels: "bug"
--- ---
<!-- <!--

View File

@ -1,8 +1,8 @@
--- ---
name: '📜 Documentation' name: "📜 Documentation"
about: 'Correct spelling errors, improvements or additions to documentation files (README, CONTRIBUTING...).' about: "Correct spelling errors, improvements or additions to documentation files (README, CONTRIBUTING...)."
title: '[Documentation]' title: "[Documentation]"
labels: 'documentation' labels: "documentation"
--- ---
<!-- Please make sure your issue has not already been fixed. --> <!-- Please make sure your issue has not already been fixed. -->

View File

@ -1,8 +1,8 @@
--- ---
name: '✨ Feature Request' name: "✨ Feature Request"
about: 'Suggest a new feature idea.' about: "Suggest a new feature idea."
title: '[Feature]' title: "[Feature]"
labels: 'feature request' labels: "feature request"
--- ---
<!-- Please make sure your issue has not already been fixed. --> <!-- Please make sure your issue has not already been fixed. -->

View File

@ -1,8 +1,8 @@
--- ---
name: '🔧 Improvement' name: "🔧 Improvement"
about: 'Improve structure/format/performance/refactor/tests of the code.' about: "Improve structure/format/performance/refactor/tests of the code."
title: '[Improvement]' title: "[Improvement]"
labels: 'improvement' labels: "improvement"
--- ---
<!-- Please make sure your issue has not already been fixed. --> <!-- Please make sure your issue has not already been fixed. -->

View File

@ -1,8 +1,8 @@
--- ---
name: '🙋 Question' name: "🙋 Question"
about: 'Further information is requested.' about: "Further information is requested."
title: '[Question]' title: "[Question]"
labels: 'question' labels: "question"
--- ---
### Question ### Question

View File

@ -1,6 +1,6 @@
<!-- Please first discuss the change you wish to make via issue before making a change. It might avoid a waste of your time. --> <!-- Please first discuss the change you wish to make via issue before making a change. It might avoid a waste of your time. -->
## What changes this PR introduce? # What changes this PR introduce?
## List any relevant issue numbers ## List any relevant issue numbers

View File

@ -1,4 +1,4 @@
name: 'Build' name: "Build"
on: on:
push: push:
@ -8,18 +8,20 @@ on:
jobs: jobs:
build: build:
runs-on: 'ubuntu-latest' runs-on: "ubuntu-latest"
steps: steps:
- uses: 'actions/checkout@v3.0.0' - uses: "actions/checkout@v4.1.1"
- name: 'Use Node.js' - name: "Setup Node.js"
uses: 'actions/setup-node@v3.1.0' uses: "actions/setup-node@v4.0.1"
with: with:
node-version: 'lts/*' node-version: "lts/*"
cache: 'npm' cache: "npm"
- name: 'Install' - name: "Install dependencies"
run: 'npm install' run: "npm clean-install"
- name: 'Build' - name: "Build"
run: 'npm run build' run: "npm run build"
- run: "npm run build:typescript"

View File

@ -1,4 +1,4 @@
name: 'Lint' name: "Lint"
on: on:
push: push:
@ -8,21 +8,21 @@ on:
jobs: jobs:
lint: lint:
runs-on: 'ubuntu-latest' runs-on: "ubuntu-latest"
steps: steps:
- uses: 'actions/checkout@v3.0.0' - uses: "actions/checkout@v4.1.1"
- name: 'Use Node.js' - name: "Setup Node.js"
uses: 'actions/setup-node@v3.1.0' uses: "actions/setup-node@v4.0.1"
with: with:
node-version: 'lts/*' node-version: "lts/*"
cache: 'npm' cache: "npm"
- name: 'Install' - name: "Install dependencies"
run: 'npm install' run: "npm clean-install"
- run: 'npm run lint:commit -- --to "${{ github.sha }}"' - run: 'npm run lint:commit -- --to "${{ github.sha }}"'
- run: 'npm run lint:editorconfig' - run: "npm run lint:editorconfig"
- run: 'npm run lint:markdown' - run: "npm run lint:markdown"
- run: 'npm run lint:typescript' - run: "npm run lint:eslint"
- run: 'npm run lint:prettier' - run: "npm run lint:prettier"

View File

@ -1,4 +1,4 @@
name: 'Release' name: "Release"
on: on:
push: push:
@ -6,27 +6,37 @@ on:
jobs: jobs:
release: release:
runs-on: 'ubuntu-latest' runs-on: "ubuntu-latest"
permissions:
contents: "write"
issues: "write"
pull-requests: "write"
id-token: "write"
steps: steps:
- uses: 'actions/checkout@v3.0.0' - uses: "actions/checkout@v4.1.1"
with: with:
fetch-depth: 0 fetch-depth: 0
persist-credentials: false persist-credentials: false
- name: 'Use Node.js' - name: "Setup Node.js"
uses: 'actions/setup-node@v3.1.0' uses: "actions/setup-node@v4.0.1"
with: with:
node-version: 'lts/*' node-version: "lts/*"
cache: 'npm' cache: "npm"
- name: 'Install' - name: "Install dependencies"
run: 'npm install' run: "npm clean-install"
- name: 'Build' - name: "Build"
run: 'npm run build' run: "npm run build"
- name: 'Release' - run: "npm run build:typescript"
run: 'npm run release'
- name: "Verify the integrity of provenance attestations and registry signatures for installed dependencies"
run: "npm audit signatures"
- name: "Release"
run: "npm run release"
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -1,4 +1,4 @@
name: 'Test' name: "Test"
on: on:
push: push:
@ -8,21 +8,21 @@ on:
jobs: jobs:
test: test:
runs-on: 'ubuntu-latest' runs-on: "ubuntu-latest"
steps: steps:
- uses: 'actions/checkout@v3.0.0' - uses: "actions/checkout@v4.1.1"
- name: 'Use Node.js' - name: "Setup Node.js"
uses: 'actions/setup-node@v3.1.0' uses: "actions/setup-node@v4.0.1"
with: with:
node-version: 'lts/*' node-version: "lts/*"
cache: 'npm' cache: "npm"
- name: 'Install' - name: "Install dependencies"
run: 'npm install' run: "npm clean-install"
- name: 'Build' - name: "Build"
run: 'npm run build' run: "npm run build"
- name: 'Test' - name: "Test"
run: 'npm run test' run: "npm run test"

View File

@ -0,0 +1,3 @@
{
"files": ["./example/build/index.html", "./example/build/about.html"]
}

View File

@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint:commit -- --edit

View File

@ -1,5 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint:staged
npm run build

11
.markdownlint-cli2.jsonc Normal file
View File

@ -0,0 +1,11 @@
{
"config": {
"extends": "markdownlint/style/prettier",
"relative-links": true,
"default": true,
"MD033": false
},
"globs": ["**/*.{md,mdx}"],
"ignores": ["**/node_modules"],
"customRules": ["markdownlint-rule-relative-links"]
}

View File

@ -1,6 +0,0 @@
{
"default": true,
"MD013": false,
"MD033": false,
"MD041": false
}

1
.npmrc
View File

@ -1 +1,2 @@
save-exact=true save-exact=true
provenance=true

1
.prettierignore Normal file
View File

@ -0,0 +1 @@
fixtures

View File

@ -1,6 +1,3 @@
{ {
"singleQuote": true, "semi": false
"jsxSingleQuote": true,
"semi": false,
"trailingComma": "none"
} }

15
.swcrc
View File

@ -1,22 +1,13 @@
{ {
"sourceMaps": true,
"jsc": { "jsc": {
"parser": { "parser": {
"syntax": "typescript", "syntax": "typescript",
"decorators": true,
"dynamicImport": true "dynamicImport": true
}, },
"transform": { "target": "esnext"
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es2022",
"loose": true
}, },
"module": { "module": {
"type": "es6", "type": "es6"
"strict": false,
"strictMode": true,
"lazy": false,
"noInterop": false
} }
} }

9
.taprc
View File

@ -1,9 +0,0 @@
ts: false
jsx: false
flow: false
check-coverage: false
coverage: false
timeout: 120000
files:
- 'build/**/*.test.js'

View File

@ -3,7 +3,6 @@
"editorconfig.editorconfig", "editorconfig.editorconfig",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"davidanson.vscode-markdownlint", "davidanson.vscode-markdownlint"
"ms-azuretools.vscode-docker"
] ]
} }

View File

@ -5,7 +5,7 @@
"prettier.configPath": ".prettierrc.json", "prettier.configPath": ".prettierrc.json",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll": true "source.fixAll": "explicit"
}, },
"eslint.options": { "eslint.options": {
"ignorePath": ".gitignore" "ignorePath": ".gitignore"

View File

@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at reported to the community leaders responsible for enforcement at
contact@divlo.fr. <contact@theoludwig.fr>.
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the All community leaders are obligated to respect the privacy and security of the

View File

@ -2,6 +2,14 @@
Thanks a lot for your interest in contributing to **html-w3c-validator**! 🎉 Thanks a lot for your interest in contributing to **html-w3c-validator**! 🎉
## Code of Conduct
**html-w3c-validator** adopted the [Contributor Covenant](https://www.contributor-covenant.org/) as its Code of Conduct, and we expect project participants to adhere to it. Please read [the full text](./CODE_OF_CONDUCT.md) so that you can understand what actions will and will not be tolerated.
## Open Development
All work on **html-w3c-validator** happens directly on this repository. Both core team members and external contributors send pull requests which go through the same review process.
## Types of contributions ## Types of contributions
- Reporting a bug. - Reporting a bug.
@ -11,7 +19,7 @@ Thanks a lot for your interest in contributing to **html-w3c-validator**! 🎉
## Pull Requests ## Pull Requests
- **Please first discuss** the change you wish to make via [issue](https://github.com/Divlo/html-w3c-validator/issues) before making a change. It might avoid a waste of your time. - **Please first discuss** the change you wish to make via [issue](https://github.com/theoludwig/html-w3c-validator/issues) before making a change. It might avoid a waste of your time.
- Ensure your code respect linting. - Ensure your code respect linting.
@ -21,26 +29,4 @@ If you're adding new features to **html-w3c-validator**, please include tests.
## Commits ## Commits
The commit message guidelines respect [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional) and [Semantic Versioning](https://semver.org/) for releases. The commit message guidelines adheres to [Conventional Commits](https://www.conventionalcommits.org/) and [Semantic Versioning](https://semver.org/) for releases.
### Types
Types define which kind of changes you made to the project.
| Types | Description |
| -------- | ------------------------------------------------------------------------------------------------------------ |
| feat | A new feature. |
| fix | A bug fix. |
| docs | Documentation only changes. |
| style | Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc). |
| refactor | A code change that neither fixes a bug nor adds a feature. |
| perf | A code change that improves performance. |
| test | Adding missing tests or correcting existing tests. |
| build | Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm). |
| ci | Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs). |
| chore | Other changes that don't modify src or test files. |
| revert | Reverts a previous commit. |
### Scopes
Scopes define what part of the code changed.

View File

@ -1,6 +1,6 @@
MIT License # MIT License
Copyright (c) Divlo Copyright (c) Théo LUDWIG
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -7,13 +7,13 @@
</p> </p>
<p align="center"> <p align="center">
<a href="./CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" /></a> <a href="./CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="CONTRIBUTING" /></a>
<a href="./LICENSE"><img src="https://img.shields.io/badge/licence-MIT-blue.svg" alt="Licence MIT"/></a> <a href="./LICENSE"><img src="https://img.shields.io/badge/licence-MIT-blue.svg" alt="Licence MIT"/></a>
<a href="./CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Contributor Covenant" /></a> <a href="./CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Contributor Covenant" /></a>
<br /> <br />
<a href="https://github.com/Divlo/html-w3c-validator/actions/workflows/build.yml"><img src="https://github.com/Divlo/html-w3c-validator/actions/workflows/build.yml/badge.svg?branch=develop" /></a> <a href="https://github.com/theoludwig/html-w3c-validator/actions/workflows/build.yml"><img src="https://github.com/theoludwig/html-w3c-validator/actions/workflows/build.yml/badge.svg?branch=develop" alt="Build" /></a>
<a href="https://github.com/Divlo/html-w3c-validator/actions/workflows/lint.yml"><img src="https://github.com/Divlo/html-w3c-validator/actions/workflows/lint.yml/badge.svg?branch=develop" /></a> <a href="https://github.com/theoludwig/html-w3c-validator/actions/workflows/lint.yml"><img src="https://github.com/theoludwig/html-w3c-validator/actions/workflows/lint.yml/badge.svg?branch=develop" alt="Lint" /></a>
<a href="https://github.com/Divlo/html-w3c-validator/actions/workflows/test.yml"><img src="https://github.com/Divlo/html-w3c-validator/actions/workflows/test.yml/badge.svg?branch=develop" /></a> <a href="https://github.com/theoludwig/html-w3c-validator/actions/workflows/test.yml"><img src="https://github.com/theoludwig/html-w3c-validator/actions/workflows/test.yml/badge.svg?branch=develop" alt="Test" /></a>
<br /> <br />
<a href="https://conventionalcommits.org"><img src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg" alt="Conventional Commits" /></a> <a href="https://conventionalcommits.org"><img src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg" alt="Conventional Commits" /></a>
<a href="https://github.com/semantic-release/semantic-release"><img src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg" alt="semantic-release" /></a> <a href="https://github.com/semantic-release/semantic-release"><img src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg" alt="semantic-release" /></a>
@ -71,7 +71,10 @@ npm install --save-dev html-w3c-validator start-server-and-test
"urls": ["http://127.0.0.1:3000/", "http://127.0.0.1:3000/about"], "urls": ["http://127.0.0.1:3000/", "http://127.0.0.1:3000/about"],
// You can also specify HTML files instead of URLs // You can also specify HTML files instead of URLs
"files": ["./index.html", "./about.html"] "files": ["./index.html", "./about.html"],
// Specify the severities of the validator (default: ["warning", "error"])
"severities": ["info", "warning", "error"]
} }
``` ```

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />

1243
example/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,12 +2,12 @@
"name": "example", "name": "example",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "serve \"./build\"", "start": "serve ./build",
"test:html-w3c-validator": "start-server-and-test \"start\" \"http://127.0.0.1:3000\" \"html-w3c-validator\"" "test:html-w3c-validator": "start-server-and-test \"start\" \"http://127.0.0.1:3000\" \"html-w3c-validator\""
}, },
"devDependencies": { "devDependencies": {
"serve": "14.0.1", "serve": "14.2.1",
"start-server-and-test": "1.14.0", "start-server-and-test": "2.0.3",
"html-w3c-validator": "file:.." "html-w3c-validator": "file:.."
} }
} }

24003
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,20 @@
{ {
"name": "html-w3c-validator", "name": "html-w3c-validator",
"version": "0.0.0-development", "version": "0.0.0-development",
"public": true,
"description": "CLI for validating multiple html pages using validator.w3.org.", "description": "CLI for validating multiple html pages using validator.w3.org.",
"public": true,
"types": "module",
"type": "module", "type": "module",
"author": "Divlo <contact@divlo.fr>", "author": "Théo LUDWIG <contact@theoludwig.fr>",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/Divlo/html-w3c-validator.git" "url": "https://github.com/theoludwig/html-w3c-validator.git"
}, },
"bugs": { "bugs": {
"url": "https://github.com/Divlo/html-w3c-validator/issues" "url": "https://github.com/theoludwig/html-w3c-validator/issues"
}, },
"homepage": "https://github.com/Divlo/html-w3c-validator#readme", "homepage": "https://github.com/theoludwig/html-w3c-validator#readme",
"keywords": [ "keywords": [
"cli", "cli",
"html-validator", "html-validator",
@ -22,65 +23,71 @@
], ],
"main": "build/index.js", "main": "build/index.js",
"bin": "build/index.js", "bin": "build/index.js",
"files": [
"build",
"!**/*.test.js",
"!**/*.map"
],
"publishConfig": {
"access": "public",
"provenance": true
},
"engines": { "engines": {
"node": ">=16.0.0", "node": ">=16.0.0",
"npm": ">=8.0.0" "npm": ">=9.0.0"
}, },
"files": [
"build"
],
"scripts": { "scripts": {
"build": "rimraf ./build && swc ./src --out-dir ./build && tsc", "build": "rimraf ./build && swc ./src --out-dir ./build",
"build:typescript": "tsc",
"start": "node --enable-source-maps build/index.js",
"lint:commit": "commitlint", "lint:commit": "commitlint",
"lint:editorconfig": "editorconfig-checker", "lint:editorconfig": "editorconfig-checker",
"lint:markdown": "markdownlint \"**/*.md\" --dot --ignore-path \".gitignore\"", "lint:markdown": "markdownlint-cli2",
"lint:typescript": "eslint \"**/*.{js,jsx,ts,tsx}\" --ignore-path \".gitignore\"", "lint:eslint": "eslint . --max-warnings 0 --report-unused-disable-directives --ignore-path .gitignore",
"lint:prettier": "prettier \".\" --check --ignore-path \".gitignore\"", "lint:prettier": "prettier . --check",
"lint:staged": "lint-staged", "lint:staged": "lint-staged",
"test": "tap", "test": "node --enable-source-maps --test build/",
"release": "semantic-release", "release": "semantic-release"
"postinstall": "husky install",
"prepublishOnly": "pinst --disable",
"postpublish": "pinst --enable"
}, },
"dependencies": { "dependencies": {
"chalk": "5.1.2", "chalk": "5.3.0",
"clipanion": "3.1.0", "clipanion": "3.2.1",
"html-validator": "6.0.1", "html-validator": "6.0.1",
"log-symbols": "5.1.0", "log-symbols": "6.0.0",
"ora": "6.1.2", "ora": "8.0.1",
"read-pkg": "7.1.0", "read-pkg": "9.0.1",
"table": "6.8.0" "table": "6.8.1"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "17.1.2", "@commitlint/cli": "18.4.3",
"@commitlint/config-conventional": "17.1.0", "@commitlint/config-conventional": "18.4.3",
"@swc/cli": "0.1.57", "@swc/cli": "0.1.63",
"@swc/core": "1.3.9", "@swc/core": "1.3.101",
"@types/html-validator": "5.0.3", "@tsconfig/strictest": "2.0.2",
"@types/mock-fs": "4.13.1", "@types/html-validator": "5.0.6",
"@types/node": "18.11.3", "@types/mock-fs": "4.13.4",
"@types/tap": "15.0.7", "@types/node": "20.10.5",
"@typescript-eslint/eslint-plugin": "5.40.1", "@types/sinon": "17.0.2",
"editorconfig-checker": "4.0.2", "@typescript-eslint/eslint-plugin": "6.16.0",
"eslint": "8.25.0", "@typescript-eslint/parser": "6.16.0",
"eslint-config-conventions": "5.0.0", "editorconfig-checker": "5.1.2",
"eslint-config-prettier": "8.5.0", "eslint": "8.56.0",
"eslint-plugin-import": "2.26.0", "eslint-config-conventions": "13.1.0",
"eslint-plugin-prettier": "4.2.1", "eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-prettier": "5.1.2",
"eslint-plugin-promise": "6.1.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-unicorn": "44.0.2", "eslint-plugin-unicorn": "50.0.1",
"execa": "6.1.0", "execa": "8.0.1",
"husky": "8.0.1", "lint-staged": "15.2.0",
"lint-staged": "13.0.3", "markdownlint-cli2": "0.11.0",
"markdownlint-cli": "0.32.2", "markdownlint-rule-relative-links": "2.1.2",
"mock-fs": "5.1.4", "mock-fs": "5.2.0",
"pinst": "3.0.0", "prettier": "3.1.1",
"prettier": "2.7.1", "rimraf": "5.0.5",
"rimraf": "3.0.2", "semantic-release": "22.0.12",
"semantic-release": "19.0.5", "serve": "14.2.1",
"serve": "14.0.1", "sinon": "17.0.1",
"tap": "16.3.0", "typescript": "5.3.3"
"typescript": "4.8.4"
} }
} }

View File

@ -1,25 +1,30 @@
import path from 'node:path' import path from "node:path"
import fs from 'node:fs' import fs from "node:fs"
import { Command } from 'clipanion' import { Command, Option } from "clipanion"
import chalk from 'chalk' import * as typanion from "typanion"
import ora from 'ora' import chalk from "chalk"
import logSymbols from 'log-symbols' import ora from "ora"
import logSymbols from "log-symbols"
import type { import type {
ValidationMessageLocationObject, ValidationMessageLocationObject,
ParsedJsonAsValidationResults ParsedJsonAsValidationResults,
} from 'html-validator' } from "html-validator"
import validateHTML from 'html-validator' import validateHTML from "html-validator"
import { table } from 'table' import { table } from "table"
import { isExistingPath } from './utils/isExistingPath.js' import { isExistingPath } from "./utils/isExistingPath.js"
const CURRENT_DIRECTORY = process.cwd() export const CONFIG_FILE_NAME = ".html-w3c-validatorrc.json"
const CONFIG_FILE_NAME = '.html-w3c-validatorrc.json'
export const SEVERITIES = ["error", "warning", "info"] as const
export type Severity = (typeof SEVERITIES)[number]
interface Config { interface Config {
urls?: string[] urls?: string[]
files?: string[] files?: string[]
severities?: Severity[]
} }
interface Error { interface Error {
@ -43,21 +48,30 @@ const printResults = (results: Result[]): void => {
} }
export class HTMLValidatorCommand extends Command { export class HTMLValidatorCommand extends Command {
static usage = { static override usage = {
description: description:
'CLI for validating multiple html pages using <https://validator.w3.org/>.' "CLI for validating multiple html pages using <https://validator.w3.org/>.",
} }
async execute(): Promise<number> { public currentWorkingDirectory = Option.String(
const configPath = path.join(CURRENT_DIRECTORY, CONFIG_FILE_NAME) "--current-working-directory",
process.cwd(),
{
description: "The current working directory.",
validator: typanion.isString(),
},
)
public async execute(): Promise<number> {
const configPath = path.join(this.currentWorkingDirectory, CONFIG_FILE_NAME)
try { try {
if (!(await isExistingPath(configPath))) { if (!(await isExistingPath(configPath))) {
throw new Error( throw new Error(
`No config file found at ${configPath}. Please create ${CONFIG_FILE_NAME}.` `No config file found at ${configPath}. Please create "${CONFIG_FILE_NAME}".`,
) )
} }
const configData = await fs.promises.readFile(configPath, { const configData = await fs.promises.readFile(configPath, {
encoding: 'utf-8' encoding: "utf-8",
}) })
let config: Config = { urls: [], files: [] } let config: Config = { urls: [], files: [] }
let isValidConfig = true let isValidConfig = true
@ -66,27 +80,56 @@ export class HTMLValidatorCommand extends Command {
} catch { } catch {
isValidConfig = false isValidConfig = false
} }
isValidConfig =
isValidConfig &&
(Array.isArray(config.urls) || Array.isArray(config.urls))
if (!isValidConfig) { if (!isValidConfig) {
throw new Error( throw new Error(
`Invalid config file at ${configPath}. Please check the syntax.` `Invalid config file at "${configPath}". Please check the JSON syntax.`,
)
}
if (config.urls != null && !Array.isArray(config.urls)) {
throw new Error(
`Invalid config file at "${configPath}". Please include an array of URLs.`,
)
}
if (config.files != null && !Array.isArray(config.files)) {
throw new Error(
`Invalid config file at "${configPath}". Please include an array of files.`,
) )
} }
const urls = const urls =
config.urls == null config.urls == null
? [] ? []
: config.urls.map((url) => { : config.urls.map((url) => {
return { type: 'url', data: url } return { type: "url", data: url }
}) })
const files = const files =
config.files == null config.files == null
? [] ? []
: config.files.map((file) => { : config.files.map((file) => {
return { type: 'file', data: file } return { type: "file", data: file }
}) })
const dataToValidate = [...urls, ...files] const dataToValidate = [...urls, ...files]
if (dataToValidate.length === 0) {
throw new Error(
`Invalid config file at "${configPath}". Please add URLs or files.`,
)
}
const severities: Severity[] = config.severities ?? ["warning", "error"]
for (const severity of severities) {
if (!SEVERITIES.includes(severity)) {
throw new Error(
`Invalid config file at "${configPath}". Please add valid severities (${SEVERITIES.join(
", ",
)}).`,
)
}
}
if (severities.length === 0) {
throw new Error(
`Invalid config file at "${configPath}". Please add valid severities (${SEVERITIES.join(
", ",
)}).`,
)
}
const errors: Error[] = [] const errors: Error[] = []
let isValid = true let isValid = true
const loader = ora(`Validating HTML (W3C)...`).start() const loader = ora(`Validating HTML (W3C)...`).start()
@ -95,34 +138,37 @@ export class HTMLValidatorCommand extends Command {
dataToValidate.map(async ({ data, type }) => { dataToValidate.map(async ({ data, type }) => {
try { try {
const options = { const options = {
format: 'json' as 'json' | undefined format: "json" as "json" | undefined,
} }
let result: ParsedJsonAsValidationResults | undefined let result: ParsedJsonAsValidationResults | undefined
if (type === 'url') { if (type === "url") {
result = await validateHTML({ result = await validateHTML({
url: data, url: data,
isLocal: true, isLocal: true,
...options ...options,
}) })
} else if (type === 'file') { } else if (type === "file") {
const htmlPath = path.resolve(CURRENT_DIRECTORY, data) const htmlPath = path.resolve(this.currentWorkingDirectory, data)
if (!(await isExistingPath(htmlPath))) { if (!(await isExistingPath(htmlPath))) {
throw new Error( throw new Error(
`No file found at ${htmlPath}. Please check the path.` `No file found at "${htmlPath}". Please check the path.`,
) )
} }
const html = await fs.promises.readFile(htmlPath, { const html = await fs.promises.readFile(htmlPath, {
encoding: 'utf-8' encoding: "utf-8",
}) })
result = await validateHTML({ result = await validateHTML({
data: html, data: html,
...options ...options,
}) })
} else { } else {
throw new Error('Invalid type') throw new Error("Invalid type")
} }
const hasErrors = result.messages.some((message) => { const hasErrors = result.messages.some((message) => {
return message.type === 'error' return (
severities.includes(message.type as Severity) ||
severities.includes(message.subType as Severity)
)
}) })
if (!hasErrors) { if (!hasErrors) {
results.push({ data, isSuccess: true }) results.push({ data, isSuccess: true })
@ -130,19 +176,32 @@ export class HTMLValidatorCommand extends Command {
results.push({ data, isSuccess: false }) results.push({ data, isSuccess: false })
const messagesTable: string[][] = [] const messagesTable: string[][] = []
for (const message of result.messages) { for (const message of result.messages) {
if (message.type === 'error') { if (
!severities.includes(message.type as Severity) &&
!severities.includes(message.subType as Severity)
) {
continue
}
const row: string[] = [] const row: string[] = []
if (message.type === "info") {
if (message.subType === "warning") {
row.push(chalk.yellow(message.subType))
} else {
row.push(chalk.blue(message.type))
}
} else {
row.push(chalk.red(message.type)) row.push(chalk.red(message.type))
}
row.push(message.message) row.push(message.message)
const violation = message as ValidationMessageLocationObject const violation = message as ValidationMessageLocationObject
if (violation.extract != null) { if (violation.extract != null) {
row.push( row.push(
`line: ${violation.lastLine}, column: ${violation.firstColumn}-${violation.lastColumn}` `line: ${violation.lastLine}, column: ${violation.firstColumn}-${violation.lastColumn}`,
) )
} }
messagesTable.push(row) messagesTable.push(row)
} }
}
errors.push({ data, messagesTable }) errors.push({ data, messagesTable })
isValid = false isValid = false
} }
@ -153,7 +212,7 @@ export class HTMLValidatorCommand extends Command {
errors.push({ data, messagesTable }) errors.push({ data, messagesTable })
} }
} }
}) }),
) )
if (!isValid) { if (!isValid) {
loader.fail() loader.fail()
@ -161,22 +220,22 @@ export class HTMLValidatorCommand extends Command {
for (const error of errors) { for (const error of errors) {
console.error(`\n${error.data}`) console.error(`\n${error.data}`)
console.error(table(error.messagesTable)) console.error(table(error.messagesTable))
console.error('------------------------------') console.error("------------------------------")
} }
console.error() console.error()
throw new Error('HTML validation (W3C) failed!') throw new Error("HTML validation (W3C) failed!")
} }
loader.succeed( loader.succeed(
`${chalk.bold.green('Success:')} HTML validation (W3C) passed! 🎉` `${chalk.bold.green("Success:")} HTML validation (W3C) passed! 🎉`,
) )
printResults(results) printResults(results)
return 0 return 0
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
console.error(`${chalk.bold.red('Error:')} ${error.message}`) console.error(`${chalk.bold.red("Error:")} ${error.message}`)
} else { } else {
console.error( console.error(
`${chalk.bold.red('Error:')} HTML validation (W3C) failed!` `${chalk.bold.red("Error:")} HTML validation (W3C) failed!`,
) )
} }
return 1 return 1

View File

@ -1,21 +1,416 @@
import tap from 'tap' import test from "node:test"
import { execa } from 'execa' import assert from "node:assert/strict"
import path from "node:path"
import { PassThrough } from "node:stream"
import { cli } from '../cli.js' import sinon from "sinon"
import { HTMLValidatorCommand } from '../HTMLValidatorCommand.js' import { execa } from "execa"
import { table } from "table"
import chalk from "chalk"
import logSymbols from "log-symbols"
await tap.test('html-w3c-validator', async (t) => { import { cli } from "../cli.js"
await t.test('should be instance of the command', async (t) => { import {
HTMLValidatorCommand,
CONFIG_FILE_NAME,
SEVERITIES,
} from "../HTMLValidatorCommand.js"
const FIXTURES_PATH = path.join(process.cwd(), "src", "__test__", "fixtures")
await test("html-w3c-validator", async (t) => {
t.afterEach(() => {
sinon.restore()
})
await t.test("should be instance of the command", async () => {
const command = cli.process([]) const command = cli.process([])
t.equal(command instanceof HTMLValidatorCommand, true) assert(command instanceof HTMLValidatorCommand)
}) })
await t.test('succeeds and validate the html correctly', async (t) => { await t.test(
const exampleURL = new URL('../../example', import.meta.url) "succeeds and validate the html correctly (example)",
async () => {
const exampleURL = new URL("../../example", import.meta.url)
process.chdir(exampleURL.pathname) process.chdir(exampleURL.pathname)
await execa('rimraf', ['node_modules']) await execa("rimraf", ["node_modules"])
await execa('npm', ['install']) await execa("npm", ["install"])
const { exitCode } = await execa('npm', ['run', 'test:html-w3c-validator']) const { exitCode } = await execa("npm", [
t.equal(exitCode, 0) "run",
"test:html-w3c-validator",
])
assert.strictEqual(exitCode, 0)
},
)
await t.test(
"succeeds and validate the html correctly (example without working directory)",
async () => {
const logs: string[] = []
sinon.stub(console, "log").value((log: string) => {
logs.push(log)
})
const consoleLogSpy = sinon.spy(console, "log")
const stream = new PassThrough()
const exitCode = await cli.run([], {
stdin: process.stdin,
stdout: stream,
stderr: stream,
})
stream.end()
assert.strictEqual(exitCode, 0)
assert.strictEqual(
consoleLogSpy.calledWith(
logSymbols.success,
"./example/build/index.html",
),
true,
logs.join("\n"),
)
assert.strictEqual(
consoleLogSpy.calledWith(
logSymbols.success,
"./example/build/about.html",
),
true,
logs.join("\n"),
)
},
)
await t.test(
"succeeds and validate the html correctly (fixture)",
async () => {
const workingDirectory = path.join(FIXTURES_PATH, "success")
const logs: string[] = []
sinon.stub(console, "log").value((log: string) => {
logs.push(log)
})
const consoleLogSpy = sinon.spy(console, "log")
const stream = new PassThrough()
const exitCode = await cli.run(
[`--current-working-directory=${workingDirectory}`],
{
stdin: process.stdin,
stdout: stream,
stderr: stream,
},
)
stream.end()
assert.strictEqual(exitCode, 0)
assert.strictEqual(
consoleLogSpy.calledWith(logSymbols.success, "./build/index.html"),
true,
logs.join("\n"),
)
assert.strictEqual(
consoleLogSpy.calledWith(logSymbols.success, "./build/about.html"),
true,
logs.join("\n"),
)
},
)
await t.test("fails with not found config", async () => {
const workingDirectory = path.join(FIXTURES_PATH, "error-config-not-found")
const configPath = path.join(workingDirectory, CONFIG_FILE_NAME)
const errors: string[] = []
sinon.stub(console, "error").value((error: string) => {
errors.push(error)
})
const consoleErrorSpy = sinon.spy(console, "error")
const stream = new PassThrough()
const exitCode = await cli.run(
[`--current-working-directory=${workingDirectory}`],
{
stdin: process.stdin,
stdout: stream,
stderr: stream,
},
)
stream.end()
assert.strictEqual(exitCode, 1)
assert.strictEqual(
consoleErrorSpy.calledWith(
chalk.bold.red("Error:") +
` No config file found at ${configPath}. Please create "${CONFIG_FILE_NAME}".`,
),
true,
errors.join("\n"),
)
})
await t.test("fails with invalid JSON config", async () => {
const workingDirectory = path.join(
FIXTURES_PATH,
"error-config-invalid-json",
)
const configPath = path.join(workingDirectory, CONFIG_FILE_NAME)
const errors: string[] = []
sinon.stub(console, "error").value((error: string) => {
errors.push(error)
})
const consoleErrorSpy = sinon.spy(console, "error")
const stream = new PassThrough()
const exitCode = await cli.run(
[`--current-working-directory=${workingDirectory}`],
{
stdin: process.stdin,
stdout: stream,
stderr: stream,
},
)
stream.end()
assert.strictEqual(exitCode, 1)
assert.strictEqual(
consoleErrorSpy.calledWith(
chalk.bold.red("Error:") +
` Invalid config file at "${configPath}". Please check the JSON syntax.`,
),
true,
errors.join("\n"),
)
})
await t.test("fails with invalid URLs config", async () => {
const workingDirectory = path.join(
FIXTURES_PATH,
"error-config-invalid-urls",
)
const configPath = path.join(workingDirectory, CONFIG_FILE_NAME)
const errors: string[] = []
sinon.stub(console, "error").value((error: string) => {
errors.push(error)
})
const consoleErrorSpy = sinon.spy(console, "error")
const stream = new PassThrough()
const exitCode = await cli.run(
[`--current-working-directory=${workingDirectory}`],
{
stdin: process.stdin,
stdout: stream,
stderr: stream,
},
)
stream.end()
assert.strictEqual(exitCode, 1)
assert.strictEqual(
consoleErrorSpy.calledWith(
chalk.bold.red("Error:") +
` Invalid config file at "${configPath}". Please include an array of URLs.`,
),
true,
errors.join("\n"),
)
})
await t.test("fails with invalid files config", async () => {
const workingDirectory = path.join(
FIXTURES_PATH,
"error-config-invalid-files",
)
const configPath = path.join(workingDirectory, CONFIG_FILE_NAME)
const errors: string[] = []
sinon.stub(console, "error").value((error: string) => {
errors.push(error)
})
const consoleErrorSpy = sinon.spy(console, "error")
const stream = new PassThrough()
const exitCode = await cli.run(
[`--current-working-directory=${workingDirectory}`],
{
stdin: process.stdin,
stdout: stream,
stderr: stream,
},
)
stream.end()
assert.strictEqual(exitCode, 1)
assert.strictEqual(
consoleErrorSpy.calledWith(
chalk.bold.red("Error:") +
` Invalid config file at "${configPath}". Please include an array of files.`,
),
true,
errors.join("\n"),
)
})
await t.test("fails with invalid files and urls config", async () => {
const workingDirectory = path.join(
FIXTURES_PATH,
"error-config-invalid-files-and-urls",
)
const configPath = path.join(workingDirectory, CONFIG_FILE_NAME)
const errors: string[] = []
sinon.stub(console, "error").value((error: string) => {
errors.push(error)
})
const consoleErrorSpy = sinon.spy(console, "error")
const stream = new PassThrough()
const exitCode = await cli.run(
[`--current-working-directory=${workingDirectory}`],
{
stdin: process.stdin,
stdout: stream,
stderr: stream,
},
)
stream.end()
assert.strictEqual(exitCode, 1)
assert.strictEqual(
consoleErrorSpy.calledWith(
chalk.bold.red("Error:") +
` Invalid config file at "${configPath}". Please add URLs or files.`,
),
true,
errors.join("\n"),
)
})
await t.test("fails with invalid severities config", async () => {
const workingDirectory = path.join(
FIXTURES_PATH,
"error-config-invalid-severities",
)
const configPath = path.join(workingDirectory, CONFIG_FILE_NAME)
const errors: string[] = []
sinon.stub(console, "error").value((error: string) => {
errors.push(error)
})
const consoleErrorSpy = sinon.spy(console, "error")
const stream = new PassThrough()
const exitCode = await cli.run(
[`--current-working-directory=${workingDirectory}`],
{
stdin: process.stdin,
stdout: stream,
stderr: stream,
},
)
stream.end()
assert.strictEqual(exitCode, 1)
assert.strictEqual(
consoleErrorSpy.calledWith(
chalk.bold.red("Error:") +
` Invalid config file at "${configPath}". Please add valid severities (${SEVERITIES.join(
", ",
)}).`,
),
true,
errors.join("\n"),
)
})
await t.test("fails with invalid empty severities config", async () => {
const workingDirectory = path.join(
FIXTURES_PATH,
"error-config-invalid-severities-empty",
)
const configPath = path.join(workingDirectory, CONFIG_FILE_NAME)
const errors: string[] = []
sinon.stub(console, "error").value((error: string) => {
errors.push(error)
})
const consoleErrorSpy = sinon.spy(console, "error")
const stream = new PassThrough()
const exitCode = await cli.run(
[`--current-working-directory=${workingDirectory}`],
{
stdin: process.stdin,
stdout: stream,
stderr: stream,
},
)
stream.end()
assert.strictEqual(exitCode, 1)
assert.strictEqual(
consoleErrorSpy.calledWith(
chalk.bold.red("Error:") +
` Invalid config file at "${configPath}". Please add valid severities (${SEVERITIES.join(
", ",
)}).`,
),
true,
errors.join("\n"),
)
})
await t.test("fails with invalid files paths to check", async () => {
const workingDirectory = path.join(
FIXTURES_PATH,
"error-invalid-files-paths-to-check",
)
const htmlPath = path.resolve(workingDirectory, "index.html")
const errors: string[] = []
sinon.stub(console, "error").value((error: string) => {
errors.push(error)
})
const consoleErrorSpy = sinon.spy(console, "error")
const stream = new PassThrough()
const exitCode = await cli.run(
[`--current-working-directory=${workingDirectory}`],
{
stdin: process.stdin,
stdout: stream,
stderr: stream,
},
)
stream.end()
assert.strictEqual(exitCode, 1)
const messagesTable = [
[`No file found at "${htmlPath}". Please check the path.`],
]
assert.strictEqual(
consoleErrorSpy.calledWith(
chalk.bold.red("Error:") + " HTML validation (W3C) failed!",
),
true,
errors.join("\n"),
)
assert.strictEqual(
consoleErrorSpy.calledWith(table(messagesTable)),
true,
errors.join("\n"),
)
})
await t.test("fails with invalid W3C HTML", async () => {
const workingDirectory = path.join(FIXTURES_PATH, "error-invalid-w3c-html")
const errors: string[] = []
sinon.stub(console, "error").value((error: string) => {
errors.push(error)
})
const consoleErrorSpy = sinon.spy(console, "error")
const stream = new PassThrough()
const exitCode = await cli.run(
[`--current-working-directory=${workingDirectory}`],
{
stdin: process.stdin,
stdout: stream,
stderr: stream,
},
)
stream.end()
assert.strictEqual(exitCode, 1)
const messagesTable = [
[
chalk.yellow("warning"),
"Consider adding a “lang” attribute to the “html” start tag to declare the language of this document.",
"line: 2, column: 16-6",
],
]
assert.strictEqual(
consoleErrorSpy.calledWith(
chalk.bold.red("Error:") + " HTML validation (W3C) failed!",
),
true,
errors.join("\n"),
)
assert.strictEqual(
consoleErrorSpy.calledWith(table(messagesTable)),
true,
errors.join("\n"),
)
}) })
}) })

View File

@ -0,0 +1,4 @@
{
"urls": [],
"files": []
}

View File

@ -0,0 +1,3 @@
{
"files": "Invalid"
}

View File

@ -0,0 +1 @@
Invalid JSON

View File

@ -0,0 +1,4 @@
{
"files": ["./index.html"],
"severities": []
}

View File

@ -0,0 +1,9 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home</title>
</head>
<body></body>
</html>

View File

@ -0,0 +1,4 @@
{
"files": ["./index.html"],
"severities": ["errors-invalid"]
}

View File

@ -0,0 +1,9 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home</title>
</head>
<body></body>
</html>

View File

@ -0,0 +1,3 @@
{
"urls": "Invalid"
}

View File

@ -0,0 +1,3 @@
{
"files": ["./index.html"]
}

View File

@ -0,0 +1,3 @@
{
"files": ["./build/index.html", "./build/about.html"]
}

View File

@ -0,0 +1,9 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>About</title>
</head>
<body></body>
</html>

View File

@ -0,0 +1,9 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home</title>
</head>
<body></body>
</html>

View File

@ -0,0 +1,3 @@
{
"files": ["./build/index.html", "./build/about.html"]
}

View File

@ -0,0 +1,9 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>About</title>
</head>
<body></body>
</html>

View File

@ -0,0 +1,9 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home</title>
</head>
<body></body>
</html>

View File

@ -1,12 +1,12 @@
import { Builtins, Cli } from 'clipanion' import { Builtins, Cli } from "clipanion"
import { HTMLValidatorCommand } from './HTMLValidatorCommand.js' import { HTMLValidatorCommand } from "./HTMLValidatorCommand.js"
import { packageJSON } from './packageJSON.js' import { packageJSON } from "./packageJSON.js"
export const cli = new Cli({ export const cli = new Cli({
binaryLabel: packageJSON.name, binaryLabel: packageJSON.name,
binaryName: packageJSON.name, binaryName: packageJSON.name,
binaryVersion: packageJSON.version binaryVersion: packageJSON.version,
}) })
cli.register(Builtins.HelpCommand) cli.register(Builtins.HelpCommand)
cli.register(Builtins.VersionCommand) cli.register(Builtins.VersionCommand)

View File

@ -1,7 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
import { Cli } from 'clipanion' import { Cli } from "clipanion"
import { cli } from './cli.js' import { cli } from "./cli.js"
const [, , ...arguments_] = process.argv const [, , ...arguments_] = process.argv

View File

@ -1,5 +1,5 @@
import { readPackage } from 'read-pkg' import { readPackage } from "read-pkg"
export const packageJSON = await readPackage({ export const packageJSON = await readPackage({
cwd: new URL('..', import.meta.url) cwd: new URL("..", import.meta.url),
}) })

View File

@ -1,24 +1,26 @@
import fsMock from 'mock-fs' import test from "node:test"
import tap from 'tap' import assert from "node:assert/strict"
import { isExistingPath } from '../isExistingPath.js' import fsMock from "mock-fs"
await tap.test('utils/isExistingPath', async (t) => { import { isExistingPath } from "../isExistingPath.js"
await test("utils/isExistingPath", async (t) => {
t.afterEach(() => { t.afterEach(() => {
fsMock.restore() fsMock.restore()
}) })
await t.test('should return true if the file exists', async () => { await t.test("should return true if the file exists", async () => {
fsMock({ fsMock({
'/file.txt': '' "/file.txt": "",
}) })
t.equal(await isExistingPath('/file.txt'), true) assert.strictEqual(await isExistingPath("/file.txt"), true)
}) })
await t.test("should return false if the file doesn't exists", async () => { await t.test("should return false if the file doesn't exists", async () => {
fsMock({ fsMock({
'/file.txt': '' "/file.txt": "",
}) })
t.equal(await isExistingPath('/randomfile.txt'), false) assert.strictEqual(await isExistingPath("/randomfile.txt"), false)
}) })
}) })

View File

@ -1,4 +1,4 @@
import fs from 'node:fs' import fs from "node:fs"
export const isExistingPath = async (path: string): Promise<boolean> => { export const isExistingPath = async (path: string): Promise<boolean> => {
try { try {

View File

@ -1,14 +1,14 @@
{ {
"extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext",
"lib": ["ESNext"], "lib": ["ESNext"],
"moduleResolution": "node", "module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./build", "outDir": "./build",
"rootDir": "./src", "rootDir": "./src",
"noEmit": true, "noEmit": true,
"strict": true, "exactOptionalPropertyTypes": false,
"skipLibCheck": true, "checkJs": false
"esModuleInterop": true
} }
} }