@1e0zj/dsh-plugin-mall 0.1.4 → 0.1.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/package.json +1 -1
- package/src/client.js +44 -75
- package/src/installer.js +23 -12
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -30,8 +30,6 @@ window.__ModuleLoader__.load({
|
|
|
30
30
|
".mkt_btnDanger:hover:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}",
|
|
31
31
|
".mkt_btnSm{padding:3px 10px;font-size:12px}",
|
|
32
32
|
".mkt_error{color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}",
|
|
33
|
-
".mkt_columns{display:grid;grid-template-columns:1fr 320px;gap:14px;align-items:start}",
|
|
34
|
-
"@media (max-width:860px){.mkt_columns{grid-template-columns:1fr}}",
|
|
35
33
|
".mkt_list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;align-items:stretch;min-width:0}",
|
|
36
34
|
"@media (max-width:820px){.mkt_list{grid-template-columns:minmax(0,1fr)}}",
|
|
37
35
|
".mkt_listHead{grid-column:1/-1;font-size:12px;color:var(--dsw-alias-label-tertiary)}",
|
|
@@ -41,7 +39,7 @@ window.__ModuleLoader__.load({
|
|
|
41
39
|
".mkt_meta{font-size:12px;color:var(--dsw-alias-label-tertiary)}",
|
|
42
40
|
".mkt_desc{font-size:12.5px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}",
|
|
43
41
|
".mkt_cardActions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:auto;padding-top:4px}",
|
|
44
|
-
".
|
|
42
|
+
".mkt_cardActions .mkt_btn{text-decoration:none}",
|
|
45
43
|
".mkt_panelTitle{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);margin:0}",
|
|
46
44
|
".mkt_pre{font-family:Consolas,Monaco,monospace;font-size:11.5px;line-height:16px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-secondary,#f6f7f8);border-radius:6px;padding:8px;max-height:220px;overflow:auto;white-space:pre-wrap;word-break:break-all}",
|
|
47
45
|
".mkt_ok{color:var(--dsw-alias-state-success-primary,#2f855a)}",
|
|
@@ -49,8 +47,8 @@ window.__ModuleLoader__.load({
|
|
|
49
47
|
".mkt_link{color:var(--dsw-alias-state-business-primary);font-size:12px;text-decoration:none;cursor:pointer}",
|
|
50
48
|
".mkt_installedHead{display:flex;align-items:center;gap:8px;width:100%;background:none;border:0;padding:0;margin:0;cursor:pointer;font:inherit;text-align:left}",
|
|
51
49
|
".mkt_installedHead:hover .mkt_panelTitle{color:var(--dsw-alias-state-business-primary)}",
|
|
52
|
-
".
|
|
53
|
-
"
|
|
50
|
+
".mkt_installedToggle{margin-left:auto}",
|
|
51
|
+
".mkt_depList{display:flex;flex-direction:column;gap:6px;margin-top:8px}",
|
|
54
52
|
".mkt_depRow{display:flex;justify-content:space-between;align-items:center;gap:8px}",
|
|
55
53
|
].join("\n");
|
|
56
54
|
var tagId = "@1e0zj/dsh-plugin-mall/market-tab.css";
|
|
@@ -131,6 +129,15 @@ window.__ModuleLoader__.load({
|
|
|
131
129
|
function RepoCard(props) {
|
|
132
130
|
var item = props.item;
|
|
133
131
|
var installing = props.installing === true;
|
|
132
|
+
var job = props.installJob;
|
|
133
|
+
var installLabel = "安装";
|
|
134
|
+
var installDisabled = installing;
|
|
135
|
+
if (!installing && job) {
|
|
136
|
+
if (job.status === "running") { installLabel = "安装中…"; installDisabled = true; }
|
|
137
|
+
else if (job.status === "completed") { installLabel = "✓ 已装 · 重启生效"; installDisabled = true; }
|
|
138
|
+
else if (job.status === "failed") { installLabel = "安装失败 · 见任务日志"; }
|
|
139
|
+
else { installLabel = "已取消 · 重试"; }
|
|
140
|
+
}
|
|
134
141
|
return h("div", { className: "mkt_card" },
|
|
135
142
|
h("div", { className: "mkt_cardHead" },
|
|
136
143
|
h("span", { className: "mkt_name" }, item.fullName),
|
|
@@ -142,43 +149,13 @@ window.__ModuleLoader__.load({
|
|
|
142
149
|
item.description ? h("div", { className: "mkt_desc" }, clip(item.description, 180)) : null,
|
|
143
150
|
h("div", { className: "mkt_meta" }, "更新 " + (item.updatedAt || "").slice(0, 10)),
|
|
144
151
|
h("div", { className: "mkt_cardActions" },
|
|
145
|
-
h("button", { className: "mkt_btn mkt_btnPrimary", disabled:
|
|
146
|
-
|
|
147
|
-
h("
|
|
152
|
+
h("button", { className: "mkt_btn mkt_btnPrimary", disabled: installDisabled, onClick: function () { props.onInstall(item.fullName); } },
|
|
153
|
+
installLabel),
|
|
154
|
+
h("a", { className: "mkt_btn", href: item.htmlUrl, target: "_blank", rel: "noreferrer" }, "前往仓库 ↗")
|
|
148
155
|
)
|
|
149
156
|
);
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
// ── info panel ──────────────────────────────────────────────────────────
|
|
153
|
-
function InfoPanel(props) {
|
|
154
|
-
var info = props.info;
|
|
155
|
-
if (!info) return null;
|
|
156
|
-
var data = info.data;
|
|
157
|
-
return h("div", { className: "mkt_card" },
|
|
158
|
-
h("p", { className: "mkt_panelTitle" }, "仓库详情"),
|
|
159
|
-
info.loading ? h("div", { className: "mkt_meta" }, "加载中…")
|
|
160
|
-
: info.error ? h("div", { className: "mkt_error" }, info.error)
|
|
161
|
-
: !data ? null
|
|
162
|
-
: h(React.Fragment, null,
|
|
163
|
-
h("div", { className: "mkt_name" }, data.meta.fullName),
|
|
164
|
-
h("div", { className: "mkt_desc" }, clip(data.meta.description, 240)),
|
|
165
|
-
h("div", { className: "mkt_meta" },
|
|
166
|
-
"★" + data.meta.stars + " · " + (data.meta.language || "-") + " · " + (data.meta.license || "-") + " · 分支 " + data.meta.defaultBranch),
|
|
167
|
-
data.packageJson == null
|
|
168
|
-
? h("div", { className: "mkt_meta" }, "package.json: 未找到(可能不是 npm 插件)")
|
|
169
|
-
: h(React.Fragment, null,
|
|
170
|
-
h("div", { className: "mkt_meta" }, "package.json: " + (data.packageJson.name || "?") + "@" + (data.packageJson.version || "?")),
|
|
171
|
-
data.packageJson.dshBundlePatch != null
|
|
172
|
-
? h("div", { className: "mkt_ok" }, "✓ dsh.bundle 宿主插件")
|
|
173
|
-
: data.packageJson.dshClientPlatform != null
|
|
174
|
-
? h("div", { className: "mkt_ok" }, "✓ dsh.client 浏览器UI插件")
|
|
175
|
-
: h("div", { className: "mkt_meta" }, "⚠ 无 bundle/client 声明,装上是普通依赖")),
|
|
176
|
-
h("button", { className: "mkt_btn mkt_btnPrimary", onClick: function () { props.onInstall(data.meta.fullName); } }, "安装 github:" + data.meta.fullName),
|
|
177
|
-
h("a", { className: "mkt_link", href: data.meta.htmlUrl, target: "_blank", rel: "noreferrer" }, "打开仓库 ↗")
|
|
178
|
-
)
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
159
|
// ── jobs panel ──────────────────────────────────────────────────────────
|
|
183
160
|
function JobsPanel(props) {
|
|
184
161
|
var ids = Object.keys(props.jobs);
|
|
@@ -220,7 +197,7 @@ window.__ModuleLoader__.load({
|
|
|
220
197
|
},
|
|
221
198
|
h("span", { className: "mkt_panelTitle" }, "已装插件"),
|
|
222
199
|
h("span", { className: "mkt_badge" }, count + " 个"),
|
|
223
|
-
h("span", { className: "mkt_meta" }, open ? "收起 ▲" : "展开 ▼")
|
|
200
|
+
h("span", { className: "mkt_meta mkt_installedToggle" }, open ? "收起 ▲" : "展开 ▼")
|
|
224
201
|
),
|
|
225
202
|
installed.error
|
|
226
203
|
? h("div", { className: "mkt_error" }, installed.error)
|
|
@@ -259,9 +236,6 @@ window.__ModuleLoader__.load({
|
|
|
259
236
|
var _error = useState(null);
|
|
260
237
|
var error = _error[0];
|
|
261
238
|
var setError = _error[1];
|
|
262
|
-
var _info = useState(null);
|
|
263
|
-
var info = _info[0];
|
|
264
|
-
var setInfo = _info[1];
|
|
265
239
|
var _installed = useState(null);
|
|
266
240
|
var installed = _installed[0];
|
|
267
241
|
var setInstalled = _installed[1];
|
|
@@ -291,15 +265,6 @@ window.__ModuleLoader__.load({
|
|
|
291
265
|
});
|
|
292
266
|
}, [call, query, sort]);
|
|
293
267
|
|
|
294
|
-
var doInfo = useCallback(function (repo) {
|
|
295
|
-
setInfo({ repo: repo, loading: true });
|
|
296
|
-
call("info", { repo: repo }).then(function (value) {
|
|
297
|
-
setInfo({ repo: repo, data: value });
|
|
298
|
-
}).catch(function (e) {
|
|
299
|
-
setInfo({ repo: repo, error: errorText(e) });
|
|
300
|
-
});
|
|
301
|
-
}, [call]);
|
|
302
|
-
|
|
303
268
|
var refreshInstalled = useCallback(function () {
|
|
304
269
|
call("installed", {}).then(function (value) {
|
|
305
270
|
setInstalled(value);
|
|
@@ -361,6 +326,10 @@ window.__ModuleLoader__.load({
|
|
|
361
326
|
refreshInstalled();
|
|
362
327
|
}, [refreshInstalled]);
|
|
363
328
|
|
|
329
|
+
// 活动任务置顶(紧跟已装面板,点安装立刻看到进度);全部静止后归档到底部当日志。
|
|
330
|
+
var jobsActive = false;
|
|
331
|
+
for (var jid in jobs) { if (jobs[jid] && jobs[jid].status === "running") { jobsActive = true; break; } }
|
|
332
|
+
|
|
364
333
|
return h("div", { className: "mkt_root" },
|
|
365
334
|
h("div", { className: "mkt_head" },
|
|
366
335
|
h("div", { className: "mkt_title" }, "插件市场"),
|
|
@@ -388,30 +357,30 @@ window.__ModuleLoader__.load({
|
|
|
388
357
|
),
|
|
389
358
|
error ? h("div", { className: "mkt_error" }, error) : null,
|
|
390
359
|
h(InstalledPanel, { installed: installed, removing: removing, onUninstall: doUninstall }),
|
|
391
|
-
h(
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
)
|
|
360
|
+
jobsActive ? h(JobsPanel, { jobs: jobs }) : null,
|
|
361
|
+
h("div", { className: "mkt_list" },
|
|
362
|
+
results == null
|
|
363
|
+
? h("div", { className: "mkt_meta mkt_listHead" }, loading ? "正在加载最热插件…" : "—")
|
|
364
|
+
: results.items.length === 0
|
|
365
|
+
? h("div", { className: "mkt_meta mkt_listHead" }, "没有匹配的仓库。")
|
|
366
|
+
: h(React.Fragment, null,
|
|
367
|
+
h("div", { className: "mkt_meta mkt_listHead" }, "共 " + results.total + " 个仓库(显示 " + results.items.length + " 个)"),
|
|
368
|
+
results.items.map(function (item) {
|
|
369
|
+
var installJob = null;
|
|
370
|
+
for (var jobId in jobs) {
|
|
371
|
+
if (jobs[jobId] && jobs[jobId].spec === "github:" + item.fullName) { installJob = jobs[jobId]; break; }
|
|
372
|
+
}
|
|
373
|
+
return h(RepoCard, {
|
|
374
|
+
key: item.fullName,
|
|
375
|
+
item: item,
|
|
376
|
+
installing: installing[item.fullName] === true,
|
|
377
|
+
installJob: installJob,
|
|
378
|
+
onInstall: doInstall,
|
|
379
|
+
});
|
|
380
|
+
})
|
|
381
|
+
)
|
|
382
|
+
),
|
|
383
|
+
jobsActive ? null : h(JobsPanel, { jobs: jobs })
|
|
415
384
|
);
|
|
416
385
|
}
|
|
417
386
|
|
package/src/installer.js
CHANGED
|
@@ -218,19 +218,26 @@ export function removeClientRow(profileDir, packageName) {
|
|
|
218
218
|
// ── build-script allow-listing ──────────────────────────────────────────────
|
|
219
219
|
|
|
220
220
|
/** Extract package names from pnpm's "Ignored build scripts: ..." output. */
|
|
221
|
+
/** Valid npm package name (scoped or bare) — anything else is not a name. */
|
|
222
|
+
const NPM_NAME_RE = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/i;
|
|
223
|
+
|
|
221
224
|
function parseIgnoredBuilds(output) {
|
|
222
225
|
const names = new Set();
|
|
223
|
-
|
|
226
|
+
// Only pnpm's own notice line is a parsing source. "allowBuilds" also
|
|
227
|
+
// appears in pnpm's advice/error text (never followed by a name list), and
|
|
228
|
+
// matching it fed error echoes into the allow-list, corrupting the YAML.
|
|
229
|
+
const pattern = /(?:Ignored build scripts|onlyBuiltDependencies)\s*:\s*([^\n]+)/gi;
|
|
224
230
|
let match;
|
|
225
231
|
while ((match = pattern.exec(output)) !== null) {
|
|
226
232
|
for (const raw of match[1].split(",")) {
|
|
227
|
-
const
|
|
228
|
-
if (
|
|
229
|
-
// Strip a trailing @version so `foo@1.2.3` -> `foo`,
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
233
|
+
const candidate = raw.trim();
|
|
234
|
+
if (candidate.length === 0) continue;
|
|
235
|
+
// Strip a trailing @version or @tarball-url so `foo@1.2.3` -> `foo`,
|
|
236
|
+
// `@s/n@1.0.0` -> `@s/n`, `@s/n@https://…` -> `@s/n`. Whatever remains
|
|
237
|
+
// must be a valid npm name; pnpm error echoes ("9 | - pkg", advice
|
|
238
|
+
// sentences) are dropped instead of being written to the YAML.
|
|
239
|
+
const name = candidate.replace(/@(?:[\w.+-]+|https?:\/\/\S+|file:\S+|link:\S+|github:\S+)$/, "");
|
|
240
|
+
if (NPM_NAME_RE.test(name)) names.add(name);
|
|
234
241
|
}
|
|
235
242
|
}
|
|
236
243
|
return [...names];
|
|
@@ -238,6 +245,8 @@ function parseIgnoredBuilds(output) {
|
|
|
238
245
|
|
|
239
246
|
/** Merge new names into the profile's pnpm-workspace.yaml `allowBuilds` list. */
|
|
240
247
|
function ensureAllowBuilds(profileDir, names) {
|
|
248
|
+
const valid = names.filter((name) => NPM_NAME_RE.test(name));
|
|
249
|
+
if (valid.length === 0) return;
|
|
241
250
|
const workspacePath = join(profileDir, "pnpm-workspace.yaml");
|
|
242
251
|
let content = existsSync(workspacePath)
|
|
243
252
|
? readFileSync(workspacePath, "utf8")
|
|
@@ -246,22 +255,24 @@ function ensureAllowBuilds(profileDir, names) {
|
|
|
246
255
|
const keyIndex = lines.findIndex((line) => /^allowBuilds\s*:/.test(line));
|
|
247
256
|
if (keyIndex === -1) {
|
|
248
257
|
if (!content.endsWith("\n")) content += "\n";
|
|
249
|
-
|
|
258
|
+
// Quote every entry: a bare `@scope/name` starts with YAML's reserved
|
|
259
|
+
// `@` indicator and fails to parse.
|
|
260
|
+
content += `\nallowBuilds:\n${valid.map((name) => ` - '${name}'`).join("\n")}\n`;
|
|
250
261
|
} else {
|
|
251
262
|
const existing = new Set();
|
|
252
263
|
let insertIndex = keyIndex + 1;
|
|
253
264
|
for (let index = keyIndex + 1; index < lines.length; index++) {
|
|
254
265
|
const item = /^\s*-\s+(.+?)\s*$/.exec(lines[index]);
|
|
255
266
|
if (item) {
|
|
256
|
-
existing.add(item[1]);
|
|
267
|
+
existing.add(item[1].replace(/^['"](.*)['"]$/, "$1"));
|
|
257
268
|
insertIndex = index + 1;
|
|
258
269
|
continue;
|
|
259
270
|
}
|
|
260
271
|
if (/^\S/.test(lines[index])) break;
|
|
261
272
|
}
|
|
262
|
-
const additions =
|
|
273
|
+
const additions = valid.filter((name) => !existing.has(name));
|
|
263
274
|
if (additions.length > 0) {
|
|
264
|
-
lines.splice(insertIndex, 0, ...additions.map((name) => ` - ${name}`));
|
|
275
|
+
lines.splice(insertIndex, 0, ...additions.map((name) => ` - '${name}'`));
|
|
265
276
|
content = lines.join("\n");
|
|
266
277
|
}
|
|
267
278
|
}
|