diff --git a/package-lock.json b/package-lock.json index bd4488e..8808e6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,22 +1,26 @@ { "name": "hubl", - "version": "1.3.2", + "version": "1.4.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hubl", - "version": "1.3.2", + "version": "1.4.2", "dependencies": { "@hubspot/local-dev-lib": "^3.1.0", + "@hubspot/project-parsing-lib": "0.2.0", "dayjs": "^1.11.7", + "debounce": "1.2.1", "findup-sync": "^5.0.0", "fs-extra": "^9.0.1", "husky": "^7.0.4", + "jsonc-parser": "^3.3.1", "semver": "^7.5.2", "set-value": ">=4.0.1" }, "devDependencies": { + "@types/debounce": "^1.2.1", "@types/findup-sync": "^4.0.2", "@types/js-yaml": "^4.0.5", "@types/node": "^15.12.4", @@ -50,7 +54,7 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", + "version": "7.19.1", "dev": true, "license": "MIT", "engines": { @@ -58,14 +62,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.25.9", + "version": "7.18.6", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" }, "engines": { "node": ">=6.9.0" @@ -146,15 +149,6 @@ "node": ">=12" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "dev": true, @@ -182,21 +176,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/@eslint/eslintrc/node_modules/argparse": { "version": "1.0.10", "dev": true, @@ -206,11 +185,11 @@ } }, "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.4.0", + "version": "4.3.4", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "ms": "2.1.2" }, "engines": { "node": ">=6.0" @@ -233,15 +212,13 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.2", "dev": true, "license": "MIT" }, "node_modules/@hubspot/local-dev-lib": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/@hubspot/local-dev-lib/-/local-dev-lib-3.5.5.tgz", - "integrity": "sha512-nmwc+r7LbYPfRhorAL7/VAysvD6ucE7NJ4Y+UbY+9UGT2OUNV+b3s0b/EECFX/SfT1TK90VGJ7KafIwu0kao3g==", + "version": "3.5.4", "license": "Apache-2.0", "dependencies": { "address": "2.0.2", @@ -363,6 +340,53 @@ "node": ">=4" } }, + "node_modules/@hubspot/project-parsing-lib": { + "version": "0.2.0", + "license": "Apache-2.0", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@hubspot/local-dev-lib": "^3.1.0" + } + }, + "node_modules/@hubspot/project-parsing-lib/node_modules/ajv": { + "version": "8.17.1", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@hubspot/project-parsing-lib/node_modules/ajv-formats": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@hubspot/project-parsing-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "license": "MIT" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.5.0", "dev": true, @@ -377,11 +401,11 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.4.0", + "version": "4.3.4", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "ms": "2.1.2" }, "engines": { "node": ">=6.0" @@ -392,26 +416,31 @@ } } }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", "dev": true, "license": "BSD-3-Clause" }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", + "version": "0.3.2", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", + "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", + "version": "3.1.0", "dev": true, "license": "MIT", "engines": { @@ -419,7 +448,7 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.2.1", + "version": "1.1.2", "dev": true, "license": "MIT", "engines": { @@ -427,30 +456,30 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.6", + "version": "0.3.2", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", + "version": "1.4.14", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", + "version": "0.3.9", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, "node_modules/@tsconfig/node10": { - "version": "1.0.11", + "version": "1.0.9", "dev": true, "license": "MIT" }, @@ -465,17 +494,22 @@ "license": "MIT" }, "node_modules/@tsconfig/node16": { - "version": "1.0.4", + "version": "1.0.3", "dev": true, "license": "MIT" }, "node_modules/@types/braces": { - "version": "3.0.5", + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/debounce": { + "version": "1.2.4", "dev": true, "license": "MIT" }, "node_modules/@types/eslint": { - "version": "9.6.1", + "version": "8.4.10", "dev": true, "license": "MIT", "dependencies": { @@ -484,7 +518,7 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.7", + "version": "3.7.4", "dev": true, "license": "MIT", "dependencies": { @@ -493,30 +527,30 @@ } }, "node_modules/@types/estree": { - "version": "1.0.7", + "version": "0.0.51", "dev": true, "license": "MIT" }, "node_modules/@types/findup-sync": { - "version": "4.0.5", + "version": "4.0.2", "dev": true, "license": "MIT", "dependencies": { - "@types/micromatch": "^4.0.0" + "@types/micromatch": "*" } }, "node_modules/@types/js-yaml": { - "version": "4.0.9", + "version": "4.0.5", "dev": true, "license": "MIT" }, "node_modules/@types/json-schema": { - "version": "7.0.15", + "version": "7.0.11", "dev": true, "license": "MIT" }, "node_modules/@types/micromatch": { - "version": "4.0.9", + "version": "4.0.2", "dev": true, "license": "MIT", "dependencies": { @@ -525,28 +559,20 @@ }, "node_modules/@types/node": { "version": "15.14.9", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/node-fetch": { - "version": "2.6.12", + "version": "2.6.3", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/node-fetch/node_modules/@types/node": { - "version": "22.15.15", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" + "form-data": "^3.0.0" } }, "node_modules/@types/semver": { - "version": "7.7.0", + "version": "7.3.13", "dev": true, "license": "MIT" }, @@ -563,66 +589,58 @@ "@types/node": "*" } }, - "node_modules/@types/yauzl/node_modules/@types/node": { - "version": "22.15.15", - "license": "MIT", - "optional": true, - "dependencies": { - "undici-types": "~6.21.0" - } - }, "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", + "version": "1.11.1", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", + "version": "1.11.1", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", + "version": "1.11.1", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", + "version": "1.11.1", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", + "version": "1.11.1", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", + "version": "1.11.1", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", + "version": "1.11.1", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", + "version": "1.11.1", "dev": true, "license": "MIT", "dependencies": { @@ -630,7 +648,7 @@ } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", + "version": "1.11.1", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -638,67 +656,67 @@ } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", + "version": "1.11.1", "dev": true, "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", + "version": "1.11.1", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", + "version": "1.11.1", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", + "version": "1.11.1", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", + "version": "1.11.1", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", + "version": "1.11.1", "dev": true, "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/ast": "1.11.1", "@xtuc/long": "4.2.2" } }, @@ -757,7 +775,7 @@ } }, "node_modules/acorn": { - "version": "8.14.1", + "version": "8.8.1", "dev": true, "license": "MIT", "bin": { @@ -767,13 +785,18 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-walk": { - "version": "8.3.4", + "version": "8.2.0", "dev": true, "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, "engines": { "node": ">=0.4.0" } @@ -786,14 +809,14 @@ } }, "node_modules/ajv": { - "version": "8.17.1", + "version": "6.12.6", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { "type": "github", @@ -801,14 +824,11 @@ } }, "node_modules/ajv-keywords": { - "version": "5.1.0", + "version": "3.5.2", "dev": true, "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, "peerDependencies": { - "ajv": "^8.8.2" + "ajv": "^6.9.1" } }, "node_modules/ansi-colors": { @@ -893,6 +913,18 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "dev": true, @@ -948,17 +980,17 @@ } }, "node_modules/braces": { - "version": "3.0.3", + "version": "3.0.2", "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "fill-range": "^7.0.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.24.5", + "version": "4.21.4", "dev": true, "funding": [ { @@ -968,18 +1000,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001716", - "electron-to-chromium": "^1.5.149", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" }, "bin": { "browserslist": "cli.js" @@ -1041,7 +1069,7 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001717", + "version": "1.0.30001445", "dev": true, "funding": [ { @@ -1051,10 +1079,6 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" } ], "license": "CC-BY-4.0" @@ -1097,7 +1121,7 @@ } }, "node_modules/chrome-trace-event": { - "version": "1.0.4", + "version": "1.0.3", "dev": true, "license": "MIT", "engines": { @@ -1134,7 +1158,7 @@ "license": "MIT" }, "node_modules/colorette": { - "version": "2.0.20", + "version": "2.0.19", "dev": true, "license": "MIT" }, @@ -1206,7 +1230,7 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.6", + "version": "7.0.3", "dev": true, "license": "MIT", "dependencies": { @@ -1219,7 +1243,7 @@ } }, "node_modules/dayjs": { - "version": "1.11.13", + "version": "1.11.7", "license": "MIT" }, "node_modules/debounce": { @@ -1233,10 +1257,6 @@ "ms": "2.0.0" } }, - "node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "license": "MIT" - }, "node_modules/deep-is": { "version": "0.1.4", "dev": true, @@ -1307,7 +1327,7 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.150", + "version": "1.4.284", "dev": true, "license": "ISC" }, @@ -1339,7 +1359,7 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.1", + "version": "5.12.0", "dev": true, "license": "MIT", "dependencies": { @@ -1351,19 +1371,18 @@ } }, "node_modules/enquirer": { - "version": "2.4.1", + "version": "2.3.6", "dev": true, "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" + "ansi-colors": "^4.1.1" }, "engines": { "node": ">=8.6" } }, "node_modules/envinfo": { - "version": "7.14.0", + "version": "7.8.1", "dev": true, "license": "MIT", "bin": { @@ -1388,7 +1407,7 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", + "version": "0.9.3", "dev": true, "license": "MIT" }, @@ -1402,21 +1421,8 @@ "node": ">= 0.4" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/escalade": { - "version": "3.2.0", + "version": "3.1.1", "dev": true, "license": "MIT", "engines": { @@ -1536,21 +1542,6 @@ "node": ">=4" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/eslint/node_modules/argparse": { "version": "1.0.10", "dev": true, @@ -1560,11 +1551,11 @@ } }, "node_modules/eslint/node_modules/debug": { - "version": "4.4.0", + "version": "4.3.4", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "ms": "2.1.2" }, "engines": { "node": ">=6.0" @@ -1595,8 +1586,8 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", + "node_modules/eslint/node_modules/ms": { + "version": "2.1.2", "dev": true, "license": "MIT" }, @@ -1645,7 +1636,7 @@ } }, "node_modules/esquery": { - "version": "1.6.0", + "version": "1.4.0", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1774,10 +1765,10 @@ } }, "node_modules/extract-zip/node_modules/debug": { - "version": "4.4.0", + "version": "4.3.4", "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "ms": "2.1.2" }, "engines": { "node": ">=6.0" @@ -1788,9 +1779,12 @@ } } }, + "node_modules/extract-zip/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { @@ -1805,7 +1799,6 @@ }, "node_modules/fast-uri": { "version": "3.0.6", - "dev": true, "funding": [ { "type": "github", @@ -1845,7 +1838,7 @@ } }, "node_modules/fill-range": { - "version": "7.1.1", + "version": "7.0.1", "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -1895,21 +1888,12 @@ "node": ">= 10.13.0" } }, - "node_modules/flat": { - "version": "5.0.2", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, "node_modules/flat-cache": { - "version": "3.2.0", + "version": "3.0.4", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", + "flatted": "^3.1.0", "rimraf": "^3.0.2" }, "engines": { @@ -1917,12 +1901,12 @@ } }, "node_modules/flatted": { - "version": "3.3.3", + "version": "3.2.7", "dev": true, "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.9", + "version": "1.15.6", "funding": [ { "type": "individual", @@ -1940,12 +1924,12 @@ } }, "node_modules/form-data": { - "version": "4.0.2", + "version": "3.0.1", + "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -2124,7 +2108,7 @@ } }, "node_modules/globals": { - "version": "13.24.0", + "version": "13.19.0", "dev": true, "license": "MIT", "dependencies": { @@ -2148,9 +2132,20 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.11", + "version": "4.2.10", "license": "ISC" }, + "node_modules/has": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/has-flag": { "version": "4.0.0", "dev": true, @@ -2169,19 +2164,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasown": { "version": "2.0.2", "license": "MIT", @@ -2248,7 +2230,7 @@ } }, "node_modules/import-fresh": { - "version": "3.3.1", + "version": "3.3.0", "dev": true, "license": "MIT", "dependencies": { @@ -2263,7 +2245,7 @@ } }, "node_modules/import-local": { - "version": "3.2.0", + "version": "3.1.0", "dev": true, "license": "MIT", "dependencies": { @@ -2331,14 +2313,11 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", + "version": "2.11.0", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" + "has": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2424,14 +2403,6 @@ "node": ">= 10.13.0" } }, - "node_modules/jest-worker/node_modules/@types/node": { - "version": "22.15.15", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", "dev": true, @@ -2461,18 +2432,13 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "dev": true, - "license": "MIT" - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { - "version": "1.0.0", + "version": "0.4.1", "dev": true, "license": "MIT" }, @@ -2492,6 +2458,10 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "license": "MIT" + }, "node_modules/jsonfile": { "version": "6.1.0", "license": "MIT", @@ -2502,14 +2472,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/keyv": { - "version": "4.5.4", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, "node_modules/kind-of": { "version": "6.0.3", "dev": true, @@ -2572,6 +2534,16 @@ "dev": true, "license": "MIT" }, + "node_modules/lru-cache": { + "version": "6.0.0", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/make-error": { "version": "1.3.6", "dev": true, @@ -2611,10 +2583,10 @@ } }, "node_modules/micromatch": { - "version": "4.0.8", + "version": "4.0.5", "license": "MIT", "dependencies": { - "braces": "^3.0.3", + "braces": "^3.0.2", "picomatch": "^2.3.1" }, "engines": { @@ -2667,7 +2639,7 @@ } }, "node_modules/ms": { - "version": "2.1.3", + "version": "2.0.0", "license": "MIT" }, "node_modules/natural-compare": { @@ -2688,7 +2660,7 @@ "license": "MIT" }, "node_modules/node-fetch": { - "version": "2.7.0", + "version": "2.6.9", "dev": true, "license": "MIT", "dependencies": { @@ -2707,11 +2679,11 @@ } }, "node_modules/node-loader": { - "version": "2.1.0", + "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { - "loader-utils": "^2.0.3" + "loader-utils": "^2.0.0" }, "engines": { "node": ">= 10.13.0" @@ -2725,7 +2697,7 @@ } }, "node_modules/node-releases": { - "version": "2.0.19", + "version": "2.0.8", "dev": true, "license": "MIT" }, @@ -2771,7 +2743,7 @@ } }, "node_modules/optionator": { - "version": "0.9.4", + "version": "0.9.1", "dev": true, "license": "MIT", "dependencies": { @@ -2780,7 +2752,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.5" + "word-wrap": "^1.2.3" }, "engines": { "node": ">= 0.8.0" @@ -2913,7 +2885,7 @@ "license": "MIT" }, "node_modules/picocolors": { - "version": "1.1.1", + "version": "1.0.0", "dev": true, "license": "ISC" }, @@ -2947,7 +2919,7 @@ } }, "node_modules/prettier": { - "version": "2.8.8", + "version": "2.8.3", "dev": true, "license": "MIT", "bin": { @@ -2984,7 +2956,7 @@ "license": "MIT" }, "node_modules/pump": { - "version": "3.0.2", + "version": "3.0.0", "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -3078,27 +3050,23 @@ }, "node_modules/require-from-string": { "version": "2.0.2", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve": { - "version": "1.22.10", + "version": "1.22.1", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3178,14 +3146,13 @@ "license": "MIT" }, "node_modules/schema-utils": { - "version": "4.3.2", + "version": "3.1.1", "dev": true, "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { "node": ">= 10.13.0" @@ -3195,25 +3162,12 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/schema-utils/node_modules/ajv-formats": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/semver": { - "version": "7.7.1", + "version": "7.5.2", "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" }, @@ -3250,8 +3204,12 @@ "node": ">= 0.8" } }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, "node_modules/serialize-javascript": { - "version": "6.0.2", + "version": "6.0.0", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3402,11 +3360,11 @@ } }, "node_modules/source-map": { - "version": "0.7.4", + "version": "0.6.1", "dev": true, "license": "BSD-3-Clause", "engines": { - "node": ">= 8" + "node": ">=0.10.0" } }, "node_modules/source-map-support": { @@ -3418,14 +3376,6 @@ "source-map": "^0.6.0" } }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "dev": true, @@ -3496,7 +3446,7 @@ } }, "node_modules/table": { - "version": "6.9.0", + "version": "6.8.1", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3510,6 +3460,26 @@ "node": ">=10.0.0" } }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, "node_modules/tapable": { "version": "2.2.1", "dev": true, @@ -3519,12 +3489,12 @@ } }, "node_modules/terser": { - "version": "5.39.0", + "version": "5.16.1", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -3536,15 +3506,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.14", + "version": "5.3.6", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", + "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.14.1" }, "engines": { "node": ">= 10.13.0" @@ -3568,6 +3538,15 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "dev": true, @@ -3601,15 +3580,14 @@ "license": "MIT" }, "node_modules/ts-loader": { - "version": "9.5.2", + "version": "9.4.2", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" + "semver": "^7.3.4" }, "engines": { "node": ">=12.0.0" @@ -3620,7 +3598,7 @@ } }, "node_modules/ts-node": { - "version": "10.9.2", + "version": "10.9.1", "dev": true, "license": "MIT", "dependencies": { @@ -3695,7 +3673,7 @@ } }, "node_modules/typescript": { - "version": "4.9.5", + "version": "4.9.4", "dev": true, "license": "Apache-2.0", "bin": { @@ -3711,13 +3689,8 @@ "dev": true, "license": "MIT" }, - "node_modules/undici-types": { - "version": "6.21.0", - "devOptional": true, - "license": "MIT" - }, "node_modules/universalify": { - "version": "2.0.1", + "version": "2.0.0", "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -3751,7 +3724,7 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", + "version": "1.0.10", "dev": true, "funding": [ { @@ -3761,19 +3734,15 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" + "escalade": "^3.1.1", + "picocolors": "^1.0.0" }, "bin": { - "update-browserslist-db": "cli.js" + "browserslist-lint": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -3795,7 +3764,7 @@ } }, "node_modules/v8-compile-cache": { - "version": "2.4.0", + "version": "2.3.0", "dev": true, "license": "MIT" }, @@ -3812,7 +3781,7 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", + "version": "2.4.0", "dev": true, "license": "MIT", "dependencies": { @@ -3829,33 +3798,33 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.99.8", + "version": "5.76.0", "dev": true, "license": "MIT", "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", + "graceful-fs": "^4.2.9", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", + "schema-utils": "^3.1.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, "bin": { @@ -3921,12 +3890,11 @@ } }, "node_modules/webpack-merge": { - "version": "5.10.0", + "version": "5.8.0", "dev": true, "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", - "flat": "^5.0.2", "wildcard": "^2.0.0" }, "engines": { @@ -3973,7 +3941,7 @@ } }, "node_modules/wildcard": { - "version": "2.0.1", + "version": "2.0.0", "dev": true, "license": "MIT" }, @@ -3989,6 +3957,10 @@ "version": "1.0.2", "license": "ISC" }, + "node_modules/yallist": { + "version": "4.0.0", + "license": "ISC" + }, "node_modules/yauzl": { "version": "2.10.0", "license": "MIT", diff --git a/package.json b/package.json index 362eb94..3686634 100644 --- a/package.json +++ b/package.json @@ -588,14 +588,18 @@ }, "dependencies": { "@hubspot/local-dev-lib": "^3.1.0", + "@hubspot/project-parsing-lib": "0.2.0", "dayjs": "^1.11.7", + "debounce": "1.2.1", "findup-sync": "^5.0.0", "fs-extra": "^9.0.1", "husky": "^7.0.4", + "jsonc-parser": "^3.3.1", "semver": "^7.5.2", "set-value": ">=4.0.1" }, "devDependencies": { + "@types/debounce": "^1.2.1", "@types/findup-sync": "^4.0.2", "@types/js-yaml": "^4.0.5", "@types/node": "^15.12.4", diff --git a/src/extension.ts b/src/extension.ts index 5a2189f..7c54374 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,6 +16,7 @@ import { initializePanels } from './lib/panels'; import { initializeTracking, trackEvent } from './lib/tracking'; import { initializeGlobalState } from './lib/globalState'; import { initializeHubLAutoDetect } from './lib/autoDetect'; +import { initializeProjectConfigValidation } from './lib/projectConfigValidation'; export const activate = async (context: ExtensionContext) => { initializeCliLibLogger(); @@ -37,6 +38,7 @@ export const activate = async (context: ExtensionContext) => { initializeTerminal(); initializeStatusBar(context); initializeHubLAutoDetect(context); + initializeProjectConfigValidation(context); if (rootPath) { initializeConfig(rootPath); diff --git a/src/lib/fileHelpers.ts b/src/lib/fileHelpers.ts index 2be98d5..f9fd3e8 100644 --- a/src/lib/fileHelpers.ts +++ b/src/lib/fileHelpers.ts @@ -122,3 +122,17 @@ export const onClickCreateFolder = (extension: string, doAfter: Function) => { } }; }; + +export async function doesFileExist(uri: Uri): Promise { + try { + await workspace.fs.stat(uri); + return true; + } catch (err) { + return false; + } +} + +// VSCode doesn't provide a dirname function, that's in the github:/microsoft/vscode-uri package +export function dirname(uri: Uri): Uri { + return uri.with({ path: path.dirname(uri.path) }); +} diff --git a/src/lib/projectConfigValidation.ts b/src/lib/projectConfigValidation.ts new file mode 100644 index 0000000..b651e3c --- /dev/null +++ b/src/lib/projectConfigValidation.ts @@ -0,0 +1,391 @@ +import { + workspace, + Uri, + Diagnostic, + DiagnosticSeverity, + languages, + Range, + DiagnosticCollection, + ExtensionContext, + TextDocument, +} from 'vscode'; +import { + getIntermediateRepresentationSchema, + mapToInternalType, + createAjvInstance, + validateUid, + getInvalidJsonError, + getMissingTypeError, + getMissingConfigError, + getMissingAccountIdError, + getMissingRequiredFieldError, + getFailedToFetchSchemasError, + getUnsupportedTypeError, + AjvErrorKeyword, + ValidateFunction, + ErrorObject, + metafileExtension, + Components, + hsProjectJsonFilename, +} from '@hubspot/project-parsing-lib'; +import { getAccountId } from '@hubspot/local-dev-lib/config'; +import * as jsonc from 'jsonc-parser'; +import { debounce } from 'debounce'; +import { doesFileExist, dirname } from './fileHelpers'; +import { VALIDATION_DEBOUNCE_TIME } from './constants'; +import { + PlatformVersionSchemaCache, + SchemaAndValidator, + SchemaCacheEntry, +} from './types'; + +type ProjectConfigValidationState = { + diagnosticsCollection: DiagnosticCollection; + ajv: ReturnType; + schemaCache: PlatformVersionSchemaCache; +}; + +export function initializeProjectConfigValidation( + context: ExtensionContext +): void { + const state: ProjectConfigValidationState = { + diagnosticsCollection: languages.createDiagnosticCollection( + 'hubspot-project-config-schema' + ), + ajv: createAjvInstance(), + schemaCache: {}, + }; + context.subscriptions.push(state.diagnosticsCollection); + + workspace.onDidOpenTextDocument((doc) => validateDocument(state, doc)); + workspace.onDidChangeTextDocument( + debounce( + (event) => validateDocument(state, event.document), + VALIDATION_DEBOUNCE_TIME + ) + ); + workspace.onDidSaveTextDocument((doc) => validateDocument(state, doc)); + workspace.onDidCloseTextDocument((doc) => + state.diagnosticsCollection.delete(doc.uri) + ); + workspace.textDocuments.forEach((doc) => validateDocument(state, doc)); +} + +async function validateDocument( + state: ProjectConfigValidationState, + document: TextDocument +): Promise { + try { + const diagnostics = await validateDocumentDiagnostics(state, document); + state.diagnosticsCollection.set(document.uri, diagnostics); + } catch (error) { + console.error('Error validating document:', error); + } +} + +async function validateDocumentDiagnostics( + state: ProjectConfigValidationState, + document: TextDocument +): Promise { + if (!document.fileName.endsWith(metafileExtension)) { + return []; + } + const hsProjectFile = await findHsProjectFile(document.uri); + if (!hsProjectFile) { + return []; + } + const projectVersion = await getProjectVersion(hsProjectFile); + if (!projectVersion) { + return []; + } + // Any file that fails the above checks is one that we don't handle + let metaFile: Partial; + try { + metaFile = JSON.parse(document.getText()); + } catch (error) { + return [newErrorDiagnostic(getInvalidJsonError())]; + } + const root = jsonc.parseTree(document.getText()); + if (!root) { + // Should not happen, we've already parsed it above without jsonc + return [newErrorDiagnostic(getInvalidJsonError())]; + } + if (!metaFile.type) { + return [newErrorDiagnostic(getMissingTypeError())]; + } + const uidError = validateUid(metaFile.uid || ''); + if (uidError) { + const uidNode = jsonc.findNodeAtLocation(root, ['uid']); + return [ + newErrorDiagnostic( + uidError, + uidNode + ? new Range( + document.positionAt(uidNode.offset), + document.positionAt(uidNode.offset + uidNode.length) + ) + : undefined + ), + ]; + } + if (!metaFile.config) { + return [newErrorDiagnostic(getMissingConfigError())]; + } + const accountId = getAccountId(); + if (!accountId) { + return [ + newErrorDiagnostic( + `${getMissingAccountIdError()}. Authenticate account with the HubSpot CLI` + ), + ]; + } + const cachedValidator = await getCachedValidatorForExternalType( + state, + hsProjectFile, + projectVersion, + accountId, + metaFile.type + ); + if (cachedValidator.type === 'error') { + return [newErrorDiagnostic(cachedValidator.error)]; + } + if (cachedValidator.validator(metaFile['config'])) { + return []; + } + let diagnostics: Record = {}; + (cachedValidator.validator.errors || []).forEach((error) => { + const { message, nodePath, onlyKeyNode } = customizeAjvError(error); + if (!message) { + return; + } + const deduplicationKey = diagnosticDeduplicationKey( + nodePath, + error.keyword, + error.params + ); + // This is a duplicate error, skip it + if (deduplicationKey in diagnostics) { + return; + } + let node = jsonc.findNodeAtLocation(root, nodePath); + if (node && onlyKeyNode) { + // Sometimes we want to highlight the key that corresponds to a node + node = getKeyNode(node); + } + if (!node) { + // If we couldn't find the exact location, create a generic error + diagnostics[deduplicationKey] = newErrorDiagnostic(message); + return; + } + const startPos = document.positionAt(node.offset); + const endPos = document.positionAt(node.offset + node.length); + diagnostics[deduplicationKey] = newErrorDiagnostic( + message, + new Range(startPos, endPos) + ); + }); + return Object.values(diagnostics); +} + +function customizeAjvError(error: ErrorObject): { + message: string | undefined; + nodePath: (string | number)[]; + onlyKeyNode: boolean; +} { + let nodePath = [ + 'config', + ...error.instancePath + .split('/') + .slice(1) + .map((p) => (p.match(/\d+/) ? parseInt(p) : p)), + ]; + // For some errors, we only want to highlight the key + // that corresponds to the error, not the value that caused + // the error. For example, if there's an unknown field, we + // don't want to highlight the entire value to report this. + let onlyKeyNode = false; + let message = error.message; + switch (error.keyword) { + case AjvErrorKeyword.AdditionalItems: + message = `Too many items, need at most ${error.params.limit}`; + break; + case AjvErrorKeyword.AdditionalProperties: + nodePath.push(error.params.additionalProperty); + onlyKeyNode = true; + message = `Unknown field: ${error.params.additionalProperty}`; + break; + case AjvErrorKeyword.Dependencies: + message = `When you have this property, you must have all of these as well: ${error.params.deps}. You are missing at least: ${error.params.missingProperty}`; + nodePath.push(error.params.property); + onlyKeyNode = true; + break; + case AjvErrorKeyword.Enum: + // Enum errors often happen on types and have cascading effects. We let users know that other errors can lead to this one. + if (error.params.allowedValues) { + message = `Try one of: ${error.params.allowedValues.join(', ')}`; + } else { + message = `Value not allowed. Other errors can cause this`; + } + break; + case AjvErrorKeyword.Maximum: + case AjvErrorKeyword.Minimum: + case AjvErrorKeyword.ExclusiveMaximum: + case AjvErrorKeyword.ExclusiveMinimum: + message = `This value must ${error.params.comparison}${error.params.limit}`; + break; + case AjvErrorKeyword.MaxItems: + message = `Value is too long, needs at most ${error.params.limit} items`; + break; + case AjvErrorKeyword.MaxLength: + message = `Value is too long, needs at most ${error.params.limit} characters`; + break; + case AjvErrorKeyword.MaxProperties: + message = `Too many properties, need at most ${error.params.limit}`; + break; + case AjvErrorKeyword.MinItems: + message = `Value is too short, needs at least ${error.params.limit} items`; + break; + case AjvErrorKeyword.MinLength: + message = `Value is too short, needs at least ${error.params.limit} characters`; + break; + case AjvErrorKeyword.MinProperties: + message = `Too few properties, need at least ${error.params.limit}`; + break; + case AjvErrorKeyword.MultipleOf: + message = `This value must be a multiple of ${error.params.multipleOf}`; + break; + case AjvErrorKeyword.OneOf: + onlyKeyNode = true; + message = `Does not match any of the allowed schemas`; + break; + case AjvErrorKeyword.Pattern: + message = `Value does not match pattern: ${error.params.pattern}`; + break; + case AjvErrorKeyword.Required: + onlyKeyNode = true; + message = getMissingRequiredFieldError(error.params.missingProperty); + break; + case AjvErrorKeyword.Type: + message = `Incorrect type: ${error.message}`; + break; + default: + break; + } + return { message, nodePath, onlyKeyNode }; +} + +function newErrorDiagnostic(error: string, range?: Range): Diagnostic { + return new Diagnostic( + range || new Range(0, 0, 0, 0), + error, + DiagnosticSeverity.Error + ); +} + +function diagnosticDeduplicationKey( + nodePath: (string | number)[], + keyword: string, + params: any +): string { + return `${keyword}::${nodePath.join('/')}::${JSON.stringify(params)}`; +} + +function getKeyNode(node: jsonc.Node): jsonc.Node | undefined { + return node.parent?.type === 'property' ? node.parent.children?.[0] : node; +} + +async function getSchemasForAllTypes( + state: ProjectConfigValidationState, + hsProjectFile: Uri, + platformVersion: string, + accountId: number +): Promise { + // If schema cache has a schema for this version, return it + // If there is an error, we're going to return that too + if (state.schemaCache[platformVersion]) { + return state.schemaCache[platformVersion]; + } + try { + const response = await getIntermediateRepresentationSchema({ + projectSourceDir: hsProjectFile.fsPath, + platformVersion, + accountId, + }); + const schemasByType: Record = {}; + for (const [key, schema] of Object.entries(response)) { + schemasByType[key] = { schema }; + } + state.schemaCache[platformVersion] = { + type: 'schema', + schemasByType, + }; + } catch (error) { + const message = `${getFailedToFetchSchemasError()}. Reload your window or restart to try again`; + console.error(message, error); + state.schemaCache[platformVersion] = { + type: 'error', + error: message, + }; + } + return state.schemaCache[platformVersion]; +} + +async function getCachedValidatorForExternalType( + state: ProjectConfigValidationState, + hsProjectFile: Uri, + platformVersion: string, + accountId: number, + externalType: string +): Promise< + | { type: 'validator'; validator: ValidateFunction } + | { type: 'error'; error: string } +> { + const cachedSchemas = await getSchemasForAllTypes( + state, + hsProjectFile, + platformVersion, + accountId + ); + if (cachedSchemas.type === 'error') { + return cachedSchemas; + } + const internalType = mapToInternalType(externalType); + if (!(internalType in cachedSchemas.schemasByType)) { + return { type: 'error', error: getUnsupportedTypeError(externalType) }; + } + const schemaAndValidator = cachedSchemas.schemasByType[internalType]; + let validator = schemaAndValidator.validator; + if (!validator) { + validator = state.ajv.compile(schemaAndValidator.schema); + schemaAndValidator.validator = validator; + } + return { type: 'validator', validator }; +} + +async function findHsProjectFile(start: Uri): Promise { + let currentDir = dirname(start); + while (true) { + const current = Uri.joinPath(currentDir, hsProjectJsonFilename); + if (await doesFileExist(current)) { + return current; + } + const parent = dirname(currentDir); + if (parent.fsPath === currentDir.fsPath) { + return null; + } + currentDir = parent; + } +} + +async function getProjectVersion(hsProjectFile: Uri): Promise { + try { + const fileContents = await workspace.fs.readFile(hsProjectFile); + const config = JSON.parse(fileContents.toString()); + if ('platformVersion' in config) { + return config.platformVersion; + } + } catch (error) { + console.error(`Error reading ${hsProjectJsonFilename}:`, error); + } + return null; +} diff --git a/src/lib/types.ts b/src/lib/types.ts index e2c2b50..21351ec 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,3 +1,4 @@ +import { AnySchema, ValidateFunction } from '@hubspot/project-parsing-lib'; import { Command } from 'vscode'; export interface Link { @@ -31,3 +32,25 @@ export interface RemoteFsDirectory { export interface FileAssociations { [key: string]: string; } + +export type SchemaAndValidator = { + schema: AnySchema; + validator?: ValidateFunction; +}; + +export type SchemaCacheEntry = + | { + type: 'schema'; + schemasByType: Record< + string, // internal type + SchemaAndValidator + >; + } + | { type: 'error'; error: string }; + +// A schema cache maps a platform version to a set of schemas and validators indexed by the internal type. +// Validators are compiled on demand and then cached. +export type PlatformVersionSchemaCache = Record< + string, // platform version + SchemaCacheEntry +>;