UNPKG

openapi-typescript

Version:

Generate TypeScript types from Swagger OpenAPI specs

189 lines 7.79 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveSchema = exports.VIRTUAL_JSON_URL = void 0; const node_fetch_1 = __importDefault(require("node-fetch")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const url_1 = require("url"); const slash_1 = __importDefault(require("slash")); const mime_1 = __importDefault(require("mime")); const js_yaml_1 = __importDefault(require("js-yaml")); const kleur_1 = require("kleur"); const utils_1 = require("./utils"); exports.VIRTUAL_JSON_URL = `file:///_json`; function parseSchema(schema, type) { if (type === "YAML") { try { return js_yaml_1.default.load(schema); } catch (err) { throw new Error(`YAML: ${err.toString()}`); } } else { try { return JSON.parse(schema); } catch (err) { throw new Error(`JSON: ${err.toString()}`); } } } function isFile(url) { return url.protocol === "file:"; } function resolveSchema(url) { if (url.startsWith("http://") || url.startsWith("https://")) { return new url_1.URL(url); } const localPath = path_1.default.isAbsolute(url) ? new url_1.URL("", `file://${(0, slash_1.default)(url)}`) : new url_1.URL(url, `file://${(0, slash_1.default)(process.cwd())}/`); if (!fs_1.default.existsSync(localPath)) { throw new Error(`Could not locate ${url}`); } else if (fs_1.default.statSync(localPath).isDirectory()) { throw new Error(`${localPath} is a directory not a file`); } return localPath; } exports.resolveSchema = resolveSchema; function parseHttpHeaders(httpHeaders) { const finalHeaders = {}; for (const [k, v] of Object.entries(httpHeaders)) { if (typeof v === "string") { finalHeaders[k] = v; } else { try { const stringVal = JSON.stringify(v); finalHeaders[k] = stringVal; } catch (err) { console.error((0, kleur_1.red)(`Cannot parse key: ${k} into JSON format. Continuing with the next HTTP header that is specified`)); } } } return finalHeaders; } async function load(schema, options) { const urlCache = options.urlCache || new Set(); const isJSON = schema instanceof url_1.URL === false; let schemaID = isJSON ? new url_1.URL(exports.VIRTUAL_JSON_URL).href : schema.href; const schemas = options.schemas; if (isJSON) { schemas[schemaID] = schema; } else { if (urlCache.has(schemaID)) return options.schemas; urlCache.add(schemaID); let contents = ""; let contentType = ""; const schemaURL = schema; if (isFile(schemaURL)) { contents = await fs_1.default.promises.readFile(schemaURL, "utf8"); contentType = mime_1.default.getType(schemaID) || ""; } else { const headers = { "User-Agent": "openapi-typescript", }; if (options.auth) headers.Authorizaton = options.auth; if (options.httpHeaders) { const parsedHeaders = parseHttpHeaders(options.httpHeaders); for (const [k, v] of Object.entries(parsedHeaders)) { headers[k] = v; } } const res = await (0, node_fetch_1.default)(schemaID, { method: options.httpMethod || "GET", headers }); contentType = res.headers.get("Content-Type") || ""; contents = await res.text(); } const isYAML = contentType === "application/openapi+yaml" || contentType === "text/yaml"; const isJSON = contentType === "application/json" || contentType === "application/json5" || contentType === "application/openapi+json"; if (isYAML) { schemas[schemaID] = parseSchema(contents, "YAML"); } else if (isJSON) { schemas[schemaID] = parseSchema(contents, "JSON"); } else { try { schemas[schemaID] = parseSchema(contents, "JSON"); } catch (err1) { try { schemas[schemaID] = parseSchema(contents, "YAML"); } catch (err2) { throw new Error(`Unknown format${contentType ? `: "${contentType}"` : ""}. Only YAML or JSON supported.`); } } } } const refPromises = []; schemas[schemaID] = JSON.parse(JSON.stringify(schemas[schemaID]), (k, v) => { if (k !== "$ref" || typeof v !== "string") return v; const { url: refURL } = (0, utils_1.parseRef)(v); if (refURL) { const isRemoteURL = refURL.startsWith("http://") || refURL.startsWith("https://"); if (isJSON && !isRemoteURL) { throw new Error(`Can’t load URL "${refURL}" from dynamic JSON. Load this schema from a URL instead.`); } const nextURL = isRemoteURL ? new url_1.URL(refURL) : new url_1.URL((0, slash_1.default)(refURL), schema); refPromises.push(load(nextURL, { ...options, urlCache }).then((subschemas) => { for (const subschemaURL of Object.keys(subschemas)) { schemas[subschemaURL] = subschemas[subschemaURL]; } })); return v.replace(refURL, nextURL.href); } return v; }); await Promise.all(refPromises); if (schemaID === options.rootURL.href) { for (const subschemaURL of Object.keys(schemas)) { schemas[subschemaURL] = JSON.parse(JSON.stringify(schemas[subschemaURL]), (k, v) => { if (k !== "$ref" || typeof v !== "string") return v; if (!v.includes("#")) return v; const { url, parts } = (0, utils_1.parseRef)(v); if (url && new url_1.URL(url).href !== options.rootURL.href) { const relativeURL = isFile(new url_1.URL(url)) && isFile(options.rootURL) ? path_1.default.posix.relative(path_1.default.posix.dirname(options.rootURL.href), url) : url; return `external["${relativeURL}"]["${parts.join('"]["')}"]`; } if (!url && subschemaURL !== options.rootURL.href) { const relativeURL = isFile(new url_1.URL(subschemaURL)) && isFile(options.rootURL) ? path_1.default.posix.relative(path_1.default.posix.dirname(options.rootURL.href), subschemaURL) : subschemaURL; return `external["${relativeURL}"]["${parts.join('"]["')}"]`; } const [base, ...rest] = parts; return `${base}["${rest.join('"]["')}"]`; }); if (subschemaURL !== options.rootURL.href) { const relativeURL = isFile(new url_1.URL(subschemaURL)) && isFile(options.rootURL) ? path_1.default.posix.relative(path_1.default.posix.dirname(options.rootURL.href), subschemaURL) : subschemaURL; if (relativeURL !== subschemaURL) { schemas[relativeURL] = schemas[subschemaURL]; delete schemas[subschemaURL]; } } } } return schemas; } exports.default = load; //# sourceMappingURL=load.js.map