dsh-plugin-pack 1.0.14 → 1.0.15
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/client.js +22 -0
- package/lib/import.js +107 -22
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -204,6 +204,9 @@ window.__ModuleLoader__.load({ id: 'dsh-plugin-pack', factory: (require) => {
|
|
|
204
204
|
'backup.rollback': '回滚',
|
|
205
205
|
'backup.rolling': '正在回滚…',
|
|
206
206
|
'backup.done': '回滚完成',
|
|
207
|
+
'backup.surgicalHint': '只撤销了这次导入加的东西,你之后改的其它配置都保留着。',
|
|
208
|
+
'backup.fullHint': '这次的备份是老格式,只能整份还原 —— 导入之后改的配置可能被覆盖。',
|
|
209
|
+
'backup.preSnapshot': '回滚前的配置已另存(想反悔可从这儿取回):',
|
|
207
210
|
'backup.confirm': '确定要回滚到这份备份吗?本次导入新增的插件会被删掉。',
|
|
208
211
|
'backup.createdAt': '时间',
|
|
209
212
|
'backup.items': '项',
|
|
@@ -400,6 +403,9 @@ window.__ModuleLoader__.load({ id: 'dsh-plugin-pack', factory: (require) => {
|
|
|
400
403
|
'backup.rollback': 'Roll back',
|
|
401
404
|
'backup.rolling': 'Rolling back…',
|
|
402
405
|
'backup.done': 'Rollback finished',
|
|
406
|
+
'backup.surgicalHint': 'Only what this import added was reverted; your later config changes are untouched.',
|
|
407
|
+
'backup.fullHint': 'This backup is an old format and can only be restored wholesale — config you changed after the import may be overwritten.',
|
|
408
|
+
'backup.preSnapshot': 'Your config before rollback was saved here (recover from it if needed): ',
|
|
403
409
|
'backup.confirm': 'Roll back to this snapshot? Plugins added by that import will be removed.',
|
|
404
410
|
'backup.createdAt': 'Time',
|
|
405
411
|
'backup.items': 'items',
|
|
@@ -2982,9 +2988,25 @@ window.__ModuleLoader__.load({ id: 'dsh-plugin-pack', factory: (require) => {
|
|
|
2982
2988
|
'div',
|
|
2983
2989
|
null,
|
|
2984
2990
|
h(Alert, { kind: 'ok' }, `${t('backup.done')} · ${t('import.restart')}`),
|
|
2991
|
+
h(
|
|
2992
|
+
'div',
|
|
2993
|
+
{ className: 'pp-hint', style: { marginTop: 6 } },
|
|
2994
|
+
rollbackResult.mode === 'surgical'
|
|
2995
|
+
? t('backup.surgicalHint')
|
|
2996
|
+
: t('backup.fullHint'),
|
|
2997
|
+
),
|
|
2985
2998
|
rollbackResult.restored?.length
|
|
2986
2999
|
? h('div', { className: 'pp-note' }, `恢复: ${rollbackResult.restored.join(', ')}`)
|
|
2987
3000
|
: null,
|
|
3001
|
+
// 回滚前把当时配置另存了一份,路径给出来方便反悔
|
|
3002
|
+
rollbackResult.preRollbackDir
|
|
3003
|
+
? h(
|
|
3004
|
+
'div',
|
|
3005
|
+
{ className: 'pp-hint', style: { marginTop: 6 } },
|
|
3006
|
+
t('backup.preSnapshot'),
|
|
3007
|
+
h('code', { className: 'pp-mono', style: { fontSize: 11, wordBreak: 'break-all' } }, rollbackResult.preRollbackDir),
|
|
3008
|
+
)
|
|
3009
|
+
: null,
|
|
2988
3010
|
)
|
|
2989
3011
|
: null,
|
|
2990
3012
|
),
|
package/lib/import.js
CHANGED
|
@@ -447,6 +447,11 @@ export function importBundle(options = {}) {
|
|
|
447
447
|
pkg.dsh = pkg.dsh && typeof pkg.dsh === 'object' ? pkg.dsh : {};
|
|
448
448
|
pkg.dsh.profile = pkg.dsh.profile && typeof pkg.dsh.profile === 'object' ? pkg.dsh.profile : {};
|
|
449
449
|
pkg.dsh.profile.bundles = Array.isArray(pkg.dsh.profile.bundles) ? pkg.dsh.profile.bundles : [];
|
|
450
|
+
/* 改写前先记下"每个被碰过的字段原来是什么"。
|
|
451
|
+
* 回滚时只把这几个字段还原,不动用户后来改的其它配置 ——
|
|
452
|
+
* 早先回滚是整份覆盖,会把导入之后设的 API 密钥之类一起抹掉。 */
|
|
453
|
+
const depsBefore = { ...pkg.dependencies };
|
|
454
|
+
const bundlesBefore = [...pkg.dsh.profile.bundles];
|
|
450
455
|
/* enable=false:只把文件装上、依赖写好,但不进 dsh.profile.bundles
|
|
451
456
|
* —— 也就是"装好了但先不启用"。用户之后可以在界面上再打开。
|
|
452
457
|
* files 与依赖照常写,所以不启用不等于没装。 */
|
|
@@ -460,13 +465,30 @@ export function importBundle(options = {}) {
|
|
|
460
465
|
}
|
|
461
466
|
writeJson(pkgFile, pkg);
|
|
462
467
|
|
|
468
|
+
// 记录:哪些依赖被改(含改前值)、哪些 bundle 是新加的
|
|
469
|
+
record.pkgDepsBefore = {};
|
|
470
|
+
for (const item of plan) {
|
|
471
|
+
if (item.action === 'skip' || item.pathPlugin) continue;
|
|
472
|
+
const had = Object.prototype.hasOwnProperty.call(depsBefore, item.name);
|
|
473
|
+
record.pkgDepsBefore[item.name] = {
|
|
474
|
+
before: had ? depsBefore[item.name] : null,
|
|
475
|
+
after: `link:./local-plugins/${item.segment}`,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
record.pkgBundlesAdded = pkg.dsh.profile.bundles.filter((name) => !bundlesBefore.includes(name));
|
|
479
|
+
|
|
463
480
|
// cordis.patch.yml
|
|
464
481
|
let nextPatch = patchText;
|
|
465
482
|
if (patchPlan.topRows.length) nextPatch = appendTopRows(nextPatch, patchPlan.topRows, `plugin-pack ${stampText}`);
|
|
466
483
|
if (patchPlan.insertRows.length) {
|
|
467
484
|
nextPatch = appendInsertBlock(nextPatch, dedentRows(patchPlan.insertRows), `plugin-pack ${stampText}`).text;
|
|
468
485
|
}
|
|
469
|
-
if (nextPatch !== patchText)
|
|
486
|
+
if (nextPatch !== patchText) {
|
|
487
|
+
writeText(patchFile, nextPatch);
|
|
488
|
+
/* 记下这次"追加的那一段原文"。回滚时只把这几个字串摘掉,
|
|
489
|
+
* 保留用户后来在同一个文件里做的其它修改。 */
|
|
490
|
+
record.patchAppended = nextPatch.startsWith(patchText) ? nextPatch.slice(patchText.length) : null;
|
|
491
|
+
}
|
|
470
492
|
|
|
471
493
|
// node_modules 链接
|
|
472
494
|
onProgress('建立 node_modules 链接', 92);
|
|
@@ -701,15 +723,77 @@ export function rollbackImport(options = {}) {
|
|
|
701
723
|
const removed = [];
|
|
702
724
|
const relinked = [];
|
|
703
725
|
|
|
704
|
-
const
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
726
|
+
const pkgFile = path.join(profile, 'package.json');
|
|
727
|
+
const patchFile = path.join(profile, 'cordis.patch.yml');
|
|
728
|
+
|
|
729
|
+
/* 第一步:先把**当前**的配置文件另存一份。
|
|
730
|
+
*
|
|
731
|
+
* 回滚会改 package.json / cordis.patch.yml,早先直接整份覆盖,
|
|
732
|
+
* 用户在这之后设的 API 密钥、改过的 provider 全没了且找不回来。
|
|
733
|
+
* 现在无论走精准回滚还是整份还原,都先留一份可反悔的副本。 */
|
|
734
|
+
const preRollbackDir = path.join(backupRoot, `pre-rollback-${stamp()}`);
|
|
735
|
+
let preRollbackFiles = [];
|
|
736
|
+
try {
|
|
737
|
+
ensureDir(preRollbackDir);
|
|
738
|
+
for (const [src, name] of [
|
|
739
|
+
[pkgFile, 'package.json'],
|
|
740
|
+
[patchFile, 'cordis.patch.yml'],
|
|
741
|
+
]) {
|
|
742
|
+
if (isFile(src)) {
|
|
743
|
+
fs.copyFileSync(src, path.join(preRollbackDir, name));
|
|
744
|
+
preRollbackFiles.push(name);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
} catch {
|
|
748
|
+
/* 存不了也继续,不阻断回滚 */
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/* 第二步:优先精准回滚 —— 只撤销这次导入加的东西,保留用户后续改动。
|
|
752
|
+
* 老备份没有这些字段,才退回整份还原(那种情况确实是快照替换)。 */
|
|
753
|
+
let mode = 'full';
|
|
754
|
+
|
|
755
|
+
if (record.pkgDepsBefore || Array.isArray(record.pkgBundlesAdded)) {
|
|
756
|
+
mode = 'surgical';
|
|
757
|
+
const pkg = readJson(pkgFile, null);
|
|
758
|
+
if (pkg && typeof pkg === 'object') {
|
|
759
|
+
pkg.dependencies = pkg.dependencies && typeof pkg.dependencies === 'object' ? pkg.dependencies : {};
|
|
760
|
+
// 依赖:只在"当前值还是我们写进去的那个"时才还原,避免覆盖用户后来的改动
|
|
761
|
+
for (const [name, info] of Object.entries(record.pkgDepsBefore ?? {})) {
|
|
762
|
+
if (pkg.dependencies[name] !== info.after) continue;
|
|
763
|
+
if (info.before === null) delete pkg.dependencies[name];
|
|
764
|
+
else pkg.dependencies[name] = info.before;
|
|
765
|
+
}
|
|
766
|
+
// bundles:只移除这次加的,且现在还在的
|
|
767
|
+
const addedSet = new Set(record.pkgBundlesAdded ?? []);
|
|
768
|
+
if (Array.isArray(pkg.dsh?.profile?.bundles)) {
|
|
769
|
+
pkg.dsh.profile.bundles = pkg.dsh.profile.bundles.filter((name) => !addedSet.has(name));
|
|
770
|
+
}
|
|
771
|
+
writeJson(pkgFile, pkg);
|
|
772
|
+
restored.push('package.json(仅撤销本次导入的依赖与 bundles)');
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (typeof record.patchAppended === 'string') {
|
|
777
|
+
mode = mode === 'surgical' ? 'surgical' : 'surgical';
|
|
778
|
+
const currentPatch = readText(patchFile, null);
|
|
779
|
+
if (typeof currentPatch === 'string' && record.patchAppended && currentPatch.includes(record.patchAppended)) {
|
|
780
|
+
// 只摘掉当初追加的那一段,其它内容(含用户后加的)原样保留
|
|
781
|
+
const nextText = currentPatch.replace(record.patchAppended, '');
|
|
782
|
+
writeText(patchFile, nextText);
|
|
783
|
+
restored.push('cordis.patch.yml(仅移除本次导入追加的行)');
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// 精准回滚没覆盖到的老备份,才退回整份还原
|
|
788
|
+
const pkgHandled = restored.some((item) => item.startsWith('package.json'));
|
|
789
|
+
const patchHandled = restored.some((item) => item.startsWith('cordis.patch.yml'));
|
|
790
|
+
if (!pkgHandled && isFile(path.join(backupRoot, 'package.json'))) {
|
|
791
|
+
fs.copyFileSync(path.join(backupRoot, 'package.json'), pkgFile);
|
|
792
|
+
restored.push('package.json(整份还原:老备份没有精准记录)');
|
|
709
793
|
}
|
|
710
|
-
if (isFile(
|
|
711
|
-
fs.copyFileSync(
|
|
712
|
-
restored.push('cordis.patch.yml');
|
|
794
|
+
if (!patchHandled && isFile(path.join(backupRoot, 'cordis.patch.yml'))) {
|
|
795
|
+
fs.copyFileSync(path.join(backupRoot, 'cordis.patch.yml'), patchFile);
|
|
796
|
+
restored.push('cordis.patch.yml(整份还原:老备份没有精准记录)');
|
|
713
797
|
}
|
|
714
798
|
|
|
715
799
|
for (const dest of record.addedDirs ?? []) {
|
|
@@ -748,32 +832,33 @@ export function rollbackImport(options = {}) {
|
|
|
748
832
|
removed.push(file);
|
|
749
833
|
}
|
|
750
834
|
|
|
751
|
-
//
|
|
835
|
+
// 回滚改了配置和插件目录,扫描缓存必须失效
|
|
836
|
+
// (早先这行写在 return 之后,成功路径根本执行不到)
|
|
837
|
+
clearScanCache();
|
|
838
|
+
|
|
839
|
+
/* 标记该备份已消费:重命名加 .done,之后「最新备份」指向更早的、还没回滚过的那次。
|
|
840
|
+
* 注意:pre-rollback 快照在备份目录里,重命名后它跟着一起搬到 .done 里,
|
|
841
|
+
* 仍然可以取出来反悔。 */
|
|
842
|
+
let backupArchived = null;
|
|
752
843
|
try {
|
|
753
844
|
const done = `${backupRoot}.done`;
|
|
754
845
|
fs.renameSync(backupRoot, done);
|
|
755
|
-
|
|
756
|
-
ok: true,
|
|
757
|
-
backup: backupRoot,
|
|
758
|
-
backupArchived: done,
|
|
759
|
-
restored,
|
|
760
|
-
removed,
|
|
761
|
-
unlinked: relinked,
|
|
762
|
-
restartRequired: true,
|
|
763
|
-
};
|
|
846
|
+
backupArchived = done;
|
|
764
847
|
} catch {
|
|
765
848
|
/* 重命名失败不影响回滚结果 */
|
|
766
849
|
}
|
|
767
850
|
|
|
768
|
-
// 回滚改了配置和插件目录,扫描缓存失效
|
|
769
|
-
clearScanCache();
|
|
770
|
-
|
|
771
851
|
return {
|
|
772
852
|
ok: true,
|
|
773
853
|
backup: backupRoot,
|
|
854
|
+
backupArchived,
|
|
855
|
+
mode,
|
|
774
856
|
restored,
|
|
775
857
|
removed,
|
|
776
858
|
unlinked: relinked,
|
|
859
|
+
// 回滚前把当时的配置另存了一份,路径给出来方便反悔
|
|
860
|
+
preRollbackDir: preRollbackFiles.length ? preRollbackDir : null,
|
|
861
|
+
preRollbackFiles,
|
|
777
862
|
restartRequired: true,
|
|
778
863
|
};
|
|
779
864
|
}
|
package/package.json
CHANGED