Skip to content

Commit

Permalink
cli: Remove some deprecations, improve console output, improve typings (
Browse files Browse the repository at this point in the history
#4455)

* Remove some deprecated execution paths

* Tweaks
  • Loading branch information
hyperupcall authored Feb 17, 2025
1 parent 8786550 commit e5a8d75
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 70 deletions.
118 changes: 55 additions & 63 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import fsCb from 'node:fs'
import readline from 'node:readline'
import util from 'node:util'

import _AjvDraft04 from 'ajv-draft-04'
import AjvDraft04 from 'ajv-draft-04'
import { Ajv as AjvDraft06And07 } from 'ajv'
import _Ajv2019 from 'ajv/dist/2019.js'
import _Ajv2020 from 'ajv/dist/2020.js'
import _addFormats from 'ajv-formats'
import Ajv2019 from 'ajv/dist/2019.js'
import Ajv2020 from 'ajv/dist/2020.js'
import addFormats from 'ajv-formats'
import { ajvFormatsDraft2019 } from '@hyperupcall/ajv-formats-draft2019'
import schemasafe from '@exodus/schemasafe'
import TOML from 'smol-toml'
Expand All @@ -26,24 +26,6 @@ import fetch from 'node-fetch'
* @import { Ora } from 'ora'
*/

/**
* Ajv defines types, but they don't work when importing the library with
* ESM syntax. Tweaking `jsconfig.json` with `esModuleInterop` didn't seem
* to fix things, so manually set the types with a cast. This issue is
* tracked upstream at https://github.com/ajv-validator/ajv/issues/2132.
*/
/** @type {typeof _AjvDraft04.default} */
const AjvDraft04 = /** @type {any} */ (_AjvDraft04)

/** @type {typeof _Ajv2019.default} */
const Ajv2019 = /** @type {any} */ (_Ajv2019)

/** @type {typeof _Ajv2020.default} */
const Ajv2020 = /** @type {any} */ (_Ajv2020)

/** @type {typeof _addFormats.default} */
const addFormats = /** @type {any} */ (_addFormats)

// Declare constants.
const AjvDraft06SchemaJson = await readJsonFile(
'node_modules/ajv/dist/refs/json-schema-draft-06.json',
Expand Down Expand Up @@ -275,7 +257,7 @@ async function toFile(/** @type {string} */ schemaPath) {
text,
json: await readDataFile({ filepath: schemaPath, text }),
name: path.basename(schemaPath),
path: schemaPath,
path: schemaPath.replace(/^\.\//u, ''),
}
}

Expand All @@ -288,34 +270,42 @@ async function readDataFile(
try {
return JSON.parse(obj.text)
} catch (err) {
printErrorAndExit(err, [`Failed to parse JSON file "${obj.filepath}"`])
printErrorAndExit(err, [
`Failed to parse JSON file "./${obj.filepath}"`,
])
}
break
case '.jsonc':
try {
return jsoncParser.parse(obj.text)
} catch (err) {
printErrorAndExit(err, [`Failed to parse JSONC file "${obj.filepath}"`])
printErrorAndExit(err, [
`Failed to parse JSONC file "./${obj.filepath}"`,
])
}
break
case '.yaml':
case '.yml':
try {
return YAML.parse(obj.text)
} catch (err) {
printErrorAndExit(err, [`Failed to parse YAML file "${obj.filepath}"`])
printErrorAndExit(err, [
`Failed to parse YAML file "./${obj.filepath}"`,
])
}
break
case '.toml':
try {
return TOML.parse(obj.text)
} catch (err) {
printErrorAndExit(err, [`Failed to parse TOML file "${obj.filepath}"`])
printErrorAndExit(err, [
`Failed to parse TOML file "./${obj.filepath}"`,
])
}
break
default:
printErrorAndExit(new Error(), [
`Unable to handle file extension "${fileExtension}" for file "${obj.filepath}"`,
`Unable to handle file extension "${fileExtension}" for file "./${obj.filepath}"`,
])
break
}
Expand Down Expand Up @@ -586,7 +576,7 @@ async function taskCheck() {
// Check schema-validation.jsonc.
await assertFileValidatesAgainstSchema(
SchemaValidationFile,
'./src/schema-validation.schema.json',
'src/schema-validation.schema.json',
)
await assertFilePassesJsonLint(await toFile(SchemaValidationFile), {
ignoreComments: true,
Expand Down Expand Up @@ -655,7 +645,7 @@ async function taskCheck() {
spinner.fail()
printErrorAndExit(
err,
[`Failed to create Ajv instance for schema "${schemaFile.path}"`],
[`Failed to create Ajv instance for schema "./${schemaFile.path}"`],
JSON.stringify({ options, schemaDialect, isFullStrictMode }, null, 2),
)
}
Expand All @@ -666,7 +656,7 @@ async function taskCheck() {
} catch (err) {
spinner.fail()
printErrorAndExit(err, [
`Failed to compile schema file "${schemaFile.path}"`,
`Failed to compile schema file "./${schemaFile.path}"`,
])
}

Expand All @@ -681,7 +671,7 @@ async function taskCheck() {
printErrorAndExit(
validate.err,
[
`Schema validation failed ./${testFile.path}`,
`Schema validation failed for test file "./${testFile.path}"`,
`Showing first error out of ${validate.errors?.length ?? '?'} total error(s)`,
],
util.formatWithOptions(
Expand All @@ -697,8 +687,8 @@ async function taskCheck() {
if (validate(testFile.json)) {
spinner.fail()
printErrorAndExit(new Error(), [
`Schema validation succeeded but was supposed to fail ./${testFile.path}`,
`For schema ${schemaFile.path}`,
`Schema validation succeeded for test file "./${testFile.path}", but was supposed to fail `,
`For schema "./${schemaFile.path}"`,
])
}
},
Expand All @@ -723,7 +713,7 @@ async function taskCheckStrict() {
validateFn = ajv.compile(metaSchemaFile.json)
} catch (err) {
printErrorAndExit(err, [
`Failed to compile schema file ${metaSchemaFile.path}`,
`Failed to compile schema file "./${metaSchemaFile.path}"`,
])
}

Expand All @@ -736,7 +726,7 @@ async function taskCheckStrict() {
printErrorAndExit(
validate.err,
[
`Schema validation failed ./${schemaFile.path}`,
`Schema validation failed "./${schemaFile.path}"`,
`Showing first error out of ${validate.errors?.length ?? '?'} total error(s)`,
],
util.formatWithOptions(
Expand Down Expand Up @@ -855,8 +845,8 @@ async function assertFileSystemIsValid() {
const schemaPath = path.join(SchemaDir, testDir + '.json')
if (!(await exists(schemaPath))) {
printErrorAndExit(new Error(), [
`Failed to find a schema file at "${schemaPath}"`,
`Expected schema file computed from directory at "${path.join(rootTestDir, testDir)}"`,
`Failed to find a schema file at "./${schemaPath}"`,
`Expected schema file computed from directory at "./${path.join(rootTestDir, testDir)}"`,
])
}
}
Expand Down Expand Up @@ -999,7 +989,7 @@ async function assertCatalogJsonLocalUrlsMustRefFile() {
// Check if schema file exist or not.
if (!exists(path.join(SchemaDir, filename))) {
printErrorAndExit(new Error(), [
`Expected schema file to exist at "${path.join(SchemaDir, filename)}", but no file found`,
`Expected schema file to exist at "./${path.join(SchemaDir, filename)}", but no file found`,
`Schema file path inferred from catalog entry with a "url" of "${catalogUrl}" in file "${CatalogFile}"`,
])
}
Expand Down Expand Up @@ -1114,7 +1104,7 @@ async function assertTestFileHasSchemaPragma(
}
} else {
printErrorAndExit(new Error(), [
`Failed to find schema pragma for YAML File "${testFile.path}"`,
`Failed to find schema pragma for YAML File "./${testFile.path}"`,
`Expected first line of file to be "${expected}"`,
`But, found first line of file to be "${firstLine}"`,
`Append "--fix" to the command line to automatically fix all fixable issues`,
Expand All @@ -1140,7 +1130,7 @@ async function assertTestFileHasSchemaPragma(
}
} else {
printErrorAndExit(new Error(), [
`Failed to find schema pragma for TOML File "${testFile.path}"`,
`Failed to find schema pragma for TOML File "./${testFile.path}"`,
`Expected first line of file to be "${expected}"`,
`But, found first line of file to be "${firstLine}"`,
`Append "--fix" to the command line to automatically fix all fixable issues`,
Expand Down Expand Up @@ -1196,14 +1186,14 @@ function assertSchemaValidationJsonHasValidSkipTest() {

if (FoldersPositiveTest.includes(folderName)) {
printErrorAndExit(new Error(), [
`Did not expect to find positive test directory at "${path.join(TestPositiveDir, folderName)}"`,
`Did not expect to find positive test directory at "./${path.join(TestPositiveDir, folderName)}"`,
`Because filename "${schemaName}" is listed under "skiptest", it should not have any positive test files`,
])
}

if (FoldersNegativeTest.includes(folderName)) {
printErrorAndExit(new Error(), [
`Did not expect to find negative test directory at "${path.join(TestNegativeDir, folderName)}"`,
`Did not expect to find negative test directory at "./${path.join(TestNegativeDir, folderName)}"`,
`Because filename "${schemaName}" is listed under "skiptest", it should not have any negative test files`,
])
}
Expand All @@ -1218,7 +1208,7 @@ function assertFileHasCorrectExtensions(
) {
if (!allowedExtensions.includes(path.parse(pathname).ext)) {
printErrorAndExit(new Error(), [
`Expected schema file "${pathname}" to have a valid file extension`,
`Expected schema file "./${pathname}" to have a valid file extension`,
`Valid file extensions: ${JSON.stringify(allowedExtensions, null, 2)}`,
])
}
Expand All @@ -1241,7 +1231,7 @@ function assertFileHasNoBom(/** @type {DataFile} */ file) {

if (bomFound) {
printErrorAndExit(new Error(), [
`File must not have ${bom.name} BOM: ${file.path}`,
`Expected to have no BOM (${bom.name} BOM) in file "./${file.path}"`,
])
}
}
Expand All @@ -1263,7 +1253,7 @@ async function assertFilePassesJsonLint(
})
} catch (err) {
printErrorAndExit(err, [
`Failed strict jsonlint parse of file "${path.basename(file.path)}"`,
`Failed strict jsonlint parse of file "./${file.path}"`,
])
}
}
Expand All @@ -1288,7 +1278,7 @@ async function assertFileValidatesAgainstSchema(
printErrorAndExit(
new Error(),
[
`Failed to validate file "${path.basename(filepath)}" against schema file "${schemaFilepath}"`,
`Failed to validate file "${filepath}" against schema file "./${schemaFilepath}"`,
`Showing first error out of ${ajv.errors?.length ?? '?'} total error(s)`,
],
util.formatWithOptions({ colors: true }, '%O', ajv.errors?.[0] ?? '???'),
Expand All @@ -1304,7 +1294,7 @@ async function assertSchemaHasValidSchemaField(
)
if (!schemaDialectUrls.includes(schema.json.$schema)) {
printErrorAndExit(new Error(), [
`Schema file has invalid or missing '$schema' keyword => ${schema.name}`,
`Invalid or missing '$schema' keyword in schema file "${schema.name}"`,
`Valid schemas: ${JSON.stringify(schemaDialectUrls)}`,
])
}
Expand All @@ -1315,9 +1305,10 @@ async function assertSchemaHasValidSchemaField(
).map((schemaDialect) => schemaDialect.url)
if (tooHighSchemas.includes(schema.json.$schema)) {
printErrorAndExit(new Error(), [
`Schema version is too high => in file ${schema.name}`,
`Schema version '${schema.json.$schema}' is not supported by many editors and IDEs`,
`Schema file "${schema.path}" must use a lower schema version.`,
`Found a too high schema version in file "./${schema.path}"`,
`Schema version "${schema.json.$schema}" is not supported by many editors and IDEs`,
`We recommend using a lower schema version.`,
`To ignore this error, append to the "highSchemaVersion" key in "${SchemaValidationFile}"`,
])
}
}
Expand All @@ -1337,23 +1328,23 @@ async function assertSchemaHasValidIdField(/** @type {SchemaFile} */ schema) {
if (schemasWithDollarlessId.includes(schema.json.$schema)) {
if (schema.json.id === undefined) {
printErrorAndExit(new Error(), [
`Missing property 'id' for schema 'src/schemas/json/${schema.name}'`,
`Missing property 'id' for schema "./${path.join(SchemaDir, schema.name)}"`,
])
}
schemaId = schema.json.id
} else {
if (schema.json.$id === undefined) {
printErrorAndExit(new Error(), [
`Missing property '$id' for schema 'src/schemas/json/${schema.name}'`,
`Missing property '$id' for schema "./${path.join(SchemaDir, schema.name)}"`,
])
}
schemaId = schema.json.$id
}

if (!schemaId.startsWith('https://') && !schemaId.startsWith('http://')) {
printErrorAndExit(new Error(), [
schemaId,
`Schema id/$id must begin with 'https://' or 'http://' for schema 'src/schemas/json/${schema.name}'`,
`Expected schema id/$id to begin with 'https://' or 'http://'`,
`Found schema with value of "${schemaId}" in "./${path.join(SchemaDir, schema.name)}"`,
])
}
}
Expand All @@ -1369,31 +1360,31 @@ async function assertSchemaHasCorrectMetadata(
if (schemasWithDollarlessId.includes(schema.json.$schema)) {
if (schema.json.$id) {
printErrorAndExit(new Error(), [
`Expected to find correct metadata on schema file "${schema.path}"`,
`Expected to find correct metadata on schema file "./${schema.path}"`,
`Bad property of '$id'; expected 'id' for this schema version`,
])
}

if (schema.json.id !== `https://json.schemastore.org/${schema.name}`) {
printErrorAndExit(new Error(), [
`Expected to find correct metadata on schema file "${schema.path}"`,
`Incorrect property 'id' for schema 'src/schemas/json/${schema.name}'`,
`Expected to find correct metadata on schema file "./${schema.path}"`,
`Incorrect property 'id' for schema "./${path.join(SchemaDir, schema.name)}"`,
`Expected value of "https://json.schemastore.org/${schema.name}"`,
`Found value of "${schema.json.id}"`,
])
}
} else {
if (schema.json.id) {
printErrorAndExit(new Error(), [
`Expected to find correct metadata on schema file "${schema.path}"`,
`Expected to find correct metadata on schema file "./${schema.path}"`,
`Bad property of 'id'; expected '$id' for this schema version`,
])
}

if (schema.json.$id !== `https://json.schemastore.org/${schema.name}`) {
printErrorAndExit(new Error(), [
`Expected to find correct metadata on schema file "${schema.path}"`,
`Incorrect property '$id' for schema 'src/schemas/json/${schema.name}'`,
`Expected to find correct metadata on schema file "./${schema.path}"`,
`Incorrect property '$id' for schema "./${path.join(SchemaDir, schema.name)}"`,
`Expected value of "https://json.schemastore.org/${schema.name}"`,
`Found value of "${schema.json.$id}"`,
])
Expand All @@ -1412,7 +1403,8 @@ async function assertSchemaNoSmartQuotes(/** @type {SchemaFile} */ schema) {
for (const quote of smartQuotes) {
if (line.includes(quote)) {
printErrorAndExit(new Error(), [
`Schema file should not have a smart quote: ${schema.path}:${++i}`,
`Expected file to have no smart quotes`,
`Found smart quotes in file "./${schema.path}:${++i}"`,
])
}
}
Expand All @@ -1424,7 +1416,7 @@ async function assertTopLevelRefIsStandalone(/** @type {SchemaFile} */ schema) {
for (const [member] of Object.entries(schema.json)) {
if (member !== '$ref') {
printErrorAndExit(new Error(), [
`Schemas that reference a remote schema must only have $ref as a property. Found property "${member}" for ${schema.name}`,
`Schemas that reference a remote schema must only have $ref as a property. Found property "${member}" for "${schema.name}"`,
])
}
}
Expand Down
7 changes: 0 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@
"prettier:fix": "prettier --config .prettierrc.cjs --ignore-path .gitignore --write .",
"eslint": "eslint ./cli.js",
"eslint:fix": "eslint --fix ./cli.js",
"new-schema": "echo \"WARNING: Please use 'node ./cli.js' instead of 'npm run'. This method for execution will be removed.\" && node --no-deprecation ./cli.js new-schema",
"check": "echo \"WARNING: Please use 'node ./cli.js' instead of 'npm run'. This method for execution will be removed.\" && node ./cli.js check",
"check-strict": "echo \"WARNING: Please use 'node ./cli.js' instead of 'npm run'. This method for execution will be removed.\" && node ./cli.js check-strict",
"check-remote": "echo \"WARNING: Please use 'node ./cli.js' instead of 'npm run'. This method for execution will be removed.\" && node ./cli.js check-remote",
"report": "echo \"WARNING: Please use 'node ./cli.js' instead of 'npm run'. This method for execution will be removed.\" && node ./cli.js report",
"maintenance": "echo \"WARNING: Please use 'node ./cli.js' instead of 'npm run'. This method for execution will be removed.\" && node ./cli.js maintenance",
"lint": "echo \"WARNING: Please use 'node ./cli.js' instead of 'npm run'. This method for execution will be removed.\" && node ./cli.js lint",
"build": "echo \"WARNING: Please use 'node ./cli.js' instead of 'npm run'. This method for execution will be removed.\" && npm run eslint && node ./cli.js check && ./scripts/dirty_repository_check.sh"
},
"devDependencies": {
Expand Down

0 comments on commit e5a8d75

Please sign in to comment.