feat(bundle): accept an ordered patch file list in dsh.bundle.patch (#4722)
* feat(bundle): accept an ordered patch file list in dsh.bundle.patch `dsh.bundle.patch` now accepts `string | string[]`. A list names patch files applied in order as one bundle layer; each file's relative plugin paths still resolve beside that file. `bundlePatchPaths` in dsh-app-boot is the one resolver from a declaration to absolute paths, and every reader uses it: profile loading, plugin-manager reconcile and bundle rows, the client-runtime roster, and the workspace, cordis-config, and product-isolation gates. `ProfileLayer.patchPath` becomes `patchPaths`. * fix(bundle): keep declared patch files package-relative in the workspace gate path.join produced backslash entries on Windows, so the expected files list did not match package.json files. bundlePatchFiles returns the validated declaration as written; bundlePatchPaths joins it.
This commit is contained in:
1 parent
d495b2e5f2
commit
654caa4bbd
25 files changed
+166
-73
No files matched your search
@@ -232,6 +232,15 @@ describe('package payload constraints', () => {
|
||||
'cordis.patch.yml',
|
||||
'lib/types/**/*.d.ts',
|
||||
])
|
||||
expect(expectedDshPackageFiles({
|
||||
name: '@deepseek-ai/dsh-private-profile',
|
||||
dsh: { bundle: { patch: ['./cordis.patch.yml', './layers/web.patch.yml'] } },
|
||||
})).toEqual([
|
||||
'lib/index.js',
|
||||
'cordis.patch.yml',
|
||||
'layers/web.patch.yml',
|
||||
'lib/types/**/*.d.ts',
|
||||
])
|
||||
})
|
||||
|
||||
it.each([
|
||||
|
||||
@@ -13,7 +13,8 @@ import {
|
||||
PRIVATE_EXPERIMENTAL_PACKAGE_DIRECTORIES,
|
||||
} from './experimental-package-policy.ts'
|
||||
import { hasTypertRemoteNavigation, isForbiddenPublicationFile } from './publication-payload.ts'
|
||||
import { OPTIONAL_BUNDLES } from '../packages/boot/app-boot/src/profile.ts'
|
||||
import type { DshBundleManifest } from '../packages/util/package-manifest/src/types.ts'
|
||||
import { OPTIONAL_BUNDLES, bundlePatchFiles } from '../packages/boot/app-boot/src/profile.ts'
|
||||
import { collectProjectReferenceFaceViolations } from './project-reference-faces.ts'
|
||||
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
@@ -98,9 +99,7 @@ export interface PackageManifest {
|
||||
dependencies?: Record<string, string>
|
||||
optionalDependencies?: Record<string, string>
|
||||
dsh?: {
|
||||
bundle?: {
|
||||
patch?: string
|
||||
}
|
||||
bundle?: DshBundleManifest
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,8 +221,8 @@ export function expectedDshPackageFiles(manifest: PackageManifest): readonly str
|
||||
const target = exportDefault(manifest, resource)
|
||||
if (target?.startsWith('./') && target.endsWith('.json')) localeFiles.add(target.slice(2))
|
||||
}
|
||||
const declaredPatch = manifest.dsh?.bundle?.patch
|
||||
const bundleFiles = declaredPatch === undefined ? [] : [declaredPatch.replace(/^\.\//, '')]
|
||||
const bundle = manifest.dsh?.bundle
|
||||
const bundleFiles = bundle === undefined ? [] : bundlePatchFiles(bundle).map(file => file.replace(/^\.\//, ''))
|
||||
const extras = [
|
||||
...bundleFiles,
|
||||
...(manifest.name ? packageFileExtras[manifest.name] ?? [] : []),
|
||||
|
||||
@@ -14,6 +14,8 @@ import { globSync, readFileSync } from 'node:fs'
|
||||
import { dirname, relative, resolve } from 'node:path'
|
||||
import { Script } from 'node:vm'
|
||||
import ts from 'typescript'
|
||||
import type { DshBundleManifest } from '../packages/util/package-manifest/src/types.ts'
|
||||
import { bundlePatchPaths } from '../packages/boot/app-boot/src/profile.ts'
|
||||
import { cordisConfigFiles } from './cordis-config-files.ts'
|
||||
import { isCordisGroupEntry, isJsExpr, loadCordisYaml } from './cordis-yaml.ts'
|
||||
|
||||
@@ -22,7 +24,7 @@ export interface PackageManifest {
|
||||
dependencies?: Record<string, string>
|
||||
devDependencies?: Record<string, string>
|
||||
optionalDependencies?: Record<string, string>
|
||||
dsh?: { bundle?: { patch?: string } }
|
||||
dsh?: { bundle?: DshBundleManifest }
|
||||
}
|
||||
|
||||
export interface PluginReference {
|
||||
@@ -257,10 +259,11 @@ function validateAppResolution(): string[] {
|
||||
for (const manifestPath of bundleManifests) {
|
||||
const bundleDir = manifestPath.replace(/\/package\.json$/, '')
|
||||
const manifest = readManifest(manifestPath)
|
||||
const patch = manifest.dsh?.bundle?.patch
|
||||
if (typeof patch !== 'string') continue
|
||||
const patchFile = relative(root, resolve(root, bundleDir, patch)).replaceAll('\\', '/')
|
||||
const references = pluginReferences.filter(reference => reference.file === patchFile)
|
||||
const bundle = manifest.dsh?.bundle
|
||||
if (bundle === undefined) continue
|
||||
const patchFiles = new Set(bundlePatchPaths(resolve(root, bundleDir), bundle)
|
||||
.map(file => relative(root, file).replaceAll('\\', '/')))
|
||||
const references = pluginReferences.filter(reference => patchFiles.has(reference.file))
|
||||
violations.push(...bundlePluginDependencyErrors(manifestPath, manifest, references))
|
||||
}
|
||||
return violations
|
||||
@@ -362,7 +365,7 @@ function packageTestManifestPath(file: string): string | undefined {
|
||||
*/
|
||||
export function bundleManifestPaths(repoRoot: string = root): string[] {
|
||||
return globSync('packages/*/*/package.json', { cwd: repoRoot })
|
||||
.filter(path => typeof readManifest(path, repoRoot).dsh?.bundle?.patch === 'string')
|
||||
.filter(path => readManifest(path, repoRoot).dsh?.bundle?.patch !== undefined)
|
||||
.map(path => path.replaceAll('\\', '/'))
|
||||
.sort()
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import ts from 'typescript'
|
||||
import { applyEntryPatches, type PatchOptions } from '@deepseek-ai/cordis-plugin-include'
|
||||
import type { EntryOptions } from '@deepseek-ai/cordis-plugin-loader'
|
||||
import { loadOverlayPatches } from '../packages/boot/app-boot/src/index.ts'
|
||||
import { composeEntries } from '../packages/boot/app-boot/src/profile.ts'
|
||||
import { bundlePatchPaths, composeEntries } from '../packages/boot/app-boot/src/profile.ts'
|
||||
import type { DshBundleManifest } from '../packages/util/package-manifest/src/types.ts'
|
||||
import { isCordisGroupEntry, loadCordisYaml } from './cordis-yaml.ts'
|
||||
import {
|
||||
collectRuntimeLocalSourceSpecifiers,
|
||||
@@ -32,7 +33,7 @@ interface Manifest {
|
||||
optionalDependencies?: Record<string, string>
|
||||
peerDependencies?: Record<string, string>
|
||||
devDependencies?: Record<string, string>
|
||||
dsh?: { bundle?: { patch?: string }; configTrees?: Array<{ path: string }> }
|
||||
dsh?: { bundle?: DshBundleManifest; configTrees?: Array<{ path: string }> }
|
||||
}
|
||||
|
||||
interface Package {
|
||||
@@ -238,9 +239,9 @@ export function verifyDefaultProductIsolation(root: string): ProductIsolationRes
|
||||
}
|
||||
const webLayers = selection.webBundles.flatMap((name) => {
|
||||
const pkg = packages.get(name)
|
||||
const patch = pkg?.manifest.dsh?.bundle?.patch
|
||||
if (pkg === undefined || patch === undefined) return []
|
||||
return [loadOverlayPatches('verify-default-product-isolation', resolve(pkg.directory, patch))]
|
||||
const bundle = pkg?.manifest.dsh?.bundle
|
||||
if (pkg === undefined || bundle === undefined) return []
|
||||
return [bundlePatchPaths(pkg.directory, bundle).flatMap(file => loadOverlayPatches('verify-default-product-isolation', file))]
|
||||
})
|
||||
if (webLayers.length !== selection.webBundles.length) {
|
||||
failures.push(`${PROFILE_SOURCE}: default Web bundle layers are incomplete`)
|
||||
@@ -285,7 +286,7 @@ export function verifyDefaultProductIsolation(root: string): ProductIsolationRes
|
||||
dependency(name, range, pkg, `${manifest.name} ${section}`)
|
||||
}
|
||||
}
|
||||
if (manifest.dsh?.bundle?.patch !== undefined) scanConfig(resolve(pkg.directory, manifest.dsh.bundle.patch))
|
||||
for (const file of manifest.dsh?.bundle === undefined ? [] : bundlePatchPaths(pkg.directory, manifest.dsh.bundle)) scanConfig(file)
|
||||
for (const tree of manifest.dsh?.configTrees ?? []) {
|
||||
const treePath = resolve(pkg.directory, tree.path)
|
||||
const files = existsSync(treePath) && statSync(treePath).isDirectory()
|
||||
|
||||
Reference in new issue
Block a user