pax_global_header00006660000000000000000000000064151470760030014515gustar00rootroot0000000000000052 comment=54a6ce82d4551ea043cfe734c2ed2335bd01c3e0 vscode-json-languageservice-5.7.2/000077500000000000000000000000001514707600300171245ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/.github/000077500000000000000000000000001514707600300204645ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/.github/assignment.yml000066400000000000000000000000631514707600300233560ustar00rootroot00000000000000 { perform: true, assignees: [ aeschli ] } vscode-json-languageservice-5.7.2/.github/workflows/000077500000000000000000000000001514707600300225215ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/.github/workflows/node.js.yml000066400000000000000000000011201514707600300245760ustar00rootroot00000000000000name: Node.js CI on: push: branches: [ main ] pull_request: branches: [ main ] permissions: {} jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: [22.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v4 with: persist-credentials: false - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm i - run: npm test vscode-json-languageservice-5.7.2/.gitignore000066400000000000000000000000651514707600300211150ustar00rootroot00000000000000lib/ node_modules/ .nyc_output/ coverage/ yarn.lock vscode-json-languageservice-5.7.2/.mocharc.json000066400000000000000000000001331514707600300215060ustar00rootroot00000000000000{ "ui": "tdd", "color": true, "spec": "./lib/umd/test", "recursive": true }vscode-json-languageservice-5.7.2/.npmignore000066400000000000000000000002421514707600300211210ustar00rootroot00000000000000.vscode/ .github/ build/ lib/**/test/ lib/**/example/ lib/**/*.js.map lib/*/*/*.d.ts src/ test/ .gitignore .travis.yml eslint.config.mjs gulpfile.js .mocharc.jsonvscode-json-languageservice-5.7.2/.vscode/000077500000000000000000000000001514707600300204655ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/.vscode/launch.json000066400000000000000000000016701514707600300226360ustar00rootroot00000000000000{ "version": "0.1.0", // List of configurations. Add new configurations or edit existing ones. "configurations": [ { "name": "Attach", "type": "node", "request": "attach", "port": 6004, "sourceMaps": true, "outFiles": [ "${workspaceRoot}/lib/umd/**/*.js" ] }, { "name": "Unit Tests", "type": "node", "request": "launch", "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", "stopOnEntry": false, "args": [ "--timeout", "999999", "--colors" ], "cwd": "${workspaceRoot}", "runtimeExecutable": null, "runtimeArgs": [], "env": {}, "sourceMaps": true, "outFiles": [ "${workspaceRoot}/lib/umd/**/*.js" ], "preLaunchTask": "npm: watch" }, { "name": "Attach Language Server", "type": "node", "request": "attach", "port": 6004, "sourceMaps": true, "outFiles": [ "${workspaceRoot}/server/out/**/*.js" ] } ] }vscode-json-languageservice-5.7.2/.vscode/settings.json000066400000000000000000000003561514707600300232240ustar00rootroot00000000000000{ "git.branchProtection": [ "main" ], "git.branchProtectionPrompt": "alwaysCommitToNewBranch", "git.branchRandomName.enable": true, "githubPullRequests.assignCreated": "${user}", "githubPullRequests.defaultMergeMethod": "squash" }vscode-json-languageservice-5.7.2/.vscode/tasks.json000066400000000000000000000005551514707600300225120ustar00rootroot00000000000000// See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format { "version": "2.0.0", "tasks": [ { "type": "npm", "script": "watch", "problemMatcher": "$tsc-watch", "isBackground": true, "presentation": { "reveal": "never" }, "group": { "kind": "build", "isDefault": true } } ] }vscode-json-languageservice-5.7.2/CHANGELOG.md000066400000000000000000000160671514707600300207470ustar00rootroot000000000000005.7.0 / 2026-01-12 ================ * the error code for `SchemaResolveError` is now 0x10000 plus the error code returned by the request service. 5.6.0 / 2025-05-28 ================ * added `Schema.enumSortTexts` and `Schema.enumDetails` to control the sort order and presentation of suggestions for enums 5.5.0 / 2025-03-25 ================ * `newJSONDocument` API now also accepts undefined as ASTRoots an optionally comment ranges. 5.3.1 / 2023-02-24 ================ * Fixing bugs in the sort feature 5.3.0 / 2023-02-15 ================ * new API `LanguageService.sort` for sorting all properties in a JSON document * new API `SortOptions` 5.2.0 / 2023-02-02 ================ * Added `SchemaConfiguration.folderUri` for folder-specific schema associations 5.1.1 / 2022-08/19 ================ * new type `SchemaDraft`, representing the offical JSON schema draft versions * new property `DocumentLanguageSettings.schemaDraft` to specify the schema version to use, if the schema does not contain a `$schema` property 5.1.0 / 2022-07-11 ================ * new API option `FormattingOptions.keepLines` to indicate the formatter should keep the initial line positions 5.0.0 / 2022-05-17 ================ * Update to `vscode-languageserver-types@3.17` * Add more schema support * Schema 2019-09: unevaluatedProperties, unevaluatedItems, minContains, maxContains, deprecated, dependentRequired, dependentSchemas, $defs, $anchor * Schema 2020-12: prefixItem 4.2.0 / 2022-01-25 ================ * new API `LanguageService.getLanguageStatus` * support for $ref with $id 4.1.6 / 2021-07-16 ================ * Replace minimatch with glob-to-regexp 4.1.0 / 2021-04-24 ================ * `SchemaConfiguration.fileMatch` now supports glob patterns (e.g. /foo/**/bar.json') 4.0.0 / 2020-12-14 ================ * Update to `vscode-languageserver-types@3.16` * Removed deprecated `findColorSymbols` 3.11.0 / 2020-11-30 ================ * new API `FormattingOptions.insertFinalNewline` 3.10.0 / 2020-11-03 ================ * new API `findLinks` return links for local `$ref` links. Replaces `findDefinition` which no longer returns results ( kept for API compatibility) 3.9.0 / 2020-09-28 ================= * new API `DocumentLanguageSettings.schemaValidation`. The severity of problems from schema validation. If set to 'ignore', schema validation will be skipped. If not set, 'warning' is used. * new API `DocumentLanguageSettings.schemaRequest`. The severity of problems that occurred while resolving and loading schemas. If set to 'ignore', schema resolving problems are not reported. If not set, 'warning' is used. 3.8.0 / 2020-08-02 ================= * new API `LanguageService.getMatchingSchemas`. Matches a document against its schema and list all AST nodes along with the matching sub schemas. 3.7.0 / 2020-06-04 ================== * New API `JSONSchema.suggestSortText` to set the sort order of completion proposals (VS Code specific JSON schema extension) 3.6.0 / 2020-04-27 ================== * New API `findDefinition` to find a definition of a `$ref` link 3.5.0 / 2020-02-20 ================== * Support for exclusive file pattern starting with '!'. A file match consists of an array of patterns. A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'. 3.4.4 / 2019-11-04 ================== * Using `vscode-languageserver-textdocument` for TextDocument 3.4.0 / 2019-10-28 ================== * Added `DocumentSymbolsContext` and `ColorInformationContext` with `resultLimit` and `onResultLimitExceeded`. `onResultLimitExceeded` is called when the result was cropped. * Added commit characters for completion proposals (if supported by ClientCapabilities) * Warn when using draft-03 or draft-08 schemas 3.3.4 / 2019-09-20 ================== * Renamed `schema.allowsTrailingCommas` -> `schema.allowTrailingCommas` 3.3.3 / 2019-08-29 ================== * Schemas can configure whether comments and/or trailing commas are permitted. 3.3.0 / 2019-06-12 ================== * New API `LanguageService.getSelectionRanges` to get semantic selection ranges. * Manage schema dependencies so that `resetSchema` also resets schemas that depend on the schema. 3.2.0 / 2018-09-27 ================== * New API `LanguageServiceParams.ClientCapabilities` to define what LSP capabilities the client supports. * For the best experiences, clients should always use `LanguageServiceParams.ClientCapabilities.LATEST`, which has all the latest LSP capabilities enabled. * `LanguageServiceParams.ClientCapabilities` can allow `MarkupKind.Markdown` as valid documentationFormat (used by completions if schemas use `markdownDescription` or `markdownEnumDescriptions`). * Snippets can now provide the description also in markdown format. * Bundled draft-07-schema with descriptions. * Propose `examples` in code completions. 3.1.5 / 2018-08-14 ================== * support for JSON schema draft-07 * New API `LanguageService.findDocumentSymbols2` to get document symbols as `DocumentSymbol[]` 3.1.2 / 2018-07-25 ================== * New API `LanguageService.getFoldingRanges` * doValidation can also be used with a given schema 3.1.0 / 2018-04-09 ================== * new APIs: `newJSONDocument` to create a JSON document from a custom AST * new API types: ObjectASTNode, PropertyASTNode, ArrayASTNode, StringASTNode, NumberASTNode, BooleanASTNode, NullASTNode that allow creating a custom AST 3.0.9 / 2018-03-07 ================== * Provide ems modules in lib/esm 3.0.2 / 2017-01-27 ================== * Added document specific validation parameters: `DocumentLanguageSettings` * API to define the severity of reported comments and trailing commas (`DocumentLanguageSettings.comments`, `DocumentLanguageSettings.trailingCommas`) 3.0.0 / 2017-01-11 ================== * Changed parameters of API `LanguageService.getColorPresentations` to separate color and range . 2.0.19 / 2017-09-21 =================== * New API `LanguageService.getColorPresentations` returning presentations for a given color. * New API type `ColorPresentation` added. 2.0.15 / 2017-08-28 =================== * New API `LanguageService.findDocumentColors` returning the location and value of all colors in a document. * New API types `ColorInformation` and `Color` added. * Deprecated `LanguageService.findColorSymbols`. Use `LanguageService.findDocumentColors` instead. 2.0.8 / 2017-04-25 ================== * error code for CommentsNotAllowed 2.0.5 / 2017-03-27 ================== * Add new API findColorSymbols that returns all color values in a JSON document. To mark a value as a color, specify `"format": "color"` in the schema. 2.0.4 / 2017-02-27 ================== * Support for custom schema property 'patternErrorMessage'. The message is used as error message if the object is of type string and has a 'pattern' property that does not match the object to validate. 2.0.1 / 2017-02-21 ================== * Fixes for formatting content with errors 2.0.0 / 2017-02-17 ================== * Updating to [language server type 3.0](https://github.com/Microsoft/vscode-languageserver-node/tree/master/types) API vscode-json-languageservice-5.7.2/LICENSE.md000066400000000000000000000046721514707600300205410ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Glob matching is based on code from https://github.com/fitzgen/glob-to-regexp Copyright (c) 2013, Nick Fitzgerald All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.vscode-json-languageservice-5.7.2/README.md000066400000000000000000000065771514707600300204220ustar00rootroot00000000000000# vscode-json-languageservice JSON language service extracted from VSCode to be reused, e.g in the Monaco editor. [![npm Package](https://img.shields.io/npm/v/vscode-json-languageservice.svg?style=flat-square)](https://www.npmjs.org/package/vscode-json-languageservice) [![NPM Downloads](https://img.shields.io/npm/dm/vscode-json-languageservice.svg)](https://npmjs.org/package/vscode-json-languageservice) [![Build Status](https://github.com/microsoft/vscode-json-languageservice/actions/workflows/node.js.yml/badge.svg)](https://github.com/microsoft/vscode-json-languageservice/actions) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) ## Why? The _vscode-json-languageservice_ contains the language smarts behind the JSON editing experience of Visual Studio Code and the Monaco editor. - _doValidation_ analyses an input string and returns syntax and lint errors. - _doComplete_ provides completion proposals for a given location. - _doResolve_ resolves a completion proposals. - _doHover_ provides a hover text for a given location. - _findDocumentSymbols_ provides all symbols in the given document. - _findDocumentColors_ provides all color symbols in the given document. - _getColorPresentations_ returns available color formats for a color symbol. - _format_ formats the code at the given range. - _getFoldingRanges_ gets folding ranges for the given document. - _getSelectionRanges_ gets selection ranges for a given location. - _getMatchingSchemas_ matches a document against its schema and returns all AST nodes along with the matching sub schemas. - _parseJSONDocument_ creates a JSON document from source code. - _newJSONDocument_ creates a JSON document from an AST. For the complete API see [jsonLanguageService.ts](./src/jsonLanguageService.ts) and [jsonLanguageTypes.ts](./src/jsonLanguageTypes.ts) ## Installation npm install --save vscode-json-languageservice ## Sample usage See [sample.ts](./src/example/sample.ts) for an example on how to use the JSON language service. To run the sample use `yarn sample` ## Development git clone https://github.com/microsoft/vscode-json-languageservice cd vscode-json-languageservice yarn Use `yarn test` to compile and run tests ### How can I run and debug the service? - open the folder in VSCode. - set breakpoints, e.g. in `jsonCompletion.ts` - run the Unit tests from the run viewlet and wait until a breakpoint is hit: ![image](https://user-images.githubusercontent.com/6461412/94239202-bdad4e80-ff11-11ea-99c3-cb9dbeb1c0b2.png) ### How can I run and debug the service inside an instance of VSCode? - run VSCode out of sources setup as described here: https://github.com/Microsoft/vscode/wiki/How-to-Contribute - use `yarn link vscode-json-languageservice` in `vscode/extensions/json-language-features/server` to run VSCode with the latest changes from `vscode-json-languageservice` - run VSCode out of source (`vscode/scripts/code.sh|bat`) and open a `.json` file - in VSCode window that is open on the `vscode-json-languageservice` sources, run command `Debug: Attach to Node process` and pick the `code-oss` process with the `json-language-features` path ![image](https://user-images.githubusercontent.com/6461412/94242925-061b3b00-ff17-11ea-8c17-8da15268f1a1.png) - set breakpoints, e.g. in `jsonCompletion.ts` - in the instance run from sources, invoke code completion in the `.json` file vscode-json-languageservice-5.7.2/SECURITY.md000066400000000000000000000053341514707600300207220ustar00rootroot00000000000000 ## Security Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. ## Preferred Languages We prefer all communications to be in English. ## Policy Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). vscode-json-languageservice-5.7.2/build/000077500000000000000000000000001514707600300202235ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/build/bundle-schemas.cjs000066400000000000000000000172231514707600300236230ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ const fs = require('fs').promises; const Bundler = require("@hyperjump/json-schema-bundle"); (async function () { bundle(`https://json-schema.org/draft/2019-09/schema`, 'draft-2019-09', 'https://json-schema.org/draft/2019-09'); bundle(`https://json-schema.org/draft/2020-12/schema`, 'draft-2020-12', 'https://json-schema.org/draft/2020-12'); }()); async function bundle(uri, filename, derivedURL) { const metaSchema = await Bundler.get(uri); let bundle = await Bundler.bundle(metaSchema); bundle = JSON.parse(JSON.stringify(bundle, null, 2).replace(/"undefined": ""/g, '"$dynamicAnchor": "meta"')); fs.writeFile(`./${filename}.json`, JSON.stringify(bundle, null, 2), 'utf8'); bundle = flattenDraftMetaSchema(bundle); const jsified = getCopyright(derivedURL) + 'export default ' + printObject(bundle); fs.writeFile(`./${filename}-flat.json`, JSON.stringify(bundle, null, 2), 'utf8'); fs.writeFile(`./src/services/schemas/${filename}-flat.ts`, jsified, 'utf8'); } function getCopyright(derivedURL) { return [ '/*---------------------------------------------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', ' * Licensed under the MIT License. See License.txt in the project root for license information.', ' *--------------------------------------------------------------------------------------------*/', '', '// This file is generated - do not edit directly!', '// Derived from ' + derivedURL, ].join('\n') + '\n\n'; } function printLiteral(value) { if (typeof value === 'string') { return `'${value}'`; } return value; } function printKey(value) { if (value.match(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/)) { return `${value}`; } return `'${value}'`; } function indent(level) { return '\t'.repeat(level); } function printObject(obj, indentLevel = 0) { const result = []; if (Array.isArray(obj)) { result.push(`[`); for (const item of obj) { if (typeof item === 'object' && item !== null) { result.push(`${indent(indentLevel + 1)}${printObject(item, indentLevel + 1)},`); } else { result.push(`${indent(indentLevel + 1)}${printLiteral(item)},`); } } result.push(`${indent(indentLevel)}]`); return result.join('\n'); } if (obj === null) { result.push(`null`); return result.join('\n'); } result.push(`{`); for (const [key, value] of Object.entries(obj)) { if (typeof value === 'object' && value !== null) { result.push(`${indent(indentLevel + 1)}${printKey(key)}: ${printObject(value, indentLevel + 1)},`); } else { result.push(`${indent(indentLevel + 1)}${printKey(key)}: ${printLiteral(value)},`); } } result.push(`${indent(indentLevel)}}`); return result.join('\n'); } // flatten const DEFAULT_ANCHOR = 'meta'; function visit(node, callback) { if (!node || typeof node !== 'object') return; if (Array.isArray(node)) { for (const item of node) { visit(item, callback); } return; } for (const key of Object.keys(node)) { callback(node, key); visit(node[key], callback); } } /** Recursively replace $dynamicRef:#meta with $ref:#meta */ function replaceDynamicRefs(node, anchorName = DEFAULT_ANCHOR) { visit(node, (n, k) => { const v = n[k]; if (k === '$dynamicRef' && v === '#' + anchorName) { n['$ref'] = '#'; delete n['$dynamicRef']; }; }); } /** Recursively replace $dynamicRef:#meta with $ref:#meta */ function replaceRecursiveRefs(node, anchorName = DEFAULT_ANCHOR) { visit(node, (n, k) => { const v = n[k]; if (k === '$recursiveRef') { n['$ref'] = v; delete n['$recursiveRef']; }; }); } /** Replace refs that point to a vocabulary */ function replaceOldRefs(node, anchorName = DEFAULT_ANCHOR) { visit(node, (n, k) => { const v = n[k]; if (k === '$ref' && typeof v === 'string' && v.startsWith(anchorName + '/')) { const segments = v.split('#'); if (segments.length === 2) { n['$ref'] = `#${segments[1]}`; } } }); } /** Remove all $dynamicAnchor occurrences (except keep keyword definition property) */ function stripDynamicAnchors(node) { visit(node, (n, k) => { if (k === '$dynamicAnchor') { delete n[k]; } }); } /** Collect vocabulary object definitions from $defs */ function collectVocabularies(schema) { const vocabularies = []; const defs = schema.$defs || {}; for (const [key, value] of Object.entries(defs)) { if (value && typeof value === 'object' && !Array.isArray(value) && value.$id && value.$dynamicAnchor === DEFAULT_ANCHOR && value.properties) { vocabularies.push(value); } } return vocabularies; } /** Merge properties from each vocabulary into root.properties (shallow) */ function mergeVocabularyProperties(root, vocabularies) { if (!root.properties) root.properties = {}; replaceOldRefs(root); for (const vocab of vocabularies) { for (const [propName, propSchema] of Object.entries(vocab.properties || {})) { if (!(propName in root.properties)) { root.properties[propName] = propSchema; } else { // Simple heuristic: if both are objects, attempt shallow merge, else keep existing const existing = root.properties[propName]; if (isPlainObject(existing) && isPlainObject(propSchema)) { root.properties[propName] = { ...existing, ...propSchema }; } } } } } function isPlainObject(o) { return !!o && typeof o === 'object' && !Array.isArray(o); } /** Gather unified $defs from vocab $defs (only specific keys) */ function buildUnifiedDefs(schema, vocabularies) { const unified = schema.$defs && !referencesVocabulary(schema.$defs) ? { ...schema.$defs } : {}; function harvest(defsObj) { if (!defsObj || typeof defsObj !== 'object') return; for (const [k, v] of Object.entries(defsObj)) { if (!(k in unified)) { unified[k] = v; } else { console.warn(`Warning: duplicate definition for key ${k} found while building unified $defs. Keeping the first occurrence.`); } } } for (const vocab of vocabularies) harvest(vocab.$defs); // Adjust schemaArray items dynamicRef->ref later with global replacement return unified; } function referencesVocabulary(defs) { return Object.keys(defs).some(k => k.startsWith('https://json-schema.org/draft/')); } function flattenDraftMetaSchema(original) { // Clone to avoid mutating input reference const schema = JSON.parse(JSON.stringify(original)); const anchorName = schema.$dynamicAnchor || DEFAULT_ANCHOR; // 1. Collect vocabulary schemas const vocabularies = collectVocabularies(schema); // 2. Merge vocabulary properties into root mergeVocabularyProperties(schema, vocabularies); // 3. Build unified $defs const unifiedDefs = buildUnifiedDefs(schema, vocabularies); // 4. Remove top-level allOf (flatten composition) delete schema.allOf; // 5. Remove vocabulary objects from $defs if (schema.$defs) { for (const k of Object.keys(schema.$defs)) { if (schema.$defs[k] && schema.$defs[k].$id && schema.$defs[k].$dynamicAnchor === anchorName) { delete schema.$defs[k]; } } } // 6. Assign unified defs schema.$defs = unifiedDefs; // 7. Convert dynamic recursion markers replaceDynamicRefs(schema, anchorName); replaceRecursiveRefs(schema, anchorName); stripDynamicAnchors(schema); // 8. Add static anchor at root delete schema.$dynamicAnchor; // 9. Update title to signal flattening if (schema.title) { schema.title = '(Flattened static) ' + schema.title; } else { schema.title = 'Flattened Draft 2020-12 meta-schema'; } return schema; } vscode-json-languageservice-5.7.2/build/pipeline.yml000066400000000000000000000026501514707600300225560ustar00rootroot00000000000000name: $(Date:yyyyMMdd)$(Rev:.r) trigger: batch: true branches: include: - main pr: none resources: repositories: - repository: templates type: github name: microsoft/vscode-engineering ref: main endpoint: Monaco parameters: - name: quality displayName: Quality type: string default: latest values: - latest - next - name: publishPackage displayName: 🚀 Publish vscode-json-languageservice type: boolean default: false extends: template: azure-pipelines/npm-package/pipeline.yml@templates parameters: npmPackages: - name: vscode-json-languageservice buildSteps: - script: npm ci displayName: Install dependencies # the rest of the build steps are part of the 'prepack' script, automatically run when the pipeline invokes 'npm pack' tag: ${{ parameters.quality }} preReleaseTag: next publishPackage: ${{ parameters.publishPackage }} publishRequiresApproval: false testPlatforms: - name: Linux nodeVersions: - 22.x - name: MacOS nodeVersions: - 22.x - name: Windows nodeVersions: - 22.x testSteps: - script: npm ci displayName: Install dependencies - script: npm test displayName: Test npm package vscode-json-languageservice-5.7.2/build/remove-sourcemap-refs.js000066400000000000000000000024571514707600300250170ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ const fs = require('fs'); const path = require('path'); function deleteRefs(dir) { const files = fs.readdirSync(dir); for (let file of files) { const filePath = path.join(dir, file); const stat = fs.statSync(filePath); if (stat.isDirectory()) { deleteRefs(filePath); } else if (path.extname(file) === '.js') { const content = fs.readFileSync(filePath, 'utf8'); const newContent = content.replace(/\/\/\# sourceMappingURL=[^]+.js.map/, '') if (content.length !== newContent.length) { console.log('remove sourceMappingURL in ' + filePath); fs.writeFileSync(filePath, newContent); } } else if (path.extname(file) === '.map') { fs.unlinkSync(filePath) console.log('remove ' + filePath); } } } let location = path.join(__dirname, '..', 'lib'); console.log('process ' + location); deleteRefs(location);vscode-json-languageservice-5.7.2/eslint.config.mjs000066400000000000000000000011421514707600300223770ustar00rootroot00000000000000import typescriptEslint from "@typescript-eslint/eslint-plugin"; import tsParser from "@typescript-eslint/parser"; export default [{ files: ["**/*.ts"], plugins: { "@typescript-eslint": typescriptEslint, }, languageOptions: { parser: tsParser, ecmaVersion: 6, sourceType: "module", }, rules: { "@typescript-eslint/naming-convention": ["warn", { selector: "typeLike", format: ["PascalCase"], }], curly: "warn", eqeqeq: "warn", "no-throw-literal": "warn", semi: "off", }, }];vscode-json-languageservice-5.7.2/package-lock.json000066400000000000000000004332341514707600300223510ustar00rootroot00000000000000{ "name": "vscode-json-languageservice", "version": "5.7.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vscode-json-languageservice", "version": "5.7.2", "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", "vscode-uri": "^3.1.0" }, "devDependencies": { "@types/mocha": "^10.0.10", "@types/node": "22.x", "@typescript-eslint/eslint-plugin": "^8.56.0", "@typescript-eslint/parser": "^8.56.0", "eslint": "^9.39.3", "json-schema-test-suite": "https://github.com/json-schema-org/JSON-Schema-Test-Suite.git#69acf52990b004240839ae19b4bec8fb01d50876", "mocha": "^11.7.5", "rimraf": "^6.1.3", "typescript": "^5.9.3" } }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/@eslint/config-array/node_modules/minimatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/@eslint/js": { "version": "9.39.3", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { "version": "0.16.7", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.22" }, "funding": { "type": "github", "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18" }, "funding": { "type": "github", "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { "node": ">=12" } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "optional": true, "engines": { "node": ">=14" } }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, "node_modules/@types/mocha": { "version": "10.0.10", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { "version": "22.18.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.7.tgz", "integrity": "sha512-3E97nlWEVp2V6J7aMkR8eOnw/w0pArPwf/5/W0865f+xzBoGL/ZuHkTAKAGN7cOWNwd+sG+hZOqj+fjzeHS75g==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/type-utils": "8.56.0", "@typescript-eslint/utils": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "@typescript-eslint/parser": "^8.56.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0", "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz", "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.0", "@typescript-eslint/types": "^8.56.0", "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz", "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz", "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz", "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0", "@typescript-eslint/utils": "8.56.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz", "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/typescript-estree": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz", "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/project-service": "8.56.0", "@typescript-eslint/tsconfig-utils": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/utils": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz", "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz", "integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.56.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@vscode/l10n": { "version": "0.0.18", "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==" }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" }, "engines": { "node": ">=0.4.0" } }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/ajv": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "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/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "node_modules/brace-expansion": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { "node": "18 || 20 || >=22" } }, "node_modules/brace-expansion/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" }, "engines": { "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" }, "engines": { "node": ">=12" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/diff": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "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/eslint": { "version": "9.39.3", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz", "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.3", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://eslint.org/donate" }, "peerDependencies": { "jiti": "*" }, "peerDependenciesMeta": { "jiti": { "optional": true } } }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree/node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, "engines": { "node": ">=0.10" } }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, "engines": { "node": ">=4.0" } }, "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" }, "peerDependencies": { "picomatch": "^3 || ^4" }, "peerDependenciesMeta": { "picomatch": { "optional": true } } }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, "engines": { "node": ">=16.0.0" } }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, "bin": { "flat": "cli.js" } }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" }, "engines": { "node": ">=16" } }, "node_modules/flatted": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, "engines": { "node": ">=10.13.0" } }, "node_modules/glob/node_modules/minimatch": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { "node": ">=8" } }, "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/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-schema-test-suite": { "version": "0.1.0", "resolved": "git+ssh://git@github.com/json-schema-org/JSON-Schema-Test-Suite.git#69acf52990b004240839ae19b4bec8fb01d50876", "integrity": "sha512-r4QkUgo97QaZjv1s9dReqXKozxefqCFttOle1/+okdn2Lyilw6kFy0Bat2XIoDj/7ckVPTU0E8RIUe/GBO1p1A==", "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==" }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { "p-locate": "^5.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lru-cache": { "version": "11.2.6", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, "node_modules/minimatch": { "version": "9.0.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/mocha": { "version": "11.7.5", "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", "dev": true, "license": "MIT", "dependencies": { "browser-stdout": "^1.3.1", "chokidar": "^4.0.1", "debug": "^4.3.5", "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^10.4.5", "he": "^1.2.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", "minimatch": "^9.0.5", "ms": "^2.1.3", "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^9.2.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", "mocha": "bin/mocha.js" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/mocha/node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mocha/node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, "funding": { "url": "https://github.com/sponsors/isaacs" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/mocha/node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, "license": "ISC" }, "node_modules/mocha/node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { "p-limit": "^3.0.2" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, "engines": { "node": ">=6" } }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/path-scurry": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "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, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "dependencies": { "safe-buffer": "^5.1.0" } }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", "engines": { "node": ">= 14.18.0" }, "funding": { "type": "individual", "url": "https://paulmillr.com/funding/" } }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/rimraf": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "glob": "^13.0.3", "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" }, "engines": { "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ] }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" }, "funding": { "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { "node": ">=18.12" }, "peerDependencies": { "typescript": ">=4.8.4" } }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=14.17" } }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, "node_modules/vscode-languageserver-textdocument": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==" }, "node_modules/vscode-languageserver-types": { "version": "3.17.5", "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "node_modules/vscode-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", "license": "MIT" }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/workerpool": { "version": "9.3.3", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.3.tgz", "integrity": "sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw==", "dev": true, "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" } }, "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/yargs-unparser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" }, "engines": { "node": ">=10" } }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } } }, "dependencies": { "@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "requires": { "eslint-visitor-keys": "^3.4.3" } }, "@eslint-community/regexpp": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true }, "@eslint/config-array": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "requires": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, "dependencies": { "brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "minimatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } } } }, "@eslint/config-helpers": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "requires": { "@eslint/core": "^0.17.0" } }, "@eslint/core": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.15" } }, "@eslint/eslintrc": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "dependencies": { "brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true }, "minimatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } } } }, "@eslint/js": { "version": "9.39.3", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", "dev": true }, "@eslint/object-schema": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true }, "@eslint/plugin-kit": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "requires": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true }, "@humanfs/node": { "version": "0.16.7", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "requires": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true }, "@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "requires": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "dependencies": { "ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true }, "ansi-styles": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true }, "emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, "string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "requires": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "requires": { "ansi-regex": "^6.0.1" } }, "wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "requires": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } } } }, "@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "optional": true }, "@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, "@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/mocha": { "version": "10.0.10", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", "dev": true }, "@types/node": { "version": "22.18.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.7.tgz", "integrity": "sha512-3E97nlWEVp2V6J7aMkR8eOnw/w0pArPwf/5/W0865f+xzBoGL/ZuHkTAKAGN7cOWNwd+sG+hZOqj+fjzeHS75g==", "dev": true, "requires": { "undici-types": "~6.21.0" } }, "@typescript-eslint/eslint-plugin": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/type-utils": "8.56.0", "@typescript-eslint/utils": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" } }, "@typescript-eslint/parser": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "requires": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0", "debug": "^4.4.3" } }, "@typescript-eslint/project-service": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz", "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==", "dev": true, "requires": { "@typescript-eslint/tsconfig-utils": "^8.56.0", "@typescript-eslint/types": "^8.56.0", "debug": "^4.4.3" } }, "@typescript-eslint/scope-manager": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz", "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==", "dev": true, "requires": { "@typescript-eslint/types": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0" } }, "@typescript-eslint/tsconfig-utils": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz", "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==", "dev": true, "requires": {} }, "@typescript-eslint/type-utils": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz", "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==", "dev": true, "requires": { "@typescript-eslint/types": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0", "@typescript-eslint/utils": "8.56.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" } }, "@typescript-eslint/types": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz", "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==", "dev": true }, "@typescript-eslint/typescript-estree": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz", "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==", "dev": true, "requires": { "@typescript-eslint/project-service": "8.56.0", "@typescript-eslint/tsconfig-utils": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/visitor-keys": "8.56.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" } }, "@typescript-eslint/utils": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz", "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", "@typescript-eslint/typescript-estree": "8.56.0" } }, "@typescript-eslint/visitor-keys": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz", "integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==", "dev": true, "requires": { "@typescript-eslint/types": "8.56.0", "eslint-visitor-keys": "^5.0.0" }, "dependencies": { "eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true } } }, "@vscode/l10n": { "version": "0.0.18", "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==" }, "acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, "ajv": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { "color-convert": "^2.0.1" } }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "brace-expansion": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", "dev": true, "requires": { "balanced-match": "^4.0.2" }, "dependencies": { "balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true } } }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "requires": { "readdirp": "^4.0.1" } }, "cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { "color-name": "~1.1.4" } }, "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { "ms": "^2.1.3" } }, "decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "diff": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true }, "eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "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 }, "eslint": { "version": "9.39.3", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz", "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.3", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "dependencies": { "brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true }, "ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true }, "minimatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } } } }, "eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "requires": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" }, "dependencies": { "eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true } } }, "esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "requires": { "estraverse": "^5.1.0" } }, "esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { "estraverse": "^5.2.0" } }, "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "requires": {} }, "file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "requires": { "flat-cache": "^4.0.0" } }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, "flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "requires": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "flatted": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, "foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "requires": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, "glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, "requires": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" }, "dependencies": { "minimatch": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", "dev": true, "requires": { "brace-expansion": "^5.0.2" } } } }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { "is-glob": "^4.0.3" } }, "globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "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 }, "ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true }, "import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" } }, "is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "requires": { "argparse": "^2.0.1" } }, "json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, "json-schema-test-suite": { "version": "git+ssh://git@github.com/json-schema-org/JSON-Schema-Test-Suite.git#69acf52990b004240839ae19b4bec8fb01d50876", "integrity": "sha512-r4QkUgo97QaZjv1s9dReqXKozxefqCFttOle1/+okdn2Lyilw6kFy0Bat2XIoDj/7ckVPTU0E8RIUe/GBO1p1A==", "dev": true, "from": "json-schema-test-suite@https://github.com/json-schema-org/JSON-Schema-Test-Suite.git#69acf52990b004240839ae19b4bec8fb01d50876" }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==" }, "keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "requires": { "json-buffer": "3.0.1" } }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "requires": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { "p-locate": "^5.0.0" } }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "lru-cache": { "version": "11.2.6", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", "dev": true }, "minimatch": { "version": "9.0.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", "dev": true, "requires": { "brace-expansion": "^5.0.2" } }, "minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true }, "mocha": { "version": "11.7.5", "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", "dev": true, "requires": { "browser-stdout": "^1.3.1", "chokidar": "^4.0.1", "debug": "^4.3.5", "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^10.4.5", "he": "^1.2.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", "minimatch": "^9.0.5", "ms": "^2.1.3", "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^9.2.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "dependencies": { "glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "requires": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" } }, "jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "requires": { "@isaacs/cliui": "^8.0.2", "@pkgjs/parseargs": "^0.11.0" } }, "lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, "path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "requires": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { "has-flag": "^4.0.0" } } } }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "requires": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { "yocto-queue": "^0.1.0" } }, "p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { "p-limit": "^3.0.2" } }, "package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { "callsites": "^3.0.0" } }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-scurry": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, "requires": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "requires": { "safe-buffer": "^5.1.0" } }, "readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "rimraf": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, "requires": { "glob": "^13.0.3", "package-json-from-dist": "^1.0.1" } }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true }, "serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { "randombytes": "^2.1.0" } }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { "shebang-regex": "^3.0.0" } }, "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "string-width-cjs": { "version": "npm:string-width@4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" } }, "strip-ansi-cjs": { "version": "npm:strip-ansi@6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { "ansi-regex": "^5.0.1" } }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" } }, "tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "requires": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "requires": {} }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "requires": { "prelude-ls": "^1.2.1" } }, "typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true }, "undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" } }, "vscode-languageserver-textdocument": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==" }, "vscode-languageserver-types": { "version": "3.17.5", "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "vscode-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==" }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" } }, "word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "workerpool": { "version": "9.3.3", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.3.tgz", "integrity": "sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw==", "dev": true }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "wrap-ansi-cjs": { "version": "npm:wrap-ansi@7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "requires": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, "yargs-unparser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" } }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true } } } vscode-json-languageservice-5.7.2/package.json000066400000000000000000000034751514707600300214230ustar00rootroot00000000000000{ "name": "vscode-json-languageservice", "version": "5.7.2", "description": "Language service for JSON", "main": "./lib/umd/jsonLanguageService.js", "typings": "./lib/umd/jsonLanguageService", "module": "./lib/esm/jsonLanguageService.js", "author": "Microsoft Corporation", "repository": { "type": "git", "url": "https://github.com/Microsoft/vscode-json-languageservice" }, "license": "MIT", "bugs": { "url": "https://github.com/Microsoft/vscode-json-languageservice" }, "devDependencies": { "@types/mocha": "^10.0.10", "@types/node": "22.x", "@typescript-eslint/eslint-plugin": "^8.56.0", "@typescript-eslint/parser": "^8.56.0", "eslint": "^9.39.3", "json-schema-test-suite": "https://github.com/json-schema-org/JSON-Schema-Test-Suite.git#69acf52990b004240839ae19b4bec8fb01d50876", "mocha": "^11.7.5", "rimraf": "^6.1.3", "typescript": "^5.9.3" }, "dependencies": { "jsonc-parser": "^3.3.1", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", "vscode-uri": "^3.1.0", "@vscode/l10n": "^0.0.18" }, "scripts": { "prepack": "npm run clean && npm run compile-esm && npm run test && npm run remove-sourcemap-refs", "compile": "tsc -p ./src", "compile-esm": "tsc -p ./src/tsconfig.esm.json", "clean": "rimraf lib", "remove-sourcemap-refs": "node ./build/remove-sourcemap-refs.js", "watch": "tsc -w -p ./src", "pretest": "npm run compile", "test": "mocha", "posttest": "npm run lint", "coverage": "npx nyc -r lcov npm run test", "lint": "eslint src/**/*.ts", "install-types-next": "npm install vscode-languageserver-types@next -f -S && npm install vscode-languageserver-textdocument@next -f -S", "sample": "npm run compile && node ./lib/umd/example/sample.js" } } vscode-json-languageservice-5.7.2/src/000077500000000000000000000000001514707600300177135ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/src/example/000077500000000000000000000000001514707600300213465ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/src/example/sample.ts000066400000000000000000000035761514707600300232120ustar00rootroot00000000000000 import { getLanguageService, TextDocument } from '../jsonLanguageService'; async function main() { const jsonContentUri = 'foo://server/example.data.json'; const jsonContent = `{ "name": 12 "country": "Ireland" }`; const jsonSchemaUri = "foo://server/data.schema.json"; const jsonSchema = { "type": "object", "properties": { "name": { "type": "string" }, "country": { "type": "string", "enum": ["Ireland", "Iceland"] } } }; const textDocument = TextDocument.create(jsonContentUri, 'json', 1, jsonContent); const jsonLanguageService = getLanguageService({ schemaRequestService: (uri) => { if (uri === jsonSchemaUri) { return Promise.resolve(JSON.stringify(jsonSchema)); } return Promise.reject(`Unabled to load schema at ${uri}`); } }); // associate `*.data.json` with the `foo://server/data.schema.json` schema jsonLanguageService.configure({ allowComments: false, schemas: [{ fileMatch: ["*.data.json"], uri: jsonSchemaUri }] }); const jsonDocument = jsonLanguageService.parseJSONDocument(textDocument); const diagnostics = await jsonLanguageService.doValidation(textDocument, jsonDocument); console.log('Validation results:', diagnostics.map(d => `[line ${d.range.start.line}] ${d.message}`)); /* * > Validation results: [ * > '[line 1] Incorrect type. Expected "string".', * > '[line 2] Expected comma' * > ] */ const competionResult = await jsonLanguageService.doComplete(textDocument, { line: 2, character: 18 }, jsonDocument); console.log('Completion proposals:', competionResult?.items.map(i => `${i.label}`)); /* * Completion proposals: [ '"Ireland"', '"Iceland"' ] */ } main(); vscode-json-languageservice-5.7.2/src/jsonContributions.ts000066400000000000000000000025141514707600300240210ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { MarkedString, CompletionItem } from './jsonLanguageService'; export interface JSONWorkerContribution { getInfoContribution(uri: string, location: JSONPath): PromiseLike; collectPropertyCompletions(uri: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): PromiseLike; collectValueCompletions(uri: string, location: JSONPath, propertyKey: string, result: CompletionsCollector): PromiseLike; collectDefaultCompletions(uri: string, result: CompletionsCollector): PromiseLike; resolveCompletion?(item: CompletionItem): PromiseLike; } export type Segment = string | number; export type JSONPath = Segment[]; export type JSONCompletionItem = CompletionItem & { insertText: string }; export interface CompletionsCollector { add(suggestion: JSONCompletionItem & { insertText: string}): void; error(message: string): void; setAsIncomplete(): void; getNumberOfProposals(): number; } vscode-json-languageservice-5.7.2/src/jsonLanguageService.ts000066400000000000000000000137321514707600300242270ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { JSONCompletion } from './services/jsonCompletion'; import { JSONHover } from './services/jsonHover'; import { JSONValidation } from './services/jsonValidation'; import { JSONDocumentSymbols } from './services/jsonDocumentSymbols'; import { parse as parseJSON, newJSONDocument } from './parser/jsonParser'; import { schemaContributions } from './services/configuration'; import { JSONSchemaService } from './services/jsonSchemaService'; import { getFoldingRanges } from './services/jsonFolding'; import { getSelectionRanges } from './services/jsonSelectionRanges'; import { sort } from './utils/sort'; import { format } from './utils/format'; import { ASTNode, Color, ColorInformation, ColorPresentation, LanguageServiceParams, LanguageSettings, DocumentLanguageSettings, FoldingRange, JSONSchema, SelectionRange, FoldingRangesContext, DocumentSymbolsContext, ColorInformationContext as DocumentColorsContext, TextDocument, Position, CompletionItem, CompletionList, Hover, Range, SymbolInformation, Diagnostic, TextEdit, FormattingOptions, DocumentSymbol, DefinitionLink, MatchingSchema, JSONLanguageStatus, SortOptions } from './jsonLanguageTypes'; import { findLinks } from './services/jsonLinks'; import { DocumentLink } from 'vscode-languageserver-types'; export type JSONDocument = { root: ASTNode | undefined; getNodeFromOffset(offset: number, includeRightBound?: boolean): ASTNode | undefined; }; export * from './jsonLanguageTypes'; export interface LanguageService { configure(settings: LanguageSettings): void; doValidation(document: TextDocument, jsonDocument: JSONDocument, documentSettings?: DocumentLanguageSettings, schema?: JSONSchema): PromiseLike; parseJSONDocument(document: TextDocument): JSONDocument; newJSONDocument(rootNode: ASTNode | undefined, syntaxDiagnostics?: Diagnostic[], comments?: Range[]): JSONDocument; resetSchema(uri: string): boolean; getMatchingSchemas(document: TextDocument, jsonDocument: JSONDocument, schema?: JSONSchema): PromiseLike; getLanguageStatus(document: TextDocument, jsonDocument: JSONDocument): JSONLanguageStatus; doResolve(item: CompletionItem): PromiseLike; doComplete(document: TextDocument, position: Position, doc: JSONDocument): PromiseLike; findDocumentSymbols(document: TextDocument, doc: JSONDocument, context?: DocumentSymbolsContext): SymbolInformation[]; findDocumentSymbols2(document: TextDocument, doc: JSONDocument, context?: DocumentSymbolsContext): DocumentSymbol[]; findDocumentColors(document: TextDocument, doc: JSONDocument, context?: DocumentColorsContext): PromiseLike; getColorPresentations(document: TextDocument, doc: JSONDocument, color: Color, range: Range): ColorPresentation[]; doHover(document: TextDocument, position: Position, doc: JSONDocument): PromiseLike; getFoldingRanges(document: TextDocument, context?: FoldingRangesContext): FoldingRange[]; getSelectionRanges(document: TextDocument, positions: Position[], doc: JSONDocument): SelectionRange[]; findDefinition(document: TextDocument, position: Position, doc: JSONDocument): PromiseLike; findLinks(document: TextDocument, doc: JSONDocument): PromiseLike; format(document: TextDocument, range: Range | undefined, options: FormattingOptions): TextEdit[]; sort(document: TextDocument, options: SortOptions): TextEdit[]; } export function getLanguageService(params: LanguageServiceParams): LanguageService { const promise = params.promiseConstructor || Promise; const jsonSchemaService = new JSONSchemaService(params.schemaRequestService, params.workspaceContext, promise); jsonSchemaService.setSchemaContributions(schemaContributions); const jsonCompletion = new JSONCompletion(jsonSchemaService, params.contributions, promise, params.clientCapabilities); const jsonHover = new JSONHover(jsonSchemaService, params.contributions, promise); const jsonDocumentSymbols = new JSONDocumentSymbols(jsonSchemaService); const jsonValidation = new JSONValidation(jsonSchemaService, promise); return { configure: (settings: LanguageSettings) => { jsonSchemaService.clearExternalSchemas(); settings.schemas?.forEach(jsonSchemaService.registerExternalSchema.bind(jsonSchemaService)); jsonValidation.configure(settings); }, resetSchema: (uri: string) => jsonSchemaService.onResourceChange(uri), doValidation: jsonValidation.doValidation.bind(jsonValidation), getLanguageStatus: jsonValidation.getLanguageStatus.bind(jsonValidation), parseJSONDocument: (document: TextDocument) => parseJSON(document, { collectComments: true }), newJSONDocument: (root: ASTNode | undefined, diagnostics?: Diagnostic[], comments?: Range[]) => newJSONDocument(root, diagnostics, comments), getMatchingSchemas: jsonSchemaService.getMatchingSchemas.bind(jsonSchemaService), doResolve: jsonCompletion.doResolve.bind(jsonCompletion), doComplete: jsonCompletion.doComplete.bind(jsonCompletion), findDocumentSymbols: jsonDocumentSymbols.findDocumentSymbols.bind(jsonDocumentSymbols), findDocumentSymbols2: jsonDocumentSymbols.findDocumentSymbols2.bind(jsonDocumentSymbols), findDocumentColors: jsonDocumentSymbols.findDocumentColors.bind(jsonDocumentSymbols), getColorPresentations: jsonDocumentSymbols.getColorPresentations.bind(jsonDocumentSymbols), doHover: jsonHover.doHover.bind(jsonHover), getFoldingRanges, getSelectionRanges, findDefinition: () => Promise.resolve([]), findLinks, format: (document: TextDocument, range: Range | undefined, options: FormattingOptions) => format(document, options, range), sort: (document: TextDocument, options: FormattingOptions) => sort(document, options) }; } vscode-json-languageservice-5.7.2/src/jsonLanguageTypes.ts000066400000000000000000000265671514707600300237450ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { JSONWorkerContribution, JSONPath, Segment, CompletionsCollector } from './jsonContributions'; import { JSONSchema } from './jsonSchema'; import { Range, Position, DocumentUri, MarkupContent, MarkupKind, Color, ColorInformation, ColorPresentation, FoldingRange, FoldingRangeKind, SelectionRange, Diagnostic, DiagnosticSeverity, CompletionItem, CompletionItemKind, CompletionList, CompletionItemTag, InsertTextFormat, SymbolInformation, SymbolKind, DocumentSymbol, Location, Hover, MarkedString, FormattingOptions as LSPFormattingOptions, DefinitionLink, CodeActionContext, Command, CodeAction, DocumentHighlight, DocumentLink, WorkspaceEdit, TextEdit, CodeActionKind, TextDocumentEdit, VersionedTextDocumentIdentifier, DocumentHighlightKind } from 'vscode-languageserver-types'; import { TextDocument, TextDocumentContentChangeEvent } from 'vscode-languageserver-textdocument'; export { TextDocument, TextDocumentContentChangeEvent, Range, Position, DocumentUri, MarkupContent, MarkupKind, JSONSchema, JSONWorkerContribution, JSONPath, Segment, CompletionsCollector, Color, ColorInformation, ColorPresentation, FoldingRange, FoldingRangeKind, SelectionRange, Diagnostic, DiagnosticSeverity, CompletionItem, CompletionItemKind, CompletionList, CompletionItemTag, InsertTextFormat, DefinitionLink, SymbolInformation, SymbolKind, DocumentSymbol, Location, Hover, MarkedString, CodeActionContext, Command, CodeAction, DocumentHighlight, DocumentLink, WorkspaceEdit, TextEdit, CodeActionKind, TextDocumentEdit, VersionedTextDocumentIdentifier, DocumentHighlightKind }; /** * Error codes used by diagnostics */ export enum ErrorCode { Undefined = 0, EnumValueMismatch = 1, Deprecated = 2, UnexpectedEndOfComment = 0x101, UnexpectedEndOfString = 0x102, UnexpectedEndOfNumber = 0x103, InvalidUnicode = 0x104, InvalidEscapeCharacter = 0x105, InvalidCharacter = 0x106, PropertyExpected = 0x201, CommaExpected = 0x202, ColonExpected = 0x203, ValueExpected = 0x204, CommaOrCloseBacketExpected = 0x205, CommaOrCloseBraceExpected = 0x206, TrailingComma = 0x207, DuplicateKey = 0x208, CommentNotPermitted = 0x209, PropertyKeysMustBeDoublequoted = 0x210, SchemaUnsupportedFeature = 0x301, SchemaResolveError = 0x10000, } export function isSchemaResolveError(code: number): boolean { return code >= ErrorCode.SchemaResolveError; } export type ASTNode = ObjectASTNode | PropertyASTNode | ArrayASTNode | StringASTNode | NumberASTNode | BooleanASTNode | NullASTNode; export interface BaseASTNode { readonly type: 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null'; readonly parent?: ASTNode; readonly offset: number; readonly length: number; readonly children?: ASTNode[]; readonly value?: string | boolean | number | null; } export interface ObjectASTNode extends BaseASTNode { readonly type: 'object'; readonly properties: PropertyASTNode[]; readonly children: ASTNode[]; } export interface PropertyASTNode extends BaseASTNode { readonly type: 'property'; readonly keyNode: StringASTNode; readonly valueNode?: ASTNode; readonly colonOffset?: number; readonly children: ASTNode[]; } export interface ArrayASTNode extends BaseASTNode { readonly type: 'array'; readonly items: ASTNode[]; readonly children: ASTNode[]; } export interface StringASTNode extends BaseASTNode { readonly type: 'string'; readonly value: string; } export interface NumberASTNode extends BaseASTNode { readonly type: 'number'; readonly value: number; readonly isInteger: boolean; } export interface BooleanASTNode extends BaseASTNode { readonly type: 'boolean'; readonly value: boolean; } export interface NullASTNode extends BaseASTNode { readonly type: 'null'; readonly value: null; } export interface MatchingSchema { node: ASTNode; schema: JSONSchema; } export interface JSONLanguageStatus { schemas: string[]; } export interface LanguageSettings { /** * If set, the validator will return syntax and semantic errors. */ validate?: boolean; /** * Defines whether comments are allowed or not. If set to false, comments will be reported as errors. * DocumentLanguageSettings.allowComments will override this setting. */ allowComments?: boolean; /** * A list of known schemas and/or associations of schemas to file names. */ schemas?: SchemaConfiguration[]; } export type SeverityLevel = 'error' | 'warning' | 'ignore'; export enum SchemaDraft { v3 = 3, v4 = 4, v6 = 6, v7 = 7, v2019_09 = 19, v2020_12 = 20 } export interface DocumentLanguageSettings { /** * The severity of reported comments. If not set, 'LanguageSettings.allowComments' defines whether comments are ignored or reported as errors. */ comments?: SeverityLevel; /** * The severity of reported trailing commas. If not set, trailing commas will be reported as errors. */ trailingCommas?: SeverityLevel; /** * The severity of problems from schema validation. If set to 'ignore', schema validation will be skipped. If not set, 'warning' is used. */ schemaValidation?: SeverityLevel; /** * The severity of problems that occurred when resolving and loading schemas. If set to 'ignore', schema resolving problems are not reported. If not set, 'warning' is used. */ schemaRequest?: SeverityLevel; /** * The draft version of schema to use if the schema doesn't specify one at $schema */ schemaDraft?: SchemaDraft; } export interface SchemaConfiguration { /** * The URI of the schema, which is also the identifier of the schema. */ uri: string; /** * A list of glob patterns that describe for which file URIs the JSON schema will be used. * '*' and '**' wildcards are supported. Exclusion patterns start with '!'. * For example '*.schema.json', 'package.json', '!foo*.schema.json', 'foo/**\/BADRESP.json'. * A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'. */ fileMatch?: string[]; /** * The schema for the given URI. * If no schema is provided, the schema will be fetched with the schema request service (if available). */ schema?: JSONSchema; /** * A parent folder for folder specifc associations. An association that has a folder URI set is only used * if the document that is validated has the folderUri as parent */ folderUri?: string; } export interface WorkspaceContextService { resolveRelativePath(relativePath: string, resource: string): string; } /** * The schema request service is used to fetch schemas. If successful, returns a resolved promise with the content of the schema. * In case of an error, returns a rejected promise with an Error object. If the type is of form { message: string, code: number }, the * error code will be used for diagnostics. */ export interface SchemaRequestService { (uri: string): PromiseLike; } export interface PromiseConstructor { /** * Creates a new Promise. * @param executor A callback used to initialize the promise. This callback is passed two arguments: * a resolve callback used resolve the promise with a value or the result of another promise, * and a reject callback used to reject the promise with a provided reason or error. */ new (executor: (resolve: (value?: T | PromiseLike) => void, reject: (reason?: any) => void) => void): PromiseLike; /** * Creates a Promise that is resolved with an array of results when all of the provided Promises * resolve, or rejected when any Promise is rejected. * @param values An array of Promises. * @returns A new Promise. */ all(values: Array>): PromiseLike; /** * Creates a new rejected promise for the provided reason. * @param reason The reason the promise was rejected. * @returns A new rejected Promise. */ reject(reason: any): PromiseLike; /** * Creates a new resolved promise for the provided value. * @param value A promise. * @returns A promise whose internal state matches the provided promise. */ resolve(value: T | PromiseLike): PromiseLike; } /** * A deprecated alias of {@link PromiseLike} * * @deprecated */ export interface Thenable extends PromiseLike {} export interface LanguageServiceParams { /** * The schema request service is used to fetch schemas from a URI. The provider returns the schema file content, or, * in case of an error, a displayable error string */ schemaRequestService?: SchemaRequestService; /** * The workspace context is used to resolve relative paths for relative schema references. */ workspaceContext?: WorkspaceContextService; /** * An optional set of completion and hover participants. */ contributions?: JSONWorkerContribution[]; /** * A promise constructor. If not set, the ES5 Promise will be used. */ promiseConstructor?: PromiseConstructor; /** * Describes the LSP capabilities the client supports. */ clientCapabilities?: ClientCapabilities; } /** * Describes what LSP capabilities the client supports */ export interface ClientCapabilities { /** * The text document client capabilities */ textDocument?: { /** * Capabilities specific to completions. */ completion?: { /** * The client supports the following `CompletionItem` specific * capabilities. */ completionItem?: { /** * Client supports the follow content formats for the documentation * property. The order describes the preferred format of the client. */ documentationFormat?: MarkupKind[]; /** * The client supports commit characters on a completion item. */ commitCharactersSupport?: boolean; /** * The client has support for completion item label * details (see also `CompletionItemLabelDetails`). */ labelDetailsSupport?: boolean; }; }; /** * Capabilities specific to hovers. */ hover?: { /** * Client supports the follow content formats for the content * property. The order describes the preferred format of the client. */ contentFormat?: MarkupKind[]; }; }; } export namespace ClientCapabilities { export const LATEST: ClientCapabilities = { textDocument: { completion: { completionItem: { documentationFormat: [MarkupKind.Markdown, MarkupKind.PlainText], commitCharactersSupport: true, labelDetailsSupport: true } } } }; } export interface FoldingRangesContext { /** * The maximal number of ranges returned. */ rangeLimit?: number; /** * Called when the result was cropped. */ onRangeLimitExceeded?: (uri: string) => void; } export interface DocumentSymbolsContext { /** * The maximal number of document symbols returned. */ resultLimit?: number; /** * Called when the result was cropped. */ onResultLimitExceeded?: (uri: string) => void; } export interface ColorInformationContext { /** * The maximal number of color informations returned. */ resultLimit?: number; /** * Called when the result was cropped. */ onResultLimitExceeded?: (uri: string) => void; } export interface FormattingOptions extends LSPFormattingOptions { insertFinalNewline?: boolean; keepLines?: boolean; } export interface SortOptions extends LSPFormattingOptions { insertFinalNewline?: boolean; } vscode-json-languageservice-5.7.2/src/jsonSchema.ts000066400000000000000000000057151514707600300223650ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ export type JSONSchemaRef = JSONSchema | boolean; export interface JSONSchema { id?: string; $id?: string; $schema?: string; type?: string | string[]; title?: string; default?: any; definitions?: { [name: string]: JSONSchema }; description?: string; properties?: JSONSchemaMap; patternProperties?: JSONSchemaMap; additionalProperties?: JSONSchemaRef; minProperties?: number; maxProperties?: number; dependencies?: JSONSchemaMap | { [prop: string]: string[] }; items?: JSONSchemaRef | JSONSchemaRef[]; minItems?: number; maxItems?: number; uniqueItems?: boolean; additionalItems?: JSONSchemaRef; pattern?: string; minLength?: number; maxLength?: number; minimum?: number; maximum?: number; exclusiveMinimum?: boolean | number; exclusiveMaximum?: boolean | number; multipleOf?: number; required?: string[]; $ref?: string; anyOf?: JSONSchemaRef[]; allOf?: JSONSchemaRef[]; oneOf?: JSONSchemaRef[]; not?: JSONSchemaRef; enum?: any[]; format?: string; // schema draft 06 const?: any; contains?: JSONSchemaRef; propertyNames?: JSONSchemaRef; examples?: any[]; // schema draft 07 $comment?: string; if?: JSONSchemaRef; then?: JSONSchemaRef; else?: JSONSchemaRef; // schema 2019-09 unevaluatedProperties?: boolean | JSONSchemaRef; unevaluatedItems?: boolean | JSONSchemaRef; minContains?: number; maxContains?: number; deprecated?: boolean; dependentRequired?: { [prop: string]: string[] }; dependentSchemas?: JSONSchemaMap; $defs?: { [name: string]: JSONSchema }; $anchor?: string; $recursiveRef?: string; $recursiveAnchor?: string; $vocabulary?: any; // schema 2020-12 prefixItems?: JSONSchemaRef[]; $dynamicRef?: string; $dynamicAnchor?: string; // VSCode extensions defaultSnippets?: { label?: string; description?: string; markdownDescription?: string; body?: any; bodyText?: string; }[]; // VSCode extension: body: a object that will be converted to a JSON string. bodyText: text with \t and \n errorMessage?: string; // VSCode extension patternErrorMessage?: string; // VSCode extension deprecationMessage?: string; // VSCode extension enumDescriptions?: string[]; // VSCode extension enumSortTexts?: string[]; // VSCode extension enumDetails?: string[]; // VSCode extension markdownEnumDescriptions?: string[]; // VSCode extension markdownDescription?: string; // VSCode extension doNotSuggest?: boolean; // VSCode extension suggestSortText?: string; // VSCode extension allowComments?: boolean; // VSCode extension allowTrailingCommas?: boolean; // VSCode extension completionDetail?: string; // VSCode extension } export interface JSONSchemaMap { [name: string]: JSONSchemaRef; } vscode-json-languageservice-5.7.2/src/parser/000077500000000000000000000000001514707600300212075ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/src/parser/jsonParser.ts000066400000000000000000001532511514707600300237140ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as Json from 'jsonc-parser'; import { JSONSchema, JSONSchemaRef } from '../jsonSchema'; import { isNumber, equals, isBoolean, isString, isDefined, isObject } from '../utils/objects'; import { extendedRegExp, stringLength } from '../utils/strings'; import { TextDocument, ASTNode, ObjectASTNode, ArrayASTNode, BooleanASTNode, NumberASTNode, StringASTNode, NullASTNode, PropertyASTNode, JSONPath, ErrorCode, Diagnostic, DiagnosticSeverity, Range, SchemaDraft } from '../jsonLanguageTypes'; import { URI } from 'vscode-uri'; import * as l10n from '@vscode/l10n'; export interface IRange { offset: number; length: number; } const formats = { 'color-hex': { errorMessage: l10n.t('Invalid color format. Use #RGB, #RGBA, #RRGGBB or #RRGGBBAA.'), pattern: /^#([0-9A-Fa-f]{3,4}|([0-9A-Fa-f]{2}){3,4})$/ }, 'date-time': { errorMessage: l10n.t('String is not a RFC3339 date-time.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i }, 'date': { errorMessage: l10n.t('String is not a RFC3339 date.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/i }, 'time': { errorMessage: l10n.t('String is not a RFC3339 time.'), pattern: /^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i }, 'email': { errorMessage: l10n.t('String is not an e-mail address.'), pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}))$/ }, 'hostname': { errorMessage: l10n.t('String is not a hostname.'), pattern: /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i }, 'ipv4': { errorMessage: l10n.t('String is not an IPv4 address.'), pattern: /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ }, 'ipv6': { errorMessage: l10n.t('String is not an IPv6 address.'), pattern: /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i }, }; export interface IProblem { location: IRange; severity?: DiagnosticSeverity; code?: ErrorCode; message: string; } export abstract class ASTNodeImpl { public readonly abstract type: 'object' | 'property' | 'array' | 'number' | 'boolean' | 'null' | 'string'; public offset: number; public length: number; public readonly parent: ASTNode | undefined; constructor(parent: ASTNode | undefined, offset: number, length: number = 0) { this.offset = offset; this.length = length; this.parent = parent; } public get children(): ASTNode[] { return []; } public toString(): string { return 'type: ' + this.type + ' (' + this.offset + '/' + this.length + ')' + (this.parent ? ' parent: {' + this.parent.toString() + '}' : ''); } } export class NullASTNodeImpl extends ASTNodeImpl implements NullASTNode { public type: 'null' = 'null'; public value: null = null; constructor(parent: ASTNode | undefined, offset: number) { super(parent, offset); } } export class BooleanASTNodeImpl extends ASTNodeImpl implements BooleanASTNode { public type: 'boolean' = 'boolean'; public value: boolean; constructor(parent: ASTNode | undefined, boolValue: boolean, offset: number) { super(parent, offset); this.value = boolValue; } } export class ArrayASTNodeImpl extends ASTNodeImpl implements ArrayASTNode { public type: 'array' = 'array'; public items: ASTNode[]; constructor(parent: ASTNode | undefined, offset: number) { super(parent, offset); this.items = []; } public get children(): ASTNode[] { return this.items; } } export class NumberASTNodeImpl extends ASTNodeImpl implements NumberASTNode { public type: 'number' = 'number'; public isInteger: boolean; public value: number; constructor(parent: ASTNode | undefined, offset: number) { super(parent, offset); this.isInteger = true; this.value = Number.NaN; } } export class StringASTNodeImpl extends ASTNodeImpl implements StringASTNode { public type: 'string' = 'string'; public value: string; constructor(parent: ASTNode | undefined, offset: number, length?: number) { super(parent, offset, length); this.value = ''; } } export class PropertyASTNodeImpl extends ASTNodeImpl implements PropertyASTNode { public type: 'property' = 'property'; public keyNode: StringASTNode; public valueNode?: ASTNode; public colonOffset: number; constructor(parent: ObjectASTNode | undefined, offset: number, keyNode: StringASTNode) { super(parent, offset); this.colonOffset = -1; this.keyNode = keyNode; } public get children(): ASTNode[] { return this.valueNode ? [this.keyNode, this.valueNode] : [this.keyNode]; } } export class ObjectASTNodeImpl extends ASTNodeImpl implements ObjectASTNode { public type: 'object' = 'object'; public properties: PropertyASTNode[]; constructor(parent: ASTNode | undefined, offset: number) { super(parent, offset); this.properties = []; } public get children(): ASTNode[] { return this.properties; } } export function asSchema(schema: JSONSchemaRef): JSONSchema; export function asSchema(schema: JSONSchemaRef | undefined): JSONSchema | undefined; export function asSchema(schema: JSONSchemaRef | undefined): JSONSchema | undefined { if (isBoolean(schema)) { return schema ? {} : { "not": {} }; } return schema; } export interface JSONDocumentConfig { collectComments?: boolean; schemaDraft?: SchemaDraft; } export interface IApplicableSchema { node: ASTNode; inverted?: boolean; schema: JSONSchema; } export enum EnumMatch { Key, Enum } const httpPrefix = `http://json-schema.org/`; const httpsPrefix = `https://json-schema.org/`; export function normalizeId(id: string): string { // use the https prefix for the old json-schema.org meta schemas // See https://github.com/microsoft/vscode/issues/195189 if (id.startsWith(httpPrefix)) { id = httpsPrefix + id.substring(httpPrefix.length); } // remove trailing '#', normalize drive capitalization try { return URI.parse(id).toString(true); } catch (e) { return id; } } export function getSchemaDraftFromId(schemaId: string): SchemaDraft | undefined { return schemaDraftFromId[normalizeId(schemaId)] ?? undefined; } const schemaDraftFromId: { [id: string]: SchemaDraft } = { 'https://json-schema.org/draft-03/schema': SchemaDraft.v3, 'https://json-schema.org/draft-04/schema': SchemaDraft.v4, 'https://json-schema.org/draft-06/schema': SchemaDraft.v6, 'https://json-schema.org/draft-07/schema': SchemaDraft.v7, 'https://json-schema.org/draft/2019-09/schema': SchemaDraft.v2019_09, 'https://json-schema.org/draft/2020-12/schema': SchemaDraft.v2020_12 }; export interface ISchemaCollector { schemas: IApplicableSchema[]; add(schema: IApplicableSchema): void; merge(other: ISchemaCollector): void; include(node: ASTNode): boolean; newSub(): ISchemaCollector; } export interface IEvaluationContext { readonly schemaDraft: SchemaDraft; } class EvaluationContext implements IEvaluationContext { constructor(public readonly schemaDraft: SchemaDraft) { } } class SchemaCollector implements ISchemaCollector { schemas: IApplicableSchema[] = []; constructor(private focusOffset = -1, private exclude?: ASTNode) { } add(schema: IApplicableSchema) { this.schemas.push(schema); } merge(other: ISchemaCollector) { Array.prototype.push.apply(this.schemas, other.schemas); } include(node: ASTNode) { return (this.focusOffset === -1 || contains(node, this.focusOffset)) && (node !== this.exclude); } newSub(): ISchemaCollector { return new SchemaCollector(-1, this.exclude); } } class NoOpSchemaCollector implements ISchemaCollector { private constructor() { } get schemas() { return []; } add(_schema: IApplicableSchema) { } merge(_other: ISchemaCollector) { } include(_node: ASTNode) { return true; } newSub(): ISchemaCollector { return this; } static instance = new NoOpSchemaCollector(); } export class ValidationResult { public problems: IProblem[]; public propertiesMatches: number; public processedProperties: Set; public propertiesValueMatches: number; public primaryValueMatches: number; public enumValueMatch: boolean; public enumValues: any[] | undefined; constructor() { this.problems = []; this.propertiesMatches = 0; this.processedProperties = new Set(); this.propertiesValueMatches = 0; this.primaryValueMatches = 0; this.enumValueMatch = false; this.enumValues = undefined; } public hasProblems(): boolean { return !!this.problems.length; } public merge(validationResult: ValidationResult): void { this.problems = this.problems.concat(validationResult.problems); this.propertiesMatches += validationResult.propertiesMatches; this.propertiesValueMatches += validationResult.propertiesValueMatches; this.mergeProcessedProperties(validationResult); } public mergeEnumValues(validationResult: ValidationResult): void { if (!this.enumValueMatch && !validationResult.enumValueMatch && this.enumValues && validationResult.enumValues) { this.enumValues = this.enumValues.concat(validationResult.enumValues); } } public updateEnumMismatchProblemMessages(): void { if (!this.enumValueMatch && this.enumValues) { for (const error of this.problems) { if (error.code === ErrorCode.EnumValueMismatch) { error.message = l10n.t('Value is not accepted. Valid values: {0}.', this.enumValues.map(v => JSON.stringify(v)).join(', ')); } } } } public mergePropertyMatch(propertyValidationResult: ValidationResult): void { this.problems = this.problems.concat(propertyValidationResult.problems); this.propertiesMatches++; if (propertyValidationResult.enumValueMatch || !propertyValidationResult.hasProblems() && propertyValidationResult.propertiesMatches) { this.propertiesValueMatches++; } if (propertyValidationResult.enumValueMatch && propertyValidationResult.enumValues && propertyValidationResult.enumValues.length === 1) { this.primaryValueMatches++; } } public mergeProcessedProperties(validationResult: ValidationResult): void { validationResult.processedProperties.forEach(p => this.processedProperties.add(p)); } public compare(other: ValidationResult): number { const hasProblems = this.hasProblems(); if (hasProblems !== other.hasProblems()) { return hasProblems ? -1 : 1; } if (this.enumValueMatch !== other.enumValueMatch) { return other.enumValueMatch ? -1 : 1; } if (this.primaryValueMatches !== other.primaryValueMatches) { return this.primaryValueMatches - other.primaryValueMatches; } if (this.propertiesValueMatches !== other.propertiesValueMatches) { return this.propertiesValueMatches - other.propertiesValueMatches; } return this.propertiesMatches - other.propertiesMatches; } } export function newJSONDocument(root: ASTNode | undefined, diagnostics: Diagnostic[] = [], comments: Range[] = []) { return new JSONDocument(root, diagnostics, comments); } export function getNodeValue(node: ASTNode): any { return Json.getNodeValue(node); } export function getNodePath(node: ASTNode): JSONPath { return Json.getNodePath(node); } export function contains(node: ASTNode, offset: number, includeRightBound = false): boolean { return offset >= node.offset && offset < (node.offset + node.length) || includeRightBound && offset === (node.offset + node.length); } export class JSONDocument { constructor(public readonly root: ASTNode | undefined, public readonly syntaxErrors: Diagnostic[] = [], public readonly comments: Range[] = []) { } public getNodeFromOffset(offset: number, includeRightBound = false): ASTNode | undefined { if (this.root) { return Json.findNodeAtOffset(this.root, offset, includeRightBound); } return undefined; } public visit(visitor: (node: ASTNode) => boolean): void { if (this.root) { const doVisit = (node: ASTNode): boolean => { let ctn = visitor(node); const children = node.children; if (Array.isArray(children)) { for (let i = 0; i < children.length && ctn; i++) { ctn = doVisit(children[i]); } } return ctn; }; doVisit(this.root); } } public validate(textDocument: TextDocument, schema: JSONSchema | undefined, severity: DiagnosticSeverity = DiagnosticSeverity.Warning, schemaDraft?: SchemaDraft): Diagnostic[] | undefined { if (this.root && schema) { const validationResult = new ValidationResult(); validate(this.root, schema, validationResult, NoOpSchemaCollector.instance, new EvaluationContext(schemaDraft ?? getSchemaDraft(schema))); return validationResult.problems.map(p => { const range = Range.create(textDocument.positionAt(p.location.offset), textDocument.positionAt(p.location.offset + p.location.length)); return Diagnostic.create(range, p.message, p.severity ?? severity, p.code); }); } return undefined; } public getMatchingSchemas(schema: JSONSchema, focusOffset: number = -1, exclude?: ASTNode): IApplicableSchema[] { if (this.root && schema) { const matchingSchemas = new SchemaCollector(focusOffset, exclude); const schemaDraft = getSchemaDraft(schema); const context = new EvaluationContext(schemaDraft); validate(this.root, schema, new ValidationResult(), matchingSchemas, context); return matchingSchemas.schemas; } return []; } } function getSchemaDraft(schema: JSONSchema, fallBack = SchemaDraft.v2020_12) { let schemaId = schema.$schema; if (schemaId) { return getSchemaDraftFromId(schemaId) ?? fallBack; } return fallBack; } function validate(n: ASTNode | undefined, schema: JSONSchema, validationResult: ValidationResult, matchingSchemas: ISchemaCollector, context: IEvaluationContext): void { if (!n || !matchingSchemas.include(n)) { return; } if (n.type === 'property') { return validate(n.valueNode, schema, validationResult, matchingSchemas, context); } const node = n; _validateNode(); switch (node.type) { case 'object': _validateObjectNode(node); break; case 'array': _validateArrayNode(node); break; case 'string': _validateStringNode(node); break; case 'number': _validateNumberNode(node); break; } matchingSchemas.add({ node: node, schema: schema }); function _validateNode() { function matchesType(type: string) { return node.type === type || (type === 'integer' && node.type === 'number' && node.isInteger); } if (Array.isArray(schema.type)) { if (!schema.type.some(matchesType)) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: schema.errorMessage || l10n.t('Incorrect type. Expected one of {0}.', (schema.type).join(', ')) }); } } else if (schema.type) { if (!matchesType(schema.type)) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: schema.errorMessage || l10n.t('Incorrect type. Expected "{0}".', schema.type) }); } } if (Array.isArray(schema.allOf)) { for (const subSchemaRef of schema.allOf) { const subValidationResult = new ValidationResult(); const subMatchingSchemas = matchingSchemas.newSub(); validate(node, asSchema(subSchemaRef), subValidationResult, subMatchingSchemas, context); validationResult.merge(subValidationResult); matchingSchemas.merge(subMatchingSchemas); } } const notSchema = asSchema(schema.not); if (notSchema) { const subValidationResult = new ValidationResult(); const subMatchingSchemas = matchingSchemas.newSub(); validate(node, notSchema, subValidationResult, subMatchingSchemas, context); if (!subValidationResult.hasProblems()) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: schema.errorMessage || l10n.t("Matches a schema that is not allowed.") }); } for (const ms of subMatchingSchemas.schemas) { ms.inverted = !ms.inverted; matchingSchemas.add(ms); } } const testAlternatives = (alternatives: JSONSchemaRef[], maxOneMatch: boolean) => { const matches = []; const alternativesToTest = _tryDiscriminatorOptimization(alternatives) ?? alternatives; // remember the best match that is used for error messages let bestMatch: { schema: JSONSchema; validationResult: ValidationResult; matchingSchemas: ISchemaCollector; } | undefined = undefined; for (const subSchemaRef of alternativesToTest) { const subSchema = asSchema(subSchemaRef); const subValidationResult = new ValidationResult(); const subMatchingSchemas = matchingSchemas.newSub(); validate(node, subSchema, subValidationResult, subMatchingSchemas, context); if (!subValidationResult.hasProblems()) { matches.push(subSchema); } if (!bestMatch) { bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas }; } else { if (!maxOneMatch && !subValidationResult.hasProblems() && !bestMatch.validationResult.hasProblems()) { // no errors, both are equally good matches bestMatch.matchingSchemas.merge(subMatchingSchemas); bestMatch.validationResult.propertiesMatches += subValidationResult.propertiesMatches; bestMatch.validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches; bestMatch.validationResult.mergeProcessedProperties(subValidationResult); } else { const compareResult = subValidationResult.compare(bestMatch.validationResult); if (compareResult > 0) { // our node is the best matching so far bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas }; } else if (compareResult === 0) { // there's already a best matching but we are as good bestMatch.matchingSchemas.merge(subMatchingSchemas); bestMatch.validationResult.mergeEnumValues(subValidationResult); } } } } if (matches.length > 1 && maxOneMatch) { validationResult.problems.push({ location: { offset: node.offset, length: 1 }, message: l10n.t("Matches multiple schemas when only one must validate.") }); } if (bestMatch) { bestMatch.validationResult.updateEnumMismatchProblemMessages(); validationResult.merge(bestMatch.validationResult); matchingSchemas.merge(bestMatch.matchingSchemas); } return matches.length; }; if (Array.isArray(schema.anyOf)) { testAlternatives(schema.anyOf, false); } if (Array.isArray(schema.oneOf)) { testAlternatives(schema.oneOf, true); } const testBranch = (schema: JSONSchemaRef) => { const subValidationResult = new ValidationResult(); const subMatchingSchemas = matchingSchemas.newSub(); validate(node, asSchema(schema), subValidationResult, subMatchingSchemas, context); validationResult.merge(subValidationResult); matchingSchemas.merge(subMatchingSchemas); }; const testCondition = (ifSchema: JSONSchemaRef, thenSchema?: JSONSchemaRef, elseSchema?: JSONSchemaRef) => { const subSchema = asSchema(ifSchema); const subValidationResult = new ValidationResult(); const subMatchingSchemas = matchingSchemas.newSub(); validate(node, subSchema, subValidationResult, subMatchingSchemas, context); matchingSchemas.merge(subMatchingSchemas); validationResult.mergeProcessedProperties(subValidationResult); if (!subValidationResult.hasProblems()) { if (thenSchema) { testBranch(thenSchema); } } else if (elseSchema) { testBranch(elseSchema); } }; const ifSchema = asSchema(schema.if); if (ifSchema) { testCondition(ifSchema, asSchema(schema.then), asSchema(schema.else)); } if (Array.isArray(schema.enum)) { const val = getNodeValue(node); let enumValueMatch = false; for (const e of schema.enum) { if (equals(val, e)) { enumValueMatch = true; break; } } validationResult.enumValues = schema.enum; validationResult.enumValueMatch = enumValueMatch; if (!enumValueMatch) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, code: ErrorCode.EnumValueMismatch, message: schema.errorMessage || l10n.t('Value is not accepted. Valid values: {0}.', schema.enum.map(v => JSON.stringify(v)).join(', ')) }); } } if (isDefined(schema.const)) { const val = getNodeValue(node); if (!equals(val, schema.const)) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, code: ErrorCode.EnumValueMismatch, message: schema.errorMessage || l10n.t('Value must be {0}.', JSON.stringify(schema.const)) }); validationResult.enumValueMatch = false; } else { validationResult.enumValueMatch = true; } validationResult.enumValues = [schema.const]; } let deprecationMessage = schema.deprecationMessage; if (deprecationMessage || schema.deprecated) { deprecationMessage = deprecationMessage || l10n.t('Value is deprecated'); let targetNode = node.parent?.type === 'property' ? node.parent : node; validationResult.problems.push({ location: { offset: targetNode.offset, length: targetNode.length }, severity: DiagnosticSeverity.Warning, message: deprecationMessage, code: ErrorCode.Deprecated }); } } function _tryDiscriminatorOptimization(alternatives: JSONSchemaRef[]): JSONSchemaRef[] | undefined { if (alternatives.length < 2) { return undefined; } const buildConstMap = (getSchemas: (alt: JSONSchema, idx: number) => [string | number, JSONSchema][] | undefined) => { const constMap = new Map>(); for (let i = 0; i < alternatives.length; i++) { const schemas = getSchemas(asSchema(alternatives[i]), i); if (!schemas) { return undefined; // Early exit if any alternative can't be processed } schemas.forEach(([key, schema]) => { if (schema.const !== undefined) { if (!constMap.has(key)) { constMap.set(key, new Map()); } const valueMap = constMap.get(key)!; if (!valueMap.has(schema.const)) { valueMap.set(schema.const, []); } valueMap.get(schema.const)!.push(i); } }); } return constMap; }; const findDiscriminator = (constMap: Map>, getValue: (key: string | number) => any) => { for (const [key, valueMap] of constMap) { const coveredAlts = new Set(); valueMap.forEach(indices => indices.forEach(idx => coveredAlts.add(idx))); if (coveredAlts.size === alternatives.length) { const discriminatorValue = getValue(key); const matchingIndices = valueMap.get(discriminatorValue); if (matchingIndices?.length) { return matchingIndices.map(idx => alternatives[idx]); } break; // Found valid discriminator but no match } } return undefined; }; if (node.type === 'object' && node.properties?.length) { const constMap = buildConstMap((schema) => schema.properties ? Object.entries(schema.properties).map(([k, v]) => [k, asSchema(v)]) : undefined ); if (constMap) { return findDiscriminator(constMap, (propName) => { const prop = node.properties.find(p => p.keyNode.value === propName); return prop?.valueNode?.type === 'string' ? prop.valueNode.value : undefined; }); } } else if (node.type === 'array' && node.items?.length) { const constMap = buildConstMap((schema) => { const itemSchemas = schema.prefixItems || (Array.isArray(schema.items) ? schema.items : undefined); return itemSchemas ? itemSchemas.map((item, idx) => [idx, asSchema(item)]) : undefined; }); if (constMap) { return findDiscriminator(constMap, (itemIndex) => { const item = node.items[itemIndex as number]; return item?.type === 'string' ? item.value : undefined; }); } } return undefined; } function _validateNumberNode(node: NumberASTNode): void { const val = node.value; function normalizeFloats(float: number): { value: number, multiplier: number } | null { const parts = /^(-?\d+)(?:\.(\d+))?(?:e([-+]\d+))?$/.exec(float.toString()); return parts && { value: Number(parts[1] + (parts[2] || '')), multiplier: (parts[2]?.length || 0) - (parseInt(parts[3]) || 0) }; }; if (isNumber(schema.multipleOf)) { let remainder: number = -1; if (Number.isInteger(schema.multipleOf)) { remainder = val % schema.multipleOf; } else { let normMultipleOf = normalizeFloats(schema.multipleOf); let normValue = normalizeFloats(val); if (normMultipleOf && normValue) { const multiplier = 10 ** Math.abs(normValue.multiplier - normMultipleOf.multiplier); if (normValue.multiplier < normMultipleOf.multiplier) { normValue.value *= multiplier; } else { normMultipleOf.value *= multiplier; } remainder = normValue.value % normMultipleOf.value; } } if (remainder !== 0) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Value is not divisible by {0}.', schema.multipleOf) }); } } function getExclusiveLimit(limit: number | undefined, exclusive: boolean | number | undefined): number | undefined { if (isNumber(exclusive)) { return exclusive; } if (isBoolean(exclusive) && exclusive) { return limit; } return undefined; } function getLimit(limit: number | undefined, exclusive: boolean | number | undefined): number | undefined { if (!isBoolean(exclusive) || !exclusive) { return limit; } return undefined; } const exclusiveMinimum = getExclusiveLimit(schema.minimum, schema.exclusiveMinimum); if (isNumber(exclusiveMinimum) && val <= exclusiveMinimum) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Value is below the exclusive minimum of {0}.', exclusiveMinimum) }); } const exclusiveMaximum = getExclusiveLimit(schema.maximum, schema.exclusiveMaximum); if (isNumber(exclusiveMaximum) && val >= exclusiveMaximum) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Value is above the exclusive maximum of {0}.', exclusiveMaximum) }); } const minimum = getLimit(schema.minimum, schema.exclusiveMinimum); if (isNumber(minimum) && val < minimum) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Value is below the minimum of {0}.', minimum) }); } const maximum = getLimit(schema.maximum, schema.exclusiveMaximum); if (isNumber(maximum) && val > maximum) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Value is above the maximum of {0}.', maximum) }); } } function _validateStringNode(node: StringASTNode): void { if (isNumber(schema.minLength) && stringLength(node.value) < schema.minLength) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('String is shorter than the minimum length of {0}.', schema.minLength) }); } if (isNumber(schema.maxLength) && stringLength(node.value) > schema.maxLength) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('String is longer than the maximum length of {0}.', schema.maxLength) }); } if (isString(schema.pattern)) { const regex = extendedRegExp(schema.pattern); if (regex && !(regex.test(node.value))) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: schema.patternErrorMessage || schema.errorMessage || l10n.t('String does not match the pattern of "{0}".', schema.pattern) }); } } if (schema.format) { switch (schema.format) { case 'uri': case 'uri-reference': { let errorMessage; if (!node.value) { errorMessage = l10n.t('URI expected.'); } else { const match = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/.exec(node.value); if (!match) { errorMessage = l10n.t('URI is expected.'); } else if (!match[2] && schema.format === 'uri') { errorMessage = l10n.t('URI with a scheme is expected.'); } } if (errorMessage) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: schema.patternErrorMessage || schema.errorMessage || l10n.t('String is not a URI: {0}', errorMessage) }); } } break; case 'color-hex': case 'date-time': case 'date': case 'time': case 'email': case 'hostname': case 'ipv4': case 'ipv6': const format = formats[schema.format]; if (!node.value || !format.pattern.exec(node.value)) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: schema.patternErrorMessage || schema.errorMessage || format.errorMessage }); } default: } } } function _validateArrayNode(node: ArrayASTNode): void { let prefixItemsSchemas: JSONSchemaRef[] | undefined; let additionalItemSchema: JSONSchemaRef | undefined; if (context.schemaDraft >= SchemaDraft.v2020_12) { prefixItemsSchemas = schema.prefixItems; additionalItemSchema = !Array.isArray(schema.items) ? schema.items : undefined; } else { prefixItemsSchemas = Array.isArray(schema.items) ? schema.items : undefined; additionalItemSchema = !Array.isArray(schema.items) ? schema.items : schema.additionalItems; } let index = 0; if (prefixItemsSchemas !== undefined) { const max = Math.min(prefixItemsSchemas.length, node.items.length); for (; index < max; index++) { const subSchemaRef = prefixItemsSchemas[index]; const subSchema = asSchema(subSchemaRef); const itemValidationResult = new ValidationResult(); const item = node.items[index]; if (item) { validate(item, subSchema, itemValidationResult, matchingSchemas, context); validationResult.mergePropertyMatch(itemValidationResult); } validationResult.processedProperties.add(String(index)); } } if (additionalItemSchema !== undefined && index < node.items.length) { if (typeof additionalItemSchema === 'boolean') { if (additionalItemSchema === false) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Array has too many items according to schema. Expected {0} or fewer.', index) }); } for (; index < node.items.length; index++) { validationResult.processedProperties.add(String(index)); validationResult.propertiesValueMatches++; } } else { for (; index < node.items.length; index++) { const itemValidationResult = new ValidationResult(); validate(node.items[index], additionalItemSchema, itemValidationResult, matchingSchemas, context); validationResult.mergePropertyMatch(itemValidationResult); validationResult.processedProperties.add(String(index)); } } } const containsSchema = asSchema(schema.contains); if (containsSchema) { let containsCount = 0; for (let index = 0; index < node.items.length; index++) { const item = node.items[index]; const itemValidationResult = new ValidationResult(); validate(item, containsSchema, itemValidationResult, NoOpSchemaCollector.instance, context); if (!itemValidationResult.hasProblems()) { containsCount++; if (context.schemaDraft >= SchemaDraft.v2020_12) { validationResult.processedProperties.add(String(index)); } } } if (containsCount === 0 && !isNumber(schema.minContains)) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: schema.errorMessage || l10n.t('Array does not contain required item.') }); } if (isNumber(schema.minContains) && containsCount < schema.minContains) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: schema.errorMessage || l10n.t('Array has too few items that match the contains contraint. Expected {0} or more.', schema.minContains) }); } if (isNumber(schema.maxContains) && containsCount > schema.maxContains) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: schema.errorMessage || l10n.t('Array has too many items that match the contains contraint. Expected {0} or less.', schema.maxContains) }); } } const unevaluatedItems = schema.unevaluatedItems; if (unevaluatedItems !== undefined) { for (let i = 0; i < node.items.length; i++) { if (!validationResult.processedProperties.has(String(i))) { if (unevaluatedItems === false) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Item does not match any validation rule from the array.') }); } else { const itemValidationResult = new ValidationResult(); validate(node.items[i], schema.unevaluatedItems, itemValidationResult, matchingSchemas, context); validationResult.mergePropertyMatch(itemValidationResult); } } validationResult.processedProperties.add(String(i)); validationResult.propertiesValueMatches++; } } if (isNumber(schema.minItems) && node.items.length < schema.minItems) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Array has too few items. Expected {0} or more.', schema.minItems) }); } if (isNumber(schema.maxItems) && node.items.length > schema.maxItems) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Array has too many items. Expected {0} or fewer.', schema.maxItems) }); } if (schema.uniqueItems === true) { const values = getNodeValue(node); function hasDuplicates() { for (let i = 0; i < values.length - 1; i++) { const value = values[i]; for (let j = i + 1; j < values.length; j++) { if (equals(value, values[j])) { return true; } } } return false; } if (hasDuplicates()) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Array has duplicate items.') }); } } } function _validateObjectNode(node: ObjectASTNode): void { const seenKeys: { [key: string]: ASTNode | undefined } = Object.create(null); const unprocessedProperties: Set = new Set(); for (const propertyNode of node.properties) { const key = propertyNode.keyNode.value; seenKeys[key] = propertyNode.valueNode; unprocessedProperties.add(key); } if (Array.isArray(schema.required)) { for (const propertyName of schema.required) { if (!seenKeys[propertyName]) { const keyNode = node.parent && node.parent.type === 'property' && node.parent.keyNode; const location = keyNode ? { offset: keyNode.offset, length: keyNode.length } : { offset: node.offset, length: 1 }; validationResult.problems.push({ location: location, message: l10n.t('Missing property "{0}".', propertyName) }); } } } const propertyProcessed = (prop: string) => { unprocessedProperties.delete(prop); validationResult.processedProperties.add(prop); }; if (schema.properties) { for (const propertyName of Object.keys(schema.properties)) { propertyProcessed(propertyName); const propertySchema = schema.properties[propertyName]; const child = seenKeys[propertyName]; if (child) { if (isBoolean(propertySchema)) { if (!propertySchema) { const propertyNode = child.parent; validationResult.problems.push({ location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length }, message: schema.errorMessage || l10n.t('Property {0} is not allowed.', propertyName) }); } else { validationResult.propertiesMatches++; validationResult.propertiesValueMatches++; } } else { const propertyValidationResult = new ValidationResult(); validate(child, propertySchema, propertyValidationResult, matchingSchemas, context); validationResult.mergePropertyMatch(propertyValidationResult); } } } } if (schema.patternProperties) { for (const propertyPattern of Object.keys(schema.patternProperties)) { const regex = extendedRegExp(propertyPattern); if (regex) { const processed = []; for (const propertyName of unprocessedProperties) { if (regex.test(propertyName)) { processed.push(propertyName); const child = seenKeys[propertyName]; if (child) { const propertySchema = schema.patternProperties[propertyPattern]; if (isBoolean(propertySchema)) { if (!propertySchema) { const propertyNode = child.parent; validationResult.problems.push({ location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length }, message: schema.errorMessage || l10n.t('Property {0} is not allowed.', propertyName) }); } else { validationResult.propertiesMatches++; validationResult.propertiesValueMatches++; } } else { const propertyValidationResult = new ValidationResult(); validate(child, propertySchema, propertyValidationResult, matchingSchemas, context); validationResult.mergePropertyMatch(propertyValidationResult); } } } } processed.forEach(propertyProcessed); } } } const additionalProperties = schema.additionalProperties; if (additionalProperties !== undefined) { for (const propertyName of unprocessedProperties) { propertyProcessed(propertyName); const child = seenKeys[propertyName]; if (child) { if (additionalProperties === false) { const propertyNode = child.parent; validationResult.problems.push({ location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length }, message: schema.errorMessage || l10n.t('Property {0} is not allowed.', propertyName) }); } else if (additionalProperties !== true) { const propertyValidationResult = new ValidationResult(); validate(child, additionalProperties, propertyValidationResult, matchingSchemas, context); validationResult.mergePropertyMatch(propertyValidationResult); } } } } const unevaluatedProperties = schema.unevaluatedProperties; if (unevaluatedProperties !== undefined) { const processed = []; for (const propertyName of unprocessedProperties) { if (!validationResult.processedProperties.has(propertyName)) { processed.push(propertyName); const child = seenKeys[propertyName]; if (child) { if (unevaluatedProperties === false) { const propertyNode = child.parent; validationResult.problems.push({ location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length }, message: schema.errorMessage || l10n.t('Property {0} is not allowed.', propertyName) }); } else if (unevaluatedProperties !== true) { const propertyValidationResult = new ValidationResult(); validate(child, unevaluatedProperties, propertyValidationResult, matchingSchemas, context); validationResult.mergePropertyMatch(propertyValidationResult); } } } } processed.forEach(propertyProcessed); } if (isNumber(schema.maxProperties)) { if (node.properties.length > schema.maxProperties) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Object has more properties than limit of {0}.', schema.maxProperties) }); } } if (isNumber(schema.minProperties)) { if (node.properties.length < schema.minProperties) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Object has fewer properties than the required number of {0}', schema.minProperties) }); } } if (schema.dependentRequired) { for (const key in schema.dependentRequired) { const prop = seenKeys[key]; const propertyDeps = schema.dependentRequired[key]; if (prop && Array.isArray(propertyDeps)) { _validatePropertyDependencies(key, propertyDeps); } } } if (schema.dependentSchemas) { for (const key in schema.dependentSchemas) { const prop = seenKeys[key]; const propertyDeps = schema.dependentSchemas[key]; if (prop && isObject(propertyDeps)) { _validatePropertyDependencies(key, propertyDeps); } } } if (schema.dependencies) { for (const key in schema.dependencies) { const prop = seenKeys[key]; if (prop) { _validatePropertyDependencies(key, schema.dependencies[key]); } } } const propertyNames = asSchema(schema.propertyNames); if (propertyNames) { for (const f of node.properties) { const key = f.keyNode; if (key) { validate(key, propertyNames, validationResult, matchingSchemas, context); } } } function _validatePropertyDependencies(key: string, propertyDep: string[] | JSONSchemaRef) { if (Array.isArray(propertyDep)) { for (const requiredProp of propertyDep) { if (!seenKeys[requiredProp]) { validationResult.problems.push({ location: { offset: node.offset, length: node.length }, message: l10n.t('Object is missing property {0} required by property {1}.', requiredProp, key) }); } else { validationResult.propertiesValueMatches++; } } } else { const propertySchema = asSchema(propertyDep); if (propertySchema) { const propertyValidationResult = new ValidationResult(); validate(node, propertySchema, propertyValidationResult, matchingSchemas, context); validationResult.mergePropertyMatch(propertyValidationResult); } } } } } export function parse(textDocument: TextDocument, config?: JSONDocumentConfig): JSONDocument { const problems: Diagnostic[] = []; let lastProblemOffset = -1; const text = textDocument.getText(); const scanner = Json.createScanner(text, false); const commentRanges: Range[] | undefined = config && config.collectComments ? [] : undefined; function _scanNext(): Json.SyntaxKind { while (true) { const token = scanner.scan(); _checkScanError(); switch (token) { case Json.SyntaxKind.LineCommentTrivia: case Json.SyntaxKind.BlockCommentTrivia: if (Array.isArray(commentRanges)) { commentRanges.push(Range.create(textDocument.positionAt(scanner.getTokenOffset()), textDocument.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()))); } break; case Json.SyntaxKind.Trivia: case Json.SyntaxKind.LineBreakTrivia: break; default: return token; } } } function _accept(token: Json.SyntaxKind): boolean { if (scanner.getToken() === token) { _scanNext(); return true; } return false; } function _errorAtRange(message: string, code: ErrorCode, startOffset: number, endOffset: number, severity: DiagnosticSeverity = DiagnosticSeverity.Error): void { if (problems.length === 0 || startOffset !== lastProblemOffset) { const range = Range.create(textDocument.positionAt(startOffset), textDocument.positionAt(endOffset)); problems.push(Diagnostic.create(range, message, severity, code, textDocument.languageId)); lastProblemOffset = startOffset; } } function _error(message: string, code: ErrorCode, node: T | undefined = undefined, skipUntilAfter: Json.SyntaxKind[] = [], skipUntil: Json.SyntaxKind[] = []): T | undefined { let start = scanner.getTokenOffset(); let end = scanner.getTokenOffset() + scanner.getTokenLength(); if (start === end && start > 0) { start--; while (start > 0 && /\s/.test(text.charAt(start))) { start--; } end = start + 1; } _errorAtRange(message, code, start, end); if (node) { _finalize(node, false); } if (skipUntilAfter.length + skipUntil.length > 0) { let token = scanner.getToken(); while (token !== Json.SyntaxKind.EOF) { if (skipUntilAfter.indexOf(token) !== -1) { _scanNext(); break; } else if (skipUntil.indexOf(token) !== -1) { break; } token = _scanNext(); } } return node; } function _checkScanError(): boolean { switch (scanner.getTokenError()) { case Json.ScanError.InvalidUnicode: _error(l10n.t('Invalid unicode sequence in string.'), ErrorCode.InvalidUnicode); return true; case Json.ScanError.InvalidEscapeCharacter: _error(l10n.t('Invalid escape character in string.'), ErrorCode.InvalidEscapeCharacter); return true; case Json.ScanError.UnexpectedEndOfNumber: _error(l10n.t('Unexpected end of number.'), ErrorCode.UnexpectedEndOfNumber); return true; case Json.ScanError.UnexpectedEndOfComment: _error(l10n.t('Unexpected end of comment.'), ErrorCode.UnexpectedEndOfComment); return true; case Json.ScanError.UnexpectedEndOfString: _error(l10n.t('Unexpected end of string.'), ErrorCode.UnexpectedEndOfString); return true; case Json.ScanError.InvalidCharacter: _error(l10n.t('Invalid characters in string. Control characters must be escaped.'), ErrorCode.InvalidCharacter); return true; } return false; } function _finalize(node: T, scanNext: boolean): T { node.length = scanner.getTokenOffset() + scanner.getTokenLength() - node.offset; if (scanNext) { _scanNext(); } return node; } function _parseArray(parent: ASTNode | undefined): ArrayASTNode | undefined { if (scanner.getToken() !== Json.SyntaxKind.OpenBracketToken) { return undefined; } const node = new ArrayASTNodeImpl(parent, scanner.getTokenOffset()); _scanNext(); // consume OpenBracketToken const count = 0; let needsComma = false; while (scanner.getToken() !== Json.SyntaxKind.CloseBracketToken && scanner.getToken() !== Json.SyntaxKind.EOF) { if (scanner.getToken() === Json.SyntaxKind.CommaToken) { if (!needsComma) { _error(l10n.t('Value expected'), ErrorCode.ValueExpected); } const commaOffset = scanner.getTokenOffset(); _scanNext(); // consume comma if (scanner.getToken() === Json.SyntaxKind.CloseBracketToken) { if (needsComma) { _errorAtRange(l10n.t('Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1); } continue; } } else if (needsComma) { _error(l10n.t('Expected comma'), ErrorCode.CommaExpected); } const item = _parseValue(node); if (!item) { _error(l10n.t('Value expected'), ErrorCode.ValueExpected, undefined, [], [Json.SyntaxKind.CloseBracketToken, Json.SyntaxKind.CommaToken]); } else { node.items.push(item); } needsComma = true; } if (scanner.getToken() !== Json.SyntaxKind.CloseBracketToken) { return _error(l10n.t('Expected comma or closing bracket'), ErrorCode.CommaOrCloseBacketExpected, node); } return _finalize(node, true); } const keyPlaceholder = new StringASTNodeImpl(undefined, 0, 0); function _parseProperty(parent: ObjectASTNode | undefined, keysSeen: { [key: string]: (PropertyASTNode | boolean) }): PropertyASTNode | undefined { const node = new PropertyASTNodeImpl(parent, scanner.getTokenOffset(), keyPlaceholder); let key = _parseString(node); if (!key) { if (scanner.getToken() === Json.SyntaxKind.Unknown) { // give a more helpful error message _error(l10n.t('Property keys must be doublequoted'), ErrorCode.PropertyKeysMustBeDoublequoted); const keyNode = new StringASTNodeImpl(node, scanner.getTokenOffset(), scanner.getTokenLength()); keyNode.value = scanner.getTokenValue(); key = keyNode; _scanNext(); // consume Unknown } else { return undefined; } } node.keyNode = key; // For JSON files that forbid code comments, there is a convention to use the key name "//" to add comments. // Multiple instances of "//" are okay. if (key.value !== "//") { const seen = keysSeen[key.value]; if (seen) { _errorAtRange(l10n.t("Duplicate object key"), ErrorCode.DuplicateKey, node.keyNode.offset, node.keyNode.offset + node.keyNode.length, DiagnosticSeverity.Warning); if (isObject(seen)) { _errorAtRange(l10n.t("Duplicate object key"), ErrorCode.DuplicateKey, seen.keyNode.offset, seen.keyNode.offset + seen.keyNode.length, DiagnosticSeverity.Warning); } keysSeen[key.value] = true; // if the same key is duplicate again, avoid duplicate error reporting } else { keysSeen[key.value] = node; } } if (scanner.getToken() === Json.SyntaxKind.ColonToken) { node.colonOffset = scanner.getTokenOffset(); _scanNext(); // consume ColonToken } else { _error(l10n.t('Colon expected'), ErrorCode.ColonExpected); if (scanner.getToken() === Json.SyntaxKind.StringLiteral && textDocument.positionAt(key.offset + key.length).line < textDocument.positionAt(scanner.getTokenOffset()).line) { node.length = key.length; return node; } } const value = _parseValue(node); if (!value) { return _error(l10n.t('Value expected'), ErrorCode.ValueExpected, node, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]); } node.valueNode = value; node.length = value.offset + value.length - node.offset; return node; } function _parseObject(parent: ASTNode | undefined): ObjectASTNode | undefined { if (scanner.getToken() !== Json.SyntaxKind.OpenBraceToken) { return undefined; } const node = new ObjectASTNodeImpl(parent, scanner.getTokenOffset()); const keysSeen: any = Object.create(null); _scanNext(); // consume OpenBraceToken let needsComma = false; while (scanner.getToken() !== Json.SyntaxKind.CloseBraceToken && scanner.getToken() !== Json.SyntaxKind.EOF) { if (scanner.getToken() === Json.SyntaxKind.CommaToken) { if (!needsComma) { _error(l10n.t('Property expected'), ErrorCode.PropertyExpected); } const commaOffset = scanner.getTokenOffset(); _scanNext(); // consume comma if (scanner.getToken() === Json.SyntaxKind.CloseBraceToken) { if (needsComma) { _errorAtRange(l10n.t('Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1); } continue; } } else if (needsComma) { _error(l10n.t('Expected comma'), ErrorCode.CommaExpected); } const property = _parseProperty(node, keysSeen); if (!property) { _error(l10n.t('Property expected'), ErrorCode.PropertyExpected, undefined, [], [Json.SyntaxKind.CloseBraceToken, Json.SyntaxKind.CommaToken]); } else { node.properties.push(property); } needsComma = true; } if (scanner.getToken() !== Json.SyntaxKind.CloseBraceToken) { return _error(l10n.t('Expected comma or closing brace'), ErrorCode.CommaOrCloseBraceExpected, node); } return _finalize(node, true); } function _parseString(parent: ASTNode | undefined): StringASTNode | undefined { if (scanner.getToken() !== Json.SyntaxKind.StringLiteral) { return undefined; } const node = new StringASTNodeImpl(parent, scanner.getTokenOffset()); node.value = scanner.getTokenValue(); return _finalize(node, true); } function _parseNumber(parent: ASTNode | undefined): NumberASTNode | undefined { if (scanner.getToken() !== Json.SyntaxKind.NumericLiteral) { return undefined; } const node = new NumberASTNodeImpl(parent, scanner.getTokenOffset()); if (scanner.getTokenError() === Json.ScanError.None) { const tokenValue = scanner.getTokenValue(); try { const numberValue = JSON.parse(tokenValue); if (!isNumber(numberValue)) { return _error(l10n.t('Invalid number format.'), ErrorCode.Undefined, node); } node.value = numberValue; } catch (e) { return _error(l10n.t('Invalid number format.'), ErrorCode.Undefined, node); } node.isInteger = tokenValue.indexOf('.') === -1; } return _finalize(node, true); } function _parseLiteral(parent: ASTNode | undefined): ASTNode | undefined { let node: ASTNodeImpl; switch (scanner.getToken()) { case Json.SyntaxKind.NullKeyword: return _finalize(new NullASTNodeImpl(parent, scanner.getTokenOffset()), true); case Json.SyntaxKind.TrueKeyword: return _finalize(new BooleanASTNodeImpl(parent, true, scanner.getTokenOffset()), true); case Json.SyntaxKind.FalseKeyword: return _finalize(new BooleanASTNodeImpl(parent, false, scanner.getTokenOffset()), true); default: return undefined; } } function _parseValue(parent: ASTNode | undefined): ASTNode | undefined { return _parseArray(parent) || _parseObject(parent) || _parseString(parent) || _parseNumber(parent) || _parseLiteral(parent); } let _root: ASTNode | undefined = undefined; const token = _scanNext(); if (token !== Json.SyntaxKind.EOF) { _root = _parseValue(_root); if (!_root) { _error(l10n.t('Expected a JSON object, array or literal.'), ErrorCode.Undefined); } else if (scanner.getToken() !== Json.SyntaxKind.EOF) { _error(l10n.t('End of file expected.'), ErrorCode.Undefined); } } return new JSONDocument(_root, problems, commentRanges); } vscode-json-languageservice-5.7.2/src/services/000077500000000000000000000000001514707600300215365ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/src/services/configuration.ts000066400000000000000000000344531514707600300247660ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { ISchemaContributions } from './jsonSchemaService'; import draft201909Flat from './schemas/draft-2019-09-flat'; import draft202012Flat from './schemas/draft-2020-12-flat'; import * as l10n from '@vscode/l10n'; export const schemaContributions: ISchemaContributions = { schemaAssociations: [], schemas: { // bundle the schema-schema to include (localized) descriptions 'https://json-schema.org/draft-04/schema': { 'definitions': { 'schemaArray': { 'type': 'array', 'minItems': 1, 'items': { '$ref': '#' } }, 'positiveInteger': { 'type': 'integer', 'minimum': 0 }, 'positiveIntegerDefault0': { 'allOf': [ { '$ref': '#/definitions/positiveInteger' }, { 'default': 0 } ] }, 'simpleTypes': { 'type': 'string', 'enum': [ 'array', 'boolean', 'integer', 'null', 'number', 'object', 'string' ] }, 'stringArray': { 'type': 'array', 'items': { 'type': 'string' }, 'minItems': 1, 'uniqueItems': true } }, 'type': 'object', 'properties': { 'id': { 'type': 'string', 'format': 'uri' }, '$schema': { 'type': 'string', 'format': 'uri' }, 'title': { 'type': 'string' }, 'description': { 'type': 'string' }, 'default': {}, 'multipleOf': { 'type': 'number', 'minimum': 0, 'exclusiveMinimum': true }, 'maximum': { 'type': 'number' }, 'exclusiveMaximum': { 'type': 'boolean', 'default': false }, 'minimum': { 'type': 'number' }, 'exclusiveMinimum': { 'type': 'boolean', 'default': false }, 'maxLength': { 'allOf': [ { '$ref': '#/definitions/positiveInteger' } ] }, 'minLength': { 'allOf': [ { '$ref': '#/definitions/positiveIntegerDefault0' } ] }, 'pattern': { 'type': 'string', 'format': 'regex' }, 'additionalItems': { 'anyOf': [ { 'type': 'boolean' }, { '$ref': '#' } ], 'default': {} }, 'items': { 'anyOf': [ { '$ref': '#' }, { '$ref': '#/definitions/schemaArray' } ], 'default': {} }, 'maxItems': { 'allOf': [ { '$ref': '#/definitions/positiveInteger' } ] }, 'minItems': { 'allOf': [ { '$ref': '#/definitions/positiveIntegerDefault0' } ] }, 'uniqueItems': { 'type': 'boolean', 'default': false }, 'maxProperties': { 'allOf': [ { '$ref': '#/definitions/positiveInteger' } ] }, 'minProperties': { 'allOf': [ { '$ref': '#/definitions/positiveIntegerDefault0' } ] }, 'required': { 'allOf': [ { '$ref': '#/definitions/stringArray' } ] }, 'additionalProperties': { 'anyOf': [ { 'type': 'boolean' }, { '$ref': '#' } ], 'default': {} }, 'definitions': { 'type': 'object', 'additionalProperties': { '$ref': '#' }, 'default': {} }, 'properties': { 'type': 'object', 'additionalProperties': { '$ref': '#' }, 'default': {} }, 'patternProperties': { 'type': 'object', 'additionalProperties': { '$ref': '#' }, 'default': {} }, 'dependencies': { 'type': 'object', 'additionalProperties': { 'anyOf': [ { '$ref': '#' }, { '$ref': '#/definitions/stringArray' } ] } }, 'enum': { 'type': 'array', 'minItems': 1, 'uniqueItems': true }, 'type': { 'anyOf': [ { '$ref': '#/definitions/simpleTypes' }, { 'type': 'array', 'items': { '$ref': '#/definitions/simpleTypes' }, 'minItems': 1, 'uniqueItems': true } ] }, 'format': { 'anyOf': [ { 'type': 'string', 'enum': [ 'date-time', 'uri', 'email', 'hostname', 'ipv4', 'ipv6', 'regex' ] }, { 'type': 'string' } ] }, 'allOf': { 'allOf': [ { '$ref': '#/definitions/schemaArray' } ] }, 'anyOf': { 'allOf': [ { '$ref': '#/definitions/schemaArray' } ] }, 'oneOf': { 'allOf': [ { '$ref': '#/definitions/schemaArray' } ] }, 'not': { 'allOf': [ { '$ref': '#' } ] } }, 'dependencies': { 'exclusiveMaximum': [ 'maximum' ], 'exclusiveMinimum': [ 'minimum' ] }, 'default': {} }, 'https://json-schema.org/draft-07/schema': { 'definitions': { 'schemaArray': { 'type': 'array', 'minItems': 1, 'items': { '$ref': '#' } }, 'nonNegativeInteger': { 'type': 'integer', 'minimum': 0 }, 'nonNegativeIntegerDefault0': { 'allOf': [ { '$ref': '#/definitions/nonNegativeInteger' }, { 'default': 0 } ] }, 'simpleTypes': { 'enum': [ 'array', 'boolean', 'integer', 'null', 'number', 'object', 'string' ] }, 'stringArray': { 'type': 'array', 'items': { 'type': 'string' }, 'uniqueItems': true, 'default': [] } }, 'type': ['object', 'boolean'], 'properties': { '$id': { 'type': 'string', 'format': 'uri-reference' }, '$schema': { 'type': 'string', 'format': 'uri' }, '$ref': { 'type': 'string', 'format': 'uri-reference' }, '$comment': { 'type': 'string' }, 'title': { 'type': 'string' }, 'description': { 'type': 'string' }, 'default': true, 'readOnly': { 'type': 'boolean', 'default': false }, 'examples': { 'type': 'array', 'items': true }, 'multipleOf': { 'type': 'number', 'exclusiveMinimum': 0 }, 'maximum': { 'type': 'number' }, 'exclusiveMaximum': { 'type': 'number' }, 'minimum': { 'type': 'number' }, 'exclusiveMinimum': { 'type': 'number' }, 'maxLength': { '$ref': '#/definitions/nonNegativeInteger' }, 'minLength': { '$ref': '#/definitions/nonNegativeIntegerDefault0' }, 'pattern': { 'type': 'string', 'format': 'regex' }, 'additionalItems': { '$ref': '#' }, 'items': { 'anyOf': [ { '$ref': '#' }, { '$ref': '#/definitions/schemaArray' } ], 'default': true }, 'maxItems': { '$ref': '#/definitions/nonNegativeInteger' }, 'minItems': { '$ref': '#/definitions/nonNegativeIntegerDefault0' }, 'uniqueItems': { 'type': 'boolean', 'default': false }, 'contains': { '$ref': '#' }, 'maxProperties': { '$ref': '#/definitions/nonNegativeInteger' }, 'minProperties': { '$ref': '#/definitions/nonNegativeIntegerDefault0' }, 'required': { '$ref': '#/definitions/stringArray' }, 'additionalProperties': { '$ref': '#' }, 'definitions': { 'type': 'object', 'additionalProperties': { '$ref': '#' }, 'default': {} }, 'properties': { 'type': 'object', 'additionalProperties': { '$ref': '#' }, 'default': {} }, 'patternProperties': { 'type': 'object', 'additionalProperties': { '$ref': '#' }, 'propertyNames': { 'format': 'regex' }, 'default': {} }, 'dependencies': { 'type': 'object', 'additionalProperties': { 'anyOf': [ { '$ref': '#' }, { '$ref': '#/definitions/stringArray' } ] } }, 'propertyNames': { '$ref': '#' }, 'const': true, 'enum': { 'type': 'array', 'items': true, 'minItems': 1, 'uniqueItems': true }, 'type': { 'anyOf': [ { '$ref': '#/definitions/simpleTypes' }, { 'type': 'array', 'items': { '$ref': '#/definitions/simpleTypes' }, 'minItems': 1, 'uniqueItems': true } ] }, 'format': { 'type': 'string' }, 'contentMediaType': { 'type': 'string' }, 'contentEncoding': { 'type': 'string' }, 'if': { '$ref': '#' }, 'then': { '$ref': '#' }, 'else': { '$ref': '#' }, 'allOf': { '$ref': '#/definitions/schemaArray' }, 'anyOf': { '$ref': '#/definitions/schemaArray' }, 'oneOf': { '$ref': '#/definitions/schemaArray' }, 'not': { '$ref': '#' } }, 'default': true }, 'https://json-schema.org/draft/2020-12/schema': draft202012Flat, 'https://json-schema.org/draft/2019-09/schema': draft201909Flat } }; const descriptions: { [prop: string]: string } = { id: l10n.t("A unique identifier for the schema."), $schema: l10n.t("The schema to verify this document against."), title: l10n.t("A descriptive title of the schema."), description: l10n.t("A long description of the schema. Used in hover menus and suggestions."), default: l10n.t("A default value. Used by suggestions."), multipleOf: l10n.t("A number that should cleanly divide the current value (i.e. have no remainder)."), maximum: l10n.t("The maximum numerical value, inclusive by default."), exclusiveMaximum: l10n.t("Makes the maximum property exclusive."), minimum: l10n.t("The minimum numerical value, inclusive by default."), exclusiveMinimum: l10n.t("Makes the minimum property exclusive."), maxLength: l10n.t("The maximum length of a string."), minLength: l10n.t("The minimum length of a string."), pattern: l10n.t("A regular expression to match the string against. It is not implicitly anchored."), additionalItems: l10n.t("For arrays, only when items is set as an array. If items are a schema, this schema validates items after the ones specified by the items schema. If false, additional items will cause validation to fail."), items: l10n.t("For arrays. Can either be a schema to validate every element against or an array of schemas to validate each item against in order (the first schema will validate the first element, the second schema will validate the second element, and so on."), maxItems: l10n.t("The maximum number of items that can be inside an array. Inclusive."), minItems: l10n.t("The minimum number of items that can be inside an array. Inclusive."), uniqueItems: l10n.t("If all of the items in the array must be unique. Defaults to false."), maxProperties: l10n.t("The maximum number of properties an object can have. Inclusive."), minProperties: l10n.t("The minimum number of properties an object can have. Inclusive."), required: l10n.t("An array of strings that lists the names of all properties required on this object."), additionalProperties: l10n.t("Either a schema or a boolean. If a schema, used to validate all properties not matched by 'properties', 'propertyNames', or 'patternProperties'. If false, any properties not defined by the adjacent keywords will cause this schema to fail."), definitions: l10n.t("Not used for validation. Place subschemas here that you wish to reference inline with $ref."), properties: l10n.t("A map of property names to schemas for each property."), patternProperties: l10n.t("A map of regular expressions on property names to schemas for matching properties."), dependencies: l10n.t("A map of property names to either an array of property names or a schema. An array of property names means the property named in the key depends on the properties in the array being present in the object in order to be valid. If the value is a schema, then the schema is only applied to the object if the property in the key exists on the object."), enum: l10n.t("The set of literal values that are valid."), type: l10n.t("Either a string of one of the basic schema types (number, integer, null, array, object, boolean, string) or an array of strings specifying a subset of those types."), format: l10n.t("Describes the format expected for the value. By default, not used for validation"), allOf: l10n.t("An array of schemas, all of which must match."), anyOf: l10n.t("An array of schemas, where at least one must match."), oneOf: l10n.t("An array of schemas, exactly one of which must match."), not: l10n.t("A schema which must not match."), $id: l10n.t("A unique identifier for the schema."), $ref: l10n.t("Reference a definition hosted on any location."), $comment: l10n.t("Comments from schema authors to readers or maintainers of the schema."), readOnly: l10n.t("Indicates that the value of the instance is managed exclusively by the owning authority."), examples: l10n.t("Sample JSON values associated with a particular schema, for the purpose of illustrating usage."), contains: l10n.t("An array instance is valid against \"contains\" if at least one of its elements is valid against the given schema."), propertyNames: l10n.t("If the instance is an object, this keyword validates if every property name in the instance validates against the provided schema."), const: l10n.t("An instance validates successfully against this keyword if its value is equal to the value of the keyword."), contentMediaType: l10n.t("Describes the media type of a string property."), contentEncoding: l10n.t("Describes the content encoding of a string property."), if: l10n.t("The validation outcome of the \"if\" subschema controls which of the \"then\" or \"else\" keywords are evaluated."), then: l10n.t("The \"then\" subschema is used for validation when the \"if\" subschema succeeds."), else: l10n.t("The \"else\" subschema is used for validation when the \"if\" subschema fails.") }; for (const schemaName in schemaContributions.schemas) { const schema = schemaContributions.schemas[schemaName]; for (const property in schema.properties) { let propertyObject = schema.properties[property]; if (typeof propertyObject === 'boolean') { propertyObject = schema.properties[property] = {}; } const description = descriptions[property]; if (description) { propertyObject['description'] = description; } } }vscode-json-languageservice-5.7.2/src/services/jsonCompletion.ts000066400000000000000000001106201514707600300251110ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as Parser from '../parser/jsonParser'; import * as Json from 'jsonc-parser'; import * as SchemaService from './jsonSchemaService'; import { JSONSchema, JSONSchemaRef } from '../jsonSchema'; import { JSONWorkerContribution, CompletionsCollector, JSONCompletionItem } from '../jsonContributions'; import { stringifyObject } from '../utils/json'; import { endsWith, extendedRegExp } from '../utils/strings'; import { isDefined } from '../utils/objects'; import { PromiseConstructor, ASTNode, ObjectASTNode, ArrayASTNode, PropertyASTNode, ClientCapabilities, TextDocument, CompletionItem, CompletionItemKind, CompletionList, Position, Range, TextEdit, InsertTextFormat, MarkupContent, MarkupKind } from '../jsonLanguageTypes'; import * as l10n from '@vscode/l10n'; const valueCommitCharacters = [',', '}', ']']; const propertyCommitCharacters = [':']; export class JSONCompletion { private supportsMarkdown: boolean | undefined; private supportsCommitCharacters: boolean | undefined; private labelDetailsSupport: boolean | undefined; constructor( private schemaService: SchemaService.IJSONSchemaService, private contributions: JSONWorkerContribution[] = [], private promiseConstructor: PromiseConstructor = Promise, private clientCapabilities: ClientCapabilities = {}) { } public doResolve(item: CompletionItem): PromiseLike { for (let i = this.contributions.length - 1; i >= 0; i--) { const resolveCompletion = this.contributions[i].resolveCompletion; if (resolveCompletion) { const resolver = resolveCompletion(item); if (resolver) { return resolver; } } } return this.promiseConstructor.resolve(item); } public doComplete(document: TextDocument, position: Position, doc: Parser.JSONDocument): PromiseLike { const result: CompletionList = { items: [], isIncomplete: false }; const text = document.getText(); const offset = document.offsetAt(position); let node = doc.getNodeFromOffset(offset, true); if (this.isInComment(document, node ? node.offset : 0, offset)) { return Promise.resolve(result); } if (node && (offset === node.offset + node.length) && offset > 0) { const ch = text[offset - 1]; if (node.type === 'object' && ch === '}' || node.type === 'array' && ch === ']') { // after ] or } node = node.parent; } } const currentWord = this.getCurrentWord(document, offset); let overwriteRange: Range; if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) { overwriteRange = Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length)); } else { let overwriteStart = offset - currentWord.length; if (overwriteStart > 0 && text[overwriteStart - 1] === '"') { overwriteStart--; } overwriteRange = Range.create(document.positionAt(overwriteStart), position); } const supportsCommitCharacters = false; //this.doesSupportsCommitCharacters(); disabled for now, waiting for new API: https://github.com/microsoft/vscode/issues/42544 const proposed = new Map(); const collector: CompletionsCollector = { add: (suggestion: JSONCompletionItem) => { let label = suggestion.label; const existing = proposed.get(label); if (!existing) { label = label.replace(/[\n]/g, '↵'); if (label.length > 60) { const shortendedLabel = label.substr(0, 57).trim() + '...'; if (!proposed.has(shortendedLabel)) { label = shortendedLabel; } } suggestion.textEdit = TextEdit.replace(overwriteRange, suggestion.insertText); if (supportsCommitCharacters) { suggestion.commitCharacters = suggestion.kind === CompletionItemKind.Property ? propertyCommitCharacters : valueCommitCharacters; } suggestion.label = label; proposed.set(label, suggestion); result.items.push(suggestion); } else { if (!existing.documentation) { existing.documentation = suggestion.documentation; } if (!existing.detail) { existing.detail = suggestion.detail; } if (!existing.labelDetails) { existing.labelDetails = suggestion.labelDetails; } } }, setAsIncomplete: () => { result.isIncomplete = true; }, error: (message: string) => { console.error(message); }, getNumberOfProposals: () => { return result.items.length; } }; return this.schemaService.getSchemaForResource(document.uri, doc).then((schema) => { const collectionPromises: PromiseLike[] = []; let addValue = true; let currentKey = ''; let currentProperty: PropertyASTNode | undefined = undefined; if (node) { if (node.type === 'string') { const parent = node.parent; if (parent && parent.type === 'property' && parent.keyNode === node) { addValue = !parent.valueNode; currentProperty = parent; currentKey = text.substr(node.offset + 1, node.length - 2); if (parent) { node = parent.parent; } } } } // proposals for properties if (node && node.type === 'object') { // don't suggest keys when the cursor is just before the opening curly brace if (node.offset === offset) { return result; } // don't suggest properties that are already present const properties = node.properties; properties.forEach(p => { if (!currentProperty || currentProperty !== p) { proposed.set(p.keyNode.value, CompletionItem.create('__')); } }); let separatorAfter = ''; if (addValue) { separatorAfter = this.evaluateSeparatorAfter(document, document.offsetAt(overwriteRange.end)); } if (schema) { // property proposals with schema this.getPropertyCompletions(schema, doc, node, addValue, separatorAfter, collector); } else { // property proposals without schema this.getSchemaLessPropertyCompletions(doc, node, currentKey, collector); } const location = Parser.getNodePath(node); this.contributions.forEach((contribution) => { const collectPromise = contribution.collectPropertyCompletions(document.uri, location, currentWord, addValue, separatorAfter === '', collector); if (collectPromise) { collectionPromises.push(collectPromise); } }); if ((!schema && currentWord.length > 0 && text.charAt(offset - currentWord.length - 1) !== '"')) { collector.add({ kind: CompletionItemKind.Property, label: this.getLabelForValue(currentWord), insertText: this.getInsertTextForProperty(currentWord, undefined, false, separatorAfter), insertTextFormat: InsertTextFormat.Snippet, documentation: '', }); collector.setAsIncomplete(); } } // proposals for values const types: { [type: string]: boolean } = {}; if (schema) { // value proposals with schema this.getValueCompletions(schema, doc, node, offset, document, collector, types); } else { // value proposals without schema this.getSchemaLessValueCompletions(doc, node, offset, document, collector); } if (this.contributions.length > 0) { this.getContributedValueCompletions(doc, node, offset, document, collector, collectionPromises); } return this.promiseConstructor.all(collectionPromises).then(() => { if (collector.getNumberOfProposals() === 0) { let offsetForSeparator = offset; if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) { offsetForSeparator = node.offset + node.length; } const separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator); this.addFillerValueCompletions(types, separatorAfter, collector); } return result; }); }); } private getPropertyCompletions(schema: SchemaService.ResolvedSchema, doc: Parser.JSONDocument, node: ASTNode, addValue: boolean, separatorAfter: string, collector: CompletionsCollector): void { const matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset); matchingSchemas.forEach((s) => { if (s.node === node && !s.inverted) { const schemaProperties = s.schema.properties; if (schemaProperties) { Object.keys(schemaProperties).forEach((key: string) => { const propertySchema = schemaProperties[key]; if (typeof propertySchema === 'object' && !propertySchema.deprecationMessage && !propertySchema.doNotSuggest) { const proposal: JSONCompletionItem = { kind: CompletionItemKind.Property, label: key, insertText: this.getInsertTextForProperty(key, propertySchema, addValue, separatorAfter), insertTextFormat: InsertTextFormat.Snippet, filterText: this.getFilterTextForValue(key), documentation: this.fromMarkup(propertySchema.markdownDescription) || propertySchema.description || '' }; if (propertySchema.completionDetail !== undefined) { proposal.detail = propertySchema.completionDetail; } if (propertySchema.suggestSortText !== undefined) { proposal.sortText = propertySchema.suggestSortText; } if (proposal.insertText && endsWith(proposal.insertText, `$1${separatorAfter}`)) { proposal.command = { title: 'Suggest', command: 'editor.action.triggerSuggest' }; } collector.add(proposal); } }); } const schemaPropertyNames = s.schema.propertyNames; if (typeof schemaPropertyNames === 'object' && !schemaPropertyNames.deprecationMessage && !schemaPropertyNames.doNotSuggest) { const propertyNameCompletionItem = (name: string, documentation: string | MarkupContent | undefined, detail: string | undefined, sortText: string | undefined) => { const proposal: JSONCompletionItem = { kind: CompletionItemKind.Property, label: name, insertText: this.getInsertTextForProperty(name, undefined, addValue, separatorAfter), insertTextFormat: InsertTextFormat.Snippet, filterText: this.getFilterTextForValue(name), documentation: documentation || this.fromMarkup(schemaPropertyNames.markdownDescription) || schemaPropertyNames.description || '', sortText, detail }; if (proposal.insertText && endsWith(proposal.insertText, `$1${separatorAfter}`)) { proposal.command = { title: 'Suggest', command: 'editor.action.triggerSuggest' }; } collector.add(proposal); }; if (schemaPropertyNames.enum) { for (let i = 0; i < schemaPropertyNames.enum.length; i++) { let enumDescription = undefined; if (schemaPropertyNames.markdownEnumDescriptions && i < schemaPropertyNames.markdownEnumDescriptions.length) { enumDescription = this.fromMarkup(schemaPropertyNames.markdownEnumDescriptions[i]); } else if (schemaPropertyNames.enumDescriptions && i < schemaPropertyNames.enumDescriptions.length) { enumDescription = schemaPropertyNames.enumDescriptions[i]; } const enumSortText = schemaPropertyNames.enumSortTexts?.[i]; const enumDetails = schemaPropertyNames.enumDetails?.[i]; propertyNameCompletionItem(schemaPropertyNames.enum[i], enumDescription, enumDetails, enumSortText); } } if (schemaPropertyNames.examples) { for (let i = 0; i < schemaPropertyNames.examples.length; i++) { propertyNameCompletionItem(schemaPropertyNames.examples[i], undefined, undefined, undefined); } } if (schemaPropertyNames.const) { propertyNameCompletionItem(schemaPropertyNames.const, undefined, schemaPropertyNames.completionDetail, schemaPropertyNames.suggestSortText); } } } }); } private getSchemaLessPropertyCompletions(doc: Parser.JSONDocument, node: ASTNode, currentKey: string, collector: CompletionsCollector): void { const collectCompletionsForSimilarObject = (obj: ObjectASTNode) => { obj.properties.forEach((p) => { const key = p.keyNode.value; collector.add({ kind: CompletionItemKind.Property, label: key, insertText: this.getInsertTextForValue(key, ''), insertTextFormat: InsertTextFormat.Snippet, filterText: this.getFilterTextForValue(key), documentation: '' }); }); }; if (node.parent) { if (node.parent.type === 'property') { // if the object is a property value, check the tree for other objects that hang under a property of the same name const parentKey = node.parent.keyNode.value; doc.visit(n => { if (n.type === 'property' && n !== node.parent && n.keyNode.value === parentKey && n.valueNode && n.valueNode.type === 'object') { collectCompletionsForSimilarObject(n.valueNode); } return true; }); } else if (node.parent.type === 'array') { // if the object is in an array, use all other array elements as similar objects node.parent.items.forEach(n => { if (n.type === 'object' && n !== node) { collectCompletionsForSimilarObject(n); } }); } } else if (node.type === 'object') { collector.add({ kind: CompletionItemKind.Property, label: '$schema', insertText: this.getInsertTextForProperty('$schema', undefined, true, ''), insertTextFormat: InsertTextFormat.Snippet, documentation: '', filterText: this.getFilterTextForValue("$schema") }); } } private getSchemaLessValueCompletions(doc: Parser.JSONDocument, node: ASTNode | undefined, offset: number, document: TextDocument, collector: CompletionsCollector): void { let offsetForSeparator = offset; if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) { offsetForSeparator = node.offset + node.length; node = node.parent; } if (!node) { collector.add({ kind: this.getSuggestionKind('object'), label: 'Empty object', insertText: this.getInsertTextForValue({}, ''), insertTextFormat: InsertTextFormat.Snippet, documentation: '' }); collector.add({ kind: this.getSuggestionKind('array'), label: 'Empty array', insertText: this.getInsertTextForValue([], ''), insertTextFormat: InsertTextFormat.Snippet, documentation: '' }); return; } const separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator); const collectSuggestionsForValues = (value: ASTNode) => { if (value.parent && !Parser.contains(value.parent, offset, true)) { collector.add({ kind: this.getSuggestionKind(value.type), label: this.getLabelTextForMatchingNode(value, document), insertText: this.getInsertTextForMatchingNode(value, document, separatorAfter), insertTextFormat: InsertTextFormat.Snippet, documentation: '' }); } if (value.type === 'boolean') { this.addBooleanValueCompletion(!value.value, separatorAfter, collector); } }; if (node.type === 'property') { if (offset > (node.colonOffset || 0)) { const valueNode = node.valueNode; if (valueNode && (offset > (valueNode.offset + valueNode.length) || valueNode.type === 'object' || valueNode.type === 'array')) { return; } // suggest values at the same key const parentKey = node.keyNode.value; doc.visit(n => { if (n.type === 'property' && n.keyNode.value === parentKey && n.valueNode) { collectSuggestionsForValues(n.valueNode); } return true; }); if (parentKey === '$schema' && node.parent && !node.parent.parent) { this.addDollarSchemaCompletions(separatorAfter, collector); } } } if (node.type === 'array') { if (node.parent && node.parent.type === 'property') { // suggest items of an array at the same key const parentKey = node.parent.keyNode.value; doc.visit((n) => { if (n.type === 'property' && n.keyNode.value === parentKey && n.valueNode && n.valueNode.type === 'array') { n.valueNode.items.forEach(collectSuggestionsForValues); } return true; }); } else { // suggest items in the same array node.items.forEach(collectSuggestionsForValues); } } } private getValueCompletions(schema: SchemaService.ResolvedSchema, doc: Parser.JSONDocument, node: ASTNode | undefined, offset: number, document: TextDocument, collector: CompletionsCollector, types: { [type: string]: boolean }): void { let offsetForSeparator = offset; let parentKey: string | undefined = undefined; let valueNode: ASTNode | undefined = undefined; if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) { offsetForSeparator = node.offset + node.length; valueNode = node; node = node.parent; } if (!node) { this.addSchemaValueCompletions(schema.schema, '', collector, types); return; } if ((node.type === 'property') && offset > (node.colonOffset || 0)) { const valueNode = node.valueNode; if (valueNode && offset > (valueNode.offset + valueNode.length)) { return; // we are past the value node } parentKey = node.keyNode.value; node = node.parent; } if (node && (parentKey !== undefined || node.type === 'array')) { const separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator); const matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset, valueNode); for (const s of matchingSchemas) { if (s.node === node && !s.inverted && s.schema) { if (node.type === 'array' && s.schema.items) { let c = collector; if (s.schema.uniqueItems) { const existingValues = new Set(); node.children.forEach(n => { if (n.type !== 'array' && n.type !== 'object') { existingValues.add(this.getLabelForValue(Parser.getNodeValue(n))); } }); c = { ...collector, add(suggestion: JSONCompletionItem) { if (!existingValues.has(suggestion.label)) { collector.add(suggestion); } } }; } if (Array.isArray(s.schema.items)) { const index = this.findItemAtOffset(node, document, offset); if (index < s.schema.items.length) { this.addSchemaValueCompletions(s.schema.items[index], separatorAfter, c, types); } } else { this.addSchemaValueCompletions(s.schema.items, separatorAfter, c, types); } } if (parentKey !== undefined) { let propertyMatched = false; if (s.schema.properties) { const propertySchema = s.schema.properties[parentKey]; if (propertySchema) { propertyMatched = true; this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types); } } if (s.schema.patternProperties && !propertyMatched) { for (const pattern of Object.keys(s.schema.patternProperties)) { const regex = extendedRegExp(pattern); if (regex?.test(parentKey)) { propertyMatched = true; const propertySchema = s.schema.patternProperties[pattern]; this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types); } } } if (s.schema.additionalProperties && !propertyMatched) { const propertySchema = s.schema.additionalProperties; this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types); } } } } if (parentKey === '$schema' && !node.parent) { this.addDollarSchemaCompletions(separatorAfter, collector); } if (types['boolean']) { this.addBooleanValueCompletion(true, separatorAfter, collector); this.addBooleanValueCompletion(false, separatorAfter, collector); } if (types['null']) { this.addNullValueCompletion(separatorAfter, collector); } } } private getContributedValueCompletions(doc: Parser.JSONDocument, node: ASTNode | undefined, offset: number, document: TextDocument, collector: CompletionsCollector, collectionPromises: PromiseLike[]) { if (!node) { this.contributions.forEach((contribution) => { const collectPromise = contribution.collectDefaultCompletions(document.uri, collector); if (collectPromise) { collectionPromises.push(collectPromise); } }); } else { if (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null') { node = node.parent; } if (node && (node.type === 'property') && offset > (node.colonOffset || 0)) { const parentKey = node.keyNode.value; const valueNode = node.valueNode; if ((!valueNode || offset <= (valueNode.offset + valueNode.length)) && node.parent) { const location = Parser.getNodePath(node.parent); this.contributions.forEach((contribution) => { const collectPromise = contribution.collectValueCompletions(document.uri, location, parentKey, collector); if (collectPromise) { collectionPromises.push(collectPromise); } }); } } } } private addSchemaValueCompletions(schema: JSONSchemaRef, separatorAfter: string, collector: CompletionsCollector, types: { [type: string]: boolean }): void { if (typeof schema === 'object') { this.addEnumValueCompletions(schema, separatorAfter, collector); this.addDefaultValueCompletions(schema, separatorAfter, collector); this.collectTypes(schema, types); if (Array.isArray(schema.allOf)) { schema.allOf.forEach(s => this.addSchemaValueCompletions(s, separatorAfter, collector, types)); } if (Array.isArray(schema.anyOf)) { schema.anyOf.forEach(s => this.addSchemaValueCompletions(s, separatorAfter, collector, types)); } if (Array.isArray(schema.oneOf)) { schema.oneOf.forEach(s => this.addSchemaValueCompletions(s, separatorAfter, collector, types)); } } } private addDefaultValueCompletions(schema: JSONSchema, separatorAfter: string, collector: CompletionsCollector, arrayDepth = 0): void { let hasProposals = false; if (isDefined(schema.default)) { let type = schema.type; let value = schema.default; for (let i = arrayDepth; i > 0; i--) { value = [value]; type = 'array'; } const completionItem: JSONCompletionItem = { kind: this.getSuggestionKind(type), label: this.getLabelForValue(value), insertText: this.getInsertTextForValue(value, separatorAfter), insertTextFormat: InsertTextFormat.Snippet }; if (this.doesSupportsLabelDetails()) { completionItem.labelDetails = { description: l10n.t('Default value') }; } else { completionItem.detail = l10n.t('Default value'); } collector.add(completionItem); hasProposals = true; } if (Array.isArray(schema.examples)) { schema.examples.forEach(example => { let type = schema.type; let value = example; for (let i = arrayDepth; i > 0; i--) { value = [value]; type = 'array'; } collector.add({ kind: this.getSuggestionKind(type), label: this.getLabelForValue(value), insertText: this.getInsertTextForValue(value, separatorAfter), insertTextFormat: InsertTextFormat.Snippet }); hasProposals = true; }); } if (Array.isArray(schema.defaultSnippets)) { schema.defaultSnippets.forEach(s => { let type = schema.type; let value = s.body; let label = s.label; let insertText: string; let filterText: string; if (isDefined(value)) { let type = schema.type; for (let i = arrayDepth; i > 0; i--) { value = [value]; type = 'array'; } insertText = this.getInsertTextForSnippetValue(value, separatorAfter); filterText = this.getFilterTextForSnippetValue(value); label = label || this.getLabelForSnippetValue(value); } else if (typeof s.bodyText === 'string') { let prefix = '', suffix = '', indent = ''; for (let i = arrayDepth; i > 0; i--) { prefix = prefix + indent + '[\n'; suffix = suffix + '\n' + indent + ']'; indent += '\t'; type = 'array'; } insertText = prefix + indent + s.bodyText.split('\n').join('\n' + indent) + suffix + separatorAfter; label = label || insertText, filterText = insertText.replace(/[\n]/g, ''); // remove new lines } else { return; } collector.add({ kind: this.getSuggestionKind(type), label, documentation: this.fromMarkup(s.markdownDescription) || s.description, insertText, insertTextFormat: InsertTextFormat.Snippet, filterText }); hasProposals = true; }); } if (!hasProposals && typeof schema.items === 'object' && !Array.isArray(schema.items) && arrayDepth < 5 /* beware of recursion */) { this.addDefaultValueCompletions(schema.items, separatorAfter, collector, arrayDepth + 1); } } private addEnumValueCompletions(schema: JSONSchema, separatorAfter: string, collector: CompletionsCollector): void { if (isDefined(schema.const)) { collector.add({ kind: this.getSuggestionKind(schema.type), label: this.getLabelForValue(schema.const), insertText: this.getInsertTextForValue(schema.const, separatorAfter), insertTextFormat: InsertTextFormat.Snippet, documentation: this.fromMarkup(schema.markdownDescription) || schema.description }); } if (Array.isArray(schema.enum)) { for (let i = 0, length = schema.enum.length; i < length; i++) { const enm = schema.enum[i]; let documentation: string | MarkupContent | undefined = this.fromMarkup(schema.markdownDescription) || schema.description; if (schema.markdownEnumDescriptions && i < schema.markdownEnumDescriptions.length && this.doesSupportMarkdown()) { documentation = this.fromMarkup(schema.markdownEnumDescriptions[i]); } else if (schema.enumDescriptions && i < schema.enumDescriptions.length) { documentation = schema.enumDescriptions[i]; } collector.add({ kind: this.getSuggestionKind(schema.type), label: this.getLabelForValue(enm), insertText: this.getInsertTextForValue(enm, separatorAfter), insertTextFormat: InsertTextFormat.Snippet, sortText: schema.enumSortTexts?.[i], detail: schema.enumDetails?.[i], documentation }); } } } private collectTypes(schema: JSONSchema, types: { [type: string]: boolean }) { if (Array.isArray(schema.enum) || isDefined(schema.const)) { return; } const type = schema.type; if (Array.isArray(type)) { type.forEach(t => types[t] = true); } else if (type) { types[type] = true; } } private addFillerValueCompletions(types: { [type: string]: boolean }, separatorAfter: string, collector: CompletionsCollector): void { if (types['object']) { collector.add({ kind: this.getSuggestionKind('object'), label: '{}', insertText: this.getInsertTextForGuessedValue({}, separatorAfter), insertTextFormat: InsertTextFormat.Snippet, detail: l10n.t('New object'), documentation: '' }); } if (types['array']) { collector.add({ kind: this.getSuggestionKind('array'), label: '[]', insertText: this.getInsertTextForGuessedValue([], separatorAfter), insertTextFormat: InsertTextFormat.Snippet, detail: l10n.t('New array'), documentation: '' }); } } private addBooleanValueCompletion(value: boolean, separatorAfter: string, collector: CompletionsCollector): void { collector.add({ kind: this.getSuggestionKind('boolean'), label: value ? 'true' : 'false', insertText: this.getInsertTextForValue(value, separatorAfter), insertTextFormat: InsertTextFormat.Snippet, documentation: '' }); } private addNullValueCompletion(separatorAfter: string, collector: CompletionsCollector): void { collector.add({ kind: this.getSuggestionKind('null'), label: 'null', insertText: 'null' + separatorAfter, insertTextFormat: InsertTextFormat.Snippet, documentation: '' }); } private addDollarSchemaCompletions(separatorAfter: string, collector: CompletionsCollector): void { const schemaIds = this.schemaService.getRegisteredSchemaIds(schema => schema === 'http' || schema === 'https'); schemaIds.forEach(schemaId => { if (schemaId.startsWith('https://json-schema.org/draft-')) { schemaId = schemaId + '#'; } collector.add({ kind: CompletionItemKind.Module, label: this.getLabelForValue(schemaId), filterText: this.getFilterTextForValue(schemaId), insertText: this.getInsertTextForValue(schemaId, separatorAfter), insertTextFormat: InsertTextFormat.Snippet, documentation: '' }); }); } private getLabelForValue(value: any): string { return JSON.stringify(value); } private getValueFromLabel(value: any): string { return JSON.parse(value); } private getFilterTextForValue(value: any): string { return JSON.stringify(value); } private getFilterTextForSnippetValue(value: any): string { return JSON.stringify(value).replace(/\$\{\d+:([^}]+)\}|\$\d+/g, '$1'); } private getLabelForSnippetValue(value: any): string { const label = JSON.stringify(value); return label.replace(/\$\{\d+:([^}]+)\}|\$\d+/g, '$1'); } private getInsertTextForPlainText(text: string): string { return text.replace(/[\\\$\}]/g, '\\$&'); // escape $, \ and } } private getInsertTextForValue(value: any, separatorAfter: string): string { const text = JSON.stringify(value, null, '\t'); if (text === '{}') { return '{$1}' + separatorAfter; } else if (text === '[]') { return '[$1]' + separatorAfter; } return this.getInsertTextForPlainText(text + separatorAfter); } private getInsertTextForSnippetValue(value: any, separatorAfter: string): string { const replacer = (value: any) => { if (typeof value === 'string') { if (value[0] === '^') { return value.substr(1); } } return JSON.stringify(value); }; return stringifyObject(value, '', replacer) + separatorAfter; } private getInsertTextForGuessedValue(value: any, separatorAfter: string): string { switch (typeof value) { case 'object': if (value === null) { return '${1:null}' + separatorAfter; } return this.getInsertTextForValue(value, separatorAfter); case 'string': let snippetValue = JSON.stringify(value); snippetValue = snippetValue.substr(1, snippetValue.length - 2); // remove quotes snippetValue = this.getInsertTextForPlainText(snippetValue); // escape \ and } return '"${1:' + snippetValue + '}"' + separatorAfter; case 'number': case 'boolean': return '${1:' + JSON.stringify(value) + '}' + separatorAfter; } return this.getInsertTextForValue(value, separatorAfter); } private getSuggestionKind(type: any): CompletionItemKind { if (Array.isArray(type)) { const array = type; type = array.length > 0 ? array[0] : undefined; } if (!type) { return CompletionItemKind.Value; } switch (type) { case 'string': return CompletionItemKind.Value; case 'object': return CompletionItemKind.Module; case 'property': return CompletionItemKind.Property; default: return CompletionItemKind.Value; } } private getLabelTextForMatchingNode(node: ASTNode, document: TextDocument): string { switch (node.type) { case 'array': return '[]'; case 'object': return '{}'; default: const content = document.getText().substr(node.offset, node.length); return content; } } private getInsertTextForMatchingNode(node: ASTNode, document: TextDocument, separatorAfter: string): string { switch (node.type) { case 'array': return this.getInsertTextForValue([], separatorAfter); case 'object': return this.getInsertTextForValue({}, separatorAfter); default: const content = document.getText().substr(node.offset, node.length) + separatorAfter; return this.getInsertTextForPlainText(content); } } private getInsertTextForProperty(key: string, propertySchema: JSONSchema | undefined, addValue: boolean, separatorAfter: string): string { const propertyText = this.getInsertTextForValue(key, ''); if (!addValue) { return propertyText; } const resultText = propertyText + ': '; let value; let nValueProposals = 0; if (propertySchema) { if (Array.isArray(propertySchema.defaultSnippets)) { if (propertySchema.defaultSnippets.length === 1) { const body = propertySchema.defaultSnippets[0].body; if (isDefined(body)) { value = this.getInsertTextForSnippetValue(body, ''); } } nValueProposals += propertySchema.defaultSnippets.length; } if (propertySchema.enum) { if (!value && propertySchema.enum.length === 1) { value = this.getInsertTextForGuessedValue(propertySchema.enum[0], ''); } nValueProposals += propertySchema.enum.length; } if (isDefined(propertySchema.const)) { if (!value) { value = this.getInsertTextForGuessedValue(propertySchema.const, ''); } nValueProposals++; } if (isDefined(propertySchema.default)) { if (!value) { value = this.getInsertTextForGuessedValue(propertySchema.default, ''); } nValueProposals++; } if (Array.isArray(propertySchema.examples) && propertySchema.examples.length) { if (!value) { value = this.getInsertTextForGuessedValue(propertySchema.examples[0], ''); } nValueProposals += propertySchema.examples.length; } if (nValueProposals === 0) { let type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type; if (!type) { if (propertySchema.properties) { type = 'object'; } else if (propertySchema.items) { type = 'array'; } } switch (type) { case 'boolean': value = '$1'; break; case 'string': value = '"$1"'; break; case 'object': value = '{$1}'; break; case 'array': value = '[$1]'; break; case 'number': case 'integer': value = '${1:0}'; break; case 'null': value = '${1:null}'; break; default: return propertyText; } } } if (!value || nValueProposals > 1) { value = '$1'; } return resultText + value + separatorAfter; } private getCurrentWord(document: TextDocument, offset: number) { let i = offset - 1; const text = document.getText(); while (i >= 0 && ' \t\n\r\v":{[,]}'.indexOf(text.charAt(i)) === -1) { i--; } return text.substring(i + 1, offset); } private evaluateSeparatorAfter(document: TextDocument, offset: number) { const scanner = Json.createScanner(document.getText(), true); scanner.setPosition(offset); const token = scanner.scan(); switch (token) { case Json.SyntaxKind.CommaToken: case Json.SyntaxKind.CloseBraceToken: case Json.SyntaxKind.CloseBracketToken: case Json.SyntaxKind.EOF: return ''; default: return ','; } } private findItemAtOffset(node: ArrayASTNode, document: TextDocument, offset: number) { const scanner = Json.createScanner(document.getText(), true); const children = node.items; for (let i = children.length - 1; i >= 0; i--) { const child = children[i]; if (offset > child.offset + child.length) { scanner.setPosition(child.offset + child.length); const token = scanner.scan(); if (token === Json.SyntaxKind.CommaToken && offset >= scanner.getTokenOffset() + scanner.getTokenLength()) { return i + 1; } return i; } else if (offset >= child.offset) { return i; } } return 0; } private isInComment(document: TextDocument, start: number, offset: number) { const scanner = Json.createScanner(document.getText(), false); scanner.setPosition(start); let token = scanner.scan(); while (token !== Json.SyntaxKind.EOF && (scanner.getTokenOffset() + scanner.getTokenLength() < offset)) { token = scanner.scan(); } return (token === Json.SyntaxKind.LineCommentTrivia || token === Json.SyntaxKind.BlockCommentTrivia) && scanner.getTokenOffset() <= offset; } private fromMarkup(markupString: string | undefined): MarkupContent | string | undefined { if (markupString && this.doesSupportMarkdown()) { return { kind: MarkupKind.Markdown, value: markupString }; } return undefined; } private doesSupportMarkdown() { if (!isDefined(this.supportsMarkdown)) { const documentationFormat = this.clientCapabilities.textDocument?.completion?.completionItem?.documentationFormat; this.supportsMarkdown = Array.isArray(documentationFormat) && documentationFormat.indexOf(MarkupKind.Markdown) !== -1; } return this.supportsMarkdown; } private doesSupportsCommitCharacters() { if (!isDefined(this.supportsCommitCharacters)) { this.labelDetailsSupport = this.clientCapabilities.textDocument?.completion?.completionItem?.commitCharactersSupport; } return this.supportsCommitCharacters; } private doesSupportsLabelDetails() { if (!isDefined(this.labelDetailsSupport)) { this.labelDetailsSupport = this.clientCapabilities.textDocument?.completion?.completionItem?.labelDetailsSupport; } return this.labelDetailsSupport; } } vscode-json-languageservice-5.7.2/src/services/jsonDocumentSymbols.ts000066400000000000000000000234651514707600300261410ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as Parser from '../parser/jsonParser'; import * as Strings from '../utils/strings'; import { colorFromHex } from '../utils/colors'; import * as l10n from '@vscode/l10n'; import { TextDocument, ColorInformation, ColorPresentation, Color, ASTNode, PropertyASTNode, DocumentSymbolsContext, Range, TextEdit, SymbolInformation, SymbolKind, DocumentSymbol, Location } from "../jsonLanguageTypes"; import { IJSONSchemaService } from "./jsonSchemaService"; export class JSONDocumentSymbols { constructor(private schemaService: IJSONSchemaService) { } public findDocumentSymbols(document: TextDocument, doc: Parser.JSONDocument, context: DocumentSymbolsContext = { resultLimit: Number.MAX_VALUE }): SymbolInformation[] { const root = doc.root; if (!root) { return []; } let limit = context.resultLimit || Number.MAX_VALUE; // special handling for key bindings const resourceString = document.uri; if ((resourceString === 'vscode://defaultsettings/keybindings.json') || Strings.endsWith(resourceString.toLowerCase(), '/user/keybindings.json')) { if (root.type === 'array') { const result: SymbolInformation[] = []; for (const item of root.items) { if (item.type === 'object') { for (const property of item.properties) { if (property.keyNode.value === 'key' && property.valueNode) { const location = Location.create(document.uri, getRange(document, item)); result.push({ name: getName(property.valueNode), kind: SymbolKind.Function, location: location }); limit--; if (limit <= 0) { if (context && context.onResultLimitExceeded) { context.onResultLimitExceeded(resourceString); } return result; } } } } } return result; } } const toVisit: { node: ASTNode, containerName: string }[] = [ { node: root, containerName: '' } ]; let nextToVisit = 0; let limitExceeded = false; const result: SymbolInformation[] = []; const collectOutlineEntries = (node: ASTNode, containerName: string): void => { if (node.type === 'array') { node.items.forEach(node => { if (node) { toVisit.push({ node, containerName }); } }); } else if (node.type === 'object') { node.properties.forEach((property: PropertyASTNode) => { const valueNode = property.valueNode; if (valueNode) { if (limit > 0) { limit--; const location = Location.create(document.uri, getRange(document, property)); const childContainerName = containerName ? containerName + '.' + property.keyNode.value : property.keyNode.value; result.push({ name: this.getKeyLabel(property), kind: this.getSymbolKind(valueNode.type), location: location, containerName: containerName }); toVisit.push({ node: valueNode, containerName: childContainerName }); } else { limitExceeded = true; } } }); } }; // breath first traversal while (nextToVisit < toVisit.length) { const next = toVisit[nextToVisit++]; collectOutlineEntries(next.node, next.containerName); } if (limitExceeded && context && context.onResultLimitExceeded) { context.onResultLimitExceeded(resourceString); } return result; } public findDocumentSymbols2(document: TextDocument, doc: Parser.JSONDocument, context: DocumentSymbolsContext = { resultLimit: Number.MAX_VALUE }): DocumentSymbol[] { const root = doc.root; if (!root) { return []; } let limit = context.resultLimit || Number.MAX_VALUE; // special handling for key bindings const resourceString = document.uri; if ((resourceString === 'vscode://defaultsettings/keybindings.json') || Strings.endsWith(resourceString.toLowerCase(), '/user/keybindings.json')) { if (root.type === 'array') { const result: DocumentSymbol[] = []; for (const item of root.items) { if (item.type === 'object') { for (const property of item.properties) { if (property.keyNode.value === 'key' && property.valueNode) { const range = getRange(document, item); const selectionRange = getRange(document, property.keyNode); result.push({ name: getName(property.valueNode), kind: SymbolKind.Function, range, selectionRange }); limit--; if (limit <= 0) { if (context && context.onResultLimitExceeded) { context.onResultLimitExceeded(resourceString); } return result; } } } } } return result; } } const result: DocumentSymbol[] = []; const toVisit: { node: ASTNode, result: DocumentSymbol[] }[] = [ { node: root, result } ]; let nextToVisit = 0; let limitExceeded = false; const collectOutlineEntries = (node: ASTNode, result: DocumentSymbol[]) => { if (node.type === 'array') { node.items.forEach((node, index) => { if (node) { if (limit > 0) { limit--; const range = getRange(document, node); const selectionRange = range; const name = String(index); const symbol = { name, kind: this.getSymbolKind(node.type), range, selectionRange, children: [] }; result.push(symbol); toVisit.push({ result: symbol.children, node }); } else { limitExceeded = true; } } }); } else if (node.type === 'object') { node.properties.forEach((property: PropertyASTNode) => { const valueNode = property.valueNode; if (valueNode) { if (limit > 0) { limit--; const range = getRange(document, property); const selectionRange = getRange(document, property.keyNode); const children: DocumentSymbol[] = []; const symbol: DocumentSymbol = { name: this.getKeyLabel(property), kind: this.getSymbolKind(valueNode.type), range, selectionRange, children, detail: this.getDetail(valueNode) }; result.push(symbol); toVisit.push({ result: children, node: valueNode }); } else { limitExceeded = true; } } }); } }; // breath first traversal while (nextToVisit < toVisit.length) { const next = toVisit[nextToVisit++]; collectOutlineEntries(next.node, next.result); } if (limitExceeded && context && context.onResultLimitExceeded) { context.onResultLimitExceeded(resourceString); } return result; } private getSymbolKind(nodeType: string): SymbolKind { switch (nodeType) { case 'object': return SymbolKind.Module; case 'string': return SymbolKind.String; case 'number': return SymbolKind.Number; case 'array': return SymbolKind.Array; case 'boolean': return SymbolKind.Boolean; default: // 'null' return SymbolKind.Variable; } } private getKeyLabel(property: PropertyASTNode) { let name = property.keyNode.value; if (name) { name = name.replace(/[\n]/g, '↵'); } if (name && name.trim()) { return name; } return `"${name}"`; } private getDetail(node: ASTNode | undefined) { if (!node) { return undefined; } if (node.type === 'boolean' || node.type === 'number' || node.type === 'null' || node.type === 'string') { return String(node.value); } else { if (node.type === 'array') { return node.children.length ? undefined : '[]'; } else if (node.type === 'object') { return node.children.length ? undefined : '{}'; } } return undefined; } public findDocumentColors(document: TextDocument, doc: Parser.JSONDocument, context?: DocumentSymbolsContext): PromiseLike { return this.schemaService.getSchemaForResource(document.uri, doc).then(schema => { const result: ColorInformation[] = []; if (schema) { let limit = context && typeof context.resultLimit === 'number' ? context.resultLimit : Number.MAX_VALUE; const matchingSchemas = doc.getMatchingSchemas(schema.schema); const visitedNode: { [nodeId: string]: boolean } = {}; for (const s of matchingSchemas) { if (!s.inverted && s.schema && (s.schema.format === 'color' || s.schema.format === 'color-hex') && s.node && s.node.type === 'string') { const nodeId = String(s.node.offset); if (!visitedNode[nodeId]) { const color = colorFromHex(Parser.getNodeValue(s.node)); if (color) { const range = getRange(document, s.node); result.push({ color, range }); } visitedNode[nodeId] = true; limit--; if (limit <= 0) { if (context && context.onResultLimitExceeded) { context.onResultLimitExceeded(document.uri); } return result; } } } } } return result; }); } public getColorPresentations(document: TextDocument, doc: Parser.JSONDocument, color: Color, range: Range): ColorPresentation[] { const result: ColorPresentation[] = []; const red256 = Math.round(color.red * 255), green256 = Math.round(color.green * 255), blue256 = Math.round(color.blue * 255); function toTwoDigitHex(n: number): string { const r = n.toString(16); return r.length !== 2 ? '0' + r : r; } let label; if (color.alpha === 1) { label = `#${toTwoDigitHex(red256)}${toTwoDigitHex(green256)}${toTwoDigitHex(blue256)}`; } else { label = `#${toTwoDigitHex(red256)}${toTwoDigitHex(green256)}${toTwoDigitHex(blue256)}${toTwoDigitHex(Math.round(color.alpha * 255))}`; } result.push({ label: label, textEdit: TextEdit.replace(range, JSON.stringify(label)) }); return result; } } function getRange(document: TextDocument, node: ASTNode) { return Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length)); } function getName(node: ASTNode) { return Parser.getNodeValue(node) || l10n.t(''); } vscode-json-languageservice-5.7.2/src/services/jsonFolding.ts000066400000000000000000000101101514707600300243530ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { createScanner, SyntaxKind, ScanError } from 'jsonc-parser'; import { TextDocument, FoldingRangeKind, FoldingRange, FoldingRangesContext, Position } from '../jsonLanguageTypes'; export function getFoldingRanges(document: TextDocument, context?: FoldingRangesContext): FoldingRange[] { const ranges: FoldingRange[] = []; const nestingLevels: number[] = []; const stack: FoldingRange[] = []; let prevStart = -1; const scanner = createScanner(document.getText(), false); let token = scanner.scan(); function addRange(range: FoldingRange) { ranges.push(range); nestingLevels.push(stack.length); } while (token !== SyntaxKind.EOF) { switch (token) { case SyntaxKind.OpenBraceToken: case SyntaxKind.OpenBracketToken: { const startLine = document.positionAt(scanner.getTokenOffset()).line; const range = { startLine, endLine: startLine, kind: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' }; stack.push(range); break; } case SyntaxKind.CloseBraceToken: case SyntaxKind.CloseBracketToken: { const kind = token === SyntaxKind.CloseBraceToken ? 'object' : 'array'; if (stack.length > 0 && stack[stack.length - 1].kind === kind) { const range = stack.pop(); const line = document.positionAt(scanner.getTokenOffset()).line; if (range && line > range.startLine + 1 && prevStart !== range.startLine) { range.endLine = line - 1; addRange(range); prevStart = range.startLine; } } break; } case SyntaxKind.BlockCommentTrivia: { const startLine = document.positionAt(scanner.getTokenOffset()).line; const endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line; if (scanner.getTokenError() === ScanError.UnexpectedEndOfComment && startLine + 1 < document.lineCount) { scanner.setPosition(document.offsetAt(Position.create(startLine + 1, 0))); } else { if (startLine < endLine) { addRange({ startLine, endLine, kind: FoldingRangeKind.Comment }); prevStart = startLine; } } break; } case SyntaxKind.LineCommentTrivia: { const text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength()); const m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/); if (m) { const line = document.positionAt(scanner.getTokenOffset()).line; if (m[1]) { // start pattern match const range = { startLine: line, endLine: line, kind: FoldingRangeKind.Region }; stack.push(range); } else { let i = stack.length - 1; while (i >= 0 && stack[i].kind !== FoldingRangeKind.Region) { i--; } if (i >= 0) { const range = stack[i]; stack.length = i; if (line > range.startLine && prevStart !== range.startLine) { range.endLine = line; addRange(range); prevStart = range.startLine; } } } } break; } } token = scanner.scan(); } const rangeLimit = context && context.rangeLimit; if (typeof rangeLimit !== 'number' || ranges.length <= rangeLimit) { return ranges; } if (context && context.onRangeLimitExceeded) { context.onRangeLimitExceeded(document.uri); } const counts: number[] = []; for (let level of nestingLevels) { if (level < 30) { counts[level] = (counts[level] || 0) + 1; } } let entries = 0; let maxLevel = 0; for (let i = 0; i < counts.length; i++) { const n = counts[i]; if (n) { if (n + entries > rangeLimit) { maxLevel = i; break; } entries += n; } } const result = []; for (let i = 0; i < ranges.length; i++) { const level = nestingLevels[i]; if (typeof level === 'number') { if (level < maxLevel || (level === maxLevel && entries++ < rangeLimit)) { result.push(ranges[i]); } } } return result; }vscode-json-languageservice-5.7.2/src/services/jsonHover.ts000066400000000000000000000116361514707600300240720ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as Parser from '../parser/jsonParser'; import * as SchemaService from './jsonSchemaService'; import { JSONWorkerContribution } from '../jsonContributions'; import { TextDocument, PromiseConstructor, Position, Range, Hover, MarkedString } from '../jsonLanguageTypes'; export class JSONHover { private schemaService: SchemaService.IJSONSchemaService; private contributions: JSONWorkerContribution[]; private promise: PromiseConstructor; constructor(schemaService: SchemaService.IJSONSchemaService, contributions: JSONWorkerContribution[] = [], promiseConstructor: PromiseConstructor) { this.schemaService = schemaService; this.contributions = contributions; this.promise = promiseConstructor || Promise; } public doHover(document: TextDocument, position: Position, doc: Parser.JSONDocument): PromiseLike { const offset = document.offsetAt(position); let node = doc.getNodeFromOffset(offset); if (!node || (node.type === 'object' || node.type === 'array') && offset > node.offset + 1 && offset < node.offset + node.length - 1) { return this.promise.resolve(null); } const hoverRangeNode = node; // use the property description when hovering over an object key if (node.type === 'string') { const parent = node.parent; if (parent && parent.type === 'property' && parent.keyNode === node) { node = parent.valueNode; if (!node) { return this.promise.resolve(null); } } } const hoverRange = Range.create(document.positionAt(hoverRangeNode.offset), document.positionAt(hoverRangeNode.offset + hoverRangeNode.length)); const createHover = (contents: MarkedString[]) => { const result: Hover = { contents: contents, range: hoverRange }; return result; }; const location = Parser.getNodePath(node); for (let i = this.contributions.length - 1; i >= 0; i--) { const contribution = this.contributions[i]; const promise = contribution.getInfoContribution(document.uri, location); if (promise) { return promise.then(htmlContent => createHover(htmlContent)); } } return this.schemaService.getSchemaForResource(document.uri, doc).then((schema) => { if (!schema) { return null } let title: string | undefined = undefined; let markdownDescription: string | undefined = undefined; let markdownEnumValueDescription: string | undefined = undefined, enumValue: string | undefined = undefined; const matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset).filter((s) => s.node === node && !s.inverted).map((s) => s.schema); for (const schema of matchingSchemas) { title = title || schema.title; markdownDescription = markdownDescription || schema.markdownDescription || toMarkdown(schema.description); if (schema.enum) { const idx = schema.enum.indexOf(Parser.getNodeValue(node)); if (schema.markdownEnumDescriptions) { markdownEnumValueDescription = schema.markdownEnumDescriptions[idx]; } else if (schema.enumDescriptions) { markdownEnumValueDescription = toMarkdown(schema.enumDescriptions[idx]); } if (markdownEnumValueDescription) { enumValue = schema.enum[idx]; if (typeof enumValue !== 'string') { enumValue = JSON.stringify(enumValue); } } } } let result = ''; if (title) { result = toMarkdown(title); } if (markdownDescription) { if (result.length > 0) { result += "\n\n"; } result += markdownDescription; } if (markdownEnumValueDescription) { if (result.length > 0) { result += "\n\n"; } result += `\`${toMarkdownCodeBlock(enumValue!)}\`: ${markdownEnumValueDescription}`; } return createHover([result]); }); } } function toMarkdown(plain: string): string; function toMarkdown(plain: string | undefined): string | undefined; function toMarkdown(plain: string | undefined): string | undefined { if (plain) { return plain .trim() .replace(/[\\`*_{}[\]()<>#+\-.!]/g, '\\$&') // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash .replace(/(^ +)/mg, (_match, g1) => ' '.repeat(g1.length)) // escape leading spaces on each line .replace(/( {2,})/g, (_match, g1) => ' ' + ' '.repeat(g1.length - 1)) // escape consecutive spaces .replace(/(\t+)/g, (_match, g1) => ' '.repeat(g1.length * 4)) // escape tabs .replace(/\n/g, '\\\n'); // escape new lines } return undefined; } function toMarkdownCodeBlock(content: string) { // see https://daringfireball.net/projects/markdown/syntax#precode if (content.indexOf('`') !== -1) { return '`` ' + content + ' ``'; } return content; } vscode-json-languageservice-5.7.2/src/services/jsonLinks.ts000066400000000000000000000051241514707600300240620ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { DocumentLink } from 'vscode-languageserver-types'; import { TextDocument, ASTNode, PropertyASTNode, Range } from '../jsonLanguageTypes'; import { JSONDocument } from '../parser/jsonParser'; export function findLinks(document: TextDocument, doc: JSONDocument): PromiseLike { const links: DocumentLink[] = []; doc.visit(node => { if (node.type === "property" && node.keyNode.value === "$ref" && node.valueNode?.type === 'string') { const path = node.valueNode.value; const targetNode = findTargetNode(doc, path); if (targetNode) { const targetPos = document.positionAt(targetNode.offset); links.push({ target: `${document.uri}#${targetPos.line + 1},${targetPos.character + 1}`, range: createRange(document, node.valueNode) }); } } return true; }); return Promise.resolve(links); } function createRange(document: TextDocument, node: ASTNode): Range { return Range.create(document.positionAt(node.offset + 1), document.positionAt(node.offset + node.length - 1)); } function findTargetNode(doc: JSONDocument, path: string): ASTNode | null { const tokens = parseJSONPointer(path); if (!tokens) { return null; } return findNode(tokens, doc.root); } function findNode(pointer: string[], node: ASTNode | null | undefined): ASTNode | null { if (!node) { return null; } if (pointer.length === 0) { return node; } const token: string = pointer.shift() as string; if (node && node.type === 'object') { const propertyNode: PropertyASTNode | undefined = node.properties.find((propertyNode) => propertyNode.keyNode.value === token); if (!propertyNode) { return null; } return findNode(pointer, propertyNode.valueNode); } else if (node && node.type === 'array') { if (token.match(/^(0|[1-9][0-9]*)$/)) { const index = Number.parseInt(token); const arrayItem = node.items[index]; if (!arrayItem) { return null; } return findNode(pointer, arrayItem); } } return null; } function parseJSONPointer(path: string): string[] | null { if (path === "#") { return []; } if (path[0] !== '#' || path[1] !== '/') { return null; } return path.substring(2).split(/\//).map(unescape); } function unescape(str: string): string { return str.replace(/~1/g, '/').replace(/~0/g, '~'); } vscode-json-languageservice-5.7.2/src/services/jsonSchemaService.ts000066400000000000000000000625601514707600300255320ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as Json from 'jsonc-parser'; import { JSONSchema, JSONSchemaMap, JSONSchemaRef } from '../jsonSchema'; import { URI } from 'vscode-uri'; import * as Strings from '../utils/strings'; import { asSchema, getSchemaDraftFromId, JSONDocument, normalizeId } from '../parser/jsonParser'; import { SchemaRequestService, WorkspaceContextService, PromiseConstructor, MatchingSchema, TextDocument, SchemaConfiguration, SchemaDraft, ErrorCode } from '../jsonLanguageTypes'; import * as l10n from '@vscode/l10n'; import { createRegex } from '../utils/glob'; import { isObject, isString } from '../utils/objects'; import { DiagnosticRelatedInformation, Range } from 'vscode-languageserver-types'; export interface IJSONSchemaService { /** * Registers a schema file in the current workspace to be applicable to files that match the pattern */ registerExternalSchema(config: SchemaConfiguration): ISchemaHandle; /** * Clears all cached schema files */ clearExternalSchemas(): void; /** * Registers contributed schemas */ setSchemaContributions(schemaContributions: ISchemaContributions): void; /** * Looks up the appropriate schema for the given URI */ getSchemaForResource(resource: string, document?: JSONDocument): PromiseLike; /** * Returns all registered schema ids */ getRegisteredSchemaIds(filter?: (scheme: string) => boolean): string[]; } export interface SchemaAssociation { pattern: string[]; uris: string[]; folderUri?: string; } export interface ISchemaContributions { schemas?: { [id: string]: JSONSchema }; schemaAssociations?: SchemaAssociation[]; } export interface ISchemaHandle { /** * The schema id */ uri: string; /** * The schema from the file, with potential $ref references */ getUnresolvedSchema(): PromiseLike; /** * The schema from the file, with references resolved */ getResolvedSchema(): PromiseLike; } const BANG = '!'; const PATH_SEP = '/'; interface IGlobWrapper { regexp: RegExp; include: boolean; } class FilePatternAssociation { private readonly globWrappers: IGlobWrapper[]; constructor(pattern: string[], private readonly folderUri: string | undefined, private readonly uris: string[]) { this.globWrappers = []; try { for (let patternString of pattern) { const include = patternString[0] !== BANG; if (!include) { patternString = patternString.substring(1); } if (patternString.length > 0) { if (patternString[0] === PATH_SEP) { patternString = patternString.substring(1); } this.globWrappers.push({ regexp: createRegex('**/' + patternString, { extended: true, globstar: true }), include: include, }); } }; if (folderUri) { folderUri = normalizeResourceForMatching(folderUri); if (!folderUri.endsWith('/')) { folderUri = folderUri + '/'; } this.folderUri = folderUri; } } catch (e) { this.globWrappers.length = 0; this.uris = []; } } public matchesPattern(fileName: string): boolean { if (this.folderUri && !fileName.startsWith(this.folderUri)) { return false; } let match = false; for (const { regexp, include } of this.globWrappers) { if (regexp.test(fileName)) { match = include; } } return match; } public getURIs() { return this.uris; } } type SchemaDependencies = Set; class SchemaHandle implements ISchemaHandle { public readonly uri: string; public readonly dependencies: SchemaDependencies; public anchors: Map | undefined; private resolvedSchema: PromiseLike | undefined; private unresolvedSchema: PromiseLike | undefined; private readonly service: JSONSchemaService; constructor(service: JSONSchemaService, uri: string, unresolvedSchemaContent?: JSONSchema) { this.service = service; this.uri = uri; this.dependencies = new Set(); this.anchors = undefined; if (unresolvedSchemaContent) { this.unresolvedSchema = this.service.promise.resolve(new UnresolvedSchema(unresolvedSchemaContent)); } } public getUnresolvedSchema(): PromiseLike { if (!this.unresolvedSchema) { this.unresolvedSchema = this.service.loadSchema(this.uri); } return this.unresolvedSchema; } public getResolvedSchema(): PromiseLike { if (!this.resolvedSchema) { this.resolvedSchema = this.getUnresolvedSchema().then(unresolved => { return this.service.resolveSchemaContent(unresolved, this); }); } return this.resolvedSchema; } public clearSchema(): boolean { const hasChanges = !!this.unresolvedSchema; this.resolvedSchema = undefined; this.unresolvedSchema = undefined; this.dependencies.clear(); this.anchors = undefined; return hasChanges; } } export class UnresolvedSchema { public readonly schema: JSONSchema; public readonly errors: SchemaDiagnostic[]; constructor(schema: JSONSchema, errors: SchemaDiagnostic[] = []) { this.schema = schema; this.errors = errors; } } export type SchemaDiagnostic = { readonly message: string; readonly code: ErrorCode; relatedInformation?: DiagnosticRelatedInformation[] } function toDiagnostic(message: string, code: ErrorCode, relatedURL?: string): SchemaDiagnostic { const relatedInformation: DiagnosticRelatedInformation[] | undefined = relatedURL ? [{ location: { uri: relatedURL, range: Range.create(0, 0, 0, 0) }, message }] : undefined; return { message, code, relatedInformation }; } export class ResolvedSchema { public readonly schema: JSONSchema; public readonly errors: SchemaDiagnostic[]; public readonly warnings: SchemaDiagnostic[]; public readonly schemaDraft: SchemaDraft | undefined; constructor(schema: JSONSchema, errors: SchemaDiagnostic[] = [], warnings: SchemaDiagnostic[] = [], schemaDraft: SchemaDraft | undefined) { this.schema = schema; this.errors = errors; this.warnings = warnings; this.schemaDraft = schemaDraft; } public getSection(path: string[]): JSONSchema | undefined { const schemaRef = this.getSectionRecursive(path, this.schema); if (schemaRef) { return asSchema(schemaRef); } return undefined; } private getSectionRecursive(path: string[], schema: JSONSchemaRef): JSONSchemaRef | undefined { if (!schema || typeof schema === 'boolean' || path.length === 0) { return schema; } const next = path.shift()!; if (schema.properties && typeof schema.properties[next]) { return this.getSectionRecursive(path, schema.properties[next]); } else if (schema.patternProperties) { for (const pattern of Object.keys(schema.patternProperties)) { const regex = Strings.extendedRegExp(pattern); if (regex?.test(next)) { return this.getSectionRecursive(path, schema.patternProperties[pattern]); } } } else if (typeof schema.additionalProperties === 'object') { return this.getSectionRecursive(path, schema.additionalProperties); } else if (next.match('[0-9]+')) { if (Array.isArray(schema.items)) { const index = parseInt(next, 10); if (!isNaN(index) && schema.items[index]) { return this.getSectionRecursive(path, schema.items[index]); } } else if (schema.items) { return this.getSectionRecursive(path, schema.items); } } return undefined; } } export class JSONSchemaService implements IJSONSchemaService { private contributionSchemas: { [id: string]: SchemaHandle }; private contributionAssociations: FilePatternAssociation[]; private schemasById: { [id: string]: SchemaHandle }; private filePatternAssociations: FilePatternAssociation[]; private registeredSchemasIds: { [id: string]: boolean }; private contextService: WorkspaceContextService | undefined; private callOnDispose: Function[]; private requestService: SchemaRequestService | undefined; private promiseConstructor: PromiseConstructor; private cachedSchemaForResource: { resource: string; resolvedSchema: PromiseLike } | undefined; constructor(requestService?: SchemaRequestService, contextService?: WorkspaceContextService, promiseConstructor?: PromiseConstructor) { this.contextService = contextService; this.requestService = requestService; this.promiseConstructor = promiseConstructor || Promise; this.callOnDispose = []; this.contributionSchemas = {}; this.contributionAssociations = []; this.schemasById = {}; this.filePatternAssociations = []; this.registeredSchemasIds = {}; } public getRegisteredSchemaIds(filter?: (scheme: string) => boolean): string[] { return Object.keys(this.registeredSchemasIds).filter(id => { const scheme = URI.parse(id).scheme; return scheme !== 'schemaservice' && (!filter || filter(scheme)); }); } public get promise() { return this.promiseConstructor; } public dispose(): void { while (this.callOnDispose.length > 0) { this.callOnDispose.pop()!(); } } public onResourceChange(uri: string): boolean { // always clear this local cache when a resource changes this.cachedSchemaForResource = undefined; let hasChanges = false; uri = normalizeId(uri); const toWalk = [uri]; const all: (SchemaHandle | undefined)[] = Object.keys(this.schemasById).map(key => this.schemasById[key]); while (toWalk.length) { const curr = toWalk.pop()!; for (let i = 0; i < all.length; i++) { const handle = all[i]; if (handle && (handle.uri === curr || handle.dependencies.has(curr))) { if (handle.uri !== curr) { toWalk.push(handle.uri); } if (handle.clearSchema()) { hasChanges = true; } all[i] = undefined; } } } return hasChanges; } public setSchemaContributions(schemaContributions: ISchemaContributions): void { if (schemaContributions.schemas) { const schemas = schemaContributions.schemas; for (const id in schemas) { const normalizedId = normalizeId(id); this.contributionSchemas[normalizedId] = this.addSchemaHandle(normalizedId, schemas[id]); } } if (Array.isArray(schemaContributions.schemaAssociations)) { const schemaAssociations = schemaContributions.schemaAssociations; for (let schemaAssociation of schemaAssociations) { const uris = schemaAssociation.uris.map(normalizeId); const association = this.addFilePatternAssociation(schemaAssociation.pattern, schemaAssociation.folderUri, uris); this.contributionAssociations.push(association); } } } private addSchemaHandle(id: string, unresolvedSchemaContent?: JSONSchema): SchemaHandle { const schemaHandle = new SchemaHandle(this, id, unresolvedSchemaContent); this.schemasById[id] = schemaHandle; return schemaHandle; } private getOrAddSchemaHandle(id: string, unresolvedSchemaContent?: JSONSchema): SchemaHandle { return this.schemasById[id] || this.addSchemaHandle(id, unresolvedSchemaContent); } private addFilePatternAssociation(pattern: string[], folderUri: string | undefined, uris: string[]): FilePatternAssociation { const fpa = new FilePatternAssociation(pattern, folderUri, uris); this.filePatternAssociations.push(fpa); return fpa; } public registerExternalSchema(config: SchemaConfiguration): ISchemaHandle { const id = normalizeId(config.uri); this.registeredSchemasIds[id] = true; this.cachedSchemaForResource = undefined; if (config.fileMatch && config.fileMatch.length) { this.addFilePatternAssociation(config.fileMatch, config.folderUri, [id]); } return config.schema ? this.addSchemaHandle(id, config.schema) : this.getOrAddSchemaHandle(id); } public clearExternalSchemas(): void { this.schemasById = {}; this.filePatternAssociations = []; this.registeredSchemasIds = {}; this.cachedSchemaForResource = undefined; for (const id in this.contributionSchemas) { this.schemasById[id] = this.contributionSchemas[id]; this.registeredSchemasIds[id] = true; } for (const contributionAssociation of this.contributionAssociations) { this.filePatternAssociations.push(contributionAssociation); } } public getResolvedSchema(schemaId: string): PromiseLike { const id = normalizeId(schemaId); const schemaHandle = this.schemasById[id]; if (schemaHandle) { return schemaHandle.getResolvedSchema(); } return this.promise.resolve(undefined); } public loadSchema(url: string): PromiseLike { if (!this.requestService) { const errorMessage = l10n.t('Unable to load schema from \'{0}\'. No schema request service available', toDisplayString(url)); return this.promise.resolve(new UnresolvedSchema({}, [toDiagnostic(errorMessage, ErrorCode.SchemaResolveError, url)])); } return this.requestService(url).then( content => { if (!content) { const errorMessage = l10n.t('Unable to load schema from \'{0}\': No content.', toDisplayString(url)); return new UnresolvedSchema({}, [toDiagnostic(errorMessage, ErrorCode.SchemaResolveError, url)]); } const errors = []; if (content.charCodeAt(0) === 65279) { errors.push(toDiagnostic(l10n.t('Problem reading content from \'{0}\': UTF-8 with BOM detected, only UTF 8 is allowed.', toDisplayString(url)), ErrorCode.SchemaResolveError, url)); content = content.trimStart(); } let schemaContent: JSONSchema = {}; const jsonErrors: Json.ParseError[] = []; schemaContent = Json.parse(content, jsonErrors); if (jsonErrors.length) { errors.push(toDiagnostic(l10n.t('Unable to parse content from \'{0}\': Parse error at offset {1}.', toDisplayString(url), jsonErrors[0].offset), ErrorCode.SchemaResolveError, url)); } return new UnresolvedSchema(schemaContent, errors); }, (error: any) => { let { message, code } = error; if (typeof message !== 'string') { let errorMessage = error.toString() as string; const errorSplit = error.toString().split('Error: '); if (errorSplit.length > 1) { // more concise error message, URL and context are attached by caller anyways errorMessage = errorSplit[1]; } if (Strings.endsWith(errorMessage, '.')) { errorMessage = errorMessage.substr(0, errorMessage.length - 1); } message = errorMessage; } let errorCode = ErrorCode.SchemaResolveError; if (typeof code === 'number' && code < 0x10000) { errorCode += code; } const errorMessage = l10n.t('Unable to load schema from \'{0}\': {1}.', toDisplayString(url), message); return new UnresolvedSchema({}, [toDiagnostic(errorMessage, errorCode, url)]); } ); } public resolveSchemaContent(schemaToResolve: UnresolvedSchema, handle: SchemaHandle): PromiseLike { const resolveErrors: SchemaDiagnostic[] = schemaToResolve.errors.slice(0); const schema = schemaToResolve.schema; const schemaDraft = schema.$schema ? getSchemaDraftFromId(schema.$schema) : undefined; if (schemaDraft === SchemaDraft.v3) { return this.promise.resolve(new ResolvedSchema({}, [toDiagnostic(l10n.t("Draft-03 schemas are not supported."), ErrorCode.SchemaUnsupportedFeature)], [], schemaDraft)); } let usesUnsupportedFeatures = new Set(); const contextService = this.contextService; const findSectionByJSONPointer = (schema: JSONSchema, path: string): any => { path = decodeURIComponent(path); let current: any = schema; if (path[0] === '/') { path = path.substring(1); } path.split('/').some((part) => { part = part.replace(/~1/g, '/').replace(/~0/g, '~'); current = current[part]; return !current; }); return current; }; const findSchemaById = (schema: JSONSchema, handle: SchemaHandle, id: string) => { if (!handle.anchors) { handle.anchors = collectAnchors(schema); } return handle.anchors.get(id); }; const merge = (target: JSONSchema, section: any): void => { for (const key in section) { if (section.hasOwnProperty(key) && key !== 'id' && key !== '$id') { (target)[key] = section[key]; } } }; const mergeRef = (target: JSONSchema, sourceRoot: JSONSchema, sourceHandle: SchemaHandle, refSegment: string | undefined): void => { let section; if (refSegment === undefined || refSegment.length === 0) { section = sourceRoot; } else if (refSegment.charAt(0) === '/') { // A $ref to a JSON Pointer (i.e #/definitions/foo) section = findSectionByJSONPointer(sourceRoot, refSegment); } else { // A $ref to a sub-schema with an $id (i.e #hello) section = findSchemaById(sourceRoot, sourceHandle, refSegment); } if (section) { merge(target, section); } else { const message = l10n.t('$ref \'{0}\' in \'{1}\' can not be resolved.', refSegment || '', sourceHandle.uri) resolveErrors.push(toDiagnostic(message, ErrorCode.SchemaResolveError)); } }; const resolveExternalLink = (node: JSONSchema, uri: string, refSegment: string | undefined, parentHandle: SchemaHandle): PromiseLike => { if (contextService && !/^[A-Za-z][A-Za-z0-9+\-.+]*:\/.*/.test(uri)) { uri = contextService.resolveRelativePath(uri, parentHandle.uri); } uri = normalizeId(uri); const referencedHandle = this.getOrAddSchemaHandle(uri); return referencedHandle.getUnresolvedSchema().then(unresolvedSchema => { parentHandle.dependencies.add(uri); if (unresolvedSchema.errors.length) { const error = unresolvedSchema.errors[0]; const loc = refSegment ? uri + '#' + refSegment : uri; const errorMessage = refSegment? l10n.t('Problems loading reference \'{0}\': {1}', refSegment, error.message) : error.message; resolveErrors.push(toDiagnostic(errorMessage, error.code, uri)); } mergeRef(node, unresolvedSchema.schema, referencedHandle, refSegment); return resolveRefs(node, unresolvedSchema.schema, referencedHandle); }); }; const resolveRefs = (node: JSONSchema, parentSchema: JSONSchema, parentHandle: SchemaHandle): PromiseLike => { const openPromises: PromiseLike[] = []; this.traverseNodes(node, next => { const seenRefs = new Set(); while (next.$ref) { const ref = next.$ref; const segments = ref.split('#', 2); delete next.$ref; if (segments[0].length > 0) { // This is a reference to an external schema openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentHandle)); return; } else { // This is a reference inside the current schema if (!seenRefs.has(ref)) { const id = segments[1]; mergeRef(next, parentSchema, parentHandle, id); seenRefs.add(ref); } } } if (next.$recursiveRef) { usesUnsupportedFeatures.add('$recursiveRef'); } if (next.$dynamicRef) { usesUnsupportedFeatures.add('$dynamicRef'); } }); return this.promise.all(openPromises); }; const collectAnchors = (root: JSONSchema): Map => { const result = new Map(); this.traverseNodes(root, next => { const id = next.$id || next.id; const anchor = isString(id) && id.charAt(0) === '#' ? id.substring(1) : next.$anchor; if (anchor) { if (result.has(anchor)) { resolveErrors.push(toDiagnostic(l10n.t('Duplicate anchor declaration: \'{0}\'', anchor), ErrorCode.SchemaResolveError)); } else { result.set(anchor, next); } } if (next.$recursiveAnchor) { usesUnsupportedFeatures.add('$recursiveAnchor'); } if (next.$dynamicAnchor) { usesUnsupportedFeatures.add('$dynamicAnchor'); } }); return result; }; return resolveRefs(schema, schema, handle).then(_ => { let resolveWarnings: SchemaDiagnostic[] = []; if (usesUnsupportedFeatures.size) { resolveWarnings.push(toDiagnostic(l10n.t('The schema uses meta-schema features ({0}) that are not yet supported by the validator.', Array.from(usesUnsupportedFeatures.keys()).join(', ')), ErrorCode.SchemaUnsupportedFeature)); } return new ResolvedSchema(schema, resolveErrors, resolveWarnings, schemaDraft); }); } private traverseNodes(root: JSONSchema, handle: (node: JSONSchema) => void) { if (!root || typeof root !== 'object') { return Promise.resolve(null); } const seen = new Set(); const collectEntries = (...entries: (JSONSchemaRef | undefined)[]) => { for (const entry of entries) { if (isObject(entry)) { toWalk.push(entry); } } }; const collectMapEntries = (...maps: (JSONSchemaMap | undefined)[]) => { for (const map of maps) { if (isObject(map)) { for (const k in map) { const key = k as keyof JSONSchemaMap; const entry = map[key]; if (isObject(entry)) { toWalk.push(entry); } } } } }; const collectArrayEntries = (...arrays: (JSONSchemaRef[] | undefined)[]) => { for (const array of arrays) { if (Array.isArray(array)) { for (const entry of array) { if (isObject(entry)) { toWalk.push(entry); } } } } }; const collectEntryOrArrayEntries = (items: (JSONSchemaRef[] | JSONSchemaRef | undefined)) => { if (Array.isArray(items)) { for (const entry of items) { if (isObject(entry)) { toWalk.push(entry); } } } else if (isObject(items)) { toWalk.push(items); } }; const toWalk: JSONSchema[] = [root]; let next = toWalk.pop(); while (next) { if (!seen.has(next)) { seen.add(next); handle(next); collectEntries(next.additionalItems, next.additionalProperties, next.not, next.contains, next.propertyNames, next.if, next.then, next.else, next.unevaluatedItems, next.unevaluatedProperties); collectMapEntries(next.definitions, next.$defs, next.properties, next.patternProperties, next.dependencies, next.dependentSchemas); collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.prefixItems); collectEntryOrArrayEntries(next.items); } next = toWalk.pop(); } }; private getSchemaFromProperty(resource: string, document: JSONDocument): string | undefined { if (document.root?.type === 'object') { for (const p of document.root.properties) { if (p.keyNode.value === '$schema' && p.valueNode?.type === 'string') { let schemaId = p.valueNode.value; if (this.contextService && !/^\w[\w\d+.-]*:/.test(schemaId)) { // has scheme schemaId = this.contextService.resolveRelativePath(schemaId, resource); } return schemaId; } } } return undefined; } private getAssociatedSchemas(resource: string): string[] { const seen: { [schemaId: string]: boolean } = Object.create(null); const schemas: string[] = []; const normalizedResource = normalizeResourceForMatching(resource); for (const entry of this.filePatternAssociations) { if (entry.matchesPattern(normalizedResource)) { for (const schemaId of entry.getURIs()) { if (!seen[schemaId]) { schemas.push(schemaId); seen[schemaId] = true; } } } } return schemas; } public getSchemaURIsForResource(resource: string, document?: JSONDocument): string[] { let schemeId = document && this.getSchemaFromProperty(resource, document); if (schemeId) { return [schemeId]; } return this.getAssociatedSchemas(resource); } public getSchemaForResource(resource: string, document?: JSONDocument): PromiseLike { if (document) { // first use $schema if present let schemeId = this.getSchemaFromProperty(resource, document); if (schemeId) { const id = normalizeId(schemeId); return this.getOrAddSchemaHandle(id).getResolvedSchema(); } } if (this.cachedSchemaForResource && this.cachedSchemaForResource.resource === resource) { return this.cachedSchemaForResource.resolvedSchema; } const schemas = this.getAssociatedSchemas(resource); const resolvedSchema = schemas.length > 0 ? this.createCombinedSchema(resource, schemas).getResolvedSchema() : this.promise.resolve(undefined); this.cachedSchemaForResource = { resource, resolvedSchema }; return resolvedSchema; } private createCombinedSchema(resource: string, schemaIds: string[]): ISchemaHandle { if (schemaIds.length === 1) { return this.getOrAddSchemaHandle(schemaIds[0]); } else { const combinedSchemaId = 'schemaservice://combinedSchema/' + encodeURIComponent(resource); const combinedSchema: JSONSchema = { allOf: schemaIds.map(schemaId => ({ $ref: schemaId })) }; return this.addSchemaHandle(combinedSchemaId, combinedSchema); } } public getMatchingSchemas(document: TextDocument, jsonDocument: JSONDocument, schema?: JSONSchema): PromiseLike { if (schema) { const id = schema.id || ('schemaservice://untitled/matchingSchemas/' + idCounter++); const handle = this.addSchemaHandle(id, schema); return handle.getResolvedSchema().then(resolvedSchema => { return jsonDocument.getMatchingSchemas(resolvedSchema.schema).filter(s => !s.inverted); }); } return this.getSchemaForResource(document.uri, jsonDocument).then(schema => { if (schema) { return jsonDocument.getMatchingSchemas(schema.schema).filter(s => !s.inverted); } return []; }); } } let idCounter = 0; function normalizeResourceForMatching(resource: string): string { // remove queries and fragments, normalize drive capitalization try { return URI.parse(resource).with({ fragment: null, query: null }).toString(true); } catch (e) { return resource; } } function toDisplayString(url: string) { try { const uri = URI.parse(url); if (uri.scheme === 'file') { return uri.fsPath; } } catch (e) { // ignore } return url; } vscode-json-languageservice-5.7.2/src/services/jsonSelectionRanges.ts000066400000000000000000000047641514707600300261000ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Range, Position, SelectionRange, TextDocument } from '../jsonLanguageTypes'; import { JSONDocument } from '../parser/jsonParser'; import { SyntaxKind, createScanner } from 'jsonc-parser'; export function getSelectionRanges(document: TextDocument, positions: Position[], doc: JSONDocument): SelectionRange[] { function getSelectionRange(position: Position): SelectionRange { let offset = document.offsetAt(position); let node = doc.getNodeFromOffset(offset, true); const result: Range[] = []; while (node) { switch (node.type) { case 'string': case 'object': case 'array': // range without ", [ or { const cStart = node.offset + 1, cEnd = node.offset + node.length - 1; if (cStart < cEnd && offset >= cStart && offset <= cEnd) { result.push(newRange(cStart, cEnd)); } result.push(newRange(node.offset, node.offset + node.length)); break; case 'number': case 'boolean': case 'null': case 'property': result.push(newRange(node.offset, node.offset + node.length)); break; } if (node.type === 'property' || node.parent && node.parent.type === 'array') { const afterCommaOffset = getOffsetAfterNextToken(node.offset + node.length, SyntaxKind.CommaToken); if (afterCommaOffset !== -1) { result.push(newRange(node.offset, afterCommaOffset)); } } node = node.parent; } let current: SelectionRange | undefined = undefined; for (let index = result.length - 1; index >= 0; index--) { current = SelectionRange.create(result[index], current); } if (!current) { current = SelectionRange.create(Range.create(position, position)); } return current; } function newRange(start: number, end: number): Range { return Range.create(document.positionAt(start), document.positionAt(end)); } const scanner = createScanner(document.getText(), true); function getOffsetAfterNextToken(offset: number, expectedToken: SyntaxKind): number { scanner.setPosition(offset); let token = scanner.scan(); if (token === expectedToken) { return scanner.getTokenOffset() + scanner.getTokenLength(); } return -1; } return positions.map(getSelectionRange); } vscode-json-languageservice-5.7.2/src/services/jsonValidation.ts000066400000000000000000000155371514707600300251050ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { JSONSchemaService, ResolvedSchema } from './jsonSchemaService'; import { JSONDocument } from '../parser/jsonParser'; import { TextDocument, ErrorCode, PromiseConstructor, LanguageSettings, DocumentLanguageSettings, SeverityLevel, Diagnostic, DiagnosticSeverity, Range, JSONLanguageStatus } from '../jsonLanguageTypes'; import * as l10n from '@vscode/l10n'; import { JSONSchemaRef, JSONSchema } from '../jsonSchema'; import { isBoolean } from '../utils/objects'; import { DiagnosticRelatedInformation } from 'vscode-languageserver-types'; export class JSONValidation { private jsonSchemaService: JSONSchemaService; private promise: PromiseConstructor; private validationEnabled: boolean | undefined; private commentSeverity: DiagnosticSeverity | undefined; public constructor(jsonSchemaService: JSONSchemaService, promiseConstructor: PromiseConstructor) { this.jsonSchemaService = jsonSchemaService; this.promise = promiseConstructor; this.validationEnabled = true; } public configure(raw: LanguageSettings | undefined): void { if (raw) { this.validationEnabled = raw.validate !== false; this.commentSeverity = raw.allowComments ? undefined : DiagnosticSeverity.Error; } } public doValidation(textDocument: TextDocument, jsonDocument: JSONDocument, documentSettings?: DocumentLanguageSettings, schema?: JSONSchema): PromiseLike { if (!this.validationEnabled) { return this.promise.resolve([]); } const diagnostics: Diagnostic[] = []; const added: { [signature: string]: boolean } = {}; const addProblem = (problem: Diagnostic) => { // remove duplicated messages const signature = problem.range.start.line + ' ' + problem.range.start.character + ' ' + problem.message; if (!added[signature]) { added[signature] = true; diagnostics.push(problem); } }; const getDiagnostics = (schema: ResolvedSchema | undefined) => { let trailingCommaSeverity = documentSettings?.trailingCommas ? toDiagnosticSeverity(documentSettings.trailingCommas) : DiagnosticSeverity.Error; let commentSeverity = documentSettings?.comments ? toDiagnosticSeverity(documentSettings.comments) : this.commentSeverity; let schemaValidation = documentSettings?.schemaValidation ? toDiagnosticSeverity(documentSettings.schemaValidation) : DiagnosticSeverity.Warning; let schemaRequest = documentSettings?.schemaRequest ? toDiagnosticSeverity(documentSettings.schemaRequest) : DiagnosticSeverity.Warning; if (schema) { const addSchemaProblem = (errorMessage: string, errorCode: ErrorCode, relatedInformation?: DiagnosticRelatedInformation[]) => { if (jsonDocument.root && schemaRequest) { const astRoot = jsonDocument.root; const property = astRoot.type === 'object' ? astRoot.properties[0] : undefined; if (property && property.keyNode.value === '$schema') { const node = property.valueNode || property; const range = Range.create(textDocument.positionAt(node.offset), textDocument.positionAt(node.offset + node.length)); addProblem(Diagnostic.create(range, errorMessage, schemaRequest, errorCode, 'json', relatedInformation)); } else { const range = Range.create(textDocument.positionAt(astRoot.offset), textDocument.positionAt(astRoot.offset + 1)); addProblem(Diagnostic.create(range, errorMessage, schemaRequest, errorCode, 'json', relatedInformation)); } } }; if (schema.errors.length) { const error = schema.errors[0]; addSchemaProblem(error.message, error.code, error.relatedInformation); } else if (schemaValidation) { for (const warning of schema.warnings) { addSchemaProblem(warning.message, warning.code, warning.relatedInformation); } const semanticErrors = jsonDocument.validate(textDocument, schema.schema, schemaValidation, documentSettings?.schemaDraft); if (semanticErrors) { semanticErrors.forEach(addProblem); } } if (schemaAllowsComments(schema.schema)) { commentSeverity = undefined; } if (schemaAllowsTrailingCommas(schema.schema)) { trailingCommaSeverity = undefined; } } for (const p of jsonDocument.syntaxErrors) { if (p.code === ErrorCode.TrailingComma) { if (typeof trailingCommaSeverity !== 'number') { continue; } p.severity = trailingCommaSeverity; } addProblem(p); } if (typeof commentSeverity === 'number') { const message = l10n.t('Comments are not permitted in JSON.'); jsonDocument.comments.forEach(c => { addProblem(Diagnostic.create(c, message, commentSeverity, ErrorCode.CommentNotPermitted)); }); } return diagnostics; }; if (schema) { const uri = schema.id || ('schemaservice://untitled/' + idCounter++); const handle = this.jsonSchemaService.registerExternalSchema({ uri, schema }); return handle.getResolvedSchema().then(resolvedSchema => { return getDiagnostics(resolvedSchema); }); } return this.jsonSchemaService.getSchemaForResource(textDocument.uri, jsonDocument).then(schema => { return getDiagnostics(schema); }); } public getLanguageStatus(textDocument: TextDocument, jsonDocument: JSONDocument): JSONLanguageStatus { return { schemas: this.jsonSchemaService.getSchemaURIsForResource(textDocument.uri, jsonDocument) }; } } let idCounter = 0; function schemaAllowsComments(schemaRef: JSONSchemaRef): boolean | undefined { if (schemaRef && typeof schemaRef === 'object') { if (isBoolean(schemaRef.allowComments)) { return schemaRef.allowComments; } if (schemaRef.allOf) { for (const schema of schemaRef.allOf) { const allow = schemaAllowsComments(schema); if (isBoolean(allow)) { return allow; } } } } return undefined; } function schemaAllowsTrailingCommas(schemaRef: JSONSchemaRef): boolean | undefined { if (schemaRef && typeof schemaRef === 'object') { if (isBoolean(schemaRef.allowTrailingCommas)) { return schemaRef.allowTrailingCommas; } const deprSchemaRef = schemaRef as any; if (isBoolean(deprSchemaRef['allowsTrailingCommas'])) { // deprecated return deprSchemaRef['allowsTrailingCommas']; } if (schemaRef.allOf) { for (const schema of schemaRef.allOf) { const allow = schemaAllowsTrailingCommas(schema); if (isBoolean(allow)) { return allow; } } } } return undefined; } function toDiagnosticSeverity(severityLevel: SeverityLevel | undefined): DiagnosticSeverity | undefined { switch (severityLevel) { case 'error': return DiagnosticSeverity.Error; case 'warning': return DiagnosticSeverity.Warning; case 'ignore': return undefined; } return undefined; } vscode-json-languageservice-5.7.2/src/services/schemas/000077500000000000000000000000001514707600300231615ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/src/services/schemas/draft-2019-09-flat.ts000066400000000000000000000123601514707600300263760ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ // This file is generated - do not edit directly! // Derived from https://json-schema.org/draft/2019-09 export default { $id: 'https://json-schema.org/draft/2019-09/schema', $schema: 'https://json-schema.org/draft/2019-09/schema', title: '(Flattened static) Core and Validation specifications meta-schema', type: [ 'object', 'boolean', ], properties: { definitions: { $comment: 'While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.', type: 'object', additionalProperties: { $ref: '#', }, default: { }, }, dependencies: { $comment: '"dependencies" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to "dependentSchemas" and "dependentRequired"', type: 'object', additionalProperties: { anyOf: [ { $ref: '#', }, { $ref: '#/$defs/stringArray', }, ], }, }, $id: { type: 'string', format: 'uri-reference', $comment: 'Non-empty fragments not allowed.', pattern: '^[^#]*#?$', }, $schema: { type: 'string', format: 'uri', }, $anchor: { type: 'string', pattern: '^[A-Za-z][-A-Za-z0-9.:_]*$', }, $ref: { type: 'string', format: 'uri-reference', }, $recursiveAnchor: { type: 'boolean', default: false, }, $vocabulary: { type: 'object', propertyNames: { type: 'string', format: 'uri', }, additionalProperties: { type: 'boolean', }, }, $comment: { type: 'string', }, $defs: { type: 'object', additionalProperties: { $ref: '#', }, default: { }, }, additionalItems: { $ref: '#', }, unevaluatedItems: { $ref: '#', }, items: { anyOf: [ { $ref: '#', }, { $ref: '#/$defs/schemaArray', }, ], }, contains: { $ref: '#', }, additionalProperties: { $ref: '#', }, unevaluatedProperties: { $ref: '#', }, properties: { type: 'object', additionalProperties: { $ref: '#', }, default: { }, }, patternProperties: { type: 'object', additionalProperties: { $ref: '#', }, propertyNames: { format: 'regex', }, default: { }, }, dependentSchemas: { type: 'object', additionalProperties: { $ref: '#', }, }, propertyNames: { $ref: '#', }, if: { $ref: '#', }, then: { $ref: '#', }, else: { $ref: '#', }, allOf: { $ref: '#/$defs/schemaArray', }, anyOf: { $ref: '#/$defs/schemaArray', }, oneOf: { $ref: '#/$defs/schemaArray', }, not: { $ref: '#', }, multipleOf: { type: 'number', exclusiveMinimum: 0, }, maximum: { type: 'number', }, exclusiveMaximum: { type: 'number', }, minimum: { type: 'number', }, exclusiveMinimum: { type: 'number', }, maxLength: { $ref: '#/$defs/nonNegativeInteger', }, minLength: { $ref: '#/$defs/nonNegativeIntegerDefault0', }, pattern: { type: 'string', format: 'regex', }, maxItems: { $ref: '#/$defs/nonNegativeInteger', }, minItems: { $ref: '#/$defs/nonNegativeIntegerDefault0', }, uniqueItems: { type: 'boolean', default: false, }, maxContains: { $ref: '#/$defs/nonNegativeInteger', }, minContains: { $ref: '#/$defs/nonNegativeInteger', default: 1, }, maxProperties: { $ref: '#/$defs/nonNegativeInteger', }, minProperties: { $ref: '#/$defs/nonNegativeIntegerDefault0', }, required: { $ref: '#/$defs/stringArray', }, dependentRequired: { type: 'object', additionalProperties: { $ref: '#/$defs/stringArray', }, }, const: true, enum: { type: 'array', items: true, }, type: { anyOf: [ { $ref: '#/$defs/simpleTypes', }, { type: 'array', items: { $ref: '#/$defs/simpleTypes', }, minItems: 1, uniqueItems: true, }, ], }, title: { type: 'string', }, description: { type: 'string', }, default: true, deprecated: { type: 'boolean', default: false, }, readOnly: { type: 'boolean', default: false, }, writeOnly: { type: 'boolean', default: false, }, examples: { type: 'array', items: true, }, format: { type: 'string', }, contentMediaType: { type: 'string', }, contentEncoding: { type: 'string', }, contentSchema: { $ref: '#', }, }, $defs: { schemaArray: { type: 'array', minItems: 1, items: { $ref: '#', }, }, nonNegativeInteger: { type: 'integer', minimum: 0, }, nonNegativeIntegerDefault0: { $ref: '#/$defs/nonNegativeInteger', default: 0, }, simpleTypes: { enum: [ 'array', 'boolean', 'integer', 'null', 'number', 'object', 'string', ], }, stringArray: { type: 'array', items: { type: 'string', }, uniqueItems: true, default: [ ], }, }, }vscode-json-languageservice-5.7.2/src/services/schemas/draft-2020-12-flat.ts000066400000000000000000000122611514707600300263600ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ // This file is generated - do not edit directly! // Derived from https://json-schema.org/draft/2020-12 export default { $id: 'https://json-schema.org/draft/2020-12/schema', $schema: 'https://json-schema.org/draft/2020-12/schema', title: '(Flattened static) Core and Validation specifications meta-schema', type: [ 'object', 'boolean', ], properties: { definitions: { $comment: 'While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.', type: 'object', additionalProperties: { $ref: '#', }, default: { }, }, dependencies: { $comment: '"dependencies" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to "dependentSchemas" and "dependentRequired"', type: 'object', additionalProperties: { anyOf: [ { $ref: '#', }, { $ref: '#/$defs/stringArray', }, ], }, }, $id: { type: 'string', format: 'uri-reference', $comment: 'Non-empty fragments not allowed.', pattern: '^[^#]*#?$', }, $schema: { type: 'string', format: 'uri', }, $anchor: { type: 'string', pattern: '^[A-Za-z_][-A-Za-z0-9._]*$', }, $ref: { type: 'string', format: 'uri-reference', }, $dynamicRef: { type: 'string', format: 'uri-reference', }, $vocabulary: { type: 'object', propertyNames: { type: 'string', format: 'uri', }, additionalProperties: { type: 'boolean', }, }, $comment: { type: 'string', }, $defs: { type: 'object', additionalProperties: { $ref: '#', }, default: { }, }, prefixItems: { $ref: '#/$defs/schemaArray', }, items: { $ref: '#', }, contains: { $ref: '#', }, additionalProperties: { $ref: '#', }, properties: { type: 'object', additionalProperties: { $ref: '#', }, default: { }, }, patternProperties: { type: 'object', additionalProperties: { $ref: '#', }, propertyNames: { format: 'regex', }, default: { }, }, dependentSchemas: { type: 'object', additionalProperties: { $ref: '#', }, }, propertyNames: { $ref: '#', }, if: { $ref: '#', }, then: { $ref: '#', }, else: { $ref: '#', }, allOf: { $ref: '#/$defs/schemaArray', }, anyOf: { $ref: '#/$defs/schemaArray', }, oneOf: { $ref: '#/$defs/schemaArray', }, not: { $ref: '#', }, unevaluatedItems: { $ref: '#', }, unevaluatedProperties: { $ref: '#', }, multipleOf: { type: 'number', exclusiveMinimum: 0, }, maximum: { type: 'number', }, exclusiveMaximum: { type: 'number', }, minimum: { type: 'number', }, exclusiveMinimum: { type: 'number', }, maxLength: { $ref: '#/$defs/nonNegativeInteger', }, minLength: { $ref: '#/$defs/nonNegativeIntegerDefault0', }, pattern: { type: 'string', format: 'regex', }, maxItems: { $ref: '#/$defs/nonNegativeInteger', }, minItems: { $ref: '#/$defs/nonNegativeIntegerDefault0', }, uniqueItems: { type: 'boolean', default: false, }, maxContains: { $ref: '#/$defs/nonNegativeInteger', }, minContains: { $ref: '#/$defs/nonNegativeInteger', default: 1, }, maxProperties: { $ref: '#/$defs/nonNegativeInteger', }, minProperties: { $ref: '#/$defs/nonNegativeIntegerDefault0', }, required: { $ref: '#/$defs/stringArray', }, dependentRequired: { type: 'object', additionalProperties: { $ref: '#/$defs/stringArray', }, }, const: true, enum: { type: 'array', items: true, }, type: { anyOf: [ { $ref: '#/$defs/simpleTypes', }, { type: 'array', items: { $ref: '#/$defs/simpleTypes', }, minItems: 1, uniqueItems: true, }, ], }, title: { type: 'string', }, description: { type: 'string', }, default: true, deprecated: { type: 'boolean', default: false, }, readOnly: { type: 'boolean', default: false, }, writeOnly: { type: 'boolean', default: false, }, examples: { type: 'array', items: true, }, format: { type: 'string', }, contentMediaType: { type: 'string', }, contentEncoding: { type: 'string', }, contentSchema: { $ref: '#', }, }, $defs: { schemaArray: { type: 'array', minItems: 1, items: { $ref: '#', }, }, nonNegativeInteger: { type: 'integer', minimum: 0, }, nonNegativeIntegerDefault0: { $ref: '#/$defs/nonNegativeInteger', default: 0, }, simpleTypes: { enum: [ 'array', 'boolean', 'integer', 'null', 'number', 'object', 'string', ], }, stringArray: { type: 'array', items: { type: 'string', }, uniqueItems: true, default: [ ], }, }, }vscode-json-languageservice-5.7.2/src/test/000077500000000000000000000000001514707600300206725ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/src/test/completion.test.ts000066400000000000000000001165351514707600300244040ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { getLanguageService, JSONSchema, TextDocument, ClientCapabilities, CompletionList, CompletionItemKind, Position, MarkupContent, TextEdit } from '../jsonLanguageService'; import { repeat } from '../utils/strings'; import { CompletionItemLabelDetails } from 'vscode-languageserver-types'; const applyEdits = TextDocument.applyEdits; interface ItemDescription { label: string; detail?: string; labelDetails?: CompletionItemLabelDetails; documentation?: string | MarkupContent; kind?: CompletionItemKind; resultText?: string; notAvailable?: boolean; sortText?: string; } const assertCompletion = function (completions: CompletionList, expected: ItemDescription, document: TextDocument, offset: number) { const matches = completions.items.filter(completion => { return completion.label === expected.label; }); if (expected.notAvailable) { assert.equal(matches.length, 0, expected.label + " should not existing is results"); return; } assert.equal(matches.length, 1, expected.label + " should only existing once: Actual: " + completions.items.map(c => c.label).join(', ')); const match = matches[0]; if (expected.detail !== undefined) { assert.equal(match.detail, expected.detail); } if (expected.labelDetails !== undefined) { assert.deepEqual(match.labelDetails, expected.labelDetails); } if (expected.documentation !== undefined) { assert.deepEqual(match.documentation, expected.documentation); } if (expected.kind !== undefined) { assert.equal(match.kind, expected.kind); } if (expected.resultText !== undefined && match.textEdit !== undefined) { const edit = TextEdit.is(match.textEdit) ? match.textEdit : TextEdit.replace(match.textEdit.replace, match.textEdit.newText); assert.equal(applyEdits(document, [edit]), expected.resultText); } if (expected.sortText !== undefined) { assert.equal(match.sortText, expected.sortText); } }; suite('JSON Completion', () => { const testCompletionsFor = function (value: string, schema: JSONSchema | null, expected: { count?: number, items?: ItemDescription[] }, clientCapabilities = ClientCapabilities.LATEST): PromiseLike { const offset = value.indexOf('|'); value = value.substr(0, offset) + value.substr(offset + 1); const ls = getLanguageService({ clientCapabilities }); if (schema) { ls.configure({ schemas: [{ uri: 'http://myschemastore/test1', schema, fileMatch: ["*.json"] }] }); } const document = TextDocument.create('test://test/test.json', 'json', 0, value); const position = Position.create(0, offset); const jsonDoc = ls.parseJSONDocument(document); return ls.doComplete(document, position, jsonDoc).then(list => { if (expected.count) { assert.equal(list!.items.length, expected.count, value + ' ' + list!.items.map(i => i.label).join(', ')); } if (expected.items) { for (const item of expected.items) { assertCompletion(list!, item, document, offset); } } }); }; test('Complete property no schema', async function () { await testCompletionsFor('[ { "name": "John", "age": 44 }, { | }', null, { count: 2, items: [ { label: 'name', resultText: '[ { "name": "John", "age": 44 }, { "name" }' }, { label: 'age', resultText: '[ { "name": "John", "age": 44 }, { "age" }' } ] }); await testCompletionsFor('[ { "name": "John", "age": 44 }, { "| }', null, { count: 2, items: [ { label: 'name', resultText: '[ { "name": "John", "age": 44 }, { "name"' }, { label: 'age', resultText: '[ { "name": "John", "age": 44 }, { "age"' } ] }); await testCompletionsFor('[ { "name": "John", "age": 44 }, { "n| }', null, { count: 2, items: [ { label: 'name', resultText: '[ { "name": "John", "age": 44 }, { "name"' }, { label: 'age', resultText: '[ { "name": "John", "age": 44 }, { "age"' } ] }); await testCompletionsFor('[ { "name": "John", "age": 44 }, { "name|" }', null, { count: 2, items: [ { label: 'name', resultText: '[ { "name": "John", "age": 44 }, { "name" }' }, { label: 'age', resultText: '[ { "name": "John", "age": 44 }, { "age" }' } ] }); await testCompletionsFor('[ { "name": "John", "age": 44, "city": "DC" }, { "name|": "Paul", "age": 23 }', null, { items: [ { label: 'city', resultText: '[ { "name": "John", "age": 44, "city": "DC" }, { "city": "Paul", "age": 23 }' }, ] }); await testCompletionsFor('[ { "name": "John", "address": { "street" : "MH Road", "number" : 5 } }, { "name": "Jack", "address": { "street" : "100 Feet Road", | }', null, { count: 1, items: [ { label: 'number', resultText: '[ { "name": "John", "address": { "street" : "MH Road", "number" : 5 } }, { "name": "Jack", "address": { "street" : "100 Feet Road", "number" }' } ] }); }); test('Complete values no schema', async function () { await testCompletionsFor('[ { "name": "John", "age": 44 }, { "name": |', null, { count: 1, items: [ { label: '"John"', resultText: '[ { "name": "John", "age": 44 }, { "name": "John"' } ] }); await testCompletionsFor('[ { "data": { "key": 1, "data": true } }, { "data": |', null, { count: 3, items: [ { label: '{}', resultText: '[ { "data": { "key": 1, "data": true } }, { "data": {$1}' }, { label: 'true', resultText: '[ { "data": { "key": 1, "data": true } }, { "data": true' }, { label: 'false', resultText: '[ { "data": { "key": 1, "data": true } }, { "data": false' } ] }); await testCompletionsFor('[ { "data": "foo" }, { "data": "bar" }, { "data": "|" } ]', null, { count: 2, items: [ { label: '"foo"', resultText: '[ { "data": "foo" }, { "data": "bar" }, { "data": "foo" } ]' }, { label: '"bar"', resultText: '[ { "data": "foo" }, { "data": "bar" }, { "data": "bar" } ]' } ] }); await testCompletionsFor('[ { "data": "foo" }, { "data": "bar" }, { "data": "f|" } ]', null, { count: 2, items: [ { label: '"foo"', resultText: '[ { "data": "foo" }, { "data": "bar" }, { "data": "foo" } ]' }, { label: '"bar"', resultText: '[ { "data": "foo" }, { "data": "bar" }, { "data": "bar" } ]' } ] }); await testCompletionsFor('[ { "data": "foo" }, { "data": "bar" }, { "data": "xoo"|, "o": 1 } ]', null, { count: 2, items: [ { label: '"foo"', resultText: '[ { "data": "foo" }, { "data": "bar" }, { "data": "foo", "o": 1 } ]' }, { label: '"bar"', resultText: '[ { "data": "foo" }, { "data": "bar" }, { "data": "bar", "o": 1 } ]' } ] }); await testCompletionsFor('[ { "data": "foo" }, { "data": "bar" }, { "data": "xoo" | } ]', null, { count: 0 }); }); test('Complete property with schema', async function () { const schema: JSONSchema = { type: 'object', properties: { 'a': { type: 'number', description: 'A' }, 'b': { type: 'string', description: 'B' }, 'cool': { type: 'boolean', description: 'C' } } }; await testCompletionsFor('{|}', schema, { count: 3, items: [ { label: 'a', documentation: 'A', resultText: '{"a": ${1:0}}' }, { label: 'b', documentation: 'B', resultText: '{"b": "$1"}' }, { label: 'cool', documentation: 'C', resultText: '{"cool": $1}' } ] }); await testCompletionsFor('{ "a|}', schema, { count: 3, items: [ { label: 'a', documentation: 'A', resultText: '{ "a": ${1:0}' } ] }); await testCompletionsFor('{ "b": 1 "a|}', schema, { count: 2, items: [ { label: 'a', documentation: 'A', resultText: '{ "b": 1 "a": ${1:0}' } ] }); await testCompletionsFor('{ "|}', schema, { count: 3, items: [ { label: 'a', documentation: 'A', resultText: '{ "a": ${1:0}' } ] }); await testCompletionsFor('{ a|}', schema, { count: 3, items: [ { label: 'a', documentation: 'A', resultText: '{ "a": ${1:0}}' } ] }); await testCompletionsFor('{ "a": 1,|}', schema, { count: 2, items: [ { label: 'b', documentation: 'B', resultText: '{ "a": 1,"b": "$1"}' }, { label: 'cool', documentation: 'C', resultText: '{ "a": 1,"cool": $1}' } ] }); await testCompletionsFor('{ |, "a": 1}', schema, { count: 2, items: [ { label: 'b', documentation: 'B', resultText: '{ "b": "$1", "a": 1}' }, { label: 'cool', documentation: 'C', resultText: '{ "cool": $1, "a": 1}' } ] }); await testCompletionsFor('{ "a": 1 "b|"}', schema, { items: [ { label: 'b', documentation: 'B', resultText: '{ "a": 1 "b": "$1"}' }, ] }); await testCompletionsFor('{ "c|"\n"b": "v"}', schema, { items: [ { label: 'a', resultText: '{ "a": ${1:0},\n"b": "v"}' }, { label: 'cool', resultText: '{ "cool": $1,\n"b": "v"}' }, { label: 'b', notAvailable: true } ] }); await testCompletionsFor('{ c|\n"b": "v"}', schema, { items: [ { label: 'a', resultText: '{ "a": ${1:0},\n"b": "v"}' }, { label: 'cool', resultText: '{ "cool": $1,\n"b": "v"}' }, { label: 'b', notAvailable: true } ] }); }); test('Complete JS inherited property with schema', async function () { const schema: JSONSchema = { type: 'object', properties: { 'hasOwnProperty': { type: 'integer', description: 'hasOwnProperty' }, 'constructor': { type: 'integer', description: 'constructor' }, } }; await testCompletionsFor('{|}', schema, { count: 2, items: [ { label: 'hasOwnProperty', documentation: 'hasOwnProperty', resultText: '{"hasOwnProperty": ${1:0}}' }, { label: 'constructor', documentation: 'constructor', resultText: '{"constructor": ${1:0}}' }, ] }); }); test('Complete propertyNames', async function () { const schema: JSONSchema = { type: 'object', properties: { enum: { type: 'object', propertyNames: { enum: ['a', 'bc'], description: 'Enum' }, }, const: { type: 'object', propertyNames: { const: 'a', description: 'Const' }, }, descriptions: { type: 'object', propertyNames: { enum: ['a', 'b', 'c'], markdownEnumDescriptions: ['*A*'], enumDescriptions: ['A', 'B'], markdownDescription: '*Markdown*', description: 'Nope' }, }, doNotSuggest: { type: 'object', propertyNames: { enum: ['a'], doNotSuggest: true } }, suggestSortText: { type: 'object', propertyNames: { const: 'a', suggestSortText: 'sort' } }, deprecation: { type: 'object', oneOf: [ { propertyNames: { const: 'a', deprecationMessage: 'Deprecated' } }, { propertyNames: { const: 'b' } } ] }, enumSortTexts: { type: 'object', propertyNames: { enum: ['a', 'b'], enumSortTexts: ['2', '1'], }, }, examples: { type: 'object', propertyNames: { examples: ['a', 'b'], }, } } }; await testCompletionsFor('{"enum":{|}}', schema, { count: 2, items: [ { label: 'a', documentation: 'Enum', resultText: '{"enum":{"a": $1}}' }, { label: 'bc', documentation: 'Enum', resultText: '{"enum":{"bc": $1}}' }, ] }); await testCompletionsFor('{"const":{"|', schema, { count: 1, items: [ { label: 'a', documentation: 'Const', resultText: '{"const":{"a": $1' }, ] }); await testCompletionsFor('{"descriptions":{|}}', schema, { count: 3, items: [ { label: 'a', documentation: { kind: 'markdown', value: '*A*' }, resultText: '{"descriptions":{"a": $1}}' }, { label: 'b', documentation: 'B', resultText: '{"descriptions":{"b": $1}}' }, { label: 'c', documentation: { kind: 'markdown', value: '*Markdown*' }, resultText: '{"descriptions":{"c": $1}}' }, ] }); await testCompletionsFor('{"doNotSuggest":{|}}', schema, { items: [ { label: 'a', notAvailable: true }, ] }); await testCompletionsFor('{"suggestSortText":{|}}', schema, { count: 1, items: [ { label: 'a', sortText: "sort" }, ] }); await testCompletionsFor('{"deprecation":{|}}', schema, { count: 1, items: [ { label: 'a', notAvailable: true }, { label: 'b', documentation: '' }, ] }); await testCompletionsFor('{"enumSortTexts":{|}}', schema, { items: [ { label: 'a', sortText: "2" }, { label: 'b', sortText: "1" }, ] }); await testCompletionsFor('{"examples":{|}}', schema, { items: [ { label: 'a' }, { label: 'b' }, ] }); }); test('Complete value with schema', async function () { const schema: JSONSchema = { type: 'object', properties: { 'a': { enum: ['John', 'Jeff', 'George'] }, 'c': { type: 'array', items: { type: 'string' } }, "version": { "type": "number", "const": 2, "markdownDescription": "version", }, } }; await testCompletionsFor('{ "a": | }', schema, { count: 3, items: [ { label: '"John"', resultText: '{ "a": "John" }' }, { label: '"Jeff"', resultText: '{ "a": "Jeff" }' }, { label: '"George"', resultText: '{ "a": "George" }' } ] }); await testCompletionsFor('{ "a": "J| }', schema, { count: 3, items: [ { label: '"John"', resultText: '{ "a": "John"' }, { label: '"Jeff"', resultText: '{ "a": "Jeff"' }, ] }); await testCompletionsFor('{ "a": "John"|, "b": 1 }', schema, { count: 3, items: [ { label: '"John"', resultText: '{ "a": "John", "b": 1 }' } ] }); await testCompletionsFor('{ "version": | }', schema, { count: 1, items: [ { label: '2', resultText: '{ "version": 2 }' }, ] }); await testCompletionsFor('{ "version": 2| }', schema, { count: 1, items: [ { label: '2', resultText: '{ "version": 2 }' }, ] }); }); test('Complete array value with schema', async function () { const schema: JSONSchema = { type: 'object', properties: { 'c': { type: 'array', items: { type: 'number', enum: [1, 2] } } } }; await testCompletionsFor('{ "c": [ | ] }', schema, { items: [ { label: '1', resultText: '{ "c": [ 1 ] }' }, { label: '2', resultText: '{ "c": [ 2 ] }' } ] }); await testCompletionsFor('{ "c": [ 1, | ] }', schema, { items: [ { label: '1', resultText: '{ "c": [ 1, 1 ] }' }, { label: '2', resultText: '{ "c": [ 1, 2 ] }' } ] }); await testCompletionsFor('{ "c": [ | 1] }', schema, { items: [ { label: '1', resultText: '{ "c": [ 1, 1] }' }, { label: '2', resultText: '{ "c": [ 2, 1] }' } ] }); }); test('Complete array value with schema (uniqueItems)', async function () { const schema: JSONSchema = { type: 'object', properties: { 'c': { type: 'array', uniqueItems: true, items: { type: 'number', enum: [1, 2, "hello", "world"], } } } }; await testCompletionsFor('{ "c": [ 1, "hello", | ] }', schema, { items: [ { label: '2', resultText: '{ "c": [ 1, "hello", 2 ] }' }, { label: '"world"', resultText: '{ "c": [ 1, "hello", "world" ] }' }, { notAvailable: true, label: '1' }, { notAvailable: true, label: "hello" } ] }); }); test('Complete array value with schema (uniqueItems) 2', async function () { const schema: JSONSchema = { type: 'object', properties: { 'c': { type: 'array', uniqueItems: true, items: { default: 1, defaultSnippets: [{ body: "world" }, { label: "new item", "description": "", body: "hello" }] } } } }; await testCompletionsFor('{ "c": [ 1, "hello", | ] }', schema, { items: [ { label: '"world"', resultText: '{ "c": [ 1, "hello", "world" ] }' }, { notAvailable: true, label: '1' }, { notAvailable: true, label: "hello" } ] }); }); test('Complete array value with schema 2', async function () { const schema: JSONSchema = { type: 'object', properties: { 'c': { type: 'array', items: [ { enum: [1, 2] }, { enum: [3, 4] }, { enum: [5, 6] } ] } } }; await testCompletionsFor('{ "c": [ | ] }', schema, { items: [ { label: '1', resultText: '{ "c": [ 1 ] }' }, { label: '2', resultText: '{ "c": [ 2 ] }' } ] }); await testCompletionsFor('{ "c": [ 1, | ] }', schema, { items: [ { label: '3', resultText: '{ "c": [ 1, 3 ] }' }, { label: '4', resultText: '{ "c": [ 1, 4 ] }' } ] }); await testCompletionsFor('{ "c": [ 1, 3, 6| ] }', schema, { items: [ { label: '5', resultText: '{ "c": [ 1, 3, 5 ] }' }, { label: '6', resultText: '{ "c": [ 1, 3, 6 ] }' } ] }); await testCompletionsFor('{ "c": [ | 1] }', schema, { items: [ { label: '1', resultText: '{ "c": [ 1, 1] }' }, { label: '2', resultText: '{ "c": [ 2, 1] }' } ] }); }); test('Complete array value with schema 3 (issue #81459)', async function () { const schema: JSONSchema = { type: "object", properties: { "a": { type: 'array', items: { type: 'object', properties: { foo: { type: 'string' }, bar: { type: 'string' } } } } } }; await testCompletionsFor('{ "a" : [ { "foo": "a", | } ] }', schema, { items: [ { label: 'bar', resultText: '{ "a" : [ { "foo": "a", "bar": "$1" } ] }' } ] }); await testCompletionsFor('{ "a" : [ { "bar": "a" }|, { } ] }', schema, { items: [ { label: 'foo', notAvailable: true } ] }); }); test('Complete value with schema: booleans, null', async function () { const schema: JSONSchema = { type: 'object', properties: { 'a': { type: 'boolean' }, 'b': { type: ['boolean', 'null'] }, } }; await testCompletionsFor('{ "a": | }', schema, { count: 2, items: [ { label: 'true', resultText: '{ "a": true }' }, { label: 'false', resultText: '{ "a": false }' }, ] }); await testCompletionsFor('{ "b": "| }', schema, { count: 3, items: [ { label: 'true', resultText: '{ "b": true' }, { label: 'false', resultText: '{ "b": false' }, { label: 'null', resultText: '{ "b": null' } ] }); }); test('Complete with nested schema', async function () { const content = '{|}'; const schema: JSONSchema = { oneOf: [{ type: 'object', properties: { 'a': { type: 'number', description: 'A' }, 'b': { type: 'string', description: 'B' }, } }, { type: 'array' }] }; await testCompletionsFor(content, schema, { count: 2, items: [ { label: 'a', documentation: 'A' }, { label: 'b', documentation: 'B' } ] }); }); test('Complete with required anyOf', async function () { const schema: JSONSchema = { anyOf: [{ type: 'object', required: ['a', 'b'], properties: { 'a': { type: 'string', description: 'A' }, 'b': { type: 'string', description: 'B' }, } }, { type: 'object', required: ['c', 'd'], properties: { 'c': { type: 'string', description: 'C' }, 'd': { type: 'string', description: 'D' }, } }] }; await testCompletionsFor('{|}', schema, { count: 4, items: [ { label: 'a', documentation: 'A' }, { label: 'b', documentation: 'B' }, { label: 'c', documentation: 'C' }, { label: 'd', documentation: 'D' } ] }); await testCompletionsFor('{ "a": "", |}', schema, { count: 1, items: [ { label: 'b', documentation: 'B' } ] }); }); test('Complete with anyOf', async function () { const schema: JSONSchema = { anyOf: [{ type: 'object', properties: { 'type': { enum: ['house'] }, 'b': { type: 'string' }, } }, { type: 'object', properties: { 'type': { enum: ['appartment'] }, 'c': { type: 'string' }, } }] }; await testCompletionsFor('{|}', schema, { count: 3, items: [ { label: 'type' }, { label: 'b' }, { label: 'c' } ] }); await testCompletionsFor('{ "type": "appartment", |}', schema, { count: 1, items: [ { label: 'c' } ] }); }); test('Complete with oneOf', async function () { const schema: JSONSchema = { oneOf: [{ type: 'object', allOf: [{ properties: { 'a': { type: 'string', description: 'A' } } }, { anyOf: [{ properties: { 'b1': { type: 'string', description: 'B1' } }, }, { properties: { 'b2': { type: 'string', description: 'B2' } }, }] }] }, { type: 'object', properties: { 'c': { type: 'string', description: 'C' }, 'd': { type: 'string', description: 'D' }, } }] }; await testCompletionsFor('{|}', schema, { count: 5, items: [ { label: 'a', documentation: 'A' }, { label: 'b1', documentation: 'B1' }, { label: 'b2', documentation: 'B2' }, { label: 'c', documentation: 'C' }, { label: 'd', documentation: 'D' } ] }); await testCompletionsFor('{ "b1": "", |}', schema, { count: 2, items: [ { label: 'a', documentation: 'A' }, { label: 'b2', documentation: 'B2' } ] }); }); test('Complete with oneOf and enums', async function () { const schema: JSONSchema = { oneOf: [{ type: 'object', properties: { 'type': { type: 'string', enum: ['1', '2'] }, 'a': { type: 'object', properties: { 'x': { type: 'string' }, 'y': { type: 'string' } }, "required": ['x', 'y'] }, 'b': {} }, }, { type: 'object', properties: { 'type': { type: 'string', enum: ['3'] }, 'a': { type: 'object', properties: { 'x': { type: 'string' }, 'z': { type: 'string' } }, "required": ['x', 'z'] }, 'c': {} }, }] }; await testCompletionsFor('{|}', schema, { count: 4, items: [ { label: 'type' }, { label: 'a' }, { label: 'b' }, { label: 'c' } ] }); await testCompletionsFor('{ "type": |}', schema, { count: 3, items: [ { label: '"1"' }, { label: '"2"' }, { label: '"3"' } ] }); await testCompletionsFor('{ "a": { "x": "", "y": "" }, "type": |}', schema, { count: 2, items: [ { label: '"1"' }, { label: '"2"' } ] }); await testCompletionsFor('{ "type": "1", "a" : { | }', schema, { count: 2, items: [ { label: 'x' }, { label: 'y' } ] }); await testCompletionsFor('{ "type": "1", "a" : { "x": "", "z":"" }, |', schema, { // both alternatives have errors: intellisense proposes all options count: 2, items: [ { label: 'b' }, { label: 'c' } ] }); await testCompletionsFor('{ "a" : { "x": "", "z":"" }, |', schema, { count: 2, items: [ { label: 'type' }, { label: 'c' } ] }); }); test('Escaping no schema', async function () { await testCompletionsFor('[ { "\\\\${1:b}": "John" }, { "|" }', null, { items: [ { label: '\\${1:b}' } ] }); await testCompletionsFor('[ { "\\\\${1:b}": "John" }, { | }', null, { items: [ { label: '\\${1:b}', resultText: '[ { "\\\\${1:b}": "John" }, { "\\\\\\\\\\${1:b\\}" }' } ] }); await testCompletionsFor('[ { "name": "\\{" }, { "name": | }', null, { items: [ { label: '"\\{"' } ] }); }); test('Escaping with schema', async function () { const schema: JSONSchema = { type: 'object', properties: { '{\\}': { default: "{\\}", defaultSnippets: [{ body: "${1:const}" }], enum: ['John{\\}'] } } }; await testCompletionsFor('{ | }', schema, { items: [ { label: '{\\}', resultText: '{ "{\\\\\\\\\\}": $1 }' } ] }); await testCompletionsFor('{ "{\\\\}": | }', schema, { items: [ { label: '"{\\\\}"', resultText: '{ "{\\\\}": "{\\\\\\\\\\}" }' }, { label: '"John{\\\\}"', resultText: '{ "{\\\\}": "John{\\\\\\\\\\}" }' }, { label: '"const"', resultText: '{ "{\\\\}": "${1:const}" }' } ] }); }); test('Escaping with schema - #13716', async function () { const schema: JSONSchema = { type: 'object', properties: { 'url': { default: "http://foo/bar" } } }; await testCompletionsFor('{ | }', schema, { items: [ { label: 'url', resultText: '{ "url": "${1:http://foo/bar}" }' } ] }); }); test('Sanititize', async function () { const longLabel = repeat('abcd', 20); const schema: JSONSchema = { type: 'object', properties: { 'a\nb': { default: 1 }, [longLabel]: { default: 2 } } }; await testCompletionsFor('{ | }', schema, { items: [ { label: 'a↵b', resultText: '{ "a\\\\nb": ${1:1} }' }, { label: 'abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcda...', resultText: `{ "${longLabel}": \${1:2} }` } ] }); }); test('Enum and defaults', async function () { let schema: JSONSchema = { type: 'object', properties: { prop: { type: ['boolean', 'string'], enum: [false, 'rcodetools'], default: '' } } }; await testCompletionsFor('{ "prop": | }', schema, { items: [ { label: 'false', resultText: '{ "prop": false }' }, { label: '"rcodetools"', resultText: '{ "prop": "rcodetools" }' }, { label: '""', resultText: '{ "prop": "" }' } ], count: 3 }); schema = { type: 'object', properties: { prop: { type: ['string'], enum: ['green', 'red'], default: 'red' } } }; await testCompletionsFor('{ "prop": | }', schema, { items: [ { label: '"green"', resultText: '{ "prop": "green" }', labelDetails: undefined }, { label: '"red"', resultText: '{ "prop": "red" }', labelDetails: { description: 'Default value' } }, ], count: 2 }); }); test('examples', async function () { const schema: JSONSchema = { type: 'object', properties: { prop: { type: ['string'], examples: ['a', 'b'], default: 'c' } } }; await testCompletionsFor('{ "prop": | }', schema, { items: [ { label: '"a"', resultText: '{ "prop": "a" }' }, { label: '"b"', resultText: '{ "prop": "b" }' }, { label: '"c"', resultText: '{ "prop": "c" }' } ], count: 3 }); }); test('Const', async function () { const schema: JSONSchema = { type: 'object', properties: { prop: { type: 'string', const: 'hello' }, propBool: { type: 'boolean', const: false } } }; await testCompletionsFor('{ "prop": | }', schema, { items: [ { label: '"hello"', resultText: '{ "prop": "hello" }' }, ] }); await testCompletionsFor('{ "propBool": | }', schema, { items: [ { label: 'false', resultText: '{ "propBool": false }' } ], count: 1 }); }); test('$schema', async function () { const schema: JSONSchema = { type: 'object', }; await testCompletionsFor('{ "$sc| }', null, { items: [ { label: '$schema', resultText: '{ "\\$schema": $1' } ] }); await testCompletionsFor('{ "$schema": | }', schema, { items: [ { label: '"http://myschemastore/test1"', resultText: '{ "$schema": "http://myschemastore/test1" }' }, { label: '"https://json-schema.org/draft-04/schema#"', resultText: '{ "$schema": "https://json-schema.org/draft-04/schema#" }' }, { label: '"https://json-schema.org/draft-07/schema#"', resultText: '{ "$schema": "https://json-schema.org/draft-07/schema#" }' } ] }); await testCompletionsFor('{ "$schema": "|', schema, { items: [ { label: '"http://myschemastore/test1"', resultText: '{ "$schema": "http://myschemastore/test1"' } ] }); await testCompletionsFor('{ "$schema": "h|', schema, { items: [ { label: '"http://myschemastore/test1"', resultText: '{ "$schema": "http://myschemastore/test1"' } ] }); await testCompletionsFor('{ "$schema": "http://myschemastore/test1"| }', schema, { items: [ { label: '"http://myschemastore/test1"', resultText: '{ "$schema": "http://myschemastore/test1" }' } ] }); }); test('root default proposals', async function () { const schema1: JSONSchema = { type: 'object', default: { hello: 'world' } }; const schema2: JSONSchema = { anyOf: [ { default: {} }, { defaultSnippets: [{ label: 'def1', description: 'def1Desc', body: { hello: '${1:world}' } }, { body: { "${1:hello}": ["${2:world}"] } }] } ], type: 'object', default: { hello: 'world' } }; await testCompletionsFor('|', schema1, { items: [ { label: '{"hello":"world"}', resultText: '{\n\t"hello": "world"\n\\}' } ] }); await testCompletionsFor('|', schema2, { items: [ { label: '{}', resultText: '{$1}' }, { label: 'def1', documentation: 'def1Desc', resultText: '{\n\t"hello": "${1:world}"\n}' }, { label: '{"hello":["world"]}', resultText: '{\n\t"${1:hello}": [\n\t\t"${2:world}"\n\t]\n}' } ] }); }); test('Default snippet', async function () { const schema: JSONSchema = { type: 'array', items: { type: 'object', defaultSnippets: [ { label: 'foo', bodyText: '{\n\t"foo": "${1:b}"\n}' }, { label: 'foo2', body: { key1: '^$1' } } ] } }; await testCompletionsFor('|', schema, { items: [ { label: 'foo', resultText: '[\n\t{\n\t\t"foo": "${1:b}"\n\t}\n]' }, { label: 'foo2', resultText: '[\n\t{\n\t\t"key1": $1\n\t}\n]' } ] }); }); test('Default snippets for complex properties', async function () { const schema: JSONSchema = { type: 'object', properties: { property_test: { type: 'object', defaultSnippets: [{ label: 'property snippet', bodyText: '{}' }] } }, patternProperties: { pattern: { type: 'object', defaultSnippets: [{ label: 'pattern snippet', bodyText: '{}' }] }, "(?i)caseinsensitive": { type: 'object', defaultSnippets: [{ label: 'case insensitive snippet', bodyText: '{}' }] } }, additionalProperties: { type: 'object', defaultSnippets: [{ label: 'additional snippet', bodyText: '{}' }] } }; await testCompletionsFor('{"property_test":|', schema, { items: [{ label: 'property snippet' }] }); await testCompletionsFor('{"pattern_test":|', schema, { items: [{ label: 'pattern snippet' }] }); await testCompletionsFor('{"CaseInsensitive_test":|', schema, { items: [{ label: 'case insensitive snippet' }] }); await testCompletionsFor('{"additional_test":|', schema, { items: [{ label: 'additional snippet' }] }); }); test('Deprecation message', async function () { const schema: JSONSchema = { type: 'object', properties: { 'prop1': { deprecationMessage: "Prop is deprecated" }, 'prop2': { type: 'string' }, } }; await testCompletionsFor('{ |', schema, { items: [ { label: 'prop2' }, { label: 'prop1', notAvailable: true } ] }); }); test('Enum description', async function () { const schema: JSONSchema = { type: 'object', properties: { 'prop1': { enum: ['e1', 'e2', 'e3'], enumDescriptions: ['E1', 'E2', 'E3'], }, 'prop2': { description: 'prop2', enum: ['e1', 'e2', 'e3'], }, } }; await testCompletionsFor('{ "prop1": |', schema, { items: [ { label: '"e1"', documentation: 'E1' }, { label: '"e2"', documentation: 'E2' } ] }); await testCompletionsFor('{ "prop2": |', schema, { items: [ { label: '"e1"', documentation: 'prop2' }, { label: '"e2"', documentation: 'prop2' } ] }); }); test('Enum markdown description', async function () { const schema: JSONSchema = { type: 'object', properties: { 'prop1': { enum: ['e1', 'e2', 'e3'], markdownEnumDescriptions: ['*E1*', '*E2*', '*E3*'], }, 'prop2': { enum: ['e1', 'e2', 'e3'], enumDescriptions: ['E1', 'E2', 'E3'], markdownEnumDescriptions: ['*E1*', '*E2*', '*E3*'], }, 'prop3': { description: 'Hello', markdownDescription: '*Hello*', enum: ['e1', 'e2', 'e3'], markdownEnumDescriptions: ['*E1*', '*E2*', '*E3*'], }, 'prop4': { markdownDescription: '*prop4*', enum: ['e1', 'e2', 'e3'], }, 'prop5': { description: 'prop5', markdownDescription: '*prop5*', enum: ['e1', 'e2', 'e3'], }, 'prop6': { enum: ['e1', 'e2', 'e3'], markdownEnumDescriptions: ['*E1*', '*E2*', '*E3*'], enumDetails: [ 'D1', 'D2', 'D3' ], enumSortTexts: ['s1', 's2', 's3'] }, } }; await testCompletionsFor('{ "prop1": |', schema, { items: [ { label: '"e1"', documentation: { kind: 'markdown', value: '*E1*' } }, { label: '"e2"', documentation: { kind: 'markdown', value: '*E2*' } } ] }); await testCompletionsFor('{ "prop2": |', schema, { items: [ { label: '"e1"', documentation: { kind: 'markdown', value: '*E1*' } }, { label: '"e2"', documentation: { kind: 'markdown', value: '*E2*' } } ] }); await testCompletionsFor('{ "prop3": |', schema, { items: [ { label: '"e1"', documentation: { kind: 'markdown', value: '*E1*' } }, { label: '"e2"', documentation: { kind: 'markdown', value: '*E2*' } } ] }); await testCompletionsFor('{ "prop4": |', schema, { items: [ { label: '"e1"', documentation: { kind: 'markdown', value: '*prop4*' } }, { label: '"e2"', documentation: { kind: 'markdown', value: '*prop4*' } } ] }); await testCompletionsFor('{ "prop5": |', schema, { items: [ { label: '"e1"', documentation: { kind: 'markdown', value: '*prop5*' } }, { label: '"e2"', documentation: { kind: 'markdown', value: '*prop5*' } } ] }); await testCompletionsFor('{ "prop6": |', schema, { items: [ { label: '"e1"', documentation: { kind: 'markdown', value: '*E1*' }, detail: 'D1', sortText: 's1' }, { label: '"e2"', documentation: { kind: 'markdown', value: '*E2*' }, detail: 'D2', sortText: 's2' } ] }); // without markdown capability await testCompletionsFor('{ "prop1": |', schema, { items: [ { label: '"e1"', documentation: undefined }, ] }, {}); await testCompletionsFor('{ "prop2": |', schema, { items: [ { label: '"e1"', documentation: 'E1' }, ] }, {}); }); test('In comment', async function () { await testCompletionsFor('[{ "name": "John", "age": 44 }, { /* | */ }', null, { count: 0 }); await testCompletionsFor('[{ "name": "John", "age": 44 }, {\n // |', null, { count: 0 }); }); test('DoNotSuggest', async function () { const schema: JSONSchema = { type: 'object', properties: { 'prop1': { doNotSuggest: true }, 'prop2': { doNotSuggest: false }, 'prop3': { doNotSuggest: false }, } }; await testCompletionsFor('{ |', schema, { items: [ { label: 'prop2' }, { label: 'prop3' } ] }); }); test('suggestSortText', async function () { const schema: JSONSchema = { type: 'object', properties: { 'prop1': { suggestSortText: 'a' }, 'prop2': { suggestSortText: 'b' }, 'prop3': { doNotSuggest: false }, } }; await testCompletionsFor('{ |', schema, { items: [ { label: 'prop2', sortText: 'b' }, { label: 'prop3', sortText: undefined } ] }); }); test('Primary property', async function () { const schema: JSONSchema = { type: 'object', oneOf: [{ properties: { type: { enum: ['foo'], }, prop1: { enum: ['e1', 'e2'] } } }, { type: 'object', properties: { type: { enum: ['bar'], }, prop1: { enum: ['f1', 'f2'] } } }] }; await testCompletionsFor('{ "type": |', schema, { items: [ { label: '"foo"' }, { label: '"bar"' } ] }); await testCompletionsFor('{ "type": "f|', schema, { items: [ { label: '"foo"' }, { label: '"bar"' } ] }); await testCompletionsFor('{ "type": "foo|"', schema, { items: [ { label: '"foo"' }, { label: '"bar"' } ] }); }); test('Property with values', async function () { const schema: JSONSchema = { type: 'object', properties: { object: { type: 'object' }, string: { type: 'string' }, boolean: { type: 'boolean' }, array: { type: 'array' }, oneEnum: { enum: ['foo'], }, multiEnum: { enum: ['foo', 'bar'], }, default: { default: 'foo', }, defaultSnippet: { defaultSnippets: [{ body: 'foo' }] }, defaultSnippets: { defaultSnippets: [{ body: 'foo' }, { body: 'bar' }] }, snippetAndEnum: { defaultSnippets: [{ body: 'foo' }], enum: ['foo', 'bar'] }, defaultAndEnum: { default: 'foo', enum: ['foo', 'bar'] }, } }; await testCompletionsFor('{ |', schema, { items: [ { label: 'object', resultText: '{ "object": {$1}' }, { label: 'array', resultText: '{ "array": [$1]' }, { label: 'string', resultText: '{ "string": "$1"' }, { label: 'boolean', resultText: '{ "boolean": $1' }, { label: 'oneEnum', resultText: '{ "oneEnum": "${1:foo}"' }, { label: 'multiEnum', resultText: '{ "multiEnum": $1' }, { label: 'default', resultText: '{ "default": "${1:foo}"' }, { label: 'defaultSnippet', resultText: '{ "defaultSnippet": "foo"' }, { label: 'defaultSnippets', resultText: '{ "defaultSnippets": $1' }, { label: 'snippetAndEnum', resultText: '{ "snippetAndEnum": $1' }, { label: 'defaultAndEnum', resultText: '{ "defaultAndEnum": $1' } ] }); }); test("if then and else", async function () { await testCompletionsFor("{|}", { if: { properties: { a: { type: "string" } } } }, { count: 1, items: [{ label: "a", resultText: '{"a": "$1"}' }] }); await testCompletionsFor("{|}", { if: { properties: { a: { type: "string" } }, required: ["a"] }, then: { properties: { b: { type: "string" } } }, properties: { c: { type: "string" } } }, { count: 2, items: [{ label: "a", resultText: '{"a": "$1"}' }, { label: "c", resultText: '{"c": "$1"}' }] }); await testCompletionsFor('{"a":"test",|}', { if: { properties: { a: { type: "string" } }, required: ["a"] }, then: { properties: { b: { type: "string" } } }, else: { properties: { c: { type: "string" } } } }, { count: 1, items: [{ label: "b", resultText: '{"a":"test","b": "$1"}' }] }); await testCompletionsFor('{"a":"test",|}', { if: { properties: { a: { type: "string" } }, required: ["a"] }, then: { properties: { b: { type: "string" } } } }, { count: 1, items: [{ label: "b", resultText: '{"a":"test","b": "$1"}' }] }); }); test('Filering same label, issue #1062', async function () { const schema: JSONSchema = { "type": "array", "items": { "enum": [ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg1", "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg2", "_abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg1", "_abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg2" ] } }; await testCompletionsFor('[ |', schema, { count: 4, items: [ { label: '"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd...', resultText: '[ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg1"' }, { label: '"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg2"', resultText: '[ "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg2"' }, { label: '"_abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc...', resultText: '[ "_abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg1"' }, { label: '"_abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg2"', resultText: '[ "_abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg2"' } ] }); }); }); vscode-json-languageservice-5.7.2/src/test/documentSymbols.test.ts000066400000000000000000000250071514707600300254130ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as JsonSchema from '../jsonSchema'; import { getLanguageService, ClientCapabilities, DocumentSymbolsContext, TextDocument, Color, SymbolInformation, SymbolKind, Range, Position, TextEdit, DocumentSymbol } from "../jsonLanguageService"; import { colorFrom256RGB } from '../utils/colors'; suite('JSON Document Symbols', () => { const schemaRequestService = function (uri: string): Promise { return Promise.reject('Resource not found'); }; function getFlatOutline(value: string, context?: { resultLimit?: number }): SymbolInformation[] { const uri = 'test://test.json'; const ls = getLanguageService({ schemaRequestService, clientCapabilities: ClientCapabilities.LATEST }); const document = TextDocument.create(uri, 'json', 0, value); const jsonDoc = ls.parseJSONDocument(document); return ls.findDocumentSymbols(document, jsonDoc, context); } function getHierarchicalOutline(value: string, context?: { resultLimit?: number }): DocumentSymbol[] { const uri = 'test://test.json'; const ls = getLanguageService({ schemaRequestService, clientCapabilities: ClientCapabilities.LATEST }); const document = TextDocument.create(uri, 'json', 0, value); const jsonDoc = ls.parseJSONDocument(document); return ls.findDocumentSymbols2(document, jsonDoc, context); } function assertColors(value: string, schema: JsonSchema.JSONSchema, expectedOffsets: number[], expectedColors: Color[]): PromiseLike { const uri = 'test://test.json'; const schemaUri = "http://myschemastore/test1"; const ls = getLanguageService({ schemaRequestService, clientCapabilities: ClientCapabilities.LATEST }); ls.configure({ schemas: [{ fileMatch: ["*.json"], uri: schemaUri, schema }] }); const document = TextDocument.create(uri, 'json', 0, value); const jsonDoc = ls.parseJSONDocument(document); return ls.findDocumentColors(document, jsonDoc).then(colorInfos => { const actualOffsets = colorInfos.map(r => document.offsetAt(r.range.start)); assert.deepEqual(actualOffsets, expectedOffsets); const actualColors = colorInfos.map(r => r.color); assert.deepEqual(actualColors, expectedColors); }); } function assertColorPresentations(color: Color, ...expected: string[]) { const ls = getLanguageService({ schemaRequestService, clientCapabilities: ClientCapabilities.LATEST }); const document = TextDocument.create('test://test/test.css', 'css', 0, ''); const doc = ls.parseJSONDocument(document); const range = Range.create(Position.create(0, 0), Position.create(0, 1)); const result = ls.getColorPresentations(document, doc, color, range); assert.deepEqual(result.map(r => r.label), expected); assert.deepEqual(result.map(r => r.textEdit), expected.map(l => TextEdit.replace(range, JSON.stringify(l)))); } function assertOutline(value: string, expected: any[], message?: string) { const actual = getFlatOutline(value); assert.equal(actual.length, expected.length, message); for (let i = 0; i < expected.length; i++) { assert.equal(actual[i].name, expected[i].label, message); assert.equal(actual[i].kind, expected[i].kind, message); } } interface ExpectedDocumentSymbol { label: string; kind: SymbolKind; detail: string | undefined; children: ExpectedDocumentSymbol[]; } function assertHierarchicalOutline(value: string, expected: ExpectedDocumentSymbol[], message?: string) { function assertDocumentSymbols(actuals: DocumentSymbol[], expected: ExpectedDocumentSymbol[]) { assert.equal(actuals.length, expected.length, message); for (let i = 0; i < expected.length; i++) { assert.equal(actuals[i].name, expected[i].label, message); assert.equal(actuals[i].kind, expected[i].kind, message); assert.equal(actuals[i].detail, expected[i].detail, message); assertDocumentSymbols(actuals[i].children!, expected[i].children); } } const actual = getHierarchicalOutline(value); assertDocumentSymbols(actual, expected); assert.equal(actual.length, expected.length, message); for (let i = 0; i < expected.length; i++) { assert.equal(actual[i].name, expected[i].label, message); assert.equal(actual[i].kind, expected[i].kind, message); assert.equal(actual[i].kind, expected[i].kind, message); } } test('Outline - Base types', function () { const content = '{ "key1": 1, "key2": "foo", "key3" : true }'; const expected = [ { label: 'key1', kind: SymbolKind.Number }, { label: 'key2', kind: SymbolKind.String }, { label: 'key3', kind: SymbolKind.Boolean }, ]; assertOutline(content, expected); }); test('Outline - Arrays', function () { const content = '{ "key1": 1, "key2": [ 1, 2, 3 ], "key3" : [ { "k1": 1 }, {"k2": 2 } ] }'; const expected = [ { label: 'key1', kind: SymbolKind.Number }, { label: 'key2', kind: SymbolKind.Array }, { label: 'key3', kind: SymbolKind.Array }, { label: 'k1', kind: SymbolKind.Number }, { label: 'k2', kind: SymbolKind.Number } ]; assertOutline(content, expected); }); test('Outline - Objects', function () { const content = '{ "key1": { "key2": true }, "key3" : { "k1": { } }'; const expected = [ { label: 'key1', kind: SymbolKind.Module }, { label: 'key3', kind: SymbolKind.Module }, { label: 'key2', kind: SymbolKind.Boolean }, { label: 'k1', kind: SymbolKind.Module } ]; assertOutline(content, expected); }); test('Outline - object with syntax error', function () { const content = '{ "key1": { "key2": true, "key3":, "key4": false } }'; const expected = [ { label: 'key1', kind: SymbolKind.Module }, { label: 'key2', kind: SymbolKind.Boolean }, { label: 'key4', kind: SymbolKind.Boolean }, ]; assertOutline(content, expected); }); test('Outline - empty name', function () { const content = '{ "": 1, " ": 2 }'; const expected = [ { label: '""', kind: SymbolKind.Number }, { label: '" "', kind: SymbolKind.Number } ]; assertOutline(content, expected); const expected2: ExpectedDocumentSymbol[] = [ { label: '""', kind: SymbolKind.Number, detail: '1', children: [] }, { label: '" "', kind: SymbolKind.Number, detail: '2', children: [] } ]; assertHierarchicalOutline(content, expected2); }); test('Outline - new line in name', function () { const content = '{ "1\\n2": 1 }'; const expected = [ { label: '1↵2', kind: SymbolKind.Number } ]; assertOutline(content, expected); const expected2: ExpectedDocumentSymbol[] = [ { label: '1↵2', kind: SymbolKind.Number, detail: '1', children: [] } ]; assertHierarchicalOutline(content, expected2); }); test('Hierarchical Outline - Object', function () { const content = '{ "key1": { "key2": true }, "key3" : { "k1": { } }'; const expected: ExpectedDocumentSymbol[] = [ { label: 'key1', kind: SymbolKind.Module, detail: undefined, children: [{ label: 'key2', kind: SymbolKind.Boolean, detail: 'true', children: [] }] }, { label: 'key3', kind: SymbolKind.Module, detail: undefined, children: [{ label: 'k1', kind: SymbolKind.Module, detail: '{}', children: [] }] } ]; assertHierarchicalOutline(content, expected); }); test('Hierarchical Outline - Array', function () { const content = '{ "key1": [ { "key2": true }, { "k1": [] } ]}'; const expected: ExpectedDocumentSymbol[] = [ { label: 'key1', kind: SymbolKind.Array, detail: undefined, children: [ { label: '0', kind: SymbolKind.Module, detail: undefined, children: [{ label: 'key2', kind: SymbolKind.Boolean, detail: 'true', children: [] }] }, { label: '1', kind: SymbolKind.Module, detail: undefined, children: [{ label: 'k1', kind: SymbolKind.Array, detail: '[]', children: [] }] }] } ]; assertHierarchicalOutline(content, expected); }); test('Outline - limit 1', function () { let content = '{'; for (let i = 0; i < 100; i++) { content += `"prop${i}": ${i},`; } content += '}'; let exceededUris: string[] = []; const context: DocumentSymbolsContext = { resultLimit: 10, onResultLimitExceeded: (uri: string) => exceededUris.push(uri) }; const flatOutline = getFlatOutline(content, context); assert.equal(flatOutline.length, 10, 'flat'); assert.equal(exceededUris.length, 1); exceededUris = []; const hierarchicalOutline = getHierarchicalOutline(content, context); assert.equal(hierarchicalOutline.length, 10, 'hierarchical'); assert.equal(exceededUris.length, 1); }); test('Outline - limit 2', function () { let content = '['; for (let i = 0; i < 10; i++) { content += '{'; for (let k = 0; k < 10; k++) { content += `"${i}-${k}": ${k},`; } content += '},'; } content += ']'; let exceededUris: string[] = []; const context: DocumentSymbolsContext = { resultLimit: 25, onResultLimitExceeded: (uri: string) => exceededUris.push(uri) }; const flatOutline = getFlatOutline(content, context); assert.equal(flatOutline.length, 25, 'flat'); assert.equal(flatOutline.map(s => s.name).join(','), '0-0,0-1,0-2,0-3,0-4,0-5,0-6,0-7,0-8,0-9,1-0,1-1,1-2,1-3,1-4,1-5,1-6,1-7,1-8,1-9,2-0,2-1,2-2,2-3,2-4'); assert.equal(exceededUris.length, 1); exceededUris = []; const hierarchicalOutline = getHierarchicalOutline(content, context); assert.equal(hierarchicalOutline.length, 10, 'hierarchical'); assert.equal(hierarchicalOutline[0].children!.length, 10, 'hierarchical children of first'); assert.equal(hierarchicalOutline[1].children!.length, 5, 'hierarchical children of second'); assert.equal(hierarchicalOutline[2].children!.length, 0, 'hierarchical children of third'); assert.equal(exceededUris.length, 1); }); test('Colors', async function () { const content = '{ "a": "#FF00FF", "b": "#FF0000" }'; const schema: JsonSchema.JSONSchema = { type: 'object', description: 'a very special object', properties: { 'a': { type: 'number', description: 'A', format: 'color' }, 'b': { type: 'string', description: 'B', format: 'color' } } }; const expectedOffsets = [7, 23]; const expectedColors = [colorFrom256RGB(255, 0, 255), colorFrom256RGB(255, 0, 0)]; return assertColors(content, schema, expectedOffsets, expectedColors); }); test('color presentations', function () { assertColorPresentations(colorFrom256RGB(255, 0, 0), '#ff0000'); assertColorPresentations(colorFrom256RGB(77, 33, 111, 0.5), '#4d216f80'); }); }); vscode-json-languageservice-5.7.2/src/test/fixtures/000077500000000000000000000000001514707600300225435ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/src/test/fixtures/Microsoft.Authorization.json000066400000000000000000000030301514707600300302360ustar00rootroot00000000000000{ "id": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Microsoft.Authorization", "description": "Microsoft Microsoft.Authorization Resource Types", "definitions": { "locks": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Authorization/locks" ] }, "apiVersion": { "enum": [ "2015-01-01" ] }, "name": { "type": "string", "maxLength": 64, "description": "Name of the lock" }, "dependsOn": { "type": "array", "items": { "type": "string" }, "description": "Collection of resources this resource depends on" }, "properties": { "type": "object", "properties": { "level": { "enum": [ "CannotDelete", "ReadOnly" ], "description": "Microsoft.Authorization/locks: level - specifies the type of lock to apply to the scope. CanNotDelete allows modification but prevents deletion, ReadOnly prevents modification or deletion." }, "notes": { "type": "string", "maxLength": 512, "description": "Microsoft.Authorization/locks: notes - user defined notes for the lock" } }, "required": [ "level" ] } }, "required": [ "name", "type", "apiVersion", "properties" ], "description": "Microsoft.Authorization/locks resource" } } }vscode-json-languageservice-5.7.2/src/test/fixtures/Microsoft.Compute.json000066400000000000000000000452061514707600300270250ustar00rootroot00000000000000{ "id": "http://schema.management.azure.com/schemas/2015-08-01/Microsoft.Compute.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Microsoft.Compute", "description": "Microsoft Compute Resource Types", "resourceDefinitions": { "availabilitySets": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Compute/availabilitySets" ] }, "apiVersion": { "enum": [ "2015-05-01-preview", "2015-06-15" ] }, "properties": { "type": "object", "properties": { "platformUpdateDomainCount": { "type": "number" }, "platformFaultDomainCount": { "type": "number" } } } }, "required": [ "type", "apiVersion", "properties" ] }, "virtualMachines": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Compute/virtualMachines" ] }, "apiVersion": { "enum": [ "2015-05-01-preview", "2015-06-15" ] }, "properties": { "properties": { "availabilitySet": { "$ref": "#/definitions/id" }, "hardwareProfile": { "$ref": "#/definitions/hardwareProfile" }, "storageProfile": { "$ref": "#/definitions/storageProfile" }, "osProfile": { "$ref": "#/definitions/osProfile" }, "networkProfile": { "$ref": "#/definitions/networkProfile" } }, "type": "object", "required": [ "hardwareProfile", "storageProfile", "networkProfile" ] }, "resources": { "type": "array", "items": { "allOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/resourceBase" }, { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json#/definitions/locks" }, { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json#/definitions/links" }, { "$ref": "#/resourceDefinitions/extensionsChild" } ] } ] }, "description": "Microsoft.Compute/virtualMachines: Resource Definition for Virtual Machines." } }, "required": [ "type", "apiVersion", "properties" ] }, "virtualMachineScaleSets": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Compute/virtualMachineScaleSets" ] }, "apiVersion": { "enum": [ "2015-05-01-preview", "2015-06-15" ] }, "sku": { "$ref": "#/definitions/sku" }, "properties": { "properties": { "upgradePolicy": { "$ref": "#/definitions/upgradePolicy" }, "virtualMachineProfile": { "$ref": "#/definitions/virtualMachineProfile" } }, "type": "object", "required": [ "upgradePolicy", "virtualMachineProfile" ] } }, "required": [ "sku", "properties" ] }, "extensions": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Compute/virtualMachines/extensions" ] }, "apiVersion": { "enum": [ "2015-05-01-preview", "2015-06-15" ] }, "properties": { "properties": { "publisher": { "type": "string" }, "type": { "type": "string" }, "typeHandlerVersion": { "type": "string" }, "settings": { "type": "object" } }, "type": "object", "required": [ "publisher", "type", "typeHandlerVersion", "settings" ] } }, "required": [ "type", "apiVersion", "properties" ] }, "extensionsChild": { "type": "object", "properties": { "type": { "enum": [ "extensions" ] }, "apiVersion": { "enum": [ "2015-05-01-preview", "2015-06-15" ] }, "properties": { "properties": { "publisher": { "type": "string" }, "type": { "type": "string" }, "typeHandlerVersion": { "type": "string" }, "settings": { "type": "object" } }, "type": "object", "required": [ "publisher", "type", "typeHandlerVersion", "settings" ] } }, "required": [ "type", "apiVersion", "properties" ] } }, "definitions": { "id": { "properties": { "id": { "type": "string" } }, "type": "object", "required": [ "id" ] }, "networkInterfaces": { "properties": { "id": { "type": "string" }, "properties": { "properties": { "primary": { "type": "boolean" } }, "type": "object", "required": [ "primary" ] } }, "type": "object", "required": [ "id" ] }, "hardwareProfile": { "properties": { "vmSize": { "type": "string" } }, "type": "object", "required": [ "vmSize" ] }, "imageReference": { "properties": { "publisher": { "type": "string" }, "offer": { "type": "string" }, "sku": { "type": "string" }, "version": { "type": "string", "default": "latest" } }, "type": "object", "required": [ "publisher", "offer", "sku", "version" ] }, "vhd": { "properties": { "uri": { "type": "string" } }, "type": "object", "required": [ "uri" ] }, "osDisk": { "properties": { "osType": { "type": "string" }, "name": { "type": "string" }, "vhd": { "$ref": "#/definitions/vhd" }, "image": { "$ref": "#/definitions/vhd" }, "caching": { "type": "string" }, "createOption": { "type": "string" } }, "type": "object", "required": [ "name", "vhd", "createOption" ] }, "vhdUri": { "properties": { "uri": { "type": "string" } }, "type": "object", "required": [ "uri" ] }, "dataDisk": { "properties": { "name": { "type": "string" }, "diskSizeGB": { "type": "string" }, "lun": { "type": "number" }, "vhd": { "$ref": "#/definitions/vhdUri" }, "caching": { "type": "string" }, "createOption": { "type": "string" } }, "type": "object", "required": [ "name", "lun", "vhd", "createOption" ] }, "storageProfile": { "properties": { "imageReference": { "$ref": "#/definitions/imageReference" }, "osDisk": { "$ref": "#/definitions/osDisk" }, "dataDisks": { "type": "array", "items": { "$ref": "#/definitions/dataDisk" } } }, "type": "object", "required": [ "osDisk" ] }, "winRMListener": { "properties": { "protocol": { "oneOf": [ { "enum": [ "http", "https" ] }, { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" } ] }, "certificateUrl": { "type": "string" } }, "type": "object", "required": [ "protocol", "certificateUrl" ] }, "winRM": { "properties": { "listeners": { "type": "array", "items": { "$ref": "#/definitions/winRMListener" } } }, "type": "object", "required": [ "listeners" ] }, "additionalUnattendContent": { "properties": { "pass": { "type": "string" }, "component": { "type": "string" }, "settingName": { "type": "string" }, "content": { "type": "string" } }, "type": "object", "required": [ "pass", "component", "settingName", "content" ] }, "windowsConfiguration": { "properties": { "provisionVMAgent": { "type": "boolean" }, "winRM": { "$ref": "#/definitions/winRM" }, "additionalUnattendContent": { "type": "array", "items": { "$ref": "#/definitions/additionalUnattendContent" } }, "enableAutomaticUpdates": { "type": "boolean" } }, "type": "object" }, "publicKey": { "properties": { "path": { "type": "string" }, "keyData": { "type": "string" } }, "type": "object" }, "ssh": { "properties": { "publicKeys": { "type": "array", "items": { "$ref": "#/definitions/publicKey" } } }, "type": "object" }, "linuxConfiguration": { "properties": { "disablePasswordAuthentication": { "type": "string" }, "ssh": { "$ref": "#/definitions/ssh" } }, "type": "object" }, "vaultCertificateUrl": { "properties": { "certificateUrl": { "type": "string" } }, "type": "object", "required": [ "certificateUrl" ] }, "secret": { "properties": { "sourceVault": { "$ref": "#/definitions/id" }, "vaultCertificates": { "type": "array", "items": { "$ref": "#/definitions/vaultCertificateUrl" } } }, "type": "object", "required": [ "sourceVault", "vaultCertificates" ] }, "osProfile": { "properties": { "computerName": { "type": "string" }, "adminUsername": { "type": "string" }, "adminPassword": { "type": "string" }, "customData": { "type": "string" }, "windowsConfiguration": { "$ref": "#/definitions/windowsConfiguration" }, "linuxConfiguration": { "$ref": "#/definitions/linuxConfiguration" }, "secrets": { "type": "array", "items": { "$ref": "#/definitions/secret" } } }, "type": "object", "required": [ "computerName", "adminUsername", "adminPassword" ] }, "networkProfile": { "properties": { "networkInterfaces": { "type": "array", "items": { "$ref": "#/definitions/networkInterfaces" } } }, "type": "object", "required": [ "networkInterfaces" ] }, "sku": { "properties": { "name": { "type": "string" }, "tier": { "type": "string" }, "capacity": { "type": "integer" } }, "type": "object", "required": [ "name", "capacity" ] }, "upgradePolicy": { "properties": { "mode": { "type": "string" } }, "type": "object", "required": [ "mode" ] }, "virtualMachineScaleSetOsProfile": { "properties": { "computerNamePrefix": { "type": "string" }, "adminUsername": { "type": "string" }, "adminPassword": { "type": "string" }, "customData": { "type": "string" }, "windowsConfiguration": { "$ref": "#/definitions/windowsConfiguration" }, "linuxConfiguration": { "$ref": "#/definitions/linuxConfiguration" }, "secrets": { "type": "array", "items": { "$ref": "#/definitions/secret" } } }, "type": "object", "required": [ "computerNamePrefix", "adminUsername", "adminPassword" ] }, "virtualMachineScaleSetOSDisk": { "properties": { "osType": { "type": "string" }, "name": { "type": "string" }, "vhdContainers": { "type": "array", "items": { "type": "string" } }, "caching": { "type": "string" }, "createOption": { "type": "string" } }, "type": "object", "required": [ "name", "createOption" ] }, "virtualMachineScaleSetStorageProfile": { "properties": { "imageReference": { "oneOf": [ { "$ref": "#/definitions/imageReference" }, { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" } ] }, "osDisk": { "$ref": "#/definitions/virtualMachineScaleSetOSDisk" } }, "type": "object", "required": [ "osDisk" ] }, "virtualMachineScaleSetExtension": { "properties": { "name": { "type": "string" }, "properties": { "properties": { "publisher": { "type": "string" }, "type": { "type": "string" }, "typeHandlerVersion": { "type": "string" }, "settings": { "type": "object" } }, "type": "object", "required": [ "publisher", "type", "typeHandlerVersion" ] } } }, "virtualMachineScaleSetExtensionProfile": { "properties": { "extensions": { "type": "array", "items": { "$ref": "#/definitions/virtualMachineScaleSetExtension" } } }, "type": "object" }, "ipConfiguration": { "properties": { "name": { "type": "string" }, "subnet": { "$ref": "#/definitions/id" }, "loadBalancerBackendAddressPools": { "type": "array", "items": { "$ref": "#/definitions/id" } } }, "type": "object", "required": [ "name", "properties" ] }, "networkInterfaceConfiguration": { "properties": { "name": { "type": "string" }, "properties": { "type": "object", "properties": { "primary": { "type": "boolean" }, "ipConfigurations": { "type": "array", "items": { "$ref": "#/definitions/ipConfiguration" } } }, "required": [ "primary", "ipConfigurations" ] } }, "type": "object", "required": [ "name", "properties" ] }, "virtualMachineScaleSetNetworkProfile": { "properties": { "networkInterfaceConfigurations": { "type": "array", "items": { "$ref": "#/definitions/networkInterfaceConfiguration" } } }, "type": "object", "required": [ "networkInterfaceConfigurations" ] }, "virtualMachineProfile": { "properties": { "osProfile": { "$ref": "#/definitions/virtualMachineScaleSetOsProfile" }, "storageProfile": { "$ref": "#/definitions/virtualMachineScaleSetStorageProfile" }, "extensionProfile": { "$ref": "#/definitions/virtualMachineScaleSetExtensionProfile" }, "networkProfile": { "$ref": "#/definitions/virtualMachineScaleSetNetworkProfile" } }, "type": "object", "required": [ "osProfile", "storageProfile", "networkProfile" ] } } } vscode-json-languageservice-5.7.2/src/test/fixtures/Microsoft.Resources.json000066400000000000000000000076321514707600300273640ustar00rootroot00000000000000{ "id": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Microsoft.Resources", "description": "Microsoft Resources Resource Types", "definitions": { "deployments": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Resources/deployments" ] }, "apiVersion": { "enum": [ "2015-01-01" ] }, "name": { "type": "string", "description": "Name of the deployment" }, "dependsOn": { "type": "array", "items": { "type": "string" }, "description": "Collection of resources this deployment depends on" }, "properties": { "allOf": [ { "type": "object", "properties": { "mode": { "enum": [ "Incremental" ], "description": "Deployment mode" } }, "required": [ "mode" ] }, { "anyOf": [ { "type": "object", "properties": { "templateLink": { "$ref": "#/definitions/templateLink" } } }, { "type": "object", "properties": { "template": { "type": "string" } } } ] }, { "anyOf": [ { "type": "object", "properties": { "parametersLink": { "$ref": "#/definitions/parametersLink" } } }, { "type": "object", "properties": { "parameters": { "type": "object", "additionalProperties": { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#/definitions/parameter" } } } } ] } ] } }, "required": [ "type", "apiVersion", "name", "properties" ] }, "templateLink": { "type": "object", "properties": { "uri": { "type": "string", "description": "URI referencing the deployment template" }, "contentVersion": { "type": "string", "pattern": "(^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)", "description": "If included it must match the contentVersion in the template" } }, "required": [ "uri" ], "description": "Template file reference in a deployment" }, "parametersLink": { "type": "object", "properties": { "uri": { "type": "string", "description": "URI referencing the deployment template parameters" }, "contentVersion": { "type": "string", "pattern": "(^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)", "description": "If included it must match the contentVersion in the parameters file" } }, "required": [ "uri" ], "description": "Parameter file reference in a deployment" }, "links": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Resources/links" ] }, "apiVersion": { "enum": [ "2015-01-01" ] }, "name": { "type": "string", "maxLength": 64, "description": "Name of the link" }, "dependsOn": { "type": "array", "items": { "type": "string" }, "description": "Collection of resources this link depends on" }, "properties": { "type": "object", "properties": { "targetId": { "type": "string", "description": "Target resource id to link to" }, "notes": { "type": "string", "maxLength": 512, "description": "Notes for this link" } }, "required": [ "targetId" ] } }, "required": [ "type", "apiVersion", "name", "properties" ] } } }vscode-json-languageservice-5.7.2/src/test/fixtures/Microsoft.Sql.json000066400000000000000000000424771514707600300261570ustar00rootroot00000000000000{ "id": "http://schema.management.azure.com/schemas/2014-04-01-preview/Microsoft.Sql.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Microsoft.SQLDatabase", "description": "Microsoft SQL Database Resource Types", "definitions": { "servers": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Sql/servers" ] }, "apiVersion": { "enum": [ "2014-04-01-preview" ] }, "properties": { "type": "object", "properties": { "version": { "enum": [ "2.0", "12.0" ], "description": "Microsoft.Sql/server: Azure SQL DB server version" }, "administratorLogin": { "type": "string", "description": "Microsoft.Sql/server: administrator login name" }, "administratorLoginPassword": { "type": "string", "description": "Microsoft.Sql/server: administrator login password" } }, "required": [ "administratorLogin", "administratorLoginPassword" ] }, "resources": { "type": "array", "items": { "allOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/resourceBase" }, { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json#/definitions/locks" }, { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json#/definitions/links" }, { "$ref": "#/definitions/databasesChild" }, { "$ref": "#/definitions/firewallrulesChild" } ] } ] }, "description": "Microsoft.Sql/servers: Child resources to define databases and firewall rules." } }, "required": [ "type", "apiVersion", "properties" ] }, "databasesBaseCommon": { "type": "object", "properties": { "edition": { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" }, { "enum": [ "Web", "Business", "Basic", "Standard", "Premium" ] } ], "description": "Microsoft.Sql/server/databases: Optional. Edition of the database to be created. If omitted, the default is Web on server version 2.0 or Standard on server version 12.0." }, "collation": { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" }, { "enum": [ "SQL_Latin1_General_Cp437_CS_AS_KI_WI", "SQL_Latin1_General_Cp437_CI_AS_KI_WI", "SQL_Latin1_General_Pref_Cp437_CI_AS_KI_WI", "SQL_Latin1_General_Cp437_CI_AI_KI_WI", "SQL_Latin1_General_Cp437_BIN", "SQL_Latin1_General_Cp850_BIN", "SQL_Latin1_General_Cp850_CS_AS_KI_WI", "SQL_Latin1_General_Cp850_CI_AS_KI_WI", "SQL_Latin1_General_Cp850_CI_AI_KI_WI", "SQL_Latin1_General_Pref_Cp850_CI_AS_KI_WI", "SQL_1xCompat_Cp850_CI_AS_KI_WI", "SQL_Latin1_General_Cp1_CS_AS_KI_WI", "SQL_Latin1_General_Cp1_CI_AS_KI_WI", "SQL_Latin1_General_Pref_Cp1_CI_AS_KI_WI", "SQL_Latin1_General_Cp1_CI_AI_KI_WI", "SQL_AltDiction_Cp850_CS_AS_KI_WI", "SQL_AltDiction_Pref_Cp850_CI_AS_KI_WI", "SQL_AltDiction_Cp850_CI_AI_KI_WI", "SQL_Scandainavian_Pref_Cp850_CI_AS_KI_WI", "SQL_Scandainavian_Cp850_CS_AS_KI_WI", "SQL_Scandainavian_Cp850_CI_AS_KI_WI", "SQL_AltDiction_Cp850_CI_AS_KI_WI", "SQL_Latin1_General_1250_BIN", "SQL_Latin1_General_Cp1250_CS_AS_KI_WI", "SQL_Latin1_General_Cp1250_CI_AS_KI_WI", "SQL_Czech_Cp1250_CS_AS_KI_WI", "SQL_Czech_Cp1250_CI_AS_KI_WI", "SQL_Hungarian_Cp1250_CS_AS_KI_WI", "SQL_Hungarian_Cp1250_CI_AS_KI_WI", "SQL_Polish_Cp1250_CS_AS_KI_WI", "SQL_Polish_Cp1250_CI_AS_KI_WI", "SQL_Romanian_Cp1250_CS_AS_KI_WI", "SQL_Romanian_Cp1250_CI_AS_KI_WI", "SQL_Croatian_Cp1250_CS_AS_KI_WI", "SQL_Croatian_Cp1250_CI_AS_KI_WI", "SQL_Slovak_Cp1250_CS_AS_KI_WI", "SQL_Slovak_Cp1250_CI_AS_KI_WI", "SQL_Slovenian_Cp1250_CS_AS_KI_WI", "SQL_Slovenian_Cp1250_CI_AS_KI_WI", "SQL_Latin1_General_1251_BIN", "SQL_Latin1_General_Cp1251_CS_AS_KI_WI", "SQL_Latin1_General_Cp1251_CI_AS_KI_WI", "SQL_Ukrainian_Cp1251_CS_AS_KI_WI", "SQL_Ukrainian_Cp1251_CI_AS_KI_WI", "SQL_Latin1_General_1253_BIN", "SQL_Latin1_General_Cp1253_CS_AS_KI_WI", "SQL_Latin1_General_Cp1253_CI_AS_KI_WI", "SQL_Latin1_General_Cp1253_CI_AI_KI_WI", "SQL_Latin1_General_1254_BIN", "SQL_Latin1_General_Cp1254_CS_AS_KI_WI", "SQL_Latin1_General_Cp1254_CI_AS_KI_WI", "SQL_Latin1_General_1255_BIN", "SQL_Latin1_General_Cp1255_CS_AS_KI_WI", "SQL_Latin1_General_Cp1255_CI_AS_KI_WI", "SQL_Latin1_General_1256_BIN", "SQL_Latin1_General_Cp1256_CS_AS_KI_WI", "SQL_Latin1_General_Cp1256_CI_AS_KI_WI", "SQL_Latin1_General_1257_BIN", "SQL_Latin1_General_Cp1257_CS_AS_KI_WI", "SQL_Latin1_General_Cp1257_CI_AS_KI_WI", "SQL_Estonian_Cp1257_CS_AS_KI_WI", "SQL_Estonian_Cp1257_CI_AS_KI_WI", "SQL_Latvian_Cp1257_CS_AS_KI_WI", "SQL_Latvian_Cp1257_CI_AS_KI_WI", "SQL_Lithuanian_Cp1257_CS_AS_KI_WI", "SQL_Lithuanian_Cp1257_CI_AS_KI_WI", "SQL_Danish_Pref_Cp1_CI_AS_KI_WI", "SQL_SwedishPhone_Pref_Cp1_CI_AS_KI_WI", "SQL_SwedishStd_Pref_Cp1_CI_AS_KI_WI", "SQL_Icelandic_Pref_Cp1_CI_AS_KI_WI" ] } ], "description": "Microsoft.Sql/server/databases: Database collation" }, "maxSizeBytes": { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" }, { "enum": [ "104857600", "524288000", "1073741824", "2147483648", "5368709120", "10737418240", "21474836480", "32212254720", "42949672960", "53687091200", "107374182400", "161061273600", "214748364800", "268435456000", "322122547200", "429496729600", "536870912000" ] } ], "description": "Microsoft.Sql/server/databases: Sets the maximum size, in bytes, for the database. This value must be within the range of allowed values for Edition." }, "requestedServiceObjectiveId": { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" }, { "enum": [ "910B4FCB-8A29-4C3E-958F-F7BA794388B2", "DD6D99BB-F193-4EC1-86F2-43D3BCCBC49C", "F1173C43-91BD-4AAA-973C-54E79E15235B", "1B1EBD4D-D903-4BAA-97F9-4EA675F5E928", "455330E1-00CD-488B-B5FA-177C226F28B7", "789681B8-CA10-4EB0-BDF2-E0B050601B40", "7203483A-C4FB-4304-9E9F-17C71C904F5D", "A7D1B92D-C987-4375-B54D-2B1D0E0F5BB0", "A7C4C615-CFB1-464B-B252-925BE0A19446" ] } ], "description": "Microsoft.Sql/server/databases: The GUID corresponding to the performance level for Edition. Shared = 910B4FCB-8A29-4C3E-958F-F7BA794388B2, Basic = DD6D99BB-F193-4EC1-86F2-43D3BCCBC49C, S0 = F1173C43-91BD-4AAA-973C-54E79E15235B, S1 = 1B1EBD4D-D903-4BAA-97F9-4EA675F5E928, S2 = 455330E1-00CD-488B-B5FA-177C226F28B7, S3 = 789681B8-CA10-4EB0-BDF2-E0B050601B40, P1 = 7203483A-C4FB-4304-9E9F-17C71C904F5D, P2 = A7D1B92D-C987-4375-B54D-2B1D0E0F5BB0, P3 = A7C4C615-CFB1-464B-B252-925BE0A19446" } } }, "databasesBaseAll": { "type": "object", "properties": { "createMode": { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" }, { "enum": [ "Copy", "OnlineSecondary", "OfflineSecondary", "Recovery", "PointInTimeRestore", "Restore" ] } ], "description": "Microsoft.Sql/server/databases: Defines that databases is created as a Point-In-Time restoration of another database." }, "sourceDatabaseId": { "type": "string", "description": "Microsoft.Sql/server/databases: The URI of the source database." }, "restorePointInTime": { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/UTC", "description": "Microsoft.Sql/server/databases: The point in time for the restore." }, "sourceDatabaseDeletionDate": { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/UTC", "description": "Microsoft.Sql/server/databases: The deletion date time of the source database." } } }, "databasesBase": { "allOf": [ { "$ref": "#/definitions/databasesBaseCommon" }, { "$ref": "#/definitions/databasesBaseAll" } ] }, "databasesChild": { "type": "object", "properties": { "type": { "enum": [ "databases" ] }, "apiVersion": { "enum": [ "2014-04-01-preview" ] }, "properties": { "$ref": "#/definitions/databasesBase" } }, "required": [ "type", "apiVersion", "properties" ] }, "databases": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Sql/servers/databases" ] }, "apiVersion": { "enum": [ "2014-04-01-preview" ] }, "properties": { "$ref": "#/definitions/databasesBase" } }, "required": [ "type", "apiVersion", "properties" ] }, "firewallrulesBase": { "type": "object", "properties": { "endIpAddress": { "type": "string", "description": "Microsoft.Sql/server/firewallrules: ending IP address" }, "startIpAddress": { "type": "string", "description": "Microsoft.Sql/server/firewallrules: starting IP address" } } }, "firewallrulesChild": { "type": "object", "properties": { "type": { "enum": [ "firewallrules" ] }, "apiVersion": { "enum": [ "2014-04-01-preview" ] }, "properties": { "$ref": "#/definitions/firewallrulesBase" } }, "required": [ "type", "apiVersion", "properties" ] }, "firewallrules": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Sql/servers/firewallrules" ] }, "apiVersion": { "enum": [ "2014-04-01-preview" ] }, "properties": { "$ref": "#/definitions/firewallrulesBase" } }, "required": [ "type", "apiVersion", "properties" ] } } } vscode-json-languageservice-5.7.2/src/test/fixtures/Microsoft.Web.json000066400000000000000000000176011514707600300261240ustar00rootroot00000000000000{ "id": "http://schema.management.azure.com/schemas/2014-06-01/Microsoft.Web.json", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Microsoft.Web", "description": "Microsoft Web Resource Types", "definitions": { "serverfarms": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Web/serverfarms" ] }, "apiVersion": { "enum": [ "2014-06-01" ] }, "properties": { "type": "object", "properties": { "name": { "type": "string", "description": "Microsoft.Web/serverfarms: Name of the server farm." }, "sku": { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" }, { "enum": [ "Free", "Shared", "Basic", "Standard" ] } ], "description": "Microsoft.Web/serverfarms: Server farm sku." }, "workerSize": { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" }, { "enum": [ "Small", "Medium", "Large" ] }, { "type": "integer", "minimum": 0, "maximum": 2 } ], "description": "Microsoft.Web/serverfarms: The instance size." }, "numberOfWorkers": { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" }, { "type": "integer", "minimum": 0, "maximum": 10 } ], "description": "Microsoft.Web/serverfarms: The instance count, which is the number of virtual machines dedicated to the farm. Supported values are 1-10." } } } }, "required": [ "type", "apiVersion", "properties" ] }, "config": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Web/sites/config", "config" ] }, "apiVersion": { "enum": [ "2014-06-01" ] }, "properties": { "type": "object", "properties": { "connectionStrings": { "type": "array", "items": { "type": "object", "properties": { "ConnectionString": { "type": "string", "description": "Microsoft.Web/sites/config: connection string" }, "Name": { "type": "string", "description": "Microsoft.Web/sites/config: connection string name" }, "Type": { "type": "integer", "description": "Microsoft.Web/sites/config: connection string type" } } }, "uniqueItems": true, "description": "Microsoft.Web/sites/config: Connection strings for database and other external resources." }, "phpVersion": { "type": "string", "description": "Microsoft.Web/sites/config: PHP version (an empty string disables PHP)." }, "netFrameworkVersion": { "type": "string", "description": "Microsoft.Web/sites/config: The .Net Framework version." } } } }, "description": "Microsoft.Web/sites: Configuration settings for a web site.", "required": [ "type", "apiVersion", "properties" ] }, "extensions": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Web/sites/extensions", "extensions" ] }, "apiVersion": { "enum": [ "2014-06-01" ] }, "properties": { "type": "object", "properties": { "packageUri": { "type": "string", "description": "Microsoft.Web/sites/extensions: uri of package" }, "dbType": { "type": "string", "description": "Microsoft.Web/sites/extensions: type of database" }, "connectionString": { "type": "string", "description": "Microsoft.Web/sites/extensions: connection string" }, "setParameters": { "type": "object", "description": "Microsoft.Web/sites/extensions: parameters" } } } }, "required": [ "type", "apiVersion", "properties" ] }, "sites": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Web/sites" ] }, "apiVersion": { "enum": [ "2014-06-01" ] }, "properties": { "type": "object", "properties": { "name": { "type": "string", "description": "Microsoft.Web/sites: The name of web site." }, "serverFarm": { "type": "string", "description": "Microsoft.Web/sites: The name of server farm site belongs to." }, "hostnames": { "type": "array", "items": { "type": "string" }, "description": "Microsoft.Web/sites: An array of strings that contains the public hostnames for the site, including custom domains." }, "enabledHostnames": { "type": "array", "items": { "type": "string" }, "description": "Microsoft.Web/sites: An array of strings that contains enabled hostnames for the site. By default, these are .azurewebsites.net and .scm.azurewebsites.net." }, "hostNameSslStates": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "description": "Microsoft.Web/sites/hostNameSslStates: The URL of the web site." }, "sslState": { "oneOf": [ { "enum": [ "Disabled", "IpBasedEnabled", "SniEnabled" ] }, { "type": "integer", "minimum": 0, "maximum": 2 }, { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" } ], "description": "Microsoft.Web/sites/hostNameSslStates. The SSL state." }, "thumbprint": { "type": "string", "description": "Microsoft.Web/sites/hostNameSslStates: A string that contains the thumbprint of the SSL certificate." }, "ipBasedSslState": { "oneOf": [ { "enum": [ "Disabled", "IpBasedEnabled", "SniEnabled" ] }, { "type": "integer", "minimum": 0, "maximum": 2 }, { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" } ], "description": "Microsoft.Web/sites/hostNameSslStates: IP Based SSL state" } } }, "description": "Microsoft.Web/sites: Container for SSL states." } } }, "resources": { "type": "array", "items": { "allOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/resourceBase" }, { "oneOf": [ {"$ref": "#/definitions/config"}, {"$ref": "#/definitions/extensions"} ] } ], "description": "Microsoft.Web/sites: Child resources to define configuration and extensions." } } }, "required": [ "type", "apiVersion", "properties" ] }, "certificates": { "type": "object", "properties": { "type": { "enum": [ "Microsoft.Web/certificates" ] }, "apiVersion": { "enum": [ "2014-06-01" ] }, "properties": { "type": "object", "properties": { "pfxBlob": { "type": "string", "description": "Microsoft.Web/certificates: A base64Binary value that contains the PfxBlob of the certificate." }, "password": { "type": "string", "description": "Microsoft.Web/certficates: A string that contains the password for the certificate." } } } } } } }vscode-json-languageservice-5.7.2/src/test/fixtures/SuccessBricks.ClearDB.json000066400000000000000000000020321514707600300274340ustar00rootroot00000000000000{ "id": "http://schema.management.azure.com/schemas/2014-04-01/SuccessBricks.ClearDB.json", "$schema": "http://json-schema.org/draft-04/schema#", "title": "SuccessBricks.ClearDB", "description": "SuccessBricks ClearDB Resource Types", "definitions": { "databases": { "type":"object", "properties": { "type": { "enum": [ "SuccessBricks.ClearDB/databases" ] }, "apiVersion": { "enum": [ "2014-04-01" ] }, "plan": { "type": "object", "properties": { "name": { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression" }, { "enum": [ "Free", "Jupiter", "Saturn", "Venus" ] } ], "description": "Name of the plan" } }, "required": ["name"], "description": "ClearDB database plan" } }, "required": [ "type", "apiVersion", "plan" ] } } } vscode-json-languageservice-5.7.2/src/test/fixtures/deploymentParameters.json000066400000000000000000000023041514707600300276410ustar00rootroot00000000000000{ "id": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Parameters", "description": "An Azure deployment parameter file", "type": "object", "properties": { "$schema": { "type": "string" }, "contentVersion": { "type": "string", "pattern": "(^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)", "description": "A 4 number format for the version number of this parameter file. For example, 1.0.0.0" }, "parameters": { "type": "object", "additionalProperties": { "$ref": "#/definitions/parameter" }, "description": "Collection of parameters to pass into a template" } }, "required": [ "$schema", "contentVersion", "parameters" ], "definitions": { "parameter": { "type": "object", "properties": { "value": { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/parameterValueTypes", "description": "Input value to template" }, "metadata": { "type": "object", "description": "Client specific metadata" } }, "required": [ "value" ] } } }vscode-json-languageservice-5.7.2/src/test/fixtures/deploymentTemplate.json000066400000000000000000000206631514707600300273210ustar00rootroot00000000000000{ "id": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "$schema": "http://json-schema.org/draft-04/schema#", "title": "Template", "description": "An Azure deployment template", "type": "object", "properties": { "$schema": { "type": "string", "description": "JSON schema reference" }, "contentVersion": { "type": "string", "pattern": "(^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$)", "description": "A 4 number format for the version number of this template file. For example, 1.0.0.0" }, "variables": { "type": "object", "description": "Variable definitions" }, "parameters": { "type": "object", "description": "Input parameter definitions", "additionalProperties": { "$ref": "#/definitions/parameter" } }, "resources": { "type": "array", "description": "Collection of resources to be deployed", "items": { "oneOf": [ { "allOf": [ { "$ref": "#/definitions/resourceBase" }, { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2014-06-01/Microsoft.Web.json#/definitions/certificates" }, { "$ref": "http://schema.management.azure.com/schemas/2014-06-01/Microsoft.Web.json#/definitions/serverfarms" } ] } ] }, { "allOf": [ { "$ref": "#/definitions/resourceBaseForParentResources" }, { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2014-06-01/Microsoft.Web.json#/definitions/sites" }, { "$ref": "http://schema.management.azure.com/schemas/2014-04-01-preview/Microsoft.Sql.json#/definitions/servers" }, { "$ref": "http://schema.management.azure.com/schemas/2015-08-01/Microsoft.Compute.json#/resourceDefinitions/virtualMachines" } ] } ] }, { "allOf": [ { "$ref": "#/definitions/resourceBaseExternal" }, { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2014-04-01/SuccessBricks.ClearDB.json#/definitions/databases" } ] } ] }, { "allOf": [ { "$ref": "#/definitions/ARMResourceBase" }, { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json#/definitions/deployments" }, { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json#/definitions/links" }, { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json#/definitions/locks" } ] } ] } ] }, "minItems": 1 }, "outputs": { "type": "object", "description": "Output parameter definitions", "additionalProperties": { "$ref": "#/definitions/output" } } }, "additionalProperties": false, "required": [ "$schema", "contentVersion", "resources" ], "definitions": { "resourceBase": { "allOf": [ { "$ref": "#/definitions/resourceBaseForParentResources" }, { "properties": { "resources": { "$ref": "#/definitions/childResources" } } } ] }, "ARMResourceBase": { "type": "object", "properties": { "name": { "type": "string", "description": "Name of the resource" }, "type": { "type": "string", "description": "Resource type" }, "apiVersion": { "type": "string", "description": "API Version of the resource type" }, "dependsOn": { "type": "array", "items": { "type": "string" }, "description": "Collection of resources this resource depends on" } }, "required": [ "name", "type", "apiVersion" ] }, "proxyResourceBase": { "allOf": [ { "$ref": "#/definitions/ARMResourceBase" }, { "properties": { "resources": { "$ref": "#/definitions/childResources" }, "location": { "$ref": "#/definitions/resourceLocations", "description": "Location to deploy resource to" } } } ] }, "resourceBaseForParentResources": { "allOf": [ { "$ref": "#/definitions/ARMResourceBase" }, { "properties": { "kind": { "type": "string", "maxLength": 64, "pattern": "(^[a-zA-Z0-9_.()-]+$)", "description": "Kind of resource" }, "location": { "$ref": "#/definitions/resourceLocations", "description": "Location to deploy resource to" }, "tags": { "type": "object", "description": "Name-value pairs to add to the resource" }, "plan": { "$ref": "#/definitions/resourcePlan" } }, "required": [ "location" ] } ] }, "resourceBaseExternal": { "$ref": "#/definitions/resourceBase", "required": [ "plan" ] }, "childResources": { "type": "array", "items": { "$ref": "#/definitions/ARMChildResources" } }, "ARMChildResources": { "oneOf": [ { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json#/definitions/locks" }, { "$ref": "http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json#/definitions/links" } ] }, "resourcePlan": { "type": "object", "properties": { "name": { "type": "string", "description": "Name of the plan" }, "promotionCode": { "type": "string", "description": "Plan promotion code" }, "publisher": { "type": "string", "description": "Name of the publisher" }, "product": { "type": "string", "description": "Name of the product" } }, "required": [ "name" ], "description": "Plan of the resource" }, "resourceLocations": { "anyOf": [ { "type": "string" }, { "enum": [ "East Asia", "Southeast Asia", "Central US", "East US", "East US 2", "West US", "North Central US", "South Central US", "North Europe", "West Europe", "Japan West", "Japan East", "Brazil South", "Australia East", "Australia Southeast", "Central India", "West India", "South India" ] } ] }, "parameter": { "type": "object", "properties": { "type": { "$ref": "#/definitions/parameterTypes", "description": "Type of input parameter" }, "defaultValue": { "$ref": "#/definitions/parameterValueTypes", "description": "Default value to be used if one is not provided" }, "allowedValues": { "type": "array", "description": "Value can only be one of these values" }, "minLength": { "type": "integer", "description": "Minimum number of characters that must be used" }, "maxLength": { "type": "integer", "description": "Maximum number of characters that can be used" }, "metadata": { "type": "object", "description": "Metadata for the parameter" } }, "required": [ "type" ], "description": "Input parameter definitions" }, "output": { "type": "object", "properties": { "type": { "$ref": "#/definitions/parameterTypes", "description": "Type of output value" }, "value": { "$ref": "#/definitions/parameterValueTypes", "description": "Value assigned for output" } }, "required": [ "type", "value" ], "description": "Set of output parameters" }, "parameterTypes": { "enum": [ "string", "securestring", "int", "bool", "object", "array" ] }, "parameterValueTypes": { "type": [ "string", "boolean", "integer", "number", "object", "array", "null" ] }, "expression": { "type": "string", "pattern": "^\\[(concat|parameters|variables|reference|resourceId|resourceGroup|subscription|listKeys|listPackage|base64|providers|copyIndex|padLeft)\\(.*\\).*\\]$", "description": "Deployment template expression. Expressions are enclosed in [] and must start with a function of concat(), parameters(), variables(), reference(), resourceId(), resourceGroup(), subscription(), listKeys(), listPackage(), base64(), providers(), copyIndex(), padLeft()" }, "Iso8601Duration": { "type": "string", "pattern": "^P(\\d+Y)?(\\d+M)?(\\d+D)?(T(((\\d+H)(\\d+M)?(\\d+(\\.\\d{1,2})?S)?)|((\\d+M)(\\d+(\\.\\d{1,2})?S)?)|((\\d+(\\.\\d{1,2})?S))))?$" }, "UTC": { "type": "string", "pattern": "^\\d{4}(-(0[1-9]|1[0-2])(-([012]\\d|3[01])(T((([01]\\d|2[0123]):[0-5]\\d)|(24:00))(:(([0-5]\\d)|60)(\\.\\d{1,}){0,1}){0,1}){0,1}((Z)|([+-]((([01]\\d|2[0123]):[0-5]\\d)|(24:00)))){0,1}){0,1}){0,1}$" }, "apiVersion": { "type": "string", "pattern": "(^((\\d\\d\\d\\d-\\d\\d-\\d\\d)|([0-9]+(\\.[0-9]+)?))(-[a-zA-Z][a-zA-Z0-9]*)?$)", "description": "API version of the resource type" } } } vscode-json-languageservice-5.7.2/src/test/folding.test.ts000066400000000000000000000076561514707600300236600ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import 'mocha'; import * as assert from 'assert'; import { TextDocument, getLanguageService } from '../jsonLanguageService'; interface ExpectedIndentRange { startLine: number; endLine: number; kind?: string; } function assertRanges(lines: string[], expected: ExpectedIndentRange[], nRanges?: number): void { let document = TextDocument.create('test://foo/bar.json', 'json', 1, lines.join('\n')); let actual = getLanguageService({}).getFoldingRanges(document, { rangeLimit: nRanges }); let actualRanges = []; for (let i = 0; i < actual.length; i++) { actualRanges[i] = r(actual[i].startLine, actual[i].endLine, actual[i].kind); } actualRanges = actualRanges.sort((r1, r2) => r1.startLine - r2.startLine); assert.deepEqual(actualRanges, expected); } function r(startLine: number, endLine: number, kind?: string): ExpectedIndentRange { return { startLine, endLine, kind }; } suite('JSON Folding', () => { test('Fold one level', () => { let input = [ /*0*/'{', /*1*/'"foo":"bar"', /*2*/'}' ]; assertRanges(input, [r(0, 1, 'object')]); }); test('Fold two level', () => { let input = [ /*0*/'[', /*1*/'{', /*2*/'"foo":"bar"', /*3*/'}', /*4*/']' ]; assertRanges(input, [r(0, 3, 'array'), r(1, 2, 'object')]); }); test('Fold Arrays', () => { let input = [ /*0*/'[', /*1*/'[', /*2*/'],[', /*3*/'1', /*4*/']', /*5*/']' ]; assertRanges(input, [r(0, 4, 'array'), r(2, 3, 'array')]); }); test('Filter start on same line', () => { let input = [ /*0*/'[[', /*1*/'[', /*2*/'],[', /*3*/'1', /*4*/']', /*5*/']]' ]; assertRanges(input, [r(0, 4, 'array'), r(2, 3, 'array')]); }); test('Fold commment', () => { let input = [ /*0*/'/*', /*1*/' multi line', /*2*/'*/', ]; assertRanges(input, [r(0, 2, 'comment')]); }); test('Incomplete commment', () => { let input = [ /*0*/'/*', /*1*/'{', /*2*/'"foo":"bar"', /*3*/'}', ]; assertRanges(input, [r(1, 2, 'object')]); }); test('Fold regions', () => { let input = [ /*0*/'// #region', /*1*/'{', /*2*/'}', /*3*/'// #endregion', ]; assertRanges(input, [r(0, 3, 'region')]); }); test('Test limit', () => { let input = [ /* 0*/'[', /* 1*/' [', /* 2*/' [', /* 3*/' ', /* 4*/' ],', /* 5*/' [', /* 6*/' [', /* 7*/' ', /* 8*/' ],', /* 9*/' [', /*10*/' ', /*11*/' ],', /*12*/' ],', /*13*/' [', /*14*/' ', /*15*/' ],', /*16*/' [', /*17*/' ', /*18*/' ]', /*19*/' ]', /*20*/']', ]; assertRanges(input, [r(0, 19, 'array'), r(1, 18, 'array'), r(2, 3, 'array'), r(5, 11, 'array'), r(6, 7, 'array'), r(9, 10, 'array'), r(13, 14, 'array'), r(16, 17, 'array')], undefined); assertRanges(input, [r(0, 19, 'array'), r(1, 18, 'array'), r(2, 3, 'array'), r(5, 11, 'array'), r(6, 7, 'array'), r(9, 10, 'array'), r(13, 14, 'array'), r(16, 17, 'array')], 8); assertRanges(input, [r(0, 19, 'array'), r(1, 18, 'array'), r(2, 3, 'array'), r(5, 11, 'array'), r(6, 7, 'array'), r(13, 14, 'array'), r(16, 17, 'array')], 7); assertRanges(input, [r(0, 19, 'array'), r(1, 18, 'array'), r(2, 3, 'array'), r(5, 11, 'array'), r(13, 14, 'array'), r(16, 17, 'array')], 6); assertRanges(input, [r(0, 19, 'array'), r(1, 18, 'array'), r(2, 3, 'array'), r(5, 11, 'array'), r(13, 14, 'array')], 5); assertRanges(input, [r(0, 19, 'array'), r(1, 18, 'array'), r(2, 3, 'array'), r(5, 11, 'array')], 4); assertRanges(input, [r(0, 19, 'array'), r(1, 18, 'array'), r(2, 3, 'array')], 3); assertRanges(input, [r(0, 19, 'array'), r(1, 18, 'array')], 2); assertRanges(input, [r(0, 19, 'array')], 1); }); }); vscode-json-languageservice-5.7.2/src/test/formatter.test.ts000066400000000000000000000210721514707600300242250ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { getLanguageService, ClientCapabilities, Range, TextDocument } from '../jsonLanguageService'; import * as assert from 'assert'; const applyEdits = TextDocument.applyEdits; suite('JSON Formatter', () => { const ls = getLanguageService({ clientCapabilities: ClientCapabilities.LATEST }); function format(unformatted: string, expected: string, insertSpaces = true) { let range: Range | undefined = undefined; const uri = 'test://test.json'; const rangeStart = unformatted.indexOf('|'); const rangeEnd = unformatted.lastIndexOf('|'); if (rangeStart !== -1 && rangeEnd !== -1) { // remove '|' unformatted = unformatted.substring(0, rangeStart) + unformatted.substring(rangeStart + 1, rangeEnd) + unformatted.substring(rangeEnd + 1); const unformattedDoc = TextDocument.create(uri, 'json', 0, unformatted); const startPos = unformattedDoc.positionAt(rangeStart); const endPos = unformattedDoc.positionAt(rangeEnd); range = Range.create(startPos, endPos); } const document = TextDocument.create(uri, 'json', 0, unformatted); const edits = ls.format(document, range, { tabSize: 2, insertSpaces: insertSpaces }); const formatted = applyEdits(document, edits); assert.equal(formatted, expected); } test('object - single property', () => { const content = [ '{"x" : 1}' ].join('\n'); const expected = [ '{', ' "x": 1', '}' ].join('\n'); format(content, expected); }); test('object - multiple properties', () => { const content = [ '{"x" : 1, "y" : "foo", "z" : true}' ].join('\n'); const expected = [ '{', ' "x": 1,', ' "y": "foo",', ' "z": true', '}' ].join('\n'); format(content, expected); }); test('object - no properties ', () => { const content = [ '{"x" : { }, "y" : {}}' ].join('\n'); const expected = [ '{', ' "x": {},', ' "y": {}', '}' ].join('\n'); format(content, expected); }); test('object - nesting', () => { const content = [ '{"x" : { "y" : { "z" : { }}, "a": true}}' ].join('\n'); const expected = [ '{', ' "x": {', ' "y": {', ' "z": {}', ' },', ' "a": true', ' }', '}' ].join('\n'); format(content, expected); }); test('array - single items', () => { const content = [ '["[]"]' ].join('\n'); const expected = [ '[', ' "[]"', ']' ].join('\n'); format(content, expected); }); test('array - multiple items', () => { const content = [ '[true,null,1.2]' ].join('\n'); const expected = [ '[', ' true,', ' null,', ' 1.2', ']' ].join('\n'); format(content, expected); }); test('array - no items', () => { const content = [ '[ ]' ].join('\n'); const expected = [ '[]' ].join('\n'); format(content, expected); }); test('array - nesting', () => { const content = [ '[ [], [ [ {} ], "a" ] ]' ].join('\n'); const expected = [ '[', ' [],', ' [', ' [', ' {}', ' ],', ' "a"', ' ]', ']', ].join('\n'); format(content, expected); }); test('syntax errors', () => { const content = [ '[ null 1.2 "Hello" ]' ].join('\n'); const expected = [ '[', ' null 1.2 "Hello"', ']', ].join('\n'); format(content, expected); }); test('syntax errors 2', () => { const content = [ '{"a":"b""c":"d" }' ].join('\n'); const expected = [ '{', ' "a": "b""c": "d"', '}', ].join('\n'); format(content, expected); }); test('empty lines', () => { const content = [ '{', '"a": true,', '', '"b": true', '}', ].join('\n'); const expected = [ '{', '\t"a": true,', '\t"b": true', '}', ].join('\n'); format(content, expected, false); }); test('single line comment', () => { const content = [ '[ ', '//comment', '"foo", "bar"', '] ' ].join('\n'); const expected = [ '[', ' //comment', ' "foo",', ' "bar"', ']', ].join('\n'); format(content, expected); }); test('block line comment', () => { const content = [ '[{', ' /*comment*/ ', '"foo" : true', '}] ' ].join('\n'); const expected = [ '[', ' {', ' /*comment*/', ' "foo": true', ' }', ']', ].join('\n'); format(content, expected); }); test('single line comment on same line', () => { const content = [ ' { ', ' "a": {}// comment ', ' } ' ].join('\n'); const expected = [ '{', ' "a": {} // comment ', '}', ].join('\n'); format(content, expected); }); test('single line comment on same line 2', () => { const content = [ '{ //comment', '}' ].join('\n'); const expected = [ '{ //comment', '}' ].join('\n'); format(content, expected); }); test('block comment on same line', () => { const content = [ '{ "a": {}, /*comment*/ ', ' /*comment*/ "b": {}, ', ' "c": {/*comment*/} } ', ].join('\n'); const expected = [ '{', ' "a": {}, /*comment*/', ' /*comment*/ "b": {},', ' "c": { /*comment*/}', '}', ].join('\n'); format(content, expected); }); test('block comment on same line advanced', () => { const content = [ ' { "d": [', ' null', ' ] /*comment*/', ' ,"e": /*comment*/ [null] }', ].join('\n'); const expected = [ '{', ' "d": [', ' null', ' ] /*comment*/,', ' "e": /*comment*/ [', ' null', ' ]', '}', ].join('\n'); format(content, expected); }); test('multiple block comments on same line', () => { const content = [ '{ "a": {} /*comment*/, /*comment*/ ', ' /*comment*/ "b": {} /*comment*/ } ' ].join('\n'); const expected = [ '{', ' "a": {} /*comment*/, /*comment*/', ' /*comment*/ "b": {} /*comment*/', '}', ].join('\n'); format(content, expected); }); test('multiple mixed comments on same line', () => { const content = [ '[ /*comment*/ /*comment*/ // comment ', ']' ].join('\n'); const expected = [ '[ /*comment*/ /*comment*/ // comment ', ']' ].join('\n'); format(content, expected); }); test('range', () => { const content = [ '{ "a": {},', '|"b": [null, null]|', '} ' ].join('\n'); const expected = [ '{ "a": {},', '"b": [', ' null,', ' null', ']', '} ', ].join('\n'); format(content, expected); }); test('range with existing indent', () => { const content = [ '{ "a": {},', ' |"b": [null],', '"c": {}', '}|' ].join('\n'); const expected = [ '{ "a": {},', ' "b": [', ' null', ' ],', ' "c": {}', '}', ].join('\n'); format(content, expected); }); test('range with existing indent - tabs', () => { const content = [ '{ "a": {},', '| "b": [null], ', '"c": {}', '}| ' ].join('\n'); const expected = [ '{ "a": {},', '\t"b": [', '\t\tnull', '\t],', '\t"c": {}', '}', ].join('\n'); format(content, expected, false); }); test('property range - issue 14623', () => { const content = [ '{ |"a" :| 1,', ' "b": 1', '}' ].join('\n'); const expected = [ '{ "a": 1,', ' "b": 1', '}' ].join('\n'); format(content, expected, false); }); test('block comment none-line breaking symbols', () => { const content = [ '{ "a": [ 1', '/* comment */', ', 2', '/* comment */', ']', '/* comment */', ',', ' "b": true', '/* comment */', '}' ].join('\n'); const expected = [ '{', ' "a": [', ' 1', ' /* comment */', ' ,', ' 2', ' /* comment */', ' ]', ' /* comment */', ' ,', ' "b": true', ' /* comment */', '}', ].join('\n'); format(content, expected); }); test('line comment after none-line breaking symbols', () => { const content = [ '{ "a":', '// comment', 'null,', ' "b"', '// comment', ': null', '// comment', '}' ].join('\n'); const expected = [ '{', ' "a":', ' // comment', ' null,', ' "b"', ' // comment', ' : null', ' // comment', '}', ].join('\n'); format(content, expected); }); test('random content', () => { const content = [ 'a 1 b 1 3 true' ].join('\n'); const expected = [ 'a 1 b 1 3 true', ].join('\n'); format(content, expected); }); });vscode-json-languageservice-5.7.2/src/test/glob.test.ts000066400000000000000000000363671514707600300231620ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { createRegex } from '../utils/glob'; suite('Glob', () => { test('Glob matching, where fileMatch is a wildcard pattern, contains no double-star, and denotes filename only', async function () { const pattern = '**/*.foo.json'; const positives = ['file:///folder/a.foo.json']; const negatives = ['file:///folder/a.bar.json']; for (const positive of positives) { assertGlobMatch(pattern, positive); } for (const negative of negatives) { assertNoGlobMatch(pattern, negative); } }); test('Glob matching, where fileMatch is a wildcard pattern, contains no double-star, and denotes a path', async function () { const pattern = '**/foo/*/bar.json'; const positives = ['file:///folder/foo/bat/bar.json']; const negatives = ['file:///folder/a.bar.json', 'file:///folder/foo/bar.json', 'file:///folder/foo/can/be/as/deep/as/the/ocean/floor/bar.json', 'file:///folder/foo/bar.json/bat/bar.json', 'file:///folder/foo.bar.json', 'file:///folder/foo.bat/bar.json', 'file:///folder/foo/bar.json/bat.json', 'file:///folder/.foo/bar.json', 'file:///folder/.foo/bat/bar.json', 'file:///folder/.foo/bat/man/bar.json']; for (const positive of positives) { assertGlobMatch(pattern, positive); } for (const negative of negatives) { assertNoGlobMatch(pattern, negative); } }); test('Glob matching, where fileMatch is a wildcard pattern, contains double-star, and denotes a path', async function () { const pattern = '**/foo/**/bar.json'; const positives = ['file:///folder/foo/bar.json', 'file:///folder/foo/bat/bar.json', 'file:///folder/foo/can/be/as/deep/as/the/ocean/floor/bar.json', 'file:///folder/foo/bar.json/bat/bar.json']; const negatives = ['file:///folder/a.bar.json', 'file:///folder/foo.bar.json', 'file:///folder/foo.bat/bar.json', 'file:///folder/foo/bar.json/bat.json', 'file:///folder/.foo/bar.json', 'file:///folder/.foo/bat/bar.json', 'file:///folder/.foo/bat/man/bar.json', 'file:///folder/foo?foo/bat/man/bar.json']; for (const positive of positives) { assertGlobMatch(pattern, positive); } for (const negative of negatives) { assertNoGlobMatch(pattern, negative); } }); function assertMatch(pattern: string, input: string, expected: boolean) { try { const regex = createRegex(pattern, { extended: true, globstar: true }); const result = regex.test(input); if (result !== expected) { assert(false, `pattern: ${pattern}, regex: ${regex.source}, input: ${input}, should match ${expected}`); } } catch (e) { assert(false, `pattern: ${pattern}, input: ${input}, should match ${expected}`); } } function assertGlobMatch(pattern: string, input: string) { assertMatch(pattern, input, true); } function assertNoGlobMatch(pattern: string, input: string) { assertMatch(pattern, input, false); } test('simple', () => { let p = 'node_modules'; assertGlobMatch(p, 'node_modules'); assertNoGlobMatch(p, 'node_module'); assertNoGlobMatch(p, '/node_modules'); assertNoGlobMatch(p, 'test/node_modules'); p = 'test.txt'; assertGlobMatch(p, 'test.txt'); assertNoGlobMatch(p, 'test?txt'); assertNoGlobMatch(p, '/text.txt'); assertNoGlobMatch(p, 'test/test.txt'); p = 'test(.txt'; assertGlobMatch(p, 'test(.txt'); assertNoGlobMatch(p, 'test?txt'); p = 'qunit'; assertGlobMatch(p, 'qunit'); assertNoGlobMatch(p, 'qunit.css'); assertNoGlobMatch(p, 'test/qunit'); // Absolute p = '/DNXConsoleApp/**/*.cs'; assertGlobMatch(p, '/DNXConsoleApp/Program.cs'); assertGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); p = '*'; assertGlobMatch(p, ''); }); test('dot hidden', function () { let p = '.*'; assertGlobMatch(p, '.git'); assertGlobMatch(p, '.hidden.txt'); assertNoGlobMatch(p, 'git'); assertNoGlobMatch(p, 'hidden.txt'); assertNoGlobMatch(p, 'path/.git'); assertNoGlobMatch(p, 'path/.hidden.txt'); p = '**/.*'; assertGlobMatch(p, '.git'); assertGlobMatch(p, '.hidden.txt'); assertNoGlobMatch(p, 'git'); assertNoGlobMatch(p, 'hidden.txt'); assertGlobMatch(p, 'path/.git'); assertGlobMatch(p, 'path/.hidden.txt'); assertNoGlobMatch(p, 'path/git'); assertNoGlobMatch(p, 'pat.h/hidden.txt'); p = '._*'; assertGlobMatch(p, '._git'); assertGlobMatch(p, '._hidden.txt'); assertNoGlobMatch(p, 'git'); assertNoGlobMatch(p, 'hidden.txt'); assertNoGlobMatch(p, 'path/._git'); assertNoGlobMatch(p, 'path/._hidden.txt'); p = '**/._*'; assertGlobMatch(p, '._git'); assertGlobMatch(p, '._hidden.txt'); assertNoGlobMatch(p, 'git'); assertNoGlobMatch(p, 'hidden._txt'); assertGlobMatch(p, 'path/._git'); assertGlobMatch(p, 'path/._hidden.txt'); assertNoGlobMatch(p, 'path/git'); assertNoGlobMatch(p, 'pat.h/hidden._txt'); }); test('file pattern', function () { let p = '*.js'; assertGlobMatch(p, 'foo.js'); assertNoGlobMatch(p, 'folder/foo.js'); assertNoGlobMatch(p, '/node_modules/foo.js'); assertNoGlobMatch(p, 'foo.jss'); assertNoGlobMatch(p, 'some.js/test'); p = 'html.*'; assertGlobMatch(p, 'html.js'); assertGlobMatch(p, 'html.txt'); assertNoGlobMatch(p, 'htm.txt'); p = '*.*'; assertGlobMatch(p, 'html.js'); assertGlobMatch(p, 'html.txt'); assertGlobMatch(p, 'htm.txt'); assertNoGlobMatch(p, 'folder/foo.js'); assertNoGlobMatch(p, '/node_modules/foo.js'); p = 'node_modules/test/*.js'; assertGlobMatch(p, 'node_modules/test/foo.js'); assertNoGlobMatch(p, 'folder/foo.js'); assertNoGlobMatch(p, '/node_module/test/foo.js'); assertNoGlobMatch(p, 'foo.jss'); assertNoGlobMatch(p, 'some.js/test'); }); test('star', () => { let p = 'node*modules'; assertGlobMatch(p, 'node_modules'); assertGlobMatch(p, 'node_super_modules'); assertNoGlobMatch(p, 'node_module'); assertNoGlobMatch(p, '/node_modules'); assertNoGlobMatch(p, 'test/node_modules'); p = '*'; assertGlobMatch(p, 'html.js'); assertGlobMatch(p, 'html.txt'); assertGlobMatch(p, 'htm.txt'); assertNoGlobMatch(p, 'folder/foo.js'); assertNoGlobMatch(p, '/node_modules/foo.js'); }); test('file / folder match', function () { let p = '**/node_modules/**'; assertGlobMatch(p, 'node_modules'); assertGlobMatch(p, 'node_modules/'); assertGlobMatch(p, 'a/node_modules'); assertGlobMatch(p, 'a/node_modules/'); assertGlobMatch(p, 'node_modules/foo'); assertGlobMatch(p, 'foo/node_modules/foo/bar'); }); test('questionmark', () => { let p = 'node?modules'; assertGlobMatch(p, 'node_modules'); assertNoGlobMatch(p, 'node_super_modules'); assertNoGlobMatch(p, 'node_module'); assertNoGlobMatch(p, '/node_modules'); assertNoGlobMatch(p, 'test/node_modules'); p = '?'; assertGlobMatch(p, 'h'); assertNoGlobMatch(p, 'html.txt'); assertNoGlobMatch(p, 'htm.txt'); assertNoGlobMatch(p, 'folder/foo.js'); assertNoGlobMatch(p, '/node_modules/foo.js'); }); test('globstar', () => { let p = '**/*.js'; assertGlobMatch(p, 'foo.js'); assertGlobMatch(p, 'folder/foo.js'); assertGlobMatch(p, '/node_modules/foo.js'); assertNoGlobMatch(p, 'foo.jss'); assertNoGlobMatch(p, 'some.js/test'); assertNoGlobMatch(p, '/some.js/test'); assertNoGlobMatch(p, '/some.js/test'); p = '**/project.json'; assertGlobMatch(p, 'project.json'); assertGlobMatch(p, '/project.json'); assertGlobMatch(p, 'some/folder/project.json'); assertNoGlobMatch(p, 'some/folder/file_project.json'); assertNoGlobMatch(p, 'some/folder/fileproject.json'); assertNoGlobMatch(p, 'some/rrproject.json'); p = 'test/**'; assertGlobMatch(p, 'test'); assertGlobMatch(p, 'test/foo.js'); assertGlobMatch(p, 'test/other/foo.js'); assertNoGlobMatch(p, 'est/other/foo.js'); p = '**'; assertGlobMatch(p, 'foo.js'); assertGlobMatch(p, 'folder/foo.js'); assertGlobMatch(p, '/node_modules/foo.js'); assertGlobMatch(p, 'foo.jss'); assertGlobMatch(p, 'some.js/test'); p = 'test/**/*.js'; assertGlobMatch(p, 'test/foo.js'); assertGlobMatch(p, 'test/other/foo.js'); assertGlobMatch(p, 'test/other/more/foo.js'); assertNoGlobMatch(p, 'test/foo.ts'); assertNoGlobMatch(p, 'test/other/foo.ts'); assertNoGlobMatch(p, 'test/other/more/foo.ts'); p = '**/**/*.js'; assertGlobMatch(p, 'foo.js'); assertGlobMatch(p, 'folder/foo.js'); assertGlobMatch(p, '/node_modules/foo.js'); assertNoGlobMatch(p, 'foo.jss'); assertNoGlobMatch(p, 'some.js/test'); p = '**/node_modules/**/*.js'; assertNoGlobMatch(p, 'foo.js'); assertNoGlobMatch(p, 'folder/foo.js'); assertGlobMatch(p, 'node_modules/foo.js'); assertGlobMatch(p, 'node_modules/some/folder/foo.js'); assertNoGlobMatch(p, 'node_modules/some/folder/foo.ts'); assertNoGlobMatch(p, 'foo.jss'); assertNoGlobMatch(p, 'some.js/test'); p = '{**/node_modules/**,**/.git/**,**/bower_components/**}'; assertGlobMatch(p, 'node_modules'); assertGlobMatch(p, '/node_modules'); assertGlobMatch(p, '/node_modules/more'); assertGlobMatch(p, 'some/test/node_modules'); assertGlobMatch(p, 'some/test/node_modules'); assertGlobMatch(p, 'C://some/test/node_modules'); assertGlobMatch(p, 'C://some/test/node_modules/more'); assertGlobMatch(p, 'bower_components'); assertGlobMatch(p, 'bower_components/more'); assertGlobMatch(p, '/bower_components'); assertGlobMatch(p, 'some/test/bower_components'); assertGlobMatch(p, 'some/test/bower_components'); assertGlobMatch(p, 'C://some/test/bower_components'); assertGlobMatch(p, 'C://some/test/bower_components/more'); assertGlobMatch(p, '.git'); assertGlobMatch(p, '/.git'); assertGlobMatch(p, 'some/test/.git'); assertGlobMatch(p, 'some/test/.git'); assertGlobMatch(p, 'C://some/test/.git'); assertNoGlobMatch(p, 'tempting'); assertNoGlobMatch(p, '/tempting'); assertNoGlobMatch(p, 'some/test/tempting'); assertNoGlobMatch(p, 'some/test/tempting'); assertNoGlobMatch(p, 'C://some/test/tempting'); p = '{**/package.json,**/project.json}'; assertGlobMatch(p, 'package.json'); assertGlobMatch(p, '/package.json'); assertNoGlobMatch(p, 'xpackage.json'); assertNoGlobMatch(p, '/xpackage.json'); }); test('issue 41724', function () { let p = 'some/**/*.js'; assertGlobMatch(p, 'some/foo.js'); assertGlobMatch(p, 'some/folder/foo.js'); assertNoGlobMatch(p, 'something/foo.js'); assertNoGlobMatch(p, 'something/folder/foo.js'); p = 'some/**/*'; assertGlobMatch(p, 'some/foo.js'); assertGlobMatch(p, 'some/folder/foo.js'); assertNoGlobMatch(p, 'something/foo.js'); assertNoGlobMatch(p, 'something/folder/foo.js'); }); test('brace expansion', function () { let p = '*.{html,js}'; assertGlobMatch(p, 'foo.js'); assertGlobMatch(p, 'foo.html'); assertNoGlobMatch(p, 'folder/foo.js'); assertNoGlobMatch(p, '/node_modules/foo.js'); assertNoGlobMatch(p, 'foo.jss'); assertNoGlobMatch(p, 'some.js/test'); p = '*.{html}'; assertGlobMatch(p, 'foo.html'); assertNoGlobMatch(p, 'foo.js'); assertNoGlobMatch(p, 'folder/foo.js'); assertNoGlobMatch(p, '/node_modules/foo.js'); assertNoGlobMatch(p, 'foo.jss'); assertNoGlobMatch(p, 'some.js/test'); p = '{node_modules,testing}'; assertGlobMatch(p, 'node_modules'); assertGlobMatch(p, 'testing'); assertNoGlobMatch(p, 'node_module'); assertNoGlobMatch(p, 'dtesting'); p = '**/{foo,bar}'; assertGlobMatch(p, 'foo'); assertGlobMatch(p, 'bar'); assertGlobMatch(p, 'test/foo'); assertGlobMatch(p, 'test/bar'); assertGlobMatch(p, 'other/more/foo'); assertGlobMatch(p, 'other/more/bar'); p = '{foo,bar}/**'; assertGlobMatch(p, 'foo'); assertGlobMatch(p, 'bar'); assertGlobMatch(p, 'foo/test'); assertGlobMatch(p, 'bar/test'); assertGlobMatch(p, 'foo/other/more'); assertGlobMatch(p, 'bar/other/more'); p = '{**/*.d.ts,**/*.js}'; assertGlobMatch(p, 'foo.js'); assertGlobMatch(p, 'testing/foo.js'); assertGlobMatch(p, 'testing/foo.js'); assertGlobMatch(p, '/testing/foo.js'); assertGlobMatch(p, '/testing/foo.js'); assertGlobMatch(p, 'C:/testing/foo.js'); assertGlobMatch(p, 'foo.d.ts'); assertGlobMatch(p, 'testing/foo.d.ts'); assertGlobMatch(p, 'testing/foo.d.ts'); assertGlobMatch(p, '/testing/foo.d.ts'); assertGlobMatch(p, '/testing/foo.d.ts'); assertGlobMatch(p, 'C:/testing/foo.d.ts'); assertNoGlobMatch(p, 'foo.d'); assertNoGlobMatch(p, 'testing/foo.d'); assertNoGlobMatch(p, 'testing/foo.d'); assertNoGlobMatch(p, '/testing/foo.d'); assertNoGlobMatch(p, '/testing/foo.d'); assertNoGlobMatch(p, 'C:/testing/foo.d'); p = '{**/*.d.ts,**/*.js,path/simple.jgs}'; assertGlobMatch(p, 'foo.js'); assertGlobMatch(p, 'testing/foo.js'); assertGlobMatch(p, 'testing/foo.js'); assertGlobMatch(p, '/testing/foo.js'); assertGlobMatch(p, 'path/simple.jgs'); assertNoGlobMatch(p, '/path/simple.jgs'); assertGlobMatch(p, '/testing/foo.js'); assertGlobMatch(p, 'C:/testing/foo.js'); p = '{**/*.d.ts,**/*.js,foo.[0-9]}'; assertGlobMatch(p, 'foo.5'); assertGlobMatch(p, 'foo.8'); assertNoGlobMatch(p, 'bar.5'); assertNoGlobMatch(p, 'foo.f'); assertGlobMatch(p, 'foo.js'); p = 'prefix/{**/*.d.ts,**/*.js,foo.[0-9]}'; assertGlobMatch(p, 'prefix/foo.5'); assertGlobMatch(p, 'prefix/foo.8'); assertNoGlobMatch(p, 'prefix/bar.5'); assertNoGlobMatch(p, 'prefix/foo.f'); assertGlobMatch(p, 'prefix/foo.js'); }); });vscode-json-languageservice-5.7.2/src/test/hover.test.ts000066400000000000000000000147711514707600300233550ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { Hover, Position, TextDocument, getLanguageService, JSONSchema, LanguageServiceParams } from '../jsonLanguageService'; suite('JSON Hover', () => { async function testComputeInfo(value: string, schema: JSONSchema, position: Position, serviceParams?: LanguageServiceParams): Promise { const uri = 'test://test.json'; const schemaUri = "http://myschemastore/test1"; if (!serviceParams) { serviceParams = { schemaRequestService: requestService }; } const service = getLanguageService(serviceParams); service.configure({ schemas: [{ fileMatch: ["*.json"], uri: schemaUri, schema }] }); const document = TextDocument.create(uri, 'json', 0, value); const jsonDoc = service.parseJSONDocument(document); const hover = await service.doHover(document, position, jsonDoc); assert(hover, 'expected hover to be returned'); return hover; } let requestService = function (uri: string): Promise { return Promise.reject('Resource not found'); }; test('Simple schema', async function () { const content = '{"a": 42, "b": "hello", "c": false, "complex-description": false}'; const schema: JSONSchema = { type: 'object', description: 'a very special object', properties: { 'a': { type: 'number', description: 'A' }, 'b': { type: 'string', description: 'B' }, 'c': { type: 'boolean', description: 'C' }, 'complex-description': { type: 'boolean', description: 'For example:\n\n\n\n Test [1]' }, } }; await testComputeInfo(content, schema, { line: 0, character: 0 }).then((result) => { assert.deepEqual(result.contents, ['a very special object']); }); await testComputeInfo(content, schema, { line: 0, character: 1 }).then((result) => { assert.deepEqual(result.contents, ['A']); }); await testComputeInfo(content, schema, { line: 0, character: 32 }).then((result) => { assert.deepEqual(result.contents, ['C']); }); await testComputeInfo(content, schema, { line: 0, character: 37 }).then((result) => { assert.deepEqual(result.contents, ['For example:\\\n\\\n\\\\\n  alert\\(1\\)\\\n\\\\\n\\\n    Test \\[1\\]']); }); await testComputeInfo(content, schema, { line: 0, character: 7 }).then((result) => { assert.deepEqual(result.contents, ['A']); }); }); test('Nested schema', async function () { const content = '{"a": 42, "b": "hello"}'; const schema: JSONSchema = { oneOf: [{ type: 'object', description: 'a very special object', properties: { 'a': { type: 'number', description: 'A' }, 'b': { type: 'string', title: 'B', description: 'It\'s B' }, } }, { type: 'array' }] }; await testComputeInfo(content, schema, { line: 0, character: 0 }).then((result) => { assert.deepEqual(result.contents, ['a very special object']); }); await testComputeInfo(content, schema, { line: 0, character: 1 }).then((result) => { assert.deepEqual(result.contents, ['A']); }); await testComputeInfo(content, schema, { line: 0, character: 10 }).then((result) => { assert.deepEqual(result.contents, ['B\n\nIt\'s B']); }); }); test('Enum description', async function () { const schema: JSONSchema = { type: 'object', properties: { 'prop1': { description: "prop1", enum: ['e1', 'e2', 'e3'], enumDescriptions: ['E1', 'E2', 'E3'], }, 'prop2': { description: "prop2", enum: [null, 1, false], enumDescriptions: ['null', 'one', 'wrong'], }, 'prop3': { title: "title", markdownDescription: "*prop3*", description: "prop3", enum: [null, 1], markdownEnumDescriptions: ['Set to `null`', 'Set to `1`'], } } }; await testComputeInfo('{ "prop1": "e1', schema, { line: 0, character: 12 }).then(result => { assert.deepEqual(result.contents, ['prop1\n\n`e1`: E1']); }); await testComputeInfo('{ "prop2": null', schema, { line: 0, character: 12 }).then(result => { assert.deepEqual(result.contents, ['prop2\n\n`null`: null']); }); await testComputeInfo('{ "prop2": 1', schema, { line: 0, character: 11 }).then(result => { assert.deepEqual(result.contents, ['prop2\n\n`1`: one']); }); await testComputeInfo('{ "prop2": false', schema, { line: 0, character: 12 }).then(result => { assert.deepEqual(result.contents, ['prop2\n\n`false`: wrong']); }); await testComputeInfo('{ "prop3": null', schema, { line: 0, character: 12 }).then(result => { assert.deepEqual(result.contents, ['title\n\n*prop3*\n\n`null`: Set to `null`']); }); }); test('Multiline descriptions', async function () { const schema: JSONSchema = { type: 'object', properties: { 'prop1': { description: "line1\nline2\n\nline3\n\n\nline4\n", }, 'prop2': { description: "line1\r\nline2\r\n\r\nline3", } } }; await testComputeInfo('{ "prop1": "e1', schema, { line: 0, character: 12 }).then(result => { assert.deepEqual(result.contents, ['line1\\\nline2\\\n\\\nline3\\\n\\\n\\\nline4']); }); await testComputeInfo('{ "prop2": "e1', schema, { line: 0, character: 12 }).then(result => { assert.deepEqual(result.contents, ['line1\r\\\nline2\r\\\n\r\\\nline3']); }); }); test('Contribution descriptions', async function () { const serviceParams: LanguageServiceParams = { contributions: [{ async getInfoContribution(uri, location) { return [`${uri}: ${location.join('/')}`]; }, collectDefaultCompletions(uri, results) { return Promise.resolve(); }, collectPropertyCompletions(uri, location, currentWord, addValue, isLast, results) { return Promise.resolve(); }, collectValueCompletions(uri, location, propertyKey, results) { return Promise.resolve(); } }] }; let result = await testComputeInfo('{ "prop1": [ 1, false ] }', {}, { line: 0, character: 3 }, serviceParams); assert.deepEqual(result.contents, ['test://test.json: prop1']); result = await testComputeInfo('{ "prop1": [ 1, false ] }', {}, { line: 0, character: 13 }, serviceParams); assert.deepEqual(result.contents, ['test://test.json: prop1/0']); }); }); vscode-json-languageservice-5.7.2/src/test/jsonSchemaTestSuite.test.ts000066400000000000000000000562611514707600300261760ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as Parser from '../parser/jsonParser'; import * as fs from 'fs'; import * as url from 'url'; import * as path from 'path'; import { getLanguageService, SchemaDraft, TextDocument } from '../jsonLanguageService'; import { URI } from 'vscode-uri'; const testsPath = path.join(__dirname, "../../../node_modules/json-schema-test-suite/tests"); const drafts = [ 'draft4', 'draft6', 'draft7', 'draft2019-09', 'draft2020-12' ]; export const schemaIds: { [id: string]: SchemaDraft } = { 'draft4': SchemaDraft.v4, 'draft6': SchemaDraft.v6, 'draft7': SchemaDraft.v7, 'draft2019-09': SchemaDraft.v2019_09, 'draft2020-12': SchemaDraft.v2020_12 }; const workspaceContext = { resolveRelativePath: (relativePath: string, resource: string) => { return url.resolve(resource, relativePath); } }; const ls = getLanguageService({ workspaceContext }); async function assertSchemaValidation(input: any, schema: any, valid: boolean, description: string, draft: string, fileName: string) { const textDoc = TextDocument.create('foo://bar/file.json', 'json', 0, JSON.stringify(input)); const jsonDoc = Parser.parse(textDoc); const schemaClone = JSON.parse(JSON.stringify(schema)); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = await ls.doValidation(textDoc, jsonDoc, { schemaDraft: schemaIds[draft] }, Parser.asSchema(schemaClone)); if (valid && semanticErrors.length > 0) { assert.deepStrictEqual([], semanticErrors, `\n${fileName}\n${description}: No error expected: ${JSON.stringify(input)} against ${JSON.stringify(schema)}`); } else if (!valid && semanticErrors.length === 0) { assert.fail(`\n${fileName}\n${description}: Expected error: ${JSON.stringify(input)} against ${JSON.stringify(schema)}`); } } const collectFailedTests = false; const failedTests: string[] = []; function initializeTests() { suite(`JSON Schema Test Suite`, () => { for (const draft of drafts) { const folderPath = path.resolve(testsPath, draft); const entries = fs.readdirSync(folderPath, { withFileTypes: true }); for (const entry of entries) { if (entry.isFile() && entry.name.endsWith('json')) { const filePath = path.join(folderPath, entry.name); try { const testFileContent = JSON.parse((fs.readFileSync(filePath)).toString()); if (Array.isArray(testFileContent)) { for (const testGroupEntry of testFileContent) { suite(`${draft} - ${testGroupEntry.description}`, () => { for (const testEntry of testGroupEntry.tests) { const id = `${draft}/${entry.name}/${testGroupEntry.description}/${testEntry.description}`; const fn = async () => { try { await assertSchemaValidation(testEntry.data, testGroupEntry.schema, testEntry.valid, testEntry.description, draft, URI.file(filePath).toString()); } catch (e) { if (collectFailedTests) { failedTests.push(id); } else { throw e; } } }; if (!collectFailedTests && skippedTests.has(id)) { test.skip(testEntry.description, fn); } else if (!collectFailedTests && skippedTests.has('_' + id)) { test.only(testEntry.description, fn); } else { test(testEntry.description, fn); } } }); } } } catch (e) { console.log(e); assert.fail(`Problem parsing ${filePath}`); } } } suiteTeardown(() => { if (collectFailedTests) { fs.writeFileSync('failedTests.txt', failedTests.map(t => JSON.stringify(t)).join(',\n')); } }); } }); } const skippedTests = new Set([ "draft4/id.json/id inside an enum is not a real identifier/exact match to enum, and type matches", "draft4/id.json/id inside an enum is not a real identifier/match $ref to id", "draft4/patternProperties.json/multiple simultaneous patternProperties are validated/an invalid due to the other is invalid", "draft4/properties.json/properties, patternProperties, additionalProperties interaction/patternProperty invalidates property", "draft4/ref.json/ref overrides any sibling keywords/ref valid, maxItems ignored", "draft4/ref.json/$ref prevents a sibling id from changing the base uri/$ref resolves to /definitions/base_foo, data validates", "draft4/ref.json/Recursive references between schemas/valid tree", "draft4/ref.json/Location-independent identifier with base URI change in subschema/match", "draft4/ref.json/id must be resolved against nearest parent, not just immediate parent/number should pass", "draft4/refRemote.json/remote ref/remote ref valid", "draft4/refRemote.json/fragment within remote ref/remote fragment valid", "draft4/refRemote.json/ref within remote ref/ref within ref valid", "draft4/refRemote.json/base URI change/base URI change ref valid", "draft4/refRemote.json/base URI change - change folder/number is valid", "draft4/refRemote.json/base URI change - change folder in subschema/number is valid", "draft4/refRemote.json/root ref in remote ref/string is valid", "draft4/refRemote.json/root ref in remote ref/null is valid", "draft4/refRemote.json/Location-independent identifier in remote ref/integer is valid", "draft6/definitions.json/validate definition against metaschema/valid definition schema", "draft6/id.json/id inside an enum is not a real identifier/exact match to enum, and type matches", "draft6/id.json/id inside an enum is not a real identifier/match $ref to id", "draft6/patternProperties.json/multiple simultaneous patternProperties are validated/an invalid due to the other is invalid", "draft6/patternProperties.json/patternProperties with boolean schemas/object with a property matching both true and false is invalid", "draft6/properties.json/properties, patternProperties, additionalProperties interaction/patternProperty invalidates property", "draft6/ref.json/ref overrides any sibling keywords/ref valid, maxItems ignored", "draft6/ref.json/$ref prevents a sibling $id from changing the base uri/$ref resolves to /definitions/base_foo, data validates", "draft6/ref.json/remote ref, containing refs itself/remote ref valid", "draft6/ref.json/Recursive references between schemas/valid tree", "draft6/ref.json/Location-independent identifier with base URI change in subschema/match", "draft6/ref.json/refs with relative uris and defs/valid on both fields", "draft6/ref.json/relative refs with absolute uris and defs/valid on both fields", "draft6/refRemote.json/remote ref/remote ref valid", "draft6/refRemote.json/fragment within remote ref/remote fragment valid", "draft6/refRemote.json/ref within remote ref/ref within ref valid", "draft6/refRemote.json/base URI change/base URI change ref valid", "draft6/refRemote.json/base URI change - change folder/number is valid", "draft6/refRemote.json/base URI change - change folder in subschema/number is valid", "draft6/refRemote.json/root ref in remote ref/string is valid", "draft6/refRemote.json/root ref in remote ref/null is valid", "draft6/refRemote.json/remote ref with ref to definitions/valid", "draft6/refRemote.json/Location-independent identifier in remote ref/integer is valid", "draft6/unknownKeyword.json/$id inside an unknown keyword is not a real identifier/type matches second anyOf, which has a real schema in it", "draft7/id.json/id inside an enum is not a real identifier/exact match to enum, and type matches", "draft7/id.json/id inside an enum is not a real identifier/match $ref to id", "draft7/patternProperties.json/multiple simultaneous patternProperties are validated/an invalid due to the other is invalid", "draft7/patternProperties.json/patternProperties with boolean schemas/object with a property matching both true and false is invalid", "draft7/properties.json/properties, patternProperties, additionalProperties interaction/patternProperty invalidates property", "draft7/ref.json/ref overrides any sibling keywords/ref valid, maxItems ignored", "draft7/ref.json/$ref prevents a sibling $id from changing the base uri/$ref resolves to /definitions/base_foo, data validates", "draft7/ref.json/Recursive references between schemas/valid tree", "draft7/ref.json/Location-independent identifier with base URI change in subschema/match", "draft7/ref.json/refs with relative uris and defs/valid on both fields", "draft7/ref.json/relative refs with absolute uris and defs/valid on both fields", "draft7/ref.json/$id must be resolved against nearest parent, not just immediate parent/number should pass", "draft7/refRemote.json/remote ref/remote ref valid", "draft7/refRemote.json/fragment within remote ref/remote fragment valid", "draft7/refRemote.json/ref within remote ref/ref within ref valid", "draft7/refRemote.json/base URI change/base URI change ref valid", "draft7/refRemote.json/base URI change - change folder/number is valid", "draft7/refRemote.json/base URI change - change folder in subschema/number is valid", "draft7/refRemote.json/root ref in remote ref/string is valid", "draft7/refRemote.json/root ref in remote ref/null is valid", "draft7/refRemote.json/remote ref with ref to definitions/valid", "draft7/refRemote.json/Location-independent identifier in remote ref/integer is valid", "draft7/unknownKeyword.json/$id inside an unknown keyword is not a real identifier/type matches second anyOf, which has a real schema in it", "draft2019-09/anchor.json/Location-independent identifier with absolute URI/match", "draft2019-09/anchor.json/Location-independent identifier with base URI change in subschema/match", "draft2019-09/anchor.json/same $anchor with different base uri/$ref should resolve to /$defs/A/allOf/1", "draft2019-09/defs.json/validate definition against metaschema/valid definition schema", "draft2019-09/dependentSchemas.json/boolean subschemas/object with property having schema false is invalid", "draft2019-09/dependentSchemas.json/boolean subschemas/object with both properties is invalid", "draft2019-09/id.json/Valid use of empty fragments in location-independent $id/Identifier name with absolute URI", "draft2019-09/id.json/Valid use of empty fragments in location-independent $id/Identifier name with base URI change in subschema", "draft2019-09/id.json/Unnormalized $ids are allowed but discouraged/Unnormalized identifier", "draft2019-09/id.json/Unnormalized $ids are allowed but discouraged/Unnormalized identifier and no ref", "draft2019-09/id.json/Unnormalized $ids are allowed but discouraged/Unnormalized identifier with empty fragment", "draft2019-09/id.json/Unnormalized $ids are allowed but discouraged/Unnormalized identifier with empty fragment and no ref", "draft2019-09/id.json/$id inside an enum is not a real identifier/exact match to enum, and type matches", "draft2019-09/id.json/$id inside an enum is not a real identifier/match $ref to $id", "draft2019-09/patternProperties.json/multiple simultaneous patternProperties are validated/an invalid due to the other is invalid", "draft2019-09/patternProperties.json/patternProperties with boolean schemas/object with a property matching both true and false is invalid", "draft2019-09/properties.json/properties, patternProperties, additionalProperties interaction/patternProperty invalidates property", "draft2019-09/recursiveRef.json/$recursiveRef without $recursiveAnchor works like $ref/match", "draft2019-09/recursiveRef.json/$recursiveRef without $recursiveAnchor works like $ref/recursive match", "draft2019-09/recursiveRef.json/$recursiveRef without using nesting/integer matches at the outer level", "draft2019-09/recursiveRef.json/$recursiveRef without using nesting/single level match", "draft2019-09/recursiveRef.json/$recursiveRef without using nesting/two levels, properties match with inner definition", "draft2019-09/recursiveRef.json/$recursiveRef with nesting/integer matches at the outer level", "draft2019-09/recursiveRef.json/$recursiveRef with nesting/single level match", "draft2019-09/recursiveRef.json/$recursiveRef with nesting/integer now matches as a property value", "draft2019-09/recursiveRef.json/$recursiveRef with nesting/two levels, properties match with inner definition", "draft2019-09/recursiveRef.json/$recursiveRef with nesting/two levels, properties match with $recursiveRef", "draft2019-09/recursiveRef.json/$recursiveRef with $recursiveAnchor: false works like $ref/integer matches at the outer level", "draft2019-09/recursiveRef.json/$recursiveRef with $recursiveAnchor: false works like $ref/single level match", "draft2019-09/recursiveRef.json/$recursiveRef with $recursiveAnchor: false works like $ref/two levels, properties match with inner definition", "draft2019-09/recursiveRef.json/$recursiveRef with no $recursiveAnchor works like $ref/integer matches at the outer level", "draft2019-09/recursiveRef.json/$recursiveRef with no $recursiveAnchor works like $ref/single level match", "draft2019-09/recursiveRef.json/$recursiveRef with no $recursiveAnchor works like $ref/two levels, properties match with inner definition", "draft2019-09/recursiveRef.json/$recursiveRef with no $recursiveAnchor in the initial target schema resource/leaf node matches: recursion uses the inner schema", "draft2019-09/recursiveRef.json/$recursiveRef with no $recursiveAnchor in the outer schema resource/leaf node matches: recursion only uses inner schema", "draft2019-09/recursiveRef.json/multiple dynamic paths to the $recursiveRef keyword/recurse to anyLeafNode - floats are allowed", "draft2019-09/recursiveRef.json/dynamic $recursiveRef destination (not predictable at schema compile time)/numeric node", "draft2019-09/ref.json/remote ref, containing refs itself/remote ref valid", "draft2019-09/ref.json/Recursive references between schemas/valid tree", "draft2019-09/ref.json/ref creates new scope when adjacent to keywords/referenced subschema doesn't see annotations from properties", "draft2019-09/ref.json/refs with relative uris and defs/valid on both fields", "draft2019-09/ref.json/relative refs with absolute uris and defs/valid on both fields", "draft2019-09/ref.json/$id must be resolved against nearest parent, not just immediate parent/number should pass", "draft2019-09/ref.json/order of evaluation: $id and $ref/data is valid against first definition", "draft2019-09/ref.json/order of evaluation: $id and $anchor and $ref/data is valid against first definition", "draft2019-09/refRemote.json/remote ref/remote ref valid", "draft2019-09/refRemote.json/fragment within remote ref/remote fragment valid", "draft2019-09/refRemote.json/ref within remote ref/ref within ref valid", "draft2019-09/refRemote.json/base URI change/base URI change ref valid", "draft2019-09/refRemote.json/base URI change - change folder/number is valid", "draft2019-09/refRemote.json/base URI change - change folder in subschema/number is valid", "draft2019-09/refRemote.json/root ref in remote ref/string is valid", "draft2019-09/refRemote.json/root ref in remote ref/null is valid", "draft2019-09/refRemote.json/remote ref with ref to defs/valid", "draft2019-09/refRemote.json/Location-independent identifier in remote ref/integer is valid", "draft2019-09/unevaluatedProperties.json/unevaluatedProperties with if/then/else/when if is false and has unevaluated properties", "draft2019-09/unevaluatedProperties.json/unevaluatedProperties with if/then/else, then not defined/when if is false and has unevaluated properties", "draft2019-09/unevaluatedProperties.json/unevaluatedProperties with dependentSchemas/with no unevaluated properties", "draft2019-09/unevaluatedProperties.json/unevaluatedProperties with $ref/with no unevaluated properties", "draft2019-09/unevaluatedProperties.json/dynamic evalation inside nested refs/all is valid", "draft2019-09/unevaluatedProperties.json/dynamic evalation inside nested refs/all + foo is valid", "draft2019-09/unknownKeyword.json/$id inside an unknown keyword is not a real identifier/type matches second anyOf, which has a real schema in it", "draft2019-09/vocabulary.json/schema that uses custom metaschema with with no validation vocabulary/no validation: invalid number, but it still validates", "draft2020-12/anchor.json/Location-independent identifier with absolute URI/match", "draft2020-12/anchor.json/Location-independent identifier with base URI change in subschema/match", "draft2020-12/anchor.json/same $anchor with different base uri/$ref should resolve to /$defs/A/allOf/1", "draft2020-12/defs.json/validate definition against metaschema/valid definition schema", "draft2020-12/dependentSchemas.json/boolean subschemas/object with property having schema false is invalid", "draft2020-12/dependentSchemas.json/boolean subschemas/object with both properties is invalid", "draft2020-12/dynamicRef.json/A $dynamicRef to a $dynamicAnchor in the same schema resource should behave like a normal $ref to an $anchor/An array of strings is valid", "draft2020-12/dynamicRef.json/A $dynamicRef to an $anchor in the same schema resource should behave like a normal $ref to an $anchor/An array of strings is valid", "draft2020-12/dynamicRef.json/A $ref to a $dynamicAnchor in the same schema resource should behave like a normal $ref to an $anchor/An array of strings is valid", "draft2020-12/dynamicRef.json/A $dynamicRef should resolve to the first $dynamicAnchor still in scope that is encountered when the schema is evaluated/An array of strings is valid", "draft2020-12/dynamicRef.json/A $dynamicRef with intermediate scopes that don't include a matching $dynamicAnchor should not affect dynamic scope resolution/An array of strings is valid", "draft2020-12/dynamicRef.json/An $anchor with the same name as a $dynamicAnchor should not be used for dynamic scope resolution/Any array is valid", "draft2020-12/dynamicRef.json/A $dynamicRef without a matching $dynamicAnchor in the same schema resource should behave like a normal $ref to $anchor/Any array is valid", "draft2020-12/dynamicRef.json/A $dynamicRef with a non-matching $dynamicAnchor in the same schema resource should behave like a normal $ref to $anchor/Any array is valid", "draft2020-12/dynamicRef.json/A $dynamicRef that initially resolves to a schema with a matching $dynamicAnchor should resolve to the first $dynamicAnchor in the dynamic scope/The recursive part is valid against the root", "draft2020-12/dynamicRef.json/A $dynamicRef that initially resolves to a schema without a matching $dynamicAnchor should behave like a normal $ref to $anchor/The recursive part doesn't need to validate against the root", "draft2020-12/dynamicRef.json/multiple dynamic paths to the $dynamicRef keyword/recurse to anyLeafNode - floats are allowed", "draft2020-12/dynamicRef.json/after leaving a dynamic scope, it should not be used by a $dynamicRef//then/$defs/thingy is the final stop for the $dynamicRef", "draft2020-12/dynamicRef.json/strict-tree schema, guards against misspelled properties/instance with correct field", "draft2020-12/dynamicRef.json/tests for implementation dynamic anchor and reference link/correct extended schema", "draft2020-12/dynamicRef.json/Tests for implementation dynamic anchor and reference link. Reference should be independent of any possible ordering./correct extended schema", "draft2020-12/dynamicRef.json/Tests for implementation dynamic anchor and reference link. Reference should be independent of any possible ordering./correct extended schema", "draft2020-12/id.json/Valid use of empty fragments in location-independent $id/Identifier name with absolute URI", "draft2020-12/id.json/Valid use of empty fragments in location-independent $id/Identifier name with base URI change in subschema", "draft2020-12/id.json/Unnormalized $ids are allowed but discouraged/Unnormalized identifier", "draft2020-12/id.json/Unnormalized $ids are allowed but discouraged/Unnormalized identifier and no ref", "draft2020-12/id.json/Unnormalized $ids are allowed but discouraged/Unnormalized identifier with empty fragment", "draft2020-12/id.json/Unnormalized $ids are allowed but discouraged/Unnormalized identifier with empty fragment and no ref", "draft2020-12/id.json/$id inside an enum is not a real identifier/exact match to enum, and type matches", "draft2020-12/id.json/$id inside an enum is not a real identifier/match $ref to $id", "draft2020-12/patternProperties.json/multiple simultaneous patternProperties are validated/an invalid due to the other is invalid", "draft2020-12/patternProperties.json/patternProperties with boolean schemas/object with a property matching both true and false is invalid", "draft2020-12/properties.json/properties, patternProperties, additionalProperties interaction/patternProperty invalidates property", "draft2020-12/ref.json/remote ref, containing refs itself/remote ref valid", "draft2020-12/ref.json/Recursive references between schemas/valid tree", "draft2020-12/ref.json/ref creates new scope when adjacent to keywords/referenced subschema doesn't see annotations from properties", "draft2020-12/ref.json/refs with relative uris and defs/valid on both fields", "draft2020-12/ref.json/relative refs with absolute uris and defs/valid on both fields", "draft2020-12/ref.json/$id must be resolved against nearest parent, not just immediate parent/number should pass", "draft2020-12/ref.json/order of evaluation: $id and $ref/data is valid against first definition", "draft2020-12/ref.json/order of evaluation: $id and $anchor and $ref/data is valid against first definition", "draft2020-12/refRemote.json/remote ref/remote ref valid", "draft2020-12/refRemote.json/fragment within remote ref/remote fragment valid", "draft2020-12/refRemote.json/ref within remote ref/ref within ref valid", "draft2020-12/refRemote.json/base URI change/base URI change ref valid", "draft2020-12/refRemote.json/base URI change - change folder/number is valid", "draft2020-12/refRemote.json/base URI change - change folder in subschema/number is valid", "draft2020-12/refRemote.json/root ref in remote ref/string is valid", "draft2020-12/refRemote.json/root ref in remote ref/null is valid", "draft2020-12/refRemote.json/remote ref with ref to defs/valid", "draft2020-12/refRemote.json/Location-independent identifier in remote ref/integer is valid", "draft2020-12/unevaluatedProperties.json/unevaluatedProperties with if/then/else/when if is false and has unevaluated properties", "draft2020-12/unevaluatedProperties.json/unevaluatedProperties with if/then/else, then not defined/when if is false and has unevaluated properties", "draft2020-12/unevaluatedProperties.json/unevaluatedProperties with dependentSchemas/with no unevaluated properties", "draft2020-12/unevaluatedProperties.json/unevaluatedProperties with $ref/with no unevaluated properties", "draft2020-12/unevaluatedProperties.json/dynamic evalation inside nested refs/all is valid", "draft2020-12/unevaluatedProperties.json/dynamic evalation inside nested refs/all + foo is valid", "draft2020-12/unknownKeyword.json/$id inside an unknown keyword is not a real identifier/type matches second anyOf, which has a real schema in it", "draft2020-12/vocabulary.json/schema that uses custom metaschema with with no validation vocabulary/no validation: invalid number, but it still validates" ]); initializeTests();vscode-json-languageservice-5.7.2/src/test/links.test.ts000066400000000000000000000060231514707600300233410ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { getLanguageService, Range, TextDocument, ClientCapabilities } from '../jsonLanguageService'; suite('JSON Find Links', () => { const testFindLinksFor = function (value: string, expected: {offset: number, length: number, target: number} | null): PromiseLike { const ls = getLanguageService({ clientCapabilities: ClientCapabilities.LATEST }); const document = TextDocument.create('test://test/test.json', 'json', 0, value); const jsonDoc = ls.parseJSONDocument(document); return ls.findLinks(document, jsonDoc).then(list => { if (expected) { assert.notDeepEqual(list, []); const expectedPos = document.positionAt(expected.target); const expectedTarget = `${document.uri}#${expectedPos.line + 1},${expectedPos.character + 1}`; assert.equal(list[0].target, expectedTarget); assert.deepEqual(list[0].range, Range.create(document.positionAt(expected.offset), document.positionAt(expected.offset + expected.length))); } else { assert.deepEqual(list, []); } }); }; test('FindDefinition invalid ref', async function () { await testFindLinksFor('{}', null); await testFindLinksFor('{"name": "John"}', null); await testFindLinksFor('{"name": "John", "$ref": "#/john/name"}', null); await testFindLinksFor('{"name": "John", "$ref": "#/"}', null); }); test('FindDefinition valid ref', async function () { await testFindLinksFor('{"name": "John", "$ref": "#/name"}', {target: 9, offset: 26, length: 6}); await testFindLinksFor('{"name": "John", "$ref": "#"}', {target: 0, offset: 26, length: 1}); const doc = (ref: string) => `{"foo": ["bar", "baz"],"": 0,"a/b": 1,"c%d": 2,"e^f": 3,"i\\\\j": 5,"k\\"l": 6," ": 7,"m~n": 8, "$ref": "${ref}"}`; await testFindLinksFor(doc('#'), {target: 0, offset: 102, length: 1}); await testFindLinksFor(doc('#/foo'), {target: 8, offset: 102, length: 5}); await testFindLinksFor(doc('#/foo/0'), {target: 9, offset: 102, length: 7}); await testFindLinksFor(doc('#/foo/1'), {target: 16, offset: 102, length: 7}); await testFindLinksFor(doc('#/foo/01'), null); await testFindLinksFor(doc('#/'), {target: 27, offset: 102, length: 2}); await testFindLinksFor(doc('#/a~1b'), {target: 36, offset: 102, length: 6}); await testFindLinksFor(doc('#/c%d'), {target: 45, offset: 102, length: 5}); await testFindLinksFor(doc('#/e^f'), {target: 54, offset: 102, length: 5}); await testFindLinksFor(doc('#/i\\\\j'), {target: 64, offset: 102, length: 6}); await testFindLinksFor(doc('#/k\\"l'), {target: 74, offset: 102, length: 6}); await testFindLinksFor(doc('#/ '), {target: 81, offset: 102, length: 3}); await testFindLinksFor(doc('#/m~0n'), {target: 90, offset: 102, length: 6}); }); }); vscode-json-languageservice-5.7.2/src/test/parser.test.ts000066400000000000000000002520501514707600300235200ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { getNodePath, getNodeValue, JSONDocument } from '../parser/jsonParser'; import { TextDocument, Range, ErrorCode, ASTNode, ObjectASTNode, getLanguageService, JSONSchema, SchemaDraft } from '../jsonLanguageService'; import { DiagnosticSeverity } from 'vscode-languageserver-types'; suite('JSON Parser', () => { function isValid(json: string): void { const { jsonDoc } = toDocument(json); assert.equal(jsonDoc.syntaxErrors.length, 0); } function isInvalid(json: string, ...expectedErrors: ErrorCode[]): void { const { jsonDoc } = toDocument(json); if (expectedErrors.length === 0) { assert.ok(jsonDoc.syntaxErrors.length > 0, json); } else { assert.deepEqual(jsonDoc.syntaxErrors.map(e => e.code), expectedErrors, json); } // these should be caught by the parser, not the last-ditch guard assert.notEqual(jsonDoc.syntaxErrors[0].message, 'Invalid JSON', json); } function toDocument(text: string): { textDoc: TextDocument, jsonDoc: JSONDocument } { const textDoc = TextDocument.create('foo://bar/file.json', 'json', 0, text); const ls = getLanguageService({}); const jsonDoc = ls.parseJSONDocument(textDoc) as JSONDocument; return { textDoc, jsonDoc }; } function toRange(text: string, offset: number, length: number) { const textDoc = TextDocument.create('foo://bar/file.json', 'json', 0, text); return Range.create(textDoc.positionAt(offset), textDoc.positionAt(offset + length)); } function validate(text: string, schema: JSONSchema) { const { textDoc, jsonDoc } = toDocument(text); return validate2(jsonDoc, textDoc, schema); } function validate2(jsonDoc: JSONDocument, textDoc: TextDocument, schema: JSONSchema, draft = SchemaDraft.v7) { return jsonDoc.validate(textDoc, schema, undefined, draft); } function assertObject(node: ASTNode, expectedProperties: string[]) { assert.equal(node.type, 'object'); assert.equal((node).properties.length, expectedProperties.length); const keyList = (node).properties.map(p => p.keyNode.value); assert.deepEqual(keyList, expectedProperties); } test('Invalid body', function () { const { jsonDoc } = toDocument('*'); assert.equal(jsonDoc.syntaxErrors.length, 1); isInvalid('{}[]'); }); test('Trailing Whitespace', function () { isValid('{}\n\n'); }); test('No content', function () { isValid(''); isValid(' '); isValid('\n\n'); isValid('/*hello*/ '); }); test('Objects', function () { isValid('{}'); isValid('{"key": "value"}'); isValid('{"key1": true, "key2": 3, "key3": [null], "key4": { "nested": {}}}'); isValid('{"constructor": true }'); isInvalid('{'); isInvalid('{3:3}'); isInvalid('{\'key\': 3}'); isInvalid('{"key" 3}', ErrorCode.ColonExpected); isInvalid('{"key":3 "key2": 4}', ErrorCode.CommaExpected); isInvalid('{"key":42, }', ErrorCode.TrailingComma); isInvalid('{"key:42', ErrorCode.UnexpectedEndOfString, ErrorCode.ColonExpected); }); test('Arrays', function () { isValid('[]'); isValid('[1, 2]'); isValid('[1, "string", false, {}, [null]]'); isInvalid('['); isInvalid('[,]', ErrorCode.ValueExpected); isInvalid('[1 2]', ErrorCode.CommaExpected); isInvalid('[true false]', ErrorCode.CommaExpected); isInvalid('[1, ]', ErrorCode.TrailingComma); isInvalid('[[]', ErrorCode.CommaOrCloseBacketExpected); isInvalid('["something"'); isInvalid('[magic]'); }); test('Strings', function () { isValid('["string"]'); isValid('["\\"\\\\\\/\\b\\f\\n\\r\\t\\u1234\\u12AB"]'); isValid('["\\\\"]'); isInvalid('["'); isInvalid('["]'); isInvalid('["\\z"]'); isInvalid('["\\u"]'); isInvalid('["\\u123"]'); isInvalid('["\\u123Z"]'); isInvalid('[\'string\']'); isInvalid('"\tabc"', ErrorCode.InvalidCharacter); }); test('Numbers', function () { isValid('[0, -1, 186.1, 0.123, -1.583e+4, 1.583E-4, 5e8]'); isInvalid('[+1]'); isInvalid('[01]'); isInvalid('[1.]'); isInvalid('[1.1+3]'); isInvalid('[1.4e]'); isInvalid('[-A]'); }); test('Comments', function () { isValid('/*d*/ { } /*e*/'); isInvalid('/*d { }'); // comments in JSON keys isValid('{ "//": "comment1", "//": "comment2" }'); isInvalid('{ "regularKey": "value1", "regularKey": "value2" }', ErrorCode.DuplicateKey, ErrorCode.DuplicateKey); }); test('Simple AST', function () { { const { jsonDoc } = toDocument('{}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const node = jsonDoc.getNodeFromOffset(1)!; assert.equal(node.type, 'object'); assert.deepEqual(getNodePath(node), []); assert.strictEqual(jsonDoc.getNodeFromOffset(2), undefined); } { const { jsonDoc } = toDocument('[null]'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const node = jsonDoc.getNodeFromOffset(2)!; assert.equal(node.type, 'null'); assert.deepEqual(getNodePath(node), [0]); } { const { jsonDoc } = toDocument('{"a":true}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); let node = jsonDoc.getNodeFromOffset(3); assert.equal(node!.type, 'string'); assert.deepEqual(getNodePath(node!), ['a']); node = jsonDoc.getNodeFromOffset(4); assert.equal(node!.type, 'property'); node = jsonDoc.getNodeFromOffset(0); assert.equal(node!.type, 'object'); node = jsonDoc.getNodeFromOffset(10); assert.equal(node, undefined); node = jsonDoc.getNodeFromOffset(5); assert.equal(node!.type, 'boolean'); assert.deepEqual(getNodePath(node!), ['a']); } }); test('Nested AST', function () { const content = '{\n\t"key" : {\n\t"key2": 42\n\t}\n}'; const { jsonDoc } = toDocument(content); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); let node = jsonDoc.getNodeFromOffset(content.indexOf('key2') + 2); let location = getNodePath(node!); assert.deepEqual(location, ['key', 'key2']); node = jsonDoc.getNodeFromOffset(content.indexOf('42') + 1); location = getNodePath(node!); assert.deepEqual(location, ['key', 'key2']); }); test('Nested AST in Array', function () { const { jsonDoc } = toDocument('{"key":[{"key2":42}]}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const node = jsonDoc.getNodeFromOffset(17); const location = getNodePath(node!); assert.deepEqual(location, ['key', 0, 'key2']); }); test('Multiline', function () { { const content = '{\n\t\n}'; const { jsonDoc } = toDocument(content); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const node = jsonDoc.getNodeFromOffset(content.indexOf('\t') + 1); assert.notEqual(node, null); } { const content = '{\n"first":true\n\n}'; const { jsonDoc } = toDocument(content); let node = jsonDoc.getNodeFromOffset(content.length - 2); assert.equal(node!.type, 'object'); node = jsonDoc.getNodeFromOffset(content.length - 4); assert.equal(node!.type, 'boolean'); } }); test('Expand errors to entire tokens', function () { const content = '{\n"key":32,\nerror\n}'; const { jsonDoc } = toDocument(content); assert.equal(jsonDoc.syntaxErrors.length, 2); assert.deepEqual(jsonDoc.syntaxErrors[0].range, toRange(content, content.indexOf('error'), 5)); }); test('Errors at the end of the file', function () { const content = '{\n"key":32\n '; const { jsonDoc } = toDocument(content); assert.equal(jsonDoc.syntaxErrors.length, 1); assert.deepEqual(jsonDoc.syntaxErrors[0].range, toRange(content, 9, 1)); }); test('Getting keys out of an object', function () { const content = '{\n"key":32,\n\n"key2":45}'; const { jsonDoc } = toDocument(content); assert.equal(jsonDoc.syntaxErrors.length, 0); const node = jsonDoc.getNodeFromOffset(content.indexOf('32,\n') + 4); assertObject(node!, ['key', 'key2']); }); test('Missing colon', function () { const content = '{\n"key":32,\n"key2"\n"key3": 4 }'; const { jsonDoc } = toDocument(content); assert.equal(jsonDoc.syntaxErrors.length, 1); assert.equal(jsonDoc.syntaxErrors[0].code, ErrorCode.ColonExpected); const root = jsonDoc.root; assertObject(root!, ['key', 'key2', 'key3']); }); test('Missing comma', function () { const content = '{\n"key":32,\n"key2": 1 \n"key3": 4 }'; const { jsonDoc } = toDocument(content); assert.equal(jsonDoc.syntaxErrors.length, 1); assert.equal(jsonDoc.syntaxErrors[0].code, ErrorCode.CommaExpected); assertObject(jsonDoc.root!, ['key', 'key2', 'key3']); }); test('Validate types', function () { const str = '{"number": 3.4, "integer": 42, "string": "some string", "boolean":true, "null":null, "object":{}, "array":[1, 2]}'; const { textDoc, jsonDoc } = toDocument(str); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); let semanticErrors = validate2(jsonDoc, textDoc, { type: 'object' }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'array' }); assert.strictEqual(semanticErrors!.length, 1); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "number": { type: 'number' }, "integer": { type: 'integer' }, "string": { type: 'string' }, "boolean": { type: 'boolean' }, "null": { type: 'null' }, "object": { type: 'object' }, "array": { type: 'array' } } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "number": { type: 'array' }, "integer": { type: 'string' }, "string": { type: 'object' }, "boolean": { type: 'null' }, "null": { type: 'integer' }, "object": { type: 'boolean' }, "array": { type: 'number' } } }); assert.strictEqual(semanticErrors!.length, 7); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "number": { type: 'integer' }, } }); assert.strictEqual(semanticErrors!.length, 1); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "integer": { type: 'number' }, } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "array": { type: 'array', items: { type: 'integer' } }, } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "array": { type: 'array', items: { type: 'string' } }, } }); assert.strictEqual(semanticErrors!.length, 2); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "array": false, } }); assert.strictEqual(semanticErrors!.length, 1); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "array": true, } }); assert.strictEqual(semanticErrors!.length, 0); }); test('Required properties', function () { const str = '{"integer": 42, "string": "some string", "boolean":true}'; const { textDoc, jsonDoc } = toDocument(str); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); let semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', required: ['string'] }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', required: ['notpresent'] }); assert.strictEqual(semanticErrors!.length, 1); }); test('Arrays', function () { const str = '[1, 2, 3]'; const { textDoc, jsonDoc } = toDocument(str); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); let semanticErrors = validate2(jsonDoc, textDoc, { type: 'array', items: { type: 'number' }, minItems: 1, maxItems: 5 }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'array', items: { type: 'number' }, minItems: 10 }); assert.strictEqual(semanticErrors!.length, 1); semanticErrors = validate2(jsonDoc, textDoc, { type: 'array', items: { type: 'number' }, maxItems: 2 }); assert.strictEqual(semanticErrors!.length, 1); }); test('Strings', function () { const str = '{"one":"test"}'; const { textDoc, jsonDoc } = toDocument(str); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); let semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', minLength: 1, maxLength: 10 } } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', minLength: 10, } } }); assert.strictEqual(semanticErrors!.length, 1); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', maxLength: 3, } } }); assert.strictEqual(semanticErrors!.length, 1); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', pattern: '^test$' } } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', pattern: 'fail' } } }); assert.strictEqual(semanticErrors!.length, 1); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', pattern: '(?i)^TEST$' } } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', pattern: '(?i)^Fail$' } } }); assert.strictEqual(semanticErrors!.length, 1); // Patterns may include Unicode character classes. semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', pattern: '^[\\p{Letter}]+$', } } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', pattern: '(?i)^[\\p{Letter}]+$', } } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', pattern: '*+ (invalid pattern)', } } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'string', pattern: '(^\\d+(\\-\\d+)?$)|(.+)', } } }); assert.strictEqual(semanticErrors!.length, 0); const schemaWithURI = { type: 'object', properties: { "one": { type: 'string', format: 'uri' } } }; semanticErrors = validate2(jsonDoc, textDoc, schemaWithURI); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].message, 'String is not a URI: URI with a scheme is expected.'); semanticErrors = validate('{"one":"http://foo/bar"}', schemaWithURI); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate('{"one":""}', schemaWithURI); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].message, 'String is not a URI: URI expected.'); semanticErrors = validate('{"one":"//foo/bar"}', schemaWithURI); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].message, 'String is not a URI: URI with a scheme is expected.'); const schemaWithURIReference = { type: 'object', properties: { "one": { type: 'string', format: 'uri-reference' } } }; semanticErrors = validate('{"one":""}', schemaWithURIReference); assert.strictEqual(semanticErrors!.length, 1, 'uri-reference'); assert.strictEqual(semanticErrors![0].message, 'String is not a URI: URI expected.'); semanticErrors = validate('{"one":"//foo/bar"}', schemaWithURIReference); assert.strictEqual(semanticErrors!.length, 0, 'uri-reference'); const schemaWithHostname = { type: 'object', properties: { "hostname": { type: 'string', format: 'hostname' } } }; semanticErrors = validate('{"hostname":"code.visualstudio.com"}', schemaWithHostname); assert.strictEqual(semanticErrors!.length, 0, "hostname"); semanticErrors = validate('{"hostname":"foo/bar"}', schemaWithHostname); assert.strictEqual(semanticErrors!.length, 1, "hostname"); assert.strictEqual(semanticErrors![0].message, 'String is not a hostname.'); const schemaWithIPv4 = { type: 'object', properties: { "hostaddr4": { type: 'string', format: 'ipv4' } } }; semanticErrors = validate('{"hostaddr4":"127.0.0.1"}', schemaWithIPv4); assert.strictEqual(semanticErrors!.length, 0, "hostaddr4"); semanticErrors = validate('{"hostaddr4":"1916:0:0:0:0:F00:1:81AE"}', schemaWithIPv4); assert.strictEqual(semanticErrors!.length, 1, "hostaddr4"); assert.strictEqual(semanticErrors![0].message, 'String is not an IPv4 address.'); const schemaWithIPv6 = { type: 'object', properties: { "hostaddr6": { type: 'string', format: 'ipv6' } } }; semanticErrors = validate('{"hostaddr6":"1916:0:0:0:0:F00:1:81AE"}', schemaWithIPv6); assert.strictEqual(semanticErrors!.length, 0, "hostaddr6"); semanticErrors = validate('{"hostaddr6":"127.0.0.1"}', schemaWithIPv6); assert.strictEqual(semanticErrors!.length, 1, "hostaddr6"); assert.strictEqual(semanticErrors![0].message, 'String is not an IPv6 address.'); const schemaWithEMail = { type: 'object', properties: { "mail": { type: 'string', format: 'email' } } }; semanticErrors = validate('{"mail":"foo@bar.com"}', schemaWithEMail); assert.strictEqual(semanticErrors!.length, 0, "email"); semanticErrors = validate('{"mail":"foo"}', schemaWithEMail); assert.strictEqual(semanticErrors!.length, 1, "email"); assert.strictEqual(semanticErrors![0].message, 'String is not an e-mail address.'); const schemaWithColor = { type: 'object', properties: { "color": { type: 'string', format: 'color-hex' } } }; semanticErrors = validate('{"color":"#FF00FF"}', schemaWithColor); assert.strictEqual(semanticErrors!.length, 0, "email"); semanticErrors = validate('{"color":"#FF00F"}', schemaWithColor); assert.strictEqual(semanticErrors!.length, 1, "email"); assert.strictEqual(semanticErrors![0].message, 'Invalid color format. Use #RGB, #RGBA, #RRGGBB or #RRGGBBAA.'); const schemaWithDateTime = { type: 'object', properties: { "date-time": { type: 'string', format: 'date-time' }, "date": { type: 'string', format: 'date' }, "time": { type: 'string', format: 'time' } } }; semanticErrors = validate('{"date-time":"1985-04-12T23:20:50.52Z"}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 0, "date-time"); semanticErrors = validate('{"date-time":"1996-12-19T16:39:57-08:00"}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 0, "date-time"); semanticErrors = validate('{"date-time":"1990-12-31T23:59:60Z"}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 0, "date-time"); semanticErrors = validate('{"date-time":"1937-01-01T12:00:27.87+00:20"}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 0, "date-time"); semanticErrors = validate('{"date-time":"198a-04-12T23:20:50.52Z"}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 1, "date-time"); assert.strictEqual(semanticErrors![0].message, 'String is not a RFC3339 date-time.'); semanticErrors = validate('{"date-time":"198a-04-12"}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 1, "date-time"); assert.strictEqual(semanticErrors![0].message, 'String is not a RFC3339 date-time.'); semanticErrors = validate('{"date-time":""}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 1, "date-time"); assert.strictEqual(semanticErrors![0].message, 'String is not a RFC3339 date-time.'); semanticErrors = validate('{"date":"1937-01-01"}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 0, "date"); semanticErrors = validate('{"date":"23:20:50.52Z"}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 1, "date"); assert.strictEqual(semanticErrors![0].message, 'String is not a RFC3339 date.'); semanticErrors = validate('{"time":"23:20:50.52Z"}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 0, "time"); semanticErrors = validate('{"time":"198a-04-12T23:20:50.52Z"}', schemaWithDateTime); assert.strictEqual(semanticErrors!.length, 1, "time"); assert.strictEqual(semanticErrors![0].message, 'String is not a RFC3339 time.'); }); test('Numbers', function () { const str = '{"one": 13.45e+1}'; const { textDoc, jsonDoc } = toDocument(str); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); let semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'number', minimum: 1, maximum: 135 } } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'number', minimum: 200, } } }); assert.strictEqual(semanticErrors!.length, 1, 'below minimum'); assert.strictEqual(semanticErrors![0].message, 'Value is below the minimum of 200.'); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'number', maximum: 130, } } }); assert.strictEqual(semanticErrors!.length, 1, 'above maximum'); assert.strictEqual(semanticErrors![0].message, 'Value is above the maximum of 130.'); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'number', minimum: 134.5, exclusiveMinimum: true } } }); assert.strictEqual(semanticErrors!.length, 1, 'at exclusive mininum'); assert.strictEqual(semanticErrors![0].message, 'Value is below the exclusive minimum of 134.5.'); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'number', minimum: 134.5, exclusiveMinimum: false } } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'number', exclusiveMinimum: 134.5 } } }); assert.strictEqual(semanticErrors!.length, 1, 'at exclusive mininum'); assert.strictEqual(semanticErrors![0].message, 'Value is below the exclusive minimum of 134.5.'); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'number', maximum: 134.5, exclusiveMaximum: true } } }); assert.strictEqual(semanticErrors!.length, 1, 'at exclusive mininum'); assert.strictEqual(semanticErrors![0].message, 'Value is above the exclusive maximum of 134.5.'); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'number', maximum: 134.5, exclusiveMaximum: false } } }); assert.strictEqual(semanticErrors!.length, 0); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'number', exclusiveMaximum: 134.5 } } }); assert.strictEqual(semanticErrors!.length, 1, 'at exclusive mininum'); assert.strictEqual(semanticErrors![0].message, 'Value is above the exclusive maximum of 134.5.'); semanticErrors = validate2(jsonDoc, textDoc, { type: 'object', properties: { "one": { type: 'number', minimum: 134.5, maximum: 134.5 } } }); assert.strictEqual(semanticErrors!.length, 0, 'equal to min and max'); }); test('getNodeFromOffset', function () { const content = '{"a": 1,\n\n"d": 2}'; const { jsonDoc } = toDocument(content); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const node = jsonDoc.getNodeFromOffset(content.indexOf(': 2') + 1); assert.strictEqual(node!.type, 'property'); }); test('Duplicate keys', function () { { const { jsonDoc } = toDocument('{"a": 1, "a": 2}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 2, 'Keys should not be the same'); } { const { jsonDoc } = toDocument('{"a": { "a": 2, "a": 3}}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 2, 'Keys should not be the same'); } { const { jsonDoc } = toDocument('[{ "a": 2, "a": 3, "a": 7}]'); assert.strictEqual(jsonDoc.syntaxErrors.length, 3, 'Keys should not be the same'); } }); test('allOf', function () { const schema: JSONSchema = { id: 'test://schemas/main', allOf: [ { type: 'object' }, { properties: { 'prop1': { type: 'number' } } }, { properties: { 'prop2': { type: 'boolean' } } } ] }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": true}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": 123}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('anyOf', function () { const schema: JSONSchema = { id: 'test://schemas/main', anyOf: [ { properties: { 'prop1': { type: 'number' } } }, { properties: { 'prop2': { type: 'boolean' } } } ] }; { const str = '{"prop1": 42, "prop2": true}'; const { textDoc, jsonDoc } = toDocument(str); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": 123}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop1": "a string", "prop2": 123}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('oneOf', function () { const schema: JSONSchema = { id: 'test://schemas/main', oneOf: [ { properties: { 'prop1': { type: 'number' } } }, { properties: { 'prop2': { type: 'boolean' } } } ] }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": true}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": 123}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop1": "a string", "prop2": 123}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('not', function () { const schema: JSONSchema = { id: 'test://schemas/main', not: { properties: { 'prop1': { type: 'number' } } } }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('{"prop1": "test"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('if/then/else', function () { const schema: JSONSchema = { id: 'test://schemas/main', if: { properties: { foo: { const: 'bar' } } }, then: { properties: { abc: { type: 'boolean' } } }, else: { properties: { abc: { type: 'string' } } } }; { const { textDoc, jsonDoc } = toDocument('{"foo": "bar", "abc": true}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"foo": "bar", "abc": "baz"}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('{"foo": "test", "abc": true}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('{"foo": "test", "abc": "baz"}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('nested if/then/else', function () { const schema: JSONSchema = { id: 'test://schemas/main', if: { properties: { foo: { const: 'bar' } } }, then: { properties: { abc: { type: 'boolean' } } }, else: { if: { properties: { foo: { const: 'baz' } } }, then: { properties: { abc: { type: 'array' } } }, else: { properties: { abc: { type: 'string' } } } } }; { const { textDoc, jsonDoc } = toDocument('{"foo": "bar", "abc": true}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"foo": "bar", "abc": "baz"}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('{"foo": "baz", "abc": []}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"foo": "baz", "abc": "baz"}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('{"foo": "test", "abc": true}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('{"foo": "test", "abc": "baz"}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('minProperties', function () { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": true}'); const schema: JSONSchema = { minProperties: 2 }; let semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); schema.minProperties = 1; semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); schema.minProperties = 3; semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); }); test('maxProperties', function () { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": true}'); const schema: JSONSchema = { maxProperties: 2 }; let semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); schema.maxProperties = 3; semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); schema.maxProperties = 1; semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); }); test('patternProperties', function () { let schema: JSONSchema = { id: 'test://schemas/main', patternProperties: { '^prop\\d$': { type: 'number' } } }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": 123, "aprop3": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } schema = { id: 'test://schemas/main', patternProperties: { '^prop\\d$': true, '^invalid$': false } }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 42 }'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"invalid": 42 }'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { id: 'test://schemas/main', patternProperties: { '(?i)^foo$': true } }; { const { textDoc, jsonDoc } = toDocument('{"Foo": 42 }'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } // PatternProperties may include Unicode character classes. schema = { id: 'test://schemas/main', patternProperties: { '^letter\\p{Letter}$': true, '(?i)^number\\p{Number}$': true, }, additionalProperties: false, }; { const { textDoc, jsonDoc } = toDocument('{"letterZ": [], "NumBer2": [], "number3": []}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"other": []}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('{"letter9": [], "NumberZ": []}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 2); } }); test('additionalProperties', function () { let schema: JSONSchema = { additionalProperties: { type: 'number' } }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { properties: { "prop1": { type: 'boolean' } }, additionalProperties: { type: 'number' } }; { const { textDoc, jsonDoc } = toDocument('{"prop1": true, "prop2": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } schema = { properties: { "prop1": { type: 'boolean' } }, additionalProperties: false }; { const { textDoc, jsonDoc } = toDocument('{"prop1": true, "prop2": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('{"prop1": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('unevaluatedProperties', function () { let schema: JSONSchema = { properties: { prop1: { type: 'number' } }, unevaluatedProperties: false }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop1": 42, "prop2": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { properties: { prop1: { type: 'number' } }, unevaluatedProperties: { type: 'number' } }; { const { textDoc, jsonDoc } = toDocument('{"prop1": true, "prop2": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 2); } schema = { allOf: [ { properties: { prop1: { type: 'number' } }, }, { properties: { prop2: { type: 'number' } }, }, ], unevaluatedProperties: false }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 23, "prop2": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop3": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { anyOf: [ { properties: { prop1: { type: 'number' } }, patternProperties: { ['^x']: { type: 'boolean' } } }, { properties: { prop2: { type: 'number' } }, }, ], unevaluatedProperties: false }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 12, "prop2": 23, "x": true, "y": 23}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { oneOf: [ { properties: { prop1: { type: 'number' } }, additionalProperties: { type: 'boolean' } }, { properties: { prop2: { type: 'number' } }, required: ['prop2'] }, ], unevaluatedProperties: false }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 12, "prop3": true }'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } schema = { "title": "Vehicle", "type": "object", "oneOf": [ { "title": "Car", "required": ["wheels", "headlights"], "properties": { "wheels": {}, "headlights": {} } }, { "title": "Boat", "required": ["pontoons"], "properties": { "pontoons": {} } }, { "title": "Plane", "required": ["wings"], "properties": { "wings": {} } } ], "unevaluatedProperties": false }; { const { textDoc, jsonDoc } = toDocument('{"pontoons": 1}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"pontoons": 1, "wheels" 2}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { if: { properties: { prop1: { type: 'number' } }, }, then: { required: ['prop2'], properties: { prop2: { type: 'boolean' } }, }, else: { required: ['prop3'], properties: { prop3: { type: 'boolean' } }, }, unevaluatedProperties: false }; { const { textDoc, jsonDoc } = toDocument('{"prop1": 12, "prop2": true }'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('enum', function () { let schema: JSONSchema = { properties: { 'prop': { enum: ['violin', 'harmonica', 'banjo'] } } }; { const { textDoc, jsonDoc } = toDocument('{"prop": "harmonica"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop": "harp"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { properties: { 'prop': { enum: [1, 42, 999] } } }; { const { textDoc, jsonDoc } = toDocument('{"prop": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop": 1337}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { properties: { 'prop': { enum: ['violin', { "name": "David" }, null] } } }; { const { textDoc, jsonDoc } = toDocument('{"prop": { "name": "David" }}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('const', function () { const schema: JSONSchema = { properties: { 'prop': { const: 'violin' } } }; { const { textDoc, jsonDoc } = toDocument('{"prop": "violin"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop": "harmonica"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].code, ErrorCode.EnumValueMismatch); } { const schema = { properties: { 'prop': { const: { foo: 2 } } } }; const { textDoc, jsonDoc } = toDocument('{"prop": { "foo": 2 }'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('oneOf const', function () { const schema: JSONSchema = { properties: { 'prop': { oneOf: [ { "const": 0, "title": "Value of 0" }, { "const": 1, "title": "Value of 1" }, { "const": 2, "title": "Value of 2" } ] } } }; { const { textDoc, jsonDoc } = toDocument('{"prop": 0}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop": 4}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].code, ErrorCode.EnumValueMismatch); } }); test('propertyNames', function () { const schema: JSONSchema = { propertyNames: { type: 'string', minLength: 2, maxLength: 6 } }; { const { textDoc, jsonDoc } = toDocument('{"violin": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"harmonica": false, "violin": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].message, "String is longer than the maximum length of 6."); } }); test('uniqueItems', function () { const { textDoc, jsonDoc } = toDocument('[1, 2, 3]'); const schema: JSONSchema = { type: 'array', uniqueItems: true }; { const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[1, 2, 3, 2]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('[1, 2, "string", 52, "string"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('containsItem', function () { const schema: JSONSchema = { type: 'array', contains: { type: "number", const: 3 } }; { const { textDoc, jsonDoc } = toDocument('[1, 2, 3]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[1, 2, 5]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('minContains / maxContains', function () { let schema: JSONSchema = { type: 'array', contains: { type: "string" }, "minContains": 1, "maxContains": 3 }; { const { textDoc, jsonDoc } = toDocument('["1", 2, 3]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[1, 2, 3]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('["1", "2", "3", 4]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('["1", "2", "3", "4"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { type: 'array', contains: { type: "string" }, "minContains": 0, "maxContains": 1 }; { const { textDoc, jsonDoc } = toDocument('[ 1 ]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[ 1, "1" ]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[ 1, "1", "2" ]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('items as array / prefixItems', function () { let schema: JSONSchema = { type: 'array', items: [ { type: 'integer' }, { type: 'boolean' }, { type: 'string' } ] }; { const { textDoc, jsonDoc } = toDocument('[1, true, "string"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('["string", 1, true]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 3); } { const { textDoc, jsonDoc } = toDocument('[1, true, "string", "another", 42]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } schema = { type: 'array', prefixItems: [ { type: 'integer' }, { type: 'boolean' } ], items: { type: 'string' } }; { const { textDoc, jsonDoc } = toDocument('[1, true, "string", "another"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[1, true, "string", "another", 1]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); assert.strictEqual(semanticErrors!.length, 1); } }); test('additionalItems', function () { let schema: JSONSchema = { type: 'array', items: [ { type: 'integer' }, { type: 'boolean' }, { type: 'string' } ], additionalItems: false }; { const { textDoc, jsonDoc } = toDocument('[1, true, "string"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[1, true, "string", 42]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { type: 'array', items: [ { type: 'integer' }, { type: 'boolean' }, { type: 'string' } ], additionalItems: { type: "boolean" } }; { const { textDoc, jsonDoc } = toDocument('[1, true, "string"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[1, true, "string", false, true]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[1, true, "string", true, "Hello"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('unevaluatedItems', function () { let schema: JSONSchema = { type: 'array', items: [ { type: 'integer' }, { type: 'boolean' } ], unevaluatedItems: false }; { const { textDoc, jsonDoc } = toDocument('[1, true]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2019_09); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[1, true, "string", 42]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2019_09); assert.strictEqual(semanticErrors!.length, 2); } schema = { anyOf: [ { type: 'array', items: [ { type: 'integer' }, { type: 'integer' } ], }, { type: 'array', items: [ { type: 'integer' }, { type: 'boolean' }, { type: 'boolean' } ], }, ], unevaluatedItems: false }; { const { textDoc, jsonDoc } = toDocument('[1, 1]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2019_09); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[1, true, true]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2019_09); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[1, true, true, true, "Hello"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2019_09); assert.strictEqual(semanticErrors!.length, 2); } schema = { "type": "array", "prefixItems": [{ "type": "string" }, { "type": "string" }], "contains": { "type": "string", "minLength": 3 }, "unevaluatedItems": false }; { const { textDoc, jsonDoc } = toDocument('["Hello", "Hello", "1"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('["Hello", "Hello", "Hello"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('["Hello", "Hello", "1", "Hello"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); assert.strictEqual(semanticErrors!.length, 1); } schema = { "type": "array", "items": [{ "type": "string" }, { "type": "string" }], "contains": { "type": "string", "minLength": 3 }, "unevaluatedItems": false }; { const { textDoc, jsonDoc } = toDocument('["Hello", "Hello", "1"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2019_09); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument('["Hello", "Hello", "Hello"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2019_09); assert.strictEqual(semanticErrors!.length, 1); } }); test('multipleOf', function () { const schema: JSONSchema = { type: 'array', items: { type: 'integer', multipleOf: 2 } }; { const { textDoc, jsonDoc } = toDocument('[42]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[43]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('multipleOf with floats', function () { let schema: JSONSchema = { type: 'array', items: { type: 'number', multipleOf: 2e-4 } }; { const { textDoc, jsonDoc } = toDocument('[0.0002,0.2,0.64,2e+6,2.2e+10]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('[2e-5,2e-10,1e-4]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 3); } schema = { type: 'array', items: { type: 'number', multipleOf: 2.000000001 } }; { const { textDoc, jsonDoc } = toDocument('[2.000000001e5,6.000000003e8]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('dependencies with array / dependentRequired', function () { let schema: JSONSchema = { type: 'object', properties: { a: { type: 'boolean' } }, dependencies: { a: ['b'] } }; { const { textDoc, jsonDoc } = toDocument('{"a":true, "b":42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"a":true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { "type": "object", "properties": { "name": { "type": "string" }, "credit_card": { "type": "number" }, "billing_address": { "type": "string" } }, "required": ["name"], "dependentRequired": { "credit_card": ["billing_address"] } }; { const { textDoc, jsonDoc } = toDocument(`{ "name": "John Doe", "credit_card": 5555555555555555, "billing_address": "555 Debtor's Lane" }`); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument(`{ "name": "John Doe", "credit_card": 5555555555555555 }`); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument(`{ "name": "John Doe", "billing_address": "555 Debtor's Lane" }`); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('dependencies with schema / dependentSchemas', function () { let schema: JSONSchema = { type: 'object', properties: { a: { type: 'boolean' } }, dependencies: { a: { properties: { b: { type: 'integer' } } } } }; { const { textDoc, jsonDoc } = toDocument('{"a":true, "b":42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"a":true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"a":true, "b": "string"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { "type": "object", "properties": { "name": { "type": "string" }, "credit_card": { "type": "number" } }, "required": ["name"], "dependentSchemas": { "credit_card": { "properties": { "billing_address": { "type": "string" } }, "required": ["billing_address"] } } }; { const { textDoc, jsonDoc } = toDocument(`{ "name": "John Doe", "credit_card": 5555555555555555, "billing_address": "555 Debtor's Lane" }`); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument(`{ "name": "John Doe", "credit_card": 5555555555555555 }`); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } { const { textDoc, jsonDoc } = toDocument(`{ "name": "John Doe", "billing_address": "555 Debtor's Lane" }`); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('type as array', function () { const schema: JSONSchema = { type: 'object', properties: { 'prop': { type: ['number', 'string'] } } }; { const { textDoc, jsonDoc } = toDocument('{"prop": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop": "string"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"prop": true}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('deprecated', function () { let schema: JSONSchema = { type: 'object', properties: { 'prop': { deprecationMessage: "Prop is deprecated" } } }; { const { textDoc, jsonDoc } = toDocument('{"prop": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { type: 'object', properties: { 'prop': { deprecated: true } } }; { const { textDoc, jsonDoc } = toDocument('{"prop": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } schema = { deprecated: true, type: 'object' }; { const { textDoc, jsonDoc } = toDocument('{"prop": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('Strings with spaces', function () { const { textDoc, jsonDoc } = toDocument('{"key1":"first string", "key2":["second string"]}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); let node = jsonDoc.getNodeFromOffset(9); assert.strictEqual(getNodeValue(node!), 'first string'); node = jsonDoc.getNodeFromOffset(34); assert.strictEqual(getNodeValue(node!), 'second string'); }); test('Schema information on node', function () { const { jsonDoc } = toDocument('{"key":42}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const schema: JSONSchema = { type: 'object', properties: { 'key': { oneOf: [{ type: 'number', description: 'this is a number' }, { type: 'string', description: 'this is a string' }] } } }; const node = jsonDoc.getNodeFromOffset(7); assert.strictEqual(node!.type, 'number'); assert.strictEqual(getNodeValue(node!), 42); const matchingSchemas = jsonDoc.getMatchingSchemas(schema); const schemas = matchingSchemas.filter((s) => s.node === node && !s.inverted).map((s) => s.schema); assert.ok(Array.isArray(schemas)); // 0 is the most specific schema, // 1 is the schema that contained the "oneOf" clause, assert.strictEqual(schemas.length, 2); assert.strictEqual(schemas[0].description, 'this is a number'); }); test('parse with comments', function () { function parse(v: string): T { const { jsonDoc } = toDocument(v); assert.equal(jsonDoc.syntaxErrors.length, 0); return getNodeValue(jsonDoc.root!); } let value = parse<{ far: string; }>('// comment\n{\n"far": "boo"\n}'); assert.equal(value.far, 'boo'); value = parse<{ far: string; }>('/* comm\nent\nent */\n{\n"far": "boo"\n}'); assert.equal(value.far, 'boo'); value = parse<{ far: string; }>('{\n"far": "boo"\n}'); assert.equal(value.far, 'boo'); }); test('parse with comments collected', function () { function assertParse(v: string, expectedComments: number): void { const { jsonDoc } = toDocument(v); assert.equal(jsonDoc.comments.length, expectedComments); } assertParse('// comment\n{\n"far": "boo"\n}', 1); assertParse('/* comm\nent\nent */\n{\n"far": "boo"\n}', 1); assertParse('{\n"far": "boo"\n}', 0); }); test('validate alternatives', function () { const schema: JSONSchema = { type: 'object', properties: { 'key': { oneOf: [{ type: 'object', properties: { type: { enum: ['foo'] }, prop1: { type: 'boolean' }, prop2: { type: 'boolean' } } }, { type: 'object', properties: { type: { enum: ['bar'] }, prop2: { type: 'number' } } }] } } }; { const { textDoc, jsonDoc } = toDocument('{"key":{"type":"foo", "prop2":1 }}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].message, 'Incorrect type. Expected "boolean".'); } { const { textDoc, jsonDoc } = toDocument('{"key":{"type":"bar", "prop1":true, "prop2":false }}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].message, 'Incorrect type. Expected "number".'); } }); test('validate alternatives 2', function () { const schema: JSONSchema = { type: 'object', properties: { 'key': { oneOf: [{ type: 'object', properties: { type: { enum: ['foo'] }, prop1: { enum: ['v1, v2'] }, prop2: { enum: ['w1', 'w2'] } } }, { type: 'object', properties: { type: { enum: ['bar'] }, prop2: { enum: ['x1', 'x2'] } } }] } } }; { const { textDoc, jsonDoc } = toDocument('{"key":{"type":"foo", "prop2":"x1" }}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].message, 'Value is not accepted. Valid values: "w1", "w2".'); } { const { textDoc, jsonDoc } = toDocument('{"key":{"type":"bar", "prop1":"v1", "prop2":"w1" }}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].message, 'Value is not accepted. Valid values: "x1", "x2".'); } }); test('enum value merge', function () { const schema: JSONSchema = { type: 'object', properties: { 'key': { oneOf: [{ enum: ["a", "b"] }, { enum: ["c", "d"] }] } } }; const { textDoc, jsonDoc } = toDocument('{"key":3 }'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].message, 'Value is not accepted. Valid values: "a", "b", "c", "d".'); }); test('validate DocumentLanguageSettings: trailingCommas', async function () { const { textDoc, jsonDoc } = toDocument('{ "pages": [ "pages/index", "pages/log", ] }'); const ls = getLanguageService({}); let res = await ls.doValidation(textDoc, jsonDoc); assert.strictEqual(res.length, 1); assert.strictEqual(res[0].message, 'Trailing comma'); assert.strictEqual(res[0].severity, DiagnosticSeverity.Error); res = await ls.doValidation(textDoc, jsonDoc, {}); assert.strictEqual(res.length, 1); assert.strictEqual(res[0].message, 'Trailing comma'); assert.strictEqual(res[0].severity, DiagnosticSeverity.Error); res = await ls.doValidation(textDoc, jsonDoc, { trailingCommas: 'warning' }); assert.strictEqual(res.length, 1); assert.strictEqual(res[0].message, 'Trailing comma'); assert.strictEqual(res[0].severity, DiagnosticSeverity.Warning); res = await ls.doValidation(textDoc, jsonDoc, { trailingCommas: 'ignore' }); assert.strictEqual(res.length, 0); const schema: JSONSchema = { type: 'object', required: ['foo'] }; res = await ls.doValidation(textDoc, jsonDoc, { trailingCommas: 'ignore' }, schema); assert.strictEqual(res.length, 1); assert.strictEqual(res[0].message, 'Missing property "foo".'); }); test('validate DocumentLanguageSettings: comments', async function () { const { textDoc, jsonDoc } = toDocument('{ "count": 1 /* change */ }'); const ls = getLanguageService({}); ls.configure({ allowComments: false }); let res = await ls.doValidation(textDoc, jsonDoc); assert.strictEqual(res.length, 1); assert.strictEqual(res[0].message, 'Comments are not permitted in JSON.'); assert.strictEqual(res[0].severity, DiagnosticSeverity.Error); res = await ls.doValidation(textDoc, jsonDoc, {}); assert.strictEqual(res.length, 1); assert.strictEqual(res[0].message, 'Comments are not permitted in JSON.'); assert.strictEqual(res[0].severity, DiagnosticSeverity.Error); res = await ls.doValidation(textDoc, jsonDoc, { comments: 'ignore' }); assert.strictEqual(res.length, 0); res = await ls.doValidation(textDoc, jsonDoc, { comments: 'warning' }); assert.strictEqual(res.length, 1); assert.strictEqual(res[0].message, 'Comments are not permitted in JSON.'); assert.strictEqual(res[0].severity, DiagnosticSeverity.Warning); }); test('discriminator optimization: object with const property - oneOf', function () { // Test basic discriminator optimization with const values const schema: JSONSchema = { oneOf: [ { type: 'object', properties: { type: { const: 'cat' }, meow: { type: 'string' } } }, { type: 'object', properties: { type: { const: 'dog' }, bark: { type: 'string' } } }, { type: 'object', properties: { type: { const: 'bird' }, chirp: { type: 'string' } } } ] }; // Valid: matches cat schema { const { textDoc, jsonDoc } = toDocument('{"type": "cat", "meow": "meow"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } // Valid: matches dog schema { const { textDoc, jsonDoc } = toDocument('{"type": "dog", "bark": "woof"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } // Invalid: type matches cat but property type is wrong { const { textDoc, jsonDoc } = toDocument('{"type": "cat", "meow": 123}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.strictEqual(semanticErrors![0].message, 'Incorrect type. Expected "string".'); } // Invalid: unknown discriminator value { const { textDoc, jsonDoc } = toDocument('{"type": "fish", "swim": "yes"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('discriminator optimization: object with const property - anyOf', function () { const schema: JSONSchema = { anyOf: [ { type: 'object', properties: { kind: { const: 'circle' }, radius: { type: 'number' } } }, { type: 'object', properties: { kind: { const: 'square' }, side: { type: 'number' } } } ] }; // Valid: circle { const { textDoc, jsonDoc } = toDocument('{"kind": "circle", "radius": 5}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } // Valid: square { const { textDoc, jsonDoc } = toDocument('{"kind": "square", "side": 10}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } // Invalid: kind matches but property type wrong { const { textDoc, jsonDoc } = toDocument('{"kind": "circle", "radius": "not a number"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('discriminator optimization: no optimization when not all alternatives have const', function () { // Schema where not all alternatives have const discriminator const schema: JSONSchema = { oneOf: [ { type: 'object', properties: { type: { const: 'a' }, value: { type: 'string' } } }, { type: 'object', properties: { type: { type: 'string' }, // Not const, just type value: { type: 'number' } } } ] }; // Should still work but without optimization { const { textDoc, jsonDoc } = toDocument('{"type": "a", "value": "test"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"type": "b", "value": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('discriminator optimization: empty object', function () { const schema: JSONSchema = { oneOf: [ { type: 'object', properties: { type: { const: 'a' } } }, { type: 'object', properties: { type: { const: 'b' } } } ] }; // Empty object should not crash { const { textDoc, jsonDoc } = toDocument('{}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); // Should have errors for not matching any oneOf assert.ok(semanticErrors!.length > 0); } }); test('discriminator optimization: no properties in alternative', function () { const schema: JSONSchema = { oneOf: [ { type: 'object', properties: { type: { const: 'a' } } }, { type: 'object' // No properties defined } ] }; // Should not crash, optimization should not apply // Since one alternative has no properties, optimization won't be used // This matches multiple schemas (both alternatives), so oneOf fails { const { textDoc, jsonDoc } = toDocument('{"type": "a"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.ok(semanticErrors![0].message.includes('multiple schemas')); } }); test('discriminator optimization: non-string discriminator value', function () { const schema: JSONSchema = { oneOf: [ { type: 'object', properties: { type: { const: 'text' }, value: { type: 'string' } } }, { type: 'object', properties: { type: { const: 'number' }, value: { type: 'number' } } } ] }; // Discriminator value is a number, not string - optimization should not apply { const { textDoc, jsonDoc } = toDocument('{"type": 123, "value": "test"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.ok(semanticErrors!.length > 0); } }); test('discriminator optimization: array with const items - prefixItems', function () { const schema: JSONSchema = { oneOf: [ { type: 'array', prefixItems: [ { const: 'cat' }, { type: 'string' } ], items: false // No additional items allowed }, { type: 'array', prefixItems: [ { const: 'dog' }, { type: 'number' } ], items: false // No additional items allowed }, { type: 'array', prefixItems: [ { const: 'bird' }, { type: 'boolean' } ], items: false // No additional items allowed } ] }; // Valid: cat array { const { textDoc, jsonDoc } = toDocument('["cat", "meow"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); assert.strictEqual(semanticErrors!.length, 0); } // Valid: dog array { const { textDoc, jsonDoc } = toDocument('["dog", 42]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); assert.strictEqual(semanticErrors!.length, 0); } // Invalid: type matches cat but second element wrong type { const { textDoc, jsonDoc } = toDocument('["cat", 123]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); // With optimization, only the "cat" schema is tested // The second item should be a string, but is a number assert.strictEqual(semanticErrors!.length, 1); assert.ok(semanticErrors![0].message.includes('string')); } // Invalid: unknown discriminator { const { textDoc, jsonDoc } = toDocument('["fish", "swim"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); assert.ok(semanticErrors!.length > 0); } }); test('discriminator optimization: array with const items - items array', function () { const schema: JSONSchema = { oneOf: [ { type: 'array', items: [ { const: 'type1' }, { type: 'string' } ] }, { type: 'array', items: [ { const: 'type2' }, { type: 'number' } ] } ] }; // Valid: type1 { const { textDoc, jsonDoc } = toDocument('["type1", "value"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } // Valid: type2 { const { textDoc, jsonDoc } = toDocument('["type2", 42]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('discriminator optimization: empty array', function () { const schema: JSONSchema = { oneOf: [ { type: 'array', prefixItems: [ { const: 'a' } ] }, { type: 'array', prefixItems: [ { const: 'b' } ] } ] }; // Empty array should not crash { const { textDoc, jsonDoc } = toDocument('[]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.ok(semanticErrors!.length > 0); } }); test('discriminator optimization: array without item schemas', function () { const schema: JSONSchema = { oneOf: [ { type: 'array', prefixItems: [ { const: 'a' } ] }, { type: 'array' // No items or prefixItems } ] }; // Should not crash // Since one alternative has no item schemas, optimization won't be used // Both alternatives match, so oneOf fails { const { textDoc, jsonDoc } = toDocument('["a"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.ok(semanticErrors![0].message.includes('multiple schemas')); } }); test('discriminator optimization: array with non-string discriminator', function () { const schema: JSONSchema = { oneOf: [ { type: 'array', prefixItems: [ { const: 'type1' }, { type: 'string' } ] }, { type: 'array', prefixItems: [ { const: 'type2' }, { type: 'string' } ] } ] }; // First element is number, not string - optimization should not apply { const { textDoc, jsonDoc } = toDocument('[123, "value"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.ok(semanticErrors!.length > 0); } }); test('discriminator optimization: single alternative', function () { // With only one alternative, no optimization should be attempted const schema: JSONSchema = { oneOf: [ { type: 'object', properties: { type: { const: 'only' }, value: { type: 'string' } } } ] }; { const { textDoc, jsonDoc } = toDocument('{"type": "only", "value": "test"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"type": "other", "value": "test"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.ok(semanticErrors!.length > 0); } }); test('discriminator optimization: multiple const properties but not all alternatives covered', function () { const schema: JSONSchema = { oneOf: [ { type: 'object', properties: { type: { const: 'a' }, subtype: { const: 'x' }, value: { type: 'string' } } }, { type: 'object', properties: { type: { const: 'b' }, // No subtype const value: { type: 'number' } } } ] }; // Only 'type' covers all alternatives, should use that { const { textDoc, jsonDoc } = toDocument('{"type": "a", "subtype": "x", "value": "test"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"type": "b", "value": 42}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('discriminator optimization: discriminator matches but validation fails', function () { const schema: JSONSchema = { oneOf: [ { type: 'object', properties: { type: { const: 'person' }, name: { type: 'string', minLength: 5 }, age: { type: 'number', minimum: 18 } }, required: ['name', 'age'] }, { type: 'object', properties: { type: { const: 'company' }, name: { type: 'string' }, employees: { type: 'number' } } } ] }; // Discriminator matches 'person' but validation fails (name too short) { const { textDoc, jsonDoc } = toDocument('{"type": "person", "name": "Joe", "age": 25}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.ok(semanticErrors![0].message.includes('minimum length')); } // Discriminator matches 'person' but validation fails (age too low) { const { textDoc, jsonDoc } = toDocument('{"type": "person", "name": "Alice", "age": 15}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.ok(semanticErrors![0].message.includes('minimum')); } // Discriminator matches 'person' but required property missing { const { textDoc, jsonDoc } = toDocument('{"type": "person", "name": "Alice"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); assert.ok(semanticErrors![0].message.includes('Missing property')); } // Valid person { const { textDoc, jsonDoc } = toDocument('{"type": "person", "name": "Alice", "age": 25}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } // Valid company { const { textDoc, jsonDoc } = toDocument('{"type": "company", "name": "ACME", "employees": 100}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('discriminator optimization: complex nested schema', function () { const schema: JSONSchema = { type: 'object', properties: { data: { oneOf: [ { type: 'object', properties: { kind: { const: 'text' }, content: { type: 'string' } } }, { type: 'object', properties: { kind: { const: 'number' }, content: { type: 'number' } } } ] } } }; // Valid nested with discrimination { const { textDoc, jsonDoc } = toDocument('{"data": {"kind": "text", "content": "hello"}}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 0); } // Invalid nested - discriminator matches but type wrong { const { textDoc, jsonDoc } = toDocument('{"data": {"kind": "text", "content": 123}}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); assert.strictEqual(semanticErrors!.length, 1); } }); test('discriminator optimization: array index discriminator at different positions', function () { // Test discriminator at index 1 instead of 0 const schema: JSONSchema = { oneOf: [ { type: 'array', prefixItems: [ { type: 'number' }, { const: 'typeA' }, { type: 'string' } ], items: false // No additional items allowed }, { type: 'array', prefixItems: [ { type: 'number' }, { const: 'typeB' }, { type: 'boolean' } ], items: false // No additional items allowed } ] }; // Valid: typeA { const { textDoc, jsonDoc } = toDocument('[42, "typeA", "hello"]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); assert.strictEqual(semanticErrors!.length, 0); } // Valid: typeB { const { textDoc, jsonDoc } = toDocument('[42, "typeB", true]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); assert.strictEqual(semanticErrors!.length, 0); } // Invalid: discriminator matches typeA but third element wrong type { const { textDoc, jsonDoc } = toDocument('[42, "typeA", true]'); const semanticErrors = validate2(jsonDoc, textDoc, schema, SchemaDraft.v2020_12); // With optimization, typeA schema is correctly selected // The third element should be a string but is boolean assert.strictEqual(semanticErrors!.length, 1); assert.ok(semanticErrors![0].message.includes('string')); } }); test('discriminator optimization: discriminator value present but no matching alternative', function () { const schema: JSONSchema = { oneOf: [ { type: 'object', properties: { type: { const: 'a' }, value: { type: 'string' } } }, { type: 'object', properties: { type: { const: 'b' }, value: { type: 'number' } } } ] }; // Discriminator value 'c' doesn't match any alternative { const { textDoc, jsonDoc } = toDocument('{"type": "c", "value": "test"}'); const semanticErrors = validate2(jsonDoc, textDoc, schema); // Should fall back to testing all alternatives assert.ok(semanticErrors!.length > 0); } }); test('self-referencing schema with anyOf and deep nesting (exploding complexity test)', async function () { // Schema with discriminator and self-references const schema: JSONSchema = { "$schema": "http://json-schema.org/draft-07/schema#", "anyOf": [ { "type": "object", "properties": { "type": { "title": "literal", "type": "string", "const": "literal" }, "value": { "type": "string" } }, "required": ["type"], "additionalProperties": false }, { "type": "object", "properties": { "type": { "title": "group", "type": "string", "const": "group" }, "children": { "minItems": 2, "type": "array", "items": { "$ref": "#" } } }, "required": ["type", "children"], "additionalProperties": false }, { "type": "object", "properties": { "type": { "title": "sequence", "type": "string", "const": "sequence" }, "children": { "minItems": 2, "type": "array", "items": { "$ref": "#" } } }, "required": ["type", "children"], "additionalProperties": false }, { "type": "object", "properties": { "type": { "title": "wrapper", "type": "string", "const": "wrapper" }, "children": { "type": "array", "items": [ { "$ref": "#" } ], "minItems": 1, "maxItems": 1 } }, "required": ["type", "children"], "additionalProperties": false } ] }; // Create a deeply nested JSON (100 levels of "wrapper" operations) let deepJson: any = { "type": "literal", "value": "test" }; for (let i = 0; i < 100; i++) { deepJson = { "type": "wrapper", "children": [deepJson] }; } const jsonString = JSON.stringify(deepJson); const { textDoc, jsonDoc } = toDocument(jsonString); const ls = getLanguageService({}); ls.configure({ schemas: [{ fileMatch: ["*.json"], uri: "http://myschemastore/explode", schema }] }); let res = await ls.doValidation(textDoc, jsonDoc, undefined, schema); assert.strictEqual(res.length, 0); }); }); vscode-json-languageservice-5.7.2/src/test/schema.test.ts000066400000000000000000001733251514707600300234730ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as SchemaService from '../services/jsonSchemaService'; import * as Parser from '../parser/jsonParser'; import { promises as fs } from 'fs'; import * as url from 'url'; import * as path from 'path'; import { getLanguageService, JSONSchema, SchemaRequestService, TextDocument, MatchingSchema, LanguageService } from '../jsonLanguageService'; import { DiagnosticSeverity, ErrorCode, Range, SchemaConfiguration } from '../jsonLanguageTypes'; function toDocument(text: string, config?: Parser.JSONDocumentConfig, uri = 'foo://bar/file.json'): { textDoc: TextDocument, jsonDoc: Parser.JSONDocument } { const textDoc = TextDocument.create(uri, 'json', 0, text); const jsonDoc = Parser.parse(textDoc, config); return { textDoc, jsonDoc }; } suite('JSON Schema', () => { const fixureDocuments: { [uri: string]: string } = { 'http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json': 'deploymentTemplate.json', 'http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json': 'deploymentParameters.json', 'http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Authorization.json': 'Microsoft.Authorization.json', 'http://schema.management.azure.com/schemas/2015-01-01/Microsoft.Resources.json': 'Microsoft.Resources.json', 'http://schema.management.azure.com/schemas/2014-04-01-preview/Microsoft.Sql.json': 'Microsoft.Sql.json', 'http://schema.management.azure.com/schemas/2014-06-01/Microsoft.Web.json': 'Microsoft.Web.json', 'http://schema.management.azure.com/schemas/2014-04-01/SuccessBricks.ClearDB.json': 'SuccessBricks.ClearDB.json', 'http://schema.management.azure.com/schemas/2015-08-01/Microsoft.Compute.json': 'Microsoft.Compute.json' }; function newMockRequestService(schemas: { [uri: string]: JSONSchema } = {}, accesses: string[] = []): SchemaRequestService { return async (uri: string): Promise => { if (uri.length && uri[uri.length - 1] === '#') { uri = uri.substr(0, uri.length - 1); } const schema = schemas[uri]; if (schema) { if (accesses.indexOf(uri) === -1) { accesses.push(uri); } return Promise.resolve(JSON.stringify(schema)); } const fileName = fixureDocuments[uri]; if (fileName) { const fixturePath = path.join(__dirname, '../../../src/test/fixtures', fileName); return (await fs.readFile(fixturePath)).toString(); } throw new Error("Resource not found"); }; } const workspaceContext = { resolveRelativePath: (relativePath: string, resource: string) => { return url.resolve(resource, relativePath); } }; test('Resolving $refs', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main": { id: 'https://myschemastore/main', type: 'object', properties: { child: { '$ref': 'https://myschemastore/child' } } }, "https://myschemastore/child": { id: 'https://myschemastore/child', type: 'bool', description: 'Test description' } } }); const fs = await service.getResolvedSchema('https://myschemastore/main'); assert.deepStrictEqual(fs?.schema.properties?.['child'], { type: 'bool', description: 'Test description' }); }); test('Resolving $refs 2', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "http://json.schemastore.org/swagger-2.0": { id: 'http://json.schemastore.org/swagger-2.0', type: 'object', properties: { "responseValue": { "$ref": "#/definitions/jsonReference" } }, definitions: { "jsonReference": { "type": "object", "required": ["$ref"], "properties": { "$ref": { "type": "string" } } } } } } }); const fs = await service.getResolvedSchema('http://json.schemastore.org/swagger-2.0'); assert.deepStrictEqual(fs?.schema.properties?.['responseValue'], { type: 'object', required: ["$ref"], properties: { $ref: { type: 'string' } } }); }); test('Resolving $refs 3', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { id: 'https://myschemastore/main/schema1.json', type: 'object', properties: { p1: { '$ref': 'schema2.json#/definitions/hello' }, p2: { '$ref': './schema2.json#/definitions/hello' }, p3: { '$ref': '/main/schema2.json#/definitions/hello' } } }, "https://myschemastore/main/schema2.json": { id: 'https://myschemastore/main/schema2.json', definitions: { "hello": { "type": "string", "enum": ["object"], } } } } }); const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(fs?.schema.properties?.['p1'], { type: 'string', enum: ["object"] }); assert.deepStrictEqual(fs?.schema.properties?.['p2'], { type: 'string', enum: ["object"] }); assert.deepStrictEqual(fs?.schema.properties?.['p3'], { type: 'string', enum: ["object"] }); }); test('Resolving $refs 4', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { id: 'https://myschemastore/main/schema1.json', type: 'object', properties: { p1: { '$ref': 'schema2.json#/definitions/hello' }, p2: { '$ref': './schema2.json#/definitions/hello' }, p3: { '$ref': '/main/schema2.json#/definitions/hello' } } }, "https://myschemastore/main/schema2.json": { id: 'https://myschemastore/main/schema2.json', definitions: { "hello": { "type": "string", "enum": ["object"], } } } } }); const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(fs?.schema.properties?.['p1'], { type: 'string', enum: ["object"] }); assert.deepStrictEqual(fs?.schema.properties?.['p2'], { type: 'string', enum: ["object"] }); assert.deepStrictEqual(fs?.schema.properties?.['p3'], { type: 'string', enum: ["object"] }); }); test('Resolving escaped $refs', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { id: 'https://myschemastore/main/schema1.json', type: 'object', properties: { p1: { '$ref': 'schema2.json#/definitions/hello~0foo~1bar' }, p2: { '$ref': './schema2.json#/definitions/hello~0foo~1bar' }, p3: { '$ref': '/main/schema2.json#/definitions/hello~0foo~1bar' } } }, "https://myschemastore/main/schema2.json": { id: 'https://myschemastore/main/schema2.json', definitions: { "hello~foo/bar": { "type": "string", } } } } }); const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(fs?.schema.properties?.['p1'], { type: 'string' }); assert.deepStrictEqual(fs?.schema.properties?.['p2'], { type: 'string' }); assert.deepStrictEqual(fs?.schema.properties?.['p3'], { type: 'string' }); }); test('Resolving $refs to local $ids', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { id: 'https://myschemastore/main/schema1.json', definitions: { hello: { id: '#hello', type: 'string', const: 'hello' }, world: { $id: '#world', type: 'string', const: 'world' } }, type: 'object', properties: { p1: { $ref: '#hello' }, p2: { $ref: '#world' } } } } }); const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(fs?.schema.properties?.p1, { type: 'string', const: 'hello' }); assert.deepStrictEqual(fs?.schema.properties?.p2, { type: 'string', const: 'world' }); }); test('Resolving $refs to local $anchors', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://example.com/schemas/address": { "$id": "https://example.com/schemas/address", "type": "object", "properties": { "street_address": { "$anchor": "street_address", "type": "string" }, "city": { "type": "string" }, "state": { "type": "string" } }, "required": ["street_address", "city", "state"] }, "https://example.com/schemas/customer": { "$id": "https://example.com/schemas/customer", "type": "object", "properties": { "first_name": { "type": "string" }, "last_name": { "type": "string" }, "street_address": { "$ref": "/schemas/address#street_address" }, } } } }); const fs = await service.getResolvedSchema('https://example.com/schemas/customer'); assert.deepStrictEqual(fs?.schema.properties?.street_address, { type: 'string', $anchor: "street_address" }); }); test('Resolving $refs to external $ids', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { id: 'https://myschemastore/main/schema1.json', type: 'object', properties: { p1: { '$ref': 'schema2.json#hello' }, p2: { '$ref': './schema2.json#/definitions/hello' }, p3: { '$ref': '/main/schema2.json#/definitions/hello' } } }, "https://myschemastore/main/schema2.json": { id: 'https://myschemastore/main/schema2.json', definitions: { "hello": { $id: "#hello", "type": "string", "enum": ["object"], } } } } }); const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(fs?.schema.properties?.['p1'], { type: 'string', enum: ["object"] }); assert.deepStrictEqual(fs?.schema.properties?.['p2'], { type: 'string', enum: ["object"] }); assert.deepStrictEqual(fs?.schema.properties?.['p3'], { type: 'string', enum: ["object"] }); }); test('Resolving $refs to external $ids with same as local', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { id: 'https://myschemastore/main/schema1.json', definitions: { "hello": { $id: "#hello", "type": "string", "const": "wrong", } }, type: 'object', properties: { p1: { '$ref': 'schema2.json#hello' } } }, "https://myschemastore/main/schema2.json": { id: 'https://myschemastore/main/schema2.json', definitions: { "hello": { $id: "#hello", "type": "string", "const": "correct" } } } } }); const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(fs?.schema.properties?.['p1'], { type: 'string', const: 'correct' }); }); test('Resolving external $ref two levels', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { id: 'https://myschemastore/main/schema1.json', type: 'object', properties: { p1: { $ref: 'schema2.json#blue' } } }, "https://myschemastore/main/schema3.json": { id: 'https://myschemastore/main/schema3.json', definitions: { "world": { $id: '#world', type: 'string', const: 'world' } } }, "https://myschemastore/main/schema2.json": { id: 'https://myschemastore/main/schema2.json', definitions: { "_blue": { $id: '#blue', $ref: 'schema3.json#world', description: '_blue', } } } } }); const resolvedSchema = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(resolvedSchema?.schema.properties?.p1, { type: 'string', const: 'world', description: '_blue' }); }); test('Resolving external $ref referenced multiple times', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { id: 'https://myschemastore/main/schema1.json', type: 'object', properties: { p1: { $ref: 'schema2.json#blue' }, p2: { '$ref': 'https://myschemastore/main/schema2.json#blue' }, p3: { '$ref': 'https://myschemastore/main/schema2.json#/definitions/_blue' } } }, "https://myschemastore/main/schema2.json": { id: 'https://myschemastore/main/schema2.json', definitions: { "_blue": { $id: '#blue', const: 'blue' } } } } }); const resolvedSchema = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(resolvedSchema?.schema.properties?.p1, { const: 'blue' }); assert.deepStrictEqual(resolvedSchema?.schema.properties?.p2, { const: 'blue' }); assert.deepStrictEqual(resolvedSchema?.schema.properties?.p3, { const: 'blue' }); }); test('Resolving external $ref to ref', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { id: 'https://myschemastore/main/schema1.json', type: 'object', properties: { p1: { '$ref': 'https://myschemastore/main/schema2.json#red' } } }, "https://myschemastore/main/schema2.json": { id: 'https://myschemastore/main/schema2.json', definitions: { "_red": { $id: '#red', $ref: '#yellow' }, "_yellow": { $id: '#yellow', type: 'number', const: 5 } } } } }); const resolvedSchema = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(resolvedSchema?.schema.properties?.p1, { type: 'number', const: 5 }); }); test('Resolving external $ref recursive', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { id: 'https://myschemastore/main/schema1.json', definitions: { "world": { $id: '#world', type: 'string', const: 'world' } }, type: 'object', properties: { p1: { $ref: 'schema2.json#blue' } } }, "https://myschemastore/main/schema2.json": { id: 'https://myschemastore/main/schema2.json', definitions: { "blue": { $id: '#blue', $ref: 'schema1.json#world' } } } } }); const resolvedSchema = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(resolvedSchema?.schema.properties?.p1, { type: 'string', const: 'world' }); }); test('Resolving external $ref to already resolved schema', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { type: 'object', properties: { p1: { $ref: 'schema2.json#blue' } } }, "https://myschemastore/main/schema3.json": { type: 'object', properties: { p1: { $ref: 'schema2.json#blue' } } }, "https://myschemastore/main/schema2.json": { definitions: { "blue": { $id: '#blue', type: 'string', const: 'blue' } } } } }); const resolvedSchema1 = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(resolvedSchema1?.schema.properties?.p1, { type: 'string', const: 'blue' }); const resolvedSchema3 = await service.getResolvedSchema('https://myschemastore/main/schema3.json'); assert.deepStrictEqual(resolvedSchema3?.schema.properties?.p1, { type: 'string', const: 'blue' }); }); test('Resolving $refs 5', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { "type": "object", "properties": { "p1": { "$ref": "#hello" }, "p2": { "$ref": "#world" }, "p3": { "id": "#hello", "type": "string", "const": "hello" }, "p4": { "type": "object", "properties": { "deep": { "$id": "#world", "type": "string", "const": "world" } }, "additionalProperties": false } }, "additionalProperties": false }, } }); const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(fs?.schema.properties?.['p1'], { type: 'string', const: 'hello' }); assert.deepStrictEqual(fs?.schema.properties?.['p2'], { "type": "string", "const": "world" }); }); test('Recursive $refs to $ids', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main/schema1.json": { "type": "object", "definitions": { "foo": { "id": "#foo", "type": "object", "properties": { "bar": { "type": "string", "const": "hello" }, "foo": { "$ref": "#foo" } }, "additionalProperties": false } }, "properties": { "foo": { "$ref": "#foo" } }, "additionalProperties": false } } }); const fs = await service.getResolvedSchema('https://myschemastore/main/schema1.json'); assert.deepStrictEqual(fs?.schema.properties?.['foo'], { "type": "object", "properties": { "bar": { "type": "string", "const": "hello" }, "foo": { "additionalProperties": false, properties: fs?.schema.definitions?.['foo'].properties, type: "object" } }, "additionalProperties": false }); }); test('FileSchema', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "test://schemas/main": { id: 'test://schemas/main', type: 'object', properties: { child: { type: 'object', properties: { 'grandchild': { type: 'number', description: 'Meaning of Life' } } } } } } }); const fs = await service.getResolvedSchema('test://schemas/main'); const section = fs?.getSection(['child', 'grandchild']); assert.strictEqual(section?.description, 'Meaning of Life'); }); test('Array FileSchema', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "test://schemas/main": { id: 'test://schemas/main', type: 'object', properties: { child: { type: 'array', items: { 'type': 'object', 'properties': { 'grandchild': { type: 'number', description: 'Meaning of Life' } } } } } } } }); const fs = await service.getResolvedSchema('test://schemas/main'); const section = fs?.getSection(['child', '0', 'grandchild']); assert.strictEqual(section?.description, 'Meaning of Life'); }); test('Missing subschema', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "test://schemas/main": { id: 'test://schemas/main', type: 'object', properties: { child: { type: 'object' } } } } }); const fs = await service.getResolvedSchema('test://schemas/main'); const section = fs?.getSection(['child', 'grandchild']); assert.strictEqual(section, undefined); }); test('Preloaded Schema', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const id = 'https://myschemastore/test1'; const schema: JSONSchema = { type: 'object', properties: { child: { type: 'object', properties: { 'grandchild': { type: 'number', description: 'Meaning of Life' } } } } }; service.registerExternalSchema({ uri: id, fileMatch: ['*.json'], schema: schema }); const fs = await service.getSchemaForResource('test.json'); const section = fs?.getSection(['child', 'grandchild']); assert.strictEqual(section?.description, 'Meaning of Life'); }); test('Preloaded Schema, string as URI', async function () { // for https://github.com/microsoft/monaco-editor/issues/2683 const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const id = 'a5f8f39b-c7ee-48f8-babe-b7146ed3c055'; const schema: JSONSchema = { type: 'object', properties: { child: { type: 'object', properties: { 'grandchild': { type: 'number', description: 'Meaning of Life' } } } } }; service.registerExternalSchema({ uri: id, fileMatch: ['*.json'], schema: schema }); const fs = await service.getSchemaForResource('test.json'); const section = fs?.getSection(['child', 'grandchild']); assert.strictEqual(section?.description, 'Meaning of Life'); }); test('Multiple matches', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const id1 = 'https://myschemastore/test1'; const schema1: JSONSchema = { type: 'object', properties: { foo: { enum: [1], } } }; const id2 = 'https://myschemastore/test2'; const schema2: JSONSchema = { type: 'object', properties: { bar: { enum: [2], } } }; service.registerExternalSchema({ uri: id1, fileMatch: ['*.json'], schema: schema1 }); service.registerExternalSchema({ uri: id2, fileMatch: ['test.json'], schema: schema2 }); const fs = await service.getSchemaForResource('test.json'); const { textDoc, jsonDoc } = toDocument(JSON.stringify({ foo: true, bar: true })); const problems = jsonDoc.validate(textDoc, fs?.schema); assert.strictEqual(problems?.length, 2); }); test('External Schema', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const id = 'https://myschemastore/test1'; const schema: JSONSchema = { type: 'object', properties: { child: { type: 'object', properties: { 'grandchild': { type: 'number', description: 'Meaning of Life' } } } } }; service.registerExternalSchema({ uri: id, fileMatch: ['*.json'], schema: schema }); const fs = await service.getSchemaForResource('test.json'); const section = fs?.getSection(['child', 'grandchild']); assert.strictEqual(section?.description, 'Meaning of Life'); }); test('Resolving in-line $refs', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const id = 'https://myschemastore/test1'; const schema: JSONSchema = { id: 'test://schemas/main', type: 'object', definitions: { 'grandchild': { type: 'number', description: 'Meaning of Life' } }, properties: { child: { type: 'array', items: { 'type': 'object', 'properties': { 'grandchild': { $ref: '#/definitions/grandchild' } } } } } }; service.registerExternalSchema({ uri: id, fileMatch: ['*.json'], schema: schema }); const fs = await service.getSchemaForResource('test.json'); const section = fs?.getSection(['child', '0', 'grandchild']); assert.strictEqual(section?.description, 'Meaning of Life'); }); test('Resolving in-line $refs automatically for external schemas', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const id = 'https://myschemastore/test1'; const schema: JSONSchema = { id: 'test://schemas/main', type: 'object', definitions: { 'grandchild': { type: 'number', description: 'Meaning of Life' } }, properties: { child: { type: 'array', items: { 'type': 'object', 'properties': { 'grandchild': { $ref: '#/definitions/grandchild' } } } } } }; const fsm = service.registerExternalSchema({ uri: id, fileMatch: ['*.json'], schema: schema }); const fs = await fsm.getResolvedSchema(); const section = fs.getSection(['child', '0', 'grandchild']); assert.strictEqual(section?.description, 'Meaning of Life'); }); test('Clearing External Schemas', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const id1 = 'http://myschemastore/test1'; const schema1: JSONSchema = { type: 'object', properties: { child: { type: 'number' } } }; const id2 = 'http://myschemastore/test2'; const schema2: JSONSchema = { type: 'object', properties: { child: { type: 'string' } } }; service.registerExternalSchema({ uri: id1, fileMatch: ['test.json', 'bar.json'], schema: schema1 }); const fs = await service.getSchemaForResource('test.json'); assert.strictEqual(fs?.getSection(['child'])?.type, 'number'); service.clearExternalSchemas(); service.registerExternalSchema({ uri: id2, fileMatch: ['*.json'], schema: schema2 }); const fs2 = await service.getSchemaForResource('test.json'); assert.strictEqual(fs2?.getSection(['child'])?.type, 'string'); }); test('Schema contributions', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "http://myschemastore/myschemabar": { id: 'http://myschemastore/myschemabar', type: 'object', properties: { foo: { type: 'string' } } } }, schemaAssociations: [ { pattern: ['*.bar'], uris: ['http://myschemastore/myschemabar', 'http://myschemastore/myschemafoo'] } ] }); const id2 = 'http://myschemastore/myschemafoo'; const schema2: JSONSchema = { type: 'object', properties: { child: { type: 'string' } } }; service.registerExternalSchema({ uri: id2, schema: schema2 }); let resolvedSchema = await service.getSchemaForResource('main.bar'); assert.deepStrictEqual(resolvedSchema?.errors, []); assert.strictEqual(2, resolvedSchema?.schema.allOf?.length); service.clearExternalSchemas(); resolvedSchema = await service.getSchemaForResource('main.bar'); const message = "Unable to load schema from 'http://myschemastore/myschemafoo': Resource not found."; assert.deepStrictEqual(resolvedSchema?.errors, [ { message: message, code: ErrorCode.SchemaResolveError, relatedInformation: [{ location: { uri: 'http://myschemastore/myschemafoo', range: Range.create(0, 0, 0, 0) }, message: message }] }]); service.clearExternalSchemas(); service.registerExternalSchema({ uri: id2, schema: schema2 }); resolvedSchema = await service.getSchemaForResource('main.bar'); assert.strictEqual(resolvedSchema?.errors.length, 0); }); test('Exclusive file patterns', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "http://myschemastore/myschemabar": { maxProperties: 0 } }, schemaAssociations: [ { pattern: ['/folder/*.json', '!/folder/bar/*.json', '/folder/bar/zoo.json'], uris: ['http://myschemastore/myschemabar'] } ] }); const positives = ['/folder/a.json', '/folder/bar.json', '/folder/bar/zoo.json']; const negatives = ['/folder/bar/a.json', '/folder/bar/z.json']; for (const positive of positives) { assert.ok(await service.getSchemaForResource(positive), positive); } for (const negative of negatives) { assert.ok(!await service.getSchemaForResource(negative), negative); } }); async function assertMatchingSchemas(ls: LanguageService, positives: string[], negatives: string[]) { for (const positive of positives) { const doc = toDocument("{}", undefined, positive); const ms = await ls.getMatchingSchemas(doc.textDoc, doc.jsonDoc); assert.ok(ms.length > 0, positive); } for (const negative of negatives) { const doc = toDocument("{}", undefined, negative); const ms = await ls.getMatchingSchemas(doc.textDoc, doc.jsonDoc); assert.ok(ms.length === 0, negative); } } test('Schema matching, where fileMatch is a literal pattern, and denotes filename only', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['part.json'], schema: { type: 'object', required: ['foo'] } }] }); const positives = ['file:///folder/part.json', 'file:///folder/part.json?f=true', 'file:///folder/part.json#f=true']; const negatives = ['file:///folder/rampart.json', 'file:///folder/part.json/no.part.json', 'file:///folder/foo?part.json', 'file:///folder/foo#part.json']; assertMatchingSchemas(ls, positives, negatives); }); test('Schema matching, match files starting with dots', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['/User/settings.json'], schema: { type: 'object', required: ['foo'] } }] }); const positives = ['vscode-userdata:/home/martin/.config/Code%20-%20Insiders/User/settings.json']; assertMatchingSchemas(ls, positives, []); }); test('Schema matching, where fileMatch is a literal pattern, and denotes a path with a leading slash', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['/folder/part.json'], schema: { type: 'object', required: ['foo'] } }] }); const positives = ['file:///folder/part.json', 'file:///folder/part.json?f=true', 'file:///folder/part.json#f=true']; const negatives = ['file:///folder/rampart.json', 'file:///folder/part.json/no.part.json', 'file:///folder/foo?part.json', 'file:///folder/foo#part.json']; assertMatchingSchemas(ls, positives, negatives); }); test('Schema matching, where fileMatch is a literal pattern, and denotes a path', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['take/part.json'], schema: { type: 'object', required: ['foo'] } }] }); const positives = ['file:///folder/take/part.json', 'file:///folder/take/part.json?f=true', 'file:///folder/take/part.json#f=true']; const negatives = ['file:///folder/part.json', 'file:///folder/.take/part.json', 'file:///folder/take.part.json', 'file:///folder/take/part.json/no.part.json', 'file:///folder/take?part.json', 'file:///folder/foo?take/part.json', 'file:///folder/take#part.json', 'file:///folder/foo#take/part.json', 'file:///folder/take/no/part.json']; assertMatchingSchemas(ls, positives, negatives); }); test('Schema matching, where fileMatch is a wildcard pattern, contains no double-star, and denotes filename only', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['*.foo.json'], schema: { type: 'object', required: ['foo'] } }] }); const positives = ['file:///folder/a.foo.json', 'file:///folder/a.foo.json?f=true', 'file:///folder/a.foo.json#f=true']; const negatives = ['file:///folder/a.bar.json', 'file:///folder/foo?a.foo.json', 'file:///folder/foo#a.foo.json']; assertMatchingSchemas(ls, positives, negatives); }); test('Schema matching, where fileMatch is a wildcard pattern, contains no double-star, and denotes a path', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['foo/*/bar.json'], schema: { type: 'object', required: ['foo'] } }] }); const positives = ['file:///folder/foo/bat/bar.json', 'file:///folder/foo/bat/bar.json?f=true', 'file:///folder/foo/bat/bar.json#f=true']; const negatives = ['file:///folder/a.bar.json', 'file:///folder/foo/bar.json', 'file:///folder/foo/can/be/as/deep/as/the/ocean/floor/bar.json', 'file:///folder/foo/bar.json?f=true', 'file:///folder/foo/can/be/as/deep/as/the/ocean/floor/bar.json?f=true', 'file:///folder/foo/bar.json#f=true', 'file:///folder/foo/can/be/as/deep/as/the/ocean/floor/bar.json#f=true', 'file:///folder/foo/bar.json/bat/bar.json', 'file:///folder/foo.bar.json', 'file:///folder/foo.bat/bar.json', 'file:///folder/foo/bar.json/bat.json', 'file:///folder/.foo/bar.json', 'file:///folder/.foo/bat/bar.json', 'file:///folder/.foo/bat/man/bar.json', 'file:///folder/foo?foo/bar.json', 'file:///folder/foo?foo/bat/bar.json', 'file:///folder/foo?foo/bat/man/bar.json', 'file:///folder/foo#foo/bar.json', 'file:///folder/foo#foo/bat/bar.json', 'file:///folder/foo#foo/bat/man/bar.json']; assertMatchingSchemas(ls, positives, negatives); }); test('Schema matching, where fileMatch is a wildcard pattern, contains double-star, and denotes a path', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['foo/**/bar.json'], schema: { type: 'object', required: ['foo'] } }] }); const positives = ['file:///folder/foo/bar.json', 'file:///folder/foo/bat/bar.json', 'file:///folder/foo/can/be/as/deep/as/the/ocean/floor/bar.json', 'file:///folder/foo/bar.json?f=true', 'file:///folder/foo/bat/bar.json?f=true', 'file:///folder/foo/can/be/as/deep/as/the/ocean/floor/bar.json?f=true', 'file:///folder/foo/bar.json#f=true', 'file:///folder/foo/bat/bar.json#f=true', 'file:///folder/foo/can/be/as/deep/as/the/ocean/floor/bar.json#f=true', 'file:///folder/foo/bar.json/bat/bar.json']; const negatives = ['file:///folder/a.bar.json', 'file:///folder/foo.bar.json', 'file:///folder/foo.bat/bar.json', 'file:///folder/foo/bar.json/bat.json', 'file:///folder/.foo/bar.json', 'file:///folder/.foo/bat/bar.json', 'file:///folder/.foo/bat/man/bar.json', 'file:///folder/foo?foo/bar.json', 'file:///folder/foo?foo/bat/bar.json', 'file:///folder/foo?foo/bat/man/bar.json', 'file:///folder/foo#foo/bar.json', 'file:///folder/foo#foo/bat/bar.json', 'file:///folder/foo#foo/bat/man/bar.json']; assertMatchingSchemas(ls, positives, negatives); }); test('Schema matching with folder URI ', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['foo.json'], folderUri: 'file:///folder1', schema: { type: 'object', required: ['foo'] } }] }); const positives = ['file:///folder1/foo.json', 'file:///folder1/foo/foo.json', 'file:///folder1/foo/can/be/as/deep/as/the/ocean/floor/foo.json', 'file:///folder1/foo/foo.json?f=true', 'file:///folder1/foo/bat/foo.json?f=true', 'file:///folder1/foo/foo.json/bat/foo.json']; const negatives = ['file:///folder/foo.json', 'file:///folder11/foo.json', 'file:///folder2/foo/foo.json', 'file:///folder2/foo/can/be/as/deep/as/the/ocean/floor/foo.json', 'file:///folder2/foo/foo.json?f=true']; assertMatchingSchemas(ls, positives, negatives); }); test('Schema matching with folder URI ending with slash', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['foo.json'], folderUri: 'file:///parent/folder1/', schema: { type: 'object', required: ['foo'] } }] }); const positives = ['file:///parent/folder1/foo.json', 'file:///parent/folder1/foo/foo.json',]; const negatives = ['file:///folder1/foo.json', 'file:///folder1/parent/folder1/foo.json']; assertMatchingSchemas(ls, positives, negatives); }); test('Schema matching with encoding in folder URI', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['foo.json'], folderUri: 'file:///C%3A/parent/folder1/', schema: { type: 'object', required: ['foo'] } }] }); const positives = ['file:///C%3A/parent/folder1/foo.json', 'file:///C:/parent/folder1/foo.json', 'file:///c:/parent/folder1/foo.json']; const negatives = ['file:///d:/parent/folder1/foo.json']; assertMatchingSchemas(ls, positives, negatives); }); test('Schema matching with encoding in folder URI 2', async function () { const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas: [{ uri: 'http://myschemastore/myschemabar', fileMatch: ['foo.json'], folderUri: 'file:///C:/parent/folder1/', schema: { type: 'object', required: ['foo'] } }] }); const positives = ['file:///C%3A/parent/folder1/foo.json', 'file:///C:/parent/folder1/foo.json', 'file:///c:/parent/folder1/foo.json']; const negatives = ['file:///d:/parent/folder1/foo.json']; assertMatchingSchemas(ls, positives, negatives); }); test('Resolving circular $refs', async function () { const service: SchemaService.IJSONSchemaService = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const input = { "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "resources": [ { "name": "SQLServer", "type": "Microsoft.Sql/servers", "location": "West US", "apiVersion": "2014-04-01-preview", "dependsOn": [], "tags": { "displayName": "SQL Server" }, "properties": { "administratorLogin": "asdfasd", "administratorLoginPassword": "asdfasdfasd" } } ] }; const { textDoc, jsonDoc } = toDocument(JSON.stringify(input)); const resolveSchema = await service.getSchemaForResource('file://doc/mydoc.json', jsonDoc); assert.deepStrictEqual(resolveSchema?.errors, []); const content = JSON.stringify(resolveSchema?.schema); assert.strictEqual(content.indexOf('$ref'), -1); // no more $refs const problems = jsonDoc.validate(textDoc, resolveSchema?.schema); assert.deepStrictEqual(problems, []); }); test('Resolving circular $refs, invalid document', async function () { const service: SchemaService.IJSONSchemaService = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const input = { "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "resources": [ { "name": "foo", "type": "Microsoft.Resources/deployments", "apiVersion": "2015-01-01", } ] }; const { textDoc, jsonDoc } = toDocument(JSON.stringify(input)); const resolveSchema = await service.getSchemaForResource('file://doc/mydoc.json', jsonDoc); assert.deepStrictEqual(resolveSchema?.errors, []); const content = JSON.stringify(resolveSchema?.schema); assert.strictEqual(content.indexOf('$ref'), -1); // no more $refs const problems = jsonDoc.validate(textDoc, resolveSchema?.schema); assert.strictEqual(problems?.length, 1); }); test('$refs in $ref', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const id0 = "foo://bar/bar0"; const id1 = "foo://bar/bar1"; const schema0: JSONSchema = { "allOf": [ { $ref: id1 } ] }; const schema1: JSONSchema = { $ref: "#/definitions/foo", definitions: { foo: { type: 'object', } }, }; const fsm0 = service.registerExternalSchema({ uri: id0, fileMatch: ['*.json'], schema: schema0 }); service.registerExternalSchema({ uri: id1, fileMatch: [], schema: schema1 }); const fs0 = await fsm0.getResolvedSchema(); assert.strictEqual((fs0?.schema.allOf?.[0]).type, 'object'); }); test('$refs in $ref - circular', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main": { type: 'object', properties: { responseValue: { "$ref": "#/definitions/shellConfiguration" }, hops: { "$ref": "#/definitions/hop1" } }, definitions: { shellConfiguration: { $ref: '#/definitions/shellConfiguration', type: 'object' }, hop1: { $ref: '#/definitions/hop2', }, hop2: { $ref: '#/definitions/hop1', type: 'object' } } } } }); const fs = await service.getResolvedSchema('https://myschemastore/main'); assert.deepStrictEqual(fs?.schema.properties?.['responseValue'], { type: 'object' }); assert.deepStrictEqual(fs?.schema.properties?.['hops'], { type: 'object' }); }); test('$refs in $ref - circular 2', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main": { type: 'object', properties: { responseValue: { "$ref": "#/definitions/shellConfiguration" }, hops: { "$ref": "#/definitions/hop1" } }, definitions: { shellConfiguration: { $ref: '#/definitions/shellConfiguration', type: 'object' }, hop1: { $ref: '#/definitions/hop2', }, hop2: { $ref: '#/definitions/hop1', type: 'object' } } } } }); const fs = await service.getResolvedSchema('https://myschemastore/main'); assert.deepStrictEqual(fs?.schema.properties?.['responseValue'], { type: 'object' }); assert.deepStrictEqual(fs?.schema.properties?.['hops'], { type: 'object' }); }); test('$refs in $ref - across files', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); service.setSchemaContributions({ schemas: { "https://myschemastore/main1": { type: 'object', definitions: { blue: { properties: { red: { $ref: '#/definitions/blue' } } } } }, "https://myschemastore/main2": { type: 'object', definitions: { green: { $ref: 'main1#/definitions/blue' }, white: { $ref: 'main1#/definitions/blue' } } } } }); const fs = await service.getResolvedSchema('https://myschemastore/main2'); assert.deepStrictEqual(fs?.schema.definitions?.green, { properties: { red: { properties: fs?.schema.definitions?.green.properties } } }); }); test('$refs with encoded characters', async function () { const service = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const id0 = "foo://bar/bar0"; const schema: JSONSchema = { definitions: { 'Foo': { type: 'object', } }, "type": "object", "properties": { "p1": { "enum": ["v1", "v2"] }, "p2": { "$ref": "#/definitions/Foo%3Cnumber%3E" } } }; const fsm0 = service.registerExternalSchema({ uri: id0, fileMatch: ['*.json'], schema: schema }); const fs0 = await fsm0.getResolvedSchema(); assert.deepStrictEqual(fs0.errors, []); assert.strictEqual((fs0?.schema.properties?.p2).type, 'object'); }); test('Validate Azure Resource Definition', async function () { const service: SchemaService.IJSONSchemaService = new SchemaService.JSONSchemaService(newMockRequestService(), workspaceContext); const input = { "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "resources": [ { "apiVersion": "2015-06-15", "type": "Microsoft.Compute/virtualMachines", "name": "a", "location": "West US", "properties": { "hardwareProfile": { "vmSize": "Small" }, "osProfile": { "computername": "a", "adminUsername": "a", "adminPassword": "a" }, "storageProfile": { "imageReference": { "publisher": "a", "offer": "a", "sku": "a", "version": "latest" }, "osDisk": { "name": "osdisk", "vhd": { "uri": "[concat('http://', 'b','.blob.core.windows.net/',variables('vmStorageAccountContainerName'),'/',variables('OSDiskName'),'.vhd')]" }, "caching": "ReadWrite", "createOption": "FromImage" } }, "networkProfile": { "networkInterfaces": [ { "id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]" } ] }, "diagnosticsProfile": { "bootDiagnostics": { "enabled": "true", "storageUri": "[concat('http://',parameters('newStorageAccountName'),'.blob.core.windows.net')]" } } } } ] }; const { textDoc, jsonDoc } = toDocument(JSON.stringify(input)); const resolvedSchema = await service.getSchemaForResource('file://doc/mydoc.json', jsonDoc); assert.deepStrictEqual(resolvedSchema?.errors, []); const problems = jsonDoc.validate(textDoc, resolvedSchema?.schema); assert.strictEqual(problems?.length, 1); assert.strictEqual(problems?.[0].message, 'Missing property "computerName".'); }); test('Complex enums', function () { const input = { "group": { "kind": "build", "isDefault": false } }; const schema = { "type": "object", "properties": { "group": { "oneOf": [ { "type": "string" }, { "type": "object", "properties": { "kind": { "type": "string", "default": "none", "description": "The task\"s execution group." }, "isDefault": { "type": "boolean", "default": false, "description": "Defines if this task is the default task in the group." } } } ], "enum": [ { "kind": "build", "isDefault": true }, { "kind": "build", "isDefault": false }, { "kind": "test", "isDefault": true }, { "kind": "test", "isDefault": false }, "build", "test", "none" ] } } }; const { textDoc, jsonDoc } = toDocument(JSON.stringify(input)); const problems = jsonDoc.validate(textDoc, schema); assert.strictEqual(problems?.length, 0); }); test('resetSchema', async function () { const mainSchemaURI = "http://foo/main.schema.json"; const aSchemaURI1 = "http://foo/a.schema.json"; const bSchemaURI1 = "http://foo/b.schema.json"; const schemas: { [uri: string]: JSONSchema } = { [mainSchemaURI]: { type: 'object', properties: { bar: { $ref: aSchemaURI1 } } }, [aSchemaURI1]: { type: 'object', properties: { a: { type: 'string' } } }, [bSchemaURI1]: { type: 'boolean', } }; const accesses: string[] = []; const schemaRequestService = newMockRequestService(schemas, accesses); const ls = getLanguageService({ workspaceContext, schemaRequestService }); const testDoc = toDocument(JSON.stringify({ $schema: mainSchemaURI, bar: { a: 1 } })); let validation = await ls.doValidation(testDoc.textDoc, testDoc.jsonDoc); assert.deepStrictEqual(validation.map(v => v.message), ['Incorrect type. Expected "string".']); assert.deepStrictEqual([mainSchemaURI, aSchemaURI1], accesses); // b in not loaded as it is not references accesses.length = 0; // add a dependency to b schemas[aSchemaURI1] = { type: 'object', properties: { a: { $ref: bSchemaURI1 } } }; ls.resetSchema(aSchemaURI1); validation = await ls.doValidation(testDoc.textDoc, testDoc.jsonDoc); assert.deepStrictEqual(validation.map(v => v.message), ['Incorrect type. Expected "boolean".']); assert.deepStrictEqual([mainSchemaURI, aSchemaURI1, bSchemaURI1], accesses); // main, a and b are loaded // change to be but no reset schemas[bSchemaURI1] = { type: 'number' }; accesses.length = 0; validation = await ls.doValidation(testDoc.textDoc, testDoc.jsonDoc); assert.deepStrictEqual(validation.map(v => v.message), ['Incorrect type. Expected "boolean".']); assert.deepStrictEqual([], accesses); // no loades as there was no reset // do the reset ls.resetSchema(bSchemaURI1); validation = await ls.doValidation(testDoc.textDoc, testDoc.jsonDoc); assert.deepStrictEqual(validation.map(v => v.message), []); assert.deepStrictEqual([mainSchemaURI, aSchemaURI1, bSchemaURI1], accesses); // main, a and b are loaded, main, a depend on b accesses.length = 0; // remove the dependency schemas[aSchemaURI1] = { type: 'object', properties: { a: { type: 'boolean' } } }; ls.resetSchema(aSchemaURI1); validation = await ls.doValidation(testDoc.textDoc, testDoc.jsonDoc); assert.deepStrictEqual(validation.map(v => v.message), ['Incorrect type. Expected "boolean".']); assert.deepStrictEqual([mainSchemaURI, aSchemaURI1], accesses); accesses.length = 0; ls.resetSchema(bSchemaURI1); validation = await ls.doValidation(testDoc.textDoc, testDoc.jsonDoc); assert.deepStrictEqual(validation.map(v => v.message), ['Incorrect type. Expected "boolean".']); assert.deepStrictEqual([], accesses); // b is not depended anymore }); test('resetSchema clears current document schema cache when not using $schema property', async function () { const schemaUri = "http://foo/main.schema.json"; const schemas: { [uri: string]: JSONSchema } = { [schemaUri]: { type: 'object', properties: { bar: { type: 'string' } } } }; const accesses: string[] = []; const schemaRequestService = newMockRequestService(schemas, accesses); const testDoc = toDocument(JSON.stringify({ bar: 1 })); const ls = getLanguageService({ workspaceContext, schemaRequestService }); // configure the language service to use the schema for the test document ls.configure({ schemas: [{ uri: schemaUri, fileMatch: [testDoc.textDoc.uri.toString()], }] }); // check using the existing schema let validation = await ls.doValidation(testDoc.textDoc, testDoc.jsonDoc); assert.deepStrictEqual(validation.map(v => v.message), ['Incorrect type. Expected "string".']); assert.deepStrictEqual([schemaUri], accesses); accesses.length = 0; // change a schema property and reset the schema schemas[schemaUri] = { type: 'object', properties: { a: { type: 'number' } } }; ls.resetSchema(schemaUri); // now ensure validation occurs with the new schema validation = await ls.doValidation(testDoc.textDoc, testDoc.jsonDoc); assert.deepStrictEqual(validation.map(v => v.message), []); assert.deepStrictEqual([schemaUri], accesses); }); test('getMatchingSchemas', async function () { const schema: JSONSchema = { type: 'object', $comment: 'schema', definitions: { baz: { type: 'boolean', $comment: 'baz', }, key: { type: 'string', $comment: 'key', } }, properties: { foo: { type: 'object', $comment: 'foo', properties: { bar: { type: 'number', $comment: 'bar', }, baz: { $ref: "#/definitions/baz" } } } }, propertyNames: { $ref: "#/definitions/key" } }; const ls = getLanguageService({ workspaceContext }); const testDoc = toDocument(JSON.stringify({ foo: { bar: 1, baz: true } })); const ms = await ls.getMatchingSchemas(testDoc.textDoc, testDoc.jsonDoc, schema); function assertMatchingSchema(ms: MatchingSchema[], nodeOffset: number, comment: string) { for (const m of ms) { if (m.node.offset === nodeOffset) { assert.strictEqual(m.schema.$comment, comment); return; } } assert.fail("No node at offset " + nodeOffset); } assertMatchingSchema(ms, 0, 'schema'); assertMatchingSchema(ms, 1, 'key'); assertMatchingSchema(ms, 7, 'foo'); assertMatchingSchema(ms, 14, 'bar'); assertMatchingSchema(ms, 22, 'baz'); }); test('schema resolving severity', async function () { const schema: JSONSchema = { $schema: 'http://json-schema.org/draft-03/schema#', type: 'string' }; const ls = getLanguageService({}); { const { textDoc, jsonDoc } = toDocument(JSON.stringify('SimpleJsonString')); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const resolveError = await ls.doValidation(textDoc, jsonDoc, { schemaRequest: 'error' }, schema); assert.strictEqual(resolveError!.length, 1); assert.strictEqual(resolveError![0].severity, DiagnosticSeverity.Error); } { const { textDoc, jsonDoc } = toDocument(JSON.stringify('SimpleJsonString')); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const resolveError = await ls.doValidation(textDoc, jsonDoc, {}, schema); assert.strictEqual(resolveError!.length, 1); assert.strictEqual(resolveError![0].severity, DiagnosticSeverity.Warning); } }); test('schema with severity', async function () { const schema: JSONSchema = { type: 'object', properties: { 'name': { type: 'string', minLength: 4, }, 'age': { type: 'number', minimum: 1, }, 'address': { type: 'string', minLength: 5, }, 'email': { type: 'string', format: 'email' }, 'depr': { type: 'string', deprecationMessage: 'old stuff' } }, required: ['name', 'age', 'address', 'email'] }; const ls = getLanguageService({}); { const { textDoc, jsonDoc } = toDocument('{ "depr": "" }'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = await ls.doValidation(textDoc, jsonDoc, { schemaValidation: 'error' }, schema); assert.strictEqual(semanticErrors!.length, 5); assert.strictEqual(semanticErrors![0].severity, DiagnosticSeverity.Error); assert.strictEqual(semanticErrors![1].severity, DiagnosticSeverity.Error); assert.strictEqual(semanticErrors![2].severity, DiagnosticSeverity.Error); assert.strictEqual(semanticErrors![3].severity, DiagnosticSeverity.Error); assert.strictEqual(semanticErrors![4].severity, DiagnosticSeverity.Warning); } { const { textDoc, jsonDoc } = toDocument('{"name": "", "age": -1, "address": "SA42", "email": "wrong_mail"}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = await ls.doValidation(textDoc, jsonDoc, { schemaValidation: 'warning' }, schema); assert.strictEqual(semanticErrors!.length, 4); assert.strictEqual(semanticErrors![0].severity, DiagnosticSeverity.Warning); assert.strictEqual(semanticErrors![1].severity, DiagnosticSeverity.Warning); assert.strictEqual(semanticErrors![2].severity, DiagnosticSeverity.Warning); assert.strictEqual(semanticErrors![3].severity, DiagnosticSeverity.Warning); } { const { textDoc, jsonDoc } = toDocument('{"name": "", "age": -1, "address": "SA42", "email": "wrong_mail"}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = await ls.doValidation(textDoc, jsonDoc, { schemaValidation: 'ignore' }, schema); assert.strictEqual(semanticErrors!.length, 0); } { const { textDoc, jsonDoc } = toDocument('{"name": "Alice", "age": 23, "address": "Solarstreet 42", "email": "alice@foo.com"}'); assert.strictEqual(jsonDoc.syntaxErrors.length, 0); const semanticErrors = await ls.doValidation(textDoc, jsonDoc, {}, schema); assert.strictEqual(semanticErrors!.length, 0); } }); test('getLanguageStatus', async function () { const schemas: SchemaConfiguration[] = [{ uri: 'https://myschemastore/schema1.json', fileMatch: ['**/*.json'], schema: { type: 'object', } }, { uri: 'https://myschemastore/schema2.json', fileMatch: ['**/bar.json'], schema: { type: 'object', } }, { uri: 'https://myschemastore/schema3.json', schema: { type: 'object', } } ]; const ls = getLanguageService({ workspaceContext }); ls.configure({ schemas }); { const { textDoc, jsonDoc } = toDocument('{ }', undefined, 'foo://bar/folder/foo.json'); const info = ls.getLanguageStatus(textDoc, jsonDoc); assert.deepStrictEqual(info.schemas, ['https://myschemastore/schema1.json']); } { const { textDoc, jsonDoc } = toDocument('{ }', undefined, 'foo://bar/folder/bar.json'); const info = ls.getLanguageStatus(textDoc, jsonDoc); assert.deepStrictEqual(info.schemas, ['https://myschemastore/schema1.json', 'https://myschemastore/schema2.json']); } { const { textDoc, jsonDoc } = toDocument('{ $schema: "https://myschemastore/schema3.json" }', undefined, 'foo://bar/folder/bar.json'); const info = ls.getLanguageStatus(textDoc, jsonDoc); assert.deepStrictEqual(info.schemas, ['https://myschemastore/schema3.json']); } { const { textDoc, jsonDoc } = toDocument('{ $schema: "schema3.json" }', undefined, 'foo://bar/folder/bar.json'); const info = ls.getLanguageStatus(textDoc, jsonDoc); assert.deepStrictEqual(info.schemas, ['foo://bar/folder/schema3.json']); } { const { textDoc, jsonDoc } = toDocument('{ $schema: "./schema3.json" }', undefined, 'foo://bar/folder/bar.json'); const info = ls.getLanguageStatus(textDoc, jsonDoc); assert.deepStrictEqual(info.schemas, ['foo://bar/folder/schema3.json']); } }); test('access json-schema.org with https', async function () { const httpUrl = "http://json-schema.org/schema"; const httpsUrl = "https://json-schema.org/schema"; const schemas: { [uri: string]: JSONSchema } = { [httpsUrl]: { type: 'object', properties: { bar: { const: 3 } } } }; const accesses: string[] = []; const schemaRequestService = newMockRequestService(schemas, accesses); const ls = getLanguageService({ workspaceContext, schemaRequestService }); const testDoc = toDocument(JSON.stringify({ $schema: httpUrl, bar: 2 })); let validation = await ls.doValidation(testDoc.textDoc, testDoc.jsonDoc); assert.deepStrictEqual(validation.map(v => v.message), ['Value must be 3.']); assert.deepStrictEqual([httpsUrl], accesses); }); test('combined schemas and URIs without host', async function () { const schemas: SchemaConfiguration[] = [{ uri: 'myproto:///schema.json', fileMatch: ['foo.json'], }, { uri: 'https://myschemastore/schema2.json', fileMatch: ['foo.json'], } ]; const schemaContents: { [uri: string]: JSONSchema } = { ['myproto:/schema.json']: { type: 'object', properties: { bar: { type: 'string' } } }, ['https://myschemastore/schema2.json']: { type: 'object', properties: { foo: { type: 'string' } } } }; const accesses: string[] = []; const schemaRequestService: SchemaRequestService = async (uri: string) => { if (uri === `https://myschemastore/schema2.json` || uri === `myproto:/schema.json`) { return '{}'; } throw new Error('Unknown schema ' + uri); }; const ls = getLanguageService({ workspaceContext, schemaRequestService }); ls.configure({ schemas }); { const { textDoc, jsonDoc } = toDocument('{ }', undefined, 'foo://bar/folder/foo.json'); const res = await ls.doValidation(textDoc, jsonDoc); } }); test('validate against draft-2019-09', async function () { const schema: JSONSchema = { $schema: 'https://json-schema.org/draft/2019-09/schema', type: 'object', properties: { name: { type: 'string', minLength: 4, } }, required: ['name'] }; const ls = getLanguageService({}); { const { textDoc, jsonDoc } = toDocument(JSON.stringify(schema)); assert.deepStrictEqual(jsonDoc.syntaxErrors, []); const resolveError = await ls.doValidation(textDoc, jsonDoc, { schemaRequest: 'error' }); assert.deepStrictEqual(resolveError, []); } }); test('validate against draft-2020-12', async function () { const schema: JSONSchema = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', properties: { name: { type: 'string', minLength: 4, } }, required: ['name'] }; const ls = getLanguageService({}); { const { textDoc, jsonDoc } = toDocument(JSON.stringify(schema)); assert.deepStrictEqual(jsonDoc.syntaxErrors, []); const resolveError = await ls.doValidation(textDoc, jsonDoc, { schemaRequest: 'error' }); assert.deepStrictEqual(resolveError, []); } }); }); vscode-json-languageservice-5.7.2/src/test/selectionRanges.test.ts000066400000000000000000000105401514707600300253450ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import 'mocha'; import * as assert from 'assert'; import { getLanguageService, TextDocument } from '../jsonLanguageService'; import { SelectionRange } from 'vscode-languageserver-types'; function assertRanges(content: string, expected: (number | string)[][]): void { let message = `Test ${content}`; const offset = content.indexOf('|'); content = content.substr(0, offset) + content.substr(offset + 1); const ls = getLanguageService({}); const document = TextDocument.create('test://foo.json', 'json', 1, content); const jsonDoc = ls.parseJSONDocument(document); const actualRanges = ls.getSelectionRanges(document, [document.positionAt(offset)], jsonDoc); const offsetPairs: [number, string][] = []; let curr: SelectionRange | undefined = actualRanges[0]; while (curr) { offsetPairs.push([document.offsetAt(curr.range.start), document.getText(curr.range)]); curr = curr.parent; } message += `\n${JSON.stringify(offsetPairs)} should equal to ${JSON.stringify(expected)}`; assert.deepEqual(offsetPairs, expected, message); } suite('JSON SelectionRange', () => { test('Strings', () => { assertRanges('"ab|cd"', [ [1, 'abcd'], [0, '"abcd"'] ]); assertRanges('"|abcd"', [ [1, 'abcd'], [0, '"abcd"'] ]); assertRanges('"abcd|"', [ [1, 'abcd'], [0, '"abcd"'] ]); assertRanges('|"abcd"', [ [0, '"abcd"'] ]); assertRanges('"abcd"|', [ [0, '"abcd"'] ]); }); test('Bools, Numbers and Nulls', () => { assertRanges('|true', [ [0, 'true'] ]); assertRanges('false|', [ [0, 'false'] ]); assertRanges('fal|se', [ [0, 'false'] ]); assertRanges('|1', [ [0, '1'] ]); assertRanges('5677|', [ [0, '5677'] ]); assertRanges('567.|7', [ [0, '567.7'] ]); assertRanges('|null', [ [0, 'null'] ]); assertRanges('null|', [ [0, 'null'] ]); assertRanges('nu|ll', [ [0, 'null'] ]); }); test('Properties', () => { assertRanges('{ "f|oo": true, "bar": "bit" }', [ [3, 'foo'], [2, '"foo"'], [2, '"foo": true'], [2, '"foo": true,'], [1, ' "foo": true, "bar": "bit" '], [0, '{ "foo": true, "bar": "bit" }'], ]); assertRanges('{ "foo"|: true, "bar": "bit" }', [ [2, '"foo"'], [2, '"foo": true'], [2, '"foo": true,'], [1, ' "foo": true, "bar": "bit" '], [0, '{ "foo": true, "bar": "bit" }'], ]); assertRanges('{ "foo":| true, "bar": "bit" }', [ [2, '"foo": true'], [2, '"foo": true,'], [1, ' "foo": true, "bar": "bit" '], [0, '{ "foo": true, "bar": "bit" }'], ]); assertRanges('{ "foo": |true, "bar": "bit" }', [ [9, 'true'], [2, '"foo": true'], [2, '"foo": true,'], [1, ' "foo": true, "bar": "bit" '], [0, '{ "foo": true, "bar": "bit" }'], ]); assertRanges('{ "foo": true|, "bar": "bit" }', [ [9, 'true'], [2, '"foo": true'], [2, '"foo": true,'], [1, ' "foo": true, "bar": "bit" '], [0, '{ "foo": true, "bar": "bit" }'], ]); assertRanges('{ "foo": true,| "bar": "bit" }', [ [1, ' "foo": true, "bar": "bit" '], [0, '{ "foo": true, "bar": "bit" }'], ]); assertRanges('{ "foo": true, |"bar": "bit" }', [ [15, '"bar"'], [15, '"bar": "bit"'], [1, ' "foo": true, "bar": "bit" '], [0, '{ "foo": true, "bar": "bit" }'], ]); assertRanges('{ "foo": true, "bar": "bit"| }', [ [22, '"bit"'], [15, '"bar": "bit"'], [1, ' "foo": true, "bar": "bit" '], [0, '{ "foo": true, "bar": "bit" }'], ]); assertRanges('{ "line1": "Foo|Bar" }', [ [12, 'FooBar'], [11, '"FooBar"'], [2, '"line1": "FooBar"'], [1, ' "line1": "FooBar" '], [0, '{ "line1": "FooBar" }'], ]); }); test('Objects', () => { assertRanges('|{}', [ [0, '{}'] ]); assertRanges('{|}', [ [0, '{}'] ]); assertRanges('{| }', [ [1, ' '], [0, '{ }'] ]); }); test('Array', () => { assertRanges('|[[ ], []]', [ [0, '[[ ], []]'] ]); assertRanges('[|[ ], []]', [ [1, '[ ]'], [1, '[ ],'], [1, '[ ], []'], [0, '[[ ], []]'] ]); assertRanges('[[| ], []]', [ [2, ' '], [1, '[ ]'], [1, '[ ],'], [1, '[ ], []'], [0, '[[ ], []]'] ]); }); }); vscode-json-languageservice-5.7.2/src/test/sort.test.ts000066400000000000000000001602371514707600300232200ustar00rootroot00000000000000 import { getLanguageService, ClientCapabilities, TextDocument, SortOptions } from '../jsonLanguageService'; import * as assert from 'assert'; suite('Sort JSON', () => { const ls = getLanguageService({ clientCapabilities: ClientCapabilities.LATEST }); let formattingOptions = { tabSize: 2, insertSpaces: true, keepLines: false, eol: '\n', insertFinalNewline: false }; function testSort(unsorted: string, expected: string, options: SortOptions) { let document = TextDocument.create('test://test.json', 'json', 0, unsorted); const edits = ls.sort(document, options); const sorted = TextDocument.applyEdits(document, edits); assert.equal(sorted, expected); } test('sorting a simple JSONC object with numeric values', () => { const content = [ '{"b" : 1, "a" : 2}' ].join('\n'); const expected = [ '{\n "a": 2,\n "b": 1\n}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a simple JSONC object with an array spanning several lines', () => { const content = [ '{"array":["volleyball",', ' "drawing",', ' "hiking"]}' ].join('\n'); const expected = [ '{', ' "array": [', ' "volleyball",', ' "drawing",', ' "hiking"', ' ]', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object with nested objects', () => { const content = [ '{"name": "Brigitte","age" : 30,', '"hobbies" : ["volleyball","drawing","hiking"],', '"friends" : {', '"Marc" : {"hobbies" : ["kayaking", "mountaineering"],', '"age" : 35,},', '"Leila" : {"hobbies" : ["watching movies",', '"reading books"], "age" : 32}}}' ].join('\n'); const expected = [ '{', ' "age": 30,', ' "friends": {', ' "Leila": {', ' "age": 32,', ' "hobbies": [', ' "watching movies",', ' "reading books"', ' ]', ' },', ' "Marc": {', ' "age": 35,', ' "hobbies": [', ' "kayaking",', ' "mountaineering"', ' ]', ' }', ' },', ' "hobbies": [', ' "volleyball",', ' "drawing",', ' "hiking"', ' ],', ' "name": "Brigitte"', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object with line comments', () => { const content = [ '{ // this is a comment', '"boolean" : true,', '"array" : [', '// this is a second comment', ' "element1", "element2"]', '}' ].join('\n'); const expected = [ '{ // this is a comment', ' "array": [', ' // this is a second comment', ' "element1",', ' "element2"', ' ],', ' "boolean": true', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object with an object nested inside of an array value', () => { const content = [ '{', '"boolean" : true,', '"array" : [', ' "element1", {"property" : "element2"}, "element3"]', '}' ].join('\n'); const expected = [ '{', ' "array": [', ' "element1",', ' {', ' "property": "element2"', ' },', ' "element3"', ' ],', ' "boolean": true', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object with comments appearing before and after the main JSON object', () => { const content = [ '// comment appearing before', '', '{', '"boolean" : true,', '"array" : [', ' "element1", {"property" : "element2"}, "element3"]', '} /* block comment appearing ', 'after, it spans several', 'lines */' ].join('\n'); const expected = [ '// comment appearing before', '{', ' "array": [', ' "element1",', ' {', ' "property": "element2"', ' },', ' "element3"', ' ],', ' "boolean": true', '} /* block comment appearing ', 'after, it spans several', 'lines */' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object with new lines appearing before and after the main JSON object', () => { const content = [ '', '', '{', '"boolean" : true,', '"array" : [', ' "element1", {"property" : "element2"}, "element3"]', '}', '', '' ].join('\n'); const expected = [ '{', ' "array": [', ' "element1",', ' {', ' "property": "element2"', ' },', ' "element3"', ' ],', ' "boolean": true', '}', ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object with a block comment appearing on the same line as a comma but not ending on that line', () => { const content = [ '{', '"boolean" : true, /* this is block comment starting on', 'the line where the comma is but ending on another line */', '"array" : []', '}', ].join('\n'); const expected = [ '{', ' "array": [],', ' "boolean": true /* this is block comment starting on', 'the line where the comma is but ending on another line */', '}', ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object with a block comment starting at the end of a property and such that a new property starts on the end of that block comment', () => { const content = [ '{', '"boolean" : true, /* this is block comment starting on', 'the line where the comma is but ending on another line */ "array" : []', '}', ].join('\n'); const expected = [ '{', ' "array": [],', ' "boolean": true /* this is block comment starting on', 'the line where the comma is but ending on another line */', '}', ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object with comments between properties', () => { const content = [ '// comment appearing before', '', '{', ' // some comment', '"boolean" : true,', ' // some other comment', '"numeric" : 2,', ' /* a third comment', ' which is a block comment */', '"array": []', '}' ].join('\n'); const expected = [ '// comment appearing before', '{', ' /* a third comment', ' which is a block comment */', ' "array": [],', ' // some comment', ' "boolean": true,', ' // some other comment', ' "numeric": 2', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object with comments appearing between a value and the comma', () => { const content = [ '{', '"boolean" : true // some comment', ',', '"array" : [],', '"numeric" : 2', '}' ].join('\n'); const expected = [ '{', ' "array": [],', ' "boolean": true // some comment', ' ,', ' "numeric": 2', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object with block comments on the same line as the arrays', () => { const content = [ '/* multi-line comment', '..', '*/', '{ /* multi-line comment', '..', '*/ ', '"information_for_contributors": [', '/* multi-line comment', '..', '*/', '"This file has been converted from https://github.com/textmate/perl.tmbundle/blob/master/Syntaxes/Perl%206.tmLanguage",', '"Once accepted there, we are happy to receive an update request."', '], /* multi-line comment', '..', '*/', '"version": "https://github.com/textmate/perl.tmbundle/commit/d9841a0878239fa43f88c640f8d458590f97e8f5", /* multi-line comment', '..', '*/', '"name": "Perl 6" /* multi-line comment', '..', '*/', '}' ].join('\n'); const expected = [ '/* multi-line comment', '..', '*/', '{ /* multi-line comment', '..', '*/', ' "information_for_contributors": [', ' /* multi-line comment', '..', '*/', ' "This file has been converted from https://github.com/textmate/perl.tmbundle/blob/master/Syntaxes/Perl%206.tmLanguage",', ' "Once accepted there, we are happy to receive an update request."', ' ], /* multi-line comment', '..', '*/', ' "name": "Perl 6", /* multi-line comment', '..', '*/', ' "version": "https://github.com/textmate/perl.tmbundle/commit/d9841a0878239fa43f88c640f8d458590f97e8f5" /* multi-line comment', '..', '*/', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSONC object where the colon is not on the same line as the key or the value', () => { const content = [ '{', '"boolean"', ':', 'true // some comment', ',', '"array"', ': [],', '"numeric" : 2', '}' ].join('\n'); const expected = [ '{', ' "array": [],', ' "boolean": true // some comment', ' ,', ' "numeric": 2', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a complicated JSONC object 1', () => { const content = [ '// Comment ouside the main JSON object', '', '{', '// A comment which belongs to b', '"b": "some value",', '', '"a": "some other value" /* a block comment which starts on the same line as key a', '..*/,', '', '"array": [', '"first element",', '{', ' // comment belonging to r', ' "r" : 1,', '', ' // comment belonging to q', ' "q" : {', ' "s" : 2', ' },', ' // comment belonging to p', ' "p" : 3', '},', '"third element"', '] // some comment on the line where the array ends', ',', '', '"numeric" : [ 1, 2, 3]', '}', '', '', '/* Comment below the main JSON object', '...', '...', '*/' ].join('\n'); const expected = [ '// Comment ouside the main JSON object', '{', ' "a": "some other value" /* a block comment which starts on the same line as key a', '..*/,', ' "array": [', ' "first element",', ' {', ' // comment belonging to p', ' "p": 3,', ' // comment belonging to q', ' "q": {', ' "s": 2', ' },', ' // comment belonging to r', ' "r": 1', ' },', ' "third element"', ' ] // some comment on the line where the array ends', ' ,', ' // A comment which belongs to b', ' "b": "some value",', ' "numeric": [', ' 1,', ' 2,', ' 3', ' ]', '}', '/* Comment below the main JSON object', '...', '...', '*/' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a complicated JSONC object 2', () => { const content = [ '/*', '', 'adding some comment before the actual JSON file', '', '*/ {', ' "webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-cdn.net/insider/ef65ac1ba57f57f2a3961bfe94aa20481caca4c6/out/vs/workbench/contrib/webview/browser/pre/",', ' // some other comment', ' "builtInExtensions": [', ' {', ' "name": "ms-vscode.js-debug-companion", /** adding some more comments **/', ' "version": "1.0.18",', ' "repo": "https://github.com/microsoft/vscode-js-debug-companion",', ' "metadata": {', ' "id": "99cb0b7f-7354-4278-b8da-6cc79972169d",', ' "publisherId": {', ' "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee",', ' "publisherName": "ms-vscode" // comment', ' ,', ' "displayName": "Microsoft",', ' "flags": "verified"', ' },', ' "publisherDisplayName": "Microsoft"', ' }', ' },', ' {', ' "name": "ms-vscode.js-debug", /** adding some more comments', ' ...', ' ...', ' */ "version": "1.75.1",', ' "repo": "https://github.com/microsoft/vscode-js-debug",', ' "metadata": {', ' "id": "25629058-ddac-4e17-abba-74678e126c5d",', ' "publisherId": {', ' "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee",', ' "publisherName": "ms-vscode",', ' "displayName": "Microsoft",', ' "flags": "verified"', ' },', ' "publisherDisplayName": "Microsoft"', ' }', ' // some more comments at the end after all properties', ' },', ' {', ' "name": "ms-vscode.vscode-js-profile-table",', ' "version": "1.0.3",', ' "repo": "https://github.com/microsoft/vscode-js-profile-visualizer",', ' "metadata": {', ' "id": "7e52b41b-71ad-457b-ab7e-0620f1fc4feb",', ' "publisherId": {', ' "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee",', ' "publisherName": "ms-vscode",', ' "displayName": "Microsoft",', ' "flags": "verified"', ' },', ' "publisherDisplayName": "Microsoft"', ' }', ' } ', ' ] // comment on the end of an array', '}', ].join('\n'); const expected = [ '/*', '', 'adding some comment before the actual JSON file', '', '*/ {', ' // some other comment', ' "builtInExtensions": [', ' {', ' "metadata": {', ' "id": "99cb0b7f-7354-4278-b8da-6cc79972169d",', ' "publisherDisplayName": "Microsoft",', ' "publisherId": {', ' "displayName": "Microsoft",', ' "flags": "verified",', ' "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee",', ' "publisherName": "ms-vscode" // comment', ' }', ' },', ' "name": "ms-vscode.js-debug-companion", /** adding some more comments **/', ' "repo": "https://github.com/microsoft/vscode-js-debug-companion",', ' "version": "1.0.18"', ' },', ' {', ' "metadata": {', ' "id": "25629058-ddac-4e17-abba-74678e126c5d",', ' "publisherDisplayName": "Microsoft",', ' "publisherId": {', ' "displayName": "Microsoft",', ' "flags": "verified",', ' "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee",', ' "publisherName": "ms-vscode"', ' }', ' },', ' // some more comments at the end after all properties', ' "name": "ms-vscode.js-debug", /** adding some more comments', ' ...', ' ...', ' */', ' "repo": "https://github.com/microsoft/vscode-js-debug",', ' "version": "1.75.1"', ' },', ' {', ' "metadata": {', ' "id": "7e52b41b-71ad-457b-ab7e-0620f1fc4feb",', ' "publisherDisplayName": "Microsoft",', ' "publisherId": {', ' "displayName": "Microsoft",', ' "flags": "verified",', ' "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee",', ' "publisherName": "ms-vscode"', ' }', ' },', ' "name": "ms-vscode.vscode-js-profile-table",', ' "repo": "https://github.com/microsoft/vscode-js-profile-visualizer",', ' "version": "1.0.3"', ' }', ' ], // comment on the end of an array', ' "webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-cdn.net/insider/ef65ac1ba57f57f2a3961bfe94aa20481caca4c6/out/vs/workbench/contrib/webview/browser/pre/"', '}', ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a deeply nested JSONC object', () => { const content = [ '{', ' "a" : {', ' "y" : {},', ' "a" : {}', ' }, // comment', '', ' "b" : [', ' [', ' { /* some comment', ' */', ' "b" : {', ' "y" : [],', ' "x" : []', ' },', ' "a" : {', ' "z" : [],', ' "m" : [],', ' "b" : {', ' "b" : 1,', ' "a" : 2', ' }', ' },', ' "c" : {', ' "a" : {', ' "b" : 1', ' }', ' // comment at the end', ' }', ' }', ' ]', ' ]', '}' ].join('\n'); const expected = [ '{', ' "a": {', ' "a": {},', ' "y": {}', ' }, // comment', ' "b": [', ' [', ' { /* some comment', ' */', ' "a": {', ' "b": {', ' "a": 2,', ' "b": 1', ' },', ' "m": [],', ' "z": []', ' },', ' "b": {', ' "x": [],', ' "y": []', ' },', ' "c": {', ' "a": {', ' "b": 1', ' }', ' // comment at the end', ' }', ' }', ' ]', ' ]', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a simple JSONC document where the outer container is an array', () => { const content = [ '[', ' {', ' "hi": 1', ' },', '', ' // some comment', '', ' {', ' "b" : 2,', ' "a" : 1', ' }', ']' ].join('\n'); const expected = [ '[', ' {', ' "hi": 1', ' },', ' // some comment', ' {', ' "a": 1,', ' "b": 2', ' }', ']' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a complicated JSONC object 3', () => { const content = [ '{', ' "type": "array",', ' "items": {', ' /* multi-line comment', ' ..', ' */', ' "oneOf": [ /* multi-line comment', ' ..', ' */', ' {', ' "type": "object",', ' "required": [ /* multi-line comment', ' ..', ' */', ' "name",', ' "prependLicenseText"', ' /* multi-line comment', ' ..', ' */,', ' {', ' "name" : "some property", /* multi-line comment', ' ..', ' */', ' "value" : "some value" // some value', ' /* multi-line comment', ' ..', ' */', ' }', ' ],', ' // one-line comment', ' "properties": { // one-line comment', ' "name": {', ' "type": "string",', ' "description": "The name of the dependency" // one-line comment', ' },', ' "fullLicenseText": {', ' "type": "array",', ' "description": "The complete license text of the dependency",', ' "items": {', ' "type": "string"', ' /* multi-line comment', ' ..', ' */', ' },', ' "array" : [', ' {', ' "key" : "value" /* multi-line comment', ' ..', ' */', ' }', ' ]', ' },', ' // one-line comment', ' "prependLicenseText": {', ' "type": "array", /* multi-line comment', ' ..', ' */', ' "description": "A piece of text to prepend to the auto-detected license text of the dependency",', ' /* multi-line comment', ' ..', ' */', ' "items": {', ' "type": "string"', ' }', ' }', ' }', ' },', ' {', ' "type": "object",', ' "required": [ // one-line comment', ' "name",', ' // one-line comment', ' "fullLicenseText"', ' ],', ' "properties": {', ' /* multi-line comment', ' ..', ' */', ' "name": {', ' "type": "string",', ' "description": "The name of the dependency"', ' },', ' "fullLicenseText": {', ' "type": "array",', ' "description": "The complete license text of the dependency",', ' "items": {', ' "type": "string"', ' }', ' },', ' /* multi-line comment', ' ..', ' */', ' "prependLicenseText": {', ' "type": "array",', ' "description": "A piece of text to prepend to the auto-detected license text of the dependency", /* multi-line comment', ' ..', ' */', ' "items": {', ' "type": "string"', ' }', ' }', ' }', ' },', ' {', ' "type": "object",', ' "required": [', ' "name",', ' /* multi-line comment', ' ..', ' */', ' "fullLicenseTextUri"', ' ],', ' "properties": {', ' "name": {', ' "type": "string", /* multi-line comment', ' ..', ' */', ' "description": "The name of the dependency"', ' },', ' "fullLicenseTextUri": {', ' "type": "string",', ' "description": "The URI to the license text of this repository",', ' "format": "uri" /* multi-line comment', ' ..', ' */', ' },', ' // one-line comment', ' "prependLicenseText": { // one-line comment', ' "type": "array",', ' "description": "A piece of text to prepend to the auto-detected license text of the dependency",', ' "items": {', ' "type": "string"', ' }', ' } /* multi-line comment', ' ..', ' */', ' }', ' } /* multi-line comment', ' ..', ' */', ' ]', ' } // one-line comment', '}', ].join('\n'); const expected = [ '{', ' "items": {', ' /* multi-line comment', ' ..', ' */', ' "oneOf": [ /* multi-line comment', ' ..', ' */', ' {', ' // one-line comment', ' "properties": { // one-line comment', ' "fullLicenseText": {', ' "array": [', ' {', ' "key": "value" /* multi-line comment', ' ..', ' */', ' }', ' ],', ' "description": "The complete license text of the dependency",', ' "items": {', ' "type": "string"', ' /* multi-line comment', ' ..', ' */', ' },', ' "type": "array"', ' },', ' "name": {', ' "description": "The name of the dependency", // one-line comment', ' "type": "string"', ' },', ' // one-line comment', ' "prependLicenseText": {', ' "description": "A piece of text to prepend to the auto-detected license text of the dependency",', ' /* multi-line comment', ' ..', ' */', ' "items": {', ' "type": "string"', ' },', ' "type": "array" /* multi-line comment', ' ..', ' */', ' }', ' },', ' "required": [ /* multi-line comment', ' ..', ' */', ' "name",', ' "prependLicenseText"', ' /* multi-line comment', ' ..', ' */ ,', ' {', ' "name": "some property", /* multi-line comment', ' ..', ' */', ' "value": "some value" // some value', ' /* multi-line comment', ' ..', ' */', ' }', ' ],', ' "type": "object"', ' },', ' {', ' "properties": {', ' "fullLicenseText": {', ' "description": "The complete license text of the dependency",', ' "items": {', ' "type": "string"', ' },', ' "type": "array"', ' },', ' /* multi-line comment', ' ..', ' */', ' "name": {', ' "description": "The name of the dependency",', ' "type": "string"', ' },', ' /* multi-line comment', ' ..', ' */', ' "prependLicenseText": {', ' "description": "A piece of text to prepend to the auto-detected license text of the dependency", /* multi-line comment', ' ..', ' */', ' "items": {', ' "type": "string"', ' },', ' "type": "array"', ' }', ' },', ' "required": [ // one-line comment', ' "name",', ' // one-line comment', ' "fullLicenseText"', ' ],', ' "type": "object"', ' },', ' {', ' "properties": {', ' "fullLicenseTextUri": {', ' "description": "The URI to the license text of this repository",', ' "format": "uri", /* multi-line comment', ' ..', ' */', ' "type": "string"', ' },', ' "name": {', ' "description": "The name of the dependency",', ' "type": "string" /* multi-line comment', ' ..', ' */', ' },', ' // one-line comment', ' "prependLicenseText": { // one-line comment', ' "description": "A piece of text to prepend to the auto-detected license text of the dependency",', ' "items": {', ' "type": "string"', ' },', ' "type": "array"', ' } /* multi-line comment', ' ..', ' */', ' },', ' "required": [', ' "name",', ' /* multi-line comment', ' ..', ' */', ' "fullLicenseTextUri"', ' ],', ' "type": "object"', ' } /* multi-line comment', ' ..', ' */', ' ]', ' }, // one-line comment', ' "type": "array"', '}', ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a complicated JSONC object 4', () => { const content = [ '/** multi-line or block comment', ' ...', ' ...', ' */', '// one line comment', '{', ' "version": "2.0.0",', ' "tasks": [ // one line comment', ' { /** multi-line or block comment', ' ...', ' ...', ' */', ' "type": "npm" /** multi-line or block comment', ' ...', ' ...', ' */,', ' "script": "watch-clientd",', ' "label": "Core - Build", /** multi-line or block comment', ' ...', ' ...', ' */', ' "isBackground": true,', ' // one line comment', ' "presentation": {', ' "reveal": "never",', ' "group": "buildWatchers", // one line comment', ' "close": false', ' },', ' "problemMatcher": {', ' "owner": "typescript",', ' "applyTo": "closedDocuments",', ' "fileLocation": [', ' /** multi-line or block comment', ' ...', ' ...', ' */', ' "absolute"', ' ],', ' "pattern": {', ' "regexp": "Error: ([^(]+)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\): (.*)$",', ' "file": 1,', ' "location": 2, /** multi-line or block comment', ' ...', ' ...', ' */', ' "message": 3', ' },', ' // one line comment', ' "background": {', ' "beginsPattern": "Starting compilation...",', ' "endsPattern": "Finished compilation with"', ' }', ' } // one line comment', ' },', ' { /** multi-line or block comment', ' ...', ' ...', ' */', ' "type": "npm",', ' "script": "watch-extensionsd",', ' "label": "Ext - Build",', ' "isBackground": true,', ' "presentation": {', ' "reveal": "never",', ' "group": "buildWatchers",', ' "close": false', ' },', ' "problemMatcher": {', ' "owner": "typescript",', ' "applyTo": "closedDocuments",', ' "fileLocation": [ // one line comment', ' "absolute"', ' ],', ' "pattern": { /** multi-line or block comment', ' ...', ' ...', ' */', ' "regexp": "Error: ([^(]+)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\): (.*)$",', ' "file": 1,', ' "location": 2,', ' "message": 3', ' },', ' "background": {', ' "beginsPattern": "Starting compilation",', ' "endsPattern": "Finished compilation"', ' }', ' }', ' }', ' ]', '}', '', '/** multi-line or block comment', '...', '...', '*/', ].join('\n'); const expected = [ '/** multi-line or block comment', ' ...', ' ...', ' */', '// one line comment', '{', ' "tasks": [ // one line comment', ' { /** multi-line or block comment', ' ...', ' ...', ' */', ' "isBackground": true,', ' "label": "Core - Build", /** multi-line or block comment', ' ...', ' ...', ' */', ' // one line comment', ' "presentation": {', ' "close": false,', ' "group": "buildWatchers", // one line comment', ' "reveal": "never"', ' },', ' "problemMatcher": {', ' "applyTo": "closedDocuments",', ' // one line comment', ' "background": {', ' "beginsPattern": "Starting compilation...",', ' "endsPattern": "Finished compilation with"', ' },', ' "fileLocation": [', ' /** multi-line or block comment', ' ...', ' ...', ' */', ' "absolute"', ' ],', ' "owner": "typescript",', ' "pattern": {', ' "file": 1,', ' "location": 2, /** multi-line or block comment', ' ...', ' ...', ' */', ' "message": 3,', ' "regexp": "Error: ([^(]+)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\): (.*)$"', ' }', ' }, // one line comment', ' "script": "watch-clientd",', ' "type": "npm" /** multi-line or block comment', ' ...', ' ...', ' */', ' },', ' { /** multi-line or block comment', ' ...', ' ...', ' */', ' "isBackground": true,', ' "label": "Ext - Build",', ' "presentation": {', ' "close": false,', ' "group": "buildWatchers",', ' "reveal": "never"', ' },', ' "problemMatcher": {', ' "applyTo": "closedDocuments",', ' "background": {', ' "beginsPattern": "Starting compilation",', ' "endsPattern": "Finished compilation"', ' },', ' "fileLocation": [ // one line comment', ' "absolute"', ' ],', ' "owner": "typescript",', ' "pattern": { /** multi-line or block comment', ' ...', ' ...', ' */', ' "file": 1,', ' "location": 2,', ' "message": 3,', ' "regexp": "Error: ([^(]+)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\): (.*)$"', ' }', ' },', ' "script": "watch-extensionsd",', ' "type": "npm"', ' }', ' ],', ' "version": "2.0.0"', '}', '/** multi-line or block comment', '...', '...', '*/', ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a complicated JSONC object 5', () => { const content = [ '{ /** multi-line or block comment', '...', '...', '*/', ' "registrations": [ /** multi-line or block comment', ' ...', ' ...', ' */', ' { /** multi-line or block comment', ' ...', ' ...', ' */', ' "component": { /** multi-line or block comment', ' ...', ' ...', ' */', ' "type": "git",', ' "git": {', ' "name": "textmate/markdown.tmbundle" /** multi-line or block comment', ' ...', ' ...', ' */,', ' "repositoryUrl": /** multi-line or block comment', ' ...', ' ...', ' */ "https://github.com/textmate/markdown.tmbundle",', ' "commitHash": /** multi-line or block comment', ' ...', ' ...', ' */ "11cf764606cb2cde54badb5d0e5a0758a8871c4b"', ' }', ' },', ' "licenseDetail": /** multi-line or block comment', ' ...', ' ...', ' */ [ /** multi-line or block comment', ' ...', ' ...', ' */', ' "Copyright (c) markdown.tmbundle authors",', ' "" /** multi-line or block comment', ' ...', ' ...', ' */,', ' "If not otherwise specified (see below), files in this repository fall under the following license:",', ' "" /** multi-line or block comment', ' ...', ' ...', ' */,', ' "Permission to copy, use, modify, sell and distribute this", /** multi-line or block comment', ' ...', ' ...', ' */', ' "",', ' "An exception is made for files in readable text which contain their own license information,",', ' "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added",', ' "to the base-name name of the original file, and an extension of txt, html, or similar. For example",', ' "\"tidy\" is accompanied by \"tidy-license.txt\"."', ' ] /** multi-line or block comment', ' ...', ' ...', ' */,', ' "license": "TextMate Bundle License",', ' "version": "0.0.0"', ' } /** multi-line or block comment', ' ...', ' ...', ' */,', '', ' /** multi-line or block comment', ' ...', ' ...', ' */', ' {', ' "component": {', ' "type": "git",', ' "git": {', ' "name": "microsoft/vscode-markdown-tm-grammar",', ' "repositoryUrl": "https://github.com/microsoft/vscode-markdown-tm-grammar",', ' "commitHash": "443261e8f75b2eaa8b36a2613fe7c4354208260a"', ' }', ' },', ' /** multi-line or block comment', ' ...', ' ...', ' */', ' "license": "MIT",', ' "version": "1.0.0"', ' }', ' ],', ' "version": 1', '} /** multi-line or block comment', '...', '...', '*/' ].join('\n'); const expected = [ '{ /** multi-line or block comment', '...', '...', '*/', ' "registrations": [ /** multi-line or block comment', ' ...', ' ...', ' */', ' { /** multi-line or block comment', ' ...', ' ...', ' */', ' "component": { /** multi-line or block comment', ' ...', ' ...', ' */', ' "git": {', ' "commitHash": /** multi-line or block comment', ' ...', ' ...', ' */ "11cf764606cb2cde54badb5d0e5a0758a8871c4b",', ' "name": "textmate/markdown.tmbundle" /** multi-line or block comment', ' ...', ' ...', ' */,', ' "repositoryUrl": /** multi-line or block comment', ' ...', ' ...', ' */ "https://github.com/textmate/markdown.tmbundle"', ' },', ' "type": "git"', ' },', ' "license": "TextMate Bundle License",', ' "licenseDetail": /** multi-line or block comment', ' ...', ' ...', ' */ [ /** multi-line or block comment', ' ...', ' ...', ' */', ' "Copyright (c) markdown.tmbundle authors",', ' "" /** multi-line or block comment', ' ...', ' ...', ' */,', ' "If not otherwise specified (see below), files in this repository fall under the following license:",', ' "" /** multi-line or block comment', ' ...', ' ...', ' */,', ' "Permission to copy, use, modify, sell and distribute this", /** multi-line or block comment', ' ...', ' ...', ' */', ' "",', ' "An exception is made for files in readable text which contain their own license information,",', ' "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added",', ' "to the base-name name of the original file, and an extension of txt, html, or similar. For example",', ' "\"tidy\" is accompanied by \"tidy-license.txt\"."', ' ] /** multi-line or block comment', ' ...', ' ...', ' */,', ' "version": "0.0.0"', ' } /** multi-line or block comment', ' ...', ' ...', ' */,', ' /** multi-line or block comment', ' ...', ' ...', ' */', ' {', ' "component": {', ' "git": {', ' "commitHash": "443261e8f75b2eaa8b36a2613fe7c4354208260a",', ' "name": "microsoft/vscode-markdown-tm-grammar",', ' "repositoryUrl": "https://github.com/microsoft/vscode-markdown-tm-grammar"', ' },', ' "type": "git"', ' },', ' /** multi-line or block comment', ' ...', ' ...', ' */', ' "license": "MIT",', ' "version": "1.0.0"', ' }', ' ],', ' "version": 1', '} /** multi-line or block comment', '...', '...', '*/' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a complicated JSONC object 6', () => { const content = [ '{', ' "tsImportSorter.configuration.groupRules": [', ' [{ "builtin": true }, "^[@][^/]", {}],', ' [', ' "^@(/|$)",', ' "^apis?(/|$)",', ' "^assets(/|$)",', ' "^components?(/|$)",', ' "^pages?(/|$)",', ' "^slices?(/|$)",', ' "^store(/|$)",', ' "^typings?(/|$)",', ' "^utils(/|$)"', ' ],', ' { "flags": "named", "regex": "^[.]" },', ' [{ "flags": "scripts" }, { "flags": "scripts", "regex": "[.]((css)|(less)|(scss))$" }]', ' ]', '}', ].join('\n'); const expected = [ '{', ' "tsImportSorter.configuration.groupRules": [', ' [', ' {', ' "builtin": true', ' },', ' "^[@][^/]",', ' {}', ' ],', ' [', ' "^@(/|$)",', ' "^apis?(/|$)",', ' "^assets(/|$)",', ' "^components?(/|$)",', ' "^pages?(/|$)",', ' "^slices?(/|$)",', ' "^store(/|$)",', ' "^typings?(/|$)",', ' "^utils(/|$)"', ' ],', ' {', ' "flags": "named",', ' "regex": "^[.]"', ' },', ' [', ' {', ' "flags": "scripts"', ' },', ' {', ' "flags": "scripts",', ' "regex": "[.]((css)|(less)|(scss))$"', ' }', ' ]', ' ]', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting a JSON object with mixed case keys', () => { const content = [ '{', ' "tEst": "tEst",', ' "tesT": "tesT",', ' "teSt": "teSt",', ' "Test": "Test",', ' "test": "test"', '}' ].join('\n'); const expected = [ '{', ' "test": "test",', ' "tesT": "tesT",', ' "teSt": "teSt",', ' "tEst": "tEst",', ' "Test": "Test"', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting an already sorted JSON object with mixed case keys', () => { const content = [ '{', ' "test": "test",', ' "tesT": "tesT",', ' "teSt": "teSt",', ' "tEst": "tEst",', ' "Test": "Test"', '}' ].join('\n'); const expected = [ '{', ' "test": "test",', ' "tesT": "tesT",', ' "teSt": "teSt",', ' "tEst": "tEst",', ' "Test": "Test"', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); test('sorting symbols before letters', () => { const content = [ '{', ' "Test": "Test",', ' "test": "test",', ' "[test]: "test', '}' ].join('\n'); const expected = [ '{', ' "[test]: "test,', ' "test": "test",', ' "Test": "Test"', '}' ].join('\n'); testSort(content, expected, formattingOptions); }); }); vscode-json-languageservice-5.7.2/src/test/stringify.test.ts000066400000000000000000000017411514707600300242410ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { stringifyObject } from '../utils/json'; import * as assert from 'assert'; suite('JSON Stringify', () => { test('Object', function () { const obj = { key1: 'Hello', key2: true, key3: 1.3, key4: null, key5: { key1: '', key2: false } }; assert.equal(stringifyObject(obj, '', JSON.stringify), '{\n\t"key1": "Hello",\n\t"key2": true,\n\t"key3": 1.3,\n\t"key4": null,\n\t"key5": {\n\t\t"key1": "",\n\t\t"key2": false\n\t}\n}'); }); test('Array', function () { const arr = [ 'Hello', {}, [1234], [] ]; assert.equal(stringifyObject(arr, '', JSON.stringify), JSON.stringify(arr, null, '\t')); }); });vscode-json-languageservice-5.7.2/src/tsconfig.esm.json000066400000000000000000000003351514707600300232060ustar00rootroot00000000000000{ "compilerOptions": { "target": "es2020", "module": "es6", "moduleResolution": "node", "sourceMap": true, "declaration": true, "stripInternal": true, "outDir": "../lib/esm", "lib": [ "es2020" ] } }vscode-json-languageservice-5.7.2/src/tsconfig.json000066400000000000000000000003571514707600300224270ustar00rootroot00000000000000{ "compilerOptions": { "target": "es2020", "module": "umd", "moduleResolution": "node", "sourceMap": true, "declaration": true, "stripInternal": true, "outDir": "../lib/umd", "lib": [ "es2020" ], "strict": true } }vscode-json-languageservice-5.7.2/src/utils/000077500000000000000000000000001514707600300210535ustar00rootroot00000000000000vscode-json-languageservice-5.7.2/src/utils/colors.ts000066400000000000000000000043211514707600300227240ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Color } from "../jsonLanguageTypes"; const Digit0 = 48; const Digit9 = 57; const A = 65; const a = 97; const f = 102; export function hexDigit(charCode: number) { if (charCode < Digit0) { return 0; } if (charCode <= Digit9) { return charCode - Digit0; } if (charCode < a) { charCode += (a - A); } if (charCode >= a && charCode <= f) { return charCode - a + 10; } return 0; } export function colorFromHex(text: string): Color | undefined { if (text[0] !== '#') { return undefined; } switch (text.length) { case 4: return { red: (hexDigit(text.charCodeAt(1)) * 0x11) / 255.0, green: (hexDigit(text.charCodeAt(2)) * 0x11) / 255.0, blue: (hexDigit(text.charCodeAt(3)) * 0x11) / 255.0, alpha: 1 }; case 5: return { red: (hexDigit(text.charCodeAt(1)) * 0x11) / 255.0, green: (hexDigit(text.charCodeAt(2)) * 0x11) / 255.0, blue: (hexDigit(text.charCodeAt(3)) * 0x11) / 255.0, alpha: (hexDigit(text.charCodeAt(4)) * 0x11) / 255.0, }; case 7: return { red: (hexDigit(text.charCodeAt(1)) * 0x10 + hexDigit(text.charCodeAt(2))) / 255.0, green: (hexDigit(text.charCodeAt(3)) * 0x10 + hexDigit(text.charCodeAt(4))) / 255.0, blue: (hexDigit(text.charCodeAt(5)) * 0x10 + hexDigit(text.charCodeAt(6))) / 255.0, alpha: 1 }; case 9: return { red: (hexDigit(text.charCodeAt(1)) * 0x10 + hexDigit(text.charCodeAt(2))) / 255.0, green: (hexDigit(text.charCodeAt(3)) * 0x10 + hexDigit(text.charCodeAt(4))) / 255.0, blue: (hexDigit(text.charCodeAt(5)) * 0x10 + hexDigit(text.charCodeAt(6))) / 255.0, alpha: (hexDigit(text.charCodeAt(7)) * 0x10 + hexDigit(text.charCodeAt(8))) / 255.0 }; } return undefined; } export function colorFrom256RGB(red: number, green: number, blue: number, alpha: number = 1.0) { return { red: red / 255.0, green: green / 255.0, blue: blue / 255.0, alpha }; }vscode-json-languageservice-5.7.2/src/utils/format.ts000066400000000000000000000020641514707600300227150ustar00rootroot00000000000000import { format as formatJSON, Range as JSONCRange } from 'jsonc-parser'; import { TextDocument, Range, TextEdit, FormattingOptions } from '../jsonLanguageTypes'; export function format(documentToFormat: TextDocument, formattingOptions?: FormattingOptions, formattingRange?: Range | undefined): TextEdit[] { let range: JSONCRange | undefined = undefined; if (formattingRange) { const offset = documentToFormat.offsetAt(formattingRange.start); const length = documentToFormat.offsetAt(formattingRange.end) - offset; range = { offset, length }; } const options = { tabSize: formattingOptions ? formattingOptions.tabSize : 4, insertSpaces: formattingOptions?.insertSpaces === true, insertFinalNewline: formattingOptions?.insertFinalNewline === true, eol: '\n', keepLines : formattingOptions?.keepLines === true }; return formatJSON(documentToFormat.getText(), range, options).map(edit => { return TextEdit.replace(Range.create(documentToFormat.positionAt(edit.offset), documentToFormat.positionAt(edit.offset + edit.length)), edit.content); }); }vscode-json-languageservice-5.7.2/src/utils/glob.ts000066400000000000000000000117401514707600300223510ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Copyright (c) 2013, Nick Fitzgerald * Licensed under the MIT License. See LICENCE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ export function createRegex(glob: string, opts: { extended?: boolean, globstar?: boolean, flags?: string }) : RegExp { if (typeof glob !== 'string') { throw new TypeError('Expected a string'); } const str = String(glob); // The regexp we are building, as a string. let reStr = ""; // Whether we are matching so called "extended" globs (like bash) and should // support single character matching, matching ranges of characters, group // matching, etc. const extended = opts ? !!opts.extended : false; // When globstar is _false_ (default), '/foo/*' is translated a regexp like // '^\/foo\/.*$' which will match any string beginning with '/foo/' // When globstar is _true_, '/foo/*' is translated to regexp like // '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT // which does not have a '/' to the right of it. // E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but // these will not '/foo/bar/baz', '/foo/bar/baz.txt' // Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when // globstar is _false_ const globstar = opts ? !!opts.globstar : false; // If we are doing extended matching, this boolean is true when we are inside // a group (eg {*.html,*.js}), and false otherwise. let inGroup = false; // RegExp flags (eg "i" ) to pass in to RegExp constructor. const flags = opts && typeof (opts.flags) === "string" ? opts.flags : ""; let c; for (let i = 0, len = str.length; i < len; i++) { c = str[i]; switch (c) { case "/": case "$": case "^": case "+": case ".": case "(": case ")": case "=": case "!": case "|": reStr += "\\" + c; break; case "?": if (extended) { reStr += "."; break; } case "[": case "]": if (extended) { reStr += c; break; } case "{": if (extended) { inGroup = true; reStr += "("; break; } case "}": if (extended) { inGroup = false; reStr += ")"; break; } case ",": if (inGroup) { reStr += "|"; break; } reStr += "\\" + c; break; case "*": // Move over all consecutive "*"'s. // Also store the previous and next characters const prevChar = str[i - 1]; let starCount = 1; while (str[i + 1] === "*") { starCount++; i++; } const nextChar = str[i + 1]; if (!globstar) { // globstar is disabled, so treat any number of "*" as one reStr += ".*"; } else { // globstar is enabled, so determine if this is a globstar segment const isGlobstar = starCount > 1 // multiple "*"'s && (prevChar === "/" || prevChar === undefined || prevChar === '{' || prevChar === ',') // from the start of the segment && (nextChar === "/" || nextChar === undefined || nextChar === ',' || nextChar === '}'); // to the end of the segment if (isGlobstar) { if (nextChar === "/") { i++; // move over the "/" } else if (prevChar === '/' && reStr.endsWith('\\/')) { reStr = reStr.substr(0, reStr.length - 2); } // it's a globstar, so match zero or more path segments reStr += "((?:[^/]*(?:\/|$))*)"; } else { // it's not a globstar, so only match one path segment reStr += "([^/]*)"; } } break; default: reStr += c; } } // When regexp 'g' flag is specified don't // constrain the regular expression with ^ & $ if (!flags || !~flags.indexOf('g')) { reStr = "^" + reStr + "$"; } return new RegExp(reStr, flags); };vscode-json-languageservice-5.7.2/src/utils/json.ts000066400000000000000000000025111514707600300223730ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ export function stringifyObject(obj: any, indent: string, stringifyLiteral: (val: any) => string): string { if (obj !== null && typeof obj === 'object') { const newIndent = indent + '\t'; if (Array.isArray(obj)) { if (obj.length === 0) { return '[]'; } let result = '[\n'; for (let i = 0; i < obj.length; i++) { result += newIndent + stringifyObject(obj[i], newIndent, stringifyLiteral); if (i < obj.length - 1) { result += ','; } result += '\n'; } result += indent + ']'; return result; } else { const keys = Object.keys(obj); if (keys.length === 0) { return '{}'; } let result = '{\n'; for (let i = 0; i < keys.length; i++) { const key = keys[i]; result += newIndent + JSON.stringify(key) + ': ' + stringifyObject(obj[key], newIndent, stringifyLiteral); if (i < keys.length - 1) { result += ','; } result += '\n'; } result += indent + '}'; return result; } } return stringifyLiteral(obj); }vscode-json-languageservice-5.7.2/src/utils/objects.ts000066400000000000000000000034641514707600300230630ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ export function equals(one: any, other: any): boolean { if (one === other) { return true; } if (one === null || one === undefined || other === null || other === undefined) { return false; } if (typeof one !== typeof other) { return false; } if (typeof one !== 'object') { return false; } if ((Array.isArray(one)) !== (Array.isArray(other))) { return false; } let i: number, key: string; if (Array.isArray(one)) { if (one.length !== other.length) { return false; } for (i = 0; i < one.length; i++) { if (!equals(one[i], other[i])) { return false; } } } else { const oneKeys: string[] = []; for (key in one) { oneKeys.push(key); } oneKeys.sort(); const otherKeys: string[] = []; for (key in other) { otherKeys.push(key); } otherKeys.sort(); if (!equals(oneKeys, otherKeys)) { return false; } for (i = 0; i < oneKeys.length; i++) { if (!equals(one[oneKeys[i]], other[oneKeys[i]])) { return false; } } } return true; } export function isNumber(val: any): val is number { return typeof val === 'number'; } export function isDefined(val: any): val is object { return typeof val !== 'undefined'; } export function isBoolean(val: any): val is boolean { return typeof val === 'boolean'; } export function isString(val: any): val is string { return typeof val === 'string'; } export function isObject(val: any): val is object { return typeof val === 'object' && val !== null && !Array.isArray(val); }vscode-json-languageservice-5.7.2/src/utils/propertyTree.ts000066400000000000000000000062711514707600300241350ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ export enum Container { Object, Array } export class PropertyTree { propertyName: string; beginningLineNumber: number | undefined; endLineNumber: number | undefined; parent: PropertyTree | undefined; commaIndex: number | undefined; commaLine: number | undefined; lineWhereToAddComma: number | undefined; indexWhereToAddComa: number | undefined; type: Container | undefined; childrenProperties: PropertyTree[]; lastProperty: boolean; noKeyName: boolean; constructor( propertyName?: string, beginningLineNumber?: number ) { this.propertyName = propertyName ?? ''; this.beginningLineNumber = beginningLineNumber; this.childrenProperties = []; this.lastProperty = false; this.noKeyName = false; } addChildProperty(childProperty: PropertyTree): PropertyTree { childProperty.parent = this; if (this.childrenProperties.length > 0) { let insertionIndex = 0; if (childProperty.noKeyName) { insertionIndex = this.childrenProperties.length; } else { insertionIndex = binarySearchOnPropertyArray(this.childrenProperties, childProperty, compareProperties); } if (insertionIndex < 0) { insertionIndex = (insertionIndex * -1) - 1; } this.childrenProperties.splice(insertionIndex, 0, childProperty); } else { this.childrenProperties.push(childProperty); } return childProperty; } } function compareProperties(propertyTree1: PropertyTree, propertyTree2: PropertyTree) { const propertyName1 = propertyTree1.propertyName.toLowerCase(); const propertyName2 = propertyTree2.propertyName.toLowerCase(); if (propertyName1 < propertyName2) { return -1; } else if (propertyName1 > propertyName2) { return 1; } return 0; } function binarySearchOnPropertyArray(propertyTreeArray: PropertyTree[], propertyTree: PropertyTree, compare_fn: (p1: PropertyTree, p2: PropertyTree) => number) { const propertyName = propertyTree.propertyName.toLowerCase(); const firstPropertyInArrayName = propertyTreeArray[0].propertyName.toLowerCase(); const lastPropertyInArrayName = propertyTreeArray[propertyTreeArray.length - 1].propertyName.toLowerCase(); if (propertyName < firstPropertyInArrayName) { return 0; } if (propertyName > lastPropertyInArrayName) { return propertyTreeArray.length; } let m = 0; let n = propertyTreeArray.length - 1; while (m <= n) { let k = (n + m) >> 1; let cmp = compare_fn(propertyTree, propertyTreeArray[k]); if (cmp > 0) { m = k + 1; } else if (cmp < 0) { n = k - 1; } else { return k; } } return -m - 1; }vscode-json-languageservice-5.7.2/src/utils/sort.ts000066400000000000000000000577011514707600300224240ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { createScanner, SyntaxKind, JSONScanner } from 'jsonc-parser'; import { TextDocument, TextEdit, FormattingOptions, Position, Range, TextDocumentContentChangeEvent, SortOptions } from '../jsonLanguageTypes'; import { format } from './format'; import { PropertyTree, Container } from './propertyTree'; export function sort(documentToSort: TextDocument, formattingOptions: SortOptions): TextEdit[] { const options: FormattingOptions = { ...formattingOptions, keepLines: false, // keepLines must be false so that the properties are on separate lines for the sorting }; const formattedJsonString: string = TextDocument.applyEdits(documentToSort, format(documentToSort, options, undefined)); const formattedJsonDocument = TextDocument.create('test://test.json', 'json', 0, formattedJsonString); const jsonPropertyTree: PropertyTree = findJsoncPropertyTree(formattedJsonDocument); const sortedJsonDocument = sortJsoncDocument(formattedJsonDocument, jsonPropertyTree); const edits: TextEdit[] = format(sortedJsonDocument, options, undefined); const sortedAndFormattedJsonDocument = TextDocument.applyEdits(sortedJsonDocument, edits); return [TextEdit.replace(Range.create(Position.create(0, 0), documentToSort.positionAt(documentToSort.getText().length)), sortedAndFormattedJsonDocument)]; } function findJsoncPropertyTree(formattedDocument: TextDocument) { const formattedString = formattedDocument.getText(); const scanner: JSONScanner = createScanner(formattedString, false); // The tree that will be returned let rootTree: PropertyTree = new PropertyTree(); // The tree where the current properties can be added as children let currentTree: PropertyTree | undefined = rootTree; // The tree representing the current property analyzed let currentProperty: PropertyTree | undefined = rootTree; // The tree representing the previous property analyzed let lastProperty: PropertyTree | undefined = rootTree; // The current scanned token let token: SyntaxKind | undefined = undefined; // Line number of the last token found let lastTokenLine: number = 0; // Total number of characters on the lines prior to current line let numberOfCharactersOnPreviousLines: number = 0; // The last token scanned that is not trivial, nor a comment let lastNonTriviaNonCommentToken: SyntaxKind | undefined = undefined; // The second to last token scanned that is not trivial, nor a comment let secondToLastNonTriviaNonCommentToken: SyntaxKind | undefined = undefined; // Line number of last token that is not trivial, nor a comment let lineOfLastNonTriviaNonCommentToken: number = -1; // End index on its line of last token that is not trivial, nor a comment let endIndexOfLastNonTriviaNonCommentToken: number = -1; // Line number of the start of the range of current/next property let beginningLineNumber: number = 0; // Line number of the end of the range of current/next property let endLineNumber: number = 0; // Stack indicating whether we are inside of an object or an array let currentContainerStack: Container[] = []; // Boolean indicating that the current property end line number needs to be updated. Used only when block comments are encountered. let updateLastPropertyEndLineNumber: boolean = false; // Boolean indicating that the beginning line number should be updated. Used only when block comments are encountered. let updateBeginningLineNumber: boolean = false; while ((token = scanner.scan()) !== SyntaxKind.EOF) { // In the case when a block comment has been encountered that starts on the same line as the comma ending a property, update the end line of that // property so that it covers the block comment. For example, if we have: // 1. "key" : {}, /* some block // 2. comment */ // Then, the end line of the property "key" should be line 2 not line 1 if (updateLastPropertyEndLineNumber === true && token !== SyntaxKind.LineBreakTrivia && token !== SyntaxKind.Trivia && token !== SyntaxKind.LineCommentTrivia && token !== SyntaxKind.BlockCommentTrivia && currentProperty!.endLineNumber === undefined) { let endLineNumber = scanner.getTokenStartLine(); // Update the end line number in the case when the last property visited is a container (object or array) if (secondToLastNonTriviaNonCommentToken === SyntaxKind.CloseBraceToken || secondToLastNonTriviaNonCommentToken === SyntaxKind.CloseBracketToken) { lastProperty!.endLineNumber = endLineNumber - 1; } // Update the end line number in the case when the last property visited is a simple property else { currentProperty!.endLineNumber = endLineNumber - 1; } beginningLineNumber = endLineNumber; updateLastPropertyEndLineNumber = false; } // When a block comment follows an open brace or an open bracket, that block comment should be associated to that brace or bracket, not the property below it. For example, for: // 1. { /* // 2. ... */ // 3. "key" : {} // 4. } // Instead of associating the block comment to the property on line 3, it is associate to the property on line 1 if (updateBeginningLineNumber === true && token !== SyntaxKind.LineBreakTrivia && token !== SyntaxKind.Trivia && token !== SyntaxKind.LineCommentTrivia && token !== SyntaxKind.BlockCommentTrivia) { beginningLineNumber = scanner.getTokenStartLine(); updateBeginningLineNumber = false; } // Update the number of characters on all the previous lines each time the new token is on a different line to the previous token if (scanner.getTokenStartLine() !== lastTokenLine) { for (let i = lastTokenLine; i < scanner.getTokenStartLine(); i++) { const lengthOfLine = formattedDocument.getText(Range.create(Position.create(i, 0), Position.create(i + 1, 0))).length; numberOfCharactersOnPreviousLines = numberOfCharactersOnPreviousLines + lengthOfLine; } lastTokenLine = scanner.getTokenStartLine(); } switch (token) { // When a string is found, if it follows an open brace or a comma token and it is within an object, then it corresponds to a key name, not a simple string case SyntaxKind.StringLiteral: { if ((lastNonTriviaNonCommentToken === undefined || lastNonTriviaNonCommentToken === SyntaxKind.OpenBraceToken || (lastNonTriviaNonCommentToken === SyntaxKind.CommaToken && currentContainerStack[currentContainerStack.length - 1] === Container.Object))) { // In that case create the child property which starts at beginningLineNumber, add it to the current tree const childProperty: PropertyTree = new PropertyTree(scanner.getTokenValue(), beginningLineNumber); lastProperty = currentProperty; currentProperty = currentTree!.addChildProperty(childProperty); } break; } // When the token is an open bracket, then we enter into an array case SyntaxKind.OpenBracketToken: { // If the root tree beginning line number is not defined, then this open bracket is the first open bracket in the document if (rootTree.beginningLineNumber === undefined) { rootTree.beginningLineNumber = scanner.getTokenStartLine(); } // Suppose we are inside of an object, then the current array is associated to a key, and has already been created // We have the following configuration: {"a": "val", "array": [...], "b": "val"} // In that case navigate down to the child property if (currentContainerStack[currentContainerStack.length - 1] === Container.Object) { currentTree = currentProperty; } // Suppose we are inside of an array, then since the current array is not associated to a key, it has not been created yet // We have the following configuration: ["a", [...], "b"] // In that case create the property and navigate down else if (currentContainerStack[currentContainerStack.length - 1] === Container.Array) { const childProperty: PropertyTree = new PropertyTree(scanner.getTokenValue(), beginningLineNumber); childProperty.noKeyName = true; lastProperty = currentProperty; currentProperty = currentTree!.addChildProperty(childProperty); currentTree = currentProperty; } currentContainerStack.push(Container.Array); currentProperty!.type = Container.Array; beginningLineNumber = scanner.getTokenStartLine(); beginningLineNumber++; break; } // When the token is an open brace, then we enter into an object case SyntaxKind.OpenBraceToken: { // If the root tree beginning line number is not defined, then this open brace is the first open brace in the document if (rootTree.beginningLineNumber === undefined) { rootTree.beginningLineNumber = scanner.getTokenStartLine(); } // 1. If we are inside of an objet, the current object is associated to a key and has already been created // We have the following configuration: {"a": "val", "object": {...}, "b": "val"} // 2. Otherwise the current object property is inside of an array, not associated to a key name and the property has not yet been created // We have the following configuration: ["a", {...}, "b"] else if (currentContainerStack[currentContainerStack.length - 1] === Container.Array) { const childProperty: PropertyTree = new PropertyTree(scanner.getTokenValue(), beginningLineNumber); childProperty.noKeyName = true; lastProperty = currentProperty; currentProperty = currentTree!.addChildProperty(childProperty); } currentProperty!.type = Container.Object; currentContainerStack.push(Container.Object); currentTree = currentProperty; beginningLineNumber = scanner.getTokenStartLine(); beginningLineNumber++; break; } case SyntaxKind.CloseBracketToken: { endLineNumber = scanner.getTokenStartLine(); currentContainerStack.pop(); // If the last non-trivial non-comment token is a closing brace or bracket, then the currentProperty end line number has not been set yet so set it // The configuration considered is: [..., {}] or [..., []] if (currentProperty!.endLineNumber === undefined && (lastNonTriviaNonCommentToken === SyntaxKind.CloseBraceToken || lastNonTriviaNonCommentToken === SyntaxKind.CloseBracketToken)) { currentProperty!.endLineNumber = endLineNumber - 1; currentProperty!.lastProperty = true; currentProperty!.lineWhereToAddComma = lineOfLastNonTriviaNonCommentToken; currentProperty!.indexWhereToAddComa = endIndexOfLastNonTriviaNonCommentToken; lastProperty = currentProperty; currentProperty = currentProperty ? currentProperty.parent : undefined; currentTree = currentProperty; } rootTree.endLineNumber = endLineNumber; beginningLineNumber = endLineNumber + 1; break; } case SyntaxKind.CloseBraceToken: { endLineNumber = scanner.getTokenStartLine(); currentContainerStack.pop(); // If we are not inside of an empty object if (lastNonTriviaNonCommentToken !== SyntaxKind.OpenBraceToken) { // If current property end line number has not yet been defined, define it if (currentProperty!.endLineNumber === undefined) { currentProperty!.endLineNumber = endLineNumber - 1; // The current property is also the last property currentProperty!.lastProperty = true; // The last property of an object is associated with the line and index of where to add the comma, in case after sorting, it is no longer the last property currentProperty!.lineWhereToAddComma = lineOfLastNonTriviaNonCommentToken; currentProperty!.indexWhereToAddComa = endIndexOfLastNonTriviaNonCommentToken; } lastProperty = currentProperty; currentProperty = currentProperty ? currentProperty.parent : undefined; currentTree = currentProperty; } rootTree.endLineNumber = scanner.getTokenStartLine(); beginningLineNumber = endLineNumber + 1; break; } case SyntaxKind.CommaToken: { endLineNumber = scanner.getTokenStartLine(); // If the current container is an object or the current container is an array and the last non-trivia non-comment token is a closing brace or a closing bracket // Then update the end line number of the current property if (currentProperty!.endLineNumber === undefined && (currentContainerStack[currentContainerStack.length - 1] === Container.Object || (currentContainerStack[currentContainerStack.length - 1] === Container.Array && (lastNonTriviaNonCommentToken === SyntaxKind.CloseBraceToken || lastNonTriviaNonCommentToken === SyntaxKind.CloseBracketToken)))) { currentProperty!.endLineNumber = endLineNumber; // Store the line and the index of the comma in case it needs to be removed during the sorting currentProperty!.commaIndex = scanner.getTokenOffset() - numberOfCharactersOnPreviousLines; currentProperty!.commaLine = endLineNumber; } if (lastNonTriviaNonCommentToken === SyntaxKind.CloseBraceToken || lastNonTriviaNonCommentToken === SyntaxKind.CloseBracketToken) { lastProperty = currentProperty; currentProperty = currentProperty ? currentProperty.parent : undefined; currentTree = currentProperty; } beginningLineNumber = endLineNumber + 1; break; } case SyntaxKind.BlockCommentTrivia: { // If the last non trivia non-comment token is a comma and the block comment starts on the same line as the comma, then update the end line number of the current property. For example if: // 1. {}, /* ... // 2. ..*/ // The the property on line 1 shoud end on line 2, not line 1 // In the case we are in an array we update the end line number only if the second to last non-trivia non-comment token is a closing brace or bracket if (lastNonTriviaNonCommentToken === SyntaxKind.CommaToken && lineOfLastNonTriviaNonCommentToken === scanner.getTokenStartLine() && (currentContainerStack[currentContainerStack.length - 1] === Container.Array && (secondToLastNonTriviaNonCommentToken === SyntaxKind.CloseBraceToken || secondToLastNonTriviaNonCommentToken === SyntaxKind.CloseBracketToken) || currentContainerStack[currentContainerStack.length - 1] === Container.Object)) { if (currentContainerStack[currentContainerStack.length - 1] === Container.Array && (secondToLastNonTriviaNonCommentToken === SyntaxKind.CloseBraceToken || secondToLastNonTriviaNonCommentToken === SyntaxKind.CloseBracketToken) || currentContainerStack[currentContainerStack.length - 1] === Container.Object) { currentProperty!.endLineNumber = undefined; updateLastPropertyEndLineNumber = true; } } // When the block comment follows an open brace or an open token, we have the following scenario: // { /** // ../ // } // The block comment should be assigned to the open brace not the first property below it if ((lastNonTriviaNonCommentToken === SyntaxKind.OpenBraceToken || lastNonTriviaNonCommentToken === SyntaxKind.OpenBracketToken) && lineOfLastNonTriviaNonCommentToken === scanner.getTokenStartLine()) { updateBeginningLineNumber = true; } break; } } // Update the last and second to last non-trivia non-comment tokens if (token !== SyntaxKind.LineBreakTrivia && token !== SyntaxKind.BlockCommentTrivia && token !== SyntaxKind.LineCommentTrivia && token !== SyntaxKind.Trivia) { secondToLastNonTriviaNonCommentToken = lastNonTriviaNonCommentToken; lastNonTriviaNonCommentToken = token; lineOfLastNonTriviaNonCommentToken = scanner.getTokenStartLine(); endIndexOfLastNonTriviaNonCommentToken = scanner.getTokenOffset() + scanner.getTokenLength() - numberOfCharactersOnPreviousLines; } } return rootTree; } function sortJsoncDocument(jsonDocument: TextDocument, propertyTree: PropertyTree) { if (propertyTree.childrenProperties.length === 0) { return jsonDocument; } const sortedJsonDocument = TextDocument.create('test://test.json', 'json', 0, jsonDocument.getText()); const queueToSort: SortingRange[] = []; updateSortingQueue(queueToSort, propertyTree, propertyTree.beginningLineNumber!); while (queueToSort.length > 0) { const dataToSort = queueToSort.shift(); const propertyTreeArray: PropertyTree[] = dataToSort!.propertyTreeArray; let beginningLineNumber = dataToSort!.beginningLineNumber; for (let i = 0; i < propertyTreeArray.length; i++) { const propertyTree = propertyTreeArray[i]; const range: Range = Range.create(Position.create(propertyTree.beginningLineNumber!, 0), Position.create(propertyTree.endLineNumber! + 1, 0)); const jsonContentToReplace = jsonDocument.getText(range); const jsonDocumentToReplace = TextDocument.create('test://test.json', 'json', 0, jsonContentToReplace); if (propertyTree.lastProperty === true && i !== propertyTreeArray.length - 1) { const lineWhereToAddComma = propertyTree.lineWhereToAddComma! - propertyTree.beginningLineNumber!; const indexWhereToAddComma = propertyTree.indexWhereToAddComa!; const edit: TextDocumentContentChangeEvent = { range: Range.create(Position.create(lineWhereToAddComma, indexWhereToAddComma), Position.create(lineWhereToAddComma, indexWhereToAddComma)), text: ',' }; TextDocument.update(jsonDocumentToReplace, [edit], 1); } else if (propertyTree.lastProperty === false && i === propertyTreeArray.length - 1) { const commaIndex = propertyTree.commaIndex!; const commaLine = propertyTree.commaLine!; const lineWhereToRemoveComma = commaLine - propertyTree.beginningLineNumber!; const edit: TextDocumentContentChangeEvent = { range: Range.create(Position.create(lineWhereToRemoveComma, commaIndex), Position.create(lineWhereToRemoveComma, commaIndex + 1)), text: '' }; TextDocument.update(jsonDocumentToReplace, [edit], 1); } const length = propertyTree.endLineNumber! - propertyTree.beginningLineNumber! + 1; const edit: TextDocumentContentChangeEvent = { range: Range.create(Position.create(beginningLineNumber, 0), Position.create(beginningLineNumber + length, 0)), text: jsonDocumentToReplace.getText() }; TextDocument.update(sortedJsonDocument, [edit], 1); updateSortingQueue(queueToSort, propertyTree, beginningLineNumber); beginningLineNumber = beginningLineNumber + length; } } return sortedJsonDocument; } function sortProperties(properties: PropertyTree[]): void { properties.sort((a, b) => a.propertyName.localeCompare(b.propertyName)); } function updateSortingQueue(queue: any[], propertyTree: PropertyTree, beginningLineNumber: number) { if (propertyTree.childrenProperties.length === 0) { return; } if (propertyTree.type === Container.Object) { let minimumBeginningLineNumber = Infinity; for (const childProperty of propertyTree.childrenProperties) { if (childProperty.beginningLineNumber! < minimumBeginningLineNumber) { minimumBeginningLineNumber = childProperty.beginningLineNumber!; } } const diff = minimumBeginningLineNumber - propertyTree.beginningLineNumber!; beginningLineNumber = beginningLineNumber + diff; sortProperties(propertyTree.childrenProperties); queue.push(new SortingRange(beginningLineNumber, propertyTree.childrenProperties)); } else if (propertyTree.type === Container.Array) { updateSortingQueueForArrayProperties(queue, propertyTree, beginningLineNumber); } } function updateSortingQueueForArrayProperties(queue: any[], propertyTree: PropertyTree, beginningLineNumber: number) { for (const subObject of propertyTree.childrenProperties) { // If the child property of the array is an object, then you can sort the properties within this object if (subObject.type === Container.Object) { let minimumBeginningLineNumber = Infinity; for (const childProperty of subObject.childrenProperties) { if (childProperty.beginningLineNumber! < minimumBeginningLineNumber) { minimumBeginningLineNumber = childProperty.beginningLineNumber!; } } const diff = minimumBeginningLineNumber - subObject.beginningLineNumber!; queue.push(new SortingRange(beginningLineNumber + subObject.beginningLineNumber! - propertyTree.beginningLineNumber! + diff, subObject.childrenProperties)); } // If the child property of the array is an array, then you need to recurse on the children properties, until you find an object to sort if (subObject.type === Container.Array) { updateSortingQueueForArrayProperties(queue, subObject, beginningLineNumber + subObject.beginningLineNumber! - propertyTree.beginningLineNumber!); } } } class SortingRange { beginningLineNumber: number; propertyTreeArray: PropertyTree[]; constructor(beginningLineNumber: number, propertyTreeArray: PropertyTree[]) { this.beginningLineNumber = beginningLineNumber; this.propertyTreeArray = propertyTreeArray; } } vscode-json-languageservice-5.7.2/src/utils/strings.ts000066400000000000000000000041201514707600300231110ustar00rootroot00000000000000/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ export function startsWith(haystack: string, needle: string): boolean { if (haystack.length < needle.length) { return false; } for (let i = 0; i < needle.length; i++) { if (haystack[i] !== needle[i]) { return false; } } return true; } /** * Determines if haystack ends with needle. */ export function endsWith(haystack: string, needle: string): boolean { const diff = haystack.length - needle.length; if (diff > 0) { return haystack.lastIndexOf(needle) === diff; } else if (diff === 0) { return haystack === needle; } else { return false; } } export function convertSimple2RegExpPattern(pattern: string): string { return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*'); } export function repeat(value: string, count: number) { let s = ''; while (count > 0) { if ((count & 1) === 1) { s += value; } value += value; count = count >>> 1; } return s; } export function extendedRegExp(pattern: string): RegExp | undefined { let flags = ''; if (startsWith(pattern, '(?i)')) { pattern = pattern.substring(4); flags = 'i'; } try { return new RegExp(pattern, flags + 'u'); } catch (e) { // could be an exception due to the 'u ' flag try { return new RegExp(pattern, flags); } catch (e) { // invalid pattern return undefined; } } } // from https://tanishiking.github.io/posts/count-unicode-codepoint/#work-hard-with-for-statements export function stringLength(str: string) { let count = 0; for (let i = 0; i < str.length; i++) { count++; // obtain the i-th 16-bit const code = str.charCodeAt(i); if (0xD800 <= code && code <= 0xDBFF) { // if the i-th 16bit is an upper surrogate // skip the next 16 bits (lower surrogate) i++; } } return count; }