vcf-to-json-converter 1.0.0 → 1.0.2
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/LICENSE +21 -21
- package/README.md +457 -241
- package/index.d.ts +32 -32
- package/index.js +252 -252
- package/package.json +7 -6
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Shubhanshu Rao
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shubhanshu Rao
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,241 +1,457 @@
|
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
1
|
+
# vcf-to-json-converter
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/vcf-to-json-converter)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](https://npmjs.org/package/vcf-to-json-converter)
|
|
7
|
+
|
|
8
|
+
A lightweight, fast, and reliable Node.js library to convert VCF (vCard) files to structured JSON format with full CLI support.
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
const { vcfToJsonFromFile } = require("vcf-to-json-converter");
|
|
12
|
+
|
|
13
|
+
const contacts = vcfToJsonFromFile("./contacts.vcf");
|
|
14
|
+
console.log(contacts);
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Table of Contents
|
|
18
|
+
|
|
19
|
+
- [Features](#features)
|
|
20
|
+
- [Installation](#installation)
|
|
21
|
+
- [Quick Start](#quick-start)
|
|
22
|
+
- [Usage](#usage)
|
|
23
|
+
- [Command Line Interface](#command-line-interface)
|
|
24
|
+
- [Programmatic Usage](#programmatic-usage)
|
|
25
|
+
- [TypeScript](#typescript)
|
|
26
|
+
- [API Reference](#api-reference)
|
|
27
|
+
- [Output Format](#output-format)
|
|
28
|
+
- [Supported vCard Fields](#supported-vcard-fields)
|
|
29
|
+
- [Examples](#examples)
|
|
30
|
+
- [Troubleshooting](#troubleshooting)
|
|
31
|
+
- [Contributing](#contributing)
|
|
32
|
+
- [License](#license)
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- **Complete VCF Support** - Convert VCF/vCard files (v2.1, v3.0, v4.0) to structured JSON
|
|
37
|
+
- **Programmatic API** - Easy integration into Node.js applications
|
|
38
|
+
- **CLI Tool** - Command-line interface for quick conversions
|
|
39
|
+
- **Robust Error Handling** - Comprehensive error handling and validation
|
|
40
|
+
- **Rich Data Extraction** - Extracts all contact information including photos, organizations, and notes
|
|
41
|
+
- **TypeScript Ready** - Full TypeScript definitions included
|
|
42
|
+
- **High Performance** - Efficiently processes large VCF files with multiple contacts
|
|
43
|
+
- **Cross Platform** - Works on Windows, macOS, and Linux
|
|
44
|
+
- **Zero Dependencies** - No external dependencies for maximum compatibility
|
|
45
|
+
- **UTF-8 Support** - Handles international characters and emojis correctly
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
Using npm:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install vcf-to-json-converter
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For global CLI usage:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install -g vcf-to-json-converter
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Using yarn:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
yarn add vcf-to-json-converter
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Quick Start
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Install globally for CLI
|
|
71
|
+
npm install -g vcf-to-json-converter
|
|
72
|
+
|
|
73
|
+
# Convert a VCF file
|
|
74
|
+
vcf-to-json contacts.vcf output.json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
// Use in your Node.js project
|
|
79
|
+
const { vcfToJsonFromFile } = require("vcf-to-json-converter");
|
|
80
|
+
|
|
81
|
+
const contacts = vcfToJsonFromFile("./contacts.vcf");
|
|
82
|
+
console.log(`Converted ${contacts.length} contacts`);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
### Command Line Interface
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Convert and display JSON in terminal
|
|
91
|
+
vcf-to-json contacts.vcf
|
|
92
|
+
|
|
93
|
+
# Convert and save to file
|
|
94
|
+
vcf-to-json contacts.vcf output.json
|
|
95
|
+
|
|
96
|
+
# Convert with compact formatting (minified JSON)
|
|
97
|
+
vcf-to-json contacts.vcf output.json --compact
|
|
98
|
+
|
|
99
|
+
# Show help and version
|
|
100
|
+
vcf-to-json --help
|
|
101
|
+
vcf-to-json --version
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Programmatic Usage
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
const {
|
|
108
|
+
vcfToJson,
|
|
109
|
+
vcfToJsonFromFile,
|
|
110
|
+
saveJsonToFile,
|
|
111
|
+
} = require("vcf-to-json-converter");
|
|
112
|
+
|
|
113
|
+
// Method 1: Convert VCF text content
|
|
114
|
+
const vcfContent = `BEGIN:VCARD
|
|
115
|
+
VERSION:3.0
|
|
116
|
+
FN:John Doe
|
|
117
|
+
N:Doe;John;;;
|
|
118
|
+
TEL;TYPE=CELL:+1234567890
|
|
119
|
+
EMAIL;TYPE=HOME:john@example.com
|
|
120
|
+
ORG:ACME Corp
|
|
121
|
+
TITLE:Software Engineer
|
|
122
|
+
END:VCARD`;
|
|
123
|
+
|
|
124
|
+
const contacts = vcfToJson(vcfContent);
|
|
125
|
+
console.log("Parsed contacts:", contacts.length);
|
|
126
|
+
|
|
127
|
+
// Method 2: Convert VCF file directly
|
|
128
|
+
try {
|
|
129
|
+
const contacts = vcfToJsonFromFile("./contacts.vcf");
|
|
130
|
+
console.log(`Converted ${contacts.length} contacts`);
|
|
131
|
+
|
|
132
|
+
// Save to JSON file with pretty formatting
|
|
133
|
+
saveJsonToFile(contacts, "./output.json", true);
|
|
134
|
+
|
|
135
|
+
// Access contact data
|
|
136
|
+
contacts.forEach((contact) => {
|
|
137
|
+
console.log(
|
|
138
|
+
`${contact.fullName}: ${contact.phones.length} phones, ${contact.emails.length} emails`
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error("Conversion failed:", error.message);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### TypeScript
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { vcfToJsonFromFile, Contact } from "vcf-to-json-converter";
|
|
150
|
+
|
|
151
|
+
const contacts: Contact[] = vcfToJsonFromFile("./contacts.vcf");
|
|
152
|
+
console.log(contacts);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## API Reference
|
|
156
|
+
|
|
157
|
+
### `vcfToJson(vcfText: string): Contact[]`
|
|
158
|
+
|
|
159
|
+
Converts VCF text content to JSON.
|
|
160
|
+
|
|
161
|
+
**Parameters:**
|
|
162
|
+
|
|
163
|
+
- `vcfText` _(string)_: The VCF content as a string
|
|
164
|
+
|
|
165
|
+
**Returns:** Array of contact objects
|
|
166
|
+
|
|
167
|
+
**Throws:** Error if VCF content is invalid
|
|
168
|
+
|
|
169
|
+
**Example:**
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
const contacts = vcfToJson("BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nEND:VCARD");
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### `vcfToJsonFromFile(filePath: string): Contact[]`
|
|
176
|
+
|
|
177
|
+
Converts a VCF file to JSON.
|
|
178
|
+
|
|
179
|
+
**Parameters:**
|
|
180
|
+
|
|
181
|
+
- `filePath` _(string)_: Path to the VCF file
|
|
182
|
+
|
|
183
|
+
**Returns:** Array of contact objects
|
|
184
|
+
|
|
185
|
+
**Throws:** Error if file doesn't exist or is invalid
|
|
186
|
+
|
|
187
|
+
**Example:**
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
const contacts = vcfToJsonFromFile("./my-contacts.vcf");
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### `saveJsonToFile(jsonData: Contact[], outputPath: string, prettyPrint?: boolean): void`
|
|
194
|
+
|
|
195
|
+
Saves JSON data to a file.
|
|
196
|
+
|
|
197
|
+
**Parameters:**
|
|
198
|
+
|
|
199
|
+
- `jsonData` _(Contact[])_: The JSON data to save
|
|
200
|
+
- `outputPath` _(string)_: Path where to save the JSON file
|
|
201
|
+
- `prettyPrint` _(boolean, optional)_: Whether to format JSON with indentation (default: true)
|
|
202
|
+
|
|
203
|
+
**Example:**
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
saveJsonToFile(contacts, "./output.json", true); // Pretty formatted
|
|
207
|
+
saveJsonToFile(contacts, "./compact.json", false); // Minified
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Output Format
|
|
211
|
+
|
|
212
|
+
Each contact is converted to the following JSON structure:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"fullName": "John Doe",
|
|
217
|
+
"firstName": "John",
|
|
218
|
+
"lastName": "Doe",
|
|
219
|
+
"phones": [
|
|
220
|
+
{
|
|
221
|
+
"value": "+1234567890",
|
|
222
|
+
"type": "CELL"
|
|
223
|
+
}
|
|
224
|
+
],
|
|
225
|
+
"emails": [
|
|
226
|
+
{
|
|
227
|
+
"value": "john@example.com",
|
|
228
|
+
"type": "HOME"
|
|
229
|
+
}
|
|
230
|
+
],
|
|
231
|
+
"organization": "ACME Corp",
|
|
232
|
+
"title": "Software Engineer",
|
|
233
|
+
"note": "Important contact",
|
|
234
|
+
"photo": "",
|
|
235
|
+
"url": "",
|
|
236
|
+
"raw": {
|
|
237
|
+
/* Original parsed vCard object */
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Supported vCard Fields
|
|
243
|
+
|
|
244
|
+
- **Name:** Full name, first name, last name
|
|
245
|
+
- **Phone:** Multiple phone numbers with types
|
|
246
|
+
- **Email:** Multiple email addresses with types
|
|
247
|
+
- **Organization:** Company/organization name
|
|
248
|
+
- **Title:** Job title
|
|
249
|
+
- **Note:** Notes/comments
|
|
250
|
+
- **Photo:** Photo data (base64)
|
|
251
|
+
- **URL:** Website URLs
|
|
252
|
+
- **Raw:** Complete original vCard data
|
|
253
|
+
|
|
254
|
+
## Error Handling
|
|
255
|
+
|
|
256
|
+
The library provides comprehensive error handling for:
|
|
257
|
+
|
|
258
|
+
- Invalid file paths
|
|
259
|
+
- Missing files
|
|
260
|
+
- Permission errors
|
|
261
|
+
- Malformed VCF content
|
|
262
|
+
- Invalid input parameters
|
|
263
|
+
|
|
264
|
+
## Examples
|
|
265
|
+
|
|
266
|
+
### Basic Conversion
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
const { vcfToJsonFromFile } = require("vcf-to-json-converter");
|
|
270
|
+
|
|
271
|
+
const contacts = vcfToJsonFromFile("my-contacts.vcf");
|
|
272
|
+
console.log(`Found ${contacts.length} contacts`);
|
|
273
|
+
|
|
274
|
+
contacts.forEach((contact) => {
|
|
275
|
+
console.log(
|
|
276
|
+
`${contact.fullName}: ${contact.phones.map((p) => p.value).join(", ")}`
|
|
277
|
+
);
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Advanced Usage with Error Handling
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
const { vcfToJsonFromFile, saveJsonToFile } = require("vcf-to-json-converter");
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
// Convert VCF file
|
|
288
|
+
const contacts = vcfToJsonFromFile("./contacts.vcf");
|
|
289
|
+
|
|
290
|
+
// Filter contacts with email addresses
|
|
291
|
+
const contactsWithEmail = contacts.filter(
|
|
292
|
+
(contact) => contact.emails.length > 0
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// Extract business contacts
|
|
296
|
+
const businessContacts = contacts.filter((contact) => contact.organization);
|
|
297
|
+
|
|
298
|
+
console.log(
|
|
299
|
+
`Total: ${contacts.length}, With Email: ${contactsWithEmail.length}, Business: ${businessContacts.length}`
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
// Save different formats
|
|
303
|
+
saveJsonToFile(contacts, "./all-contacts.json", true); // Pretty
|
|
304
|
+
saveJsonToFile(businessContacts, "./business-contacts.json", false); // Compact
|
|
305
|
+
} catch (error) {
|
|
306
|
+
if (error.code === "ENOENT") {
|
|
307
|
+
console.error("File not found. Please check the file path.");
|
|
308
|
+
} else {
|
|
309
|
+
console.error("Conversion error:", error.message);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Batch Processing
|
|
315
|
+
|
|
316
|
+
```javascript
|
|
317
|
+
const fs = require("fs");
|
|
318
|
+
const path = require("path");
|
|
319
|
+
const { vcfToJsonFromFile, saveJsonToFile } = require("vcf-to-json-converter");
|
|
320
|
+
|
|
321
|
+
const vcfFiles = fs
|
|
322
|
+
.readdirSync("./vcf-files")
|
|
323
|
+
.filter((f) => f.endsWith(".vcf"));
|
|
324
|
+
|
|
325
|
+
vcfFiles.forEach((file) => {
|
|
326
|
+
try {
|
|
327
|
+
const contacts = vcfToJsonFromFile(path.join("./vcf-files", file));
|
|
328
|
+
const outputFile = file.replace(".vcf", ".json");
|
|
329
|
+
saveJsonToFile(contacts, path.join("./json-output", outputFile));
|
|
330
|
+
console.log(`Converted ${file} (${contacts.length} contacts)`);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error(`Failed to convert ${file}:`, error.message);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Troubleshooting
|
|
338
|
+
|
|
339
|
+
### Common Issues
|
|
340
|
+
|
|
341
|
+
**File not found error:**
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
Error: ENOENT: no such file or directory
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
- Ensure the VCF file path is correct
|
|
348
|
+
- Use absolute paths if relative paths don't work
|
|
349
|
+
- Check file permissions
|
|
350
|
+
|
|
351
|
+
**Invalid VCF format:**
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
Error: Invalid VCF format
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
- Ensure your file starts with `BEGIN:VCARD` and ends with `END:VCARD`
|
|
358
|
+
- Check for corrupted or incomplete vCard entries
|
|
359
|
+
- Validate the VCF file structure
|
|
360
|
+
|
|
361
|
+
**Empty output:**
|
|
362
|
+
|
|
363
|
+
- Check if the VCF file contains properly formatted vCard entries
|
|
364
|
+
- Ensure the file encoding is UTF-8
|
|
365
|
+
- Verify the VCF version is supported (v2.1, v3.0, v4.0)
|
|
366
|
+
|
|
367
|
+
**CLI command not found:**
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
'vcf-to-json' is not recognized as an internal or external command
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
- Reinstall globally: `npm install -g vcf-to-json-converter`
|
|
374
|
+
- Check your PATH environment variable
|
|
375
|
+
- Try using `npx vcf-to-json-converter` instead
|
|
376
|
+
|
|
377
|
+
## CLI Examples
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
# Show help
|
|
381
|
+
vcf-to-json --help
|
|
382
|
+
|
|
383
|
+
# Convert single file
|
|
384
|
+
vcf-to-json contacts.vcf
|
|
385
|
+
|
|
386
|
+
# Convert and save to specific file
|
|
387
|
+
vcf-to-json contacts.vcf my-contacts.json
|
|
388
|
+
|
|
389
|
+
# Compact output
|
|
390
|
+
vcf-to-json contacts.vcf contacts.json --compact
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## Requirements
|
|
394
|
+
|
|
395
|
+
- Node.js 12.0.0 or higher
|
|
396
|
+
- npm 6.0.0 or higher
|
|
397
|
+
|
|
398
|
+
## Contributing
|
|
399
|
+
|
|
400
|
+
We welcome contributions! Please follow these steps:
|
|
401
|
+
|
|
402
|
+
1. Fork the repository
|
|
403
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
404
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
405
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
406
|
+
5. Open a Pull Request
|
|
407
|
+
|
|
408
|
+
### Development Setup
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
# Clone the repository
|
|
412
|
+
git clone https://github.com/shubhanshurav/vcf-to-json.git
|
|
413
|
+
cd vcf-to-json
|
|
414
|
+
|
|
415
|
+
# Install dependencies (if any)
|
|
416
|
+
npm install
|
|
417
|
+
|
|
418
|
+
# Run tests
|
|
419
|
+
npm test
|
|
420
|
+
|
|
421
|
+
# Test CLI locally
|
|
422
|
+
node index.js contacts.vcf
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## License
|
|
426
|
+
|
|
427
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
428
|
+
|
|
429
|
+
## Author
|
|
430
|
+
|
|
431
|
+
**Shubhanshu Rao**
|
|
432
|
+
|
|
433
|
+
- GitHub: [@shubhanshurav](https://github.com/shubhanshurav)
|
|
434
|
+
- Repository: [vcf-to-json](https://github.com/shubhanshurav/vcf-to-json)
|
|
435
|
+
|
|
436
|
+
## Links
|
|
437
|
+
|
|
438
|
+
- [npm package](https://www.npmjs.com/package/vcf-to-json-converter)
|
|
439
|
+
- [GitHub repository](https://github.com/shubhanshurav/vcf-to-json)
|
|
440
|
+
- [Report Issues](https://github.com/shubhanshurav/vcf-to-json/issues)
|
|
441
|
+
- [Changelog](https://github.com/shubhanshurav/vcf-to-json/blob/main/CHANGELOG.md)
|
|
442
|
+
|
|
443
|
+
## Version History
|
|
444
|
+
|
|
445
|
+
### v1.0.0 (Latest)
|
|
446
|
+
|
|
447
|
+
- Initial release
|
|
448
|
+
- CLI support with global installation
|
|
449
|
+
- Comprehensive vCard field parsing (v2.1, v3.0, v4.0)
|
|
450
|
+
- Robust error handling and validation
|
|
451
|
+
- TypeScript definitions included
|
|
452
|
+
- UTF-8 and international character support
|
|
453
|
+
- Zero dependencies for maximum compatibility
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
**Star this repository if you find it helpful!**
|
package/index.d.ts
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
export interface Phone {
|
|
2
|
-
value: string;
|
|
3
|
-
type: string;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface Email {
|
|
7
|
-
value: string;
|
|
8
|
-
type: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface Contact {
|
|
12
|
-
fullName: string;
|
|
13
|
-
firstName: string;
|
|
14
|
-
lastName: string;
|
|
15
|
-
phones: Phone[];
|
|
16
|
-
emails: Email[];
|
|
17
|
-
organization: string;
|
|
18
|
-
title: string;
|
|
19
|
-
note: string;
|
|
20
|
-
photo: string;
|
|
21
|
-
url: string;
|
|
22
|
-
raw: any;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export declare function vcfToJson(vcfText: string): Contact[];
|
|
26
|
-
export declare function vcfToJsonFromFile(filePath: string): Contact[];
|
|
27
|
-
export declare function saveJsonToFile(
|
|
28
|
-
jsonData: Contact[],
|
|
29
|
-
outputPath: string,
|
|
30
|
-
prettyPrint?: boolean
|
|
31
|
-
): void;
|
|
32
|
-
export declare function cli(): void;
|
|
1
|
+
export interface Phone {
|
|
2
|
+
value: string;
|
|
3
|
+
type: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface Email {
|
|
7
|
+
value: string;
|
|
8
|
+
type: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Contact {
|
|
12
|
+
fullName: string;
|
|
13
|
+
firstName: string;
|
|
14
|
+
lastName: string;
|
|
15
|
+
phones: Phone[];
|
|
16
|
+
emails: Email[];
|
|
17
|
+
organization: string;
|
|
18
|
+
title: string;
|
|
19
|
+
note: string;
|
|
20
|
+
photo: string;
|
|
21
|
+
url: string;
|
|
22
|
+
raw: any;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export declare function vcfToJson(vcfText: string): Contact[];
|
|
26
|
+
export declare function vcfToJsonFromFile(filePath: string): Contact[];
|
|
27
|
+
export declare function saveJsonToFile(
|
|
28
|
+
jsonData: Contact[],
|
|
29
|
+
outputPath: string,
|
|
30
|
+
prettyPrint?: boolean
|
|
31
|
+
): void;
|
|
32
|
+
export declare function cli(): void;
|
package/index.js
CHANGED
|
@@ -1,252 +1,252 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Manual VCF parser that properly extracts contact information
|
|
6
|
-
* @param {string} vcfText - The VCF content as string
|
|
7
|
-
* @returns {Array} Array of contact objects
|
|
8
|
-
*/
|
|
9
|
-
function parseVCF(vcfText) {
|
|
10
|
-
const contacts = [];
|
|
11
|
-
|
|
12
|
-
// Split by BEGIN:VCARD to handle multiple contacts
|
|
13
|
-
const vCards = vcfText.split("BEGIN:VCARD").filter((card) => card.trim());
|
|
14
|
-
|
|
15
|
-
for (const vCard of vCards) {
|
|
16
|
-
const lines = vCard.split(/\r?\n/).filter((line) => line.trim());
|
|
17
|
-
|
|
18
|
-
const contact = {
|
|
19
|
-
fullName: "",
|
|
20
|
-
firstName: "",
|
|
21
|
-
lastName: "",
|
|
22
|
-
phones: [],
|
|
23
|
-
emails: [],
|
|
24
|
-
organization: "",
|
|
25
|
-
title: "",
|
|
26
|
-
note: "",
|
|
27
|
-
photo: "",
|
|
28
|
-
url: "",
|
|
29
|
-
raw: vCard, // Store original vCard text
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
for (const line of lines) {
|
|
33
|
-
// Parse FN (Formatted Name) - supports UTF-8/Hindi
|
|
34
|
-
if (line.startsWith("FN:")) {
|
|
35
|
-
contact.fullName = line.substring(3).trim();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Parse N (Name) for first and last name
|
|
39
|
-
if (line.startsWith("N:")) {
|
|
40
|
-
const nameParts = line.substring(2).split(";");
|
|
41
|
-
// Format: Last;First;Middle;Prefix;Suffix
|
|
42
|
-
contact.lastName = (nameParts[0] || "").trim();
|
|
43
|
-
contact.firstName = (nameParts[1] || "").trim();
|
|
44
|
-
|
|
45
|
-
// If fullName is empty, construct it from first and last name
|
|
46
|
-
if (!contact.fullName) {
|
|
47
|
-
contact.fullName = `${contact.firstName} ${contact.lastName}`.trim();
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Parse TEL (Phone)
|
|
52
|
-
if (line.startsWith("TEL")) {
|
|
53
|
-
const phoneMatch = line.match(/:([\d\s\-+()]+)/);
|
|
54
|
-
if (phoneMatch) {
|
|
55
|
-
// Extract phone type from the TEL line
|
|
56
|
-
let phoneType = "unknown";
|
|
57
|
-
if (line.includes("CELL")) phoneType = "mobile";
|
|
58
|
-
else if (line.includes("HOME")) phoneType = "home";
|
|
59
|
-
else if (line.includes("WORK")) phoneType = "work";
|
|
60
|
-
else if (line.includes("VOICE")) phoneType = "voice";
|
|
61
|
-
else if (line.includes("PREF")) phoneType = "primary";
|
|
62
|
-
|
|
63
|
-
contact.phones.push({
|
|
64
|
-
value: phoneMatch[1].trim(),
|
|
65
|
-
type: phoneType,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Parse EMAIL
|
|
71
|
-
if (line.startsWith("EMAIL")) {
|
|
72
|
-
const emailMatch = line.match(/:(.+)/);
|
|
73
|
-
if (emailMatch) {
|
|
74
|
-
// Extract email type from the EMAIL line
|
|
75
|
-
let emailType = "unknown";
|
|
76
|
-
if (line.includes("HOME")) emailType = "home";
|
|
77
|
-
else if (line.includes("WORK")) emailType = "work";
|
|
78
|
-
else if (line.includes("PREF")) emailType = "primary";
|
|
79
|
-
|
|
80
|
-
contact.emails.push({
|
|
81
|
-
value: emailMatch[1].trim(),
|
|
82
|
-
type: emailType,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Parse ORG (Organization)
|
|
88
|
-
if (line.startsWith("ORG:")) {
|
|
89
|
-
contact.organization = line.substring(4).trim();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Parse TITLE
|
|
93
|
-
if (line.startsWith("TITLE:")) {
|
|
94
|
-
contact.title = line.substring(6).trim();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Parse NOTE
|
|
98
|
-
if (line.startsWith("NOTE:")) {
|
|
99
|
-
contact.note = line.substring(5).trim();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Parse URL
|
|
103
|
-
if (line.startsWith("URL:")) {
|
|
104
|
-
contact.url = line.substring(4).trim();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Parse PHOTO (basic support)
|
|
108
|
-
if (line.startsWith("PHOTO:")) {
|
|
109
|
-
contact.photo = line.substring(6).trim();
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Only add contact if it has meaningful data
|
|
114
|
-
if (
|
|
115
|
-
contact.fullName ||
|
|
116
|
-
contact.firstName ||
|
|
117
|
-
contact.lastName ||
|
|
118
|
-
contact.phones.length > 0
|
|
119
|
-
) {
|
|
120
|
-
contacts.push(contact);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return contacts;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Convert VCF text content to JSON
|
|
129
|
-
* @param {string} vcfText - The VCF content as string
|
|
130
|
-
* @returns {Array} Array of contact objects
|
|
131
|
-
*/
|
|
132
|
-
function vcfToJson(vcfText) {
|
|
133
|
-
if (!vcfText || typeof vcfText !== "string") {
|
|
134
|
-
throw new Error("Invalid VCF content. Expected non-empty string.");
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
return parseVCF(vcfText);
|
|
139
|
-
} catch (error) {
|
|
140
|
-
throw new Error(`Failed to parse VCF content: ${error.message}`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Convert VCF file to JSON
|
|
146
|
-
* @param {string} filePath - Path to the VCF file
|
|
147
|
-
* @returns {Array} Array of contact objects
|
|
148
|
-
*/
|
|
149
|
-
function vcfToJsonFromFile(filePath) {
|
|
150
|
-
if (!filePath || typeof filePath !== "string") {
|
|
151
|
-
throw new Error("File path is required and must be a string.");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
if (!fs.existsSync(filePath)) {
|
|
156
|
-
throw new Error(`File not found: ${filePath}`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const vcfText = fs.readFileSync(filePath, "utf8");
|
|
160
|
-
return vcfToJson(vcfText);
|
|
161
|
-
} catch (error) {
|
|
162
|
-
if (error.code === "ENOENT") {
|
|
163
|
-
throw new Error(`File not found: ${filePath}`);
|
|
164
|
-
}
|
|
165
|
-
if (error.code === "EACCES") {
|
|
166
|
-
throw new Error(`Permission denied: ${filePath}`);
|
|
167
|
-
}
|
|
168
|
-
throw error;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Save JSON data to file
|
|
174
|
-
* @param {Array} jsonData - The JSON data to save
|
|
175
|
-
* @param {string} outputPath - Path to save the JSON file
|
|
176
|
-
* @param {boolean} prettyPrint - Whether to format JSON with indentation
|
|
177
|
-
*/
|
|
178
|
-
function saveJsonToFile(jsonData, outputPath, prettyPrint = true) {
|
|
179
|
-
if (!outputPath || typeof outputPath !== "string") {
|
|
180
|
-
throw new Error("Output path is required and must be a string.");
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const jsonString = prettyPrint
|
|
185
|
-
? JSON.stringify(jsonData, null, 2)
|
|
186
|
-
: JSON.stringify(jsonData);
|
|
187
|
-
|
|
188
|
-
fs.writeFileSync(outputPath, jsonString, "utf8");
|
|
189
|
-
} catch (error) {
|
|
190
|
-
throw new Error(`Failed to save JSON file: ${error.message}`);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* CLI function to convert VCF to JSON
|
|
196
|
-
*/
|
|
197
|
-
function cli() {
|
|
198
|
-
const args = process.argv.slice(2);
|
|
199
|
-
|
|
200
|
-
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
201
|
-
console.log(`
|
|
202
|
-
VCF to JSON Converter
|
|
203
|
-
|
|
204
|
-
Usage:
|
|
205
|
-
vcf-to-json <input.vcf> [output.json] [options]
|
|
206
|
-
|
|
207
|
-
Options:
|
|
208
|
-
--compact, -c Output compact JSON (no formatting)
|
|
209
|
-
--help, -h Show this help message
|
|
210
|
-
|
|
211
|
-
Examples:
|
|
212
|
-
vcf-to-json contacts.vcf
|
|
213
|
-
vcf-to-json contacts.vcf output.json
|
|
214
|
-
vcf-to-json contacts.vcf output.json --compact
|
|
215
|
-
`);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const inputFile = args[0];
|
|
220
|
-
const outputFile = args[1] || inputFile.replace(/\.vcf$/i, ".json");
|
|
221
|
-
const compact = args.includes("--compact") || args.includes("-c");
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
console.log(`Converting ${inputFile} to JSON...`);
|
|
225
|
-
const jsonData = vcfToJsonFromFile(inputFile);
|
|
226
|
-
|
|
227
|
-
if (args[1]) {
|
|
228
|
-
saveJsonToFile(jsonData, outputFile, !compact);
|
|
229
|
-
console.log(
|
|
230
|
-
`✅ Converted ${jsonData.length} contacts and saved to ${outputFile}`
|
|
231
|
-
);
|
|
232
|
-
} else {
|
|
233
|
-
console.log(JSON.stringify(jsonData, null, compact ? 0 : 2));
|
|
234
|
-
}
|
|
235
|
-
} catch (error) {
|
|
236
|
-
console.error(`❌ Error: ${error.message}`);
|
|
237
|
-
process.exit(1);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Export functions
|
|
242
|
-
module.exports = {
|
|
243
|
-
vcfToJson,
|
|
244
|
-
vcfToJsonFromFile,
|
|
245
|
-
saveJsonToFile,
|
|
246
|
-
cli,
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
// If this file is run directly, execute CLI
|
|
250
|
-
if (require.main === module) {
|
|
251
|
-
cli();
|
|
252
|
-
}
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Manual VCF parser that properly extracts contact information
|
|
6
|
+
* @param {string} vcfText - The VCF content as string
|
|
7
|
+
* @returns {Array} Array of contact objects
|
|
8
|
+
*/
|
|
9
|
+
function parseVCF(vcfText) {
|
|
10
|
+
const contacts = [];
|
|
11
|
+
|
|
12
|
+
// Split by BEGIN:VCARD to handle multiple contacts
|
|
13
|
+
const vCards = vcfText.split("BEGIN:VCARD").filter((card) => card.trim());
|
|
14
|
+
|
|
15
|
+
for (const vCard of vCards) {
|
|
16
|
+
const lines = vCard.split(/\r?\n/).filter((line) => line.trim());
|
|
17
|
+
|
|
18
|
+
const contact = {
|
|
19
|
+
fullName: "",
|
|
20
|
+
firstName: "",
|
|
21
|
+
lastName: "",
|
|
22
|
+
phones: [],
|
|
23
|
+
emails: [],
|
|
24
|
+
organization: "",
|
|
25
|
+
title: "",
|
|
26
|
+
note: "",
|
|
27
|
+
photo: "",
|
|
28
|
+
url: "",
|
|
29
|
+
raw: vCard, // Store original vCard text
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
for (const line of lines) {
|
|
33
|
+
// Parse FN (Formatted Name) - supports UTF-8/Hindi
|
|
34
|
+
if (line.startsWith("FN:")) {
|
|
35
|
+
contact.fullName = line.substring(3).trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Parse N (Name) for first and last name
|
|
39
|
+
if (line.startsWith("N:")) {
|
|
40
|
+
const nameParts = line.substring(2).split(";");
|
|
41
|
+
// Format: Last;First;Middle;Prefix;Suffix
|
|
42
|
+
contact.lastName = (nameParts[0] || "").trim();
|
|
43
|
+
contact.firstName = (nameParts[1] || "").trim();
|
|
44
|
+
|
|
45
|
+
// If fullName is empty, construct it from first and last name
|
|
46
|
+
if (!contact.fullName) {
|
|
47
|
+
contact.fullName = `${contact.firstName} ${contact.lastName}`.trim();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Parse TEL (Phone)
|
|
52
|
+
if (line.startsWith("TEL")) {
|
|
53
|
+
const phoneMatch = line.match(/:([\d\s\-+()]+)/);
|
|
54
|
+
if (phoneMatch) {
|
|
55
|
+
// Extract phone type from the TEL line
|
|
56
|
+
let phoneType = "unknown";
|
|
57
|
+
if (line.includes("CELL")) phoneType = "mobile";
|
|
58
|
+
else if (line.includes("HOME")) phoneType = "home";
|
|
59
|
+
else if (line.includes("WORK")) phoneType = "work";
|
|
60
|
+
else if (line.includes("VOICE")) phoneType = "voice";
|
|
61
|
+
else if (line.includes("PREF")) phoneType = "primary";
|
|
62
|
+
|
|
63
|
+
contact.phones.push({
|
|
64
|
+
value: phoneMatch[1].trim(),
|
|
65
|
+
type: phoneType,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Parse EMAIL
|
|
71
|
+
if (line.startsWith("EMAIL")) {
|
|
72
|
+
const emailMatch = line.match(/:(.+)/);
|
|
73
|
+
if (emailMatch) {
|
|
74
|
+
// Extract email type from the EMAIL line
|
|
75
|
+
let emailType = "unknown";
|
|
76
|
+
if (line.includes("HOME")) emailType = "home";
|
|
77
|
+
else if (line.includes("WORK")) emailType = "work";
|
|
78
|
+
else if (line.includes("PREF")) emailType = "primary";
|
|
79
|
+
|
|
80
|
+
contact.emails.push({
|
|
81
|
+
value: emailMatch[1].trim(),
|
|
82
|
+
type: emailType,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Parse ORG (Organization)
|
|
88
|
+
if (line.startsWith("ORG:")) {
|
|
89
|
+
contact.organization = line.substring(4).trim();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Parse TITLE
|
|
93
|
+
if (line.startsWith("TITLE:")) {
|
|
94
|
+
contact.title = line.substring(6).trim();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Parse NOTE
|
|
98
|
+
if (line.startsWith("NOTE:")) {
|
|
99
|
+
contact.note = line.substring(5).trim();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Parse URL
|
|
103
|
+
if (line.startsWith("URL:")) {
|
|
104
|
+
contact.url = line.substring(4).trim();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Parse PHOTO (basic support)
|
|
108
|
+
if (line.startsWith("PHOTO:")) {
|
|
109
|
+
contact.photo = line.substring(6).trim();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Only add contact if it has meaningful data
|
|
114
|
+
if (
|
|
115
|
+
contact.fullName ||
|
|
116
|
+
contact.firstName ||
|
|
117
|
+
contact.lastName ||
|
|
118
|
+
contact.phones.length > 0
|
|
119
|
+
) {
|
|
120
|
+
contacts.push(contact);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return contacts;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Convert VCF text content to JSON
|
|
129
|
+
* @param {string} vcfText - The VCF content as string
|
|
130
|
+
* @returns {Array} Array of contact objects
|
|
131
|
+
*/
|
|
132
|
+
function vcfToJson(vcfText) {
|
|
133
|
+
if (!vcfText || typeof vcfText !== "string") {
|
|
134
|
+
throw new Error("Invalid VCF content. Expected non-empty string.");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
return parseVCF(vcfText);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
throw new Error(`Failed to parse VCF content: ${error.message}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Convert VCF file to JSON
|
|
146
|
+
* @param {string} filePath - Path to the VCF file
|
|
147
|
+
* @returns {Array} Array of contact objects
|
|
148
|
+
*/
|
|
149
|
+
function vcfToJsonFromFile(filePath) {
|
|
150
|
+
if (!filePath || typeof filePath !== "string") {
|
|
151
|
+
throw new Error("File path is required and must be a string.");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
if (!fs.existsSync(filePath)) {
|
|
156
|
+
throw new Error(`File not found: ${filePath}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const vcfText = fs.readFileSync(filePath, "utf8");
|
|
160
|
+
return vcfToJson(vcfText);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (error.code === "ENOENT") {
|
|
163
|
+
throw new Error(`File not found: ${filePath}`);
|
|
164
|
+
}
|
|
165
|
+
if (error.code === "EACCES") {
|
|
166
|
+
throw new Error(`Permission denied: ${filePath}`);
|
|
167
|
+
}
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Save JSON data to file
|
|
174
|
+
* @param {Array} jsonData - The JSON data to save
|
|
175
|
+
* @param {string} outputPath - Path to save the JSON file
|
|
176
|
+
* @param {boolean} prettyPrint - Whether to format JSON with indentation
|
|
177
|
+
*/
|
|
178
|
+
function saveJsonToFile(jsonData, outputPath, prettyPrint = true) {
|
|
179
|
+
if (!outputPath || typeof outputPath !== "string") {
|
|
180
|
+
throw new Error("Output path is required and must be a string.");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const jsonString = prettyPrint
|
|
185
|
+
? JSON.stringify(jsonData, null, 2)
|
|
186
|
+
: JSON.stringify(jsonData);
|
|
187
|
+
|
|
188
|
+
fs.writeFileSync(outputPath, jsonString, "utf8");
|
|
189
|
+
} catch (error) {
|
|
190
|
+
throw new Error(`Failed to save JSON file: ${error.message}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* CLI function to convert VCF to JSON
|
|
196
|
+
*/
|
|
197
|
+
function cli() {
|
|
198
|
+
const args = process.argv.slice(2);
|
|
199
|
+
|
|
200
|
+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
201
|
+
console.log(`
|
|
202
|
+
VCF to JSON Converter
|
|
203
|
+
|
|
204
|
+
Usage:
|
|
205
|
+
vcf-to-json <input.vcf> [output.json] [options]
|
|
206
|
+
|
|
207
|
+
Options:
|
|
208
|
+
--compact, -c Output compact JSON (no formatting)
|
|
209
|
+
--help, -h Show this help message
|
|
210
|
+
|
|
211
|
+
Examples:
|
|
212
|
+
vcf-to-json contacts.vcf
|
|
213
|
+
vcf-to-json contacts.vcf output.json
|
|
214
|
+
vcf-to-json contacts.vcf output.json --compact
|
|
215
|
+
`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const inputFile = args[0];
|
|
220
|
+
const outputFile = args[1] || inputFile.replace(/\.vcf$/i, ".json");
|
|
221
|
+
const compact = args.includes("--compact") || args.includes("-c");
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
console.log(`Converting ${inputFile} to JSON...`);
|
|
225
|
+
const jsonData = vcfToJsonFromFile(inputFile);
|
|
226
|
+
|
|
227
|
+
if (args[1]) {
|
|
228
|
+
saveJsonToFile(jsonData, outputFile, !compact);
|
|
229
|
+
console.log(
|
|
230
|
+
`✅ Converted ${jsonData.length} contacts and saved to ${outputFile}`
|
|
231
|
+
);
|
|
232
|
+
} else {
|
|
233
|
+
console.log(JSON.stringify(jsonData, null, compact ? 0 : 2));
|
|
234
|
+
}
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(`❌ Error: ${error.message}`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Export functions
|
|
242
|
+
module.exports = {
|
|
243
|
+
vcfToJson,
|
|
244
|
+
vcfToJsonFromFile,
|
|
245
|
+
saveJsonToFile,
|
|
246
|
+
cli,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// If this file is run directly, execute CLI
|
|
250
|
+
if (require.main === module) {
|
|
251
|
+
cli();
|
|
252
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vcf-to-json-converter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A lightweight Node.js library to convert VCF (vCard) files to JSON format with CLI support",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Shubhanshu Rao",
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
"main": "index.js",
|
|
9
9
|
"types": "index.d.ts",
|
|
10
10
|
"bin": {
|
|
11
|
-
"vcf-to-json": "
|
|
11
|
+
"vcf-to-json": "index.js"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
14
|
"start": "node index.js",
|
|
15
15
|
"test": "node test.js",
|
|
16
|
-
"demo": "node index.js contacts.vcf"
|
|
16
|
+
"demo": "node index.js contacts.vcf",
|
|
17
|
+
"build": "node index.js"
|
|
17
18
|
},
|
|
18
19
|
"keywords": [
|
|
19
20
|
"vcf",
|
|
@@ -26,12 +27,12 @@
|
|
|
26
27
|
],
|
|
27
28
|
"repository": {
|
|
28
29
|
"type": "git",
|
|
29
|
-
"url": "https://github.com/
|
|
30
|
+
"url": "git+https://github.com/shubhanshurav/vcf-to-json.git"
|
|
30
31
|
},
|
|
31
32
|
"bugs": {
|
|
32
|
-
"url": "https://github.com/
|
|
33
|
+
"url": "https://github.com/shubhanshurav/vcf-to-json.git/issues"
|
|
33
34
|
},
|
|
34
|
-
"homepage": "https://github.com/
|
|
35
|
+
"homepage": "https://github.com/shubhanshurav/vcf-to-json#readme",
|
|
35
36
|
"engines": {
|
|
36
37
|
"node": ">=12.0.0"
|
|
37
38
|
},
|