editor-profile-sync 1.0.4 → 1.0.5
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 +0 -4
- package/index.js +34 -50
- package/lib/constants.js +0 -1
- package/lib/extensions-sync.js +2 -19
- package/lib/prompts.js +36 -81
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -140,10 +140,6 @@ These editors are currently supported:
|
|
|
140
140
|
|
|
141
141
|
**Windows/Linux**: Each editor must be installed and its CLI available in your terminal (e.g. `code`, `cursor`).
|
|
142
142
|
|
|
143
|
-
## Generated files
|
|
144
|
-
|
|
145
|
-
- `extensions.txt` is created when syncing extensions and contains the exported source extension IDs.
|
|
146
|
-
|
|
147
143
|
## License
|
|
148
144
|
|
|
149
145
|
MIT
|
package/index.js
CHANGED
|
@@ -9,16 +9,11 @@ import updateNotifier from "update-notifier";
|
|
|
9
9
|
import {
|
|
10
10
|
EDITORS,
|
|
11
11
|
EXTENSION_MODES,
|
|
12
|
-
EXTENSIONS_FILE,
|
|
13
12
|
SNIPPET_MODES,
|
|
14
13
|
SYNC_ITEMS,
|
|
15
14
|
} from "./lib/constants.js";
|
|
16
15
|
import { isEditorInstalled } from "./lib/editor-cli.js";
|
|
17
|
-
import {
|
|
18
|
-
exportExtensions,
|
|
19
|
-
readExtensionsFile,
|
|
20
|
-
syncExtensions,
|
|
21
|
-
} from "./lib/extensions-sync.js";
|
|
16
|
+
import { exportExtensions, syncExtensions } from "./lib/extensions-sync.js";
|
|
22
17
|
import { getSettingsPath, getSnippetsPath } from "./lib/profile-paths.js";
|
|
23
18
|
import { readSourceSettings, syncSettings } from "./lib/settings-sync.js";
|
|
24
19
|
import { syncSnippets } from "./lib/snippets-sync.js";
|
|
@@ -133,9 +128,6 @@ async function main() {
|
|
|
133
128
|
targetIds.includes(e.id),
|
|
134
129
|
);
|
|
135
130
|
|
|
136
|
-
const cwd = process.cwd();
|
|
137
|
-
const extensionsPath = join(cwd, EXTENSIONS_FILE);
|
|
138
|
-
|
|
139
131
|
let desiredExtensions = [];
|
|
140
132
|
if (syncItems.includes("extensions")) {
|
|
141
133
|
const exportSpinner = ora({
|
|
@@ -143,11 +135,8 @@ async function main() {
|
|
|
143
135
|
color: "cyan",
|
|
144
136
|
}).start();
|
|
145
137
|
try {
|
|
146
|
-
await exportExtensions(sourceEditor
|
|
147
|
-
desiredExtensions
|
|
148
|
-
exportSpinner.succeed(
|
|
149
|
-
`Exported ${desiredExtensions.length} extensions to ${EXTENSIONS_FILE}`,
|
|
150
|
-
);
|
|
138
|
+
desiredExtensions = await exportExtensions(sourceEditor);
|
|
139
|
+
exportSpinner.succeed(`${desiredExtensions.length} extensions detected`);
|
|
151
140
|
} catch (err) {
|
|
152
141
|
exportSpinner.fail("Extension export failed");
|
|
153
142
|
console.error(" Error:", err.message, "\n");
|
|
@@ -180,54 +169,27 @@ async function main() {
|
|
|
180
169
|
|
|
181
170
|
for (const editor of targetEditors) {
|
|
182
171
|
try {
|
|
183
|
-
console.log(chalk.bold(editor.name));
|
|
184
|
-
|
|
185
|
-
if (syncItems.includes("extensions")) {
|
|
186
|
-
const spinner = ora({
|
|
187
|
-
text: `${chalk.bold(editor.name)}: Installing extensions...`,
|
|
188
|
-
color: "cyan",
|
|
189
|
-
}).start();
|
|
190
|
-
const result = await syncExtensions(
|
|
191
|
-
editor,
|
|
192
|
-
desiredExtensions,
|
|
193
|
-
extensionMode,
|
|
194
|
-
(i, extensionId) => {
|
|
195
|
-
spinner.text = `${chalk.bold(editor.name)}: [${i + 1}/${desiredExtensions.length}] Installing ${extensionId}`;
|
|
196
|
-
},
|
|
197
|
-
);
|
|
198
|
-
const parts = [chalk.green(`${result.synced} extensions synced`)];
|
|
199
|
-
if (result.failed.length > 0) {
|
|
200
|
-
parts.push(chalk.red(`${result.failed.length} failed`));
|
|
201
|
-
}
|
|
202
|
-
spinner.succeed(`${chalk.bold(editor.name)}: ${parts.join(", ")}`);
|
|
203
|
-
if (result.failed.length > 0) {
|
|
204
|
-
console.log(
|
|
205
|
-
` ${chalk.red("Failed:")} ${result.failed.join(", ")}`,
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
172
|
+
console.log(chalk.bold(editor.name) + ":");
|
|
209
173
|
|
|
210
174
|
if (syncItems.includes("settings")) {
|
|
211
175
|
const spinner = ora({
|
|
212
|
-
text:
|
|
176
|
+
text: "Syncing settings.json...",
|
|
213
177
|
color: "cyan",
|
|
214
178
|
}).start();
|
|
215
179
|
try {
|
|
216
180
|
const targetPath = getSettingsPath(editor, { createIfMissing: true });
|
|
217
181
|
const merged = syncSettings(sourceSettings, targetPath);
|
|
218
182
|
spinner.succeed(
|
|
219
|
-
|
|
183
|
+
`settings.json synced (${Object.keys(merged).length} keys)`,
|
|
220
184
|
);
|
|
221
185
|
} catch (err) {
|
|
222
|
-
spinner.fail(
|
|
223
|
-
`${chalk.bold(editor.name)}: settings sync failed (${err.message})`,
|
|
224
|
-
);
|
|
186
|
+
spinner.fail(`settings sync failed (${err.message})`);
|
|
225
187
|
}
|
|
226
188
|
}
|
|
227
189
|
|
|
228
190
|
if (syncItems.includes("snippets")) {
|
|
229
191
|
const spinner = ora({
|
|
230
|
-
text:
|
|
192
|
+
text: `Syncing snippets (${snippetMode} mode)...`,
|
|
231
193
|
color: "cyan",
|
|
232
194
|
}).start();
|
|
233
195
|
try {
|
|
@@ -240,12 +202,34 @@ async function main() {
|
|
|
240
202
|
snippetMode,
|
|
241
203
|
);
|
|
242
204
|
spinner.succeed(
|
|
243
|
-
|
|
205
|
+
`snippets synced (${fileCount} file${fileCount === 1 ? "" : "s"}, ${snippetMode} mode)`,
|
|
244
206
|
);
|
|
245
207
|
} catch (err) {
|
|
246
|
-
spinner.fail(
|
|
247
|
-
|
|
248
|
-
|
|
208
|
+
spinner.fail(`snippets sync failed (${err.message})`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (syncItems.includes("extensions")) {
|
|
213
|
+
const spinner = ora({
|
|
214
|
+
text: "Installing extensions...",
|
|
215
|
+
color: "cyan",
|
|
216
|
+
}).start();
|
|
217
|
+
const result = await syncExtensions(
|
|
218
|
+
editor,
|
|
219
|
+
desiredExtensions,
|
|
220
|
+
extensionMode,
|
|
221
|
+
(i, extensionId) => {
|
|
222
|
+
spinner.text = `Installing extensions [${i + 1}/${desiredExtensions.length}] ${chalk.dim(extensionId)}`;
|
|
223
|
+
},
|
|
224
|
+
);
|
|
225
|
+
const successPart = chalk.green(`${result.synced} installed`);
|
|
226
|
+
const failedPart =
|
|
227
|
+
result.failed.length > 0
|
|
228
|
+
? `, ${chalk.red(`${result.failed.length} failed`)}`
|
|
229
|
+
: "";
|
|
230
|
+
spinner.succeed(`extensions synced (${successPart}${failedPart})`);
|
|
231
|
+
if (result.failed.length > 0) {
|
|
232
|
+
console.log(`${chalk.red("Failed:")} ${result.failed.join(", ")}`);
|
|
249
233
|
}
|
|
250
234
|
}
|
|
251
235
|
|
package/lib/constants.js
CHANGED
package/lib/extensions-sync.js
CHANGED
|
@@ -1,32 +1,15 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
2
1
|
import {
|
|
3
2
|
getInstalledExtensions,
|
|
4
3
|
installExtension,
|
|
5
4
|
uninstallExtension,
|
|
6
5
|
} from "./editor-cli.js";
|
|
7
6
|
|
|
8
|
-
export async function exportExtensions(sourceEditor
|
|
7
|
+
export async function exportExtensions(sourceEditor) {
|
|
9
8
|
const list = await getInstalledExtensions(sourceEditor);
|
|
10
9
|
if (list == null) {
|
|
11
10
|
throw new Error(`${sourceEditor.name} CLI not found or failed.`);
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
const sorted = [...list].sort();
|
|
15
|
-
writeFileSync(
|
|
16
|
-
filePath,
|
|
17
|
-
sorted.join("\n") + (sorted.length ? "\n" : ""),
|
|
18
|
-
"utf-8",
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
return sorted;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function readExtensionsFile(filePath) {
|
|
25
|
-
const content = readFileSync(filePath, "utf-8");
|
|
26
|
-
return content
|
|
27
|
-
.split(/\r?\n/)
|
|
28
|
-
.map((s) => s.trim())
|
|
29
|
-
.filter(Boolean);
|
|
12
|
+
return [...list].sort();
|
|
30
13
|
}
|
|
31
14
|
|
|
32
15
|
export async function syncExtensions(editor, desired, mode, onProgress) {
|
package/lib/prompts.js
CHANGED
|
@@ -1,96 +1,51 @@
|
|
|
1
|
-
import
|
|
2
|
-
import CheckboxPrompt from "inquirer/lib/prompts/checkbox.js";
|
|
3
|
-
|
|
4
|
-
class CleanCheckboxPrompt extends CheckboxPrompt {
|
|
5
|
-
constructor(questions, rl, answers) {
|
|
6
|
-
super(questions, rl, answers);
|
|
7
|
-
this.dontShowHints = true;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
let isRegistered = false;
|
|
12
|
-
|
|
13
|
-
function ensurePromptRegistered() {
|
|
14
|
-
if (isRegistered) return;
|
|
15
|
-
inquirer.registerPrompt("clean-checkbox", CleanCheckboxPrompt);
|
|
16
|
-
isRegistered = true;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const CHECKBOX_HINT =
|
|
20
|
-
"(Press <space> to select, <a> to toggle all, <enter> to continue)";
|
|
1
|
+
import { select, checkbox } from "@inquirer/prompts";
|
|
21
2
|
|
|
22
3
|
export async function promptSourceEditor(availableEditors) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
message: "Share from:",
|
|
29
|
-
choices: availableEditors.map((e) => ({ name: e.name, value: e.id })),
|
|
30
|
-
pageSize: 10,
|
|
31
|
-
},
|
|
32
|
-
]);
|
|
33
|
-
return answer.sourceId;
|
|
4
|
+
return await select({
|
|
5
|
+
message: "Share from:",
|
|
6
|
+
choices: availableEditors.map((e) => ({ name: e.name, value: e.id })),
|
|
7
|
+
pageSize: 10,
|
|
8
|
+
});
|
|
34
9
|
}
|
|
35
10
|
|
|
36
11
|
export async function promptSyncItems(syncItems, chalk) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
choices: syncItems,
|
|
44
|
-
validate: (v) => (v.length ? true : "Select at least one item."),
|
|
45
|
-
},
|
|
46
|
-
]);
|
|
47
|
-
return answer.selectedItems;
|
|
12
|
+
return await checkbox({
|
|
13
|
+
message: "Select items to sync:",
|
|
14
|
+
choices: syncItems,
|
|
15
|
+
required: true,
|
|
16
|
+
validate: (v) => (v.length ? true : "Select at least one item."),
|
|
17
|
+
});
|
|
48
18
|
}
|
|
49
19
|
|
|
50
20
|
export async function promptExtensionMode(extensionModes) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
name:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
})),
|
|
61
|
-
default: "additive",
|
|
62
|
-
},
|
|
63
|
-
]);
|
|
64
|
-
return answer.extensionMode;
|
|
21
|
+
return await select({
|
|
22
|
+
message: "Extension mode:",
|
|
23
|
+
choices: extensionModes.map((m) => ({
|
|
24
|
+
name: m.name,
|
|
25
|
+
value: m.value,
|
|
26
|
+
short: m.short,
|
|
27
|
+
})),
|
|
28
|
+
default: "additive",
|
|
29
|
+
});
|
|
65
30
|
}
|
|
66
31
|
|
|
67
32
|
export async function promptSnippetMode(snippetModes) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
name:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
})),
|
|
78
|
-
default: "merge",
|
|
79
|
-
},
|
|
80
|
-
]);
|
|
81
|
-
return answer.snippetMode;
|
|
33
|
+
return await select({
|
|
34
|
+
message: "Snippet mode:",
|
|
35
|
+
choices: snippetModes.map((m) => ({
|
|
36
|
+
name: m.name,
|
|
37
|
+
value: m.value,
|
|
38
|
+
short: m.short,
|
|
39
|
+
})),
|
|
40
|
+
default: "merge",
|
|
41
|
+
});
|
|
82
42
|
}
|
|
83
43
|
|
|
84
44
|
export async function promptTargetEditors(targetChoices, chalk) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
choices: targetChoices,
|
|
92
|
-
validate: (v) => (v.length ? true : "Select at least one editor."),
|
|
93
|
-
},
|
|
94
|
-
]);
|
|
95
|
-
return answer.targetIds;
|
|
45
|
+
return await checkbox({
|
|
46
|
+
message: "Select target editors:",
|
|
47
|
+
choices: targetChoices,
|
|
48
|
+
required: true,
|
|
49
|
+
validate: (v) => (v.length ? true : "Select at least one editor."),
|
|
50
|
+
});
|
|
96
51
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "editor-profile-sync",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Cross-platform extensions, settings.json, and snippets sync for VS Code-based editors",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"node": ">=18"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
+
"@inquirer/prompts": "^8.2.1",
|
|
35
36
|
"chalk": "^5.6.2",
|
|
36
|
-
"inquirer": "^9.2.22",
|
|
37
37
|
"jsonc-parser": "^3.3.1",
|
|
38
38
|
"ora": "^9.3.0",
|
|
39
39
|
"update-notifier": "^7.3.1"
|