diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..10541931 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## 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](), 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). + + diff --git a/monaco.d.ts b/monaco.d.ts index feb49fc4..1021c98f 100644 --- a/monaco.d.ts +++ b/monaco.d.ts @@ -143,6 +143,11 @@ declare namespace monaco.languages.typescript { noSemanticValidation?: boolean; noSyntaxValidation?: boolean; noSuggestionDiagnostics?: boolean; + /** + * Limit diagnostic computation to only visible files. + * Defaults to false. + */ + onlyVisible?: boolean; diagnosticCodesToIgnore?: number[]; } export interface WorkerOptions { diff --git a/package-lock.json b/package-lock.json index 0d7bbb0e..0d06b3f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "monaco-typescript", - "version": "4.3.1", + "version": "4.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -237,15 +237,15 @@ } }, "monaco-editor-core": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/monaco-editor-core/-/monaco-editor-core-0.23.0.tgz", - "integrity": "sha512-1LHVpCHp+FErFdUjwzal1muTe+hUkR5CfXAxzua//eHB31bHdWVe15OJPEPS3/rxmfQtl9wZqJdHi4FcGz2zog==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/monaco-editor-core/-/monaco-editor-core-0.24.0.tgz", + "integrity": "sha512-WJAzpNYEaJp8Z7crIAiLCVln1zZdo4cFXCRuhTDN4A3tz6IK2NOXAtTOZ9iLKBTtd6eitZJ2Q1Fx8JN8rN3fWw==", "dev": true }, "monaco-languages": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/monaco-languages/-/monaco-languages-2.3.0.tgz", - "integrity": "sha512-gER/vKpIUH2kKhWEWRDojMuI6cm4S7Dl9jSYNFUfToOB8uIxoWSZUJCm2FH+ECnUZmnLs6O2fNONKQQ6JCoa/g==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/monaco-languages/-/monaco-languages-2.4.0.tgz", + "integrity": "sha512-63ZMAPgqBylS0kyC22QSKJWJmCRcwM7dWql1xtt9R7mCfzszfIIViSTiJ5vDSnzj0lH0aZh8OJggh/uZY6Vgnw==", "dev": true }, "monaco-plugin-helpers": { @@ -461,9 +461,9 @@ } }, "typescript": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", - "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", "dev": true }, "which": { diff --git a/package.json b/package.json index 68d17411..3f423146 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "monaco-typescript", - "version": "4.3.1", + "version": "4.4.0", "description": "TypeScript and JavaScript language support for Monaco Editor", "scripts": { "compile-amd": "mcopy ./src/lib/typescriptServices-amd.js ./out/amd/lib/typescriptServices.js && tsc -p ./src/tsconfig.json", @@ -25,14 +25,14 @@ "devDependencies": { "@typescript/vfs": "^1.3.4", "husky": "^5.1.3", - "monaco-editor-core": "^0.23.0", - "monaco-languages": "^2.3.0", + "monaco-editor-core": "^0.24.0", + "monaco-languages": "^2.4.0", "monaco-plugin-helpers": "^1.0.3", "prettier": "^2.2.1", "pretty-quick": "^3.1.0", "requirejs": "^2.3.6", "terser": "^5.6.0", - "typescript": "^4.2.3" + "typescript": "^4.2.4" }, "husky": { "hooks": { diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index 74925c7d..067962ab 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -7,7 +7,8 @@ import { Diagnostic, DiagnosticRelatedInformation, - LanguageServiceDefaults + LanguageServiceDefaults, + typescriptDefaults } from './monaco.contribution'; import type * as ts from './lib/typescriptServices'; import type { TypeScriptWorker } from './tsWorker'; @@ -163,6 +164,15 @@ enum DiagnosticCategory { Message = 3 } +/** + * temporary interface until the editor API exposes + * `IModel.isAttachedToEditor` and `IModel.onDidChangeAttached` + */ +interface IInternalEditorModel extends editor.IModel { + onDidChangeAttached(listener: () => void): IDisposable; + isAttachedToEditor(): boolean; +} + export class DiagnosticsAdapter extends Adapter { private _disposables: IDisposable[] = []; private _listener: { [uri: string]: IDisposable } = Object.create(null); @@ -175,25 +185,52 @@ export class DiagnosticsAdapter extends Adapter { ) { super(worker); - const onModelAdd = (model: editor.IModel): void => { + const onModelAdd = (model: IInternalEditorModel): void => { if (model.getModeId() !== _selector) { return; } + const maybeValidate = () => { + const { onlyVisible } = this._defaults.getDiagnosticsOptions(); + if (onlyVisible) { + if (model.isAttachedToEditor()) { + this._doValidate(model); + } + } else { + this._doValidate(model); + } + }; + let handle: number; const changeSubscription = model.onDidChangeContent(() => { clearTimeout(handle); - handle = setTimeout(() => this._doValidate(model), 500); + handle = setTimeout(maybeValidate, 500); + }); + + const visibleSubscription = model.onDidChangeAttached(() => { + const { onlyVisible } = this._defaults.getDiagnosticsOptions(); + if (onlyVisible) { + if (model.isAttachedToEditor()) { + // this model is now attached to an editor + // => compute diagnostics + maybeValidate(); + } else { + // this model is no longer attached to an editor + // => clear existing diagnostics + editor.setModelMarkers(model, this._selector, []); + } + } }); this._listener[model.uri.toString()] = { dispose() { changeSubscription.dispose(); + visibleSubscription.dispose(); clearTimeout(handle); } }; - this._doValidate(model); + maybeValidate(); }; const onModelRemoved = (model: editor.IModel): void => { @@ -205,12 +242,12 @@ export class DiagnosticsAdapter extends Adapter { } }; - this._disposables.push(editor.onDidCreateModel(onModelAdd)); + this._disposables.push(editor.onDidCreateModel((model) => onModelAdd(model))); this._disposables.push(editor.onWillDisposeModel(onModelRemoved)); this._disposables.push( editor.onDidChangeModelLanguage((event) => { onModelRemoved(event.model); - onModelAdd(event.model); + onModelAdd(event.model); }) ); @@ -226,13 +263,13 @@ export class DiagnosticsAdapter extends Adapter { // redo diagnostics when options change for (const model of editor.getModels()) { onModelRemoved(model); - onModelAdd(model); + onModelAdd(model); } }; this._disposables.push(this._defaults.onDidChange(recomputeDiagostics)); this._disposables.push(this._defaults.onDidExtraLibsChange(recomputeDiagostics)); - editor.getModels().forEach(onModelAdd); + editor.getModels().forEach((model) => onModelAdd(model)); } public dispose(): void { @@ -753,6 +790,17 @@ export class DefinitionAdapter extends Adapter { uri: uri, range: this._textSpanToRange(refModel, entry.textSpan) }); + } else { + const matchedLibFile = typescriptDefaults.getExtraLibs()[entry.fileName] + if (matchedLibFile) { + const libModel = editor.createModel(matchedLibFile.content, 'typescript', uri); + return { + uri: uri, + range: this._textSpanToRange(libModel, entry.textSpan) + } + } + + } } return result; diff --git a/src/lib/typescriptServices-amd.js b/src/lib/typescriptServices-amd.js index 4e113ea5..af79b3e1 100644 --- a/src/lib/typescriptServices-amd.js +++ b/src/lib/typescriptServices-amd.js @@ -293,7 +293,7 @@ var ts; // The following is baselined as a literal template type without intervention /** The version of the TypeScript compiler release */ // eslint-disable-next-line @typescript-eslint/no-inferrable-types - ts.version = "4.2.3"; + ts.version = "4.2.4"; /* @internal */ var Comparison; (function (Comparison) { @@ -46611,7 +46611,9 @@ var ts; var result = new Type(checker, flags); typeCount++; result.id = typeCount; - typeCatalog.push(result); + if (ts.tracing) { + typeCatalog.push(result); + } return result; } function createOriginType(flags) { @@ -148422,7 +148424,7 @@ var ts; }; SignatureObject.prototype.getJsDocTags = function () { if (this.jsDocTags === undefined) { - this.jsDocTags = this.declaration ? getJsDocTags([this.declaration], this.checker) : []; + this.jsDocTags = this.declaration ? getJsDocTagsOfSignature(this.declaration, this.checker) : []; } return this.jsDocTags; }; @@ -148436,15 +148438,13 @@ var ts; function hasJSDocInheritDocTag(node) { return ts.getJSDocTags(node).some(function (tag) { return tag.tagName.text === "inheritDoc"; }); } - function getJsDocTags(declarations, checker) { - var tags = ts.JsDoc.getJsDocTagsFromDeclarations(declarations); - if (tags.length === 0 || declarations.some(hasJSDocInheritDocTag)) { - ts.forEachUnique(declarations, function (declaration) { - var inheritedTags = findBaseOfDeclaration(checker, declaration, function (symbol) { return symbol.getJsDocTags(); }); - if (inheritedTags) { - tags = __spreadArray(__spreadArray([], inheritedTags), tags); - } - }); + function getJsDocTagsOfSignature(declaration, checker) { + var tags = ts.JsDoc.getJsDocTagsFromDeclarations([declaration]); + if (tags.length === 0 || hasJSDocInheritDocTag(declaration)) { + var inheritedTags = findBaseOfDeclaration(checker, declaration, function (symbol) { var _a; return ((_a = symbol.declarations) === null || _a === void 0 ? void 0 : _a.length) === 1 ? symbol.getJsDocTags() : undefined; }); + if (inheritedTags) { + tags = __spreadArray(__spreadArray([], inheritedTags), tags); + } } return tags; } diff --git a/src/lib/typescriptServices.js b/src/lib/typescriptServices.js index 6142d712..0e7de0f2 100644 --- a/src/lib/typescriptServices.js +++ b/src/lib/typescriptServices.js @@ -293,7 +293,7 @@ var ts; // The following is baselined as a literal template type without intervention /** The version of the TypeScript compiler release */ // eslint-disable-next-line @typescript-eslint/no-inferrable-types - ts.version = "4.2.3"; + ts.version = "4.2.4"; /* @internal */ var Comparison; (function (Comparison) { @@ -46611,7 +46611,9 @@ var ts; var result = new Type(checker, flags); typeCount++; result.id = typeCount; - typeCatalog.push(result); + if (ts.tracing) { + typeCatalog.push(result); + } return result; } function createOriginType(flags) { @@ -148422,7 +148424,7 @@ var ts; }; SignatureObject.prototype.getJsDocTags = function () { if (this.jsDocTags === undefined) { - this.jsDocTags = this.declaration ? getJsDocTags([this.declaration], this.checker) : []; + this.jsDocTags = this.declaration ? getJsDocTagsOfSignature(this.declaration, this.checker) : []; } return this.jsDocTags; }; @@ -148436,15 +148438,13 @@ var ts; function hasJSDocInheritDocTag(node) { return ts.getJSDocTags(node).some(function (tag) { return tag.tagName.text === "inheritDoc"; }); } - function getJsDocTags(declarations, checker) { - var tags = ts.JsDoc.getJsDocTagsFromDeclarations(declarations); - if (tags.length === 0 || declarations.some(hasJSDocInheritDocTag)) { - ts.forEachUnique(declarations, function (declaration) { - var inheritedTags = findBaseOfDeclaration(checker, declaration, function (symbol) { return symbol.getJsDocTags(); }); - if (inheritedTags) { - tags = __spreadArray(__spreadArray([], inheritedTags), tags); - } - }); + function getJsDocTagsOfSignature(declaration, checker) { + var tags = ts.JsDoc.getJsDocTagsFromDeclarations([declaration]); + if (tags.length === 0 || hasJSDocInheritDocTag(declaration)) { + var inheritedTags = findBaseOfDeclaration(checker, declaration, function (symbol) { var _a; return ((_a = symbol.declarations) === null || _a === void 0 ? void 0 : _a.length) === 1 ? symbol.getJsDocTags() : undefined; }); + if (inheritedTags) { + tags = __spreadArray(__spreadArray([], inheritedTags), tags); + } } return tags; } diff --git a/src/lib/typescriptServicesMetadata.ts b/src/lib/typescriptServicesMetadata.ts index f8dc83a7..01ba296c 100644 --- a/src/lib/typescriptServicesMetadata.ts +++ b/src/lib/typescriptServicesMetadata.ts @@ -2,4 +2,4 @@ // **NOTE**: Do not edit directly! This file is generated using `npm run import-typescript` // -export const typescriptVersion = "4.2.3"; +export const typescriptVersion = "4.2.4"; diff --git a/src/monaco.contribution.ts b/src/monaco.contribution.ts index 534676f3..a2f8984d 100644 --- a/src/monaco.contribution.ts +++ b/src/monaco.contribution.ts @@ -156,6 +156,11 @@ export interface DiagnosticsOptions { noSemanticValidation?: boolean; noSyntaxValidation?: boolean; noSuggestionDiagnostics?: boolean; + /** + * Limit diagnostic computation to only visible files. + * Defaults to false. + */ + onlyVisible?: boolean; diagnosticCodesToIgnore?: number[]; } @@ -616,13 +621,13 @@ export const typescriptVersion: string = tsversion; export const typescriptDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl( { allowNonTsExtensions: true, target: ScriptTarget.Latest }, - { noSemanticValidation: false, noSyntaxValidation: false }, + { noSemanticValidation: false, noSyntaxValidation: false, onlyVisible: false }, {} ); export const javascriptDefaults: LanguageServiceDefaults = new LanguageServiceDefaultsImpl( { allowNonTsExtensions: true, allowJs: true, target: ScriptTarget.Latest }, - { noSemanticValidation: true, noSyntaxValidation: false }, + { noSemanticValidation: true, noSyntaxValidation: false, onlyVisible: false }, {} ); diff --git a/src/tsWorker.ts b/src/tsWorker.ts index f915c639..0e3efdf6 100644 --- a/src/tsWorker.ts +++ b/src/tsWorker.ts @@ -8,6 +8,7 @@ import * as ts from './lib/typescriptServices'; import { libFileMap } from './lib/lib'; import { Diagnostic, + DiagnosticRelatedInformation, IExtraLibs, TypeScriptWorker as ITypeScriptWorker } from './monaco.contribution'; @@ -177,17 +178,26 @@ export class TypeScriptWorker implements ts.LanguageServiceHost, ITypeScriptWork // --- language features - private static clearFiles(diagnostics: ts.Diagnostic[]): Diagnostic[] { + private static clearFiles(tsDiagnostics: ts.Diagnostic[]): Diagnostic[] { // Clear the `file` field, which cannot be JSON'yfied because it // contains cyclic data structures, except for the `fileName` // property. - diagnostics.forEach((diag: Diagnostic) => { - diag.file = diag.file ? { fileName: diag.file.fileName } : undefined; - diag.relatedInformation?.forEach( - (diag2) => (diag2.file = diag2.file ? { fileName: diag2.file.fileName } : undefined) - ); - }); - return diagnostics; + // Do a deep clone so we don't mutate the ts.Diagnostic object (see https://github.com/microsoft/monaco-editor/issues/2392) + const diagnostics: Diagnostic[] = []; + for (const tsDiagnostic of tsDiagnostics) { + const diagnostic: Diagnostic = { ...tsDiagnostic }; + diagnostic.file = diagnostic.file ? { fileName: diagnostic.file.fileName } : undefined; + if (tsDiagnostic.relatedInformation) { + diagnostic.relatedInformation = []; + for (const tsRelatedDiagnostic of tsDiagnostic.relatedInformation) { + const relatedDiagnostic: DiagnosticRelatedInformation = { ...tsRelatedDiagnostic }; + relatedDiagnostic.file = relatedDiagnostic.file ? { fileName: relatedDiagnostic.file.fileName } : undefined + diagnostic.relatedInformation.push(relatedDiagnostic); + } + } + diagnostics.push(diagnostic); + } + return diagnostics; } async getSyntacticDiagnostics(fileName: string): Promise {