vendor tsgo

This commit is contained in:
2026-07-09 16:50:43 -04:00
parent c06ea2e5a4
commit 98978e4930
5804 changed files with 1556156 additions and 101 deletions

View File

@@ -0,0 +1,198 @@
package packagejson
import (
"sync"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/diagnostics"
"github.com/microsoft/typescript-go/internal/semver"
"github.com/microsoft/typescript-go/internal/tspath"
)
var typeScriptVersion = semver.MustParse(core.Version())
type PackageJson struct {
Fields
Parseable bool
versionPaths VersionPaths
versionTraces []diagnosticAndArgs
once sync.Once
}
type diagnosticAndArgs struct {
message *diagnostics.Message
args []any
}
func (p *PackageJson) GetVersionPaths(trace func(m *diagnostics.Message, args ...any)) VersionPaths {
p.once.Do(func() {
if p.Fields.TypesVersions.Type == JSONValueTypeNotPresent {
p.versionTraces = append(p.versionTraces, diagnosticAndArgs{
diagnostics.X_package_json_does_not_have_a_0_field,
[]any{"typesVersions"},
})
return
}
if p.Fields.TypesVersions.Type != JSONValueTypeObject {
p.versionTraces = append(p.versionTraces, diagnosticAndArgs{
diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2,
[]any{"typesVersions", "object", p.Fields.TypesVersions.Type.String()},
})
return
}
p.versionTraces = append(p.versionTraces, diagnosticAndArgs{
diagnostics.X_package_json_has_a_typesVersions_field_with_version_specific_path_mappings,
[]any{"typesVersions"},
})
for key, value := range p.Fields.TypesVersions.AsObject().Entries() {
keyRange, ok := semver.TryParseVersionRange(key)
if !ok {
p.versionTraces = append(p.versionTraces, diagnosticAndArgs{
diagnostics.X_package_json_has_a_typesVersions_entry_0_that_is_not_a_valid_semver_range,
[]any{key},
})
continue
}
if keyRange.Test(&typeScriptVersion) {
if value.Type != JSONValueTypeObject {
p.versionTraces = append(p.versionTraces, diagnosticAndArgs{
diagnostics.Expected_type_of_0_field_in_package_json_to_be_1_got_2,
[]any{"typesVersions['" + key + "']", "object", value.Type.String()},
})
return
}
p.versionPaths = VersionPaths{
Version: key,
pathsJSON: value.AsObject(),
}
return
}
}
p.versionTraces = append(p.versionTraces, diagnosticAndArgs{
diagnostics.X_package_json_does_not_have_a_typesVersions_entry_that_matches_version_0,
[]any{core.VersionMajorMinor()},
})
})
if trace != nil {
for _, msg := range p.versionTraces {
trace(msg.message, msg.args...)
}
}
return p.versionPaths
}
type VersionPaths struct {
Version string
pathsJSON *collections.OrderedMap[string, JSONValue]
paths *collections.OrderedMap[string, []string]
}
func (v *VersionPaths) Exists() bool {
return v != nil && v.Version != "" && v.pathsJSON != nil
}
func (v *VersionPaths) GetPaths() *collections.OrderedMap[string, []string] {
if !v.Exists() {
return nil
}
if v.paths != nil {
return v.paths
}
paths := collections.NewOrderedMapWithSizeHint[string, []string](v.pathsJSON.Size())
for key, value := range v.pathsJSON.Entries() {
if value.Type != JSONValueTypeArray {
continue
}
slice := make([]string, len(value.AsArray()))
for i, path := range value.AsArray() {
if path.Type != JSONValueTypeString {
continue
}
slice[i] = path.Value.(string)
}
paths.Set(key, slice)
}
v.paths = paths
return v.paths
}
type InfoCacheEntry struct {
PackageDirectory string
DirectoryExists bool
Contents *PackageJson
}
func (p *InfoCacheEntry) Exists() bool {
return p != nil && p.Contents != nil
}
func (p *InfoCacheEntry) GetContents() *PackageJson {
if p == nil || p.Contents == nil {
return nil
}
return p.Contents
}
func (p *InfoCacheEntry) GetDirectory() string {
if p == nil {
return ""
}
return p.PackageDirectory
}
// WithPackageDirectory returns an entry whose PackageDirectory matches the
// caller's value. The package.json info cache is keyed by the canonical
// path of the package.json file, but multiple callers may look up the same
// package.json using directory paths that differ only by a trailing
// separator (e.g. "node_modules/preact/compat" vs
// "node_modules/preact/compat/"). Because the cache uses first-writer-wins
// semantics, a later caller may receive an entry whose PackageDirectory
// doesn't match its own candidate path. Downstream code compares the
// candidate against PackageDirectory, so we must return a corrected
// shallow copy when they diverge.
// See https://github.com/microsoft/TypeScript/pull/50740.
func (p *InfoCacheEntry) WithPackageDirectory(packageDirectory string) *InfoCacheEntry {
if p.PackageDirectory == packageDirectory {
return p
}
return &InfoCacheEntry{
PackageDirectory: packageDirectory,
DirectoryExists: p.DirectoryExists,
Contents: p.Contents,
}
}
type InfoCache struct {
cache collections.SyncMap[tspath.Path, *InfoCacheEntry]
currentDirectory string
useCaseSensitiveFileNames bool
}
func NewInfoCache(currentDirectory string, useCaseSensitiveFileNames bool) *InfoCache {
return &InfoCache{
currentDirectory: currentDirectory,
useCaseSensitiveFileNames: useCaseSensitiveFileNames,
}
}
func (p *InfoCache) Get(packageJsonPath string) *InfoCacheEntry {
key := tspath.ToPath(packageJsonPath, p.currentDirectory, p.useCaseSensitiveFileNames)
if value, ok := p.cache.Load(key); ok {
return value
}
return nil
}
func (p *InfoCache) Set(packageJsonPath string, info *InfoCacheEntry) *InfoCacheEntry {
key := tspath.ToPath(packageJsonPath, p.currentDirectory, p.useCaseSensitiveFileNames)
actual, _ := p.cache.LoadOrStore(key, info)
return actual
}
func (p *InfoCache) Range(f func(key tspath.Path, value *InfoCacheEntry) bool) {
p.cache.Range(f)
}

View File

@@ -0,0 +1,75 @@
package packagejson
import (
"reflect"
"github.com/microsoft/typescript-go/internal/json"
)
type Expected[T any] struct {
actualJSONType string
Null bool
Valid bool
Value T
}
func (e *Expected[T]) UnmarshalJSON(data []byte) error {
if string(data) == "null" {
*e = Expected[T]{Null: true, actualJSONType: "null"}
return nil
}
if json.Unmarshal(data, &e.Value) == nil {
e.Valid = true
}
switch data[0] {
case '"':
e.actualJSONType = "string"
case 't', 'f':
e.actualJSONType = "boolean"
case '[':
e.actualJSONType = "array"
case '{':
e.actualJSONType = "object"
default:
e.actualJSONType = "number"
}
return nil
}
func (e *Expected[T]) IsPresent() bool {
return e.actualJSONType != ""
}
func (e *Expected[T]) GetValue() (value T, ok bool) {
return e.Value, e.Valid
}
func (e *Expected[T]) IsValid() bool {
return e.Valid
}
func (e *Expected[T]) ExpectedJSONType() string {
switch reflect.TypeFor[T]().Kind() {
case reflect.String:
return "string"
case reflect.Bool:
return "boolean"
case reflect.Slice, reflect.Array:
return "array"
case reflect.Map:
return "object"
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return "number"
default:
return "unknown"
}
}
func (e *Expected[T]) ActualJSONType() string {
return e.actualJSONType
}
func ExpectedOf[T any](value T) Expected[T] {
return Expected[T]{Value: value, Valid: true, actualJSONType: (*Expected[T])(nil).ExpectedJSONType()}
}

View File

@@ -0,0 +1,44 @@
package packagejson_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/packagejson"
"gotest.tools/v3/assert"
)
func TestExpected(t *testing.T) {
t.Parallel()
type packageJson struct {
Name packagejson.Expected[string] `json:"name"`
Version packagejson.Expected[string] `json:"version"`
Exports packagejson.Expected[any] `json:"exports"`
Main packagejson.Expected[string] `json:"main"`
}
var p packageJson
jsonString := `{
"name": "test",
"version": 2,
"exports": null
}`
err := json.Unmarshal([]byte(jsonString), &p)
assert.NilError(t, err)
assert.Equal(t, p.Name.Valid, true)
assert.Equal(t, p.Name.Value, "test")
assert.Equal(t, p.Version.Valid, false)
assert.Equal(t, p.Version.Value, "")
assert.Assert(t, p.Exports.Null)
assert.Equal(t, p.Exports.Valid, false)
assert.Equal(t, p.Main.Valid, false)
assert.Equal(t, p.Main.Null, false)
assert.Equal(t, p.Main.Value, "")
}

View File

@@ -0,0 +1,84 @@
package packagejson
import (
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/json"
)
type objectKind int8
const (
objectKindUnknown objectKind = iota
objectKindSubpaths
objectKindConditions
objectKindImports
objectKindInvalid
)
type ExportsOrImports struct {
JSONValue
objectKind objectKind
}
var _ json.UnmarshalerFrom = (*ExportsOrImports)(nil)
func (e *ExportsOrImports) UnmarshalJSONFrom(dec *json.Decoder) error {
return unmarshalJSONValueV2[ExportsOrImports](&e.JSONValue, dec)
}
func (e ExportsOrImports) AsObject() *collections.OrderedMap[string, ExportsOrImports] {
if e.Type != JSONValueTypeObject {
panic("expected object")
}
return e.Value.(*collections.OrderedMap[string, ExportsOrImports])
}
func (e ExportsOrImports) AsArray() []ExportsOrImports {
if e.Type != JSONValueTypeArray {
panic("expected array")
}
return e.Value.([]ExportsOrImports)
}
func (e ExportsOrImports) IsSubpaths() bool {
e.initObjectKind()
return e.objectKind == objectKindSubpaths
}
func (e ExportsOrImports) IsImports() bool {
e.initObjectKind()
return e.objectKind == objectKindImports
}
func (e ExportsOrImports) IsConditions() bool {
e.initObjectKind()
return e.objectKind == objectKindConditions
}
func (e *ExportsOrImports) initObjectKind() {
if e.objectKind == objectKindUnknown && e.Type == JSONValueTypeObject {
if obj := e.AsObject(); obj.Size() > 0 {
seenDot, seenHash, seenOther := false, false, false
for k := range obj.Keys() {
if len(k) > 0 {
seenDot = seenDot || k[0] == '.'
seenHash = seenHash || k[0] == '#'
seenOther = seenOther || (k[0] != '.' && k[0] != '#')
if seenOther && (seenDot || seenHash) {
e.objectKind = objectKindInvalid
return
}
}
}
if seenDot {
e.objectKind = objectKindSubpaths
return
}
if seenHash {
e.objectKind = objectKindImports
return
}
}
e.objectKind = objectKindConditions
}
}

View File

@@ -0,0 +1,62 @@
package packagejson_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/packagejson"
"gotest.tools/v3/assert"
)
func TestExports(t *testing.T) {
t.Parallel()
t.Run("UnmarshalJSONV2", func(t *testing.T) {
t.Parallel()
testExports(t, func(in []byte, out any) error { return json.Unmarshal(in, out) })
})
}
func testExports(t *testing.T, unmarshal func([]byte, any) error) {
type Exports struct {
Imports packagejson.ExportsOrImports `json:"imports"`
Exports packagejson.ExportsOrImports `json:"exports"`
}
var e Exports
jsonString := `{
"imports": {
"#foo": {
"import": "./foo.ts"
}
},
"exports": {
".": {
"import": "./test.ts",
"default": "./test.ts"
},
"./test": [
"./test1.ts",
"./test2.ts",
null
],
"./null": null
}
}`
err := unmarshal([]byte(jsonString), &e)
assert.NilError(t, err)
assert.Assert(t, e.Exports.IsSubpaths())
assert.Equal(t, e.Exports.AsObject().Size(), 3)
assert.Assert(t, e.Exports.AsObject().GetOrZero(".").IsConditions())
assert.Assert(t, e.Exports.AsObject().GetOrZero(".").AsObject().GetOrZero("import").Type == packagejson.JSONValueTypeString)
assert.Equal(t, e.Exports.AsObject().GetOrZero("./test").AsArray()[2].Type, packagejson.JSONValueTypeNull)
assert.Assert(t, e.Exports.AsObject().GetOrZero("./null").Type == packagejson.JSONValueTypeNull)
assert.Assert(t, e.Imports.IsImports())
assert.Equal(t, e.Imports.AsObject().Size(), 1)
assert.Assert(t, e.Imports.AsObject().GetOrZero("#foo").IsConditions())
assert.Assert(t, e.Imports.AsObject().GetOrZero("#foo").AsObject().GetOrZero("import").Type == packagejson.JSONValueTypeString)
}

View File

@@ -0,0 +1,175 @@
package packagejson
import (
"fmt"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/json"
)
type JSONValueType int8
const (
JSONValueTypeNotPresent JSONValueType = iota
JSONValueTypeNull
JSONValueTypeString
JSONValueTypeNumber
JSONValueTypeBoolean
JSONValueTypeArray
JSONValueTypeObject
)
func (t JSONValueType) String() string {
switch t {
case JSONValueTypeNull:
return "null"
case JSONValueTypeString:
return "string"
case JSONValueTypeNumber:
return "number"
case JSONValueTypeBoolean:
return "boolean"
case JSONValueTypeArray:
return "array"
case JSONValueTypeObject:
return "object"
default:
return fmt.Sprintf("unknown(%d)", t)
}
}
type JSONValue struct {
Type JSONValueType
Value any
}
func (v *JSONValue) IsPresent() bool {
return v.Type != JSONValueTypeNotPresent
}
func (v *JSONValue) IsFalsy() bool {
switch v.Type {
case JSONValueTypeNotPresent, JSONValueTypeNull:
return true
case JSONValueTypeString:
return v.Value == ""
case JSONValueTypeNumber:
return v.Value == 0
case JSONValueTypeBoolean:
return !v.Value.(bool)
default:
return false
}
}
func (v JSONValue) AsObject() *collections.OrderedMap[string, JSONValue] {
if v.Type != JSONValueTypeObject {
panic(fmt.Sprintf("expected object, got %v", v.Type))
}
return v.Value.(*collections.OrderedMap[string, JSONValue])
}
func (v JSONValue) AsArray() []JSONValue {
if v.Type != JSONValueTypeArray {
panic(fmt.Sprintf("expected array, got %v", v.Type))
}
return v.Value.([]JSONValue)
}
func (v JSONValue) AsString() string {
if v.Type != JSONValueTypeString {
panic(fmt.Sprintf("expected string, got %v", v.Type))
}
return v.Value.(string)
}
var _ json.UnmarshalerFrom = (*JSONValue)(nil)
func (v *JSONValue) UnmarshalJSONFrom(dec *json.Decoder) error {
return unmarshalJSONValueV2[JSONValue](v, dec)
}
func unmarshalJSONValue[T any](v *JSONValue, data []byte) error {
if string(data) == "null" {
*v = JSONValue{Type: JSONValueTypeNull}
} else if data[0] == '"' {
v.Type = JSONValueTypeString
return json.Unmarshal(data, &v.Value)
} else if data[0] == '[' {
var elements []T
if err := json.Unmarshal(data, &elements); err != nil {
return err
}
v.Type = JSONValueTypeArray
v.Value = elements
} else if data[0] == '{' {
var object collections.OrderedMap[string, T]
if err := json.Unmarshal(data, &object); err != nil {
return err
}
v.Type = JSONValueTypeObject
v.Value = &object
} else if string(data) == "true" {
v.Type = JSONValueTypeBoolean
v.Value = true
} else if string(data) == "false" {
v.Type = JSONValueTypeBoolean
v.Value = false
} else {
v.Type = JSONValueTypeNumber
return json.Unmarshal(data, &v.Value)
}
return nil
}
func unmarshalJSONValueV2[T any](v *JSONValue, dec *json.Decoder) error {
switch dec.PeekKind() {
case 'n': // json.Null.Kind()
if _, err := dec.ReadToken(); err != nil {
return err
}
v.Value = nil
v.Type = JSONValueTypeNull
return nil
case '"':
v.Type = JSONValueTypeString
if err := json.UnmarshalDecode(dec, &v.Value); err != nil {
return err
}
case '[':
if _, err := dec.ReadToken(); err != nil {
return err
}
var elements []T
for dec.PeekKind() != json.EndArray.Kind() {
var element T
if err := json.UnmarshalDecode(dec, &element); err != nil {
return err
}
elements = append(elements, element)
}
if _, err := dec.ReadToken(); err != nil {
return err
}
v.Type = JSONValueTypeArray
v.Value = elements
case '{':
var object collections.OrderedMap[string, T]
if err := json.UnmarshalDecode(dec, &object); err != nil {
return err
}
v.Type = JSONValueTypeObject
v.Value = &object
case 't', 'f': // json.True.Kind(), json.False.Kind()
v.Type = JSONValueTypeBoolean
if err := json.UnmarshalDecode(dec, &v.Value); err != nil {
return err
}
default:
v.Type = JSONValueTypeNumber
if err := json.UnmarshalDecode(dec, &v.Value); err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,83 @@
package packagejson_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/packagejson"
"gotest.tools/v3/assert"
)
func TestJSONValue(t *testing.T) {
t.Parallel()
t.Run("UnmarshalJSONV2", func(t *testing.T) {
t.Parallel()
testJSONValue(t, func(in []byte, out any) error { return json.Unmarshal(in, out) })
})
}
func testJSONValue(t *testing.T, unmarshal func([]byte, any) error) {
type packageJson struct {
Private packagejson.JSONValue `json:"private"`
False packagejson.JSONValue `json:"false"`
Name packagejson.JSONValue `json:"name"`
Version packagejson.JSONValue `json:"version"`
Exports packagejson.JSONValue `json:"exports"`
Imports packagejson.JSONValue `json:"imports"`
NotPresent packagejson.JSONValue `json:"notPresent"`
}
var p packageJson
jsonString := `{
"private": true,
"false": false,
"name": "test",
"version": 2,
"exports": {
".": {
"import": "./test.ts",
"default": "./test.ts"
},
"./test": [
"./test1.ts",
"./test2.ts",
null
],
"./null": null
},
"imports": null
}`
err := unmarshal([]byte(jsonString), &p)
assert.NilError(t, err)
assert.Equal(t, p.Private.Type, packagejson.JSONValueTypeBoolean)
assert.Equal(t, p.Private.Value, true)
assert.Equal(t, p.Name.Type, packagejson.JSONValueTypeString)
assert.Equal(t, p.Name.Value, "test")
assert.Equal(t, p.Version.Type, packagejson.JSONValueTypeNumber)
assert.Equal(t, p.Version.Value, float64(2))
assert.Equal(t, p.Exports.Type, packagejson.JSONValueTypeObject)
assert.Equal(t, p.Exports.AsObject().Size(), 3)
assert.Equal(t, p.Exports.AsObject().GetOrZero(".").Type, packagejson.JSONValueTypeObject)
assert.Equal(t, p.Exports.AsObject().GetOrZero(".").AsObject().GetOrZero("import").Value, "./test.ts")
assert.Equal(t, p.Exports.AsObject().GetOrZero("./test").Type, packagejson.JSONValueTypeArray)
assert.Equal(t, len(p.Exports.AsObject().GetOrZero("./test").AsArray()), 3)
assert.Equal(t, p.Exports.AsObject().GetOrZero("./test").AsArray()[0].Value, "./test1.ts")
assert.Equal(t, p.Exports.AsObject().GetOrZero("./test").AsArray()[1].Value, "./test2.ts")
assert.Equal(t, p.Exports.AsObject().GetOrZero("./test").AsArray()[2].Type, packagejson.JSONValueTypeNull)
assert.Equal(t, p.Exports.AsObject().GetOrZero("./null").Type, packagejson.JSONValueTypeNull)
assert.Equal(t, p.Imports.Type, packagejson.JSONValueTypeNull)
assert.Equal(t, p.Imports.Value, nil)
assert.Equal(t, p.NotPresent.Type, packagejson.JSONValueTypeNotPresent)
assert.Equal(t, p.NotPresent.Value, nil)
}

View File

@@ -0,0 +1,122 @@
package packagejson
import (
"github.com/microsoft/typescript-go/internal/collections"
json "github.com/microsoft/typescript-go/internal/json"
)
type HeaderFields struct {
Name Expected[string] `json:"name"`
Version Expected[string] `json:"version"`
Type Expected[string] `json:"type"`
}
type PathFields struct {
TSConfig Expected[string] `json:"tsconfig"`
Main Expected[string] `json:"main"`
Types Expected[string] `json:"types"`
Typings Expected[string] `json:"typings"`
TypesVersions JSONValue `json:"typesVersions"`
Imports ExportsOrImports `json:"imports"`
Exports ExportsOrImports `json:"exports"`
}
type DependencyFields struct {
Dependencies Expected[map[string]string] `json:"dependencies"`
DevDependencies Expected[map[string]string] `json:"devDependencies"`
PeerDependencies Expected[map[string]string] `json:"peerDependencies"`
OptionalDependencies Expected[map[string]string] `json:"optionalDependencies"`
}
// HasDependency returns true if the package.json has a dependency with the given name
// under any of the dependency fields (dependencies, devDependencies, peerDependencies,
// optionalDependencies).
func (df *DependencyFields) HasDependency(name string) bool {
if deps, ok := df.Dependencies.GetValue(); ok {
if _, ok := deps[name]; ok {
return true
}
}
if devDeps, ok := df.DevDependencies.GetValue(); ok {
if _, ok := devDeps[name]; ok {
return true
}
}
if peerDeps, ok := df.PeerDependencies.GetValue(); ok {
if _, ok := peerDeps[name]; ok {
return true
}
}
if optDeps, ok := df.OptionalDependencies.GetValue(); ok {
if _, ok := optDeps[name]; ok {
return true
}
}
return false
}
func (df *DependencyFields) RangeDependencies(f func(name, version, dependencyField string) bool) {
if deps, ok := df.Dependencies.GetValue(); ok {
for name, version := range deps {
if !f(name, version, "dependencies") {
return
}
}
}
if devDeps, ok := df.DevDependencies.GetValue(); ok {
for name, version := range devDeps {
if !f(name, version, "devDependencies") {
return
}
}
}
if peerDeps, ok := df.PeerDependencies.GetValue(); ok {
for name, version := range peerDeps {
if !f(name, version, "peerDependencies") {
return
}
}
}
if optDeps, ok := df.OptionalDependencies.GetValue(); ok {
for name, version := range optDeps {
if !f(name, version, "optionalDependencies") {
return
}
}
}
}
func (df *DependencyFields) GetRuntimeDependencyNames() *collections.Set[string] {
var count int
deps, _ := df.Dependencies.GetValue()
count += len(deps)
peerDeps, _ := df.PeerDependencies.GetValue()
count += len(peerDeps)
optDeps, _ := df.OptionalDependencies.GetValue()
count += len(optDeps)
names := collections.NewSetWithSizeHint[string](count)
for name := range deps {
names.Add(name)
}
for name := range peerDeps {
names.Add(name)
}
for name := range optDeps {
names.Add(name)
}
return names
}
type Fields struct {
HeaderFields
PathFields
DependencyFields
}
func Parse(data []byte) (Fields, error) {
var f Fields
if err := json.Unmarshal(data, &f, json.AllowDuplicateNames(true)); err != nil {
return Fields{}, err
}
return f, nil
}

View File

@@ -0,0 +1,103 @@
package packagejson_test
import (
"path/filepath"
"testing"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/packagejson"
"github.com/microsoft/typescript-go/internal/parser"
"github.com/microsoft/typescript-go/internal/repo"
"github.com/microsoft/typescript-go/internal/testutil/filefixture"
"github.com/microsoft/typescript-go/internal/tspath"
"gotest.tools/v3/assert"
)
var packageJsonFixtures = []filefixture.Fixture{
filefixture.FromFile("package.json", filepath.Join(repo.RootPath(), "package.json")),
filefixture.FromFile("date-fns.json", filepath.Join(repo.TestDataPath(), "fixtures", "packagejson", "date-fns.json")),
}
func BenchmarkPackageJSON(b *testing.B) {
for _, f := range packageJsonFixtures {
f.SkipIfNotExist(b)
content := []byte(f.ReadFile(b))
b.Run("UnmarshalJSON", func(b *testing.B) {
b.Run(f.Name(), func(b *testing.B) {
for b.Loop() {
var p packagejson.Fields
if err := json.Unmarshal(content, &p); err != nil {
b.Fatal(err)
}
}
})
})
b.Run("UnmarshalJSONV2", func(b *testing.B) {
b.Run(f.Name(), func(b *testing.B) {
for b.Loop() {
var p packagejson.Fields
if err := json.Unmarshal(content, &p); err != nil {
b.Fatal(err)
}
}
})
})
b.Run("ParseJSONText", func(b *testing.B) {
b.Run(f.Name(), func(b *testing.B) {
fileName := "/" + f.Name()
for b.Loop() {
parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: fileName,
Path: tspath.Path(fileName),
}, string(content), core.ScriptKindJSON)
}
})
})
}
}
func TestParse(t *testing.T) {
t.Parallel()
tests := []struct {
name string
content string
want packagejson.Fields
}{
{
name: "duplicate names",
content: `{
"name": "test-package",
"name": "test-package",
"version": "1.0.0"
}`,
want: packagejson.Fields{
HeaderFields: packagejson.HeaderFields{
Name: packagejson.ExpectedOf("test-package"),
Version: packagejson.ExpectedOf("1.0.0"),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := packagejson.Parse([]byte(tt.content))
assert.NilError(t, err)
assert.DeepEqual(t, got, tt.want, cmpopts.IgnoreUnexported(
packagejson.Fields{},
packagejson.HeaderFields{},
packagejson.Expected[string]{},
packagejson.Expected[map[string]string]{},
packagejson.ExportsOrImports{},
))
})
}
}

View File

@@ -0,0 +1,8 @@
package packagejson
type TypeValidatedField interface {
IsPresent() bool
IsValid() bool
ExpectedJSONType() string
ActualJSONType() string
}