UNPKG

alm

Version:

The best IDE for TypeScript

197 lines (179 loc) 7.11 kB
/** * From : https://raw.githubusercontent.com/Microsoft/vscode/master/extensions/json/server/src/jsonSchemaService.ts * * The original has a lot of code (around XHRs and looking up schemas mainly) that we do not need * * We simply need the `JSONSchemaService.resolveSchemaContent` * - And the class `ResolvedSchema` (not sure I need this yet, but good container for errors) */ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; import Json = require('../core/jsonc-parser'); import {JSONSchema, JSONSchemaMap} from '../core/jsonSchema'; import Strings = require('../utils/strings'); import Parser = require('../core/jsonParser'); import {localize} from "../core/localize"; import * as utils from "../../../../../common/utils"; type Thenable<T> = Promise<T>; /** * Register our schemas * Mostly downloaded from http://json.schemastore.org/ * * New additions need to * - Download and put in schemas * - Update utils.ts for "supportedConfigFileNames" */ export const schemas: { fileName: string, content: JSONSchema }[] = []; Object.keys(utils.supportedAutocompleteConfigFileNames).forEach(fileName => { const rawContent = require(`../schemas/${fileName}`); resolveSchemaContent(rawContent); // Mutates it in place schemas.push({ fileName, content: rawContent }); }); export function getSchemaForResource(filePath): Promise<ResolvedSchema> { const fileName = utils.getFileName(filePath); const schema = schemas.find(s=>s.fileName === fileName).content; return Promise.resolve(new ResolvedSchema(schema)); } /** * Copied straight out of JSONSchemaService * Resolves *links* in schema definitions * NOTE: Mutates the argument `schema` too! */ export function resolveSchemaContent(schema: JSONSchema): Thenable<ResolvedSchema> { let resolveErrors: string[] = []; let findSection = (schema: JSONSchema, path: string): any => { if (!path) { return schema; } let current: any = schema; path.substr(1).split('/').some((part) => { current = current[part]; return !current; }); return current; }; let resolveLink = (node: any, linkedSchema: JSONSchema, linkPath: string): void => { let section = findSection(linkedSchema, linkPath); if (section) { for (let key in section) { if (section.hasOwnProperty(key) && !node.hasOwnProperty(key)) { node[key] = section[key]; } } } else { resolveErrors.push(localize('json.schema.invalidref', '$ref \'{0}\' in {1} can not be resolved.', linkPath, linkedSchema.id)); } delete node.$ref; }; let resolveExternalLink = (node: any, uri: string, linkPath: string): Thenable<any> => { return this.getOrAddSchemaHandle(uri).getUnresolvedSchema().then(unresolvedSchema => { if (unresolvedSchema.errors.length) { let loc = linkPath ? uri + '#' + linkPath : uri; resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0])); } resolveLink(node, unresolvedSchema.schema, linkPath); return resolveRefs(node, unresolvedSchema.schema); }); }; let resolveRefs = (node: JSONSchema, parentSchema: JSONSchema): Thenable<any> => { let toWalk : JSONSchema[] = [node]; let seen: JSONSchema[] = []; let openPromises: Thenable<any>[] = []; let collectEntries = (...entries: JSONSchema[]) => { for (let entry of entries) { if (typeof entry === 'object') { toWalk.push(entry); } } }; let collectMapEntries = (...maps: JSONSchemaMap[]) => { for (let map of maps) { if (typeof map === 'object') { for (let key in map) { let entry = map[key]; toWalk.push(entry); } } } }; let collectArrayEntries = (...arrays: JSONSchema[][]) => { for (let array of arrays) { if (Array.isArray(array)) { toWalk.push.apply(toWalk, array); } } }; while (toWalk.length) { let next = toWalk.pop(); if (seen.indexOf(next) >= 0) { continue; } seen.push(next); if (next.$ref) { let segments = next.$ref.split('#', 2); if (segments[0].length > 0) { openPromises.push(resolveExternalLink(next, segments[0], segments[1])); continue; } else { resolveLink(next, parentSchema, segments[1]); } } collectEntries(next.items as any, next.additionalProperties as any, next.not); collectMapEntries(next.definitions, next.properties, next.patternProperties, <JSONSchemaMap> next.dependencies); collectArrayEntries(next.anyOf, next.allOf, next.oneOf, <JSONSchema[]> next.items); } return Promise.all(openPromises); }; return resolveRefs(schema, schema).then(_ => new ResolvedSchema(schema, resolveErrors)); } export class ResolvedSchema { public schema: JSONSchema; public errors: string[]; constructor(schema: JSONSchema, errors: string[] = []) { this.schema = schema; this.errors = errors; } public getSection(path: string[]): JSONSchema { return this.getSectionRecursive(path, this.schema); } private getSectionRecursive(path: string[], schema: JSONSchema): JSONSchema { if (!schema || path.length === 0) { return schema; } let next = path.shift(); if (schema.properties && schema.properties[next]) { return this.getSectionRecursive(path, schema.properties[next]); } else if (schema.patternProperties) { Object.keys(schema.patternProperties).forEach((pattern) => { let regex = new RegExp(pattern); if (regex.test(next)) { return this.getSectionRecursive(path, schema.patternProperties[pattern]); } }); } else if (schema.additionalProperties) { return this.getSectionRecursive(path, schema.additionalProperties as any); } else if (next.match('[0-9]+')) { if (schema.items) { return this.getSectionRecursive(path, schema.items as any); } else if (Array.isArray(schema.items)) { try { let index = parseInt(next, 10); if (schema.items[index]) { return this.getSectionRecursive(path, schema.items[index]); } return null; } catch (e) { return null; } } } return null; } }