140 lines
3.2 KiB
JavaScript
140 lines
3.2 KiB
JavaScript
const path = require("path");
|
|
const fs = require("fs");
|
|
const request = require("sync-request");
|
|
|
|
function readJsonStringFromFile(path) {
|
|
try {
|
|
const result = fs.readFileSync(path, "utf-8");
|
|
return result;
|
|
} catch (e) {
|
|
return undefined;
|
|
}
|
|
}
|
|
function readJsonStringFromUrl(url) {
|
|
try {
|
|
const res = request("GET", url);
|
|
return res.getBody("utf-8");
|
|
} catch (e) {
|
|
return undefined;
|
|
}
|
|
}
|
|
function readJsonString(pathOrUrl) {
|
|
if (pathOrUrl.startsWith("http")) {
|
|
return readJsonStringFromUrl(pathOrUrl);
|
|
}
|
|
return readJsonStringFromFile(pathOrUrl);
|
|
}
|
|
|
|
class SchemaCache {
|
|
constructor(schemaPath, extensionPath) {
|
|
this.schemaPath = schemaPath;
|
|
this.extensionPath = extensionPath;
|
|
this.cache = {};
|
|
this.contextStack = [];
|
|
this.byTitle = {};
|
|
}
|
|
|
|
load(name) {
|
|
// First try loading relative to the current context
|
|
let path = this.resolveRelativePath(name);
|
|
|
|
const existing = this.cache[path];
|
|
if (existing) {
|
|
return existing;
|
|
}
|
|
|
|
let jsonString = readJsonString(path);
|
|
if (jsonString === undefined) {
|
|
// Next try resolving relative to the base URL
|
|
const pathFromBase = this.resolvePathFromBase(name);
|
|
|
|
const existingFromBase = this.cache[pathFromBase];
|
|
if (existingFromBase) {
|
|
return existingFromBase;
|
|
}
|
|
|
|
jsonString = readJsonString(pathFromBase);
|
|
|
|
if (jsonString === undefined) {
|
|
console.warn(
|
|
`Could not resolve ${name}. Tried:\n * ${path}\n *${pathFromBase}`
|
|
);
|
|
return undefined;
|
|
}
|
|
|
|
path = pathFromBase;
|
|
}
|
|
|
|
const result = JSON.parse(jsonString);
|
|
result.sourcePath = path;
|
|
this.cache[path] = result;
|
|
|
|
const upperTitle = result.title.toUpperCase();
|
|
if (this.byTitle[upperTitle]) {
|
|
console.warn(
|
|
`*** Two schema files share the same title, things will be broken:\n ${this.byTitle[upperTitle].sourcePath}\n ${path}`
|
|
);
|
|
}
|
|
|
|
this.byTitle[upperTitle] = result;
|
|
|
|
return result;
|
|
}
|
|
|
|
loadExtension(name) {
|
|
const path = this.resolvePath(this.extensionPath, name);
|
|
|
|
const existing = this.cache[path];
|
|
if (existing) {
|
|
return existing;
|
|
}
|
|
|
|
const jsonString = readJsonString(path);
|
|
const result = JSON.parse(jsonString, "utf-8");
|
|
result.sourcePath = path;
|
|
this.cache[name] = result;
|
|
|
|
const upperTitle = result.title.toUpperCase();
|
|
if (this.byTitle[upperTitle]) {
|
|
console.warn(
|
|
`*** Two schema files share the same title, things will be broken:\n ${this.byTitle[upperTitle].sourcePath}\n ${path}`
|
|
);
|
|
}
|
|
|
|
this.byTitle[result.title.toUpperCase()] = result;
|
|
|
|
return result;
|
|
}
|
|
|
|
pushContext(schema) {
|
|
this.contextStack.push(schema);
|
|
}
|
|
|
|
popContext() {
|
|
this.contextStack.pop();
|
|
}
|
|
|
|
resolveRelativePath(name) {
|
|
if (this.contextStack.length === 0) {
|
|
return name;
|
|
}
|
|
|
|
const base = this.contextStack[this.contextStack.length - 1].sourcePath;
|
|
return this.resolvePath(base, name);
|
|
}
|
|
|
|
resolvePathFromBase(name) {
|
|
return this.resolvePath(this.schemaPath, name);
|
|
}
|
|
|
|
resolvePath(base, name) {
|
|
if (base.startsWith("http")) {
|
|
return new URL(name, base).href;
|
|
} else {
|
|
return path.resolve(base, "..", name);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = SchemaCache;
|