From a790a92f7ddf68c3415656855da30bc6aa29899c Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 30 Jan 2018 10:53:54 -0800 Subject: [PATCH] Parse all json values at root --- src/compiler/checker.ts | 2 +- src/compiler/commandLineParser.ts | 136 +++++++++-------- src/compiler/parser.ts | 42 ++++-- src/compiler/program.ts | 5 +- src/compiler/types.ts | 131 +++++++++-------- src/compiler/utilities.ts | 7 + src/harness/unittests/tsconfigParsing.ts | 15 +- .../reference/api/tsserverlibrary.d.ts | 19 ++- tests/baselines/reference/api/typescript.d.ts | 19 ++- .../reference/requireOfJsonFileTypes.js | 85 +++++++++++ .../reference/requireOfJsonFileTypes.symbols | 103 +++++++++++++ .../reference/requireOfJsonFileTypes.types | 139 ++++++++++++++++++ .../cases/compiler/requireOfJsonFileTypes.ts | 49 ++++++ 13 files changed, 594 insertions(+), 158 deletions(-) create mode 100644 tests/baselines/reference/requireOfJsonFileTypes.js create mode 100644 tests/baselines/reference/requireOfJsonFileTypes.symbols create mode 100644 tests/baselines/reference/requireOfJsonFileTypes.types create mode 100644 tests/cases/compiler/requireOfJsonFileTypes.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 249431aa97d..5772e1a2241 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4369,7 +4369,7 @@ namespace ts { if (declaration.kind === SyntaxKind.SourceFile) { Debug.assert(isJsonSourceFile(declaration as SourceFile)); const jsonSourceFile = declaration; - return links.type = jsonSourceFile.statements.length ? checkObjectLiteral(jsonSourceFile.statements[0].expression) : emptyObjectType; + return links.type = jsonSourceFile.statements.length ? checkExpression(jsonSourceFile.statements[0].expression) : emptyObjectType; } if (declaration.kind === SyntaxKind.ExportAssignment) { return links.type = checkExpression((declaration).expression); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index a804b00f0b6..c8e7bf8d19f 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -937,9 +937,9 @@ namespace ts { * Read tsconfig.json file * @param fileName The path to the config file */ - export function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): JsonSourceFile { + export function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): TsConfigSourceFile { const textOrDiagnostic = tryReadFile(fileName, readFile); - return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : { parseDiagnostics: [textOrDiagnostic] }; + return isString(textOrDiagnostic) ? parseJsonText(fileName, textOrDiagnostic) : { parseDiagnostics: [textOrDiagnostic] }; } function tryReadFile(fileName: string, readFile: (path: string) => string | undefined): string | Diagnostic { @@ -957,58 +957,62 @@ namespace ts { return arrayToMap(options, option => option.name); } - let _tsconfigRootOptions: Map; + let _tsconfigRootOptions: TsConfigOnlyOption; function getTsconfigRootOptionsMap() { if (_tsconfigRootOptions === undefined) { - _tsconfigRootOptions = commandLineOptionsToMap([ - { - name: "compilerOptions", - type: "object", - elementOptions: commandLineOptionsToMap(optionDeclarations), - extraKeyDiagnosticMessage: Diagnostics.Unknown_compiler_option_0 - }, - { - name: "typingOptions", - type: "object", - elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations), - extraKeyDiagnosticMessage: Diagnostics.Unknown_type_acquisition_option_0 - }, - { - name: "typeAcquisition", - type: "object", - elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations), - extraKeyDiagnosticMessage: Diagnostics.Unknown_type_acquisition_option_0 - }, - { - name: "extends", - type: "string" - }, - { - name: "files", - type: "list", - element: { + _tsconfigRootOptions = { + name: undefined, // should never be needed since this is root + type: "object", + elementOptions: commandLineOptionsToMap([ + { + name: "compilerOptions", + type: "object", + elementOptions: commandLineOptionsToMap(optionDeclarations), + extraKeyDiagnosticMessage: Diagnostics.Unknown_compiler_option_0 + }, + { + name: "typingOptions", + type: "object", + elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations), + extraKeyDiagnosticMessage: Diagnostics.Unknown_type_acquisition_option_0 + }, + { + name: "typeAcquisition", + type: "object", + elementOptions: commandLineOptionsToMap(typeAcquisitionDeclarations), + extraKeyDiagnosticMessage: Diagnostics.Unknown_type_acquisition_option_0 + }, + { + name: "extends", + type: "string" + }, + { name: "files", - type: "string" - } - }, - { - name: "include", - type: "list", - element: { + type: "list", + element: { + name: "files", + type: "string" + } + }, + { name: "include", - type: "string" - } - }, - { - name: "exclude", - type: "list", - element: { + type: "list", + element: { + name: "include", + type: "string" + } + }, + { name: "exclude", - type: "string" - } - }, - compileOnSaveCommandLineOption - ]); + type: "list", + element: { + name: "exclude", + type: "string" + } + }, + compileOnSaveCommandLineOption + ]) + }; } return _tsconfigRootOptions; } @@ -1054,14 +1058,17 @@ namespace ts { function convertToObjectWorker( sourceFile: JsonSourceFile, errors: Push, - knownRootOptions: Map | undefined, + knownRootOptions: CommandLineOption | undefined, jsonConversionNotifier: JsonConversionNotifier | undefined): any { if (!sourceFile.statements.length) { return {}; } - return convertObjectLiteralExpressionToJson(sourceFile.statements[0].expression, knownRootOptions, - /*extraKeyDiagnosticMessage*/ undefined, /*parentOption*/ undefined); + return convertPropertyValueToJson(sourceFile.statements[0].expression, knownRootOptions); + + function isRootOptionMap(knownOptions: Map | undefined) { + return knownRootOptions && (knownRootOptions as TsConfigOnlyOption).elementOptions === knownOptions; + } function convertObjectLiteralExpressionToJson( node: ObjectLiteralExpression, @@ -1094,7 +1101,7 @@ namespace ts { // Notify key value set, if user asked for it if (jsonConversionNotifier && // Current callbacks are only on known parent option or if we are setting values in the root - (parentOption || knownOptions === knownRootOptions)) { + (parentOption || isRootOptionMap(knownOptions))) { const isValidOptionValue = isCompilerOptionsValue(option, value); if (parentOption) { if (isValidOptionValue) { @@ -1102,7 +1109,7 @@ namespace ts { jsonConversionNotifier.onSetValidOptionKeyValueInParent(parentOption, option, value); } } - else if (knownOptions === knownRootOptions) { + else if (isRootOptionMap(knownOptions)) { if (isValidOptionValue) { // Notify about the valid root key value being set jsonConversionNotifier.onSetValidOptionKeyValueInRoot(keyText, element.name, value, element.initializer); @@ -1408,12 +1415,12 @@ namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - export function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine { + export function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine { return parseJsonConfigFileContentWorker(/*json*/ undefined, sourceFile, host, basePath, existingOptions, configFileName, resolutionStack, extraFileExtensions); } /*@internal*/ - export function setConfigFileInOptions(options: CompilerOptions, configFile: JsonSourceFile) { + export function setConfigFileInOptions(options: CompilerOptions, configFile: TsConfigSourceFile) { if (configFile) { Object.defineProperty(options, "configFile", { enumerable: false, writable: false, value: configFile }); } @@ -1441,7 +1448,7 @@ namespace ts { */ function parseJsonConfigFileContentWorker( json: any, - sourceFile: JsonSourceFile, + sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions: CompilerOptions = {}, @@ -1562,7 +1569,7 @@ namespace ts { */ function parseConfig( json: any, - sourceFile: JsonSourceFile, + sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, configFileName: string, @@ -1639,7 +1646,7 @@ namespace ts { } function parseOwnConfigOfJsonSourceFile( - sourceFile: JsonSourceFile, + sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, configFileName: string | undefined, @@ -1729,7 +1736,7 @@ namespace ts { } function getExtendedConfig( - sourceFile: JsonSourceFile, + sourceFile: TsConfigSourceFile, extendedConfigPath: string, host: ts.ParseConfigHost, basePath: string, @@ -1981,7 +1988,7 @@ namespace ts { host: ParseConfigHost, errors: Push, extraFileExtensions: ReadonlyArray, - jsonSourceFile: JsonSourceFile + jsonSourceFile: TsConfigSourceFile ): ExpandResult { basePath = normalizePath(basePath); let validatedIncludeSpecs: ReadonlyArray, validatedExcludeSpecs: ReadonlyArray; @@ -2083,7 +2090,7 @@ namespace ts { }; } - function validateSpecs(specs: ReadonlyArray, errors: Push, allowTrailingRecursion: boolean, jsonSourceFile: JsonSourceFile, specKey: string): ReadonlyArray { + function validateSpecs(specs: ReadonlyArray, errors: Push, allowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile, specKey: string): ReadonlyArray { return specs.filter(spec => { const diag = specToDiagnostic(spec, allowTrailingRecursion); if (diag !== undefined) { @@ -2093,8 +2100,9 @@ namespace ts { }); function createDiagnostic(message: DiagnosticMessage, spec: string): Diagnostic { - if (jsonSourceFile && jsonSourceFile.statements.length) { - for (const property of getPropertyAssignment(jsonSourceFile.statements[0].expression, specKey)) { + const jsonObjectLiteral = getTsConfigObjectLiteralExpression(jsonSourceFile); + if (jsonObjectLiteral) { + for (const property of getPropertyAssignment(jsonObjectLiteral, specKey)) { if (isArrayLiteralExpression(property.initializer)) { for (const element of property.initializer.elements) { if (isStringLiteral(element) && element.text === spec) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5ce69b1ff4b..774e9d42578 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -697,23 +697,43 @@ namespace ts { nextToken(); const pos = getNodePos(); if (token() === SyntaxKind.EndOfFileToken) { + sourceFile.statements = createNodeArray([], pos, pos); sourceFile.endOfFileToken = parseTokenNode(); } - else if (token() === SyntaxKind.OpenBraceToken || - lookAhead(() => token() === SyntaxKind.StringLiteral)) { - const statement = createNode(SyntaxKind.ExpressionStatement) as JsonObjectLiteralExpressionStatement; - statement.expression = parseObjectLiteralExpression(); + else { + const statement = createNode(SyntaxKind.ExpressionStatement) as JsonObjectExpressionStatement; + switch (token()) { + case SyntaxKind.OpenBracketToken: + statement.expression = parseArrayLiteralExpression(); + break; + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + statement.expression = parseTokenNode(); + break; + case SyntaxKind.MinusToken: + if (lookAhead(() => nextToken() === SyntaxKind.NumericLiteral && nextToken() !== SyntaxKind.ColonToken)) { + statement.expression = parsePrefixUnaryExpression() as JsonMinusNumericLiteral; + } + else { + statement.expression = parseObjectLiteralExpression(); + } + break; + case SyntaxKind.NumericLiteral: + case SyntaxKind.StringLiteral: + if (lookAhead(() => nextToken() !== SyntaxKind.ColonToken)) { + statement.expression = parseLiteralNode() as StringLiteral | NumericLiteral; + break; + } + // falls through + default: + statement.expression = parseObjectLiteralExpression(); + break; + } finishNode(statement); sourceFile.statements = createNodeArray([statement], pos); sourceFile.endOfFileToken = parseExpectedToken(SyntaxKind.EndOfFileToken, Diagnostics.Unexpected_token); } - else { - parseExpected(SyntaxKind.OpenBraceToken); - } - - if (!sourceFile.statements) { - sourceFile.statements = createNodeArray([], pos, pos); - } if (setParentNodes) { fixupParentReferences(sourceFile); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7c7b480deff..362aa258bb8 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2333,8 +2333,9 @@ namespace ts { function getCompilerOptionsObjectLiteralSyntax() { if (_compilerOptionsObjectLiteralSyntax === undefined) { _compilerOptionsObjectLiteralSyntax = null; // tslint:disable-line:no-null-keyword - if (options.configFile && options.configFile.statements.length) { - for (const prop of getPropertyAssignment(options.configFile.statements[0].expression, "compilerOptions")) { + const jsonObjectLiteral = getTsConfigObjectLiteralExpression(options.configFile); + if (jsonObjectLiteral) { + for (const prop of getPropertyAssignment(jsonObjectLiteral, "compilerOptions")) { if (isObjectLiteralExpression(prop.initializer)) { _compilerOptionsObjectLiteralSyntax = prop.initializer; break; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 80adbc1fbc0..4030ae9247a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -44,8 +44,8 @@ namespace ts { /* @internal */ export const enum Comparison { - LessThan = -1, - EqualTo = 0, + LessThan = -1, + EqualTo = 0, GreaterThan = 1 } @@ -458,24 +458,24 @@ namespace ts { } export const enum NodeFlags { - None = 0, - Let = 1 << 0, // Variable declaration - Const = 1 << 1, // Variable declaration - NestedNamespace = 1 << 2, // Namespace declaration - Synthesized = 1 << 3, // Node was synthesized during transformation - Namespace = 1 << 4, // Namespace declaration - ExportContext = 1 << 5, // Export context (initialized by binding) - ContainsThis = 1 << 6, // Interface contains references to "this" - HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding) - HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding) + None = 0, + Let = 1 << 0, // Variable declaration + Const = 1 << 1, // Variable declaration + NestedNamespace = 1 << 2, // Namespace declaration + Synthesized = 1 << 3, // Node was synthesized during transformation + Namespace = 1 << 4, // Namespace declaration + ExportContext = 1 << 5, // Export context (initialized by binding) + ContainsThis = 1 << 6, // Interface contains references to "this" + HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding) + HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding) GlobalAugmentation = 1 << 9, // Set if module declaration is an augmentation for the global scope - HasAsyncFunctions = 1 << 10, // If the file has async functions (initialized by binding) - DisallowInContext = 1 << 11, // If node was parsed in a context where 'in-expressions' are not allowed - YieldContext = 1 << 12, // If node was parsed in the 'yield' context created when parsing a generator - DecoratorContext = 1 << 13, // If node was parsed as part of a decorator - AwaitContext = 1 << 14, // If node was parsed in the 'await' context created when parsing an async function - ThisNodeHasError = 1 << 15, // If the parser encountered an error when parsing the code that created this node - JavaScriptFile = 1 << 16, // If node was parsed in a JavaScript + HasAsyncFunctions = 1 << 10, // If the file has async functions (initialized by binding) + DisallowInContext = 1 << 11, // If node was parsed in a context where 'in-expressions' are not allowed + YieldContext = 1 << 12, // If node was parsed in the 'yield' context created when parsing a generator + DecoratorContext = 1 << 13, // If node was parsed as part of a decorator + AwaitContext = 1 << 14, // If node was parsed in the 'await' context created when parsing an async function + ThisNodeHasError = 1 << 15, // If the parser encountered an error when parsing the code that created this node + JavaScriptFile = 1 << 16, // If node was parsed in a JavaScript ThisNodeOrAnySubNodesHasError = 1 << 17, // If this node or any of its children had an error HasAggregatedChildData = 1 << 18, // If we've computed data from children and cached it in this node @@ -489,8 +489,8 @@ namespace ts { // we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used. /* @internal */ PossiblyContainsDynamicImport = 1 << 19, - JSDoc = 1 << 20, // If node was parsed inside jsdoc - /* @internal */ Ambient = 1 << 21, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. + JSDoc = 1 << 20, // If node was parsed inside jsdoc + /* @internal */ Ambient = 1 << 21, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. /* @internal */ InWithStatement = 1 << 22, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) JsonFile = 1 << 23, // If node was parsed in a Json @@ -507,19 +507,19 @@ namespace ts { } export const enum ModifierFlags { - None = 0, - Export = 1 << 0, // Declarations - Ambient = 1 << 1, // Declarations - Public = 1 << 2, // Property/Method - Private = 1 << 3, // Property/Method - Protected = 1 << 4, // Property/Method - Static = 1 << 5, // Property/Method - Readonly = 1 << 6, // Property/Method - Abstract = 1 << 7, // Class/Method/ConstructSignature - Async = 1 << 8, // Property/Method/Function - Default = 1 << 9, // Function/Class (export default declaration) - Const = 1 << 11, // Variable declaration - HasComputedFlags = 1 << 29, // Modifier flags have been computed + None = 0, + Export = 1 << 0, // Declarations + Ambient = 1 << 1, // Declarations + Public = 1 << 2, // Property/Method + Private = 1 << 3, // Property/Method + Protected = 1 << 4, // Property/Method + Static = 1 << 5, // Property/Method + Readonly = 1 << 6, // Property/Method + Abstract = 1 << 7, // Class/Method/ConstructSignature + Async = 1 << 8, // Property/Method/Function + Default = 1 << 9, // Function/Class (export default declaration) + Const = 1 << 11, // Variable declaration + HasComputedFlags = 1 << 29, // Modifier flags have been computed AccessibilityModifier = Public | Private | Protected, // Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property. @@ -1050,16 +1050,16 @@ namespace ts { export interface KeywordTypeNode extends TypeNode { kind: SyntaxKind.AnyKeyword - | SyntaxKind.NumberKeyword - | SyntaxKind.ObjectKeyword - | SyntaxKind.BooleanKeyword - | SyntaxKind.StringKeyword - | SyntaxKind.SymbolKeyword - | SyntaxKind.ThisKeyword - | SyntaxKind.VoidKeyword - | SyntaxKind.UndefinedKeyword - | SyntaxKind.NullKeyword - | SyntaxKind.NeverKeyword; + | SyntaxKind.NumberKeyword + | SyntaxKind.ObjectKeyword + | SyntaxKind.BooleanKeyword + | SyntaxKind.StringKeyword + | SyntaxKind.SymbolKeyword + | SyntaxKind.ThisKeyword + | SyntaxKind.VoidKeyword + | SyntaxKind.UndefinedKeyword + | SyntaxKind.NullKeyword + | SyntaxKind.NeverKeyword; } export interface ThisTypeNode extends TypeNode { @@ -2376,19 +2376,19 @@ namespace ts { } export const enum FlowFlags { - Unreachable = 1 << 0, // Unreachable code - Start = 1 << 1, // Start of flow graph - BranchLabel = 1 << 2, // Non-looping junction - LoopLabel = 1 << 3, // Looping junction - Assignment = 1 << 4, // Assignment - TrueCondition = 1 << 5, // Condition known to be true + Unreachable = 1 << 0, // Unreachable code + Start = 1 << 1, // Start of flow graph + BranchLabel = 1 << 2, // Non-looping junction + LoopLabel = 1 << 3, // Looping junction + Assignment = 1 << 4, // Assignment + TrueCondition = 1 << 5, // Condition known to be true FalseCondition = 1 << 6, // Condition known to be false - SwitchClause = 1 << 7, // Switch statement clause - ArrayMutation = 1 << 8, // Potential array mutation - Referenced = 1 << 9, // Referenced as antecedent once - Shared = 1 << 10, // Referenced as antecedent more than once - PreFinally = 1 << 11, // Injected edge that links pre-finally label and pre-try flow - AfterFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph + SwitchClause = 1 << 7, // Switch statement clause + ArrayMutation = 1 << 8, // Potential array mutation + Referenced = 1 << 9, // Referenced as antecedent once + Shared = 1 << 10, // Referenced as antecedent more than once + PreFinally = 1 << 11, // Injected edge that links pre-finally label and pre-try flow + AfterFinally = 1 << 12, // Injected edge that links post-finally flow with the rest of the graph Label = BranchLabel | LoopLabel, Condition = TrueCondition | FalseCondition } @@ -2577,12 +2577,21 @@ namespace ts { } export interface JsonSourceFile extends SourceFile { - statements: NodeArray; + statements: NodeArray; + } + + export interface TsConfigSourceFile extends JsonSourceFile { extendedSourceFiles?: string[]; } - export interface JsonObjectLiteralExpressionStatement extends ExpressionStatement { - expression: ObjectLiteralExpression; + export interface JsonMinusNumericLiteral extends PrefixUnaryExpression { + kind: SyntaxKind.PrefixUnaryExpression; + operator: SyntaxKind.MinusToken; + operand: NumericLiteral; + } + + export interface JsonObjectExpressionStatement extends ExpressionStatement { + expression: ObjectLiteralExpression | ArrayLiteralExpression | JsonMinusNumericLiteral | NumericLiteral | StringLiteral | BooleanLiteral | NullLiteral; } export interface ScriptReferenceHost { @@ -4042,7 +4051,7 @@ namespace ts { checkJs?: boolean; /* @internal */ configFilePath?: string; /** configFile is set as non enumerable property so as to avoid checking of json source files */ - /* @internal */ readonly configFile?: JsonSourceFile; + /* @internal */ readonly configFile?: TsConfigSourceFile; declaration?: boolean; emitDeclarationOnly?: boolean; declarationDir?: string; @@ -4120,7 +4129,7 @@ namespace ts { /*@internal*/ watch?: boolean; esModuleInterop?: boolean; - [option: string]: CompilerOptionsValue | JsonSourceFile | undefined; + [option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined; } export interface TypeAcquisition { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 63a02d8d7b8..084258d5392 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1040,6 +1040,13 @@ namespace ts { }); } + export function getTsConfigObjectLiteralExpression(tsConfigSourceFile: TsConfigSourceFile) { + if (tsConfigSourceFile && tsConfigSourceFile.statements.length) { + const expression = tsConfigSourceFile.statements[0].expression; + return isObjectLiteralExpression(expression) && expression; + } + } + export function getContainingFunction(node: Node): FunctionLike { return findAncestor(node.parent, isFunctionLike); } diff --git a/src/harness/unittests/tsconfigParsing.ts b/src/harness/unittests/tsconfigParsing.ts index 8d56360bba5..54e5d5a1647 100644 --- a/src/harness/unittests/tsconfigParsing.ts +++ b/src/harness/unittests/tsconfigParsing.ts @@ -8,12 +8,6 @@ namespace ts { assert.equal(JSON.stringify(parsed), JSON.stringify(expectedConfigObject)); } - function assertParseError(jsonText: string) { - const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText); - assert.deepEqual(parsed.config, {}); - assert.isTrue(undefined !== parsed.error); - } - function assertParseErrorWithExcludesKeyword(jsonText: string) { { const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText); @@ -134,7 +128,14 @@ namespace ts { }); it("returns object with error when json is invalid", () => { - assertParseError("invalid"); + const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", "invalid"); + assert.deepEqual(parsed.config, { invalid: undefined }); + const expected = ts.createCompilerDiagnostic(ts.Diagnostics._0_expected, "{"); + assert.equal(parsed.error.messageText, expected.messageText); + assert.equal(parsed.error.category, expected.category); + assert.equal(parsed.error.code, expected.code); + assert.equal(parsed.error.start, 0); + assert.equal(parsed.error.length, "invalid".length); }); it("returns object when users correctly specify library", () => { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 1abd2401a03..1c2bbee3a11 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1632,11 +1632,18 @@ declare namespace ts { sourceFiles: ReadonlyArray; } interface JsonSourceFile extends SourceFile { - statements: NodeArray; + statements: NodeArray; + } + interface TsConfigSourceFile extends JsonSourceFile { extendedSourceFiles?: string[]; } - interface JsonObjectLiteralExpressionStatement extends ExpressionStatement { - expression: ObjectLiteralExpression; + interface JsonMinusNumericLiteral extends PrefixUnaryExpression { + kind: SyntaxKind.PrefixUnaryExpression; + operator: SyntaxKind.MinusToken; + operand: NumericLiteral; + } + interface JsonObjectExpressionStatement extends ExpressionStatement { + expression: ObjectLiteralExpression | ArrayLiteralExpression | JsonMinusNumericLiteral | NumericLiteral | StringLiteral | BooleanLiteral | NullLiteral; } interface ScriptReferenceHost { getCompilerOptions(): CompilerOptions; @@ -2378,7 +2385,7 @@ declare namespace ts { /** Paths used to compute primary types search locations */ typeRoots?: string[]; esModuleInterop?: boolean; - [option: string]: CompilerOptionsValue | JsonSourceFile | undefined; + [option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined; } interface TypeAcquisition { enableAutoDiscovery?: boolean; @@ -3365,7 +3372,7 @@ declare namespace ts { * Read tsconfig.json file * @param fileName The path to the config file */ - function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): JsonSourceFile; + function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): TsConfigSourceFile; /** * Convert the json syntax tree into the json value */ @@ -3385,7 +3392,7 @@ declare namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; + function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions; errors: Diagnostic[]; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index d766ef8a5e6..7dffe561824 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1632,11 +1632,18 @@ declare namespace ts { sourceFiles: ReadonlyArray; } interface JsonSourceFile extends SourceFile { - statements: NodeArray; + statements: NodeArray; + } + interface TsConfigSourceFile extends JsonSourceFile { extendedSourceFiles?: string[]; } - interface JsonObjectLiteralExpressionStatement extends ExpressionStatement { - expression: ObjectLiteralExpression; + interface JsonMinusNumericLiteral extends PrefixUnaryExpression { + kind: SyntaxKind.PrefixUnaryExpression; + operator: SyntaxKind.MinusToken; + operand: NumericLiteral; + } + interface JsonObjectExpressionStatement extends ExpressionStatement { + expression: ObjectLiteralExpression | ArrayLiteralExpression | JsonMinusNumericLiteral | NumericLiteral | StringLiteral | BooleanLiteral | NullLiteral; } interface ScriptReferenceHost { getCompilerOptions(): CompilerOptions; @@ -2378,7 +2385,7 @@ declare namespace ts { /** Paths used to compute primary types search locations */ typeRoots?: string[]; esModuleInterop?: boolean; - [option: string]: CompilerOptionsValue | JsonSourceFile | undefined; + [option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined; } interface TypeAcquisition { enableAutoDiscovery?: boolean; @@ -4171,7 +4178,7 @@ declare namespace ts { * Read tsconfig.json file * @param fileName The path to the config file */ - function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): JsonSourceFile; + function readJsonConfigFile(fileName: string, readFile: (path: string) => string | undefined): TsConfigSourceFile; /** * Convert the json syntax tree into the json value */ @@ -4191,7 +4198,7 @@ declare namespace ts { * @param basePath A root directory to resolve relative path entries in the config * file to. e.g. outDir */ - function parseJsonSourceFileConfigFileContent(sourceFile: JsonSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; + function parseJsonSourceFileConfigFileContent(sourceFile: TsConfigSourceFile, host: ParseConfigHost, basePath: string, existingOptions?: CompilerOptions, configFileName?: string, resolutionStack?: Path[], extraFileExtensions?: ReadonlyArray): ParsedCommandLine; function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions; errors: Diagnostic[]; diff --git a/tests/baselines/reference/requireOfJsonFileTypes.js b/tests/baselines/reference/requireOfJsonFileTypes.js new file mode 100644 index 00000000000..76de6bca9f8 --- /dev/null +++ b/tests/baselines/reference/requireOfJsonFileTypes.js @@ -0,0 +1,85 @@ +//// [tests/cases/compiler/requireOfJsonFileTypes.ts] //// + +//// [file1.ts] +import b = require('./b.json'); +import c = require('./c.json'); +import d = require('./d.json'); +import e = require('./e.json'); +import f = require('./f.json'); +import g = require('./g.json'); + +let booleanLiteral: boolean, nullLiteral: null; +let stringLiteral: string; +let numberLiteral: number; + +booleanLiteral = b.a; +stringLiteral = b.b; +nullLiteral = b.c; +booleanLiteral = b.d; +const stringOrNumberOrNull: string | number | null = c[0]; +stringLiteral = d; +numberLiteral = e; +numberLiteral = f[0]; +booleanLiteral = g[0]; + +//// [b.json] +{ + "a": true, + "b": "hello", + "c": null, + "d": false +} + +//// [c.json] +["a", null, "string"] + +//// [d.json] +"dConfig" + +//// [e.json] +-10 + +//// [f.json] +[-10, 30] + +//// [g.json] +[true, false] + +//// [b.json] +{ + "a": true, + "b": "hello", + "c": null, + "d": false +} +//// [c.json] +["a", null, "string"] +//// [d.json] +"dConfig" +//// [e.json] +-10 +//// [f.json] +[-10, 30] +//// [g.json] +[true, false] +//// [file1.js] +"use strict"; +exports.__esModule = true; +var b = require("./b.json"); +var c = require("./c.json"); +var d = require("./d.json"); +var e = require("./e.json"); +var f = require("./f.json"); +var g = require("./g.json"); +var booleanLiteral, nullLiteral; +var stringLiteral; +var numberLiteral; +booleanLiteral = b.a; +stringLiteral = b.b; +nullLiteral = b.c; +booleanLiteral = b.d; +var stringOrNumberOrNull = c[0]; +stringLiteral = d; +numberLiteral = e; +numberLiteral = f[0]; +booleanLiteral = g[0]; diff --git a/tests/baselines/reference/requireOfJsonFileTypes.symbols b/tests/baselines/reference/requireOfJsonFileTypes.symbols new file mode 100644 index 00000000000..01b0f4a1dcc --- /dev/null +++ b/tests/baselines/reference/requireOfJsonFileTypes.symbols @@ -0,0 +1,103 @@ +=== tests/cases/compiler/file1.ts === +import b = require('./b.json'); +>b : Symbol(b, Decl(file1.ts, 0, 0)) + +import c = require('./c.json'); +>c : Symbol(c, Decl(file1.ts, 0, 31)) + +import d = require('./d.json'); +>d : Symbol(d, Decl(file1.ts, 1, 31)) + +import e = require('./e.json'); +>e : Symbol(e, Decl(file1.ts, 2, 31)) + +import f = require('./f.json'); +>f : Symbol(f, Decl(file1.ts, 3, 31)) + +import g = require('./g.json'); +>g : Symbol(g, Decl(file1.ts, 4, 31)) + +let booleanLiteral: boolean, nullLiteral: null; +>booleanLiteral : Symbol(booleanLiteral, Decl(file1.ts, 7, 3)) +>nullLiteral : Symbol(nullLiteral, Decl(file1.ts, 7, 28)) + +let stringLiteral: string; +>stringLiteral : Symbol(stringLiteral, Decl(file1.ts, 8, 3)) + +let numberLiteral: number; +>numberLiteral : Symbol(numberLiteral, Decl(file1.ts, 9, 3)) + +booleanLiteral = b.a; +>booleanLiteral : Symbol(booleanLiteral, Decl(file1.ts, 7, 3)) +>b.a : Symbol("a", Decl(b.json, 0, 1)) +>b : Symbol(b, Decl(file1.ts, 0, 0)) +>a : Symbol("a", Decl(b.json, 0, 1)) + +stringLiteral = b.b; +>stringLiteral : Symbol(stringLiteral, Decl(file1.ts, 8, 3)) +>b.b : Symbol("b", Decl(b.json, 1, 14)) +>b : Symbol(b, Decl(file1.ts, 0, 0)) +>b : Symbol("b", Decl(b.json, 1, 14)) + +nullLiteral = b.c; +>nullLiteral : Symbol(nullLiteral, Decl(file1.ts, 7, 28)) +>b.c : Symbol("c", Decl(b.json, 2, 17)) +>b : Symbol(b, Decl(file1.ts, 0, 0)) +>c : Symbol("c", Decl(b.json, 2, 17)) + +booleanLiteral = b.d; +>booleanLiteral : Symbol(booleanLiteral, Decl(file1.ts, 7, 3)) +>b.d : Symbol("d", Decl(b.json, 3, 14)) +>b : Symbol(b, Decl(file1.ts, 0, 0)) +>d : Symbol("d", Decl(b.json, 3, 14)) + +const stringOrNumberOrNull: string | number | null = c[0]; +>stringOrNumberOrNull : Symbol(stringOrNumberOrNull, Decl(file1.ts, 15, 5)) +>c : Symbol(c, Decl(file1.ts, 0, 31)) + +stringLiteral = d; +>stringLiteral : Symbol(stringLiteral, Decl(file1.ts, 8, 3)) +>d : Symbol(d, Decl(file1.ts, 1, 31)) + +numberLiteral = e; +>numberLiteral : Symbol(numberLiteral, Decl(file1.ts, 9, 3)) +>e : Symbol(e, Decl(file1.ts, 2, 31)) + +numberLiteral = f[0]; +>numberLiteral : Symbol(numberLiteral, Decl(file1.ts, 9, 3)) +>f : Symbol(f, Decl(file1.ts, 3, 31)) + +booleanLiteral = g[0]; +>booleanLiteral : Symbol(booleanLiteral, Decl(file1.ts, 7, 3)) +>g : Symbol(g, Decl(file1.ts, 4, 31)) + +=== tests/cases/compiler/b.json === +{ + "a": true, +>"a" : Symbol("a", Decl(b.json, 0, 1)) + + "b": "hello", +>"b" : Symbol("b", Decl(b.json, 1, 14)) + + "c": null, +>"c" : Symbol("c", Decl(b.json, 2, 17)) + + "d": false +>"d" : Symbol("d", Decl(b.json, 3, 14)) +} + +=== tests/cases/compiler/c.json === +["a", null, "string"] +No type information for this code. +No type information for this code.=== tests/cases/compiler/d.json === +"dConfig" +No type information for this code. +No type information for this code.=== tests/cases/compiler/e.json === +-10 +No type information for this code. +No type information for this code.=== tests/cases/compiler/f.json === +[-10, 30] +No type information for this code. +No type information for this code.=== tests/cases/compiler/g.json === +[true, false] +No type information for this code. \ No newline at end of file diff --git a/tests/baselines/reference/requireOfJsonFileTypes.types b/tests/baselines/reference/requireOfJsonFileTypes.types new file mode 100644 index 00000000000..d9cd38cd58f --- /dev/null +++ b/tests/baselines/reference/requireOfJsonFileTypes.types @@ -0,0 +1,139 @@ +=== tests/cases/compiler/file1.ts === +import b = require('./b.json'); +>b : { "a": boolean; "b": string; "c": null; "d": boolean; } + +import c = require('./c.json'); +>c : (string | null)[] + +import d = require('./d.json'); +>d : "dConfig" + +import e = require('./e.json'); +>e : -10 + +import f = require('./f.json'); +>f : number[] + +import g = require('./g.json'); +>g : boolean[] + +let booleanLiteral: boolean, nullLiteral: null; +>booleanLiteral : boolean +>nullLiteral : null +>null : null + +let stringLiteral: string; +>stringLiteral : string + +let numberLiteral: number; +>numberLiteral : number + +booleanLiteral = b.a; +>booleanLiteral = b.a : boolean +>booleanLiteral : boolean +>b.a : boolean +>b : { "a": boolean; "b": string; "c": null; "d": boolean; } +>a : boolean + +stringLiteral = b.b; +>stringLiteral = b.b : string +>stringLiteral : string +>b.b : string +>b : { "a": boolean; "b": string; "c": null; "d": boolean; } +>b : string + +nullLiteral = b.c; +>nullLiteral = b.c : null +>nullLiteral : null +>b.c : null +>b : { "a": boolean; "b": string; "c": null; "d": boolean; } +>c : null + +booleanLiteral = b.d; +>booleanLiteral = b.d : boolean +>booleanLiteral : boolean +>b.d : boolean +>b : { "a": boolean; "b": string; "c": null; "d": boolean; } +>d : boolean + +const stringOrNumberOrNull: string | number | null = c[0]; +>stringOrNumberOrNull : string | number | null +>null : null +>c[0] : string | null +>c : (string | null)[] +>0 : 0 + +stringLiteral = d; +>stringLiteral = d : "dConfig" +>stringLiteral : string +>d : "dConfig" + +numberLiteral = e; +>numberLiteral = e : -10 +>numberLiteral : number +>e : -10 + +numberLiteral = f[0]; +>numberLiteral = f[0] : number +>numberLiteral : number +>f[0] : number +>f : number[] +>0 : 0 + +booleanLiteral = g[0]; +>booleanLiteral = g[0] : boolean +>booleanLiteral : boolean +>g[0] : boolean +>g : boolean[] +>0 : 0 + +=== tests/cases/compiler/b.json === +{ +>{ "a": true, "b": "hello", "c": null, "d": false} : { "a": boolean; "b": string; "c": null; "d": boolean; } + + "a": true, +>"a" : boolean +>true : true + + "b": "hello", +>"b" : string +>"hello" : "hello" + + "c": null, +>"c" : null +>null : null + + "d": false +>"d" : boolean +>false : false +} + +=== tests/cases/compiler/c.json === +["a", null, "string"] +>["a", null, "string"] : (string | null)[] +>"a" : "a" +>null : null +>"string" : "string" + +=== tests/cases/compiler/d.json === +"dConfig" +>"dConfig" : "dConfig" + +=== tests/cases/compiler/e.json === +-10 +>-10 : -10 +>10 : 10 + +=== tests/cases/compiler/f.json === +[-10, 30] +>[-10, 30] : number[] +>-10 : -10 +>10 : 10 +>30 : 30 + +=== tests/cases/compiler/g.json === +[true, false] +>[true, false] : boolean[] +>true : true +>false : false + diff --git a/tests/cases/compiler/requireOfJsonFileTypes.ts b/tests/cases/compiler/requireOfJsonFileTypes.ts new file mode 100644 index 00000000000..93637909919 --- /dev/null +++ b/tests/cases/compiler/requireOfJsonFileTypes.ts @@ -0,0 +1,49 @@ +// @module: commonjs +// @outdir: out/ +// @allowJs: true +// @strictNullChecks: true + +// @Filename: file1.ts +import b = require('./b.json'); +import c = require('./c.json'); +import d = require('./d.json'); +import e = require('./e.json'); +import f = require('./f.json'); +import g = require('./g.json'); + +let booleanLiteral: boolean, nullLiteral: null; +let stringLiteral: string; +let numberLiteral: number; + +booleanLiteral = b.a; +stringLiteral = b.b; +nullLiteral = b.c; +booleanLiteral = b.d; +const stringOrNumberOrNull: string | number | null = c[0]; +stringLiteral = d; +numberLiteral = e; +numberLiteral = f[0]; +booleanLiteral = g[0]; + +// @Filename: b.json +{ + "a": true, + "b": "hello", + "c": null, + "d": false +} + +// @Filename: c.json +["a", null, "string"] + +// @Filename: d.json +"dConfig" + +// @Filename: e.json +-10 + +// @Filename: f.json +[-10, 30] + +// @Filename: g.json +[true, false] \ No newline at end of file