editor-profile-sync 1.0.6 → 1.0.7
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 +189 -189
- package/index.js +297 -286
- package/lib/constants.js +71 -71
- package/lib/editor-cli.js +151 -151
- package/lib/extensions-sync.js +44 -44
- package/lib/profile-paths.js +45 -45
- package/lib/prompts.js +51 -51
- package/lib/settings-sync.js +74 -74
- package/lib/snippets-sync.js +68 -68
- package/package.json +1 -1
package/lib/prompts.js
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import { select, checkbox } from "@inquirer/prompts";
|
|
2
|
-
|
|
3
|
-
export async function promptSourceEditor(availableEditors) {
|
|
4
|
-
return await select({
|
|
5
|
-
message: "Share from:",
|
|
6
|
-
choices: availableEditors.map((e) => ({ name: e.name, value: e.id })),
|
|
7
|
-
pageSize: 10,
|
|
8
|
-
});
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function promptSyncItems(syncItems, chalk) {
|
|
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
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function promptExtensionMode(extensionModes) {
|
|
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
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function promptSnippetMode(snippetModes) {
|
|
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
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export async function promptTargetEditors(targetChoices, chalk) {
|
|
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
|
-
});
|
|
51
|
-
}
|
|
1
|
+
import { select, checkbox } from "@inquirer/prompts";
|
|
2
|
+
|
|
3
|
+
export async function promptSourceEditor(availableEditors) {
|
|
4
|
+
return await select({
|
|
5
|
+
message: "Share from:",
|
|
6
|
+
choices: availableEditors.map((e) => ({ name: e.name, value: e.id })),
|
|
7
|
+
pageSize: 10,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function promptSyncItems(syncItems, chalk) {
|
|
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
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function promptExtensionMode(extensionModes) {
|
|
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
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function promptSnippetMode(snippetModes) {
|
|
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
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function promptTargetEditors(targetChoices, chalk) {
|
|
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
|
+
});
|
|
51
|
+
}
|
package/lib/settings-sync.js
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
-
import { dirname } from "path";
|
|
3
|
-
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
4
|
-
|
|
5
|
-
function isPlainObject(value) {
|
|
6
|
-
return value && typeof value === "object" && !Array.isArray(value);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function readJsonFile(filePath, fallback = {}) {
|
|
10
|
-
if (!existsSync(filePath)) return fallback;
|
|
11
|
-
const raw = readFileSync(filePath, "utf-8");
|
|
12
|
-
const errors = [];
|
|
13
|
-
const parsed = parse(raw, errors, {
|
|
14
|
-
allowTrailingComma: true,
|
|
15
|
-
disallowComments: false,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
if (errors.length > 0) {
|
|
19
|
-
const first = errors[0];
|
|
20
|
-
const position = getLineAndColumn(raw, first.offset);
|
|
21
|
-
throw new Error(
|
|
22
|
-
`${filePath} parse error (${printParseErrorCode(first.error)}) at line ${position.line}, column ${position.column}.`,
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!isPlainObject(parsed)) {
|
|
27
|
-
throw new Error(`${filePath} must be a JSON object.`);
|
|
28
|
-
}
|
|
29
|
-
return parsed;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function getLineAndColumn(text, offset) {
|
|
33
|
-
let line = 1;
|
|
34
|
-
let column = 1;
|
|
35
|
-
for (let i = 0; i < offset; i++) {
|
|
36
|
-
if (text[i] === "\n") {
|
|
37
|
-
line++;
|
|
38
|
-
column = 1;
|
|
39
|
-
} else {
|
|
40
|
-
column++;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return { line, column };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function writeJsonFile(filePath, data) {
|
|
47
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
48
|
-
writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function readSourceSettings(sourcePath) {
|
|
52
|
-
if (!existsSync(sourcePath)) {
|
|
53
|
-
throw new Error(`Source settings not found: ${sourcePath}`);
|
|
54
|
-
}
|
|
55
|
-
return readJsonFile(sourcePath, {});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function syncSettings(sourceSettings, targetPath) {
|
|
59
|
-
const targetSettings = readJsonFile(targetPath, {});
|
|
60
|
-
const merged = {
|
|
61
|
-
...targetSettings,
|
|
62
|
-
...sourceSettings,
|
|
63
|
-
};
|
|
64
|
-
writeJsonFile(targetPath, merged);
|
|
65
|
-
return merged;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function readJsonObject(filePath, fallback = {}) {
|
|
69
|
-
return readJsonFile(filePath, fallback);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function writeJsonObject(filePath, data) {
|
|
73
|
-
writeJsonFile(filePath, data);
|
|
74
|
-
}
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { dirname } from "path";
|
|
3
|
+
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
4
|
+
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
return value && typeof value === "object" && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function readJsonFile(filePath, fallback = {}) {
|
|
10
|
+
if (!existsSync(filePath)) return fallback;
|
|
11
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
12
|
+
const errors = [];
|
|
13
|
+
const parsed = parse(raw, errors, {
|
|
14
|
+
allowTrailingComma: true,
|
|
15
|
+
disallowComments: false,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
if (errors.length > 0) {
|
|
19
|
+
const first = errors[0];
|
|
20
|
+
const position = getLineAndColumn(raw, first.offset);
|
|
21
|
+
throw new Error(
|
|
22
|
+
`${filePath} parse error (${printParseErrorCode(first.error)}) at line ${position.line}, column ${position.column}.`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!isPlainObject(parsed)) {
|
|
27
|
+
throw new Error(`${filePath} must be a JSON object.`);
|
|
28
|
+
}
|
|
29
|
+
return parsed;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getLineAndColumn(text, offset) {
|
|
33
|
+
let line = 1;
|
|
34
|
+
let column = 1;
|
|
35
|
+
for (let i = 0; i < offset; i++) {
|
|
36
|
+
if (text[i] === "\n") {
|
|
37
|
+
line++;
|
|
38
|
+
column = 1;
|
|
39
|
+
} else {
|
|
40
|
+
column++;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { line, column };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function writeJsonFile(filePath, data) {
|
|
47
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
48
|
+
writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function readSourceSettings(sourcePath) {
|
|
52
|
+
if (!existsSync(sourcePath)) {
|
|
53
|
+
throw new Error(`Source settings not found: ${sourcePath}`);
|
|
54
|
+
}
|
|
55
|
+
return readJsonFile(sourcePath, {});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function syncSettings(sourceSettings, targetPath) {
|
|
59
|
+
const targetSettings = readJsonFile(targetPath, {});
|
|
60
|
+
const merged = {
|
|
61
|
+
...targetSettings,
|
|
62
|
+
...sourceSettings,
|
|
63
|
+
};
|
|
64
|
+
writeJsonFile(targetPath, merged);
|
|
65
|
+
return merged;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function readJsonObject(filePath, fallback = {}) {
|
|
69
|
+
return readJsonFile(filePath, fallback);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function writeJsonObject(filePath, data) {
|
|
73
|
+
writeJsonFile(filePath, data);
|
|
74
|
+
}
|
package/lib/snippets-sync.js
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import {
|
|
2
|
-
cpSync,
|
|
3
|
-
existsSync,
|
|
4
|
-
mkdirSync,
|
|
5
|
-
readdirSync,
|
|
6
|
-
rmSync,
|
|
7
|
-
statSync,
|
|
8
|
-
} from "fs";
|
|
9
|
-
import { dirname, join } from "path";
|
|
10
|
-
import { readJsonObject, writeJsonObject } from "./settings-sync.js";
|
|
11
|
-
|
|
12
|
-
function listFilesRecursive(dirPath, prefix = "") {
|
|
13
|
-
if (!existsSync(dirPath)) return [];
|
|
14
|
-
|
|
15
|
-
const entries = readdirSync(dirPath);
|
|
16
|
-
const files = [];
|
|
17
|
-
|
|
18
|
-
for (const entry of entries) {
|
|
19
|
-
const rel = prefix ? join(prefix, entry) : entry;
|
|
20
|
-
const abs = join(dirPath, entry);
|
|
21
|
-
const st = statSync(abs);
|
|
22
|
-
if (st.isDirectory()) {
|
|
23
|
-
files.push(...listFilesRecursive(abs, rel));
|
|
24
|
-
} else if (st.isFile()) {
|
|
25
|
-
files.push(rel);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return files;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function mergeSnippetFile(sourcePath, targetPath) {
|
|
33
|
-
const source = readJsonObject(sourcePath, {});
|
|
34
|
-
const target = readJsonObject(targetPath, {});
|
|
35
|
-
const merged = {
|
|
36
|
-
...target,
|
|
37
|
-
...source,
|
|
38
|
-
};
|
|
39
|
-
writeJsonObject(targetPath, merged);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function syncSnippets(sourceDir, targetDir, mode) {
|
|
43
|
-
if (!existsSync(sourceDir)) {
|
|
44
|
-
throw new Error(`Source snippets folder not found: ${sourceDir}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (mode === "replace") {
|
|
48
|
-
rmSync(targetDir, { recursive: true, force: true });
|
|
49
|
-
mkdirSync(dirname(targetDir), { recursive: true });
|
|
50
|
-
cpSync(sourceDir, targetDir, { recursive: true, force: true });
|
|
51
|
-
return listFilesRecursive(targetDir).length;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const sourceFiles = listFilesRecursive(sourceDir);
|
|
55
|
-
for (const relPath of sourceFiles) {
|
|
56
|
-
const sourcePath = join(sourceDir, relPath);
|
|
57
|
-
const targetPath = join(targetDir, relPath);
|
|
58
|
-
mkdirSync(dirname(targetPath), { recursive: true });
|
|
59
|
-
|
|
60
|
-
if (sourcePath.toLowerCase().endsWith(".json")) {
|
|
61
|
-
mergeSnippetFile(sourcePath, targetPath);
|
|
62
|
-
} else {
|
|
63
|
-
cpSync(sourcePath, targetPath, { force: true });
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return sourceFiles.length;
|
|
68
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
cpSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readdirSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
statSync,
|
|
8
|
+
} from "fs";
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
import { readJsonObject, writeJsonObject } from "./settings-sync.js";
|
|
11
|
+
|
|
12
|
+
function listFilesRecursive(dirPath, prefix = "") {
|
|
13
|
+
if (!existsSync(dirPath)) return [];
|
|
14
|
+
|
|
15
|
+
const entries = readdirSync(dirPath);
|
|
16
|
+
const files = [];
|
|
17
|
+
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
const rel = prefix ? join(prefix, entry) : entry;
|
|
20
|
+
const abs = join(dirPath, entry);
|
|
21
|
+
const st = statSync(abs);
|
|
22
|
+
if (st.isDirectory()) {
|
|
23
|
+
files.push(...listFilesRecursive(abs, rel));
|
|
24
|
+
} else if (st.isFile()) {
|
|
25
|
+
files.push(rel);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return files;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function mergeSnippetFile(sourcePath, targetPath) {
|
|
33
|
+
const source = readJsonObject(sourcePath, {});
|
|
34
|
+
const target = readJsonObject(targetPath, {});
|
|
35
|
+
const merged = {
|
|
36
|
+
...target,
|
|
37
|
+
...source,
|
|
38
|
+
};
|
|
39
|
+
writeJsonObject(targetPath, merged);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function syncSnippets(sourceDir, targetDir, mode) {
|
|
43
|
+
if (!existsSync(sourceDir)) {
|
|
44
|
+
throw new Error(`Source snippets folder not found: ${sourceDir}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (mode === "replace") {
|
|
48
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
49
|
+
mkdirSync(dirname(targetDir), { recursive: true });
|
|
50
|
+
cpSync(sourceDir, targetDir, { recursive: true, force: true });
|
|
51
|
+
return listFilesRecursive(targetDir).length;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const sourceFiles = listFilesRecursive(sourceDir);
|
|
55
|
+
for (const relPath of sourceFiles) {
|
|
56
|
+
const sourcePath = join(sourceDir, relPath);
|
|
57
|
+
const targetPath = join(targetDir, relPath);
|
|
58
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
59
|
+
|
|
60
|
+
if (sourcePath.toLowerCase().endsWith(".json")) {
|
|
61
|
+
mergeSnippetFile(sourcePath, targetPath);
|
|
62
|
+
} else {
|
|
63
|
+
cpSync(sourcePath, targetPath, { force: true });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return sourceFiles.length;
|
|
68
|
+
}
|
package/package.json
CHANGED