Skip to content

Commit

Permalink
Showing 12 changed files with 450 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
- Fix issue [#312](https://github.com/intersystems/language-server/issues/312): Fix routine existence diagnostics for routines that only exist in OBJ form
- Fix issue [#313](https://github.com/intersystems/language-server/issues/313): Add Diagnostic when `ROUTINE` header is missing
- Fix issue [#314](https://github.com/intersystems/language-server/issues/314): Suggest boolean class keywords for completion after typing `Not`
- Fix issue [#315](https://github.com/intersystems/language-server/issues/315): Add warning diagnostics for SQL reserved words in class and property names of persistent classes
- Fix issue [#316](https://github.com/intersystems/language-server/issues/316): Show class description when hovering over a typed variable
- Fix issue [#317](https://github.com/intersystems/language-server/issues/317): Add `DocumentLink` for `##class()` in class description comments
- Fix issue [#318](https://github.com/intersystems/language-server/issues/318): Support optional chaining operator in JavaScript colorer
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ This is a [LSP](https://microsoft.github.io/language-server-protocol/) compliant
- Mismatches between declared UDL Parameter types and the assigned value.
- Classes, Methods, Parameters and Properties that are [Deprecated](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=ROBJ_method_deprecated).
- `$ZUTIL` functions that [are deprecated or have been superseded](https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_replacements).
- [SQL reserved words](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_reservedwords) used in class and property names of persistent classes.
- [Folding Ranges](https://code.visualstudio.com/docs/editor/codebasics#_folding) for the following:
- ObjectScript code blocks (If/ElseIf/Else, Try/Catch, For, While, etc.)
- UDL class members
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -223,6 +223,12 @@
]
},
"description": "Controls the languages that syntax error diagnosics will be suppressed for."
},
"intersystems.language-server.diagnostics.sqlReserved": {
"scope": "resource",
"type": "boolean",
"default": true,
"markdownDescription": "Controls whether diagnostics are provided when a [SQL reserved word](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_reservedwords) is used in a class or property name of a persistent class."
}
}
},
279 changes: 279 additions & 0 deletions server/src/documentation/sqlReservedWords.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
[
"%AFTERHAVING",
"%ALLINDEX",
"%ALPHAUP",
"%ALTER",
"%BEGTRANS",
"%CHECKPRIV",
"%CLASSNAME",
"%CLASSPARAMETER",
"%DBUGFULL",
"%DELDATA",
"%DESCRIPTION",
"%EXACT",
"%EXTERNAL",
"%FILE",
"%FIRSTTABLE",
"%FLATTEN",
"%FOREACH",
"%FULL",
"%ID",
"%IDADDED",
"%IGNOREINDEX",
"%IGNOREINDICES",
"%INLIST",
"%INORDER",
"%INTERNAL",
"%INTEXT",
"%INTRANS",
"%INTRANSACTION",
"%KEY",
"%MATCHES",
"%MCODE",
"%MERGE",
"%MINUS",
"%MVR",
"%NOCHECK",
"%NODELDATA",
"%NOFLATTEN",
"%NOFPLAN",
"%NOINDEX",
"%NOLOCK",
"%NOMERGE",
"%NOPARALLEL",
"%NOREDUCE",
"%NORUNTIME",
"%NOSVSO",
"%NOTOPOPT",
"%NOTRIGGER",
"%NOUNIONOROPT",
"%NUMROWS",
"%ODBCIN",
"%ODBCOUT",
"%PARALLEL",
"%PLUS",
"%PROFILE",
"%PROFILE_ALL",
"%PUBLICROWID",
"%ROUTINE",
"%ROWCOUNT",
"%RUNTIMEIN",
"%RUNTIMEOUT",
"%STARTSWITH",
"%STARTTABLE",
"%SQLSTRING",
"%SQLUPPER",
"%STRING",
"%TABLENAME",
"%TRUNCATE",
"%UPPER",
"%VALUE",
"%VIDABSOLUTE",
"ADD",
"ALL",
"ALLOCATE",
"ALTER",
"AND",
"ANY",
"ARE",
"AS",
"ASC",
"ASSERTION",
"AT",
"AUTHORIZATION",
"AVG",
"BEGIN",
"BETWEEN",
"BIT",
"BIT_LENGTH",
"BOTH",
"BY",
"CASCADE",
"CASE",
"CAST",
"CHAR",
"CHARACTER",
"CHARACTER_LENGTH",
"CHAR_LENGTH",
"CHECK",
"CLOSE",
"COALESCE",
"COLLATE",
"COMMIT",
"CONNECT",
"CONNECTION",
"CONSTRAINT",
"CONSTRAINTS",
"CONTINUE",
"CONVERT",
"CORRESPONDING",
"COUNT",
"CREATE",
"CROSS",
"CURRENT",
"CURRENT_DATE",
"CURRENT_TIME",
"CURRENT_TIMESTAMP",
"CURRENT_USER",
"CURSOR",
"DATE",
"DEALLOCATE",
"DEC",
"DECIMAL",
"DECLARE",
"DEFAULT",
"DEFERRABLE",
"DEFERRED",
"DELETE",
"DESC",
"DESCRIBE",
"DESCRIPTOR",
"DIAGNOSTICS",
"DISCONNECT",
"DISTINCT",
"DOMAIN",
"DOUBLE",
"DROP",
"ELSE",
"END",
"ENDEXEC",
"ESCAPE",
"EXCEPT",
"EXCEPTION",
"EXEC",
"EXECUTE",
"EXISTS",
"EXTERNAL",
"EXTRACT",
"FALSE",
"FETCH",
"FIRST",
"FLOAT",
"FOR",
"FOREIGN",
"FOUND",
"FROM",
"FULL",
"GET",
"GLOBAL",
"GO",
"GOTO",
"GRANT",
"GROUP",
"HAVING",
"HOUR",
"IDENTITY",
"IMMEDIATE",
"IN",
"INDICATOR",
"INITIALLY",
"INNER",
"INPUT",
"INSENSITIVE",
"INSERT",
"INT",
"INTEGER",
"INTERSECT",
"INTERVAL",
"INTO",
"IS",
"ISOLATION",
"JOIN",
"LANGUAGE",
"LAST",
"LEADING",
"LEFT",
"LEVEL",
"LIKE",
"LOCAL",
"LOWER",
"MATCH",
"MAX",
"MIN",
"MINUTE",
"MODULE",
"NAMES",
"NATIONAL",
"NATURAL",
"NCHAR",
"NEXT",
"NO",
"NOT",
"NULL",
"NULLIF",
"NUMERIC",
"OCTET_LENGTH",
"OF",
"ON",
"ONLY",
"OPEN",
"OPTION",
"OR",
"OUTER",
"OUTPUT",
"OVERLAPS",
"PAD",
"PARTIAL",
"PREPARE",
"PRESERVE",
"PRIMARY",
"PRIOR",
"PRIVILEGES",
"PROCEDURE",
"PUBLIC",
"READ",
"REAL",
"REFERENCES",
"RELATIVE",
"RESTRICT",
"REVOKE",
"RIGHT",
"ROLE",
"ROLLBACK",
"ROWS",
"SCHEMA",
"SCROLL",
"SECOND",
"SECTION",
"SELECT",
"SESSION_USER",
"SET",
"SHARD",
"SMALLINT",
"SOME",
"SPACE",
"SQLERROR",
"SQLSTATE",
"STATISTICS",
"SUBSTRING",
"SUM",
"SYSDATE",
"SYSTEM_USER",
"TABLE",
"TEMPORARY",
"THEN",
"TIME",
"TIMEZONE_HOUR",
"TIMEZONE_MINUTE",
"TO",
"TOP",
"TRAILING",
"TRANSACTION",
"TRIM",
"TRUE",
"UNION",
"UNIQUE",
"UPDATE",
"UPPER",
"USER",
"USING",
"VALUES",
"VARCHAR",
"VARYING",
"WHEN",
"WHENEVER",
"WHERE",
"WITH",
"WORK",
"WRITE"
]
7 changes: 1 addition & 6 deletions server/src/providers/definition.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Position, TextDocumentPositionParams, Range } from 'vscode-languageserver/node';
import { getServerSpec, findFullRange, normalizeClassname, makeRESTRequest, createDefinitionUri, getMacroContext, isMacroDefinedAbove, quoteUDLIdentifier, getClassMemberContext, determineClassNameParameterClass, getParsedDocument, currentClass, getTextForUri } from '../utils/functions';
import { ServerSpec, QueryData } from '../utils/types';
import { documents, corePropertyParams } from '../utils/variables';
import { documents, corePropertyParams, classMemberTypes } from '../utils/variables';
import * as ld from '../utils/languageDefinitions';

/**
@@ -10,11 +10,6 @@ import * as ld from '../utils/languageDefinitions';
*/
const definitionTargetRangeMaxLines: number = 10;

/**
* An array containing all UDL class member types.
*/
const classMemberTypes: string[] = ["Parameter","Property","Relationship","ForeignKey","Index","Query","Storage","Trigger","XData","Projection","Method","ClassMethod","ClientMethod"];

export async function onDefinition(params: TextDocumentPositionParams) {
const doc = documents.get(params.textDocument.uri);
if (doc === undefined) {return null;}
152 changes: 145 additions & 7 deletions server/src/providers/diagnostic.ts
Original file line number Diff line number Diff line change
@@ -17,12 +17,14 @@ import {
getServerSpec,
makeRESTRequest,
normalizeClassname,
quoteUDLIdentifier
quoteUDLIdentifier,
isClassMember
} from '../utils/functions';
import { zutilFunctions, lexerLanguages, documents } from '../utils/variables';
import { ServerSpec, StudioOpenDialogFile, QueryData } from '../utils/types';
import * as ld from '../utils/languageDefinitions';
import parameterTypes = require("../documentation/parameterTypes.json");
import sqlReservedWords = require("../documentation/sqlReservedWords.json");

/**
* Helper method that appends `range` to value of `key` in `map`
@@ -64,6 +66,7 @@ export async function onDiagnostics(params: DocumentDiagnosticParams): Promise<D
var files: StudioOpenDialogFile[] = [];
var inheritedpackages: string[] = [];
var querydata: QueryData;
let isPersistent: boolean = false;
if (settings.diagnostics.routines || settings.diagnostics.classes || settings.diagnostics.deprecation) {
if (settings.diagnostics.routines && (settings.diagnostics.classes || settings.diagnostics.deprecation)) {
// Get all classes and routines
@@ -95,9 +98,12 @@ export async function onDiagnostics(params: DocumentDiagnosticParams): Promise<D
files = respdata.data.result.content;
}
}
if (doc.languageId === "objectscript-class" && (settings.diagnostics.classes || settings.diagnostics.deprecation)) {
if (doc.languageId == "objectscript-class" && (
settings.diagnostics.classes || settings.diagnostics.deprecation || settings.diagnostics.sqlReserved
)) {
var clsname = "";
var hassupers = false;
let supers: string[] = [""];

// Find the class name and if the class has supers
for (let i = 0; i < parsed.length; i++) {
@@ -113,32 +119,41 @@ export async function onDiagnostics(params: DocumentDiagnosticParams): Promise<D
for (let j = 1; j < parsed[i].length; j++) {
if (
parsed[i][j].l == ld.cls_langindex && parsed[i][j].s == ld.cls_keyword_attrindex &&
doc.getText(Range.create(
Position.create(i,parsed[i][j].p),
Position.create(i,parsed[i][j].p+parsed[i][j].c)
)).toLowerCase() === "extends"
doc.getText(Range.create(i,parsed[i][j].p,i,parsed[i][j].p+parsed[i][j].c)).toLowerCase() == "extends"
) {
// The 'Extends' keyword is present
hassupers = true;
if (!settings.diagnostics.sqlReserved) break;
} else if (parsed[i][j].l == ld.cls_langindex && parsed[i][j].s == ld.cls_clsname_attrindex && hassupers) {
supers.push(supers.pop() + doc.getText(Range.create(i,parsed[i][j].p,i,parsed[i][j].p+parsed[i][j].c)));
} else if (
parsed[i][j].l == ld.cls_langindex &&
parsed[i][j].s == ld.cls_delim_attrindex &&
[")","[","{"].includes(doc.getText(Range.create(i,parsed[i][j].p,i,parsed[i][j].p+parsed[i][j].c)))
) {
// This is the end of the superclass list
break;
}
}
break;
}
}
}
isPersistent = supers.includes("%Persistent") || supers.includes("%Library.Persistent");
if (hassupers) {
const pkgquerydata = {
query: "SELECT $LISTTOSTRING(Importall) AS Importall FROM %Dictionary.CompiledClass WHERE Name = ?",
query: "SELECT $LISTTOSTRING(Importall) AS Importall, $FIND(PrimarySuper,'~%Library.Persistent~') AS IsPersistent FROM %Dictionary.CompiledClass WHERE Name = ?",
parameters: [clsname]
};
const pkgrespdata = await makeRESTRequest("POST",1,"/action/query",server,pkgquerydata);
if (pkgrespdata?.data?.result?.content?.length == 1) {
// We got data back
inheritedpackages = pkgrespdata.data.result.content[0].Importall != "" ?
pkgrespdata.data.result.content[0].Importall.replace(/[^\x20-\x7E]/g,'').split(',') : [];
isPersistent = isPersistent || pkgrespdata.data.result.content[0].IsPersistent > 0;
}
}
if (!settings.diagnostics.sqlReserved) isPersistent = false;
}

const firstlineisroutine: boolean =
@@ -257,6 +272,51 @@ export async function onDiagnostics(params: DocumentDiagnosticParams): Promise<D
message: "A package must be specified.",
source: 'InterSystems Language Server'
});
} else if (isPersistent) {
// Check if a SqlTableName is present
let sqlTableName: Range, hasSqlTableName = false;
for (let k = j + 1; k < parsed[i].length; k++) {
if (hasSqlTableName) {
if (parsed[i][k].l == ld.cls_langindex && (
parsed[i][k].s == ld.cls_sqliden_attrindex ||
parsed[i][k].s == ld.error_attrindex || (
parsed[i][k].s == ld.cls_delim_attrindex &&
[",","]"].includes(doc.getText(Range.create(i,parsed[i][k].p,i,parsed[i][k].p+parsed[i][k].c)))
)
)) {
if (parsed[i][k].s == ld.cls_sqliden_attrindex) {
sqlTableName = Range.create(i,parsed[i][k].p,i,parsed[i][k].p+parsed[i][k].c);
}
break;
}
} else if (
parsed[i][k].l == ld.cls_langindex &&
parsed[i][k].s == ld.cls_keyword_attrindex &&
doc.getText(Range.create(i,parsed[i][k].p,i,parsed[i][k].p+parsed[i][k].c)).toLowerCase() == "sqltablename"
) {
hasSqlTableName = true;
}
}
if (hasSqlTableName) {
if (sqlTableName && sqlReservedWords.includes(doc.getText(sqlTableName).toUpperCase())) {
// The SqlTableName is a reserved word
diagnostics.push({
severity: DiagnosticSeverity.Warning,
range: sqlTableName,
message: "SqlTableName is a SQL reserved word",
source: 'InterSystems Language Server'
});
}
} else if (sqlReservedWords.includes(word.split(".").pop().toUpperCase())) {
// The short class name is a reserved word, and it's not corrected by a SqlTableName
wordrange.start.character = wordrange.end.character - word.split(".").pop().length;
diagnostics.push({
severity: DiagnosticSeverity.Warning,
range: wordrange,
message: "Class name is a SQL reserved word",
source: 'InterSystems Language Server'
});
}
}
}
else if (
@@ -903,6 +963,84 @@ export async function onDiagnostics(params: DocumentDiagnosticParams): Promise<D
}
}
}
else if (
j == 0 && parsed[i][j].l == ld.cls_langindex && parsed[i][j].s == ld.cls_keyword_attrindex &&
isPersistent && parsed[i].length > 2 && (
doc.getText(Range.create(i,0,i,8)).toLowerCase() == "property" ||
doc.getText(Range.create(i,0,i,12)).toLowerCase() == "relationship"
)
) {
// This is the start of a UDL Property definition

const propRange = Range.create(i,parsed[i][1].p,i,parsed[i][1].p+parsed[i][1].c);
const propName = quoteUDLIdentifier(doc.getText(propRange),0);

// Check if a SqlFieldName is present
let sqlFieldName: Range, hasSqlFieldName = false, inKeywords = false, brk = false;
for (let ln = i; ln < parsed.length; ln++) {
if (
ln != i && parsed[ln]?.length && parsed[ln][0].l == ld.cls_langindex && (
parsed[ln][0].s == ld.cls_desc_attrindex || (
parsed[ln][0].s == ld.cls_keyword_attrindex &&
isClassMember(doc.getText(Range.create(ln,parsed[ln][0].p,ln,parsed[ln][0].p+parsed[ln][0].c)))
)
)
) {
// This is the start of the next class member
break;
}
for (let k = 0; k < parsed[ln].length; k++) {
if (hasSqlFieldName) {
if (parsed[ln][k].l == ld.cls_langindex && (
parsed[ln][k].s == ld.cls_sqliden_attrindex ||
parsed[ln][k].s == ld.error_attrindex || (
parsed[ln][k].s == ld.cls_delim_attrindex &&
inKeywords &&
[",","]"].includes(doc.getText(Range.create(ln,parsed[ln][k].p,ln,parsed[ln][k].p+parsed[ln][k].c)))
)
)) {
if (parsed[ln][k].s == ld.cls_sqliden_attrindex) {
sqlFieldName = Range.create(ln,parsed[ln][k].p,ln,parsed[ln][k].p+parsed[ln][k].c);
}
brk = true;
break;
}
} else if (
parsed[ln][k].l == ld.cls_langindex &&
parsed[ln][k].s == ld.cls_keyword_attrindex &&
doc.getText(Range.create(ln,parsed[ln][k].p,ln,parsed[ln][k].p+parsed[ln][k].c)).toLowerCase() == "sqlfieldname"
) {
hasSqlFieldName = true;
} else if (
parsed[ln][k].l == ld.cls_langindex &&
parsed[ln][k].s == ld.cls_delim_attrindex &&
doc.getText(Range.create(ln,parsed[ln][k].p,ln,parsed[ln][k].p+parsed[ln][k].c)) == "["
) {
inKeywords = true;
}
}
if (brk) break;
}
if (hasSqlFieldName) {
if (sqlFieldName && sqlReservedWords.includes(doc.getText(sqlFieldName).toUpperCase())) {
// The SqlFieldName is a reserved word
diagnostics.push({
severity: DiagnosticSeverity.Warning,
range: sqlFieldName,
message: "SqlFieldName is a SQL reserved word",
source: 'InterSystems Language Server'
});
}
} else if (sqlReservedWords.includes(propName.toUpperCase())) {
// The property name is a reserved word, and it's not corrected by a SqlFieldName
diagnostics.push({
severity: DiagnosticSeverity.Warning,
range: propRange,
message: "Property name is a SQL reserved word",
source: 'InterSystems Language Server'
});
}
}
if (nsNestedBlockLevel > 0) {
// Determine if we're still in the different namespace

15 changes: 3 additions & 12 deletions server/src/providers/documentSymbol.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DocumentSymbol, DocumentSymbolParams, Position, SymbolKind, Range } from 'vscode-languageserver/node';
import { findFullRange, getParsedDocument, labelIsProcedureBlock } from '../utils/functions';
import { findFullRange, getParsedDocument, isClassMember, labelIsProcedureBlock } from '../utils/functions';
import { documents } from '../utils/variables';
import * as ld from '../utils/languageDefinitions';

@@ -13,15 +13,6 @@ export async function onDocumentSymbol(params: DocumentSymbolParams) {
if (doc.languageId === "objectscript-class") {
// Loop through the file and look for the class definition and class members

const isValidKeyword = (keyword: string): boolean => {
return (
keyword !== "import" &&
keyword.indexOf("include") === -1 &&
keyword !== "byref" &&
keyword !== "byval" &&
keyword !== "output"
);
};
var cls: DocumentSymbol = {
name: "",
kind: SymbolKind.Class,
@@ -56,7 +47,7 @@ export async function onDocumentSymbol(params: DocumentSymbolParams) {
cls.name = doc.getText(cls.selectionRange);
cls.range = Range.create(Position.create(line,0),Position.create(lastnonempty,parsed[lastnonempty][parsed[lastnonempty].length-1].p+parsed[lastnonempty][parsed[lastnonempty].length-1].c));
}
else if (isValidKeyword(keywordtextlower)) {
else if (isClassMember(keywordtextlower)) {
// This is a class member definition

// Loop through the file from this line to find the next class member
@@ -71,7 +62,7 @@ export async function onDocumentSymbol(params: DocumentSymbolParams) {
}
if (parsed[nl][0].s === ld.cls_keyword_attrindex) {
const nextkeytext = doc.getText(Range.create(nl,parsed[nl][0].p,nl,parsed[nl][0].p+parsed[nl][0].c)).toLowerCase();
if (isValidKeyword(nextkeytext)) {
if (isClassMember(nextkeytext)) {
break;
}
}
8 changes: 7 additions & 1 deletion server/src/utils/functions.ts
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import { URI } from 'vscode-uri';
import { parse } from 'node-html-parser';

import { ServerSpec, StudioOpenDialogFile, QueryData, compressedline, CommandDoc, LanguageServerConfiguration, MacroContext, DimResult, PossibleClasses, ClassMemberContext } from './types';
import { parsedDocuments, connection, serverSpecs, languageServerSettings, documents } from './variables';
import { parsedDocuments, connection, serverSpecs, languageServerSettings, documents, classMemberTypes } from './variables';
import * as ld from './languageDefinitions';

import commands = require("../documentation/commands.json");
@@ -2337,3 +2337,9 @@ export async function determineVariableClass(
}
return varClass;
}

/** Returns `true` if `keyword` is a valid class member type. */
export function isClassMember(keyword: string): boolean {
const keywordUpper = keyword.toUpperCase();
return classMemberTypes.some(t => t.toUpperCase() == keywordUpper);
}
1 change: 1 addition & 0 deletions server/src/utils/languageDefinitions.ts
Original file line number Diff line number Diff line change
@@ -67,6 +67,7 @@ export const cls_delim_attrindex = 0x08;
export const cls_num_attrindex = 0x09;
export const cls_str_attrindex = 0x0A;
export const cls_iden_attrindex = 0x0B;
export const cls_sqliden_attrindex = 0x0C;
export const cls_rtnname_attrindex = 0x0D;
export const cls_cparam_attrindex = 0x17;
export const cls_param_attrindex = 0x18;
3 changes: 2 additions & 1 deletion server/src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -28,7 +28,8 @@ export type LanguageServerConfiguration = {
classes: boolean,
deprecation: boolean,
zutil: boolean,
suppressSyntaxErrors: ("COS" | "SQL" | "CLS" | "HTML" | "PYTHON" | "XML" | "JAVA" | "JAVASCRIPT" | "CSS")[]
suppressSyntaxErrors: ("COS" | "SQL" | "CLS" | "HTML" | "PYTHON" | "XML" | "JAVA" | "JAVASCRIPT" | "CSS")[],
sqlReserved: boolean
},
signaturehelp: {
documentation: boolean
3 changes: 3 additions & 0 deletions server/src/utils/variables.ts
Original file line number Diff line number Diff line change
@@ -133,3 +133,6 @@ export const lexerLanguages: { moniker: string; index: number; }[] = [
{ moniker: 'SQL', index: 2 },
{ moniker: 'PYTHON', index: 7 }
];

/** All class member types */
export const classMemberTypes: string[] = ["Parameter","Property","Relationship","ForeignKey","Index","Query","Storage","Trigger","XData","Projection","Method","ClassMethod","ClientMethod"];
1 change: 1 addition & 0 deletions server/tsconfig.json
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
"moduleResolution": "node",
"sourceMap": true,
"strict": true,
"strictNullChecks": false,
"outDir": "out",
"rootDir": "src",
"noImplicitAny": false,

0 comments on commit af99740

Please sign in to comment.