@lsst/pik-plugin-select 0.8.1 → 0.9.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/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import { Parser, loadConfig, BlockSelector } from "@lsst/pik-core";
|
|
2
|
+
import { Parser, loadGlobalConfig, loadConfig, expandHome, BlockSelector } from "@lsst/pik-core";
|
|
3
3
|
import pc from "picocolors";
|
|
4
|
-
import { relative } from "path";
|
|
4
|
+
import { resolve, basename, relative } from "path";
|
|
5
5
|
import { readFile, writeFile } from "fs/promises";
|
|
6
6
|
import { glob } from "glob";
|
|
7
7
|
import { select, Separator } from "@inquirer/prompts";
|
|
@@ -27,50 +27,112 @@ class Scanner {
|
|
|
27
27
|
return results;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
function
|
|
31
|
-
if (
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
function normalizeEntry(entry) {
|
|
31
|
+
if (typeof entry === "string") {
|
|
32
|
+
const path2 = resolve(expandHome(entry));
|
|
33
|
+
return { path: path2, name: basename(path2) };
|
|
34
|
+
}
|
|
35
|
+
const path = resolve(expandHome(entry.path));
|
|
36
|
+
return {
|
|
37
|
+
path,
|
|
38
|
+
name: entry.name ?? basename(path),
|
|
39
|
+
selectors: entry.selectors
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function isOptedIn(selector, entry) {
|
|
43
|
+
if (selector.isGlobal) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (entry.selectors === void 0) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return entry.selectors.includes(selector.name);
|
|
50
|
+
}
|
|
51
|
+
async function collectGlobalSelectors() {
|
|
52
|
+
const globalConfig = await loadGlobalConfig();
|
|
53
|
+
if (!globalConfig?.projects?.length) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
const items = [];
|
|
57
|
+
for (const rawEntry of globalConfig.projects) {
|
|
58
|
+
const entry = normalizeEntry(rawEntry);
|
|
59
|
+
const projectConfig = await loadConfig(entry.path);
|
|
60
|
+
if (!projectConfig?.select) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const scanner = new Scanner(projectConfig.select);
|
|
64
|
+
const results = await scanner.scan(entry.path);
|
|
65
|
+
for (const file of results) {
|
|
66
|
+
for (const selector of file.selectors) {
|
|
67
|
+
if (isOptedIn(selector, entry)) {
|
|
68
|
+
items.push({
|
|
69
|
+
project: entry.name,
|
|
70
|
+
root: entry.path,
|
|
71
|
+
file: file.path,
|
|
72
|
+
content: file.content,
|
|
73
|
+
selector
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
37
77
|
}
|
|
38
|
-
process.exit(1);
|
|
39
78
|
}
|
|
40
|
-
return
|
|
79
|
+
return items;
|
|
41
80
|
}
|
|
42
81
|
const listCommand = new Command("list").alias("ls").description("List all selectors and their current state").option("--json", "Output in JSON format").action(async (options) => {
|
|
43
82
|
const config = await loadConfig();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
83
|
+
let localResults = [];
|
|
84
|
+
if (config?.select) {
|
|
85
|
+
const scanner = new Scanner(config.select);
|
|
86
|
+
localResults = await scanner.scan();
|
|
87
|
+
}
|
|
88
|
+
const globalItems = await collectGlobalSelectors();
|
|
47
89
|
if (options.json) {
|
|
48
|
-
const
|
|
90
|
+
const localJson = localResults.flatMap(
|
|
49
91
|
(file) => file.selectors.map((selector) => ({
|
|
50
92
|
name: selector.name,
|
|
51
93
|
file: relative(process.cwd(), file.path),
|
|
52
94
|
line: selector.line,
|
|
53
95
|
activeOption: selector.getActiveOptionName(),
|
|
54
96
|
isBlock: selector instanceof BlockSelector,
|
|
97
|
+
global: false,
|
|
98
|
+
project: null,
|
|
55
99
|
options: selector.options.map((o) => ({ name: o.name, isActive: o.isActive }))
|
|
56
100
|
}))
|
|
57
101
|
);
|
|
58
|
-
|
|
102
|
+
const globalJson = globalItems.map((item) => ({
|
|
103
|
+
name: `${item.project}:${item.selector.name}`,
|
|
104
|
+
file: item.file,
|
|
105
|
+
line: item.selector.line,
|
|
106
|
+
activeOption: item.selector.getActiveOptionName(),
|
|
107
|
+
isBlock: item.selector instanceof BlockSelector,
|
|
108
|
+
global: true,
|
|
109
|
+
project: item.project,
|
|
110
|
+
options: item.selector.options.map((o) => ({ name: o.name, isActive: o.isActive }))
|
|
111
|
+
}));
|
|
112
|
+
console.log(JSON.stringify([...localJson, ...globalJson], null, 2));
|
|
59
113
|
return;
|
|
60
114
|
}
|
|
61
|
-
if (
|
|
115
|
+
if (localResults.length === 0 && globalItems.length === 0) {
|
|
62
116
|
console.log(pc.yellow("No selectors found"));
|
|
63
117
|
return;
|
|
64
118
|
}
|
|
65
|
-
for (const file of
|
|
119
|
+
for (const file of localResults) {
|
|
66
120
|
const relativePath = relative(process.cwd(), file.path);
|
|
67
121
|
console.log(pc.cyan(relativePath));
|
|
68
|
-
|
|
69
|
-
|
|
122
|
+
printSelectors(file.selectors);
|
|
123
|
+
console.log();
|
|
124
|
+
}
|
|
125
|
+
if (globalItems.length > 0) {
|
|
126
|
+
console.log(pc.magenta("Global"));
|
|
127
|
+
for (const item of globalItems) {
|
|
128
|
+
const activeOptionName = item.selector.getActiveOptionName();
|
|
70
129
|
const activeLabel = activeOptionName ? pc.green(activeOptionName) : pc.yellow("none");
|
|
71
|
-
const blockIndicator = selector instanceof BlockSelector ? pc.dim(" [block]") : "";
|
|
72
|
-
|
|
73
|
-
|
|
130
|
+
const blockIndicator = item.selector instanceof BlockSelector ? pc.dim(" [block]") : "";
|
|
131
|
+
const name = `${item.project}:${item.selector.name}`;
|
|
132
|
+
console.log(
|
|
133
|
+
` ${pc.bold(name)}${blockIndicator}: ${activeLabel} ${pc.dim(`- ${item.file}`)}`
|
|
134
|
+
);
|
|
135
|
+
for (const option of item.selector.options) {
|
|
74
136
|
const marker = option.isActive ? pc.green("●") : pc.dim("○");
|
|
75
137
|
console.log(` ${marker} ${option.name}`);
|
|
76
138
|
}
|
|
@@ -78,10 +140,63 @@ const listCommand = new Command("list").alias("ls").description("List all select
|
|
|
78
140
|
console.log();
|
|
79
141
|
}
|
|
80
142
|
});
|
|
81
|
-
|
|
143
|
+
function printSelectors(selectors) {
|
|
144
|
+
for (const selector of selectors) {
|
|
145
|
+
const activeOptionName = selector.getActiveOptionName();
|
|
146
|
+
const activeLabel = activeOptionName ? pc.green(activeOptionName) : pc.yellow("none");
|
|
147
|
+
const blockIndicator = selector instanceof BlockSelector ? pc.dim(" [block]") : "";
|
|
148
|
+
console.log(` ${pc.bold(selector.name)}${blockIndicator}: ${activeLabel}`);
|
|
149
|
+
for (const option of selector.options) {
|
|
150
|
+
const marker = option.isActive ? pc.green("●") : pc.dim("○");
|
|
151
|
+
console.log(` ${marker} ${option.name}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const setCommand = new Command("set").description("Set a specific option for a selector").argument("<selector>", 'Selector name (use "project:selector" for a global switch)').argument("<option>", "Option to activate").action(async (selectorName, optionName) => {
|
|
156
|
+
const colonIdx = selectorName.indexOf(":");
|
|
157
|
+
if (colonIdx > 0) {
|
|
158
|
+
const projectLabel = selectorName.slice(0, colonIdx);
|
|
159
|
+
const bareName = selectorName.slice(colonIdx + 1);
|
|
160
|
+
const handled = await trySetGlobal(projectLabel, bareName, optionName);
|
|
161
|
+
if (handled) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
await setLocal(selectorName, optionName);
|
|
166
|
+
});
|
|
167
|
+
async function trySetGlobal(projectLabel, bareName, optionName) {
|
|
168
|
+
const items = await collectGlobalSelectors();
|
|
169
|
+
const matches = items.filter(
|
|
170
|
+
(item) => item.project === projectLabel && item.selector.name === bareName
|
|
171
|
+
);
|
|
172
|
+
if (matches.length === 0) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
for (const item of matches) {
|
|
176
|
+
try {
|
|
177
|
+
const newContent = item.selector.switchTo(item.content, optionName, item.file);
|
|
178
|
+
await writeFile(item.file, newContent);
|
|
179
|
+
console.log(
|
|
180
|
+
pc.green(
|
|
181
|
+
`✓ Set ${pc.bold(`${projectLabel}:${bareName}`)} to ${pc.bold(optionName)} in ${item.file}`
|
|
182
|
+
)
|
|
183
|
+
);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (error instanceof Error) {
|
|
186
|
+
console.error(pc.red(error.message));
|
|
187
|
+
}
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
async function setLocal(selectorName, optionName) {
|
|
82
194
|
const config = await loadConfig();
|
|
83
|
-
|
|
84
|
-
|
|
195
|
+
if (!config?.select) {
|
|
196
|
+
console.error(pc.red(`Selector "${selectorName}" not found`));
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
const scanner = new Scanner(config.select);
|
|
85
200
|
const results = await scanner.scan();
|
|
86
201
|
let found = false;
|
|
87
202
|
for (const file of results) {
|
|
@@ -107,7 +222,19 @@ const setCommand = new Command("set").description("Set a specific option for a s
|
|
|
107
222
|
console.error(pc.red(`Selector "${selectorName}" not found`));
|
|
108
223
|
process.exit(1);
|
|
109
224
|
}
|
|
110
|
-
}
|
|
225
|
+
}
|
|
226
|
+
function requireSelectConfig(config, options) {
|
|
227
|
+
if (!config?.select) {
|
|
228
|
+
const message = 'No pik config found or missing "select" section';
|
|
229
|
+
if (options?.json) {
|
|
230
|
+
console.log(JSON.stringify({ error: message }));
|
|
231
|
+
} else {
|
|
232
|
+
console.error(pc.red(message));
|
|
233
|
+
}
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
return config.select;
|
|
237
|
+
}
|
|
111
238
|
const BACK_VALUE = /* @__PURE__ */ Symbol("back");
|
|
112
239
|
function isExitPromptError$1(error) {
|
|
113
240
|
return error instanceof Error && error.name === "ExitPromptError";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,OAAO,aAAa,CAAC;AAMrB,eAAO,MAAM,WAAW,SAgFpB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/set.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,OAAO,aAAa,CAAC;AAErB,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../../src/lib/commands/set.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,OAAO,aAAa,CAAC;AAErB,eAAO,MAAM,UAAU,SAkBnB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type BaseSelector } from '@lsst/pik-core';
|
|
2
|
+
import './types.js';
|
|
3
|
+
export interface GlobalSelectorItem {
|
|
4
|
+
/** Display label of the owning project (used as the `project:` namespace). */
|
|
5
|
+
project: string;
|
|
6
|
+
/** Absolute project root. */
|
|
7
|
+
root: string;
|
|
8
|
+
/** Absolute path of the file holding the selector. */
|
|
9
|
+
file: string;
|
|
10
|
+
/** File content (so callers can switch without re-reading). */
|
|
11
|
+
content: string;
|
|
12
|
+
/** The parsed selector. */
|
|
13
|
+
selector: BaseSelector;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Collect selectors that opted into cross-project visibility, by scanning each
|
|
17
|
+
* project registered in the user-level global config (`~/.config/pik/config.*`).
|
|
18
|
+
*
|
|
19
|
+
* Returns an empty array when there is no global config or no opted-in selectors.
|
|
20
|
+
*/
|
|
21
|
+
export declare function collectGlobalSelectors(): Promise<GlobalSelectorItem[]>;
|
|
22
|
+
//# sourceMappingURL=global-selectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-selectors.d.ts","sourceRoot":"","sources":["../../src/lib/global-selectors.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,gBAAgB,CAAC;AAExB,OAAO,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,QAAQ,EAAE,YAAY,CAAC;CACxB;AAsCD;;;;;GAKG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAqC5E"}
|