🎉 initiate project *astro_rewrite*

This commit is contained in:
sindrekjelsrud 2023-07-19 21:31:30 +02:00
parent ffd4d5e86c
commit 2ba37bfbe3
8658 changed files with 2268794 additions and 2538 deletions

21
node_modules/@vscode/emmet-helper/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The 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.

11
node_modules/@vscode/emmet-helper/README.md generated vendored Normal file
View file

@ -0,0 +1,11 @@
# vscode-emmet-helper
A helper module to use emmet modules with Visual Studio Code
Visual Studio Code extensions that provide language service and want to provide emmet abbreviation expansions
in auto-complete can include this module and use the `doComplete` method.
Just pass the one of the emmet supported syntaxes that you would like the completion provider to use along with other parameters that you would generally pass to a completion provider.
If `emmet.includeLanguages` has a mapping for your language, then the builit-in emmet extension will provide
html emmet abbreviations. Ask the user to remove the mapping, if your extension decides to provide
emmet completions using this module

41
node_modules/@vscode/emmet-helper/SECURITY.md generated vendored Normal file
View file

@ -0,0 +1,41 @@
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->
## 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).
<!-- END MICROSOFT SECURITY.MD BLOCK -->

View file

@ -0,0 +1,15 @@
export interface SnippetsMap {
[name: string]: string;
}
/**
* Parses raw snippets definitions with possibly multiple keys into a plan
* snippet map
*/
export declare function parseSnippets(snippets: SnippetsMap): SnippetsMap;
/**
* List of all known syntaxes
*/
export declare const syntaxes: {
markup: string[];
stylesheet: string[];
};

View file

@ -0,0 +1,48 @@
"use strict";
/*
MIT License
Copyright (c) 2020 Sergey Chikuyonok <serge.che@gmail.com>
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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.syntaxes = exports.parseSnippets = void 0;
/**
* Parses raw snippets definitions with possibly multiple keys into a plan
* snippet map
*/
function parseSnippets(snippets) {
const result = {};
Object.keys(snippets).forEach(k => {
for (const name of k.split('|')) {
result[name] = snippets[k];
}
});
return result;
}
exports.parseSnippets = parseSnippets;
/**
* List of all known syntaxes
*/
exports.syntaxes = {
markup: ['html', 'xml', 'xsl', 'jsx', 'js', 'pug', 'slim', 'haml'],
stylesheet: ['css', 'sass', 'scss', 'less', 'sss', 'stylus']
};
//# sourceMappingURL=configCompat.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"configCompat.js","sourceRoot":"","sources":["../../src/configCompat.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;EAsBE;;;AAMF;;;GAGG;AACH,SAAgB,aAAa,CAAC,QAAqB;IAC/C,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;QAC9B,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;YAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;SAC9B;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAClB,CAAC;AATD,sCASC;AAED;;GAEG;AACU,QAAA,QAAQ,GAAG;IACpB,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC;IAClE,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC;CAC/D,CAAC"}

6
node_modules/@vscode/emmet-helper/lib/cjs/data.d.ts generated vendored Normal file
View file

@ -0,0 +1,6 @@
export declare const cssData: {
properties: string[];
};
export declare const htmlData: {
tags: string[];
};

15
node_modules/@vscode/emmet-helper/lib/cjs/data.js generated vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,103 @@
import { TextDocument } from 'vscode-languageserver-textdocument';
import { CompletionList, Position, Range } from 'vscode-languageserver-types';
import { URI } from 'vscode-uri';
import { FileService, FileStat, FileType } from './fileService';
import { ExtractOptions, MarkupAbbreviation, Options, StylesheetAbbreviation, SyntaxType, UserConfig } from 'emmet';
import { SnippetsMap } from './configCompat';
export { FileService, FileType, FileStat };
/**
* Emmet configuration as derived from the Emmet related VS Code settings
*/
export interface VSCodeEmmetConfig {
showExpandedAbbreviation?: string;
showAbbreviationSuggestions?: boolean;
syntaxProfiles?: object;
variables?: object;
preferences?: object;
excludeLanguages?: string[];
showSuggestionsAsSnippets?: boolean;
}
/**
* Returns all applicable emmet expansions for abbreviation at given position in a CompletionList
* @param document TextDocument in which completions are requested
* @param position Position in the document at which completions are requested
* @param syntax Emmet supported language
* @param emmetConfig Emmet Configurations as derived from VS Code
*/
export declare function doComplete(document: TextDocument, position: Position, syntax: string, emmetConfig: VSCodeEmmetConfig): CompletionList | undefined;
export declare const emmetSnippetField: (index: number, placeholder: string) => string;
/** Returns whether or not syntax is a supported stylesheet syntax, like CSS */
export declare function isStyleSheet(syntax: string): boolean;
/** Returns the syntax type, either markup (e.g. for HTML) or stylesheet (e.g. for CSS) */
export declare function getSyntaxType(syntax: string): SyntaxType;
/** Returns the default syntax (html or css) to use for the snippets registry */
export declare function getDefaultSyntax(syntax: string): string;
/** Returns the default snippets that Emmet suggests */
export declare function getDefaultSnippets(syntax: string): SnippetsMap;
/**
* Extracts abbreviation from the given position in the given document
* @param document The TextDocument from which abbreviation needs to be extracted
* @param position The Position in the given document from where abbreviation needs to be extracted
* @param options The options to pass to the @emmetio/extract-abbreviation module
*/
export declare function extractAbbreviation(document: TextDocument, position: Position, options?: Partial<ExtractOptions>): {
abbreviation: string;
abbreviationRange: Range;
filter: string | undefined;
} | undefined;
/**
* Extracts abbreviation from the given text
* @param text Text from which abbreviation needs to be extracted
* @param syntax Syntax used to extract the abbreviation from the given text
*/
export declare function extractAbbreviationFromText(text: string, syntax: string): {
abbreviation: string;
filter: string | undefined;
} | undefined;
/**
* Returns a boolean denoting validity of given abbreviation in the context of given syntax
* Not needed once https://github.com/emmetio/atom-plugin/issues/22 is fixed
* @param syntax string
* @param abbreviation string
*/
export declare function isAbbreviationValid(syntax: string, abbreviation: string): boolean;
declare type ExpandOptionsConfig = {
type: SyntaxType;
options: Partial<Options>;
variables: SnippetsMap;
snippets: SnippetsMap;
syntax: string;
text: string | string[] | undefined;
maxRepeat: number;
};
/**
* Returns options to be used by emmet
*/
export declare function getExpandOptions(syntax: string, emmetConfig?: VSCodeEmmetConfig, filter?: string): ExpandOptionsConfig;
/**
* Parses given abbreviation using given options and returns a tree
* @param abbreviation string
* @param options options used by the emmet module to parse given abbreviation
*/
export declare function parseAbbreviation(abbreviation: string, options: UserConfig): StylesheetAbbreviation | MarkupAbbreviation;
/**
* Expands given abbreviation using given options
* @param abbreviation string or parsed abbreviation
* @param config options used by the @emmetio/expand-abbreviation module to expand given abbreviation
*/
export declare function expandAbbreviation(abbreviation: string | MarkupAbbreviation | StylesheetAbbreviation, config: UserConfig): string;
/**
* Updates customizations from snippets.json and syntaxProfiles.json files in the directory configured in emmet.extensionsPath setting
* @param emmetExtensionsPathSetting setting passed from emmet.extensionsPath. Supports multiple paths
*/
export declare function updateExtensionsPath(emmetExtensionsPathSetting: string[], fs: FileService, workspaceFolderPaths?: URI[], homeDir?: URI): Promise<void>;
/**
* Get the corresponding emmet mode for given vscode language mode
* Eg: jsx for typescriptreact/javascriptreact or pug for jade
* If the language is not supported by emmet or has been exlcuded via `exlcudeLanguages` setting,
* then nothing is returned
*
* @param language
* @param exlcudedLanguages Array of language ids that user has chosen to exlcude for emmet
*/
export declare function getEmmetMode(language: string, excludedLanguages?: string[]): string | undefined;

1075
node_modules/@vscode/emmet-helper/lib/cjs/emmetHelper.js generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,46 @@
import { URI as Uri } from 'vscode-uri';
export declare enum FileType {
/**
* The file type is unknown.
*/
Unknown = 0,
/**
* A regular file.
*/
File = 1,
/**
* A directory.
*/
Directory = 2,
/**
* A symbolic link to a file.
*/
SymbolicLink = 64
}
export interface FileStat {
/**
* The type of the file, e.g. is a regular file, a directory, or symbolic link
* to a file.
*/
type: FileType;
/**
* The creation timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
*/
ctime: number;
/**
* The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
*/
mtime: number;
/**
* The size in bytes.
*/
size: number;
}
export interface FileService {
readFile(uri: Uri): Thenable<Uint8Array>;
stat(uri: Uri): Thenable<FileStat>;
}
export declare function isAbsolutePath(path: string): boolean;
export declare function resolvePath(uri: Uri, path: string): Uri;
export declare function normalizePath(parts: string[]): string;
export declare function joinPath(uri: Uri, ...paths: string[]): Uri;

View file

@ -0,0 +1,72 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.joinPath = exports.normalizePath = exports.resolvePath = exports.isAbsolutePath = exports.FileType = void 0;
var FileType;
(function (FileType) {
/**
* The file type is unknown.
*/
FileType[FileType["Unknown"] = 0] = "Unknown";
/**
* A regular file.
*/
FileType[FileType["File"] = 1] = "File";
/**
* A directory.
*/
FileType[FileType["Directory"] = 2] = "Directory";
/**
* A symbolic link to a file.
*/
FileType[FileType["SymbolicLink"] = 64] = "SymbolicLink";
})(FileType = exports.FileType || (exports.FileType = {}));
// following https://nodejs.org/api/path.html#path_path_isabsolute_path
const PathMatchRegex = new RegExp('^(/|//|\\\\\\\\|[A-Za-z]:(/|\\\\))');
const Dot = '.'.charCodeAt(0);
function isAbsolutePath(path) {
return PathMatchRegex.test(path);
}
exports.isAbsolutePath = isAbsolutePath;
function resolvePath(uri, path) {
if (isAbsolutePath(path)) {
return uri.with({ path: normalizePath(path.split('/')) });
}
return joinPath(uri, path);
}
exports.resolvePath = resolvePath;
function normalizePath(parts) {
const newParts = [];
for (const part of parts) {
if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) {
// ignore
}
else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) {
newParts.pop();
}
else {
newParts.push(part);
}
}
if (parts.length > 1 && parts[parts.length - 1].length === 0) {
newParts.push('');
}
let res = newParts.join('/');
if (parts[0].length === 0) {
res = '/' + res;
}
return res;
}
exports.normalizePath = normalizePath;
function joinPath(uri, ...paths) {
const parts = uri.path.split('/');
for (const path of paths) {
parts.push(...path.split('/'));
}
return uri.with({ path: normalizePath(parts) });
}
exports.joinPath = joinPath;
//# sourceMappingURL=fileService.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"fileService.js","sourceRoot":"","sources":["../../src/fileService.ts"],"names":[],"mappings":";AAAA;;;gGAGgG;;;AAIhG,IAAY,QAiBX;AAjBD,WAAY,QAAQ;IACnB;;OAEG;IACH,6CAAW,CAAA;IACX;;OAEG;IACH,uCAAQ,CAAA;IACR;;OAEG;IACH,iDAAa,CAAA;IACb;;OAEG;IACH,wDAAiB,CAAA;AAClB,CAAC,EAjBW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAiBnB;AA0BD,uEAAuE;AACvE,MAAM,cAAc,GAAG,IAAI,MAAM,CAAC,oCAAoC,CAAC,CAAC;AACxE,MAAM,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;AAE9B,SAAgB,cAAc,CAAC,IAAY;IAC1C,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAFD,wCAEC;AAED,SAAgB,WAAW,CAAC,GAAQ,EAAE,IAAY;IACjD,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE;QACzB,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;KAC1D;IACD,OAAO,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC5B,CAAC;AALD,kCAKC;AAED,SAAgB,aAAa,CAAC,KAAe;IAC5C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;YACzE,SAAS;SACT;aAAM,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;YACzF,QAAQ,CAAC,GAAG,EAAE,CAAC;SACf;aAAM;YACN,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACpB;KACD;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;QAC7D,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;KAClB;IACD,IAAI,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;QAC1B,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;KAChB;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAnBD,sCAmBC;AAED,SAAgB,QAAQ,CAAC,GAAQ,EAAE,GAAG,KAAe;IACpD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACzB,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;KAC/B;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACjD,CAAC;AAND,4BAMC"}

View file

@ -0,0 +1,31 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/naming-convention": [
"warn",
{
"selector": "typeLike",
"format": [
"PascalCase"
]
}
],
"@typescript-eslint/semi": "warn",
"curly": "warn",
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off",
"no-unused-expressions": "warn",
"no-duplicate-imports": "warn",
"new-parens": "warn"
}
}

View file

@ -0,0 +1,4 @@
{
"ui": "tdd",
"color": true
}

View file

@ -0,0 +1,56 @@
2.3.0 2020-07-03
==================
* new API `ModificationOptions.isArrayInsertion`: If `JSONPath` refers to an index of an array and `isArrayInsertion` is `true`, then `modify` will insert a new item at that location instead of overwriting its contents.
* `ModificationOptions.formattingOptions` is now optional. If not set, newly inserted content will not be formatted.
2.2.0 2019-10-25
==================
* added `ParseOptions.allowEmptyContent`. Default is `false`.
* new API `getNodeType`: Returns the type of a value returned by parse.
* `parse`: Fix issue with empty property name
2.1.0 2019-03-29
==================
* `JSONScanner` and `JSONVisitor` return lineNumber / character.
2.0.0 2018-04-12
==================
* renamed `Node.columnOffset` to `Node.colonOffset`
* new API `getNodePath`: Gets the JSON path of the given JSON DOM node
* new API `findNodeAtOffset`: Finds the most inner node at the given offset. If `includeRightBound` is set, also finds nodes that end at the given offset.
1.0.3 2018-03-07
==================
* provide ems modules
1.0.2 2018-03-05
==================
* added the `visit.onComment` API, reported when comments are allowed.
* added the `ParseErrorCode.InvalidCommentToken` enum value, reported when comments are disallowed.
1.0.1
==================
* added the `format` API: computes edits to format a JSON document.
* added the `modify` API: computes edits to insert, remove or replace a property or value in a JSON document.
* added the `allyEdits` API: applies edits to a document
1.0.0
==================
* remove nls dependency (remove `getParseErrorMessage`)
0.4.2 / 2017-05-05
==================
* added `ParseError.offset` & `ParseError.length`
0.4.1 / 2017-04-02
==================
* added `ParseOptions.allowTrailingComma`
0.4.0 / 2017-02-23
==================
* fix for `getLocation`. Now `getLocation` inside an object will always return a property from inside that property. Can be empty string if the object has no properties or if the offset is before a actual property `{ "a": { | }} will return location ['a', ' ']`
0.3.0 / 2017-01-17
==================
* Updating to typescript 2.0

View file

@ -0,0 +1,21 @@
The 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.

View file

@ -0,0 +1,309 @@
# jsonc-parser
Scanner and parser for JSON with comments.
[![npm Package](https://img.shields.io/npm/v/jsonc-parser.svg?style=flat-square)](https://www.npmjs.org/package/jsonc-parser)
[![NPM Downloads](https://img.shields.io/npm/dm/jsonc-parser.svg)](https://npmjs.org/package/jsonc-parser)
[![Build Status](https://travis-ci.org/Microsoft/node-jsonc-parser.svg?branch=master)](https://travis-ci.org/Microsoft/node-jsonc-parser)
Why?
----
JSONC is JSON with JavaScript style comments. This node module provides a scanner and fault tolerant parser that can process JSONC but is also useful for standard JSON.
- the *scanner* tokenizes the input string into tokens and token offsets
- the *visit* function implements a 'SAX' style parser with callbacks for the encountered properties and values.
- the *parseTree* function computes a hierarchical DOM with offsets representing the encountered properties and values.
- the *parse* function evaluates the JavaScript object represented by JSON string in a fault tolerant fashion.
- the *getLocation* API returns a location object that describes the property or value located at a given offset in a JSON document.
- the *findNodeAtLocation* API finds the node at a given location path in a JSON DOM.
- the *format* API computes edits to format a JSON document.
- the *modify* API computes edits to insert, remove or replace a property or value in a JSON document.
- the *applyEdits* API applies edits to a document.
Installation
------------
npm install --save jsonc-parser
API
---
### Scanner:
```typescript
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
export function createScanner(text:string, ignoreTrivia:boolean = false):JSONScanner;
/**
* The scanner object, representing a JSON scanner at a position in the input string.
*/
export interface JSONScanner {
/**
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
*/
setPosition(pos: number): any;
/**
* Read the next token. Returns the token code.
*/
scan(): SyntaxKind;
/**
* Returns the current scan position, which is after the last read token.
*/
getPosition(): number;
/**
* Returns the last read token.
*/
getToken(): SyntaxKind;
/**
* Returns the last read token value. The value for strings is the decoded string content. For numbers its of type number, for boolean it's true or false.
*/
getTokenValue(): string;
/**
* The start offset of the last read token.
*/
getTokenOffset(): number;
/**
* The length of the last read token.
*/
getTokenLength(): number;
/**
* The zero-based start line number of the last read token.
*/
getTokenStartLine(): number;
/**
* The zero-based start character (column) of the last read token.
*/
getTokenStartCharacter(): number;
/**
* An error code of the last scan.
*/
getTokenError(): ScanError;
}
```
### Parser:
```typescript
export interface ParseOptions {
disallowComments?: boolean;
}
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore always check the errors list to find out if the input was valid.
*/
export declare function parse(text: string, errors?: {error: ParseErrorCode;}[], options?: ParseOptions): any;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
export declare function visit(text: string, visitor: JSONVisitor, options?: ParseOptions): any;
export interface JSONVisitor {
/**
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
*/
onObjectBegin?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a property is encountered. The offset and length represent the location of the property name.
*/
onObjectProperty?: (property: string, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
*/
onObjectEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
*/
onArrayBegin?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
*/
onArrayEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
*/
onLiteralValue?: (value: any, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
*/
onSeparator?: (character: string, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
*/
onComment?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked on an error.
*/
onError?: (error: ParseErrorCode, offset: number, length: number, startLine: number, startCharacter: number) => void;
}
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
export declare function parseTree(text: string, errors?: ParseError[], options?: ParseOptions): Node;
export declare type NodeType = "object" | "array" | "property" | "string" | "number" | "boolean" | "null";
export interface Node {
type: NodeType;
value?: any;
offset: number;
length: number;
colonOffset?: number;
parent?: Node;
children?: Node[];
}
```
### Utilities:
```typescript
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
export declare function stripComments(text: string, replaceCh?: string): string;
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
export declare function getLocation(text: string, position: number): Location;
export declare type Segment = string | number;
export interface Location {
/**
* The previous property key or literal value (string, number, boolean or null) or undefined.
*/
previousNode?: Node;
/**
* The path describing the location in the JSON document. The path consists of a sequence strings
* representing an object property or numbers for array indices.
*/
path: Segment[];
/**
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
* '*' will match a single segment, of any property name or index.
* '**' will match a sequece of segments or no segment, of any property name or index.
*/
matches: (patterns: Segment[]) => boolean;
/**
* If set, the location's offset is at a property key.
*/
isAtPropertyKey: boolean;
}
/**
* Finds the node at the given path in a JSON DOM.
*/
export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined;
/**
* Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
export function findNodeAtOffset(root: Node, offset: number, includeRightBound?: boolean) : Node | undefined;
/**
* Gets the JSON path of the given JSON DOM node
*/
export function getNodePath(node: Node) : JSONPath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
export function getNodeValue(node: Node): any;
/**
* Computes the edits needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
* text in the original document. However, multiple edits can have
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
* To apply edits to an input, you can use `applyEdits`
*/
export function format(documentText: string, range: Range, options: FormattingOptions): Edit[];
/**
* Computes the edits needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
* text in the original document. However, multiple edits can have
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
* To apply edits to an input, you can use `applyEdits`
*/
export function modify(text: string, path: JSONPath, value: any, options: ModificationOptions): Edit[];
/**
* Applies edits to a input string.
*/
export function applyEdits(text: string, edits: Edit[]): string;
/**
* Represents a text modification
*/
export interface Edit {
/**
* The start offset of the modification.
*/
offset: number;
/**
* The length of the modification. Must not be negative. Empty length represents an *insert*.
*/
length: number;
/**
* The new content. Empty content represents a *remove*.
*/
content: string;
}
/**
* A text range in the document
*/
export interface Range {
/**
* The start offset of the range.
*/
offset: number;
/**
* The length of the range. Must not be negative.
*/
length: number;
}
export interface FormattingOptions {
/**
* If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent?
*/
tabSize: number;
/**
* Is indentation based on spaces?
*/
insertSpaces: boolean;
/**
* The default 'end of line' character
*/
eol: string;
}
```
License
-------
(MIT License)
Copyright 2018, Microsoft

View file

@ -0,0 +1,186 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { format, isEOL } from './format';
import { parseTree, findNodeAtLocation } from './parser';
export function removeProperty(text, path, options) {
return setProperty(text, path, void 0, options);
}
export function setProperty(text, originalPath, value, options) {
var _a;
var path = originalPath.slice();
var errors = [];
var root = parseTree(text, errors);
var parent = void 0;
var lastSegment = void 0;
while (path.length > 0) {
lastSegment = path.pop();
parent = findNodeAtLocation(root, path);
if (parent === void 0 && value !== void 0) {
if (typeof lastSegment === 'string') {
value = (_a = {}, _a[lastSegment] = value, _a);
}
else {
value = [value];
}
}
else {
break;
}
}
if (!parent) {
// empty document
if (value === void 0) { // delete
throw new Error('Can not delete in empty document');
}
return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, options);
}
else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) {
var existing = findNodeAtLocation(parent, [lastSegment]);
if (existing !== void 0) {
if (value === void 0) { // delete
if (!existing.parent) {
throw new Error('Malformed AST');
}
var propertyIndex = parent.children.indexOf(existing.parent);
var removeBegin = void 0;
var removeEnd = existing.parent.offset + existing.parent.length;
if (propertyIndex > 0) {
// remove the comma of the previous node
var previous = parent.children[propertyIndex - 1];
removeBegin = previous.offset + previous.length;
}
else {
removeBegin = parent.offset + 1;
if (parent.children.length > 1) {
// remove the comma of the next node
var next = parent.children[1];
removeEnd = next.offset;
}
}
return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: '' }, options);
}
else {
// set value of existing property
return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, options);
}
}
else {
if (value === void 0) { // delete
return []; // property does not exist, nothing to do
}
var newProperty = JSON.stringify(lastSegment) + ": " + JSON.stringify(value);
var index = options.getInsertionIndex ? options.getInsertionIndex(parent.children.map(function (p) { return p.children[0].value; })) : parent.children.length;
var edit = void 0;
if (index > 0) {
var previous = parent.children[index - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
else if (parent.children.length === 0) {
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
}
else {
edit = { offset: parent.offset + 1, length: 0, content: newProperty + ',' };
}
return withFormatting(text, edit, options);
}
}
else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) {
var insertIndex = lastSegment;
if (insertIndex === -1) {
// Insert
var newProperty = "" + JSON.stringify(value);
var edit = void 0;
if (parent.children.length === 0) {
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
}
else {
var previous = parent.children[parent.children.length - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
return withFormatting(text, edit, options);
}
else if (value === void 0 && parent.children.length >= 0) {
// Removal
var removalIndex = lastSegment;
var toRemove = parent.children[removalIndex];
var edit = void 0;
if (parent.children.length === 1) {
// only item
edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' };
}
else if (parent.children.length - 1 === removalIndex) {
// last item
var previous = parent.children[removalIndex - 1];
var offset = previous.offset + previous.length;
var parentEndOffset = parent.offset + parent.length;
edit = { offset: offset, length: parentEndOffset - 2 - offset, content: '' };
}
else {
edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' };
}
return withFormatting(text, edit, options);
}
else if (value !== void 0) {
var edit = void 0;
var newProperty = "" + JSON.stringify(value);
if (!options.isArrayInsertion && parent.children.length > lastSegment) {
var toModify = parent.children[lastSegment];
edit = { offset: toModify.offset, length: toModify.length, content: newProperty };
}
else if (parent.children.length === 0 || lastSegment === 0) {
edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + ',' };
}
else {
var index = lastSegment > parent.children.length ? parent.children.length : lastSegment;
var previous = parent.children[index - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
return withFormatting(text, edit, options);
}
else {
throw new Error("Can not " + (value === void 0 ? 'remove' : (options.isArrayInsertion ? 'insert' : 'modify')) + " Array index " + insertIndex + " as length is not sufficient");
}
}
else {
throw new Error("Can not add " + (typeof lastSegment !== 'number' ? 'index' : 'property') + " to parent of type " + parent.type);
}
}
function withFormatting(text, edit, options) {
if (!options.formattingOptions) {
return [edit];
}
// apply the edit
var newText = applyEdit(text, edit);
// format the new text
var begin = edit.offset;
var end = edit.offset + edit.content.length;
if (edit.length === 0 || edit.content.length === 0) { // insert or remove
while (begin > 0 && !isEOL(newText, begin - 1)) {
begin--;
}
while (end < newText.length && !isEOL(newText, end)) {
end++;
}
}
var edits = format(newText, { offset: begin, length: end - begin }, options.formattingOptions);
// apply the formatting edits and track the begin and end offsets of the changes
for (var i = edits.length - 1; i >= 0; i--) {
var edit_1 = edits[i];
newText = applyEdit(newText, edit_1);
begin = Math.min(begin, edit_1.offset);
end = Math.max(end, edit_1.offset + edit_1.length);
end += edit_1.content.length - edit_1.length;
}
// create a single edit with all changes
var editLength = text.length - (newText.length - end) - begin;
return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
}
export function applyEdit(text, edit) {
return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
}
export function isWS(text, offset) {
return '\r\n \t'.indexOf(text.charAt(offset)) !== -1;
}

View file

@ -0,0 +1,194 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createScanner } from './scanner';
export function format(documentText, range, options) {
var initialIndentLevel;
var formatText;
var formatTextStart;
var rangeStart;
var rangeEnd;
if (range) {
rangeStart = range.offset;
rangeEnd = rangeStart + range.length;
formatTextStart = rangeStart;
while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) {
formatTextStart--;
}
var endOffset = rangeEnd;
while (endOffset < documentText.length && !isEOL(documentText, endOffset)) {
endOffset++;
}
formatText = documentText.substring(formatTextStart, endOffset);
initialIndentLevel = computeIndentLevel(formatText, options);
}
else {
formatText = documentText;
initialIndentLevel = 0;
formatTextStart = 0;
rangeStart = 0;
rangeEnd = documentText.length;
}
var eol = getEOL(options, documentText);
var lineBreak = false;
var indentLevel = 0;
var indentValue;
if (options.insertSpaces) {
indentValue = repeat(' ', options.tabSize || 4);
}
else {
indentValue = '\t';
}
var scanner = createScanner(formatText, false);
var hasError = false;
function newLineAndIndent() {
return eol + repeat(indentValue, initialIndentLevel + indentLevel);
}
function scanNext() {
var token = scanner.scan();
lineBreak = false;
while (token === 15 /* Trivia */ || token === 14 /* LineBreakTrivia */) {
lineBreak = lineBreak || (token === 14 /* LineBreakTrivia */);
token = scanner.scan();
}
hasError = token === 16 /* Unknown */ || scanner.getTokenError() !== 0 /* None */;
return token;
}
var editOperations = [];
function addEdit(text, startOffset, endOffset) {
if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) {
editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
}
}
var firstToken = scanNext();
if (firstToken !== 17 /* EOF */) {
var firstTokenStart = scanner.getTokenOffset() + formatTextStart;
var initialIndent = repeat(indentValue, initialIndentLevel);
addEdit(initialIndent, formatTextStart, firstTokenStart);
}
while (firstToken !== 17 /* EOF */) {
var firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
var secondToken = scanNext();
var replaceContent = '';
while (!lineBreak && (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */)) {
// comments on the same line: keep them on the same line, but ignore them otherwise
var commentTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(' ', firstTokenEnd, commentTokenStart);
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
replaceContent = secondToken === 12 /* LineCommentTrivia */ ? newLineAndIndent() : '';
secondToken = scanNext();
}
if (secondToken === 2 /* CloseBraceToken */) {
if (firstToken !== 1 /* OpenBraceToken */) {
indentLevel--;
replaceContent = newLineAndIndent();
}
}
else if (secondToken === 4 /* CloseBracketToken */) {
if (firstToken !== 3 /* OpenBracketToken */) {
indentLevel--;
replaceContent = newLineAndIndent();
}
}
else {
switch (firstToken) {
case 3 /* OpenBracketToken */:
case 1 /* OpenBraceToken */:
indentLevel++;
replaceContent = newLineAndIndent();
break;
case 5 /* CommaToken */:
case 12 /* LineCommentTrivia */:
replaceContent = newLineAndIndent();
break;
case 13 /* BlockCommentTrivia */:
if (lineBreak) {
replaceContent = newLineAndIndent();
}
else {
// symbol following comment on the same line: keep on same line, separate with ' '
replaceContent = ' ';
}
break;
case 6 /* ColonToken */:
replaceContent = ' ';
break;
case 10 /* StringLiteral */:
if (secondToken === 6 /* ColonToken */) {
replaceContent = '';
break;
}
// fall through
case 7 /* NullKeyword */:
case 8 /* TrueKeyword */:
case 9 /* FalseKeyword */:
case 11 /* NumericLiteral */:
case 2 /* CloseBraceToken */:
case 4 /* CloseBracketToken */:
if (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */) {
replaceContent = ' ';
}
else if (secondToken !== 5 /* CommaToken */ && secondToken !== 17 /* EOF */) {
hasError = true;
}
break;
case 16 /* Unknown */:
hasError = true;
break;
}
if (lineBreak && (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */)) {
replaceContent = newLineAndIndent();
}
}
var secondTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(replaceContent, firstTokenEnd, secondTokenStart);
firstToken = secondToken;
}
return editOperations;
}
function repeat(s, count) {
var result = '';
for (var i = 0; i < count; i++) {
result += s;
}
return result;
}
function computeIndentLevel(content, options) {
var i = 0;
var nChars = 0;
var tabSize = options.tabSize || 4;
while (i < content.length) {
var ch = content.charAt(i);
if (ch === ' ') {
nChars++;
}
else if (ch === '\t') {
nChars += tabSize;
}
else {
break;
}
i++;
}
return Math.floor(nChars / tabSize);
}
function getEOL(options, text) {
for (var i = 0; i < text.length; i++) {
var ch = text.charAt(i);
if (ch === '\r') {
if (i + 1 < text.length && text.charAt(i + 1) === '\n') {
return '\r\n';
}
return '\r';
}
else if (ch === '\n') {
return '\n';
}
}
return (options && options.eol) || '\n';
}
export function isEOL(text, offset) {
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
}

View file

@ -0,0 +1,621 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createScanner } from './scanner';
var ParseOptions;
(function (ParseOptions) {
ParseOptions.DEFAULT = {
allowTrailingComma: false
};
})(ParseOptions || (ParseOptions = {}));
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
export function getLocation(text, position) {
var segments = []; // strings or numbers
var earlyReturnException = new Object();
var previousNode = undefined;
var previousNodeInst = {
value: {},
offset: 0,
length: 0,
type: 'object',
parent: undefined
};
var isAtPropertyKey = false;
function setPreviousNode(value, offset, length, type) {
previousNodeInst.value = value;
previousNodeInst.offset = offset;
previousNodeInst.length = length;
previousNodeInst.type = type;
previousNodeInst.colonOffset = undefined;
previousNode = previousNodeInst;
}
try {
visit(text, {
onObjectBegin: function (offset, length) {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
isAtPropertyKey = position > offset;
segments.push(''); // push a placeholder (will be replaced)
},
onObjectProperty: function (name, offset, length) {
if (position < offset) {
throw earlyReturnException;
}
setPreviousNode(name, offset, length, 'property');
segments[segments.length - 1] = name;
if (position <= offset + length) {
throw earlyReturnException;
}
},
onObjectEnd: function (offset, length) {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.pop();
},
onArrayBegin: function (offset, length) {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.push(0);
},
onArrayEnd: function (offset, length) {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.pop();
},
onLiteralValue: function (value, offset, length) {
if (position < offset) {
throw earlyReturnException;
}
setPreviousNode(value, offset, length, getNodeType(value));
if (position <= offset + length) {
throw earlyReturnException;
}
},
onSeparator: function (sep, offset, length) {
if (position <= offset) {
throw earlyReturnException;
}
if (sep === ':' && previousNode && previousNode.type === 'property') {
previousNode.colonOffset = offset;
isAtPropertyKey = false;
previousNode = undefined;
}
else if (sep === ',') {
var last = segments[segments.length - 1];
if (typeof last === 'number') {
segments[segments.length - 1] = last + 1;
}
else {
isAtPropertyKey = true;
segments[segments.length - 1] = '';
}
previousNode = undefined;
}
}
});
}
catch (e) {
if (e !== earlyReturnException) {
throw e;
}
}
return {
path: segments,
previousNode: previousNode,
isAtPropertyKey: isAtPropertyKey,
matches: function (pattern) {
var k = 0;
for (var i = 0; k < pattern.length && i < segments.length; i++) {
if (pattern[k] === segments[i] || pattern[k] === '*') {
k++;
}
else if (pattern[k] !== '**') {
return false;
}
}
return k === pattern.length;
}
};
}
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore always check the errors list to find out if the input was valid.
*/
export function parse(text, errors, options) {
if (errors === void 0) { errors = []; }
if (options === void 0) { options = ParseOptions.DEFAULT; }
var currentProperty = null;
var currentParent = [];
var previousParents = [];
function onValue(value) {
if (Array.isArray(currentParent)) {
currentParent.push(value);
}
else if (currentProperty !== null) {
currentParent[currentProperty] = value;
}
}
var visitor = {
onObjectBegin: function () {
var object = {};
onValue(object);
previousParents.push(currentParent);
currentParent = object;
currentProperty = null;
},
onObjectProperty: function (name) {
currentProperty = name;
},
onObjectEnd: function () {
currentParent = previousParents.pop();
},
onArrayBegin: function () {
var array = [];
onValue(array);
previousParents.push(currentParent);
currentParent = array;
currentProperty = null;
},
onArrayEnd: function () {
currentParent = previousParents.pop();
},
onLiteralValue: onValue,
onError: function (error, offset, length) {
errors.push({ error: error, offset: offset, length: length });
}
};
visit(text, visitor, options);
return currentParent[0];
}
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
export function parseTree(text, errors, options) {
if (errors === void 0) { errors = []; }
if (options === void 0) { options = ParseOptions.DEFAULT; }
var currentParent = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root
function ensurePropertyComplete(endOffset) {
if (currentParent.type === 'property') {
currentParent.length = endOffset - currentParent.offset;
currentParent = currentParent.parent;
}
}
function onValue(valueNode) {
currentParent.children.push(valueNode);
return valueNode;
}
var visitor = {
onObjectBegin: function (offset) {
currentParent = onValue({ type: 'object', offset: offset, length: -1, parent: currentParent, children: [] });
},
onObjectProperty: function (name, offset, length) {
currentParent = onValue({ type: 'property', offset: offset, length: -1, parent: currentParent, children: [] });
currentParent.children.push({ type: 'string', value: name, offset: offset, length: length, parent: currentParent });
},
onObjectEnd: function (offset, length) {
ensurePropertyComplete(offset + length); // in case of a missing value for a property: make sure property is complete
currentParent.length = offset + length - currentParent.offset;
currentParent = currentParent.parent;
ensurePropertyComplete(offset + length);
},
onArrayBegin: function (offset, length) {
currentParent = onValue({ type: 'array', offset: offset, length: -1, parent: currentParent, children: [] });
},
onArrayEnd: function (offset, length) {
currentParent.length = offset + length - currentParent.offset;
currentParent = currentParent.parent;
ensurePropertyComplete(offset + length);
},
onLiteralValue: function (value, offset, length) {
onValue({ type: getNodeType(value), offset: offset, length: length, parent: currentParent, value: value });
ensurePropertyComplete(offset + length);
},
onSeparator: function (sep, offset, length) {
if (currentParent.type === 'property') {
if (sep === ':') {
currentParent.colonOffset = offset;
}
else if (sep === ',') {
ensurePropertyComplete(offset);
}
}
},
onError: function (error, offset, length) {
errors.push({ error: error, offset: offset, length: length });
}
};
visit(text, visitor, options);
var result = currentParent.children[0];
if (result) {
delete result.parent;
}
return result;
}
/**
* Finds the node at the given path in a JSON DOM.
*/
export function findNodeAtLocation(root, path) {
if (!root) {
return undefined;
}
var node = root;
for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {
var segment = path_1[_i];
if (typeof segment === 'string') {
if (node.type !== 'object' || !Array.isArray(node.children)) {
return undefined;
}
var found = false;
for (var _a = 0, _b = node.children; _a < _b.length; _a++) {
var propertyNode = _b[_a];
if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment) {
node = propertyNode.children[1];
found = true;
break;
}
}
if (!found) {
return undefined;
}
}
else {
var index = segment;
if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
return undefined;
}
node = node.children[index];
}
}
return node;
}
/**
* Gets the JSON path of the given JSON DOM node
*/
export function getNodePath(node) {
if (!node.parent || !node.parent.children) {
return [];
}
var path = getNodePath(node.parent);
if (node.parent.type === 'property') {
var key = node.parent.children[0].value;
path.push(key);
}
else if (node.parent.type === 'array') {
var index = node.parent.children.indexOf(node);
if (index !== -1) {
path.push(index);
}
}
return path;
}
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
export function getNodeValue(node) {
switch (node.type) {
case 'array':
return node.children.map(getNodeValue);
case 'object':
var obj = Object.create(null);
for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
var prop = _a[_i];
var valueNode = prop.children[1];
if (valueNode) {
obj[prop.children[0].value] = getNodeValue(valueNode);
}
}
return obj;
case 'null':
case 'string':
case 'number':
case 'boolean':
return node.value;
default:
return undefined;
}
}
export function contains(node, offset, includeRightBound) {
if (includeRightBound === void 0) { includeRightBound = false; }
return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length));
}
/**
* Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
export function findNodeAtOffset(node, offset, includeRightBound) {
if (includeRightBound === void 0) { includeRightBound = false; }
if (contains(node, offset, includeRightBound)) {
var children = node.children;
if (Array.isArray(children)) {
for (var i = 0; i < children.length && children[i].offset <= offset; i++) {
var item = findNodeAtOffset(children[i], offset, includeRightBound);
if (item) {
return item;
}
}
}
return node;
}
return undefined;
}
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
export function visit(text, visitor, options) {
if (options === void 0) { options = ParseOptions.DEFAULT; }
var _scanner = createScanner(text, false);
function toNoArgVisit(visitFunction) {
return visitFunction ? function () { return visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()); } : function () { return true; };
}
function toOneArgVisit(visitFunction) {
return visitFunction ? function (arg) { return visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()); } : function () { return true; };
}
var onObjectBegin = toNoArgVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisit(visitor.onObjectProperty), onObjectEnd = toNoArgVisit(visitor.onObjectEnd), onArrayBegin = toNoArgVisit(visitor.onArrayBegin), onArrayEnd = toNoArgVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisit(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
var disallowComments = options && options.disallowComments;
var allowTrailingComma = options && options.allowTrailingComma;
function scanNext() {
while (true) {
var token = _scanner.scan();
switch (_scanner.getTokenError()) {
case 4 /* InvalidUnicode */:
handleError(14 /* InvalidUnicode */);
break;
case 5 /* InvalidEscapeCharacter */:
handleError(15 /* InvalidEscapeCharacter */);
break;
case 3 /* UnexpectedEndOfNumber */:
handleError(13 /* UnexpectedEndOfNumber */);
break;
case 1 /* UnexpectedEndOfComment */:
if (!disallowComments) {
handleError(11 /* UnexpectedEndOfComment */);
}
break;
case 2 /* UnexpectedEndOfString */:
handleError(12 /* UnexpectedEndOfString */);
break;
case 6 /* InvalidCharacter */:
handleError(16 /* InvalidCharacter */);
break;
}
switch (token) {
case 12 /* LineCommentTrivia */:
case 13 /* BlockCommentTrivia */:
if (disallowComments) {
handleError(10 /* InvalidCommentToken */);
}
else {
onComment();
}
break;
case 16 /* Unknown */:
handleError(1 /* InvalidSymbol */);
break;
case 15 /* Trivia */:
case 14 /* LineBreakTrivia */:
break;
default:
return token;
}
}
}
function handleError(error, skipUntilAfter, skipUntil) {
if (skipUntilAfter === void 0) { skipUntilAfter = []; }
if (skipUntil === void 0) { skipUntil = []; }
onError(error);
if (skipUntilAfter.length + skipUntil.length > 0) {
var token = _scanner.getToken();
while (token !== 17 /* EOF */) {
if (skipUntilAfter.indexOf(token) !== -1) {
scanNext();
break;
}
else if (skipUntil.indexOf(token) !== -1) {
break;
}
token = scanNext();
}
}
}
function parseString(isValue) {
var value = _scanner.getTokenValue();
if (isValue) {
onLiteralValue(value);
}
else {
onObjectProperty(value);
}
scanNext();
return true;
}
function parseLiteral() {
switch (_scanner.getToken()) {
case 11 /* NumericLiteral */:
var tokenValue = _scanner.getTokenValue();
var value = Number(tokenValue);
if (isNaN(value)) {
handleError(2 /* InvalidNumberFormat */);
value = 0;
}
onLiteralValue(value);
break;
case 7 /* NullKeyword */:
onLiteralValue(null);
break;
case 8 /* TrueKeyword */:
onLiteralValue(true);
break;
case 9 /* FalseKeyword */:
onLiteralValue(false);
break;
default:
return false;
}
scanNext();
return true;
}
function parseProperty() {
if (_scanner.getToken() !== 10 /* StringLiteral */) {
handleError(3 /* PropertyNameExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
return false;
}
parseString(false);
if (_scanner.getToken() === 6 /* ColonToken */) {
onSeparator(':');
scanNext(); // consume colon
if (!parseValue()) {
handleError(4 /* ValueExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
}
}
else {
handleError(5 /* ColonExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
}
return true;
}
function parseObject() {
onObjectBegin();
scanNext(); // consume open brace
var needsComma = false;
while (_scanner.getToken() !== 2 /* CloseBraceToken */ && _scanner.getToken() !== 17 /* EOF */) {
if (_scanner.getToken() === 5 /* CommaToken */) {
if (!needsComma) {
handleError(4 /* ValueExpected */, [], []);
}
onSeparator(',');
scanNext(); // consume comma
if (_scanner.getToken() === 2 /* CloseBraceToken */ && allowTrailingComma) {
break;
}
}
else if (needsComma) {
handleError(6 /* CommaExpected */, [], []);
}
if (!parseProperty()) {
handleError(4 /* ValueExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
}
needsComma = true;
}
onObjectEnd();
if (_scanner.getToken() !== 2 /* CloseBraceToken */) {
handleError(7 /* CloseBraceExpected */, [2 /* CloseBraceToken */], []);
}
else {
scanNext(); // consume close brace
}
return true;
}
function parseArray() {
onArrayBegin();
scanNext(); // consume open bracket
var needsComma = false;
while (_scanner.getToken() !== 4 /* CloseBracketToken */ && _scanner.getToken() !== 17 /* EOF */) {
if (_scanner.getToken() === 5 /* CommaToken */) {
if (!needsComma) {
handleError(4 /* ValueExpected */, [], []);
}
onSeparator(',');
scanNext(); // consume comma
if (_scanner.getToken() === 4 /* CloseBracketToken */ && allowTrailingComma) {
break;
}
}
else if (needsComma) {
handleError(6 /* CommaExpected */, [], []);
}
if (!parseValue()) {
handleError(4 /* ValueExpected */, [], [4 /* CloseBracketToken */, 5 /* CommaToken */]);
}
needsComma = true;
}
onArrayEnd();
if (_scanner.getToken() !== 4 /* CloseBracketToken */) {
handleError(8 /* CloseBracketExpected */, [4 /* CloseBracketToken */], []);
}
else {
scanNext(); // consume close bracket
}
return true;
}
function parseValue() {
switch (_scanner.getToken()) {
case 3 /* OpenBracketToken */:
return parseArray();
case 1 /* OpenBraceToken */:
return parseObject();
case 10 /* StringLiteral */:
return parseString(true);
default:
return parseLiteral();
}
}
scanNext();
if (_scanner.getToken() === 17 /* EOF */) {
if (options.allowEmptyContent) {
return true;
}
handleError(4 /* ValueExpected */, [], []);
return false;
}
if (!parseValue()) {
handleError(4 /* ValueExpected */, [], []);
return false;
}
if (_scanner.getToken() !== 17 /* EOF */) {
handleError(9 /* EndOfFileExpected */, [], []);
}
return true;
}
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
export function stripComments(text, replaceCh) {
var _scanner = createScanner(text), parts = [], kind, offset = 0, pos;
do {
pos = _scanner.getPosition();
kind = _scanner.scan();
switch (kind) {
case 12 /* LineCommentTrivia */:
case 13 /* BlockCommentTrivia */:
case 17 /* EOF */:
if (offset !== pos) {
parts.push(text.substring(offset, pos));
}
if (replaceCh !== undefined) {
parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh));
}
offset = _scanner.getPosition();
break;
}
} while (kind !== 17 /* EOF */);
return parts.join('');
}
export function getNodeType(value) {
switch (typeof value) {
case 'boolean': return 'boolean';
case 'number': return 'number';
case 'string': return 'string';
case 'object': {
if (!value) {
return 'null';
}
else if (Array.isArray(value)) {
return 'array';
}
return 'object';
}
default: return 'null';
}
}

View file

@ -0,0 +1,363 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
export function createScanner(text, ignoreTrivia) {
if (ignoreTrivia === void 0) { ignoreTrivia = false; }
var len = text.length;
var pos = 0, value = '', tokenOffset = 0, token = 16 /* Unknown */, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0 /* None */;
function scanHexDigits(count, exact) {
var digits = 0;
var value = 0;
while (digits < count || !exact) {
var ch = text.charCodeAt(pos);
if (ch >= 48 /* _0 */ && ch <= 57 /* _9 */) {
value = value * 16 + ch - 48 /* _0 */;
}
else if (ch >= 65 /* A */ && ch <= 70 /* F */) {
value = value * 16 + ch - 65 /* A */ + 10;
}
else if (ch >= 97 /* a */ && ch <= 102 /* f */) {
value = value * 16 + ch - 97 /* a */ + 10;
}
else {
break;
}
pos++;
digits++;
}
if (digits < count) {
value = -1;
}
return value;
}
function setPosition(newPosition) {
pos = newPosition;
value = '';
tokenOffset = 0;
token = 16 /* Unknown */;
scanError = 0 /* None */;
}
function scanNumber() {
var start = pos;
if (text.charCodeAt(pos) === 48 /* _0 */) {
pos++;
}
else {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
}
if (pos < text.length && text.charCodeAt(pos) === 46 /* dot */) {
pos++;
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
}
else {
scanError = 3 /* UnexpectedEndOfNumber */;
return text.substring(start, pos);
}
}
var end = pos;
if (pos < text.length && (text.charCodeAt(pos) === 69 /* E */ || text.charCodeAt(pos) === 101 /* e */)) {
pos++;
if (pos < text.length && text.charCodeAt(pos) === 43 /* plus */ || text.charCodeAt(pos) === 45 /* minus */) {
pos++;
}
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
end = pos;
}
else {
scanError = 3 /* UnexpectedEndOfNumber */;
}
}
return text.substring(start, end);
}
function scanString() {
var result = '', start = pos;
while (true) {
if (pos >= len) {
result += text.substring(start, pos);
scanError = 2 /* UnexpectedEndOfString */;
break;
}
var ch = text.charCodeAt(pos);
if (ch === 34 /* doubleQuote */) {
result += text.substring(start, pos);
pos++;
break;
}
if (ch === 92 /* backslash */) {
result += text.substring(start, pos);
pos++;
if (pos >= len) {
scanError = 2 /* UnexpectedEndOfString */;
break;
}
var ch2 = text.charCodeAt(pos++);
switch (ch2) {
case 34 /* doubleQuote */:
result += '\"';
break;
case 92 /* backslash */:
result += '\\';
break;
case 47 /* slash */:
result += '/';
break;
case 98 /* b */:
result += '\b';
break;
case 102 /* f */:
result += '\f';
break;
case 110 /* n */:
result += '\n';
break;
case 114 /* r */:
result += '\r';
break;
case 116 /* t */:
result += '\t';
break;
case 117 /* u */:
var ch3 = scanHexDigits(4, true);
if (ch3 >= 0) {
result += String.fromCharCode(ch3);
}
else {
scanError = 4 /* InvalidUnicode */;
}
break;
default:
scanError = 5 /* InvalidEscapeCharacter */;
}
start = pos;
continue;
}
if (ch >= 0 && ch <= 0x1f) {
if (isLineBreak(ch)) {
result += text.substring(start, pos);
scanError = 2 /* UnexpectedEndOfString */;
break;
}
else {
scanError = 6 /* InvalidCharacter */;
// mark as error but continue with string
}
}
pos++;
}
return result;
}
function scanNext() {
value = '';
scanError = 0 /* None */;
tokenOffset = pos;
lineStartOffset = lineNumber;
prevTokenLineStartOffset = tokenLineStartOffset;
if (pos >= len) {
// at the end
tokenOffset = len;
return token = 17 /* EOF */;
}
var code = text.charCodeAt(pos);
// trivia: whitespace
if (isWhiteSpace(code)) {
do {
pos++;
value += String.fromCharCode(code);
code = text.charCodeAt(pos);
} while (isWhiteSpace(code));
return token = 15 /* Trivia */;
}
// trivia: newlines
if (isLineBreak(code)) {
pos++;
value += String.fromCharCode(code);
if (code === 13 /* carriageReturn */ && text.charCodeAt(pos) === 10 /* lineFeed */) {
pos++;
value += '\n';
}
lineNumber++;
tokenLineStartOffset = pos;
return token = 14 /* LineBreakTrivia */;
}
switch (code) {
// tokens: []{}:,
case 123 /* openBrace */:
pos++;
return token = 1 /* OpenBraceToken */;
case 125 /* closeBrace */:
pos++;
return token = 2 /* CloseBraceToken */;
case 91 /* openBracket */:
pos++;
return token = 3 /* OpenBracketToken */;
case 93 /* closeBracket */:
pos++;
return token = 4 /* CloseBracketToken */;
case 58 /* colon */:
pos++;
return token = 6 /* ColonToken */;
case 44 /* comma */:
pos++;
return token = 5 /* CommaToken */;
// strings
case 34 /* doubleQuote */:
pos++;
value = scanString();
return token = 10 /* StringLiteral */;
// comments
case 47 /* slash */:
var start = pos - 1;
// Single-line comment
if (text.charCodeAt(pos + 1) === 47 /* slash */) {
pos += 2;
while (pos < len) {
if (isLineBreak(text.charCodeAt(pos))) {
break;
}
pos++;
}
value = text.substring(start, pos);
return token = 12 /* LineCommentTrivia */;
}
// Multi-line comment
if (text.charCodeAt(pos + 1) === 42 /* asterisk */) {
pos += 2;
var safeLength = len - 1; // For lookahead.
var commentClosed = false;
while (pos < safeLength) {
var ch = text.charCodeAt(pos);
if (ch === 42 /* asterisk */ && text.charCodeAt(pos + 1) === 47 /* slash */) {
pos += 2;
commentClosed = true;
break;
}
pos++;
if (isLineBreak(ch)) {
if (ch === 13 /* carriageReturn */ && text.charCodeAt(pos) === 10 /* lineFeed */) {
pos++;
}
lineNumber++;
tokenLineStartOffset = pos;
}
}
if (!commentClosed) {
pos++;
scanError = 1 /* UnexpectedEndOfComment */;
}
value = text.substring(start, pos);
return token = 13 /* BlockCommentTrivia */;
}
// just a single slash
value += String.fromCharCode(code);
pos++;
return token = 16 /* Unknown */;
// numbers
case 45 /* minus */:
value += String.fromCharCode(code);
pos++;
if (pos === len || !isDigit(text.charCodeAt(pos))) {
return token = 16 /* Unknown */;
}
// found a minus, followed by a number so
// we fall through to proceed with scanning
// numbers
case 48 /* _0 */:
case 49 /* _1 */:
case 50 /* _2 */:
case 51 /* _3 */:
case 52 /* _4 */:
case 53 /* _5 */:
case 54 /* _6 */:
case 55 /* _7 */:
case 56 /* _8 */:
case 57 /* _9 */:
value += scanNumber();
return token = 11 /* NumericLiteral */;
// literals and unknown symbols
default:
// is a literal? Read the full word.
while (pos < len && isUnknownContentCharacter(code)) {
pos++;
code = text.charCodeAt(pos);
}
if (tokenOffset !== pos) {
value = text.substring(tokenOffset, pos);
// keywords: true, false, null
switch (value) {
case 'true': return token = 8 /* TrueKeyword */;
case 'false': return token = 9 /* FalseKeyword */;
case 'null': return token = 7 /* NullKeyword */;
}
return token = 16 /* Unknown */;
}
// some
value += String.fromCharCode(code);
pos++;
return token = 16 /* Unknown */;
}
}
function isUnknownContentCharacter(code) {
if (isWhiteSpace(code) || isLineBreak(code)) {
return false;
}
switch (code) {
case 125 /* closeBrace */:
case 93 /* closeBracket */:
case 123 /* openBrace */:
case 91 /* openBracket */:
case 34 /* doubleQuote */:
case 58 /* colon */:
case 44 /* comma */:
case 47 /* slash */:
return false;
}
return true;
}
function scanNextNonTrivia() {
var result;
do {
result = scanNext();
} while (result >= 12 /* LineCommentTrivia */ && result <= 15 /* Trivia */);
return result;
}
return {
setPosition: setPosition,
getPosition: function () { return pos; },
scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
getToken: function () { return token; },
getTokenValue: function () { return value; },
getTokenOffset: function () { return tokenOffset; },
getTokenLength: function () { return pos - tokenOffset; },
getTokenStartLine: function () { return lineStartOffset; },
getTokenStartCharacter: function () { return tokenOffset - prevTokenLineStartOffset; },
getTokenError: function () { return scanError; },
};
}
function isWhiteSpace(ch) {
return ch === 32 /* space */ || ch === 9 /* tab */ || ch === 11 /* verticalTab */ || ch === 12 /* formFeed */ ||
ch === 160 /* nonBreakingSpace */ || ch === 5760 /* ogham */ || ch >= 8192 /* enQuad */ && ch <= 8203 /* zeroWidthSpace */ ||
ch === 8239 /* narrowNoBreakSpace */ || ch === 8287 /* mathematicalSpace */ || ch === 12288 /* ideographicSpace */ || ch === 65279 /* byteOrderMark */;
}
function isLineBreak(ch) {
return ch === 10 /* lineFeed */ || ch === 13 /* carriageReturn */ || ch === 8232 /* lineSeparator */ || ch === 8233 /* paragraphSeparator */;
}
function isDigit(ch) {
return ch >= 48 /* _0 */ && ch <= 57 /* _9 */;
}

View file

@ -0,0 +1,312 @@
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
export declare const createScanner: (text: string, ignoreTrivia?: boolean) => JSONScanner;
export declare const enum ScanError {
None = 0,
UnexpectedEndOfComment = 1,
UnexpectedEndOfString = 2,
UnexpectedEndOfNumber = 3,
InvalidUnicode = 4,
InvalidEscapeCharacter = 5,
InvalidCharacter = 6
}
export declare const enum SyntaxKind {
OpenBraceToken = 1,
CloseBraceToken = 2,
OpenBracketToken = 3,
CloseBracketToken = 4,
CommaToken = 5,
ColonToken = 6,
NullKeyword = 7,
TrueKeyword = 8,
FalseKeyword = 9,
StringLiteral = 10,
NumericLiteral = 11,
LineCommentTrivia = 12,
BlockCommentTrivia = 13,
LineBreakTrivia = 14,
Trivia = 15,
Unknown = 16,
EOF = 17
}
/**
* The scanner object, representing a JSON scanner at a position in the input string.
*/
export interface JSONScanner {
/**
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
*/
setPosition(pos: number): void;
/**
* Read the next token. Returns the token code.
*/
scan(): SyntaxKind;
/**
* Returns the current scan position, which is after the last read token.
*/
getPosition(): number;
/**
* Returns the last read token.
*/
getToken(): SyntaxKind;
/**
* Returns the last read token value. The value for strings is the decoded string content. For numbers it's of type number, for boolean it's true or false.
*/
getTokenValue(): string;
/**
* The start offset of the last read token.
*/
getTokenOffset(): number;
/**
* The length of the last read token.
*/
getTokenLength(): number;
/**
* The zero-based start line number of the last read token.
*/
getTokenStartLine(): number;
/**
* The zero-based start character (column) of the last read token.
*/
getTokenStartCharacter(): number;
/**
* An error code of the last scan.
*/
getTokenError(): ScanError;
}
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
export declare const getLocation: (text: string, position: number) => Location;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore, always check the errors list to find out if the input was valid.
*/
export declare const parse: (text: string, errors?: ParseError[], options?: ParseOptions) => any;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
export declare const parseTree: (text: string, errors?: ParseError[], options?: ParseOptions) => Node;
/**
* Finds the node at the given path in a JSON DOM.
*/
export declare const findNodeAtLocation: (root: Node, path: JSONPath) => Node | undefined;
/**
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
export declare const findNodeAtOffset: (root: Node, offset: number, includeRightBound?: boolean) => Node | undefined;
/**
* Gets the JSON path of the given JSON DOM node
*/
export declare const getNodePath: (node: Node) => JSONPath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
export declare const getNodeValue: (node: Node) => any;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
export declare const visit: (text: string, visitor: JSONVisitor, options?: ParseOptions) => any;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
export declare const stripComments: (text: string, replaceCh?: string) => string;
export interface ParseError {
error: ParseErrorCode;
offset: number;
length: number;
}
export declare const enum ParseErrorCode {
InvalidSymbol = 1,
InvalidNumberFormat = 2,
PropertyNameExpected = 3,
ValueExpected = 4,
ColonExpected = 5,
CommaExpected = 6,
CloseBraceExpected = 7,
CloseBracketExpected = 8,
EndOfFileExpected = 9,
InvalidCommentToken = 10,
UnexpectedEndOfComment = 11,
UnexpectedEndOfString = 12,
UnexpectedEndOfNumber = 13,
InvalidUnicode = 14,
InvalidEscapeCharacter = 15,
InvalidCharacter = 16
}
export declare function printParseErrorCode(code: ParseErrorCode): "InvalidSymbol" | "InvalidNumberFormat" | "PropertyNameExpected" | "ValueExpected" | "ColonExpected" | "CommaExpected" | "CloseBraceExpected" | "CloseBracketExpected" | "EndOfFileExpected" | "InvalidCommentToken" | "UnexpectedEndOfComment" | "UnexpectedEndOfString" | "UnexpectedEndOfNumber" | "InvalidUnicode" | "InvalidEscapeCharacter" | "InvalidCharacter" | "<unknown ParseErrorCode>";
export declare type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null';
export interface Node {
readonly type: NodeType;
readonly value?: any;
readonly offset: number;
readonly length: number;
readonly colonOffset?: number;
readonly parent?: Node;
readonly children?: Node[];
}
export declare type Segment = string | number;
export declare type JSONPath = Segment[];
export interface Location {
/**
* The previous property key or literal value (string, number, boolean or null) or undefined.
*/
previousNode?: Node;
/**
* The path describing the location in the JSON document. The path consists of a sequence of strings
* representing an object property or numbers for array indices.
*/
path: JSONPath;
/**
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
* '*' will match a single segment of any property name or index.
* '**' will match a sequence of segments of any property name or index, or no segment.
*/
matches: (patterns: JSONPath) => boolean;
/**
* If set, the location's offset is at a property key.
*/
isAtPropertyKey: boolean;
}
export interface ParseOptions {
disallowComments?: boolean;
allowTrailingComma?: boolean;
allowEmptyContent?: boolean;
}
export interface JSONVisitor {
/**
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
*/
onObjectBegin?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a property is encountered. The offset and length represent the location of the property name.
*/
onObjectProperty?: (property: string, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
*/
onObjectEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
*/
onArrayBegin?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
*/
onArrayEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
*/
onLiteralValue?: (value: any, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
*/
onSeparator?: (character: string, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
*/
onComment?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked on an error.
*/
onError?: (error: ParseErrorCode, offset: number, length: number, startLine: number, startCharacter: number) => void;
}
/**
* Represents a text modification
*/
export interface Edit {
/**
* The start offset of the modification.
*/
offset: number;
/**
* The length of the modification. Must not be negative. Empty length represents an *insert*.
*/
length: number;
/**
* The new content. Empty content represents a *remove*.
*/
content: string;
}
/**
* A text range in the document
*/
export interface Range {
/**
* The start offset of the range.
*/
offset: number;
/**
* The length of the range. Must not be negative.
*/
length: number;
}
export interface FormattingOptions {
/**
* If indentation is based on spaces (`insertSpaces` = true), the number of spaces that make an indent.
*/
tabSize?: number;
/**
* Is indentation based on spaces?
*/
insertSpaces?: boolean;
/**
* The default 'end of line' character. If not set, '\n' is used as default.
*/
eol?: string;
}
/**
* Computes the edits needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
* text in the original document. However, multiple edits can have
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
* To apply edits to an input, you can use `applyEdits`.
*/
export declare function format(documentText: string, range: Range | undefined, options: FormattingOptions): Edit[];
/**
* Options used when computing the modification edits
*/
export interface ModificationOptions {
/**
* Formatting options. If undefined, the newly inserted code will be inserted unformatted.
*/
formattingOptions?: FormattingOptions;
/**
* Default false. If `JSONPath` refers to an index of an array and {@property isArrayInsertion} is `true`, then
* {@function modify} will insert a new item at that location instead of overwriting its contents.
*/
isArrayInsertion?: boolean;
/**
* Optional function to define the insertion index given an existing list of properties.
*/
getInsertionIndex?: (properties: string[]) => number;
}
/**
* Computes the edits needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
* text in the original document. However, multiple edits can have
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
* To apply edits to an input, you can use `applyEdits`.
*/
export declare function modify(text: string, path: JSONPath, value: any, options: ModificationOptions): Edit[];
/**
* Applies edits to a input string.
*/
export declare function applyEdits(text: string, edits: Edit[]): string;

View file

@ -0,0 +1,116 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as formatter from './impl/format';
import * as edit from './impl/edit';
import * as scanner from './impl/scanner';
import * as parser from './impl/parser';
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
export var createScanner = scanner.createScanner;
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
export var getLocation = parser.getLocation;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore, always check the errors list to find out if the input was valid.
*/
export var parse = parser.parse;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
export var parseTree = parser.parseTree;
/**
* Finds the node at the given path in a JSON DOM.
*/
export var findNodeAtLocation = parser.findNodeAtLocation;
/**
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
export var findNodeAtOffset = parser.findNodeAtOffset;
/**
* Gets the JSON path of the given JSON DOM node
*/
export var getNodePath = parser.getNodePath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
export var getNodeValue = parser.getNodeValue;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
export var visit = parser.visit;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
export var stripComments = parser.stripComments;
export function printParseErrorCode(code) {
switch (code) {
case 1 /* InvalidSymbol */: return 'InvalidSymbol';
case 2 /* InvalidNumberFormat */: return 'InvalidNumberFormat';
case 3 /* PropertyNameExpected */: return 'PropertyNameExpected';
case 4 /* ValueExpected */: return 'ValueExpected';
case 5 /* ColonExpected */: return 'ColonExpected';
case 6 /* CommaExpected */: return 'CommaExpected';
case 7 /* CloseBraceExpected */: return 'CloseBraceExpected';
case 8 /* CloseBracketExpected */: return 'CloseBracketExpected';
case 9 /* EndOfFileExpected */: return 'EndOfFileExpected';
case 10 /* InvalidCommentToken */: return 'InvalidCommentToken';
case 11 /* UnexpectedEndOfComment */: return 'UnexpectedEndOfComment';
case 12 /* UnexpectedEndOfString */: return 'UnexpectedEndOfString';
case 13 /* UnexpectedEndOfNumber */: return 'UnexpectedEndOfNumber';
case 14 /* InvalidUnicode */: return 'InvalidUnicode';
case 15 /* InvalidEscapeCharacter */: return 'InvalidEscapeCharacter';
case 16 /* InvalidCharacter */: return 'InvalidCharacter';
}
return '<unknown ParseErrorCode>';
}
/**
* Computes the edits needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
* text in the original document. However, multiple edits can have
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
* To apply edits to an input, you can use `applyEdits`.
*/
export function format(documentText, range, options) {
return formatter.format(documentText, range, options);
}
/**
* Computes the edits needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
* text in the original document. However, multiple edits can have
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
* To apply edits to an input, you can use `applyEdits`.
*/
export function modify(text, path, value, options) {
return edit.setProperty(text, path, value, options);
}
/**
* Applies edits to a input string.
*/
export function applyEdits(text, edits) {
for (var i = edits.length - 1; i >= 0; i--) {
text = edit.applyEdit(text, edits[i]);
}
return text;
}

View file

@ -0,0 +1,202 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./format", "./parser"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.isWS = exports.applyEdit = exports.setProperty = exports.removeProperty = void 0;
var format_1 = require("./format");
var parser_1 = require("./parser");
function removeProperty(text, path, options) {
return setProperty(text, path, void 0, options);
}
exports.removeProperty = removeProperty;
function setProperty(text, originalPath, value, options) {
var _a;
var path = originalPath.slice();
var errors = [];
var root = parser_1.parseTree(text, errors);
var parent = void 0;
var lastSegment = void 0;
while (path.length > 0) {
lastSegment = path.pop();
parent = parser_1.findNodeAtLocation(root, path);
if (parent === void 0 && value !== void 0) {
if (typeof lastSegment === 'string') {
value = (_a = {}, _a[lastSegment] = value, _a);
}
else {
value = [value];
}
}
else {
break;
}
}
if (!parent) {
// empty document
if (value === void 0) { // delete
throw new Error('Can not delete in empty document');
}
return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, options);
}
else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) {
var existing = parser_1.findNodeAtLocation(parent, [lastSegment]);
if (existing !== void 0) {
if (value === void 0) { // delete
if (!existing.parent) {
throw new Error('Malformed AST');
}
var propertyIndex = parent.children.indexOf(existing.parent);
var removeBegin = void 0;
var removeEnd = existing.parent.offset + existing.parent.length;
if (propertyIndex > 0) {
// remove the comma of the previous node
var previous = parent.children[propertyIndex - 1];
removeBegin = previous.offset + previous.length;
}
else {
removeBegin = parent.offset + 1;
if (parent.children.length > 1) {
// remove the comma of the next node
var next = parent.children[1];
removeEnd = next.offset;
}
}
return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: '' }, options);
}
else {
// set value of existing property
return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, options);
}
}
else {
if (value === void 0) { // delete
return []; // property does not exist, nothing to do
}
var newProperty = JSON.stringify(lastSegment) + ": " + JSON.stringify(value);
var index = options.getInsertionIndex ? options.getInsertionIndex(parent.children.map(function (p) { return p.children[0].value; })) : parent.children.length;
var edit = void 0;
if (index > 0) {
var previous = parent.children[index - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
else if (parent.children.length === 0) {
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
}
else {
edit = { offset: parent.offset + 1, length: 0, content: newProperty + ',' };
}
return withFormatting(text, edit, options);
}
}
else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) {
var insertIndex = lastSegment;
if (insertIndex === -1) {
// Insert
var newProperty = "" + JSON.stringify(value);
var edit = void 0;
if (parent.children.length === 0) {
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
}
else {
var previous = parent.children[parent.children.length - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
return withFormatting(text, edit, options);
}
else if (value === void 0 && parent.children.length >= 0) {
// Removal
var removalIndex = lastSegment;
var toRemove = parent.children[removalIndex];
var edit = void 0;
if (parent.children.length === 1) {
// only item
edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' };
}
else if (parent.children.length - 1 === removalIndex) {
// last item
var previous = parent.children[removalIndex - 1];
var offset = previous.offset + previous.length;
var parentEndOffset = parent.offset + parent.length;
edit = { offset: offset, length: parentEndOffset - 2 - offset, content: '' };
}
else {
edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' };
}
return withFormatting(text, edit, options);
}
else if (value !== void 0) {
var edit = void 0;
var newProperty = "" + JSON.stringify(value);
if (!options.isArrayInsertion && parent.children.length > lastSegment) {
var toModify = parent.children[lastSegment];
edit = { offset: toModify.offset, length: toModify.length, content: newProperty };
}
else if (parent.children.length === 0 || lastSegment === 0) {
edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + ',' };
}
else {
var index = lastSegment > parent.children.length ? parent.children.length : lastSegment;
var previous = parent.children[index - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
return withFormatting(text, edit, options);
}
else {
throw new Error("Can not " + (value === void 0 ? 'remove' : (options.isArrayInsertion ? 'insert' : 'modify')) + " Array index " + insertIndex + " as length is not sufficient");
}
}
else {
throw new Error("Can not add " + (typeof lastSegment !== 'number' ? 'index' : 'property') + " to parent of type " + parent.type);
}
}
exports.setProperty = setProperty;
function withFormatting(text, edit, options) {
if (!options.formattingOptions) {
return [edit];
}
// apply the edit
var newText = applyEdit(text, edit);
// format the new text
var begin = edit.offset;
var end = edit.offset + edit.content.length;
if (edit.length === 0 || edit.content.length === 0) { // insert or remove
while (begin > 0 && !format_1.isEOL(newText, begin - 1)) {
begin--;
}
while (end < newText.length && !format_1.isEOL(newText, end)) {
end++;
}
}
var edits = format_1.format(newText, { offset: begin, length: end - begin }, options.formattingOptions);
// apply the formatting edits and track the begin and end offsets of the changes
for (var i = edits.length - 1; i >= 0; i--) {
var edit_1 = edits[i];
newText = applyEdit(newText, edit_1);
begin = Math.min(begin, edit_1.offset);
end = Math.max(end, edit_1.offset + edit_1.length);
end += edit_1.content.length - edit_1.length;
}
// create a single edit with all changes
var editLength = text.length - (newText.length - end) - begin;
return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
}
function applyEdit(text, edit) {
return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
}
exports.applyEdit = applyEdit;
function isWS(text, offset) {
return '\r\n \t'.indexOf(text.charAt(offset)) !== -1;
}
exports.isWS = isWS;
});

View file

@ -0,0 +1,208 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./scanner"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEOL = exports.format = void 0;
var scanner_1 = require("./scanner");
function format(documentText, range, options) {
var initialIndentLevel;
var formatText;
var formatTextStart;
var rangeStart;
var rangeEnd;
if (range) {
rangeStart = range.offset;
rangeEnd = rangeStart + range.length;
formatTextStart = rangeStart;
while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) {
formatTextStart--;
}
var endOffset = rangeEnd;
while (endOffset < documentText.length && !isEOL(documentText, endOffset)) {
endOffset++;
}
formatText = documentText.substring(formatTextStart, endOffset);
initialIndentLevel = computeIndentLevel(formatText, options);
}
else {
formatText = documentText;
initialIndentLevel = 0;
formatTextStart = 0;
rangeStart = 0;
rangeEnd = documentText.length;
}
var eol = getEOL(options, documentText);
var lineBreak = false;
var indentLevel = 0;
var indentValue;
if (options.insertSpaces) {
indentValue = repeat(' ', options.tabSize || 4);
}
else {
indentValue = '\t';
}
var scanner = scanner_1.createScanner(formatText, false);
var hasError = false;
function newLineAndIndent() {
return eol + repeat(indentValue, initialIndentLevel + indentLevel);
}
function scanNext() {
var token = scanner.scan();
lineBreak = false;
while (token === 15 /* Trivia */ || token === 14 /* LineBreakTrivia */) {
lineBreak = lineBreak || (token === 14 /* LineBreakTrivia */);
token = scanner.scan();
}
hasError = token === 16 /* Unknown */ || scanner.getTokenError() !== 0 /* None */;
return token;
}
var editOperations = [];
function addEdit(text, startOffset, endOffset) {
if (!hasError && startOffset < rangeEnd && endOffset > rangeStart && documentText.substring(startOffset, endOffset) !== text) {
editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
}
}
var firstToken = scanNext();
if (firstToken !== 17 /* EOF */) {
var firstTokenStart = scanner.getTokenOffset() + formatTextStart;
var initialIndent = repeat(indentValue, initialIndentLevel);
addEdit(initialIndent, formatTextStart, firstTokenStart);
}
while (firstToken !== 17 /* EOF */) {
var firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
var secondToken = scanNext();
var replaceContent = '';
while (!lineBreak && (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */)) {
// comments on the same line: keep them on the same line, but ignore them otherwise
var commentTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(' ', firstTokenEnd, commentTokenStart);
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
replaceContent = secondToken === 12 /* LineCommentTrivia */ ? newLineAndIndent() : '';
secondToken = scanNext();
}
if (secondToken === 2 /* CloseBraceToken */) {
if (firstToken !== 1 /* OpenBraceToken */) {
indentLevel--;
replaceContent = newLineAndIndent();
}
}
else if (secondToken === 4 /* CloseBracketToken */) {
if (firstToken !== 3 /* OpenBracketToken */) {
indentLevel--;
replaceContent = newLineAndIndent();
}
}
else {
switch (firstToken) {
case 3 /* OpenBracketToken */:
case 1 /* OpenBraceToken */:
indentLevel++;
replaceContent = newLineAndIndent();
break;
case 5 /* CommaToken */:
case 12 /* LineCommentTrivia */:
replaceContent = newLineAndIndent();
break;
case 13 /* BlockCommentTrivia */:
if (lineBreak) {
replaceContent = newLineAndIndent();
}
else {
// symbol following comment on the same line: keep on same line, separate with ' '
replaceContent = ' ';
}
break;
case 6 /* ColonToken */:
replaceContent = ' ';
break;
case 10 /* StringLiteral */:
if (secondToken === 6 /* ColonToken */) {
replaceContent = '';
break;
}
// fall through
case 7 /* NullKeyword */:
case 8 /* TrueKeyword */:
case 9 /* FalseKeyword */:
case 11 /* NumericLiteral */:
case 2 /* CloseBraceToken */:
case 4 /* CloseBracketToken */:
if (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */) {
replaceContent = ' ';
}
else if (secondToken !== 5 /* CommaToken */ && secondToken !== 17 /* EOF */) {
hasError = true;
}
break;
case 16 /* Unknown */:
hasError = true;
break;
}
if (lineBreak && (secondToken === 12 /* LineCommentTrivia */ || secondToken === 13 /* BlockCommentTrivia */)) {
replaceContent = newLineAndIndent();
}
}
var secondTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(replaceContent, firstTokenEnd, secondTokenStart);
firstToken = secondToken;
}
return editOperations;
}
exports.format = format;
function repeat(s, count) {
var result = '';
for (var i = 0; i < count; i++) {
result += s;
}
return result;
}
function computeIndentLevel(content, options) {
var i = 0;
var nChars = 0;
var tabSize = options.tabSize || 4;
while (i < content.length) {
var ch = content.charAt(i);
if (ch === ' ') {
nChars++;
}
else if (ch === '\t') {
nChars += tabSize;
}
else {
break;
}
i++;
}
return Math.floor(nChars / tabSize);
}
function getEOL(options, text) {
for (var i = 0; i < text.length; i++) {
var ch = text.charAt(i);
if (ch === '\r') {
if (i + 1 < text.length && text.charAt(i + 1) === '\n') {
return '\r\n';
}
return '\r';
}
else if (ch === '\n') {
return '\n';
}
}
return (options && options.eol) || '\n';
}
function isEOL(text, offset) {
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
}
exports.isEOL = isEOL;
});

View file

@ -0,0 +1,644 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./scanner"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.getNodeType = exports.stripComments = exports.visit = exports.findNodeAtOffset = exports.contains = exports.getNodeValue = exports.getNodePath = exports.findNodeAtLocation = exports.parseTree = exports.parse = exports.getLocation = void 0;
var scanner_1 = require("./scanner");
var ParseOptions;
(function (ParseOptions) {
ParseOptions.DEFAULT = {
allowTrailingComma: false
};
})(ParseOptions || (ParseOptions = {}));
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
function getLocation(text, position) {
var segments = []; // strings or numbers
var earlyReturnException = new Object();
var previousNode = undefined;
var previousNodeInst = {
value: {},
offset: 0,
length: 0,
type: 'object',
parent: undefined
};
var isAtPropertyKey = false;
function setPreviousNode(value, offset, length, type) {
previousNodeInst.value = value;
previousNodeInst.offset = offset;
previousNodeInst.length = length;
previousNodeInst.type = type;
previousNodeInst.colonOffset = undefined;
previousNode = previousNodeInst;
}
try {
visit(text, {
onObjectBegin: function (offset, length) {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
isAtPropertyKey = position > offset;
segments.push(''); // push a placeholder (will be replaced)
},
onObjectProperty: function (name, offset, length) {
if (position < offset) {
throw earlyReturnException;
}
setPreviousNode(name, offset, length, 'property');
segments[segments.length - 1] = name;
if (position <= offset + length) {
throw earlyReturnException;
}
},
onObjectEnd: function (offset, length) {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.pop();
},
onArrayBegin: function (offset, length) {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.push(0);
},
onArrayEnd: function (offset, length) {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.pop();
},
onLiteralValue: function (value, offset, length) {
if (position < offset) {
throw earlyReturnException;
}
setPreviousNode(value, offset, length, getNodeType(value));
if (position <= offset + length) {
throw earlyReturnException;
}
},
onSeparator: function (sep, offset, length) {
if (position <= offset) {
throw earlyReturnException;
}
if (sep === ':' && previousNode && previousNode.type === 'property') {
previousNode.colonOffset = offset;
isAtPropertyKey = false;
previousNode = undefined;
}
else if (sep === ',') {
var last = segments[segments.length - 1];
if (typeof last === 'number') {
segments[segments.length - 1] = last + 1;
}
else {
isAtPropertyKey = true;
segments[segments.length - 1] = '';
}
previousNode = undefined;
}
}
});
}
catch (e) {
if (e !== earlyReturnException) {
throw e;
}
}
return {
path: segments,
previousNode: previousNode,
isAtPropertyKey: isAtPropertyKey,
matches: function (pattern) {
var k = 0;
for (var i = 0; k < pattern.length && i < segments.length; i++) {
if (pattern[k] === segments[i] || pattern[k] === '*') {
k++;
}
else if (pattern[k] !== '**') {
return false;
}
}
return k === pattern.length;
}
};
}
exports.getLocation = getLocation;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore always check the errors list to find out if the input was valid.
*/
function parse(text, errors, options) {
if (errors === void 0) { errors = []; }
if (options === void 0) { options = ParseOptions.DEFAULT; }
var currentProperty = null;
var currentParent = [];
var previousParents = [];
function onValue(value) {
if (Array.isArray(currentParent)) {
currentParent.push(value);
}
else if (currentProperty !== null) {
currentParent[currentProperty] = value;
}
}
var visitor = {
onObjectBegin: function () {
var object = {};
onValue(object);
previousParents.push(currentParent);
currentParent = object;
currentProperty = null;
},
onObjectProperty: function (name) {
currentProperty = name;
},
onObjectEnd: function () {
currentParent = previousParents.pop();
},
onArrayBegin: function () {
var array = [];
onValue(array);
previousParents.push(currentParent);
currentParent = array;
currentProperty = null;
},
onArrayEnd: function () {
currentParent = previousParents.pop();
},
onLiteralValue: onValue,
onError: function (error, offset, length) {
errors.push({ error: error, offset: offset, length: length });
}
};
visit(text, visitor, options);
return currentParent[0];
}
exports.parse = parse;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
function parseTree(text, errors, options) {
if (errors === void 0) { errors = []; }
if (options === void 0) { options = ParseOptions.DEFAULT; }
var currentParent = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root
function ensurePropertyComplete(endOffset) {
if (currentParent.type === 'property') {
currentParent.length = endOffset - currentParent.offset;
currentParent = currentParent.parent;
}
}
function onValue(valueNode) {
currentParent.children.push(valueNode);
return valueNode;
}
var visitor = {
onObjectBegin: function (offset) {
currentParent = onValue({ type: 'object', offset: offset, length: -1, parent: currentParent, children: [] });
},
onObjectProperty: function (name, offset, length) {
currentParent = onValue({ type: 'property', offset: offset, length: -1, parent: currentParent, children: [] });
currentParent.children.push({ type: 'string', value: name, offset: offset, length: length, parent: currentParent });
},
onObjectEnd: function (offset, length) {
ensurePropertyComplete(offset + length); // in case of a missing value for a property: make sure property is complete
currentParent.length = offset + length - currentParent.offset;
currentParent = currentParent.parent;
ensurePropertyComplete(offset + length);
},
onArrayBegin: function (offset, length) {
currentParent = onValue({ type: 'array', offset: offset, length: -1, parent: currentParent, children: [] });
},
onArrayEnd: function (offset, length) {
currentParent.length = offset + length - currentParent.offset;
currentParent = currentParent.parent;
ensurePropertyComplete(offset + length);
},
onLiteralValue: function (value, offset, length) {
onValue({ type: getNodeType(value), offset: offset, length: length, parent: currentParent, value: value });
ensurePropertyComplete(offset + length);
},
onSeparator: function (sep, offset, length) {
if (currentParent.type === 'property') {
if (sep === ':') {
currentParent.colonOffset = offset;
}
else if (sep === ',') {
ensurePropertyComplete(offset);
}
}
},
onError: function (error, offset, length) {
errors.push({ error: error, offset: offset, length: length });
}
};
visit(text, visitor, options);
var result = currentParent.children[0];
if (result) {
delete result.parent;
}
return result;
}
exports.parseTree = parseTree;
/**
* Finds the node at the given path in a JSON DOM.
*/
function findNodeAtLocation(root, path) {
if (!root) {
return undefined;
}
var node = root;
for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {
var segment = path_1[_i];
if (typeof segment === 'string') {
if (node.type !== 'object' || !Array.isArray(node.children)) {
return undefined;
}
var found = false;
for (var _a = 0, _b = node.children; _a < _b.length; _a++) {
var propertyNode = _b[_a];
if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment) {
node = propertyNode.children[1];
found = true;
break;
}
}
if (!found) {
return undefined;
}
}
else {
var index = segment;
if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
return undefined;
}
node = node.children[index];
}
}
return node;
}
exports.findNodeAtLocation = findNodeAtLocation;
/**
* Gets the JSON path of the given JSON DOM node
*/
function getNodePath(node) {
if (!node.parent || !node.parent.children) {
return [];
}
var path = getNodePath(node.parent);
if (node.parent.type === 'property') {
var key = node.parent.children[0].value;
path.push(key);
}
else if (node.parent.type === 'array') {
var index = node.parent.children.indexOf(node);
if (index !== -1) {
path.push(index);
}
}
return path;
}
exports.getNodePath = getNodePath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
function getNodeValue(node) {
switch (node.type) {
case 'array':
return node.children.map(getNodeValue);
case 'object':
var obj = Object.create(null);
for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
var prop = _a[_i];
var valueNode = prop.children[1];
if (valueNode) {
obj[prop.children[0].value] = getNodeValue(valueNode);
}
}
return obj;
case 'null':
case 'string':
case 'number':
case 'boolean':
return node.value;
default:
return undefined;
}
}
exports.getNodeValue = getNodeValue;
function contains(node, offset, includeRightBound) {
if (includeRightBound === void 0) { includeRightBound = false; }
return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length));
}
exports.contains = contains;
/**
* Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
function findNodeAtOffset(node, offset, includeRightBound) {
if (includeRightBound === void 0) { includeRightBound = false; }
if (contains(node, offset, includeRightBound)) {
var children = node.children;
if (Array.isArray(children)) {
for (var i = 0; i < children.length && children[i].offset <= offset; i++) {
var item = findNodeAtOffset(children[i], offset, includeRightBound);
if (item) {
return item;
}
}
}
return node;
}
return undefined;
}
exports.findNodeAtOffset = findNodeAtOffset;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
function visit(text, visitor, options) {
if (options === void 0) { options = ParseOptions.DEFAULT; }
var _scanner = scanner_1.createScanner(text, false);
function toNoArgVisit(visitFunction) {
return visitFunction ? function () { return visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()); } : function () { return true; };
}
function toOneArgVisit(visitFunction) {
return visitFunction ? function (arg) { return visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()); } : function () { return true; };
}
var onObjectBegin = toNoArgVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisit(visitor.onObjectProperty), onObjectEnd = toNoArgVisit(visitor.onObjectEnd), onArrayBegin = toNoArgVisit(visitor.onArrayBegin), onArrayEnd = toNoArgVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisit(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
var disallowComments = options && options.disallowComments;
var allowTrailingComma = options && options.allowTrailingComma;
function scanNext() {
while (true) {
var token = _scanner.scan();
switch (_scanner.getTokenError()) {
case 4 /* InvalidUnicode */:
handleError(14 /* InvalidUnicode */);
break;
case 5 /* InvalidEscapeCharacter */:
handleError(15 /* InvalidEscapeCharacter */);
break;
case 3 /* UnexpectedEndOfNumber */:
handleError(13 /* UnexpectedEndOfNumber */);
break;
case 1 /* UnexpectedEndOfComment */:
if (!disallowComments) {
handleError(11 /* UnexpectedEndOfComment */);
}
break;
case 2 /* UnexpectedEndOfString */:
handleError(12 /* UnexpectedEndOfString */);
break;
case 6 /* InvalidCharacter */:
handleError(16 /* InvalidCharacter */);
break;
}
switch (token) {
case 12 /* LineCommentTrivia */:
case 13 /* BlockCommentTrivia */:
if (disallowComments) {
handleError(10 /* InvalidCommentToken */);
}
else {
onComment();
}
break;
case 16 /* Unknown */:
handleError(1 /* InvalidSymbol */);
break;
case 15 /* Trivia */:
case 14 /* LineBreakTrivia */:
break;
default:
return token;
}
}
}
function handleError(error, skipUntilAfter, skipUntil) {
if (skipUntilAfter === void 0) { skipUntilAfter = []; }
if (skipUntil === void 0) { skipUntil = []; }
onError(error);
if (skipUntilAfter.length + skipUntil.length > 0) {
var token = _scanner.getToken();
while (token !== 17 /* EOF */) {
if (skipUntilAfter.indexOf(token) !== -1) {
scanNext();
break;
}
else if (skipUntil.indexOf(token) !== -1) {
break;
}
token = scanNext();
}
}
}
function parseString(isValue) {
var value = _scanner.getTokenValue();
if (isValue) {
onLiteralValue(value);
}
else {
onObjectProperty(value);
}
scanNext();
return true;
}
function parseLiteral() {
switch (_scanner.getToken()) {
case 11 /* NumericLiteral */:
var tokenValue = _scanner.getTokenValue();
var value = Number(tokenValue);
if (isNaN(value)) {
handleError(2 /* InvalidNumberFormat */);
value = 0;
}
onLiteralValue(value);
break;
case 7 /* NullKeyword */:
onLiteralValue(null);
break;
case 8 /* TrueKeyword */:
onLiteralValue(true);
break;
case 9 /* FalseKeyword */:
onLiteralValue(false);
break;
default:
return false;
}
scanNext();
return true;
}
function parseProperty() {
if (_scanner.getToken() !== 10 /* StringLiteral */) {
handleError(3 /* PropertyNameExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
return false;
}
parseString(false);
if (_scanner.getToken() === 6 /* ColonToken */) {
onSeparator(':');
scanNext(); // consume colon
if (!parseValue()) {
handleError(4 /* ValueExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
}
}
else {
handleError(5 /* ColonExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
}
return true;
}
function parseObject() {
onObjectBegin();
scanNext(); // consume open brace
var needsComma = false;
while (_scanner.getToken() !== 2 /* CloseBraceToken */ && _scanner.getToken() !== 17 /* EOF */) {
if (_scanner.getToken() === 5 /* CommaToken */) {
if (!needsComma) {
handleError(4 /* ValueExpected */, [], []);
}
onSeparator(',');
scanNext(); // consume comma
if (_scanner.getToken() === 2 /* CloseBraceToken */ && allowTrailingComma) {
break;
}
}
else if (needsComma) {
handleError(6 /* CommaExpected */, [], []);
}
if (!parseProperty()) {
handleError(4 /* ValueExpected */, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
}
needsComma = true;
}
onObjectEnd();
if (_scanner.getToken() !== 2 /* CloseBraceToken */) {
handleError(7 /* CloseBraceExpected */, [2 /* CloseBraceToken */], []);
}
else {
scanNext(); // consume close brace
}
return true;
}
function parseArray() {
onArrayBegin();
scanNext(); // consume open bracket
var needsComma = false;
while (_scanner.getToken() !== 4 /* CloseBracketToken */ && _scanner.getToken() !== 17 /* EOF */) {
if (_scanner.getToken() === 5 /* CommaToken */) {
if (!needsComma) {
handleError(4 /* ValueExpected */, [], []);
}
onSeparator(',');
scanNext(); // consume comma
if (_scanner.getToken() === 4 /* CloseBracketToken */ && allowTrailingComma) {
break;
}
}
else if (needsComma) {
handleError(6 /* CommaExpected */, [], []);
}
if (!parseValue()) {
handleError(4 /* ValueExpected */, [], [4 /* CloseBracketToken */, 5 /* CommaToken */]);
}
needsComma = true;
}
onArrayEnd();
if (_scanner.getToken() !== 4 /* CloseBracketToken */) {
handleError(8 /* CloseBracketExpected */, [4 /* CloseBracketToken */], []);
}
else {
scanNext(); // consume close bracket
}
return true;
}
function parseValue() {
switch (_scanner.getToken()) {
case 3 /* OpenBracketToken */:
return parseArray();
case 1 /* OpenBraceToken */:
return parseObject();
case 10 /* StringLiteral */:
return parseString(true);
default:
return parseLiteral();
}
}
scanNext();
if (_scanner.getToken() === 17 /* EOF */) {
if (options.allowEmptyContent) {
return true;
}
handleError(4 /* ValueExpected */, [], []);
return false;
}
if (!parseValue()) {
handleError(4 /* ValueExpected */, [], []);
return false;
}
if (_scanner.getToken() !== 17 /* EOF */) {
handleError(9 /* EndOfFileExpected */, [], []);
}
return true;
}
exports.visit = visit;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
function stripComments(text, replaceCh) {
var _scanner = scanner_1.createScanner(text), parts = [], kind, offset = 0, pos;
do {
pos = _scanner.getPosition();
kind = _scanner.scan();
switch (kind) {
case 12 /* LineCommentTrivia */:
case 13 /* BlockCommentTrivia */:
case 17 /* EOF */:
if (offset !== pos) {
parts.push(text.substring(offset, pos));
}
if (replaceCh !== undefined) {
parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh));
}
offset = _scanner.getPosition();
break;
}
} while (kind !== 17 /* EOF */);
return parts.join('');
}
exports.stripComments = stripComments;
function getNodeType(value) {
switch (typeof value) {
case 'boolean': return 'boolean';
case 'number': return 'number';
case 'string': return 'string';
case 'object': {
if (!value) {
return 'null';
}
else if (Array.isArray(value)) {
return 'array';
}
return 'object';
}
default: return 'null';
}
}
exports.getNodeType = getNodeType;
});

View file

@ -0,0 +1,376 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.createScanner = void 0;
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
function createScanner(text, ignoreTrivia) {
if (ignoreTrivia === void 0) { ignoreTrivia = false; }
var len = text.length;
var pos = 0, value = '', tokenOffset = 0, token = 16 /* Unknown */, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0 /* None */;
function scanHexDigits(count, exact) {
var digits = 0;
var value = 0;
while (digits < count || !exact) {
var ch = text.charCodeAt(pos);
if (ch >= 48 /* _0 */ && ch <= 57 /* _9 */) {
value = value * 16 + ch - 48 /* _0 */;
}
else if (ch >= 65 /* A */ && ch <= 70 /* F */) {
value = value * 16 + ch - 65 /* A */ + 10;
}
else if (ch >= 97 /* a */ && ch <= 102 /* f */) {
value = value * 16 + ch - 97 /* a */ + 10;
}
else {
break;
}
pos++;
digits++;
}
if (digits < count) {
value = -1;
}
return value;
}
function setPosition(newPosition) {
pos = newPosition;
value = '';
tokenOffset = 0;
token = 16 /* Unknown */;
scanError = 0 /* None */;
}
function scanNumber() {
var start = pos;
if (text.charCodeAt(pos) === 48 /* _0 */) {
pos++;
}
else {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
}
if (pos < text.length && text.charCodeAt(pos) === 46 /* dot */) {
pos++;
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
}
else {
scanError = 3 /* UnexpectedEndOfNumber */;
return text.substring(start, pos);
}
}
var end = pos;
if (pos < text.length && (text.charCodeAt(pos) === 69 /* E */ || text.charCodeAt(pos) === 101 /* e */)) {
pos++;
if (pos < text.length && text.charCodeAt(pos) === 43 /* plus */ || text.charCodeAt(pos) === 45 /* minus */) {
pos++;
}
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
end = pos;
}
else {
scanError = 3 /* UnexpectedEndOfNumber */;
}
}
return text.substring(start, end);
}
function scanString() {
var result = '', start = pos;
while (true) {
if (pos >= len) {
result += text.substring(start, pos);
scanError = 2 /* UnexpectedEndOfString */;
break;
}
var ch = text.charCodeAt(pos);
if (ch === 34 /* doubleQuote */) {
result += text.substring(start, pos);
pos++;
break;
}
if (ch === 92 /* backslash */) {
result += text.substring(start, pos);
pos++;
if (pos >= len) {
scanError = 2 /* UnexpectedEndOfString */;
break;
}
var ch2 = text.charCodeAt(pos++);
switch (ch2) {
case 34 /* doubleQuote */:
result += '\"';
break;
case 92 /* backslash */:
result += '\\';
break;
case 47 /* slash */:
result += '/';
break;
case 98 /* b */:
result += '\b';
break;
case 102 /* f */:
result += '\f';
break;
case 110 /* n */:
result += '\n';
break;
case 114 /* r */:
result += '\r';
break;
case 116 /* t */:
result += '\t';
break;
case 117 /* u */:
var ch3 = scanHexDigits(4, true);
if (ch3 >= 0) {
result += String.fromCharCode(ch3);
}
else {
scanError = 4 /* InvalidUnicode */;
}
break;
default:
scanError = 5 /* InvalidEscapeCharacter */;
}
start = pos;
continue;
}
if (ch >= 0 && ch <= 0x1f) {
if (isLineBreak(ch)) {
result += text.substring(start, pos);
scanError = 2 /* UnexpectedEndOfString */;
break;
}
else {
scanError = 6 /* InvalidCharacter */;
// mark as error but continue with string
}
}
pos++;
}
return result;
}
function scanNext() {
value = '';
scanError = 0 /* None */;
tokenOffset = pos;
lineStartOffset = lineNumber;
prevTokenLineStartOffset = tokenLineStartOffset;
if (pos >= len) {
// at the end
tokenOffset = len;
return token = 17 /* EOF */;
}
var code = text.charCodeAt(pos);
// trivia: whitespace
if (isWhiteSpace(code)) {
do {
pos++;
value += String.fromCharCode(code);
code = text.charCodeAt(pos);
} while (isWhiteSpace(code));
return token = 15 /* Trivia */;
}
// trivia: newlines
if (isLineBreak(code)) {
pos++;
value += String.fromCharCode(code);
if (code === 13 /* carriageReturn */ && text.charCodeAt(pos) === 10 /* lineFeed */) {
pos++;
value += '\n';
}
lineNumber++;
tokenLineStartOffset = pos;
return token = 14 /* LineBreakTrivia */;
}
switch (code) {
// tokens: []{}:,
case 123 /* openBrace */:
pos++;
return token = 1 /* OpenBraceToken */;
case 125 /* closeBrace */:
pos++;
return token = 2 /* CloseBraceToken */;
case 91 /* openBracket */:
pos++;
return token = 3 /* OpenBracketToken */;
case 93 /* closeBracket */:
pos++;
return token = 4 /* CloseBracketToken */;
case 58 /* colon */:
pos++;
return token = 6 /* ColonToken */;
case 44 /* comma */:
pos++;
return token = 5 /* CommaToken */;
// strings
case 34 /* doubleQuote */:
pos++;
value = scanString();
return token = 10 /* StringLiteral */;
// comments
case 47 /* slash */:
var start = pos - 1;
// Single-line comment
if (text.charCodeAt(pos + 1) === 47 /* slash */) {
pos += 2;
while (pos < len) {
if (isLineBreak(text.charCodeAt(pos))) {
break;
}
pos++;
}
value = text.substring(start, pos);
return token = 12 /* LineCommentTrivia */;
}
// Multi-line comment
if (text.charCodeAt(pos + 1) === 42 /* asterisk */) {
pos += 2;
var safeLength = len - 1; // For lookahead.
var commentClosed = false;
while (pos < safeLength) {
var ch = text.charCodeAt(pos);
if (ch === 42 /* asterisk */ && text.charCodeAt(pos + 1) === 47 /* slash */) {
pos += 2;
commentClosed = true;
break;
}
pos++;
if (isLineBreak(ch)) {
if (ch === 13 /* carriageReturn */ && text.charCodeAt(pos) === 10 /* lineFeed */) {
pos++;
}
lineNumber++;
tokenLineStartOffset = pos;
}
}
if (!commentClosed) {
pos++;
scanError = 1 /* UnexpectedEndOfComment */;
}
value = text.substring(start, pos);
return token = 13 /* BlockCommentTrivia */;
}
// just a single slash
value += String.fromCharCode(code);
pos++;
return token = 16 /* Unknown */;
// numbers
case 45 /* minus */:
value += String.fromCharCode(code);
pos++;
if (pos === len || !isDigit(text.charCodeAt(pos))) {
return token = 16 /* Unknown */;
}
// found a minus, followed by a number so
// we fall through to proceed with scanning
// numbers
case 48 /* _0 */:
case 49 /* _1 */:
case 50 /* _2 */:
case 51 /* _3 */:
case 52 /* _4 */:
case 53 /* _5 */:
case 54 /* _6 */:
case 55 /* _7 */:
case 56 /* _8 */:
case 57 /* _9 */:
value += scanNumber();
return token = 11 /* NumericLiteral */;
// literals and unknown symbols
default:
// is a literal? Read the full word.
while (pos < len && isUnknownContentCharacter(code)) {
pos++;
code = text.charCodeAt(pos);
}
if (tokenOffset !== pos) {
value = text.substring(tokenOffset, pos);
// keywords: true, false, null
switch (value) {
case 'true': return token = 8 /* TrueKeyword */;
case 'false': return token = 9 /* FalseKeyword */;
case 'null': return token = 7 /* NullKeyword */;
}
return token = 16 /* Unknown */;
}
// some
value += String.fromCharCode(code);
pos++;
return token = 16 /* Unknown */;
}
}
function isUnknownContentCharacter(code) {
if (isWhiteSpace(code) || isLineBreak(code)) {
return false;
}
switch (code) {
case 125 /* closeBrace */:
case 93 /* closeBracket */:
case 123 /* openBrace */:
case 91 /* openBracket */:
case 34 /* doubleQuote */:
case 58 /* colon */:
case 44 /* comma */:
case 47 /* slash */:
return false;
}
return true;
}
function scanNextNonTrivia() {
var result;
do {
result = scanNext();
} while (result >= 12 /* LineCommentTrivia */ && result <= 15 /* Trivia */);
return result;
}
return {
setPosition: setPosition,
getPosition: function () { return pos; },
scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
getToken: function () { return token; },
getTokenValue: function () { return value; },
getTokenOffset: function () { return tokenOffset; },
getTokenLength: function () { return pos - tokenOffset; },
getTokenStartLine: function () { return lineStartOffset; },
getTokenStartCharacter: function () { return tokenOffset - prevTokenLineStartOffset; },
getTokenError: function () { return scanError; },
};
}
exports.createScanner = createScanner;
function isWhiteSpace(ch) {
return ch === 32 /* space */ || ch === 9 /* tab */ || ch === 11 /* verticalTab */ || ch === 12 /* formFeed */ ||
ch === 160 /* nonBreakingSpace */ || ch === 5760 /* ogham */ || ch >= 8192 /* enQuad */ && ch <= 8203 /* zeroWidthSpace */ ||
ch === 8239 /* narrowNoBreakSpace */ || ch === 8287 /* mathematicalSpace */ || ch === 12288 /* ideographicSpace */ || ch === 65279 /* byteOrderMark */;
}
function isLineBreak(ch) {
return ch === 10 /* lineFeed */ || ch === 13 /* carriageReturn */ || ch === 8232 /* lineSeparator */ || ch === 8233 /* paragraphSeparator */;
}
function isDigit(ch) {
return ch >= 48 /* _0 */ && ch <= 57 /* _9 */;
}
});

View file

@ -0,0 +1,312 @@
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
export declare const createScanner: (text: string, ignoreTrivia?: boolean) => JSONScanner;
export declare const enum ScanError {
None = 0,
UnexpectedEndOfComment = 1,
UnexpectedEndOfString = 2,
UnexpectedEndOfNumber = 3,
InvalidUnicode = 4,
InvalidEscapeCharacter = 5,
InvalidCharacter = 6
}
export declare const enum SyntaxKind {
OpenBraceToken = 1,
CloseBraceToken = 2,
OpenBracketToken = 3,
CloseBracketToken = 4,
CommaToken = 5,
ColonToken = 6,
NullKeyword = 7,
TrueKeyword = 8,
FalseKeyword = 9,
StringLiteral = 10,
NumericLiteral = 11,
LineCommentTrivia = 12,
BlockCommentTrivia = 13,
LineBreakTrivia = 14,
Trivia = 15,
Unknown = 16,
EOF = 17
}
/**
* The scanner object, representing a JSON scanner at a position in the input string.
*/
export interface JSONScanner {
/**
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
*/
setPosition(pos: number): void;
/**
* Read the next token. Returns the token code.
*/
scan(): SyntaxKind;
/**
* Returns the current scan position, which is after the last read token.
*/
getPosition(): number;
/**
* Returns the last read token.
*/
getToken(): SyntaxKind;
/**
* Returns the last read token value. The value for strings is the decoded string content. For numbers it's of type number, for boolean it's true or false.
*/
getTokenValue(): string;
/**
* The start offset of the last read token.
*/
getTokenOffset(): number;
/**
* The length of the last read token.
*/
getTokenLength(): number;
/**
* The zero-based start line number of the last read token.
*/
getTokenStartLine(): number;
/**
* The zero-based start character (column) of the last read token.
*/
getTokenStartCharacter(): number;
/**
* An error code of the last scan.
*/
getTokenError(): ScanError;
}
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
export declare const getLocation: (text: string, position: number) => Location;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore, always check the errors list to find out if the input was valid.
*/
export declare const parse: (text: string, errors?: ParseError[], options?: ParseOptions) => any;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
export declare const parseTree: (text: string, errors?: ParseError[], options?: ParseOptions) => Node;
/**
* Finds the node at the given path in a JSON DOM.
*/
export declare const findNodeAtLocation: (root: Node, path: JSONPath) => Node | undefined;
/**
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
export declare const findNodeAtOffset: (root: Node, offset: number, includeRightBound?: boolean) => Node | undefined;
/**
* Gets the JSON path of the given JSON DOM node
*/
export declare const getNodePath: (node: Node) => JSONPath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
export declare const getNodeValue: (node: Node) => any;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
export declare const visit: (text: string, visitor: JSONVisitor, options?: ParseOptions) => any;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
export declare const stripComments: (text: string, replaceCh?: string) => string;
export interface ParseError {
error: ParseErrorCode;
offset: number;
length: number;
}
export declare const enum ParseErrorCode {
InvalidSymbol = 1,
InvalidNumberFormat = 2,
PropertyNameExpected = 3,
ValueExpected = 4,
ColonExpected = 5,
CommaExpected = 6,
CloseBraceExpected = 7,
CloseBracketExpected = 8,
EndOfFileExpected = 9,
InvalidCommentToken = 10,
UnexpectedEndOfComment = 11,
UnexpectedEndOfString = 12,
UnexpectedEndOfNumber = 13,
InvalidUnicode = 14,
InvalidEscapeCharacter = 15,
InvalidCharacter = 16
}
export declare function printParseErrorCode(code: ParseErrorCode): "InvalidSymbol" | "InvalidNumberFormat" | "PropertyNameExpected" | "ValueExpected" | "ColonExpected" | "CommaExpected" | "CloseBraceExpected" | "CloseBracketExpected" | "EndOfFileExpected" | "InvalidCommentToken" | "UnexpectedEndOfComment" | "UnexpectedEndOfString" | "UnexpectedEndOfNumber" | "InvalidUnicode" | "InvalidEscapeCharacter" | "InvalidCharacter" | "<unknown ParseErrorCode>";
export declare type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null';
export interface Node {
readonly type: NodeType;
readonly value?: any;
readonly offset: number;
readonly length: number;
readonly colonOffset?: number;
readonly parent?: Node;
readonly children?: Node[];
}
export declare type Segment = string | number;
export declare type JSONPath = Segment[];
export interface Location {
/**
* The previous property key or literal value (string, number, boolean or null) or undefined.
*/
previousNode?: Node;
/**
* The path describing the location in the JSON document. The path consists of a sequence of strings
* representing an object property or numbers for array indices.
*/
path: JSONPath;
/**
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
* '*' will match a single segment of any property name or index.
* '**' will match a sequence of segments of any property name or index, or no segment.
*/
matches: (patterns: JSONPath) => boolean;
/**
* If set, the location's offset is at a property key.
*/
isAtPropertyKey: boolean;
}
export interface ParseOptions {
disallowComments?: boolean;
allowTrailingComma?: boolean;
allowEmptyContent?: boolean;
}
export interface JSONVisitor {
/**
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
*/
onObjectBegin?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a property is encountered. The offset and length represent the location of the property name.
*/
onObjectProperty?: (property: string, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
*/
onObjectEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
*/
onArrayBegin?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
*/
onArrayEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
*/
onLiteralValue?: (value: any, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
*/
onSeparator?: (character: string, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
*/
onComment?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked on an error.
*/
onError?: (error: ParseErrorCode, offset: number, length: number, startLine: number, startCharacter: number) => void;
}
/**
* Represents a text modification
*/
export interface Edit {
/**
* The start offset of the modification.
*/
offset: number;
/**
* The length of the modification. Must not be negative. Empty length represents an *insert*.
*/
length: number;
/**
* The new content. Empty content represents a *remove*.
*/
content: string;
}
/**
* A text range in the document
*/
export interface Range {
/**
* The start offset of the range.
*/
offset: number;
/**
* The length of the range. Must not be negative.
*/
length: number;
}
export interface FormattingOptions {
/**
* If indentation is based on spaces (`insertSpaces` = true), the number of spaces that make an indent.
*/
tabSize?: number;
/**
* Is indentation based on spaces?
*/
insertSpaces?: boolean;
/**
* The default 'end of line' character. If not set, '\n' is used as default.
*/
eol?: string;
}
/**
* Computes the edits needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
* text in the original document. However, multiple edits can have
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
* To apply edits to an input, you can use `applyEdits`.
*/
export declare function format(documentText: string, range: Range | undefined, options: FormattingOptions): Edit[];
/**
* Options used when computing the modification edits
*/
export interface ModificationOptions {
/**
* Formatting options. If undefined, the newly inserted code will be inserted unformatted.
*/
formattingOptions?: FormattingOptions;
/**
* Default false. If `JSONPath` refers to an index of an array and {@property isArrayInsertion} is `true`, then
* {@function modify} will insert a new item at that location instead of overwriting its contents.
*/
isArrayInsertion?: boolean;
/**
* Optional function to define the insertion index given an existing list of properties.
*/
getInsertionIndex?: (properties: string[]) => number;
}
/**
* Computes the edits needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
* text in the original document. However, multiple edits can have
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
* To apply edits to an input, you can use `applyEdits`.
*/
export declare function modify(text: string, path: JSONPath, value: any, options: ModificationOptions): Edit[];
/**
* Applies edits to a input string.
*/
export declare function applyEdits(text: string, edits: Edit[]): string;

View file

@ -0,0 +1,132 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./impl/format", "./impl/edit", "./impl/scanner", "./impl/parser"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyEdits = exports.modify = exports.format = exports.printParseErrorCode = exports.stripComments = exports.visit = exports.getNodeValue = exports.getNodePath = exports.findNodeAtOffset = exports.findNodeAtLocation = exports.parseTree = exports.parse = exports.getLocation = exports.createScanner = void 0;
var formatter = require("./impl/format");
var edit = require("./impl/edit");
var scanner = require("./impl/scanner");
var parser = require("./impl/parser");
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
exports.createScanner = scanner.createScanner;
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
exports.getLocation = parser.getLocation;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore, always check the errors list to find out if the input was valid.
*/
exports.parse = parser.parse;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
exports.parseTree = parser.parseTree;
/**
* Finds the node at the given path in a JSON DOM.
*/
exports.findNodeAtLocation = parser.findNodeAtLocation;
/**
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
exports.findNodeAtOffset = parser.findNodeAtOffset;
/**
* Gets the JSON path of the given JSON DOM node
*/
exports.getNodePath = parser.getNodePath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
exports.getNodeValue = parser.getNodeValue;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
exports.visit = parser.visit;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
exports.stripComments = parser.stripComments;
function printParseErrorCode(code) {
switch (code) {
case 1 /* InvalidSymbol */: return 'InvalidSymbol';
case 2 /* InvalidNumberFormat */: return 'InvalidNumberFormat';
case 3 /* PropertyNameExpected */: return 'PropertyNameExpected';
case 4 /* ValueExpected */: return 'ValueExpected';
case 5 /* ColonExpected */: return 'ColonExpected';
case 6 /* CommaExpected */: return 'CommaExpected';
case 7 /* CloseBraceExpected */: return 'CloseBraceExpected';
case 8 /* CloseBracketExpected */: return 'CloseBracketExpected';
case 9 /* EndOfFileExpected */: return 'EndOfFileExpected';
case 10 /* InvalidCommentToken */: return 'InvalidCommentToken';
case 11 /* UnexpectedEndOfComment */: return 'UnexpectedEndOfComment';
case 12 /* UnexpectedEndOfString */: return 'UnexpectedEndOfString';
case 13 /* UnexpectedEndOfNumber */: return 'UnexpectedEndOfNumber';
case 14 /* InvalidUnicode */: return 'InvalidUnicode';
case 15 /* InvalidEscapeCharacter */: return 'InvalidEscapeCharacter';
case 16 /* InvalidCharacter */: return 'InvalidCharacter';
}
return '<unknown ParseErrorCode>';
}
exports.printParseErrorCode = printParseErrorCode;
/**
* Computes the edits needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
* text in the original document. However, multiple edits can have
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
* To apply edits to an input, you can use `applyEdits`.
*/
function format(documentText, range, options) {
return formatter.format(documentText, range, options);
}
exports.format = format;
/**
* Computes the edits needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns A list of edit operations describing the formatting changes to the original document. Edits can be either inserts, replacements or
* removals of text segments. All offsets refer to the original state of the document. No two edits must change or remove the same range of
* text in the original document. However, multiple edits can have
* the same offset, for example multiple inserts, or an insert followed by a remove or replace. The order in the array defines which edit is applied first.
* To apply edits to an input, you can use `applyEdits`.
*/
function modify(text, path, value, options) {
return edit.setProperty(text, path, value, options);
}
exports.modify = modify;
/**
* Applies edits to a input string.
*/
function applyEdits(text, edits) {
for (var i = edits.length - 1; i >= 0; i--) {
text = edit.applyEdit(text, edits[i]);
}
return text;
}
exports.applyEdits = applyEdits;
});

View file

@ -0,0 +1,40 @@
{
"name": "jsonc-parser",
"version": "2.3.1",
"description": "Scanner and parser for JSON with comments.",
"main": "./lib/umd/main.js",
"typings": "./lib/umd/main",
"module": "./lib/esm/main.js",
"author": "Microsoft Corporation",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/node-jsonc-parser"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/microsoft/node-jsonc-parser/issues"
},
"devDependencies": {
"mocha": "^8.1.3",
"typescript": "^4.0.2",
"@types/node": "^10.12.12",
"@types/mocha": "^5.2.7",
"@typescript-eslint/eslint-plugin": "^4.1.1",
"@typescript-eslint/parser": "^4.1.1",
"eslint": "^7.9.0",
"rimraf": "^3.0.2"
},
"scripts": {
"prepublishOnly": "npm run clean && npm run compile-esm && npm run test && npm run remove-sourcemap-refs",
"postpublish": "node ./build/post-publish.js",
"compile": "tsc -p ./src && npm run lint",
"compile-esm": "tsc -p ./src/tsconfig.esm.json",
"remove-sourcemap-refs": "node ./build/remove-sourcemap-refs.js",
"clean": "rimraf lib",
"watch": "tsc -w -p ./src",
"test": "npm run compile && mocha ./lib/umd/test",
"lint": "eslint src/**/*.ts",
"preversion": "npm test",
"postversion": "git push && git push --tags"
}
}

View file

@ -0,0 +1,8 @@
language: node_js
sudo: false
node_js:
- "5"
script:
- npm install

View file

@ -0,0 +1,4 @@
// Place your settings in this file to overwrite default and user settings.
{
}

View file

@ -0,0 +1,9 @@
The 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.

View file

@ -0,0 +1,54 @@
## vscode-uri
[![Build Status](https://travis-ci.org/Microsoft/vscode-uri.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-uri)
This module contains the URI implementation that is used by VS Code and its extensions.
It has support for parsing a string into `scheme`, `authority`, `path`, `query`, and
`fragment` URI components as defined in: http://tools.ietf.org/html/rfc3986
```
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
| _____________________|__
/ \ / \
urn:example:animal:ferret:nose
```
## Usage
```js
import { URI } from 'vscode-uri'
// parse an URI from string
let uri = URI.parse('https://code.visualstudio.com/docs/extensions/overview#frag')
assert.ok(uri.scheme === 'https');
assert.ok(uri.authority === 'code.visualstudio.com');
assert.ok(uri.path === '/docs/extensions/overview');
assert.ok(uri.query === '');
assert.ok(uri.fragment === 'frag');
assert.ok(uri.toString() === 'https://code.visualstudio.com/docs/extensions/overview#frag')
// create an URI from a fs path
let uri = URI.file('/users/me/c#-projects/');
assert.ok(uri.scheme === 'file');
assert.ok(uri.authority === '');
assert.ok(uri.path === '/users/me/c#-projects/');
assert.ok(uri.query === '');
assert.ok(uri.fragment === '');
assert.ok(uri.toString() === 'file:///users/me/c%23-projects/')
```
## Contributing
The source of this module is taken straight from the [vscode](https://github.com/Microsoft/vscode)-sources and because of that issues and pull request should be created in that repository. Thanks and Happy Coding!
## Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

View file

@ -0,0 +1,145 @@
/**
* Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
* This class is a simple parser which creates the basic component parts
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
* and encoding.
*
* ```txt
* foo://example.com:8042/over/there?name=ferret#nose
* \_/ \______________/\_________/ \_________/ \__/
* | | | | |
* scheme authority path query fragment
* | _____________________|__
* / \ / \
* urn:example:animal:ferret:nose
* ```
*/
export declare class URI implements UriComponents {
static isUri(thing: any): thing is URI;
/**
* scheme is the 'http' part of 'http://www.msft.com/some/path?query#fragment'.
* The part before the first colon.
*/
readonly scheme: string;
/**
* authority is the 'www.msft.com' part of 'http://www.msft.com/some/path?query#fragment'.
* The part between the first double slashes and the next slash.
*/
readonly authority: string;
/**
* path is the '/some/path' part of 'http://www.msft.com/some/path?query#fragment'.
*/
readonly path: string;
/**
* query is the 'query' part of 'http://www.msft.com/some/path?query#fragment'.
*/
readonly query: string;
/**
* fragment is the 'fragment' part of 'http://www.msft.com/some/path?query#fragment'.
*/
readonly fragment: string;
/**
* @internal
*/
protected constructor(scheme: string, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean);
/**
* @internal
*/
protected constructor(components: UriComponents);
/**
* Returns a string representing the corresponding file system path of this URI.
* Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the
* platform specific path separator.
*
* * Will *not* validate the path for invalid characters and semantics.
* * Will *not* look at the scheme of this URI.
* * The result shall *not* be used for display purposes but for accessing a file on disk.
*
*
* The *difference* to `URI#path` is the use of the platform specific separator and the handling
* of UNC paths. See the below sample of a file-uri with an authority (UNC path).
*
* ```ts
const u = URI.parse('file://server/c$/folder/file.txt')
u.authority === 'server'
u.path === '/shares/c$/file.txt'
u.fsPath === '\\server\c$\folder\file.txt'
```
*
* Using `URI#path` to read a file (using fs-apis) would not be enough because parts of the path,
* namely the server name, would be missing. Therefore `URI#fsPath` exists - it's sugar to ease working
* with URIs that represent files on disk (`file` scheme).
*/
readonly fsPath: string;
with(change: {
scheme?: string;
authority?: string | null;
path?: string | null;
query?: string | null;
fragment?: string | null;
}): URI;
/**
* Creates a new URI from a string, e.g. `http://www.msft.com/some/path`,
* `file:///usr/home`, or `scheme:with/path`.
*
* @param value A string which represents an URI (see `URI#toString`).
*/
static parse(value: string, _strict?: boolean): URI;
/**
* Creates a new URI from a file system path, e.g. `c:\my\files`,
* `/usr/home`, or `\\server\share\some\path`.
*
* The *difference* between `URI#parse` and `URI#file` is that the latter treats the argument
* as path, not as stringified-uri. E.g. `URI.file(path)` is **not the same as**
* `URI.parse('file://' + path)` because the path might contain characters that are
* interpreted (# and ?). See the following sample:
* ```ts
const good = URI.file('/coding/c#/project1');
good.scheme === 'file';
good.path === '/coding/c#/project1';
good.fragment === '';
const bad = URI.parse('file://' + '/coding/c#/project1');
bad.scheme === 'file';
bad.path === '/coding/c'; // path is now broken
bad.fragment === '/project1';
```
*
* @param path A file system path (see `URI#fsPath`)
*/
static file(path: string): URI;
static from(components: {
scheme: string;
authority?: string;
path?: string;
query?: string;
fragment?: string;
}): URI;
/**
* Creates a string representation for this URI. It's guaranteed that calling
* `URI.parse` with the result of this function creates an URI which is equal
* to this URI.
*
* * The result shall *not* be used for display purposes but for externalization or transport.
* * The result will be encoded using the percentage encoding and encoding happens mostly
* ignore the scheme-specific encoding rules.
*
* @param skipEncoding Do not encode the result, default is `false`
*/
toString(skipEncoding?: boolean): string;
toJSON(): UriComponents;
static revive(data: UriComponents | URI): URI;
static revive(data: UriComponents | URI | undefined): URI | undefined;
static revive(data: UriComponents | URI | null): URI | null;
static revive(data: UriComponents | URI | undefined | null): URI | undefined | null;
}
export interface UriComponents {
scheme: string;
authority: string;
path: string;
query: string;
fragment: string;
}
/**
* Compute `fsPath` for the given uri
*/
export declare function uriToFsPath(uri: URI, keepDriveLetterCasing: boolean): string;

View file

@ -0,0 +1,643 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var _a;
var isWindows;
if (typeof process === 'object') {
isWindows = process.platform === 'win32';
}
else if (typeof navigator === 'object') {
var userAgent = navigator.userAgent;
isWindows = userAgent.indexOf('Windows') >= 0;
}
function isHighSurrogate(charCode) {
return (0xD800 <= charCode && charCode <= 0xDBFF);
}
function isLowSurrogate(charCode) {
return (0xDC00 <= charCode && charCode <= 0xDFFF);
}
function isLowerAsciiHex(code) {
return code >= 97 /* a */ && code <= 102 /* f */;
}
function isLowerAsciiLetter(code) {
return code >= 97 /* a */ && code <= 122 /* z */;
}
function isUpperAsciiLetter(code) {
return code >= 65 /* A */ && code <= 90 /* Z */;
}
function isAsciiLetter(code) {
return isLowerAsciiLetter(code) || isUpperAsciiLetter(code);
}
//#endregion
var _schemePattern = /^\w[\w\d+.-]*$/;
var _singleSlashStart = /^\//;
var _doubleSlashStart = /^\/\//;
function _validateUri(ret, _strict) {
// scheme, must be set
if (!ret.scheme && _strict) {
throw new Error("[UriError]: Scheme is missing: {scheme: \"\", authority: \"" + ret.authority + "\", path: \"" + ret.path + "\", query: \"" + ret.query + "\", fragment: \"" + ret.fragment + "\"}");
}
// scheme, https://tools.ietf.org/html/rfc3986#section-3.1
// ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
if (ret.scheme && !_schemePattern.test(ret.scheme)) {
throw new Error('[UriError]: Scheme contains illegal characters.');
}
// path, http://tools.ietf.org/html/rfc3986#section-3.3
// If a URI contains an authority component, then the path component
// must either be empty or begin with a slash ("/") character. If a URI
// does not contain an authority component, then the path cannot begin
// with two slash characters ("//").
if (ret.path) {
if (ret.authority) {
if (!_singleSlashStart.test(ret.path)) {
throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character');
}
}
else {
if (_doubleSlashStart.test(ret.path)) {
throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")');
}
}
}
}
// for a while we allowed uris *without* schemes and this is the migration
// for them, e.g. an uri without scheme and without strict-mode warns and falls
// back to the file-scheme. that should cause the least carnage and still be a
// clear warning
function _schemeFix(scheme, _strict) {
if (!scheme && !_strict) {
return 'file';
}
return scheme;
}
// implements a bit of https://tools.ietf.org/html/rfc3986#section-5
function _referenceResolution(scheme, path) {
// the slash-character is our 'default base' as we don't
// support constructing URIs relative to other URIs. This
// also means that we alter and potentially break paths.
// see https://tools.ietf.org/html/rfc3986#section-5.1.4
switch (scheme) {
case 'https':
case 'http':
case 'file':
if (!path) {
path = _slash;
}
else if (path[0] !== _slash) {
path = _slash + path;
}
break;
}
return path;
}
var _empty = '';
var _slash = '/';
var _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
/**
* Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
* This class is a simple parser which creates the basic component parts
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
* and encoding.
*
* ```txt
* foo://example.com:8042/over/there?name=ferret#nose
* \_/ \______________/\_________/ \_________/ \__/
* | | | | |
* scheme authority path query fragment
* | _____________________|__
* / \ / \
* urn:example:animal:ferret:nose
* ```
*/
var URI = /** @class */ (function () {
/**
* @internal
*/
function URI(schemeOrData, authority, path, query, fragment, _strict) {
if (_strict === void 0) { _strict = false; }
if (typeof schemeOrData === 'object') {
this.scheme = schemeOrData.scheme || _empty;
this.authority = schemeOrData.authority || _empty;
this.path = schemeOrData.path || _empty;
this.query = schemeOrData.query || _empty;
this.fragment = schemeOrData.fragment || _empty;
// no validation because it's this URI
// that creates uri components.
// _validateUri(this);
}
else {
this.scheme = _schemeFix(schemeOrData, _strict);
this.authority = authority || _empty;
this.path = _referenceResolution(this.scheme, path || _empty);
this.query = query || _empty;
this.fragment = fragment || _empty;
_validateUri(this, _strict);
}
}
URI.isUri = function (thing) {
if (thing instanceof URI) {
return true;
}
if (!thing) {
return false;
}
return typeof thing.authority === 'string'
&& typeof thing.fragment === 'string'
&& typeof thing.path === 'string'
&& typeof thing.query === 'string'
&& typeof thing.scheme === 'string'
&& typeof thing.fsPath === 'function'
&& typeof thing.with === 'function'
&& typeof thing.toString === 'function';
};
Object.defineProperty(URI.prototype, "fsPath", {
// ---- filesystem path -----------------------
/**
* Returns a string representing the corresponding file system path of this URI.
* Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the
* platform specific path separator.
*
* * Will *not* validate the path for invalid characters and semantics.
* * Will *not* look at the scheme of this URI.
* * The result shall *not* be used for display purposes but for accessing a file on disk.
*
*
* The *difference* to `URI#path` is the use of the platform specific separator and the handling
* of UNC paths. See the below sample of a file-uri with an authority (UNC path).
*
* ```ts
const u = URI.parse('file://server/c$/folder/file.txt')
u.authority === 'server'
u.path === '/shares/c$/file.txt'
u.fsPath === '\\server\c$\folder\file.txt'
```
*
* Using `URI#path` to read a file (using fs-apis) would not be enough because parts of the path,
* namely the server name, would be missing. Therefore `URI#fsPath` exists - it's sugar to ease working
* with URIs that represent files on disk (`file` scheme).
*/
get: function () {
// if (this.scheme !== 'file') {
// console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`);
// }
return uriToFsPath(this, false);
},
enumerable: true,
configurable: true
});
// ---- modify to new -------------------------
URI.prototype.with = function (change) {
if (!change) {
return this;
}
var scheme = change.scheme, authority = change.authority, path = change.path, query = change.query, fragment = change.fragment;
if (scheme === undefined) {
scheme = this.scheme;
}
else if (scheme === null) {
scheme = _empty;
}
if (authority === undefined) {
authority = this.authority;
}
else if (authority === null) {
authority = _empty;
}
if (path === undefined) {
path = this.path;
}
else if (path === null) {
path = _empty;
}
if (query === undefined) {
query = this.query;
}
else if (query === null) {
query = _empty;
}
if (fragment === undefined) {
fragment = this.fragment;
}
else if (fragment === null) {
fragment = _empty;
}
if (scheme === this.scheme
&& authority === this.authority
&& path === this.path
&& query === this.query
&& fragment === this.fragment) {
return this;
}
return new _URI(scheme, authority, path, query, fragment);
};
// ---- parse & validate ------------------------
/**
* Creates a new URI from a string, e.g. `http://www.msft.com/some/path`,
* `file:///usr/home`, or `scheme:with/path`.
*
* @param value A string which represents an URI (see `URI#toString`).
*/
URI.parse = function (value, _strict) {
if (_strict === void 0) { _strict = false; }
var match = _regexp.exec(value);
if (!match) {
return new _URI(_empty, _empty, _empty, _empty, _empty);
}
return new _URI(match[2] || _empty, percentDecode(match[4] || _empty), percentDecode(match[5] || _empty), percentDecode(match[7] || _empty), percentDecode(match[9] || _empty), _strict);
};
/**
* Creates a new URI from a file system path, e.g. `c:\my\files`,
* `/usr/home`, or `\\server\share\some\path`.
*
* The *difference* between `URI#parse` and `URI#file` is that the latter treats the argument
* as path, not as stringified-uri. E.g. `URI.file(path)` is **not the same as**
* `URI.parse('file://' + path)` because the path might contain characters that are
* interpreted (# and ?). See the following sample:
* ```ts
const good = URI.file('/coding/c#/project1');
good.scheme === 'file';
good.path === '/coding/c#/project1';
good.fragment === '';
const bad = URI.parse('file://' + '/coding/c#/project1');
bad.scheme === 'file';
bad.path === '/coding/c'; // path is now broken
bad.fragment === '/project1';
```
*
* @param path A file system path (see `URI#fsPath`)
*/
URI.file = function (path) {
var authority = _empty;
// normalize to fwd-slashes on windows,
// on other systems bwd-slashes are valid
// filename character, eg /f\oo/ba\r.txt
if (isWindows) {
path = path.replace(/\\/g, _slash);
}
// check for authority as used in UNC shares
// or use the path as given
if (path[0] === _slash && path[1] === _slash) {
var idx = path.indexOf(_slash, 2);
if (idx === -1) {
authority = path.substring(2);
path = _slash;
}
else {
authority = path.substring(2, idx);
path = path.substring(idx) || _slash;
}
}
return new _URI('file', authority, path, _empty, _empty);
};
URI.from = function (components) {
return new _URI(components.scheme, components.authority, components.path, components.query, components.fragment);
};
// /**
// * Join a URI path with path fragments and normalizes the resulting path.
// *
// * @param uri The input URI.
// * @param pathFragment The path fragment to add to the URI path.
// * @returns The resulting URI.
// */
// static joinPath(uri: URI, ...pathFragment: string[]): URI {
// if (!uri.path) {
// throw new Error(`[UriError]: cannot call joinPaths on URI without path`);
// }
// let newPath: string;
// if (isWindows && uri.scheme === 'file') {
// newPath = URI.file(paths.win32.join(uriToFsPath(uri, true), ...pathFragment)).path;
// } else {
// newPath = paths.posix.join(uri.path, ...pathFragment);
// }
// return uri.with({ path: newPath });
// }
// ---- printing/externalize ---------------------------
/**
* Creates a string representation for this URI. It's guaranteed that calling
* `URI.parse` with the result of this function creates an URI which is equal
* to this URI.
*
* * The result shall *not* be used for display purposes but for externalization or transport.
* * The result will be encoded using the percentage encoding and encoding happens mostly
* ignore the scheme-specific encoding rules.
*
* @param skipEncoding Do not encode the result, default is `false`
*/
URI.prototype.toString = function (skipEncoding) {
if (skipEncoding === void 0) { skipEncoding = false; }
return _asFormatted(this, skipEncoding);
};
URI.prototype.toJSON = function () {
return this;
};
URI.revive = function (data) {
if (!data) {
return data;
}
else if (data instanceof URI) {
return data;
}
else {
var result = new _URI(data);
result._formatted = data.external;
result._fsPath = data._sep === _pathSepMarker ? data.fsPath : null;
return result;
}
};
return URI;
}());
export { URI };
var _pathSepMarker = isWindows ? 1 : undefined;
// eslint-disable-next-line @typescript-eslint/class-name-casing
var _URI = /** @class */ (function (_super) {
__extends(_URI, _super);
function _URI() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this._formatted = null;
_this._fsPath = null;
return _this;
}
Object.defineProperty(_URI.prototype, "fsPath", {
get: function () {
if (!this._fsPath) {
this._fsPath = uriToFsPath(this, false);
}
return this._fsPath;
},
enumerable: true,
configurable: true
});
_URI.prototype.toString = function (skipEncoding) {
if (skipEncoding === void 0) { skipEncoding = false; }
if (!skipEncoding) {
if (!this._formatted) {
this._formatted = _asFormatted(this, false);
}
return this._formatted;
}
else {
// we don't cache that
return _asFormatted(this, true);
}
};
_URI.prototype.toJSON = function () {
var res = {
$mid: 1
};
// cached state
if (this._fsPath) {
res.fsPath = this._fsPath;
res._sep = _pathSepMarker;
}
if (this._formatted) {
res.external = this._formatted;
}
// uri components
if (this.path) {
res.path = this.path;
}
if (this.scheme) {
res.scheme = this.scheme;
}
if (this.authority) {
res.authority = this.authority;
}
if (this.query) {
res.query = this.query;
}
if (this.fragment) {
res.fragment = this.fragment;
}
return res;
};
return _URI;
}(URI));
// reserved characters: https://tools.ietf.org/html/rfc3986#section-2.2
var encodeTable = (_a = {},
_a[58 /* Colon */] = '%3A',
_a[47 /* Slash */] = '%2F',
_a[63 /* QuestionMark */] = '%3F',
_a[35 /* Hash */] = '%23',
_a[91 /* OpenSquareBracket */] = '%5B',
_a[93 /* CloseSquareBracket */] = '%5D',
_a[64 /* AtSign */] = '%40',
_a[33 /* ExclamationMark */] = '%21',
_a[36 /* DollarSign */] = '%24',
_a[38 /* Ampersand */] = '%26',
_a[39 /* SingleQuote */] = '%27',
_a[40 /* OpenParen */] = '%28',
_a[41 /* CloseParen */] = '%29',
_a[42 /* Asterisk */] = '%2A',
_a[43 /* Plus */] = '%2B',
_a[44 /* Comma */] = '%2C',
_a[59 /* Semicolon */] = '%3B',
_a[61 /* Equals */] = '%3D',
_a[32 /* Space */] = '%20',
_a);
function encodeURIComponentFast(uriComponent, allowSlash) {
var res = undefined;
var nativeEncodePos = -1;
for (var pos = 0; pos < uriComponent.length; pos++) {
var code = uriComponent.charCodeAt(pos);
// unreserved characters: https://tools.ietf.org/html/rfc3986#section-2.3
if ((code >= 97 /* a */ && code <= 122 /* z */)
|| (code >= 65 /* A */ && code <= 90 /* Z */)
|| (code >= 48 /* Digit0 */ && code <= 57 /* Digit9 */)
|| code === 45 /* Dash */
|| code === 46 /* Period */
|| code === 95 /* Underline */
|| code === 126 /* Tilde */
|| (allowSlash && code === 47 /* Slash */)) {
// check if we are delaying native encode
if (nativeEncodePos !== -1) {
res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos));
nativeEncodePos = -1;
}
// check if we write into a new string (by default we try to return the param)
if (res !== undefined) {
res += uriComponent.charAt(pos);
}
}
else {
// encoding needed, we need to allocate a new string
if (res === undefined) {
res = uriComponent.substr(0, pos);
}
// check with default table first
var escaped = encodeTable[code];
if (escaped !== undefined) {
// check if we are delaying native encode
if (nativeEncodePos !== -1) {
res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos));
nativeEncodePos = -1;
}
// append escaped variant to result
res += escaped;
}
else if (nativeEncodePos === -1) {
// use native encode only when needed
nativeEncodePos = pos;
}
}
}
if (nativeEncodePos !== -1) {
res += encodeURIComponent(uriComponent.substring(nativeEncodePos));
}
return res !== undefined ? res : uriComponent;
}
function encodeURIComponentMinimal(path) {
var res = undefined;
for (var pos = 0; pos < path.length; pos++) {
var code = path.charCodeAt(pos);
if (code === 35 /* Hash */ || code === 63 /* QuestionMark */) {
if (res === undefined) {
res = path.substr(0, pos);
}
res += encodeTable[code];
}
else {
if (res !== undefined) {
res += path[pos];
}
}
}
return res !== undefined ? res : path;
}
/**
* Compute `fsPath` for the given uri
*/
export function uriToFsPath(uri, keepDriveLetterCasing) {
var value;
if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') {
// unc path: file://shares/c$/far/boo
value = "//" + uri.authority + uri.path;
}
else if (uri.path.charCodeAt(0) === 47 /* Slash */
&& (uri.path.charCodeAt(1) >= 65 /* A */ && uri.path.charCodeAt(1) <= 90 /* Z */ || uri.path.charCodeAt(1) >= 97 /* a */ && uri.path.charCodeAt(1) <= 122 /* z */)
&& uri.path.charCodeAt(2) === 58 /* Colon */) {
if (!keepDriveLetterCasing) {
// windows drive letter: file:///c:/far/boo
value = uri.path[1].toLowerCase() + uri.path.substr(2);
}
else {
value = uri.path.substr(1);
}
}
else {
// other path
value = uri.path;
}
if (isWindows) {
value = value.replace(/\//g, '\\');
}
return value;
}
/**
* Create the external version of a uri
*/
function _asFormatted(uri, skipEncoding) {
var encoder = !skipEncoding
? encodeURIComponentFast
: encodeURIComponentMinimal;
var res = '';
var scheme = uri.scheme, authority = uri.authority, path = uri.path, query = uri.query, fragment = uri.fragment;
if (scheme) {
res += scheme;
res += ':';
}
if (authority || scheme === 'file') {
res += _slash;
res += _slash;
}
if (authority) {
var idx = authority.indexOf('@');
if (idx !== -1) {
// <user>@<auth>
var userinfo = authority.substr(0, idx);
authority = authority.substr(idx + 1);
idx = userinfo.indexOf(':');
if (idx === -1) {
res += encoder(userinfo, false);
}
else {
// <user>:<pass>@<auth>
res += encoder(userinfo.substr(0, idx), false);
res += ':';
res += encoder(userinfo.substr(idx + 1), false);
}
res += '@';
}
authority = authority.toLowerCase();
idx = authority.indexOf(':');
if (idx === -1) {
res += encoder(authority, false);
}
else {
// <auth>:<port>
res += encoder(authority.substr(0, idx), false);
res += authority.substr(idx);
}
}
if (path) {
// lower-case windows drive letters in /C:/fff or C:/fff
if (path.length >= 3 && path.charCodeAt(0) === 47 /* Slash */ && path.charCodeAt(2) === 58 /* Colon */) {
var code = path.charCodeAt(1);
if (code >= 65 /* A */ && code <= 90 /* Z */) {
path = "/" + String.fromCharCode(code + 32) + ":" + path.substr(3); // "/c:".length === 3
}
}
else if (path.length >= 2 && path.charCodeAt(1) === 58 /* Colon */) {
var code = path.charCodeAt(0);
if (code >= 65 /* A */ && code <= 90 /* Z */) {
path = String.fromCharCode(code + 32) + ":" + path.substr(2); // "/c:".length === 3
}
}
// encode the rest of the path
res += encoder(path, true);
}
if (query) {
res += '?';
res += encoder(query, false);
}
if (fragment) {
res += '#';
res += !skipEncoding ? encodeURIComponentFast(fragment, false) : fragment;
}
return res;
}
// --- decode
function decodeURIComponentGraceful(str) {
try {
return decodeURIComponent(str);
}
catch (_a) {
if (str.length > 3) {
return str.substr(0, 3) + decodeURIComponentGraceful(str.substr(3));
}
else {
return str;
}
}
}
var _rEncodedAsHex = /(%[0-9A-Za-z][0-9A-Za-z])+/g;
function percentDecode(str) {
if (!str.match(_rEncodedAsHex)) {
return str;
}
return str.replace(_rEncodedAsHex, function (match) { return decodeURIComponentGraceful(match); });
}

View file

@ -0,0 +1,145 @@
/**
* Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
* This class is a simple parser which creates the basic component parts
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
* and encoding.
*
* ```txt
* foo://example.com:8042/over/there?name=ferret#nose
* \_/ \______________/\_________/ \_________/ \__/
* | | | | |
* scheme authority path query fragment
* | _____________________|__
* / \ / \
* urn:example:animal:ferret:nose
* ```
*/
export declare class URI implements UriComponents {
static isUri(thing: any): thing is URI;
/**
* scheme is the 'http' part of 'http://www.msft.com/some/path?query#fragment'.
* The part before the first colon.
*/
readonly scheme: string;
/**
* authority is the 'www.msft.com' part of 'http://www.msft.com/some/path?query#fragment'.
* The part between the first double slashes and the next slash.
*/
readonly authority: string;
/**
* path is the '/some/path' part of 'http://www.msft.com/some/path?query#fragment'.
*/
readonly path: string;
/**
* query is the 'query' part of 'http://www.msft.com/some/path?query#fragment'.
*/
readonly query: string;
/**
* fragment is the 'fragment' part of 'http://www.msft.com/some/path?query#fragment'.
*/
readonly fragment: string;
/**
* @internal
*/
protected constructor(scheme: string, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean);
/**
* @internal
*/
protected constructor(components: UriComponents);
/**
* Returns a string representing the corresponding file system path of this URI.
* Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the
* platform specific path separator.
*
* * Will *not* validate the path for invalid characters and semantics.
* * Will *not* look at the scheme of this URI.
* * The result shall *not* be used for display purposes but for accessing a file on disk.
*
*
* The *difference* to `URI#path` is the use of the platform specific separator and the handling
* of UNC paths. See the below sample of a file-uri with an authority (UNC path).
*
* ```ts
const u = URI.parse('file://server/c$/folder/file.txt')
u.authority === 'server'
u.path === '/shares/c$/file.txt'
u.fsPath === '\\server\c$\folder\file.txt'
```
*
* Using `URI#path` to read a file (using fs-apis) would not be enough because parts of the path,
* namely the server name, would be missing. Therefore `URI#fsPath` exists - it's sugar to ease working
* with URIs that represent files on disk (`file` scheme).
*/
readonly fsPath: string;
with(change: {
scheme?: string;
authority?: string | null;
path?: string | null;
query?: string | null;
fragment?: string | null;
}): URI;
/**
* Creates a new URI from a string, e.g. `http://www.msft.com/some/path`,
* `file:///usr/home`, or `scheme:with/path`.
*
* @param value A string which represents an URI (see `URI#toString`).
*/
static parse(value: string, _strict?: boolean): URI;
/**
* Creates a new URI from a file system path, e.g. `c:\my\files`,
* `/usr/home`, or `\\server\share\some\path`.
*
* The *difference* between `URI#parse` and `URI#file` is that the latter treats the argument
* as path, not as stringified-uri. E.g. `URI.file(path)` is **not the same as**
* `URI.parse('file://' + path)` because the path might contain characters that are
* interpreted (# and ?). See the following sample:
* ```ts
const good = URI.file('/coding/c#/project1');
good.scheme === 'file';
good.path === '/coding/c#/project1';
good.fragment === '';
const bad = URI.parse('file://' + '/coding/c#/project1');
bad.scheme === 'file';
bad.path === '/coding/c'; // path is now broken
bad.fragment === '/project1';
```
*
* @param path A file system path (see `URI#fsPath`)
*/
static file(path: string): URI;
static from(components: {
scheme: string;
authority?: string;
path?: string;
query?: string;
fragment?: string;
}): URI;
/**
* Creates a string representation for this URI. It's guaranteed that calling
* `URI.parse` with the result of this function creates an URI which is equal
* to this URI.
*
* * The result shall *not* be used for display purposes but for externalization or transport.
* * The result will be encoded using the percentage encoding and encoding happens mostly
* ignore the scheme-specific encoding rules.
*
* @param skipEncoding Do not encode the result, default is `false`
*/
toString(skipEncoding?: boolean): string;
toJSON(): UriComponents;
static revive(data: UriComponents | URI): URI;
static revive(data: UriComponents | URI | undefined): URI | undefined;
static revive(data: UriComponents | URI | null): URI | null;
static revive(data: UriComponents | URI | undefined | null): URI | undefined | null;
}
export interface UriComponents {
scheme: string;
authority: string;
path: string;
query: string;
fragment: string;
}
/**
* Compute `fsPath` for the given uri
*/
export declare function uriToFsPath(uri: URI, keepDriveLetterCasing: boolean): string;

View file

@ -0,0 +1,655 @@
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
var isWindows;
if (typeof process === 'object') {
isWindows = process.platform === 'win32';
}
else if (typeof navigator === 'object') {
var userAgent = navigator.userAgent;
isWindows = userAgent.indexOf('Windows') >= 0;
}
function isHighSurrogate(charCode) {
return (0xD800 <= charCode && charCode <= 0xDBFF);
}
function isLowSurrogate(charCode) {
return (0xDC00 <= charCode && charCode <= 0xDFFF);
}
function isLowerAsciiHex(code) {
return code >= 97 /* a */ && code <= 102 /* f */;
}
function isLowerAsciiLetter(code) {
return code >= 97 /* a */ && code <= 122 /* z */;
}
function isUpperAsciiLetter(code) {
return code >= 65 /* A */ && code <= 90 /* Z */;
}
function isAsciiLetter(code) {
return isLowerAsciiLetter(code) || isUpperAsciiLetter(code);
}
//#endregion
var _schemePattern = /^\w[\w\d+.-]*$/;
var _singleSlashStart = /^\//;
var _doubleSlashStart = /^\/\//;
function _validateUri(ret, _strict) {
// scheme, must be set
if (!ret.scheme && _strict) {
throw new Error("[UriError]: Scheme is missing: {scheme: \"\", authority: \"" + ret.authority + "\", path: \"" + ret.path + "\", query: \"" + ret.query + "\", fragment: \"" + ret.fragment + "\"}");
}
// scheme, https://tools.ietf.org/html/rfc3986#section-3.1
// ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
if (ret.scheme && !_schemePattern.test(ret.scheme)) {
throw new Error('[UriError]: Scheme contains illegal characters.');
}
// path, http://tools.ietf.org/html/rfc3986#section-3.3
// If a URI contains an authority component, then the path component
// must either be empty or begin with a slash ("/") character. If a URI
// does not contain an authority component, then the path cannot begin
// with two slash characters ("//").
if (ret.path) {
if (ret.authority) {
if (!_singleSlashStart.test(ret.path)) {
throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character');
}
}
else {
if (_doubleSlashStart.test(ret.path)) {
throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")');
}
}
}
}
// for a while we allowed uris *without* schemes and this is the migration
// for them, e.g. an uri without scheme and without strict-mode warns and falls
// back to the file-scheme. that should cause the least carnage and still be a
// clear warning
function _schemeFix(scheme, _strict) {
if (!scheme && !_strict) {
return 'file';
}
return scheme;
}
// implements a bit of https://tools.ietf.org/html/rfc3986#section-5
function _referenceResolution(scheme, path) {
// the slash-character is our 'default base' as we don't
// support constructing URIs relative to other URIs. This
// also means that we alter and potentially break paths.
// see https://tools.ietf.org/html/rfc3986#section-5.1.4
switch (scheme) {
case 'https':
case 'http':
case 'file':
if (!path) {
path = _slash;
}
else if (path[0] !== _slash) {
path = _slash + path;
}
break;
}
return path;
}
var _empty = '';
var _slash = '/';
var _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
/**
* Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
* This class is a simple parser which creates the basic component parts
* (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
* and encoding.
*
* ```txt
* foo://example.com:8042/over/there?name=ferret#nose
* \_/ \______________/\_________/ \_________/ \__/
* | | | | |
* scheme authority path query fragment
* | _____________________|__
* / \ / \
* urn:example:animal:ferret:nose
* ```
*/
var URI = /** @class */ (function () {
/**
* @internal
*/
function URI(schemeOrData, authority, path, query, fragment, _strict) {
if (_strict === void 0) { _strict = false; }
if (typeof schemeOrData === 'object') {
this.scheme = schemeOrData.scheme || _empty;
this.authority = schemeOrData.authority || _empty;
this.path = schemeOrData.path || _empty;
this.query = schemeOrData.query || _empty;
this.fragment = schemeOrData.fragment || _empty;
// no validation because it's this URI
// that creates uri components.
// _validateUri(this);
}
else {
this.scheme = _schemeFix(schemeOrData, _strict);
this.authority = authority || _empty;
this.path = _referenceResolution(this.scheme, path || _empty);
this.query = query || _empty;
this.fragment = fragment || _empty;
_validateUri(this, _strict);
}
}
URI.isUri = function (thing) {
if (thing instanceof URI) {
return true;
}
if (!thing) {
return false;
}
return typeof thing.authority === 'string'
&& typeof thing.fragment === 'string'
&& typeof thing.path === 'string'
&& typeof thing.query === 'string'
&& typeof thing.scheme === 'string'
&& typeof thing.fsPath === 'function'
&& typeof thing.with === 'function'
&& typeof thing.toString === 'function';
};
Object.defineProperty(URI.prototype, "fsPath", {
// ---- filesystem path -----------------------
/**
* Returns a string representing the corresponding file system path of this URI.
* Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the
* platform specific path separator.
*
* * Will *not* validate the path for invalid characters and semantics.
* * Will *not* look at the scheme of this URI.
* * The result shall *not* be used for display purposes but for accessing a file on disk.
*
*
* The *difference* to `URI#path` is the use of the platform specific separator and the handling
* of UNC paths. See the below sample of a file-uri with an authority (UNC path).
*
* ```ts
const u = URI.parse('file://server/c$/folder/file.txt')
u.authority === 'server'
u.path === '/shares/c$/file.txt'
u.fsPath === '\\server\c$\folder\file.txt'
```
*
* Using `URI#path` to read a file (using fs-apis) would not be enough because parts of the path,
* namely the server name, would be missing. Therefore `URI#fsPath` exists - it's sugar to ease working
* with URIs that represent files on disk (`file` scheme).
*/
get: function () {
// if (this.scheme !== 'file') {
// console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`);
// }
return uriToFsPath(this, false);
},
enumerable: true,
configurable: true
});
// ---- modify to new -------------------------
URI.prototype.with = function (change) {
if (!change) {
return this;
}
var scheme = change.scheme, authority = change.authority, path = change.path, query = change.query, fragment = change.fragment;
if (scheme === undefined) {
scheme = this.scheme;
}
else if (scheme === null) {
scheme = _empty;
}
if (authority === undefined) {
authority = this.authority;
}
else if (authority === null) {
authority = _empty;
}
if (path === undefined) {
path = this.path;
}
else if (path === null) {
path = _empty;
}
if (query === undefined) {
query = this.query;
}
else if (query === null) {
query = _empty;
}
if (fragment === undefined) {
fragment = this.fragment;
}
else if (fragment === null) {
fragment = _empty;
}
if (scheme === this.scheme
&& authority === this.authority
&& path === this.path
&& query === this.query
&& fragment === this.fragment) {
return this;
}
return new _URI(scheme, authority, path, query, fragment);
};
// ---- parse & validate ------------------------
/**
* Creates a new URI from a string, e.g. `http://www.msft.com/some/path`,
* `file:///usr/home`, or `scheme:with/path`.
*
* @param value A string which represents an URI (see `URI#toString`).
*/
URI.parse = function (value, _strict) {
if (_strict === void 0) { _strict = false; }
var match = _regexp.exec(value);
if (!match) {
return new _URI(_empty, _empty, _empty, _empty, _empty);
}
return new _URI(match[2] || _empty, percentDecode(match[4] || _empty), percentDecode(match[5] || _empty), percentDecode(match[7] || _empty), percentDecode(match[9] || _empty), _strict);
};
/**
* Creates a new URI from a file system path, e.g. `c:\my\files`,
* `/usr/home`, or `\\server\share\some\path`.
*
* The *difference* between `URI#parse` and `URI#file` is that the latter treats the argument
* as path, not as stringified-uri. E.g. `URI.file(path)` is **not the same as**
* `URI.parse('file://' + path)` because the path might contain characters that are
* interpreted (# and ?). See the following sample:
* ```ts
const good = URI.file('/coding/c#/project1');
good.scheme === 'file';
good.path === '/coding/c#/project1';
good.fragment === '';
const bad = URI.parse('file://' + '/coding/c#/project1');
bad.scheme === 'file';
bad.path === '/coding/c'; // path is now broken
bad.fragment === '/project1';
```
*
* @param path A file system path (see `URI#fsPath`)
*/
URI.file = function (path) {
var authority = _empty;
// normalize to fwd-slashes on windows,
// on other systems bwd-slashes are valid
// filename character, eg /f\oo/ba\r.txt
if (isWindows) {
path = path.replace(/\\/g, _slash);
}
// check for authority as used in UNC shares
// or use the path as given
if (path[0] === _slash && path[1] === _slash) {
var idx = path.indexOf(_slash, 2);
if (idx === -1) {
authority = path.substring(2);
path = _slash;
}
else {
authority = path.substring(2, idx);
path = path.substring(idx) || _slash;
}
}
return new _URI('file', authority, path, _empty, _empty);
};
URI.from = function (components) {
return new _URI(components.scheme, components.authority, components.path, components.query, components.fragment);
};
// /**
// * Join a URI path with path fragments and normalizes the resulting path.
// *
// * @param uri The input URI.
// * @param pathFragment The path fragment to add to the URI path.
// * @returns The resulting URI.
// */
// static joinPath(uri: URI, ...pathFragment: string[]): URI {
// if (!uri.path) {
// throw new Error(`[UriError]: cannot call joinPaths on URI without path`);
// }
// let newPath: string;
// if (isWindows && uri.scheme === 'file') {
// newPath = URI.file(paths.win32.join(uriToFsPath(uri, true), ...pathFragment)).path;
// } else {
// newPath = paths.posix.join(uri.path, ...pathFragment);
// }
// return uri.with({ path: newPath });
// }
// ---- printing/externalize ---------------------------
/**
* Creates a string representation for this URI. It's guaranteed that calling
* `URI.parse` with the result of this function creates an URI which is equal
* to this URI.
*
* * The result shall *not* be used for display purposes but for externalization or transport.
* * The result will be encoded using the percentage encoding and encoding happens mostly
* ignore the scheme-specific encoding rules.
*
* @param skipEncoding Do not encode the result, default is `false`
*/
URI.prototype.toString = function (skipEncoding) {
if (skipEncoding === void 0) { skipEncoding = false; }
return _asFormatted(this, skipEncoding);
};
URI.prototype.toJSON = function () {
return this;
};
URI.revive = function (data) {
if (!data) {
return data;
}
else if (data instanceof URI) {
return data;
}
else {
var result = new _URI(data);
result._formatted = data.external;
result._fsPath = data._sep === _pathSepMarker ? data.fsPath : null;
return result;
}
};
return URI;
}());
exports.URI = URI;
var _pathSepMarker = isWindows ? 1 : undefined;
// eslint-disable-next-line @typescript-eslint/class-name-casing
var _URI = /** @class */ (function (_super) {
__extends(_URI, _super);
function _URI() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this._formatted = null;
_this._fsPath = null;
return _this;
}
Object.defineProperty(_URI.prototype, "fsPath", {
get: function () {
if (!this._fsPath) {
this._fsPath = uriToFsPath(this, false);
}
return this._fsPath;
},
enumerable: true,
configurable: true
});
_URI.prototype.toString = function (skipEncoding) {
if (skipEncoding === void 0) { skipEncoding = false; }
if (!skipEncoding) {
if (!this._formatted) {
this._formatted = _asFormatted(this, false);
}
return this._formatted;
}
else {
// we don't cache that
return _asFormatted(this, true);
}
};
_URI.prototype.toJSON = function () {
var res = {
$mid: 1
};
// cached state
if (this._fsPath) {
res.fsPath = this._fsPath;
res._sep = _pathSepMarker;
}
if (this._formatted) {
res.external = this._formatted;
}
// uri components
if (this.path) {
res.path = this.path;
}
if (this.scheme) {
res.scheme = this.scheme;
}
if (this.authority) {
res.authority = this.authority;
}
if (this.query) {
res.query = this.query;
}
if (this.fragment) {
res.fragment = this.fragment;
}
return res;
};
return _URI;
}(URI));
// reserved characters: https://tools.ietf.org/html/rfc3986#section-2.2
var encodeTable = (_a = {},
_a[58 /* Colon */] = '%3A',
_a[47 /* Slash */] = '%2F',
_a[63 /* QuestionMark */] = '%3F',
_a[35 /* Hash */] = '%23',
_a[91 /* OpenSquareBracket */] = '%5B',
_a[93 /* CloseSquareBracket */] = '%5D',
_a[64 /* AtSign */] = '%40',
_a[33 /* ExclamationMark */] = '%21',
_a[36 /* DollarSign */] = '%24',
_a[38 /* Ampersand */] = '%26',
_a[39 /* SingleQuote */] = '%27',
_a[40 /* OpenParen */] = '%28',
_a[41 /* CloseParen */] = '%29',
_a[42 /* Asterisk */] = '%2A',
_a[43 /* Plus */] = '%2B',
_a[44 /* Comma */] = '%2C',
_a[59 /* Semicolon */] = '%3B',
_a[61 /* Equals */] = '%3D',
_a[32 /* Space */] = '%20',
_a);
function encodeURIComponentFast(uriComponent, allowSlash) {
var res = undefined;
var nativeEncodePos = -1;
for (var pos = 0; pos < uriComponent.length; pos++) {
var code = uriComponent.charCodeAt(pos);
// unreserved characters: https://tools.ietf.org/html/rfc3986#section-2.3
if ((code >= 97 /* a */ && code <= 122 /* z */)
|| (code >= 65 /* A */ && code <= 90 /* Z */)
|| (code >= 48 /* Digit0 */ && code <= 57 /* Digit9 */)
|| code === 45 /* Dash */
|| code === 46 /* Period */
|| code === 95 /* Underline */
|| code === 126 /* Tilde */
|| (allowSlash && code === 47 /* Slash */)) {
// check if we are delaying native encode
if (nativeEncodePos !== -1) {
res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos));
nativeEncodePos = -1;
}
// check if we write into a new string (by default we try to return the param)
if (res !== undefined) {
res += uriComponent.charAt(pos);
}
}
else {
// encoding needed, we need to allocate a new string
if (res === undefined) {
res = uriComponent.substr(0, pos);
}
// check with default table first
var escaped = encodeTable[code];
if (escaped !== undefined) {
// check if we are delaying native encode
if (nativeEncodePos !== -1) {
res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos));
nativeEncodePos = -1;
}
// append escaped variant to result
res += escaped;
}
else if (nativeEncodePos === -1) {
// use native encode only when needed
nativeEncodePos = pos;
}
}
}
if (nativeEncodePos !== -1) {
res += encodeURIComponent(uriComponent.substring(nativeEncodePos));
}
return res !== undefined ? res : uriComponent;
}
function encodeURIComponentMinimal(path) {
var res = undefined;
for (var pos = 0; pos < path.length; pos++) {
var code = path.charCodeAt(pos);
if (code === 35 /* Hash */ || code === 63 /* QuestionMark */) {
if (res === undefined) {
res = path.substr(0, pos);
}
res += encodeTable[code];
}
else {
if (res !== undefined) {
res += path[pos];
}
}
}
return res !== undefined ? res : path;
}
/**
* Compute `fsPath` for the given uri
*/
function uriToFsPath(uri, keepDriveLetterCasing) {
var value;
if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') {
// unc path: file://shares/c$/far/boo
value = "//" + uri.authority + uri.path;
}
else if (uri.path.charCodeAt(0) === 47 /* Slash */
&& (uri.path.charCodeAt(1) >= 65 /* A */ && uri.path.charCodeAt(1) <= 90 /* Z */ || uri.path.charCodeAt(1) >= 97 /* a */ && uri.path.charCodeAt(1) <= 122 /* z */)
&& uri.path.charCodeAt(2) === 58 /* Colon */) {
if (!keepDriveLetterCasing) {
// windows drive letter: file:///c:/far/boo
value = uri.path[1].toLowerCase() + uri.path.substr(2);
}
else {
value = uri.path.substr(1);
}
}
else {
// other path
value = uri.path;
}
if (isWindows) {
value = value.replace(/\//g, '\\');
}
return value;
}
exports.uriToFsPath = uriToFsPath;
/**
* Create the external version of a uri
*/
function _asFormatted(uri, skipEncoding) {
var encoder = !skipEncoding
? encodeURIComponentFast
: encodeURIComponentMinimal;
var res = '';
var scheme = uri.scheme, authority = uri.authority, path = uri.path, query = uri.query, fragment = uri.fragment;
if (scheme) {
res += scheme;
res += ':';
}
if (authority || scheme === 'file') {
res += _slash;
res += _slash;
}
if (authority) {
var idx = authority.indexOf('@');
if (idx !== -1) {
// <user>@<auth>
var userinfo = authority.substr(0, idx);
authority = authority.substr(idx + 1);
idx = userinfo.indexOf(':');
if (idx === -1) {
res += encoder(userinfo, false);
}
else {
// <user>:<pass>@<auth>
res += encoder(userinfo.substr(0, idx), false);
res += ':';
res += encoder(userinfo.substr(idx + 1), false);
}
res += '@';
}
authority = authority.toLowerCase();
idx = authority.indexOf(':');
if (idx === -1) {
res += encoder(authority, false);
}
else {
// <auth>:<port>
res += encoder(authority.substr(0, idx), false);
res += authority.substr(idx);
}
}
if (path) {
// lower-case windows drive letters in /C:/fff or C:/fff
if (path.length >= 3 && path.charCodeAt(0) === 47 /* Slash */ && path.charCodeAt(2) === 58 /* Colon */) {
var code = path.charCodeAt(1);
if (code >= 65 /* A */ && code <= 90 /* Z */) {
path = "/" + String.fromCharCode(code + 32) + ":" + path.substr(3); // "/c:".length === 3
}
}
else if (path.length >= 2 && path.charCodeAt(1) === 58 /* Colon */) {
var code = path.charCodeAt(0);
if (code >= 65 /* A */ && code <= 90 /* Z */) {
path = String.fromCharCode(code + 32) + ":" + path.substr(2); // "/c:".length === 3
}
}
// encode the rest of the path
res += encoder(path, true);
}
if (query) {
res += '?';
res += encoder(query, false);
}
if (fragment) {
res += '#';
res += !skipEncoding ? encodeURIComponentFast(fragment, false) : fragment;
}
return res;
}
// --- decode
function decodeURIComponentGraceful(str) {
try {
return decodeURIComponent(str);
}
catch (_a) {
if (str.length > 3) {
return str.substr(0, 3) + decodeURIComponentGraceful(str.substr(3));
}
else {
return str;
}
}
}
var _rEncodedAsHex = /(%[0-9A-Za-z][0-9A-Za-z])+/g;
function percentDecode(str) {
if (!str.match(_rEncodedAsHex)) {
return str;
}
return str.replace(_rEncodedAsHex, function (match) { return decodeURIComponentGraceful(match); });
}
});

View file

@ -0,0 +1,25 @@
{
"name": "vscode-uri",
"author": "Microsoft",
"version": "2.1.2",
"description": "The URI implementation that is used by VS Code and its extensions",
"main": "./lib/umd/index.js",
"typings": "./lib/umd/index",
"module": "./lib/esm/index.js",
"scripts": {
"compile": "tsc -p ./src && tsc -p ./src/tsconfig.esm.json",
"prepublish": "npm run compile"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Microsoft/vscode-uri.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/Microsoft/vscode-uri/issues"
},
"homepage": "https://github.com/Microsoft/vscode-uri#readme",
"devDependencies": {
"typescript": "^3.6.4"
}
}

42
node_modules/@vscode/emmet-helper/package.json generated vendored Normal file
View file

@ -0,0 +1,42 @@
{
"name": "@vscode/emmet-helper",
"version": "2.9.2",
"description": "Helper to use emmet modules in Visual Studio Code",
"main": "./lib/cjs/emmetHelper.js",
"module": "./lib/esm/emmetHelper.js",
"types": "./lib/cjs/emmetHelper.d.ts",
"author": "Microsoft Corporation",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-emmet-helper"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/Microsoft/vscode-emmet-helper"
},
"devDependencies": {
"@types/mocha": "^10.0.1",
"@types/node": "^16.0.0",
"@types/vscode": "^1.78.0",
"mocha": "^10.2.0",
"rimraf": "^3.0.2",
"typescript": "^4.0.0"
},
"dependencies": {
"emmet": "^2.4.3",
"jsonc-parser": "^2.3.0",
"vscode-languageserver-textdocument": "^1.0.1",
"vscode-languageserver-types": "^3.15.1",
"vscode-uri": "^2.1.2"
},
"scripts": {
"prepublishOnly": "npm run clean && yarn && npm run compile && npm run compile-esm && npm run test",
"watch": "tsc -watch -p ./",
"compile": "tsc -p ./",
"clean": "rimraf lib",
"compile-esm": "tsc -p ./tsconfig.esm.json",
"test": "mocha lib/cjs/test",
"preversion": "npm test",
"postversion": "git push && git push --tags"
}
}

View file

@ -0,0 +1,53 @@
THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
vscode-emmet-helper incorporates third party material from the projects listed below. The original copyright
notice and the license under which Microsoft received such third party material are set forth below. Microsoft
reserves all other rights not expressly granted, whether by implication, estoppel or otherwise.
1. expand-abbreivation (https://github.com/emmetio/expand-abbreivation)
MIT License
Copyright (c) 2017 Emmet.io
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.
2. extract-abbreivation (https://github.com/emmetio/extract-abbreivation)
MIT License
Copyright (c) 2017 Emmet.io
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.