mirror of
https://github.com/TheGiddyLimit/homebrew.git
synced 2026-08-02 15:35:51 -04:00
feat: sync tests; migrate tests to shared lib
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
import {BrewCleanerHtml} from "./html-cleaner.js";
|
||||
|
||||
await BrewCleanerHtml.pRun();
|
||||
@@ -1,47 +0,0 @@
|
||||
import {isMainThread, parentPort} from "worker_threads";
|
||||
import {BrewCleanerHtml} from "./html-cleaner.js";
|
||||
|
||||
if (isMainThread) throw new Error(`Worker must not be started in main thread!`);
|
||||
|
||||
let isCancelled = false;
|
||||
|
||||
parentPort
|
||||
.on("message", async msg => {
|
||||
switch (msg.type) {
|
||||
case "init": {
|
||||
parentPort.postMessage({
|
||||
type: "ready",
|
||||
payload: {},
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "cancel": {
|
||||
isCancelled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case "work": {
|
||||
if (isCancelled) {
|
||||
parentPort.postMessage({
|
||||
type: "done",
|
||||
payload: {},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const {messages = []} = BrewCleanerHtml.getFileMessages({file: msg.payload.file});
|
||||
|
||||
parentPort.postMessage({
|
||||
type: "done",
|
||||
payload: {
|
||||
isError: !!messages.length,
|
||||
messages,
|
||||
},
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,177 +0,0 @@
|
||||
import {getCleanJson, ObjectWalker, Uf, Um} from "5etools-utils";
|
||||
import he from "he";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
import fs from "fs";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import url from "url";
|
||||
import {Worker} from "worker_threads";
|
||||
import {Deferred, WorkerList} from "5etools-utils/lib/WorkerList.js";
|
||||
|
||||
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
||||
|
||||
export class BrewCleanerHtml {
|
||||
static _LOG_TAG = `HTML`;
|
||||
|
||||
static _OPTS_SANITIZE = {
|
||||
allowedTags: [
|
||||
// region Custom things which look like tags
|
||||
"<$name$>",
|
||||
// endregion
|
||||
],
|
||||
allowedAttributes: {},
|
||||
};
|
||||
|
||||
static _getCleanFileMeta ({file}) {
|
||||
const fileData = Uf.readJsonSync(file);
|
||||
|
||||
const messages = [];
|
||||
|
||||
const {_meta, _test} = fileData;
|
||||
delete fileData._meta;
|
||||
delete fileData._test;
|
||||
|
||||
const keyStack = [];
|
||||
const objectStack = [];
|
||||
|
||||
const isInFoundryDescriptionEffect = () => {
|
||||
if (objectStack.at(-1)?.key !== "system.description.value") return false;
|
||||
return keyStack.at(-1) === "changes" && ["effects", "foundryEffects"].includes(keyStack.at(-2));
|
||||
};
|
||||
|
||||
const fileOut = ObjectWalker.walk({
|
||||
obj: fileData,
|
||||
filePath: file,
|
||||
primitiveHandlers: {
|
||||
string: (str, {filePath, lastKey}) => {
|
||||
if (lastKey === "value" && isInFoundryDescriptionEffect()) return str;
|
||||
|
||||
const clean = he.unescape(
|
||||
sanitizeHtml(
|
||||
str,
|
||||
this._OPTS_SANITIZE,
|
||||
),
|
||||
);
|
||||
|
||||
if (clean !== str) {
|
||||
const msg = `Sanitized ${keyStack.map(k => `"${k}"`).join(" -> ")}:\n${str}\n${clean}`;
|
||||
messages.push(msg);
|
||||
Um.info(this._LOG_TAG, msg);
|
||||
}
|
||||
|
||||
return clean;
|
||||
},
|
||||
preObject: (obj) => objectStack.push(obj),
|
||||
postObject: () => objectStack.pop(),
|
||||
preArray: (_, {lastKey}) => keyStack.push(lastKey),
|
||||
postArray: () => keyStack.pop(),
|
||||
},
|
||||
isModify: true,
|
||||
});
|
||||
|
||||
const out = {$schema: fileOut.$schema, _meta, _test};
|
||||
Object.assign(out, fileOut);
|
||||
|
||||
return {
|
||||
messages,
|
||||
out,
|
||||
};
|
||||
}
|
||||
|
||||
static async _pUpdateDir (dir) {
|
||||
Uf.listJsonFiles(dir)
|
||||
.forEach(file => {
|
||||
const {messages, out} = this._getCleanFileMeta({file})
|
||||
if (!messages?.length) return;
|
||||
|
||||
fs.writeFileSync(file, getCleanJson(out));
|
||||
});
|
||||
}
|
||||
|
||||
static async pRun () {
|
||||
await Uf.pRunOnDirs(
|
||||
async (dir) => {
|
||||
Um.info(this._LOG_TAG, `Sanitizing HTML in dir "${dir}"...`);
|
||||
await this._pUpdateDir(dir);
|
||||
},
|
||||
{
|
||||
isSerial: true,
|
||||
},
|
||||
);
|
||||
Um.info(this._LOG_TAG, "Done!");
|
||||
}
|
||||
|
||||
static getFileMessages ({file}) {
|
||||
return this._getCleanFileMeta({file});
|
||||
}
|
||||
|
||||
static async pGetErrorsOnDirsWorkers ({isFailFast = false} = {}) {
|
||||
Um.info(this._LOG_TAG, `Testing for HTML...`);
|
||||
|
||||
const cntWorkers = Math.max(1, os.cpus().length - 1);
|
||||
|
||||
const messages = [];
|
||||
|
||||
const fileQueue = [];
|
||||
Uf.runOnDirs((dir) => fileQueue.push(...Uf.listJsonFiles(dir)));
|
||||
|
||||
const workerList = new WorkerList();
|
||||
|
||||
let cntFailures = 0;
|
||||
const workers = [...new Array(cntWorkers)]
|
||||
.map(() => {
|
||||
// Relative `Worker` paths do not function in packages, so give an exact path
|
||||
const worker = new Worker(path.join(__dirname, "html-cleaner-test-worker"));
|
||||
|
||||
worker.on("message", (msg) => {
|
||||
switch (msg.type) {
|
||||
case "ready":
|
||||
case "done": {
|
||||
if (msg.payload.isError) {
|
||||
messages.push(...msg.payload.messages);
|
||||
|
||||
if (isFailFast) workers.forEach(worker => worker.postMessage({type: "cancel"}));
|
||||
}
|
||||
|
||||
if (worker.dIsActive) worker.dIsActive.resolve();
|
||||
workerList.add(worker);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
worker.on("error", e => {
|
||||
console.error(e);
|
||||
cntFailures++;
|
||||
});
|
||||
|
||||
worker.postMessage({
|
||||
type: "init",
|
||||
payload: {},
|
||||
});
|
||||
|
||||
return worker;
|
||||
});
|
||||
|
||||
while (fileQueue.length) {
|
||||
if (isFailFast && messages.length) break;
|
||||
|
||||
const file = fileQueue.shift();
|
||||
const worker = await workerList.get();
|
||||
|
||||
worker.dIsActive = new Deferred();
|
||||
worker.postMessage({
|
||||
type: "work",
|
||||
payload: {
|
||||
file,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(workers.map(it => it.dIsActive?.promise));
|
||||
await Promise.all(workers.map(it => it.terminate()));
|
||||
|
||||
return {messages, isUnknownError: !!cntFailures};
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import fs from "fs";
|
||||
import {DataTester, BraceCheck, EscapeCharacterCheck} from "5etools-utils";
|
||||
import * as Uf from "5etools-utils/lib/UtilFs.js";
|
||||
import {ImageUrlCheck} from "./test-file-contents/ImageUrlCheck.js";
|
||||
import {CopySourceCheck} from "./test-file-contents/CopySourceCheck.js";
|
||||
|
||||
const TIME_TAG = "\tRun duration";
|
||||
console.time(TIME_TAG);
|
||||
|
||||
async function main () {
|
||||
const dataTesters = [
|
||||
new BraceCheck(),
|
||||
new EscapeCharacterCheck(),
|
||||
new ImageUrlCheck(),
|
||||
new CopySourceCheck(),
|
||||
];
|
||||
DataTester.register({dataTesters});
|
||||
|
||||
await Uf.pRunOnDirs(
|
||||
async (dir) => {
|
||||
console.log(`Running on directory "${dir}"...`);
|
||||
await DataTester.pRun(
|
||||
dir,
|
||||
dataTesters,
|
||||
);
|
||||
},
|
||||
{
|
||||
isSerial: true,
|
||||
},
|
||||
);
|
||||
|
||||
const outMessage = DataTester.getLogReport(dataTesters);
|
||||
|
||||
if (outMessage) fs.writeFileSync("./_test/test-data.error.log", outMessage, "utf-8");
|
||||
|
||||
console.timeEnd(TIME_TAG);
|
||||
|
||||
if (outMessage) process.exit(1);
|
||||
}
|
||||
|
||||
export default main();
|
||||
@@ -1,103 +0,0 @@
|
||||
import {DataTesterBase} from "5etools-utils/lib/TestData.js";
|
||||
import {ObjectWalker} from "5etools-utils/lib/ObjectWalker.js";
|
||||
import {UtilSource} from "5etools-utils/lib/UtilSource.js";
|
||||
|
||||
export class CopySourceCheck extends DataTesterBase {
|
||||
static _FileState = class {
|
||||
sources;
|
||||
dependencies;
|
||||
internalCopies;
|
||||
|
||||
constructor (
|
||||
{
|
||||
contents,
|
||||
}
|
||||
) {
|
||||
this.sources = new Set(
|
||||
(contents._meta?.sources?.map(src => src?.json) || [])
|
||||
.filter(Boolean),
|
||||
);
|
||||
this.dependencies = Object.fromEntries(
|
||||
Object.entries(contents._meta?.dependencies || {})
|
||||
.map(([prop, arr]) => [prop, new Set(arr)]),
|
||||
);
|
||||
this.internalCopies = new Set(contents._meta?.internalCopies || []);
|
||||
}
|
||||
}
|
||||
|
||||
registerParsedFileCheckers (parsedJsonChecker) {
|
||||
parsedJsonChecker.registerFileHandler(this);
|
||||
}
|
||||
|
||||
handleFile (file, contents) {
|
||||
if (!file.includes("Kobold Press; Scarlet Citadel.json")) return;
|
||||
|
||||
const fileState = new this.constructor._FileState({contents});
|
||||
|
||||
Object.entries(contents)
|
||||
.filter(([prop, arr]) => {
|
||||
if (prop.startsWith("_")) return;
|
||||
if (!(arr instanceof Array)) return;
|
||||
|
||||
arr
|
||||
.forEach(ent => {
|
||||
const propStack = [prop];
|
||||
const inlineDependencies = new Set();
|
||||
|
||||
ObjectWalker.walk({
|
||||
obj: ent,
|
||||
filePath: file,
|
||||
primitiveHandlers: {
|
||||
preObject: this._onPreObject.bind(this, {fileState, propStack, inlineDependencies}),
|
||||
object: this._checkObject.bind(this, {fileState, propStack, inlineDependencies}),
|
||||
postObject: this._onPostObject.bind(this, {fileState, propStack, inlineDependencies}),
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_onPreObject ({fileState, propStack, inlineDependencies}, obj) {
|
||||
if (obj.type !== "statblockInline") return;
|
||||
|
||||
propStack.push(obj.dataType);
|
||||
(obj.dependencies || []).forEach(dep => inlineDependencies.add(dep));
|
||||
}
|
||||
|
||||
_onPostObject ({fileState, propStack, inlineDependencies}, obj) {
|
||||
if (obj.type !== "statblockInline") return;
|
||||
|
||||
propStack.pop();
|
||||
inlineDependencies.clear();
|
||||
}
|
||||
|
||||
_checkObject ({fileState, propStack, inlineDependencies}, obj, {filePath}) {
|
||||
if (!obj._copy?.source) return;
|
||||
|
||||
const prop = propStack.at(-1);
|
||||
const sourceCopy = obj._copy.source;
|
||||
|
||||
// Classes/subclasses have an alternate structure
|
||||
if (["class", "subclass"].includes(prop) && UtilSource.isSiteSource(sourceCopy)) {
|
||||
const classNameLower = obj._copy.className?.toLowerCase();
|
||||
if (
|
||||
fileState.dependencies[prop]?.has(classNameLower)
|
||||
|| inlineDependencies.has(classNameLower)
|
||||
) return;
|
||||
}
|
||||
|
||||
// If a "root" entity, i.e. not in a `statblockInline`, allow internal copies
|
||||
if (
|
||||
propStack.length === 1
|
||||
&& fileState.internalCopies.has(prop)
|
||||
&& fileState.sources.has(sourceCopy)
|
||||
) return;
|
||||
|
||||
if (
|
||||
fileState.dependencies[prop]?.has(sourceCopy)
|
||||
|| inlineDependencies.has(sourceCopy)
|
||||
) return;
|
||||
|
||||
this._addMessage(`Entity "${propStack.join(" -> ")}" "${obj.name}" "_copy" source "${sourceCopy}" did not match sources found in dependencies in file "${filePath}"\n`);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
import {DataTesterBase} from "5etools-utils/lib/TestData.js";
|
||||
import {ObjectWalker} from "5etools-utils/lib/ObjectWalker.js";
|
||||
|
||||
export class ImageUrlCheck extends DataTesterBase {
|
||||
static _URL_PREFIX_HOMEBREW_IMG = `https://raw.githubusercontent.com/TheGiddyLimit/homebrew-img/main/`;
|
||||
static _RE_HOMEBREW_IMG_PATH = /^(?<type>img|pdf)\/(?<source>[^/]+)\//;
|
||||
|
||||
static _FileState = class {
|
||||
sources;
|
||||
|
||||
constructor (
|
||||
{
|
||||
contents,
|
||||
}
|
||||
) {
|
||||
this.sources = new Set(
|
||||
[
|
||||
...(contents._meta?.sources?.map(src => src?.json) || [])
|
||||
.filter(Boolean)
|
||||
.map(srcJson => srcJson.replace(/:/g, "")),
|
||||
...(contents._test?.additionalImageSources || [])
|
||||
.map(srcJson => srcJson.replace(/:/g, "")),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
registerParsedFileCheckers (parsedJsonChecker) {
|
||||
parsedJsonChecker.registerFileHandler(this);
|
||||
}
|
||||
|
||||
handleFile (file, contents) {
|
||||
const fileState = new this.constructor._FileState({contents});
|
||||
|
||||
ObjectWalker.walk({
|
||||
obj: contents,
|
||||
filePath: file,
|
||||
primitiveHandlers: {
|
||||
object: this._checkObject.bind(this, {fileState}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_checkObject ({fileState}, obj, {filePath}) {
|
||||
if (obj.type !== "image" || obj.href?.type !== "external" || !obj.href?.url) return;
|
||||
|
||||
const {url} = obj.href;
|
||||
if (!url.toLowerCase().startsWith(this.constructor._URL_PREFIX_HOMEBREW_IMG.toLowerCase())) return;
|
||||
|
||||
const mPath = this.constructor._RE_HOMEBREW_IMG_PATH.exec(url.slice(this.constructor._URL_PREFIX_HOMEBREW_IMG.length));
|
||||
if (!mPath) {
|
||||
this._addMessage(`Unknown "homebrew-img" URL pattern in file "${filePath}": "${url}"\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
const {source, type} = mPath.groups;
|
||||
if (fileState.sources.has(source)) return;
|
||||
|
||||
this._addMessage(`Image source part "${source}" in "homebrew-img" ${type} URL did not match sources found in file "_meta" or "_test" in file "${filePath}": "${url}"\n`);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import {BrewCleanerHtml} from "../_node/html-cleaner.js";
|
||||
import {BrewCleanerHtml} from "5etools-utils";
|
||||
import {Um} from "5etools-utils";
|
||||
|
||||
const {messages, isUnknownError = false} = await BrewCleanerHtml.pGetErrorsOnDirsWorkers();
|
||||
|
||||
+4
-24
@@ -1,6 +1,5 @@
|
||||
import fs from "fs";
|
||||
import {BrewTester} from "5etools-utils";
|
||||
|
||||
const _TIME_TAG = "\tRun duration";
|
||||
const _ALLOWLIST_DIRS = new Set([
|
||||
"Adventurers League",
|
||||
"AL",
|
||||
@@ -141,25 +140,6 @@ const _ALLOWLIST_DIRS = new Set([
|
||||
"YARV",
|
||||
]);
|
||||
|
||||
async function main () {
|
||||
console.time(_TIME_TAG);
|
||||
|
||||
if (!fs.existsSync("_img")) {
|
||||
console.timeEnd(_TIME_TAG);
|
||||
return;
|
||||
}
|
||||
|
||||
const extraDirs = fs.readdirSync("_img")
|
||||
.filter(dir => !_ALLOWLIST_DIRS.has(dir));
|
||||
|
||||
if (!extraDirs.length) {
|
||||
console.timeEnd(_TIME_TAG);
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(`Extra directories found in "_img":\n${extraDirs.map(d => `\t${d}`).join("\n")}`);
|
||||
console.timeEnd(_TIME_TAG);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
export default main();
|
||||
await BrewTester.pTestImgDirectories({
|
||||
dirAllowlist: _ALLOWLIST_DIRS,
|
||||
});
|
||||
|
||||
Generated
+1
-338
@@ -10,9 +10,7 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"5etools-utils": "^0.15.3",
|
||||
"commander": "^14.0.3",
|
||||
"he": "^1.2.0",
|
||||
"sanitize-html": "^2.17.1"
|
||||
"commander": "^14.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/5etools-utils": {
|
||||
@@ -83,94 +81,6 @@
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/deepmerge": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domelementtype": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -212,43 +122,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
|
||||
@@ -275,70 +148,12 @@
|
||||
"integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/number-precision": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/number-precision/-/number-precision-1.6.0.tgz",
|
||||
"integrity": "sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/parse-srcset": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
|
||||
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.47",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.1.0",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
@@ -349,30 +164,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sanitize-html": {
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.1.tgz",
|
||||
"integrity": "sha512-ehFCW+q1a4CSOWRAdX97BX/6/PDEkCqw7/0JXZAGQV57FQB3YOkTa/rrzHPeJ+Aghy4vZAFfWMYyfxIiB7F/gw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"htmlparser2": "^8.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"parse-srcset": "^1.0.2",
|
||||
"postcss": "^8.3.11"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
|
||||
@@ -429,61 +220,6 @@
|
||||
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
|
||||
"dev": true
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
||||
"dev": true
|
||||
},
|
||||
"dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"domelementtype": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||
"dev": true
|
||||
},
|
||||
"domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"domutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3"
|
||||
}
|
||||
},
|
||||
"entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -506,30 +242,6 @@
|
||||
"type-fest": "^4.41.0"
|
||||
}
|
||||
},
|
||||
"he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||
"dev": true
|
||||
},
|
||||
"htmlparser2": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
||||
"dev": true
|
||||
},
|
||||
"is-stream": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
|
||||
@@ -548,67 +260,18 @@
|
||||
"integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==",
|
||||
"dev": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"dev": true
|
||||
},
|
||||
"number-precision": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/number-precision/-/number-precision-1.6.0.tgz",
|
||||
"integrity": "sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==",
|
||||
"dev": true
|
||||
},
|
||||
"parse-srcset": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
|
||||
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.47",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.1.0",
|
||||
"source-map-js": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||
"dev": true
|
||||
},
|
||||
"sanitize-html": {
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.1.tgz",
|
||||
"integrity": "sha512-ehFCW+q1a4CSOWRAdX97BX/6/PDEkCqw7/0JXZAGQV57FQB3YOkTa/rrzHPeJ+Aghy4vZAFfWMYyfxIiB7F/gw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"htmlparser2": "^8.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"parse-srcset": "^1.0.2",
|
||||
"postcss": "^8.3.11"
|
||||
}
|
||||
},
|
||||
"source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
|
||||
|
||||
+3
-5
@@ -9,10 +9,10 @@
|
||||
"build:index": "node _node/generate-index.js",
|
||||
"build:clean": "npm run build:clean:base",
|
||||
"build:clean:base": "node _node/clean-and-test.js",
|
||||
"build:clean:html": "node _node/clean-html.js",
|
||||
"build:clean:html": "clean-html",
|
||||
"test:json": "test-json-brew",
|
||||
"test:clean": "npm run build:clean:base -- --dry",
|
||||
"test:file-contents": "node _test/test-file-contents.js",
|
||||
"test:file-contents": "test-file-contents --img-repo-name homebrew-img --url-prefix-expected https://raw.githubusercontent.com/TheGiddyLimit/homebrew-img/main/",
|
||||
"test:file-locations": "test-file-locations",
|
||||
"test:file-names": "test-file-names",
|
||||
"test:file-props": "test-file-props",
|
||||
@@ -33,8 +33,6 @@
|
||||
"homepage": "https://github.com/TheGiddyLimit/homebrew#readme",
|
||||
"devDependencies": {
|
||||
"5etools-utils": "^0.15.3",
|
||||
"commander": "^14.0.3",
|
||||
"he": "^1.2.0",
|
||||
"sanitize-html": "^2.17.1"
|
||||
"commander": "^14.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user