@sparkelf/dsh-plus 0.2.1-rc.2.4 → 0.2.2-alpha.2.10
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 +7 -5
- package/README.zh.md +7 -5
- package/lib/apply-08huHfLG.js +665 -0
- package/lib/apply-DmICvIGh.js +653 -0
- package/lib/apply.js +1 -1
- package/lib/bin.js +229 -47
- package/lib/types/apply.d.ts +1 -1
- package/lib/types/apply.js +56 -117
- package/lib/types/patch-set.d.ts +59 -0
- package/lib/types/patch-set.js +132 -0
- package/lib/types/standalone-cli.js +93 -2
- package/lib/types/standalone-profile.d.ts +8 -1
- package/lib/types/standalone-profile.js +191 -53
- package/package.json +603 -175
- package/patches/approval-allow-policy.patch +108 -0
- package/patches/atomic-write-zombie-lock-holder.patch +53 -0
- package/patches/bootstrap-exempt-names.patch +32 -0
- package/patches/browser-auth-mode.patch +127 -0
- package/patches/composer-popover-boundaries.patch +337 -0
- package/patches/diagram-preview.patch +562 -0
- package/patches/image-offload-hoist-replay.patch +533 -0
- package/patches/legacy-code-preset.patch +148 -0
- package/patches/llm-pi-ai-transport-truncation.patch +33 -0
- package/patches/mobile-journal-generation.patch +76 -0
- package/patches/office-preview-stable-load.patch +34 -0
- package/patches/officecli-deliverables.patch +42 -0
- package/patches/ptc-mcp-schema-types.patch +156 -0
- package/patches/responses-reasoning-status.patch +289 -0
- package/patches/session-envelope-ignorable.patch +63 -0
- package/patches/session-export-chinese.patch +85 -0
- package/patches/session-format-legacy-restart.patch +35 -0
- package/patches/session-log-trajectory-toolbar.patch +352 -0
- package/patches/state-dot-brand-blue.patch +185 -0
- package/patches/ui-settings-compact-phone.patch +76 -0
- package/patches/web-runtime-compat.patch +83 -0
- package/patches/workspace-storage-restore.patch +186 -0
- package/patches/wsl-native-open.patch +265 -0
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
* bundle's own list.
|
|
10
10
|
*/
|
|
11
11
|
import { spawnSync } from 'node:child_process';
|
|
12
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
13
13
|
import { createRequire } from 'node:module';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
14
15
|
import { homedir } from 'node:os';
|
|
15
16
|
import { dirname, join, posix, resolve, win32 } from 'node:path';
|
|
16
17
|
import { parseDocument } from 'yaml';
|
|
18
|
+
import { readPatchSet } from "./patch-set.js";
|
|
17
19
|
/** Profile name a standalone installation owns when its package declares none. */
|
|
18
20
|
export const STANDALONE_PROFILE = 'plus';
|
|
19
21
|
/**
|
|
@@ -97,6 +99,12 @@ function requireStringArray(value, label) {
|
|
|
97
99
|
throw new Error(label + ' must be a string array');
|
|
98
100
|
return value;
|
|
99
101
|
}
|
|
102
|
+
/** Read one non-empty string field. */
|
|
103
|
+
function requireString(value, label) {
|
|
104
|
+
if (typeof value !== 'string' || value.trim() === '')
|
|
105
|
+
throw new Error(label + ' must be a non-empty string');
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
100
108
|
/** Resolve the DSH home, honouring the environment override the launcher itself reads. */
|
|
101
109
|
export function resolveHome(env = process.env) {
|
|
102
110
|
const configured = env.DSH_HOME;
|
|
@@ -135,12 +143,15 @@ export function isWithin(candidate, parent, platform = process.platform) {
|
|
|
135
143
|
* @returns absolute path to the installed distribution.
|
|
136
144
|
*/
|
|
137
145
|
export function resolveDistributionDirectory(anchor) {
|
|
146
|
+
const ownDirectory = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
138
147
|
let resolved;
|
|
139
148
|
try {
|
|
140
149
|
resolved = dirname(createRequire(anchor).resolve('@sparkelf/dsh-plus/package.json'));
|
|
141
150
|
}
|
|
142
151
|
catch {
|
|
143
|
-
|
|
152
|
+
// `npx @sparkelf/dsh-plus ...` runs the CLI from a temporary install, so the consumer
|
|
153
|
+
// tree has no copy to resolve. The running module's own package is the distribution.
|
|
154
|
+
return ownDirectory;
|
|
144
155
|
}
|
|
145
156
|
// A resolution that leaves the consumer tree means the module answered from its own
|
|
146
157
|
// installation, which reports health for a distribution the consumer never installed.
|
|
@@ -156,21 +167,14 @@ export function resolveDistributionDirectory(anchor) {
|
|
|
156
167
|
break;
|
|
157
168
|
current = parent;
|
|
158
169
|
}
|
|
159
|
-
|
|
170
|
+
return ownDirectory;
|
|
160
171
|
}
|
|
161
|
-
/** The reviewed bundle order and pins the installed distribution declares. */
|
|
162
172
|
export function readDistributionProfile(distributionDirectory) {
|
|
163
173
|
const manifest = requireRecord(JSON.parse(readFileSync(join(distributionDirectory, 'package.json'), 'utf8')), 'Plus distribution manifest');
|
|
164
174
|
const plus = requireRecord(manifest.dshPlus, 'dshPlus');
|
|
165
175
|
const profile = requireRecord(plus.profile, 'dshPlus.profile');
|
|
166
|
-
const
|
|
176
|
+
const dependencies = requireDependencyMap(requireRecord(profile.dependencies, 'dshPlus.profile.dependencies'), 'dshPlus.profile.dependencies');
|
|
167
177
|
const rawAllowBuilds = requireRecord(profile.allowBuilds, 'dshPlus.profile.allowBuilds');
|
|
168
|
-
const dependencies = {};
|
|
169
|
-
for (const [name, spec] of Object.entries(rawDependencies)) {
|
|
170
|
-
if (typeof spec !== 'string' || spec === '')
|
|
171
|
-
throw new Error('dshPlus.profile.dependencies.' + name + ' must be a non-empty string');
|
|
172
|
-
dependencies[name] = spec;
|
|
173
|
-
}
|
|
174
178
|
const allowBuilds = {};
|
|
175
179
|
for (const [name, allowed] of Object.entries(rawAllowBuilds)) {
|
|
176
180
|
if (typeof allowed !== 'boolean')
|
|
@@ -189,6 +193,16 @@ export function readDistributionProfile(distributionDirectory) {
|
|
|
189
193
|
throw new Error('dshPlus.profile.overrides.' + name + ' must be a non-empty string');
|
|
190
194
|
overrides[name] = spec;
|
|
191
195
|
}
|
|
196
|
+
const editions = {};
|
|
197
|
+
const rawEditions = plus.editions === undefined ? {} : requireRecord(plus.editions, 'dshPlus.editions');
|
|
198
|
+
for (const [id, value] of Object.entries(rawEditions)) {
|
|
199
|
+
const edition = requireRecord(value, 'dshPlus.editions.' + id);
|
|
200
|
+
editions[id] = {
|
|
201
|
+
label: requireString(edition.label, 'dshPlus.editions.' + id + '.label'),
|
|
202
|
+
bundles: requireStringArray(edition.bundles, 'dshPlus.editions.' + id + '.bundles'),
|
|
203
|
+
packages: requireDependencyMap(requireRecord(edition.packages, 'dshPlus.editions.' + id + '.packages'), 'dshPlus.editions.' + id + '.packages'),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
192
206
|
const compatibility = requireRecord(plus.compatibility, 'dshPlus.compatibility');
|
|
193
207
|
return {
|
|
194
208
|
name: String(manifest.name),
|
|
@@ -196,10 +210,21 @@ export function readDistributionProfile(distributionDirectory) {
|
|
|
196
210
|
dependencies,
|
|
197
211
|
allowBuilds,
|
|
198
212
|
overrides,
|
|
213
|
+
editions,
|
|
199
214
|
dshRange: String(compatibility.dsh),
|
|
200
215
|
version: String(manifest.version),
|
|
201
216
|
};
|
|
202
217
|
}
|
|
218
|
+
/** Read one package-name-to-spec map, rejecting empty entries. */
|
|
219
|
+
function requireDependencyMap(value, label) {
|
|
220
|
+
const dependencies = {};
|
|
221
|
+
for (const [name, spec] of Object.entries(value)) {
|
|
222
|
+
if (typeof spec !== 'string' || spec === '')
|
|
223
|
+
throw new Error(label + '.' + name + ' must be a non-empty string');
|
|
224
|
+
dependencies[name] = spec;
|
|
225
|
+
}
|
|
226
|
+
return dependencies;
|
|
227
|
+
}
|
|
203
228
|
/**
|
|
204
229
|
* Give the profile its own installed tree, built from the distribution's declarations.
|
|
205
230
|
*
|
|
@@ -221,7 +246,12 @@ export function readDistributionProfile(distributionDirectory) {
|
|
|
221
246
|
export function installProfilePackages(paths, consumerDirectory) {
|
|
222
247
|
const consumerModules = join(consumerDirectory, 'node_modules');
|
|
223
248
|
if (!existsSync(consumerModules)) {
|
|
224
|
-
|
|
249
|
+
// `npx @sparkelf/dsh-plus` runs from a temporary package tree that has no launcher
|
|
250
|
+
// beside it. The profile installs its own tree, so there is nothing to link from
|
|
251
|
+
// the caller; only the profile's packages matter.
|
|
252
|
+
installWithRegistryFallback(paths);
|
|
253
|
+
alignReplacedPackageNames(join(paths.profileDirectory, 'node_modules'));
|
|
254
|
+
return;
|
|
225
255
|
}
|
|
226
256
|
const profileModules = join(paths.profileDirectory, 'node_modules');
|
|
227
257
|
if (existsSync(profileModules)) {
|
|
@@ -246,10 +276,18 @@ export function installProfilePackages(paths, consumerDirectory) {
|
|
|
246
276
|
function installWithRegistryFallback(paths) {
|
|
247
277
|
const registries = registryOrder();
|
|
248
278
|
for (const [index, registry] of registries.entries()) {
|
|
279
|
+
// A lockfile written against the previous registry keeps its resolved URLs, so the
|
|
280
|
+
// second attempt would fetch from the registry that just failed. Drop it and resolve
|
|
281
|
+
// against the registry being tried.
|
|
282
|
+
if (index > 0)
|
|
283
|
+
rmSync(join(paths.profileDirectory, 'pnpm-lock.yaml'), { force: true });
|
|
249
284
|
const result = spawnSync('pnpm', ['install', '--no-frozen-lockfile', '--registry', registry], {
|
|
250
285
|
cwd: paths.profileDirectory,
|
|
251
286
|
stdio: 'inherit',
|
|
252
287
|
shell: process.platform === 'win32',
|
|
288
|
+
// A registry that accepts the connection but never answers would otherwise hold the
|
|
289
|
+
// first start for pnpm's full retry budget before the fallback got a chance.
|
|
290
|
+
timeout: 5 * 60_000,
|
|
253
291
|
});
|
|
254
292
|
if (result.error !== undefined)
|
|
255
293
|
throw result.error;
|
|
@@ -312,8 +350,12 @@ const MAINLAND_REGISTRY = 'https://registry.npmmirror.com';
|
|
|
312
350
|
*/
|
|
313
351
|
export function registryOrder() {
|
|
314
352
|
const configured = process.env.DSH_PLUS_INSTALL_REGISTRY;
|
|
315
|
-
if (configured !== undefined && configured !== '')
|
|
316
|
-
|
|
353
|
+
if (configured !== undefined && configured !== '') {
|
|
354
|
+
// The fallback must be the other registry, not the configured one again: an override
|
|
355
|
+
// set to the mirror used to fall back to the same mirror and fail twice.
|
|
356
|
+
const fallback = configured === MAINLAND_REGISTRY ? OFFICIAL_REGISTRY : MAINLAND_REGISTRY;
|
|
357
|
+
return [configured, fallback];
|
|
358
|
+
}
|
|
317
359
|
// Intl reports the system locale on every platform. The POSIX variables are empty on
|
|
318
360
|
// Windows, so reading them alone classified every Windows console as non-mainland and
|
|
319
361
|
// reached the origin first — measured in a consumer's log, which showed npmjs failing
|
|
@@ -430,12 +472,113 @@ export function resolvePaths(anchor, env = process.env) {
|
|
|
430
472
|
*
|
|
431
473
|
* @param paths - resolved standalone paths.
|
|
432
474
|
* @param consumerDirectory - directory whose `node_modules` holds the installed packages.
|
|
475
|
+
* @param edition - edition id whose package list to install, or undefined for the default.
|
|
433
476
|
* @returns whether this call created the manifest.
|
|
434
477
|
*/
|
|
435
|
-
export function ensureProfile(paths, consumerDirectory) {
|
|
478
|
+
export function ensureProfile(paths, consumerDirectory, edition) {
|
|
436
479
|
const manifestPath = join(paths.profileDirectory, 'package.json');
|
|
437
480
|
const distribution = readDistributionProfile(paths.distributionDirectory);
|
|
481
|
+
const composition = edition === undefined ? undefined : distribution.editions[edition];
|
|
482
|
+
if (edition !== undefined && composition === undefined) {
|
|
483
|
+
const offered = Object.keys(distribution.editions);
|
|
484
|
+
throw new Error('unknown edition "' + edition + '"; this release offers '
|
|
485
|
+
+ (offered.length === 0 ? '(none)' : offered.join(', ')));
|
|
486
|
+
}
|
|
487
|
+
// An edition lists only third-party, fork, and self-developed plugins. The official
|
|
488
|
+
// runtime is the CLI's responsibility and must not be duplicated in that list.
|
|
489
|
+
for (const name of Object.keys(composition?.packages ?? {})) {
|
|
490
|
+
if (name.startsWith('@deepseek-ai/')) {
|
|
491
|
+
throw new Error('edition "' + String(edition) + '" lists the official package ' + name
|
|
492
|
+
+ '; editions contain only third-party or self-developed plugins');
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
const runtime = distribution.dshRange.replace(/^>=?/u, '');
|
|
496
|
+
const baseBundles = composition === undefined
|
|
497
|
+
? distribution.bundles
|
|
498
|
+
: distribution.bundles.filter(bundle => bundle.startsWith('@deepseek-ai/') || bundle === '@sparkelf/dsh-plus');
|
|
499
|
+
const basePackages = composition === undefined
|
|
500
|
+
? distribution.dependencies
|
|
501
|
+
: {
|
|
502
|
+
'@deepseek-ai/dsh': distribution.dshRange,
|
|
503
|
+
'@sparkelf/dsh-plus': distribution.version,
|
|
504
|
+
// `dsh-app-boot` declares its launcher as peer dependencies, and pnpm does not
|
|
505
|
+
// install a profile's transitive peers. Without these the boot loader resolves
|
|
506
|
+
// `@deepseek-ai/cordis-plugin-group` and fails before any plugin mounts.
|
|
507
|
+
'@deepseek-ai/cordis': '^4.0.6-alpha.1',
|
|
508
|
+
'@deepseek-ai/cordis-plugin-group': '^1.0.6-alpha.1',
|
|
509
|
+
'@deepseek-ai/cordis-plugin-include': '^1.0.11-alpha.1',
|
|
510
|
+
'@deepseek-ai/cordis-plugin-loader': '^1.0.7-alpha.1',
|
|
511
|
+
'@deepseek-ai/dsh-home-paths': runtime,
|
|
512
|
+
'@deepseek-ai/dsh-launch-environment': runtime,
|
|
513
|
+
// `dsh-session` / `dsh-tools` declare the rest of the runtime as peers; pnpm leaves
|
|
514
|
+
// them out of a profile install for the same reason as the launcher peers above.
|
|
515
|
+
'@deepseek-ai/dsh-agent': runtime,
|
|
516
|
+
'@deepseek-ai/dsh-llm': runtime,
|
|
517
|
+
'@deepseek-ai/dsh-scope': runtime,
|
|
518
|
+
'@deepseek-ai/dsh-session': runtime,
|
|
519
|
+
'@deepseek-ai/dsh-system-prompt': runtime,
|
|
520
|
+
'@deepseek-ai/dsh-user-approval': runtime,
|
|
521
|
+
// Capability packages that the official plugins import but declare only as peers.
|
|
522
|
+
// A profile install leaves them out and boot then fails inside an otherwise
|
|
523
|
+
// healthy plugin's import with ERR_MODULE_NOT_FOUND.
|
|
524
|
+
'@deepseek-ai/dsh-attachment': runtime,
|
|
525
|
+
'@deepseek-ai/dsh-compaction': runtime,
|
|
526
|
+
'@deepseek-ai/dsh-fs': runtime,
|
|
527
|
+
'@deepseek-ai/dsh-shell': runtime,
|
|
528
|
+
'@deepseek-ai/dsh-spill': runtime,
|
|
529
|
+
'@deepseek-ai/dsh-sandbox': runtime,
|
|
530
|
+
'@deepseek-ai/dsh-ptc-runtime': runtime,
|
|
531
|
+
'@deepseek-ai/dsh-llm-deepseek': runtime,
|
|
532
|
+
'@deepseek-ai/dsh-deepseek-account': runtime,
|
|
533
|
+
'@deepseek-ai/dsh-anonymous-user-id': runtime,
|
|
534
|
+
'@deepseek-ai/dsh-bash-local': runtime,
|
|
535
|
+
'@deepseek-ai/dsh-client-store': runtime,
|
|
536
|
+
'@deepseek-ai/dsh-jobs': runtime,
|
|
537
|
+
'@deepseek-ai/dsh-sdk-protocol': runtime,
|
|
538
|
+
'@deepseek-ai/dsh-session-persistence': runtime,
|
|
539
|
+
'@deepseek-ai/dsh-session-telemetry': runtime,
|
|
540
|
+
'@deepseek-ai/dsh-session-title-llm': runtime,
|
|
541
|
+
'@deepseek-ai/dsh-util-workspace-path': runtime,
|
|
542
|
+
'@deepseek-ai/dsh-workflow': runtime,
|
|
543
|
+
...Object.fromEntries(baseBundles
|
|
544
|
+
.filter(bundle => bundle.startsWith('@deepseek-ai/dsh-'))
|
|
545
|
+
.map(bundle => [bundle, runtime])),
|
|
546
|
+
};
|
|
547
|
+
const compositionPackages = composition === undefined
|
|
548
|
+
? basePackages
|
|
549
|
+
: { ...basePackages, ...composition.packages };
|
|
550
|
+
// Official cordis rows name the historical unscoped packages. The fork ships under
|
|
551
|
+
// `@sparkelf/*`, so the row would fail to resolve unless the profile also carries the
|
|
552
|
+
// historical name as an alias to the same version.
|
|
553
|
+
const rowAliases = {
|
|
554
|
+
'dsh-better-sidebar': '@sparkelf/dsh-better-sidebar',
|
|
555
|
+
'dsh-sql-workbench': '@sparkelf/dsh-sql-workbench',
|
|
556
|
+
};
|
|
557
|
+
for (const [alias, target] of Object.entries(rowAliases)) {
|
|
558
|
+
const spec = compositionPackages[target];
|
|
559
|
+
if (spec !== undefined && compositionPackages[alias] === undefined) {
|
|
560
|
+
compositionPackages[alias] = 'npm:' + target + '@' + spec;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
const compositionBundles = composition === undefined
|
|
564
|
+
? baseBundles
|
|
565
|
+
: [...baseBundles, ...composition.bundles];
|
|
438
566
|
if (existsSync(manifestPath)) {
|
|
567
|
+
// A selected edition is an explicit install choice, so its packages and bundle order
|
|
568
|
+
// replace the profile's rather than being ignored. Other profile decisions stay.
|
|
569
|
+
if (composition !== undefined) {
|
|
570
|
+
const manifest = requireRecord(JSON.parse(readFileSync(manifestPath, 'utf8')), 'Plus profile manifest');
|
|
571
|
+
manifest.dependencies = { ...manifest.dependencies, ...compositionPackages };
|
|
572
|
+
manifest.dsh = {
|
|
573
|
+
...manifest.dsh,
|
|
574
|
+
profile: {
|
|
575
|
+
...manifest.dsh?.profile,
|
|
576
|
+
bundles: [...compositionBundles],
|
|
577
|
+
...(edition === undefined ? {} : { plusEdition: edition }),
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
581
|
+
}
|
|
439
582
|
// The workspace carries decisions the distribution owns — overrides and the build
|
|
440
583
|
// script allowlist — and a distribution release changes them. Rewriting on every
|
|
441
584
|
// start is what lets an upgraded installation receive the new values; a profile
|
|
@@ -449,18 +592,12 @@ export function ensureProfile(paths, consumerDirectory) {
|
|
|
449
592
|
name: 'dsh-profile-' + STANDALONE_PROFILE,
|
|
450
593
|
private: true,
|
|
451
594
|
type: 'module',
|
|
452
|
-
dependencies:
|
|
453
|
-
'@sparkelf/dsh-plus': distribution.version,
|
|
454
|
-
// The launcher's own tree supplies every service package the bundles mount. A
|
|
455
|
-
// profile that lists only the distribution's plugins installs a partial tree and
|
|
456
|
-
// fails at load with a module the launcher would have carried.
|
|
457
|
-
'@deepseek-ai/dsh': distribution.dshRange,
|
|
458
|
-
...distribution.dependencies,
|
|
459
|
-
},
|
|
595
|
+
dependencies: compositionPackages,
|
|
460
596
|
dsh: {
|
|
461
597
|
profile: {
|
|
462
|
-
bundles:
|
|
598
|
+
bundles: [...compositionBundles],
|
|
463
599
|
patchReload: 'live',
|
|
600
|
+
...(edition === undefined ? {} : { plusEdition: edition }),
|
|
464
601
|
},
|
|
465
602
|
},
|
|
466
603
|
};
|
|
@@ -505,7 +642,7 @@ function writeProfileOverrides(profileDirectory, overrides, allowBuilds, omitted
|
|
|
505
642
|
if (document.get('nodeLinker') === undefined)
|
|
506
643
|
document.set('nodeLinker', 'hoisted');
|
|
507
644
|
if (document.get('autoInstallPeers') === undefined)
|
|
508
|
-
document.set('autoInstallPeers',
|
|
645
|
+
document.set('autoInstallPeers', true);
|
|
509
646
|
writeFileSync(workspacePath, String(document));
|
|
510
647
|
}
|
|
511
648
|
/** Run git in one directory, returning undefined instead of throwing when asked to. */
|
|
@@ -536,34 +673,20 @@ function git(root, args, acceptFailure = false) {
|
|
|
536
673
|
*/
|
|
537
674
|
export function applyProfileNpmPatches(distributionDirectory, profileDirectory) {
|
|
538
675
|
const manifest = requireRecord(JSON.parse(readFileSync(join(distributionDirectory, 'package.json'), 'utf8')), 'Plus distribution manifest');
|
|
539
|
-
const
|
|
540
|
-
const names = plus.patchPackages;
|
|
541
|
-
if (!Array.isArray(names))
|
|
542
|
-
throw new Error('dshPlus.patchPackages must be an array');
|
|
676
|
+
const patchSet = readPatchSet(distributionDirectory, manifest);
|
|
543
677
|
const applied = [];
|
|
544
|
-
for (const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
// official checkout, which a standalone installation does not have.
|
|
557
|
-
if (target.kind !== 'npm')
|
|
558
|
-
continue;
|
|
559
|
-
const targetName = String(target.name);
|
|
560
|
-
const patched = resolveInstalledPackage(profileDirectory, targetName);
|
|
561
|
-
const file = resolve(patchPackage.directory, String(variant.file));
|
|
562
|
-
if (git(patched.directory, ['apply', '--reverse', '--check', file], true) !== undefined)
|
|
563
|
-
continue;
|
|
564
|
-
git(patched.directory, ['apply', file]);
|
|
565
|
-
applied.push(value + ' -> ' + targetName);
|
|
566
|
-
}
|
|
678
|
+
for (const entry of [...patchSet.entries, ...patchSet.thirdPartyPatches]) {
|
|
679
|
+
// Only npm targets reach an installed package; a source target needs the
|
|
680
|
+
// official checkout, which a standalone installation does not have.
|
|
681
|
+
if (entry.target.kind !== 'npm')
|
|
682
|
+
continue;
|
|
683
|
+
const targetName = entry.target.name;
|
|
684
|
+
const patched = resolveInstalledPackage(profileDirectory, targetName);
|
|
685
|
+
const file = resolve(distributionDirectory, entry.file);
|
|
686
|
+
if (git(patched.directory, ['apply', '--reverse', '--check', file], true) !== undefined)
|
|
687
|
+
continue;
|
|
688
|
+
git(patched.directory, ['apply', file]);
|
|
689
|
+
applied.push(entry.id + ' -> ' + targetName);
|
|
567
690
|
}
|
|
568
691
|
return applied;
|
|
569
692
|
}
|
|
@@ -575,7 +698,22 @@ function resolveInstalledPackage(from, packageName) {
|
|
|
575
698
|
manifestPath = requireFrom.resolve(packageName + '/package.json');
|
|
576
699
|
}
|
|
577
700
|
catch {
|
|
578
|
-
|
|
701
|
+
// Some packages do not export `./package.json`; resolve the entry module instead and walk
|
|
702
|
+
// up to the directory that holds the manifest.
|
|
703
|
+
let directory;
|
|
704
|
+
try {
|
|
705
|
+
directory = dirname(requireFrom.resolve(packageName));
|
|
706
|
+
}
|
|
707
|
+
catch {
|
|
708
|
+
throw new Error(packageName + ' is not installed under ' + from);
|
|
709
|
+
}
|
|
710
|
+
while (!existsSync(resolve(directory, 'package.json'))) {
|
|
711
|
+
const parent = dirname(directory);
|
|
712
|
+
if (parent === directory)
|
|
713
|
+
throw new Error(packageName + ' has no resolvable package manifest under ' + from);
|
|
714
|
+
directory = parent;
|
|
715
|
+
}
|
|
716
|
+
manifestPath = resolve(directory, 'package.json');
|
|
579
717
|
}
|
|
580
718
|
const manifest = requireRecord(JSON.parse(readFileSync(manifestPath, 'utf8')), packageName + ' manifest');
|
|
581
719
|
return { name: packageName, version: String(manifest.version), directory: dirname(manifestPath), manifest };
|