devkits-csv-json 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -0
- package/index.js +206 -0
- package/package.json +47 -0
- package/test.js +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# devkits-csv-json
|
|
2
|
+
|
|
3
|
+
> Convert between CSV and JSON formats
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g devkits-csv-json
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use without installation:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx devkits-csv-json [args]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# CSV to JSON
|
|
21
|
+
dk-csv -j "name,age\nJohn,30\nJane,25"
|
|
22
|
+
|
|
23
|
+
# JSON to CSV
|
|
24
|
+
dk-csv -c '[{"name":"John","age":30}]'
|
|
25
|
+
|
|
26
|
+
# From stdin (auto-detects format)
|
|
27
|
+
cat data.csv | dk-csv
|
|
28
|
+
cat data.json | dk-csv
|
|
29
|
+
|
|
30
|
+
# Custom delimiter
|
|
31
|
+
dk-csv -j "name;age\nJohn;30" -d ";"
|
|
32
|
+
|
|
33
|
+
# Pretty print JSON
|
|
34
|
+
dk-csv -j "name,age\nJohn,30" --pretty
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Output Examples
|
|
38
|
+
|
|
39
|
+
### CSV to JSON
|
|
40
|
+
```bash
|
|
41
|
+
$ dk-csv -j "name,age\nJohn,30\nJane,25"
|
|
42
|
+
|
|
43
|
+
[{"name":"John","age":30},{"name":"Jane","age":25}]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### JSON to CSV
|
|
47
|
+
```bash
|
|
48
|
+
$ dk-csv -c '[{"name":"John","age":30}]'
|
|
49
|
+
|
|
50
|
+
name,age
|
|
51
|
+
John,30
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Pretty JSON
|
|
55
|
+
```bash
|
|
56
|
+
$ dk-csv -j "name,age\nJohn,30" --pretty
|
|
57
|
+
|
|
58
|
+
[
|
|
59
|
+
{
|
|
60
|
+
"name": "John",
|
|
61
|
+
"age": 30
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Why @devkits/csv-json?
|
|
67
|
+
|
|
68
|
+
- ✅ Fast - Instant conversion
|
|
69
|
+
- ✅ Zero dependencies - Pure Node.js
|
|
70
|
+
- ✅ Offline - Works without internet
|
|
71
|
+
- ✅ Free - Open source (MIT)
|
|
72
|
+
- ✅ CLI - Use in your terminal
|
|
73
|
+
- ✅ Auto-detect from stdin
|
|
74
|
+
- ✅ Custom delimiter support
|
|
75
|
+
|
|
76
|
+
## Web Version
|
|
77
|
+
|
|
78
|
+
Prefer a GUI? Check out the web version: **https://devkits-tools.surge.sh/tools/csv-to-json**
|
|
79
|
+
|
|
80
|
+
- 84 developer tools in one PWA
|
|
81
|
+
- Works offline
|
|
82
|
+
- No signup required
|
|
83
|
+
|
|
84
|
+
## Pro Features
|
|
85
|
+
|
|
86
|
+
Upgrade to Pro for advanced features:
|
|
87
|
+
|
|
88
|
+
- 📦 Batch Processing
|
|
89
|
+
- 🔗 API Access (1000 req/day free)
|
|
90
|
+
- ☁️ Cloud Sync
|
|
91
|
+
- 🎨 Custom Themes
|
|
92
|
+
- 📊 Advanced Analytics
|
|
93
|
+
- 💬 Priority Support
|
|
94
|
+
|
|
95
|
+
**Price:** $9 one-time payment
|
|
96
|
+
**Upgrade:** https://devkits-tools.surge.sh/pro
|
|
97
|
+
**Discount:** Use code `EARLYBIRD-2026` for 20% off
|
|
98
|
+
|
|
99
|
+
## See Also
|
|
100
|
+
|
|
101
|
+
Part of the **[DevKits Tools](https://devkits-tools.surge.sh)** collection — 80+ free developer tools:
|
|
102
|
+
|
|
103
|
+
### Popular Tools
|
|
104
|
+
|
|
105
|
+
| Tool | npm Package | Description |
|
|
106
|
+
|------|-------------|-------------|
|
|
107
|
+
| **[Base64](https://devkits-tools.surge.sh/tools/base64)** | `@devkits/base64` | Encode/decode Base64 |
|
|
108
|
+
| **[JSON Formatter](https://devkits-tools.surge.sh/tools/json-formatter)** | `@devkits/json-formatter` | Format and validate JSON |
|
|
109
|
+
| **[Color Converter](https://devkits-tools.surge.sh/tools/color-converter)** | `@devkits/color-converter` | HEX/RGB/HSL conversion |
|
|
110
|
+
| **[UUID Generator](https://devkits-tools.surge.sh/tools/uuid-generator)** | `@devkits/uuid-generator` | Generate unique UUIDs |
|
|
111
|
+
| **[Hash Generator](https://devkits-tools.surge.sh/tools/hash-generator)** | `@devkits/hash-generator` | MD5, SHA1, SHA256, SHA512 |
|
|
112
|
+
| **[Regex Tester](https://devkits-tools.surge.sh/tools/regex-tester)** | `@devkits/regex-tester` | Test regex patterns |
|
|
113
|
+
|
|
114
|
+
### Other DevKits Tools
|
|
115
|
+
|
|
116
|
+
- **[HTML Tools](https://devkits-tools.surge.sh/tools/html-entities)** — HTML entity encode/decode
|
|
117
|
+
- **[CSS Tools](https://devkits-tools.surge.sh/tools/css-minifier)** — CSS minify/format
|
|
118
|
+
- **[Cron Parser](https://devkits-tools.surge.sh/tools/cron-parser)** — Parse cron expressions
|
|
119
|
+
- **[Case Convert](https://devkits-tools.surge.sh/tools/text-case)** — camelCase, snake_case, etc.
|
|
120
|
+
- **[Slugify](https://devkits-tools.surge.sh/tools/slug-generator)** — Create URL-friendly slugs
|
|
121
|
+
- **[Lorem Ipsum](https://devkits-tools.surge.sh/tools/lorem-ipsum)** — Generate placeholder text
|
|
122
|
+
- **[Password Generator](https://devkits-tools.surge.sh/tools/password-generator)** — Secure passwords
|
|
123
|
+
- **[Text Counter](https://devkits-tools.surge.sh/tools/text-counter)** — Word/char counter
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### More from DevKits
|
|
128
|
+
|
|
129
|
+
- **[Invoicely](https://invoicely-app.surge.sh)** — Free invoice generator for freelancers
|
|
130
|
+
- **[SnapOG](https://snapog.surge.sh)** — Free OG image generator with 20+ templates
|
|
131
|
+
- **[API Monitor](https://api-monitor-saas.surge.sh)** — Real-time API monitoring
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
👉 **Explore all 80+ developer tools at [DevKits Tools](https://devkits-tools.surge.sh)**
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT © [DevKits Team](https://devkits-tools.surge.sh)
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 🚀 API Monitoring for Developers
|
|
144
|
+
|
|
145
|
+
Build better APIs with **API Monitor SaaS** — real-time monitoring, alerting, and analytics.
|
|
146
|
+
|
|
147
|
+
👉 **Early Access: $1 pre-order (50% off for life)** → https://api-monitor-saas.surge.sh?utm_source=npm&utm_medium=readme&utm_campaign=phase0
|
package/index.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @devkits/csv-json - Convert between CSV and JSON formats
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* dk-csv -j 'name,age\nJohn,30' # CSV to JSON
|
|
8
|
+
* dk-csv -c '[{"name":"John"}]' # JSON to CSV
|
|
9
|
+
* cat file.csv | dk-csv -j # File conversion
|
|
10
|
+
*
|
|
11
|
+
* Web version: https://devkits-tools.surge.sh/tools/csv-to-json
|
|
12
|
+
* Pro features: https://devkits-tools.surge.sh/pro
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Parse CSV to array of objects
|
|
16
|
+
function csvToJson(csv, options = {}) {
|
|
17
|
+
const { delimiter = ',', header = true } = options;
|
|
18
|
+
const lines = csv.trim().split('\n');
|
|
19
|
+
|
|
20
|
+
if (lines.length === 0) return [];
|
|
21
|
+
|
|
22
|
+
const parseLine = (line) => {
|
|
23
|
+
const result = [];
|
|
24
|
+
let current = '';
|
|
25
|
+
let inQuotes = false;
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < line.length; i++) {
|
|
28
|
+
const char = line[i];
|
|
29
|
+
|
|
30
|
+
if (char === '"') {
|
|
31
|
+
inQuotes = !inQuotes;
|
|
32
|
+
} else if (char === delimiter && !inQuotes) {
|
|
33
|
+
result.push(current.trim());
|
|
34
|
+
current = '';
|
|
35
|
+
} else {
|
|
36
|
+
current += char;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
result.push(current.trim());
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const headers = parseLine(lines[0]);
|
|
44
|
+
const data = [];
|
|
45
|
+
|
|
46
|
+
for (let i = header ? 1 : 0; i < lines.length; i++) {
|
|
47
|
+
const values = parseLine(lines[i]);
|
|
48
|
+
if (header) {
|
|
49
|
+
const obj = {};
|
|
50
|
+
headers.forEach((h, idx) => {
|
|
51
|
+
let value = values[idx] || '';
|
|
52
|
+
// Try to parse numbers
|
|
53
|
+
if (/^-?\d+$/.test(value)) {
|
|
54
|
+
value = parseInt(value, 10);
|
|
55
|
+
} else if (/^-?\d+\.\d+$/.test(value)) {
|
|
56
|
+
value = parseFloat(value);
|
|
57
|
+
} else if (value.toLowerCase() === 'true') {
|
|
58
|
+
value = true;
|
|
59
|
+
} else if (value.toLowerCase() === 'false') {
|
|
60
|
+
value = false;
|
|
61
|
+
}
|
|
62
|
+
obj[h] = value;
|
|
63
|
+
});
|
|
64
|
+
data.push(obj);
|
|
65
|
+
} else {
|
|
66
|
+
data.push(values);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return header ? data : { headers, data };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Convert JSON array to CSV
|
|
74
|
+
function jsonToCsv(data, options = {}) {
|
|
75
|
+
const { delimiter = ',', includeHeader = true } = options;
|
|
76
|
+
|
|
77
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const headers = Object.keys(data[0]);
|
|
82
|
+
const lines = [];
|
|
83
|
+
|
|
84
|
+
if (includeHeader) {
|
|
85
|
+
lines.push(headers.join(delimiter));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const row of data) {
|
|
89
|
+
const values = headers.map(h => {
|
|
90
|
+
const value = row[h];
|
|
91
|
+
if (typeof value === 'string' && (value.includes(delimiter) || value.includes('"') || value.includes('\n'))) {
|
|
92
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
93
|
+
}
|
|
94
|
+
return value;
|
|
95
|
+
});
|
|
96
|
+
lines.push(values.join(delimiter));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return lines.join('\n');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function showHelp() {
|
|
103
|
+
console.log(`
|
|
104
|
+
@devkits/csv-json - Convert between CSV and JSON
|
|
105
|
+
|
|
106
|
+
Usage:
|
|
107
|
+
dk-csv -j 'name,age\\nJohn,30' # CSV to JSON
|
|
108
|
+
dk-csv -c '[{"name":"John"}]' # JSON to CSV
|
|
109
|
+
cat file.csv | dk-csv -j # CSV file to JSON
|
|
110
|
+
cat file.json | dk-csv -c # JSON file to CSV
|
|
111
|
+
|
|
112
|
+
Options:
|
|
113
|
+
-j, --to-json <csv> Convert CSV string to JSON
|
|
114
|
+
-c, --to-csv <json> Convert JSON string to CSV
|
|
115
|
+
-d, --delimiter <char> CSV delimiter (default: ,)
|
|
116
|
+
-p, --pretty Pretty print JSON output
|
|
117
|
+
-n, --no-header CSV has no header row
|
|
118
|
+
-h, --help Show this help message
|
|
119
|
+
|
|
120
|
+
Examples:
|
|
121
|
+
dk-csv -j "name,age\\nJohn,30\\nJane,25"
|
|
122
|
+
dk-csv -c '[{"name":"John","age":30}]'
|
|
123
|
+
cat data.csv | dk-csv -j
|
|
124
|
+
cat data.json | dk-csv -c --pretty
|
|
125
|
+
|
|
126
|
+
Web version: https://devkits-tools.surge.sh/tools/csv-to-json
|
|
127
|
+
Pro features: https://devkits-tools.surge.sh/pro
|
|
128
|
+
`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Main entry point
|
|
132
|
+
const args = process.argv.slice(2);
|
|
133
|
+
|
|
134
|
+
if (!args.length || args.includes('-h') || args.includes('--help')) {
|
|
135
|
+
showHelp();
|
|
136
|
+
process.exit(0);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Parse options
|
|
140
|
+
let toJson = null;
|
|
141
|
+
let toCsv = null;
|
|
142
|
+
let delimiter = ',';
|
|
143
|
+
let pretty = false;
|
|
144
|
+
let noHeader = false;
|
|
145
|
+
|
|
146
|
+
for (let i = 0; i < args.length; i++) {
|
|
147
|
+
if (args[i] === '-j' || args[i] === '--to-json') {
|
|
148
|
+
toJson = args[++i];
|
|
149
|
+
} else if (args[i] === '-c' || args[i] === '--to-csv') {
|
|
150
|
+
toCsv = args[++i];
|
|
151
|
+
} else if (args[i] === '-d' || args[i] === '--delimiter') {
|
|
152
|
+
delimiter = args[++i];
|
|
153
|
+
} else if (args[i] === '-p' || args[i] === '--pretty') {
|
|
154
|
+
pretty = true;
|
|
155
|
+
} else if (args[i] === '-n' || args[i] === '--no-header') {
|
|
156
|
+
noHeader = true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Process input
|
|
161
|
+
const processInput = (input, isCsv) => {
|
|
162
|
+
try {
|
|
163
|
+
if (isCsv) {
|
|
164
|
+
// CSV to JSON - handle escaped newlines
|
|
165
|
+
const csv = input.replace(/\\n/g, '\n');
|
|
166
|
+
const result = csvToJson(csv, { delimiter, header: !noHeader });
|
|
167
|
+
console.log(pretty ? JSON.stringify(result, null, 2) : JSON.stringify(result));
|
|
168
|
+
} else {
|
|
169
|
+
// JSON to CSV
|
|
170
|
+
const data = JSON.parse(input);
|
|
171
|
+
console.log(jsonToCsv(data, { delimiter, includeHeader: !noHeader }));
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error(`Error: ${err.message}`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
if (toJson !== null) {
|
|
180
|
+
processInput(toJson, true);
|
|
181
|
+
} else if (toCsv !== null) {
|
|
182
|
+
processInput(toCsv, false);
|
|
183
|
+
} else if (!process.stdin.isTTY) {
|
|
184
|
+
let stdin = '';
|
|
185
|
+
process.stdin.setEncoding('utf8');
|
|
186
|
+
process.stdin.on('readable', () => {
|
|
187
|
+
let chunk;
|
|
188
|
+
while ((chunk = process.stdin.read()) !== null) {
|
|
189
|
+
stdin += chunk;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
process.stdin.on('end', () => {
|
|
193
|
+
if (stdin.trim()) {
|
|
194
|
+
// Auto-detect format
|
|
195
|
+
const trimmed = stdin.trim();
|
|
196
|
+
const isCsv = !trimmed.startsWith('[') && !trimmed.startsWith('{');
|
|
197
|
+
processInput(stdin, isCsv);
|
|
198
|
+
} else {
|
|
199
|
+
showHelp();
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
} else {
|
|
203
|
+
console.error('Error: Please provide input with -j or -c flag, or pipe from stdin');
|
|
204
|
+
showHelp();
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "devkits-csv-json",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Convert between CSV and JSON formats",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dk-csv": "./index.js",
|
|
8
|
+
"csv-json": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node test.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"csv",
|
|
15
|
+
"json",
|
|
16
|
+
"convert",
|
|
17
|
+
"parser",
|
|
18
|
+
"devkits",
|
|
19
|
+
"developer-tools",
|
|
20
|
+
"cli",
|
|
21
|
+
"command-line",
|
|
22
|
+
"nodejs",
|
|
23
|
+
"utility",
|
|
24
|
+
"productivity",
|
|
25
|
+
"dev-tools",
|
|
26
|
+
"free-tools"
|
|
27
|
+
],
|
|
28
|
+
"author": "DevKits Team <devkits-auto@protonmail.com>",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/devkits/tools"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://devkits-tools.surge.sh/tools/csv-to-json",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/devkits/tools/issues"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=14.0.0"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"index.js",
|
|
43
|
+
"README.md",
|
|
44
|
+
"test.js"
|
|
45
|
+
],
|
|
46
|
+
"dependencies": {}
|
|
47
|
+
}
|
package/test.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tests for @devkits/csv-json
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const tests = [
|
|
10
|
+
{
|
|
11
|
+
name: 'CSV to JSON',
|
|
12
|
+
cmd: 'node index.js -j "name,age\\nJohn,30"',
|
|
13
|
+
check: (result) => {
|
|
14
|
+
const data = JSON.parse(result);
|
|
15
|
+
return Array.isArray(data) && data[0].name === 'John' && data[0].age === 30;
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'JSON to CSV',
|
|
20
|
+
cmd: 'node index.js -c \'[{"name":"John","age":30}]\'',
|
|
21
|
+
check: (result) => result.includes('name,age') && result.includes('John,30')
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'CSV with multiple rows',
|
|
25
|
+
cmd: 'node index.js -j "name,age\\nJohn,30\\nJane,25"',
|
|
26
|
+
check: (result) => {
|
|
27
|
+
const data = JSON.parse(result);
|
|
28
|
+
return data.length === 2 && data[1].name === 'Jane';
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Pretty JSON output',
|
|
33
|
+
cmd: 'node index.js -j "name,age\\nJohn,30" --pretty',
|
|
34
|
+
check: (result) => result.includes('{\n') || result.split('\n').length > 1
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'Custom delimiter',
|
|
38
|
+
cmd: 'node index.js -j "name;age\\nJohn;30" -d ";"',
|
|
39
|
+
check: (result) => {
|
|
40
|
+
const data = JSON.parse(result);
|
|
41
|
+
return data[0].name === 'John';
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
let passed = 0;
|
|
47
|
+
let failed = 0;
|
|
48
|
+
|
|
49
|
+
console.log('Running @devkits/csv-json tests...\n');
|
|
50
|
+
|
|
51
|
+
for (const test of tests) {
|
|
52
|
+
try {
|
|
53
|
+
const result = execSync(test.cmd, { encoding: 'utf8' }).trim();
|
|
54
|
+
if (test.check(result)) {
|
|
55
|
+
console.log(`✅ ${test.name}`);
|
|
56
|
+
passed++;
|
|
57
|
+
} else {
|
|
58
|
+
console.log(`❌ ${test.name}`);
|
|
59
|
+
console.log(` Got: ${result}`);
|
|
60
|
+
failed++;
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.log(`❌ ${test.name} - Error: ${err.message}`);
|
|
64
|
+
failed++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log(`\n${passed}/${tests.length} tests passed`);
|
|
69
|
+
process.exit(failed > 0 ? 1 : 0);
|