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 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, extensionsPath);
147
- desiredExtensions = readExtensionsFile(extensionsPath);
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: `${chalk.bold(editor.name)}: Syncing settings.json...`,
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
- `${chalk.bold(editor.name)}: settings.json synced (${Object.keys(merged).length} keys)`,
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: `${chalk.bold(editor.name)}: Syncing snippets (${snippetMode})...`,
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
- `${chalk.bold(editor.name)}: snippets synced (${fileCount} file${fileCount === 1 ? "" : "s"}, ${snippetMode})`,
205
+ `snippets synced (${fileCount} file${fileCount === 1 ? "" : "s"}, ${snippetMode} mode)`,
244
206
  );
245
207
  } catch (err) {
246
- spinner.fail(
247
- `${chalk.bold(editor.name)}: snippets sync failed (${err.message})`,
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
@@ -1,4 +1,3 @@
1
- export const EXTENSIONS_FILE = "extensions.txt";
2
1
  export const SETTINGS_FILE = "settings.json";
3
2
  export const SNIPPETS_DIR = "snippets";
4
3
 
@@ -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, filePath) {
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 inquirer from "inquirer";
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
- ensurePromptRegistered();
24
- const answer = await inquirer.prompt([
25
- {
26
- type: "list",
27
- name: "sourceId",
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
- ensurePromptRegistered();
38
- const answer = await inquirer.prompt([
39
- {
40
- type: "clean-checkbox",
41
- name: "selectedItems",
42
- message: `Share: ${chalk.dim(CHECKBOX_HINT)}`,
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
- const answer = await inquirer.prompt([
52
- {
53
- type: "list",
54
- name: "extensionMode",
55
- message: "Extension mode:",
56
- choices: extensionModes.map((m) => ({
57
- name: m.name,
58
- value: m.value,
59
- short: m.short,
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
- const answer = await inquirer.prompt([
69
- {
70
- type: "list",
71
- name: "snippetMode",
72
- message: "Snippet mode:",
73
- choices: snippetModes.map((m) => ({
74
- name: m.name,
75
- value: m.value,
76
- short: m.short,
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
- ensurePromptRegistered();
86
- const answer = await inquirer.prompt([
87
- {
88
- type: "clean-checkbox",
89
- name: "targetIds",
90
- message: `Sync to: ${chalk.dim(CHECKBOX_HINT)}`,
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.4",
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"