1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
package fs
import (
"bytes"
"encoding/json"
"sync"
"github.com/vercel/turbo/cli/internal/lockfile"
"github.com/vercel/turbo/cli/internal/turbopath"
)
// PackageJSON represents NodeJS package.json
type PackageJSON struct {
Name string `json:"name"`
Version string `json:"version"`
Scripts map[string]string `json:"scripts"`
Dependencies map[string]string `json:"dependencies"`
DevDependencies map[string]string `json:"devDependencies"`
OptionalDependencies map[string]string `json:"optionalDependencies"`
PeerDependencies map[string]string `json:"peerDependencies"`
PackageManager string `json:"packageManager"`
Os []string `json:"os"`
Workspaces Workspaces `json:"workspaces"`
Private bool `json:"private"`
// Exact JSON object stored in package.json including unknown fields
// During marshalling struct fields will take priority over raw fields
RawJSON map[string]interface{} `json:"-"`
// relative path from repo root to the package.json file
PackageJSONPath turbopath.AnchoredSystemPath `json:"-"`
// relative path from repo root to the package
Dir turbopath.AnchoredSystemPath `json:"-"`
InternalDeps []string `json:"-"`
UnresolvedExternalDeps map[string]string `json:"-"`
TransitiveDeps []lockfile.Package `json:"-"`
LegacyTurboConfig *TurboJSON `json:"turbo"`
Mu sync.Mutex `json:"-"`
ExternalDepsHash string `json:"-"`
}
type Workspaces []string
type WorkspacesAlt struct {
Packages []string `json:"packages,omitempty"`
}
func (r *Workspaces) UnmarshalJSON(data []byte) error {
var tmp = &WorkspacesAlt{}
if err := json.Unmarshal(data, tmp); err == nil {
*r = Workspaces(tmp.Packages)
return nil
}
var tempstr = []string{}
if err := json.Unmarshal(data, &tempstr); err != nil {
return err
}
*r = tempstr
return nil
}
// ReadPackageJSON returns a struct of package.json
func ReadPackageJSON(path turbopath.AbsoluteSystemPath) (*PackageJSON, error) {
b, err := path.ReadFile()
if err != nil {
return nil, err
}
return UnmarshalPackageJSON(b)
}
// UnmarshalPackageJSON decodes a byte slice into a PackageJSON struct
func UnmarshalPackageJSON(data []byte) (*PackageJSON, error) {
var rawJSON map[string]interface{}
if err := json.Unmarshal(data, &rawJSON); err != nil {
return nil, err
}
pkgJSON := &PackageJSON{}
if err := json.Unmarshal(data, &pkgJSON); err != nil {
return nil, err
}
pkgJSON.RawJSON = rawJSON
return pkgJSON, nil
}
// MarshalPackageJSON Serialize PackageJSON to a slice of bytes
func MarshalPackageJSON(pkgJSON *PackageJSON) ([]byte, error) {
structuredContent, err := json.Marshal(pkgJSON)
if err != nil {
return nil, err
}
var structuredFields map[string]interface{}
if err := json.Unmarshal(structuredContent, &structuredFields); err != nil {
return nil, err
}
fieldsToSerialize := make(map[string]interface{}, len(pkgJSON.RawJSON))
// copy pkgJSON.RawJSON
for key, value := range pkgJSON.RawJSON {
fieldsToSerialize[key] = value
}
for key, value := range structuredFields {
if isEmpty(value) {
delete(fieldsToSerialize, key)
} else {
fieldsToSerialize[key] = value
}
}
var b bytes.Buffer
encoder := json.NewEncoder(&b)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
if err := encoder.Encode(fieldsToSerialize); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func isEmpty(value interface{}) bool {
if value == nil {
return true
}
switch s := value.(type) {
case string:
return s == ""
case bool:
return !s
case []string:
return len(s) == 0
case map[string]interface{}:
return len(s) == 0
case Workspaces:
return len(s) == 0
default:
// Assume any unknown types aren't empty
return false
}
}
|