1323 lines
37 KiB
Go
1323 lines
37 KiB
Go
package tsoptions_test
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnosticwriter"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsonutil"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/baseline"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions/tsoptionstest"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/osvfs"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"gotest.tools/v3/assert"
|
|
)
|
|
|
|
type testConfig struct {
|
|
jsonText string
|
|
configFileName string
|
|
basePath string
|
|
allFileList map[string]string
|
|
}
|
|
|
|
var parseConfigFileTextToJsonTests = []struct {
|
|
title string
|
|
input []string
|
|
}{
|
|
{
|
|
title: "returns empty config for file with only whitespaces",
|
|
input: []string{
|
|
"",
|
|
" ",
|
|
},
|
|
},
|
|
{
|
|
title: "returns empty config for file with comments only",
|
|
input: []string{
|
|
"// Comment",
|
|
"/* Comment*/",
|
|
},
|
|
},
|
|
{
|
|
title: "returns empty config when config is empty object",
|
|
input: []string{
|
|
`{}`,
|
|
},
|
|
},
|
|
{
|
|
title: "returns config object without comments",
|
|
input: []string{
|
|
`{ // Excluded files
|
|
"exclude": [
|
|
// Exclude d.ts
|
|
"file.d.ts"
|
|
]
|
|
}`,
|
|
`{
|
|
/* Excluded
|
|
Files
|
|
*/
|
|
"exclude": [
|
|
/* multiline comments can be in the middle of a line */"file.d.ts"
|
|
]
|
|
}`,
|
|
},
|
|
},
|
|
{
|
|
title: "keeps string content untouched",
|
|
input: []string{
|
|
`{
|
|
"exclude": [
|
|
"xx//file.d.ts"
|
|
]
|
|
}`,
|
|
`{
|
|
"exclude": [
|
|
"xx/*file.d.ts*/"
|
|
]
|
|
}`,
|
|
},
|
|
},
|
|
{
|
|
title: "handles escaped characters in strings correctly",
|
|
input: []string{
|
|
`{
|
|
"exclude": [
|
|
"xx\"//files"
|
|
]
|
|
}`,
|
|
`{
|
|
"exclude": [
|
|
"xx\\" // end of line comment
|
|
]
|
|
}`,
|
|
},
|
|
},
|
|
{
|
|
title: "returns object when users correctly specify library",
|
|
input: []string{
|
|
`{
|
|
"compilerOptions": {
|
|
"lib": ["es5"]
|
|
}
|
|
}`,
|
|
`{
|
|
"compilerOptions": {
|
|
"lib": ["es5", "es6"]
|
|
}
|
|
}`,
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestParseConfigFileTextToJson(t *testing.T) {
|
|
t.Parallel()
|
|
repo.SkipIfNoTypeScriptSubmodule(t)
|
|
for _, rec := range parseConfigFileTextToJsonTests {
|
|
t.Run(rec.title, func(t *testing.T) {
|
|
t.Parallel()
|
|
var baselineContent strings.Builder
|
|
for i, jsonText := range rec.input {
|
|
baselineContent.WriteString("Input::\n")
|
|
baselineContent.WriteString(jsonText + "\n")
|
|
parsed, errors := tsoptions.ParseConfigFileTextToJson("/apath/tsconfig.json", "/apath", jsonText)
|
|
baselineContent.WriteString("Config::\n")
|
|
assert.NilError(t, writeJsonReadableText(&baselineContent, parsed), "Failed to write JSON text")
|
|
baselineContent.WriteString("\n")
|
|
baselineContent.WriteString("Errors::\n")
|
|
diagnosticwriter.FormatDiagnosticsWithColorAndContext(&baselineContent, errors, &diagnosticwriter.FormattingOptions{
|
|
NewLine: "\n",
|
|
ComparePathsOptions: tspath.ComparePathsOptions{
|
|
CurrentDirectory: "/",
|
|
UseCaseSensitiveFileNames: true,
|
|
},
|
|
})
|
|
baselineContent.WriteString("\n")
|
|
if i != len(rec.input)-1 {
|
|
baselineContent.WriteString("\n")
|
|
}
|
|
}
|
|
baseline.RunAgainstSubmodule(t, rec.title+" jsonParse.js", baselineContent.String(), baseline.Options{Subfolder: "config/tsconfigParsing"})
|
|
})
|
|
}
|
|
}
|
|
|
|
type parseJsonConfigTestCase struct {
|
|
title string
|
|
noSubmoduleBaseline bool
|
|
input []testConfig
|
|
}
|
|
|
|
var parseJsonConfigFileTests = []parseJsonConfigTestCase{
|
|
{
|
|
title: "ignore dotted files and folders",
|
|
input: []testConfig{{
|
|
jsonText: `{}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/test.ts": "", "/apath/.git/a.ts": "", "/apath/.b.ts": "", "/apath/..c.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "allow dotted files and folders when explicitly requested",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"files": ["/apath/.git/a.ts", "/apath/.b.ts", "/apath/..c.ts"]
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/test.ts": "", "/apath/.git/a.ts": "", "/apath/.b.ts": "", "/apath/..c.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "implicitly exclude common package folders",
|
|
input: []testConfig{{
|
|
jsonText: `{}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{"/node_modules/a.ts": "", "/bower_components/b.ts": "", "/jspm_packages/c.ts": "", "/d.ts": "", "/folder/e.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "generates errors for empty files list",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"files": []
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/a.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "generates errors for empty files list when no references are provided",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"files": [],
|
|
"references": []
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/a.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "generates errors for directory with no .ts files",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/a.js": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "generates errors for empty include",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"include": []
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "tests/cases/unittests",
|
|
allFileList: map[string]string{"/apath/a.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "parses tsconfig with compilerOptions, files, include, and exclude",
|
|
noSubmoduleBaseline: true,
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"outDir": "./dist",
|
|
"strict": true,
|
|
"noImplicitAny": true,
|
|
"target": "ES2017",
|
|
"module": "ESNext",
|
|
"moduleResolution": "bundler",
|
|
"moduleDetection": "auto",
|
|
"jsx": "react",
|
|
"maxNodeModuleJsDepth": 1,
|
|
"paths": {
|
|
"jquery": ["./vendor/jquery/dist/jquery"]
|
|
}
|
|
},
|
|
"files": ["/apath/src/index.ts", "/apath/src/app.ts"],
|
|
"include": ["/apath/src/**/*"],
|
|
"exclude": ["/apath/node_modules", "/apath/dist"]
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/src/index.ts": "", "/apath/src/app.ts": "", "/apath/node_modules/module.ts": "", "/apath/dist/output.js": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "generates errors when commandline option is in tsconfig",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"help": true
|
|
}
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/a.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "does not generate errors for empty files list when one or more references are provided",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"files": [],
|
|
"references": [{ "path": "/apath" }]
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/a.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "exclude outDir unless overridden",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"outDir": "bin"
|
|
}
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{"/bin/a.ts": "", "/b.ts": ""},
|
|
}, {
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"outDir": "bin"
|
|
},
|
|
"exclude": [ "obj" ]
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{"/bin/a.ts": "", "/b.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "exclude declarationDir unless overridden",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"declarationDir": "declarations"
|
|
}
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{"/declarations/a.d.ts": "", "/a.ts": ""},
|
|
}, {
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"declarationDir": "declarations"
|
|
},
|
|
"exclude": [ "types" ]
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{"/declarations/a.d.ts": "", "/a.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "generates errors for empty directory",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"allowJs": true
|
|
}
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{},
|
|
}},
|
|
},
|
|
{
|
|
title: "generates errors for includes with outDir",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"outDir": "./"
|
|
},
|
|
"include": ["**/*"]
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/a.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "generates errors when include is not string",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"include": [
|
|
[
|
|
"./**/*.ts"
|
|
]
|
|
]
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/a.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "generates errors when files is not string",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"files": [
|
|
{
|
|
"compilerOptions": {
|
|
"experimentalDecorators": true,
|
|
"allowJs": true
|
|
}
|
|
}
|
|
]
|
|
}`,
|
|
configFileName: "/apath/tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/a.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "with outDir from base tsconfig",
|
|
input: []testConfig{
|
|
{
|
|
jsonText: `{
|
|
"extends": "./tsconfigWithoutConfigDir.json"
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{
|
|
"/tsconfigWithoutConfigDir.json": tsconfigWithoutConfigDir,
|
|
"/bin/a.ts": "",
|
|
"/b.ts": "",
|
|
},
|
|
},
|
|
{
|
|
jsonText: `{
|
|
"extends": "./tsconfigWithConfigDir.json"
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{
|
|
"/tsconfigWithConfigDir.json": tsconfigWithConfigDir,
|
|
"/bin/a.ts": "",
|
|
"/b.ts": "",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
title: "returns error when tsconfig have excludes",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"lib": ["es5"]
|
|
},
|
|
"excludes": [
|
|
"foge.ts"
|
|
]
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{"/apath/test.ts": "", "/apath/foge.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "parses tsconfig with extends, files, include and other options",
|
|
noSubmoduleBaseline: true,
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"extends": "./tsconfigWithExtends.json",
|
|
"compilerOptions": {
|
|
"outDir": "./dist",
|
|
"strict": true,
|
|
"noImplicitAny": true,
|
|
"baseUrl": "",
|
|
},
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{"/tsconfigWithExtends.json": tsconfigWithExtends, "/src/index.ts": "", "/src/app.ts": "", "/node_modules/module.ts": "", "/dist/output.js": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "parses tsconfig with extends and configDir",
|
|
noSubmoduleBaseline: true,
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"extends": "./tsconfig.base.json"
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{"/tsconfig.base.json": tsconfigWithExtendsAndConfigDir, "/src/index.ts": "", "/src/app.ts": "", "/node_modules/module.ts": "", "/dist/output.js": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "reports error for an unknown option",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"unknown": true
|
|
}
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{"/app.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "reports errors for wrong type option and invalid enum value",
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"target": "invalid value",
|
|
"removeComments": "should be a boolean",
|
|
"moduleResolution": "invalid value"
|
|
}
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{"/app.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "handles empty types array",
|
|
noSubmoduleBaseline: true,
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"compilerOptions": {
|
|
"types": []
|
|
}
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{"/app.ts": ""},
|
|
}},
|
|
},
|
|
{
|
|
title: "issue 1267 scenario - extended files not picked up",
|
|
noSubmoduleBaseline: true,
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"extends": "./tsconfig-base/backend.json",
|
|
"compilerOptions": {
|
|
"baseUrl": "./",
|
|
"outDir": "dist",
|
|
"rootDir": "src",
|
|
"resolveJsonModule": true
|
|
},
|
|
"exclude": ["node_modules", "dist"],
|
|
"include": ["src/**/*"]
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{
|
|
"/tsconfig-base/backend.json": `{
|
|
"$schema": "https://json.schemastore.org/tsconfig",
|
|
"display": "Backend",
|
|
"compilerOptions": {
|
|
"allowJs": true,
|
|
"module": "nodenext",
|
|
"removeComments": true,
|
|
"emitDecoratorMetadata": true,
|
|
"experimentalDecorators": true,
|
|
"allowSyntheticDefaultImports": true,
|
|
"target": "esnext",
|
|
"lib": ["ESNext"],
|
|
"incremental": false,
|
|
"esModuleInterop": true,
|
|
"noImplicitAny": true,
|
|
"moduleResolution": "nodenext",
|
|
"types": ["node", "vitest/globals"],
|
|
"sourceMap": true,
|
|
"strictPropertyInitialization": false
|
|
},
|
|
"files": [
|
|
"types/ical2json.d.ts",
|
|
"types/express.d.ts",
|
|
"types/multer.d.ts",
|
|
"types/reset.d.ts",
|
|
"types/stripe-custom-typings.d.ts",
|
|
"types/nestjs-modules.d.ts",
|
|
"types/luxon.d.ts",
|
|
"types/nestjs-pino.d.ts"
|
|
],
|
|
"ts-node": {
|
|
"files": true
|
|
}
|
|
}`,
|
|
"/tsconfig-base/types/ical2json.d.ts": "export {}",
|
|
"/tsconfig-base/types/express.d.ts": "export {}",
|
|
"/tsconfig-base/types/multer.d.ts": "export {}",
|
|
"/tsconfig-base/types/reset.d.ts": "export {}",
|
|
"/tsconfig-base/types/stripe-custom-typings.d.ts": "export {}",
|
|
"/tsconfig-base/types/nestjs-modules.d.ts": "export {}",
|
|
"/tsconfig-base/types/luxon.d.ts": `declare module 'luxon' {
|
|
interface TSSettings {
|
|
throwOnInvalid: true
|
|
}
|
|
}
|
|
export {}`,
|
|
"/tsconfig-base/types/nestjs-pino.d.ts": "export {}",
|
|
"/src/main.ts": "export {}",
|
|
"/src/utils.ts": "export {}",
|
|
},
|
|
}},
|
|
},
|
|
{
|
|
title: "null overrides in extended tsconfig - array fields",
|
|
noSubmoduleBaseline: true,
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"extends": "./tsconfig-base.json",
|
|
"compilerOptions": {
|
|
"types": null,
|
|
"lib": null,
|
|
"typeRoots": null
|
|
}
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{
|
|
"/tsconfig-base.json": `{
|
|
"compilerOptions": {
|
|
"types": ["node", "@types/jest"],
|
|
"lib": ["es2020", "dom"],
|
|
"typeRoots": ["./types", "./node_modules/@types"]
|
|
}
|
|
}`,
|
|
"/app.ts": "",
|
|
},
|
|
}},
|
|
},
|
|
{
|
|
title: "null overrides in extended tsconfig - string fields",
|
|
noSubmoduleBaseline: true,
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"extends": "./tsconfig-base.json",
|
|
"compilerOptions": {
|
|
"outDir": null,
|
|
"baseUrl": null,
|
|
"rootDir": null
|
|
}
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{
|
|
"/tsconfig-base.json": `{
|
|
"compilerOptions": {
|
|
"outDir": "./dist",
|
|
"baseUrl": "./src",
|
|
"rootDir": "./src"
|
|
}
|
|
}`,
|
|
"/app.ts": "",
|
|
},
|
|
}},
|
|
},
|
|
{
|
|
title: "null overrides in extended tsconfig - mixed field types",
|
|
noSubmoduleBaseline: true,
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"extends": "./tsconfig-base.json",
|
|
"compilerOptions": {
|
|
"types": null,
|
|
"outDir": null,
|
|
"strict": false,
|
|
"lib": ["es2022"],
|
|
"allowJs": null
|
|
}
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{
|
|
"/tsconfig-base.json": `{
|
|
"compilerOptions": {
|
|
"types": ["node"],
|
|
"lib": ["es2020", "dom"],
|
|
"outDir": "./dist",
|
|
"strict": true,
|
|
"allowJs": true,
|
|
"target": "es2020"
|
|
}
|
|
}`,
|
|
"/app.ts": "",
|
|
},
|
|
}},
|
|
},
|
|
{
|
|
title: "null overrides with multiple extends levels",
|
|
noSubmoduleBaseline: true,
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"extends": "./tsconfig-middle.json",
|
|
"compilerOptions": {
|
|
"types": null,
|
|
"lib": null
|
|
}
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{
|
|
"/tsconfig-middle.json": `{
|
|
"extends": "./tsconfig-base.json",
|
|
"compilerOptions": {
|
|
"types": ["jest"],
|
|
"outDir": "./build"
|
|
}
|
|
}`,
|
|
"/tsconfig-base.json": `{
|
|
"compilerOptions": {
|
|
"types": ["node"],
|
|
"lib": ["es2020"],
|
|
"outDir": "./dist",
|
|
"strict": true
|
|
}
|
|
}`,
|
|
"/app.ts": "",
|
|
},
|
|
}},
|
|
},
|
|
{
|
|
title: "null overrides in middle level of extends chain",
|
|
noSubmoduleBaseline: true,
|
|
input: []testConfig{{
|
|
jsonText: `{
|
|
"extends": "./tsconfig-middle.json",
|
|
"compilerOptions": {
|
|
"outDir": "./final"
|
|
}
|
|
}`,
|
|
configFileName: "tsconfig.json",
|
|
basePath: "/",
|
|
allFileList: map[string]string{
|
|
"/tsconfig-middle.json": `{
|
|
"extends": "./tsconfig-base.json",
|
|
"compilerOptions": {
|
|
"types": null,
|
|
"lib": null,
|
|
"outDir": "./middle"
|
|
}
|
|
}`,
|
|
"/tsconfig-base.json": `{
|
|
"compilerOptions": {
|
|
"types": ["node"],
|
|
"lib": ["es2020"],
|
|
"outDir": "./base",
|
|
"strict": true
|
|
}
|
|
}`,
|
|
"/app.ts": "",
|
|
},
|
|
}},
|
|
},
|
|
}
|
|
|
|
var tsconfigWithExtends = `{
|
|
"files": ["/src/index.ts", "/src/app.ts"],
|
|
"include": ["/src/**/*"],
|
|
"exclude": [],
|
|
"ts-node": {
|
|
"compilerOptions": {
|
|
"module": "commonjs"
|
|
},
|
|
"transpileOnly": true
|
|
}
|
|
}`
|
|
|
|
var tsconfigWithoutConfigDir = `{
|
|
"compilerOptions": {
|
|
"outDir": "bin"
|
|
}
|
|
}`
|
|
|
|
var tsconfigWithConfigDir = `{
|
|
"compilerOptions": {
|
|
"outDir": "${configDir}/bin"
|
|
}
|
|
}`
|
|
|
|
var tsconfigWithExtendsAndConfigDir = `{
|
|
"compilerOptions": {
|
|
"outFile": "${configDir}/outFile",
|
|
"outDir": "${configDir}/outDir",
|
|
"rootDir": "${configDir}/rootDir",
|
|
"tsBuildInfoFile": "${configDir}/tsBuildInfoFile",
|
|
"baseUrl": "${configDir}/baseUrl",
|
|
"declarationDir": "${configDir}/declarationDir",
|
|
}
|
|
}`
|
|
|
|
func TestParseJsonConfigFileContent(t *testing.T) {
|
|
t.Parallel()
|
|
repo.SkipIfNoTypeScriptSubmodule(t)
|
|
for _, rec := range parseJsonConfigFileTests {
|
|
t.Run(rec.title+" with json api", func(t *testing.T) {
|
|
t.Parallel()
|
|
baselineParseConfigWith(t, rec.title+" with json api.js", rec.noSubmoduleBaseline, rec.input, getParsedWithJsonApi)
|
|
})
|
|
}
|
|
}
|
|
|
|
func getParsedWithJsonApi(config testConfig, host tsoptions.ParseConfigHost, basePath string) *tsoptions.ParsedCommandLine {
|
|
configFileName := tspath.GetNormalizedAbsolutePath(config.configFileName, basePath)
|
|
path := tspath.ToPath(config.configFileName, basePath, host.FS().UseCaseSensitiveFileNames())
|
|
parsed, _ := tsoptions.ParseConfigFileTextToJson(configFileName, path, config.jsonText)
|
|
return tsoptions.ParseJsonConfigFileContent(
|
|
parsed,
|
|
host,
|
|
basePath,
|
|
nil,
|
|
configFileName,
|
|
/*resolutionStack*/ nil,
|
|
/*extraFileExtensions*/ nil,
|
|
/*extendedConfigCache*/ nil,
|
|
)
|
|
}
|
|
|
|
func TestParseJsonSourceFileConfigFileContent(t *testing.T) {
|
|
t.Parallel()
|
|
repo.SkipIfNoTypeScriptSubmodule(t)
|
|
for _, rec := range parseJsonConfigFileTests {
|
|
t.Run(rec.title+" with jsonSourceFile api", func(t *testing.T) {
|
|
t.Parallel()
|
|
baselineParseConfigWith(t, rec.title+" with jsonSourceFile api.js", rec.noSubmoduleBaseline, rec.input, getParsedWithJsonSourceFileApi)
|
|
})
|
|
}
|
|
}
|
|
|
|
func getParsedWithJsonSourceFileApi(config testConfig, host tsoptions.ParseConfigHost, basePath string) *tsoptions.ParsedCommandLine {
|
|
configFileName := tspath.GetNormalizedAbsolutePath(config.configFileName, basePath)
|
|
path := tspath.ToPath(config.configFileName, basePath, host.FS().UseCaseSensitiveFileNames())
|
|
parsed := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
|
FileName: configFileName,
|
|
Path: path,
|
|
}, config.jsonText, core.ScriptKindJSON)
|
|
tsConfigSourceFile := &tsoptions.TsConfigSourceFile{
|
|
SourceFile: parsed,
|
|
}
|
|
return tsoptions.ParseJsonSourceFileConfigFileContent(
|
|
tsConfigSourceFile,
|
|
host,
|
|
host.GetCurrentDirectory(),
|
|
nil,
|
|
configFileName,
|
|
/*resolutionStack*/ nil,
|
|
/*extraFileExtensions*/ nil,
|
|
/*extendedConfigCache*/ nil,
|
|
)
|
|
}
|
|
|
|
func baselineParseConfigWith(t *testing.T, baselineFileName string, noSubmoduleBaseline bool, input []testConfig, getParsed func(config testConfig, host tsoptions.ParseConfigHost, basePath string) *tsoptions.ParsedCommandLine) {
|
|
noSubmoduleBaseline = true
|
|
var baselineContent strings.Builder
|
|
for i, config := range input {
|
|
basePath := config.basePath
|
|
if basePath == "" {
|
|
basePath = tspath.GetNormalizedAbsolutePath(tspath.GetDirectoryPath(config.configFileName), "")
|
|
}
|
|
configFileName := tspath.CombinePaths(basePath, config.configFileName)
|
|
allFileLists := make(map[string]string, len(config.allFileList)+1)
|
|
for file, content := range config.allFileList {
|
|
allFileLists[file] = content
|
|
}
|
|
allFileLists[configFileName] = config.jsonText
|
|
host := tsoptionstest.NewVFSParseConfigHost(allFileLists, config.basePath, true /*useCaseSensitiveFileNames*/)
|
|
parsedConfigFileContent := getParsed(config, host, basePath)
|
|
|
|
baselineContent.WriteString("Fs::\n")
|
|
if err := printFS(&baselineContent, host.FS(), "/"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
baselineContent.WriteString("\n")
|
|
baselineContent.WriteString("configFileName:: " + config.configFileName + "\n")
|
|
if noSubmoduleBaseline {
|
|
baselineContent.WriteString("CompilerOptions::\n")
|
|
assert.NilError(t, jsonutil.MarshalIndentWrite(&baselineContent, parsedConfigFileContent.ParsedConfig.CompilerOptions, "", " "))
|
|
baselineContent.WriteString("\n")
|
|
baselineContent.WriteString("\n")
|
|
|
|
if parsedConfigFileContent.ParsedConfig.TypeAcquisition != nil {
|
|
baselineContent.WriteString("TypeAcquisition::\n")
|
|
assert.NilError(t, jsonutil.MarshalIndentWrite(&baselineContent, parsedConfigFileContent.ParsedConfig.TypeAcquisition, "", " "))
|
|
baselineContent.WriteString("\n")
|
|
baselineContent.WriteString("\n")
|
|
}
|
|
}
|
|
baselineContent.WriteString("FileNames::\n")
|
|
baselineContent.WriteString(strings.Join(parsedConfigFileContent.ParsedConfig.FileNames, ",") + "\n")
|
|
baselineContent.WriteString("Errors::\n")
|
|
diagnosticwriter.FormatDiagnosticsWithColorAndContext(&baselineContent, parsedConfigFileContent.Errors, &diagnosticwriter.FormattingOptions{
|
|
NewLine: "\r\n",
|
|
ComparePathsOptions: tspath.ComparePathsOptions{
|
|
CurrentDirectory: basePath,
|
|
UseCaseSensitiveFileNames: true,
|
|
},
|
|
})
|
|
baselineContent.WriteString("\n")
|
|
if i != len(input)-1 {
|
|
baselineContent.WriteString("\n")
|
|
}
|
|
}
|
|
if noSubmoduleBaseline {
|
|
baseline.Run(t, baselineFileName, baselineContent.String(), baseline.Options{Subfolder: "config/tsconfigParsing"})
|
|
} else {
|
|
baseline.RunAgainstSubmodule(t, baselineFileName, baselineContent.String(), baseline.Options{Subfolder: "config/tsconfigParsing"})
|
|
}
|
|
}
|
|
|
|
func writeJsonReadableText(output io.Writer, input any) error {
|
|
return jsonutil.MarshalIndentWrite(output, input, "", " ")
|
|
}
|
|
|
|
func TestParseTypeAcquisition(t *testing.T) {
|
|
t.Parallel()
|
|
// repo.SkipIfNoTypeScriptSubmodule(t)
|
|
cases := []struct {
|
|
title string
|
|
configName string
|
|
config string
|
|
}{
|
|
{
|
|
title: "Convert correctly format tsconfig.json to typeAcquisition ",
|
|
config: `{
|
|
"typeAcquisition": {
|
|
"enable": true,
|
|
"include": ["0.d.ts", "1.d.ts"],
|
|
"exclude": ["0.js", "1.js"],
|
|
},
|
|
}`,
|
|
configName: "tsconfig.json",
|
|
},
|
|
{
|
|
title: "Convert incorrect format tsconfig.json to typeAcquisition ",
|
|
config: `{
|
|
"typeAcquisition": {
|
|
"enableAutoDiscovy": true,
|
|
}
|
|
}`, configName: "tsconfig.json",
|
|
},
|
|
{
|
|
title: "Convert default tsconfig.json to typeAcquisition ",
|
|
config: `{}`, configName: "tsconfig.json",
|
|
},
|
|
{
|
|
title: "Convert tsconfig.json with only enable property to typeAcquisition ",
|
|
config: `{
|
|
"typeAcquisition": {
|
|
"enable": true,
|
|
},
|
|
}`, configName: "tsconfig.json",
|
|
},
|
|
|
|
// jsconfig.json
|
|
{
|
|
title: "Convert jsconfig.json to typeAcquisition ",
|
|
config: `{
|
|
"typeAcquisition": {
|
|
"enable": false,
|
|
"include": ["0.d.ts"],
|
|
"exclude": ["0.js"],
|
|
},
|
|
}`,
|
|
configName: "jsconfig.json",
|
|
},
|
|
{title: "Convert default jsconfig.json to typeAcquisition ", config: `{}`, configName: "jsconfig.json"},
|
|
{
|
|
title: "Convert incorrect format jsconfig.json to typeAcquisition ",
|
|
config: `{
|
|
"typeAcquisition": {
|
|
"enableAutoDiscovy": true,
|
|
},
|
|
}`,
|
|
configName: "jsconfig.json",
|
|
},
|
|
{
|
|
title: "Convert jsconfig.json with only enable property to typeAcquisition ",
|
|
config: `{
|
|
"typeAcquisition": {
|
|
"enable": false,
|
|
},
|
|
}`,
|
|
configName: "jsconfig.json",
|
|
},
|
|
}
|
|
for _, test := range cases {
|
|
withJsonApiName := test.title + " with json api"
|
|
input := []testConfig{
|
|
{
|
|
jsonText: test.config,
|
|
configFileName: test.configName,
|
|
basePath: "/apath",
|
|
allFileList: map[string]string{
|
|
"/apath/a.ts": "",
|
|
"/apath/b.ts": "",
|
|
},
|
|
},
|
|
}
|
|
t.Run(withJsonApiName, func(t *testing.T) {
|
|
t.Parallel()
|
|
baselineParseConfigWith(t, withJsonApiName+".js", true, input, getParsedWithJsonApi)
|
|
})
|
|
withJsonSourceFileApiName := test.title + " with jsonSourceFile api"
|
|
t.Run(withJsonSourceFileApiName, func(t *testing.T) {
|
|
t.Parallel()
|
|
baselineParseConfigWith(t, withJsonSourceFileApiName+".js", true, input, getParsedWithJsonSourceFileApi)
|
|
})
|
|
}
|
|
}
|
|
|
|
func printFS(output io.Writer, files vfs.FS, root string) error {
|
|
return files.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if d.Type().IsRegular() {
|
|
if content, ok := files.ReadFile(path); !ok {
|
|
return fmt.Errorf("failed to read file %s", path)
|
|
} else {
|
|
if _, err := fmt.Fprintf(output, "//// [%s]\r\n%s\r\n\r\n", path, content); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func TestParseSrcCompiler(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
repo.SkipIfNoTypeScriptSubmodule(t)
|
|
|
|
compilerDir := tspath.NormalizeSlashes(filepath.Join(repo.TypeScriptSubmodulePath, "src", "compiler"))
|
|
tsconfigFileName := tspath.CombinePaths(compilerDir, "tsconfig.json")
|
|
|
|
fs := osvfs.FS()
|
|
host := &tsoptionstest.VfsParseConfigHost{
|
|
Vfs: fs,
|
|
CurrentDirectory: compilerDir,
|
|
}
|
|
|
|
jsonText, ok := fs.ReadFile(tsconfigFileName)
|
|
assert.Assert(t, ok)
|
|
tsconfigPath := tspath.ToPath(tsconfigFileName, compilerDir, fs.UseCaseSensitiveFileNames())
|
|
parsed := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
|
FileName: tsconfigFileName,
|
|
Path: tsconfigPath,
|
|
}, jsonText, core.ScriptKindJSON)
|
|
|
|
if len(parsed.Diagnostics()) > 0 {
|
|
for _, error := range parsed.Diagnostics() {
|
|
t.Log(error.Message())
|
|
}
|
|
t.FailNow()
|
|
}
|
|
|
|
tsConfigSourceFile := &tsoptions.TsConfigSourceFile{
|
|
SourceFile: parsed,
|
|
}
|
|
|
|
parseConfigFileContent := tsoptions.ParseJsonSourceFileConfigFileContent(
|
|
tsConfigSourceFile,
|
|
host,
|
|
host.GetCurrentDirectory(),
|
|
nil,
|
|
tsconfigFileName,
|
|
/*resolutionStack*/ nil,
|
|
/*extraFileExtensions*/ nil,
|
|
/*extendedConfigCache*/ nil,
|
|
)
|
|
|
|
if len(parseConfigFileContent.Errors) > 0 {
|
|
for _, error := range parseConfigFileContent.Errors {
|
|
t.Log(error.Message())
|
|
}
|
|
t.FailNow()
|
|
}
|
|
|
|
opts := parseConfigFileContent.CompilerOptions()
|
|
assert.DeepEqual(t, opts, &core.CompilerOptions{
|
|
Lib: []string{"lib.es2020.d.ts"},
|
|
Module: core.ModuleKindNodeNext,
|
|
ModuleResolution: core.ModuleResolutionKindNodeNext,
|
|
NewLine: core.NewLineKindLF,
|
|
OutDir: tspath.NormalizeSlashes(filepath.Join(repo.TypeScriptSubmodulePath, "built", "local")),
|
|
Target: core.ScriptTargetES2020,
|
|
Types: []string{"node"},
|
|
ConfigFilePath: tsconfigFileName,
|
|
Declaration: core.TSTrue,
|
|
DeclarationMap: core.TSTrue,
|
|
EmitDeclarationOnly: core.TSTrue,
|
|
AlwaysStrict: core.TSTrue,
|
|
Composite: core.TSTrue,
|
|
IsolatedDeclarations: core.TSTrue,
|
|
NoImplicitOverride: core.TSTrue,
|
|
PreserveConstEnums: core.TSTrue,
|
|
RootDir: tspath.NormalizeSlashes(filepath.Join(repo.TypeScriptSubmodulePath, "src")),
|
|
SkipLibCheck: core.TSTrue,
|
|
Strict: core.TSTrue,
|
|
StrictBindCallApply: core.TSFalse,
|
|
SourceMap: core.TSTrue,
|
|
UseUnknownInCatchVariables: core.TSFalse,
|
|
Pretty: core.TSTrue,
|
|
}, cmpopts.IgnoreUnexported(core.CompilerOptions{}))
|
|
|
|
fileNames := parseConfigFileContent.ParsedConfig.FileNames
|
|
relativePaths := make([]string, 0, len(fileNames))
|
|
for _, fileName := range fileNames {
|
|
if strings.Contains(fileName, ".generated.") {
|
|
continue
|
|
}
|
|
|
|
relativePaths = append(relativePaths, tspath.ConvertToRelativePath(fileName, tspath.ComparePathsOptions{
|
|
CurrentDirectory: compilerDir,
|
|
UseCaseSensitiveFileNames: fs.UseCaseSensitiveFileNames(),
|
|
}))
|
|
}
|
|
|
|
assert.DeepEqual(t, relativePaths, []string{
|
|
"binder.ts",
|
|
"builder.ts",
|
|
"builderPublic.ts",
|
|
"builderState.ts",
|
|
"builderStatePublic.ts",
|
|
"checker.ts",
|
|
"commandLineParser.ts",
|
|
"core.ts",
|
|
"corePublic.ts",
|
|
"debug.ts",
|
|
"emitter.ts",
|
|
"executeCommandLine.ts",
|
|
"expressionToTypeNode.ts",
|
|
"moduleNameResolver.ts",
|
|
"moduleSpecifiers.ts",
|
|
"parser.ts",
|
|
"path.ts",
|
|
"performance.ts",
|
|
"performanceCore.ts",
|
|
"program.ts",
|
|
"programDiagnostics.ts",
|
|
"resolutionCache.ts",
|
|
"scanner.ts",
|
|
"semver.ts",
|
|
"sourcemap.ts",
|
|
"symbolWalker.ts",
|
|
"sys.ts",
|
|
"tracing.ts",
|
|
"transformer.ts",
|
|
"tsbuild.ts",
|
|
"tsbuildPublic.ts",
|
|
"types.ts",
|
|
"utilities.ts",
|
|
"utilitiesPublic.ts",
|
|
"visitorPublic.ts",
|
|
"watch.ts",
|
|
"watchPublic.ts",
|
|
"watchUtilities.ts",
|
|
"_namespaces/ts.moduleSpecifiers.ts",
|
|
"_namespaces/ts.performance.ts",
|
|
"_namespaces/ts.ts",
|
|
"factory/baseNodeFactory.ts",
|
|
"factory/emitHelpers.ts",
|
|
"factory/emitNode.ts",
|
|
"factory/nodeChildren.ts",
|
|
"factory/nodeConverters.ts",
|
|
"factory/nodeFactory.ts",
|
|
"factory/nodeTests.ts",
|
|
"factory/parenthesizerRules.ts",
|
|
"factory/utilities.ts",
|
|
"factory/utilitiesPublic.ts",
|
|
"transformers/classFields.ts",
|
|
"transformers/classThis.ts",
|
|
"transformers/declarations.ts",
|
|
"transformers/destructuring.ts",
|
|
"transformers/es2016.ts",
|
|
"transformers/es2017.ts",
|
|
"transformers/es2018.ts",
|
|
"transformers/es2019.ts",
|
|
"transformers/es2020.ts",
|
|
"transformers/es2021.ts",
|
|
"transformers/esDecorators.ts",
|
|
"transformers/esnext.ts",
|
|
"transformers/jsx.ts",
|
|
"transformers/legacyDecorators.ts",
|
|
"transformers/namedEvaluation.ts",
|
|
"transformers/taggedTemplate.ts",
|
|
"transformers/ts.ts",
|
|
"transformers/typeSerializer.ts",
|
|
"transformers/utilities.ts",
|
|
"transformers/declarations/diagnostics.ts",
|
|
"transformers/module/esnextAnd2015.ts",
|
|
"transformers/module/impliedNodeFormatDependent.ts",
|
|
"transformers/module/module.ts",
|
|
"transformers/module/system.ts",
|
|
})
|
|
}
|
|
|
|
func BenchmarkParseSrcCompiler(b *testing.B) {
|
|
repo.SkipIfNoTypeScriptSubmodule(b)
|
|
|
|
compilerDir := tspath.NormalizeSlashes(filepath.Join(repo.TypeScriptSubmodulePath, "src", "compiler"))
|
|
tsconfigFileName := tspath.CombinePaths(compilerDir, "tsconfig.json")
|
|
|
|
fs := osvfs.FS()
|
|
host := &tsoptionstest.VfsParseConfigHost{
|
|
Vfs: fs,
|
|
CurrentDirectory: compilerDir,
|
|
}
|
|
|
|
jsonText, ok := fs.ReadFile(tsconfigFileName)
|
|
assert.Assert(b, ok)
|
|
tsconfigPath := tspath.ToPath(tsconfigFileName, compilerDir, fs.UseCaseSensitiveFileNames())
|
|
parsed := parser.ParseSourceFile(ast.SourceFileParseOptions{
|
|
FileName: tsconfigFileName,
|
|
Path: tsconfigPath,
|
|
}, jsonText, core.ScriptKindJSON)
|
|
|
|
b.ReportAllocs()
|
|
|
|
for b.Loop() {
|
|
tsoptions.ParseJsonSourceFileConfigFileContent(
|
|
&tsoptions.TsConfigSourceFile{
|
|
SourceFile: parsed,
|
|
},
|
|
host,
|
|
host.GetCurrentDirectory(),
|
|
nil,
|
|
tsconfigFileName,
|
|
/*resolutionStack*/ nil,
|
|
/*extraFileExtensions*/ nil,
|
|
/*extendedConfigCache*/ nil,
|
|
)
|
|
}
|
|
}
|
|
|
|
// memoCache is a minimal memoizing ExtendedConfigCache used by tests to simulate
|
|
// cache hits across multiple parses of configs that extend a common base.
|
|
type memoCache struct {
|
|
m map[tspath.Path]*tsoptions.ExtendedConfigCacheEntry
|
|
}
|
|
|
|
func (mc *memoCache) GetExtendedConfig(fileName string, path tspath.Path, parse func() *tsoptions.ExtendedConfigCacheEntry) *tsoptions.ExtendedConfigCacheEntry {
|
|
if mc.m == nil {
|
|
mc.m = make(map[tspath.Path]*tsoptions.ExtendedConfigCacheEntry)
|
|
}
|
|
if e, ok := mc.m[path]; ok {
|
|
return e
|
|
}
|
|
e := parse()
|
|
mc.m[path] = e
|
|
return e
|
|
}
|
|
|
|
var _ tsoptions.ExtendedConfigCache = (*memoCache)(nil)
|
|
|
|
// TestExtendedConfigErrorsAppearOnCacheHit verifies that diagnostics produced while parsing an
|
|
// extended config are still reported when the extended config comes from the cache.
|
|
func TestExtendedConfigErrorsAppearOnCacheHit(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("single config parsed twice", func(t *testing.T) {
|
|
t.Parallel()
|
|
files := map[string]string{
|
|
"/tsconfig.json": `{
|
|
"extends": "./base.json"
|
|
}`,
|
|
// 'excludes' instead of 'exclude' triggers diagnostic
|
|
"/base.json": `{
|
|
"excludes": ["**/*.ts"]
|
|
}`,
|
|
"/app.ts": "export {}",
|
|
}
|
|
|
|
host := tsoptionstest.NewVFSParseConfigHost(files, "/", true /*useCaseSensitiveFileNames*/)
|
|
|
|
parseConfig := func(configFileName string, cache tsoptions.ExtendedConfigCache) *tsoptions.ParsedCommandLine {
|
|
cfgPath := tspath.ToPath(configFileName, host.GetCurrentDirectory(), host.FS().UseCaseSensitiveFileNames())
|
|
jsonText, ok := host.FS().ReadFile(configFileName)
|
|
assert.Assert(t, ok, "missing %s in test fs", configFileName)
|
|
tsConfigSourceFile := &tsoptions.TsConfigSourceFile{
|
|
SourceFile: parser.ParseSourceFile(ast.SourceFileParseOptions{FileName: configFileName, Path: cfgPath}, jsonText, core.ScriptKindJSON),
|
|
}
|
|
return tsoptions.ParseJsonSourceFileConfigFileContent(
|
|
tsConfigSourceFile,
|
|
host,
|
|
host.GetCurrentDirectory(),
|
|
nil,
|
|
configFileName,
|
|
nil,
|
|
nil,
|
|
cache,
|
|
)
|
|
}
|
|
|
|
cache := &memoCache{}
|
|
first := parseConfig("/tsconfig.json", cache)
|
|
assert.Assert(t, len(first.Errors) > 0, "expected diagnostics on first parse, got 0")
|
|
second := parseConfig("/tsconfig.json", cache)
|
|
assert.Assert(t, len(second.Errors) > 0, "expected diagnostics on second parse (cache hit), got 0")
|
|
})
|
|
|
|
t.Run("two configs share same base", func(t *testing.T) {
|
|
t.Parallel()
|
|
files := map[string]string{
|
|
"/base.json": `{
|
|
"excludes": ["**/*.ts"]
|
|
}`,
|
|
"/projA/tsconfig.json": `{
|
|
"extends": "../base.json"
|
|
}`,
|
|
"/projB/tsconfig.json": `{
|
|
"extends": "../base.json"
|
|
}`,
|
|
"/projA/app.ts": "export {}",
|
|
"/projB/app.ts": "export {}",
|
|
}
|
|
|
|
host := tsoptionstest.NewVFSParseConfigHost(files, "/", true /*useCaseSensitiveFileNames*/)
|
|
|
|
parseConfig := func(configFileName string, cache tsoptions.ExtendedConfigCache) *tsoptions.ParsedCommandLine {
|
|
cfgPath := tspath.ToPath(configFileName, host.GetCurrentDirectory(), host.FS().UseCaseSensitiveFileNames())
|
|
jsonText, ok := host.FS().ReadFile(configFileName)
|
|
assert.Assert(t, ok, "missing %s in test fs", configFileName)
|
|
tsConfigSourceFile := &tsoptions.TsConfigSourceFile{
|
|
SourceFile: parser.ParseSourceFile(ast.SourceFileParseOptions{FileName: configFileName, Path: cfgPath}, jsonText, core.ScriptKindJSON),
|
|
}
|
|
return tsoptions.ParseJsonSourceFileConfigFileContent(
|
|
tsConfigSourceFile,
|
|
host,
|
|
host.GetCurrentDirectory(),
|
|
nil,
|
|
configFileName,
|
|
nil,
|
|
nil,
|
|
cache,
|
|
)
|
|
}
|
|
|
|
cache := &memoCache{}
|
|
first := parseConfig("/projA/tsconfig.json", cache)
|
|
assert.Assert(t, len(first.Errors) > 0, "expected diagnostics for projA parse, got 0")
|
|
second := parseConfig("/projB/tsconfig.json", cache)
|
|
assert.Assert(t, len(second.Errors) > 0, "expected diagnostics for projB parse (cache hit on base), got 0")
|
|
})
|
|
}
|