Files
deepseek-harness/packages/boot/app-boot/tests/package-meta.spec.ts
T
Turtle 8f0dd55c2c feat(plugins): render icons from package manifests (#4697)
* feat(plugins): render icons from package manifests

* fix(plugins): include declared icons in canonical package payloads
2026-09-20 17:00:17 +08:00

436 lines
22 KiB
TypeScript

/** Plugin locale resources resolve independently of entry execution and package display fields. */
import fs, { mkdirSync, mkdtempSync, readdirSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs'
import { syncBuiltinESMExports } from 'node:module'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { ModuleLoader } from '@deepseek-ai/cordis-plugin-loader'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { readPluginMeta, resolvePluginResource } from '../src/package-meta.ts'
let root: string
let dir: string
let parentURL: string
function file(path: string, text: string): void {
mkdirSync(dirname(path), { recursive: true })
writeFileSync(path, text)
}
function manifest(exports: object, extra: object = {}): void {
file(join(dir, 'package.json'), JSON.stringify({ name: 'localized', type: 'module', exports, ...extra }))
}
function dictionary(language: string, contents: unknown, directory = join(dir, 'locale')): void {
file(join(directory, `${language}.json`), JSON.stringify(contents))
}
beforeEach(() => {
root = mkdtempSync(join(tmpdir(), 'dsh-plugin-meta-'))
dir = join(root, 'node_modules', 'localized')
parentURL = pathToFileURL(join(root, 'entry.mjs')).href
manifest({ '.': './index.js', './locale/*.json': './locale/*.json' }, { description: 'Not local display text.' })
file(join(dir, 'index.js'), 'throw new Error("metadata must not execute the plugin")\n')
})
afterEach(() => {
vi.restoreAllMocks()
rmSync(root, { recursive: true, force: true })
})
describe('plugin locale display metadata', () => {
it.each([
'C:\\plugins\\search.js',
'C:/plugins/search.js',
'\\\\server\\share\\plugins\\search.js',
'//server/share/plugins/search.js',
'.\\plugins\\search.js',
'./plugins/search.js',
'../plugins/search.js',
'/plugins/search.js',
'file:///plugins/search.js',
'file:///C:/plugins/search.js',
'file://server/share/plugins/search.js',
])('does not resolve metadata resources for plugin file address %s', (specifier) => {
const original = ModuleLoader.fromInternal
const resolver = vi.spyOn(ModuleLoader, 'fromInternal')
try {
expect(readPluginMeta(specifier, parentURL)).toBeUndefined()
expect(resolver).not.toHaveBeenCalled()
} finally {
resolver.mockRestore()
}
expect(ModuleLoader.fromInternal).toBe(original)
})
it('omits absent resources and absent meta fields without reading npm descriptions', () => {
expect(readPluginMeta('localized', parentURL)).toBeUndefined()
expect(readPluginMeta('absent', parentURL)).toBeUndefined()
expect(readPluginMeta('node:fs', parentURL)).toBeUndefined()
expect(readPluginMeta('fs', parentURL)).toBeUndefined()
expect(readPluginMeta('cordis:group', parentURL)).toBeUndefined()
dictionary('en', { other: { title: 'Not metadata' } })
expect(readPluginMeta('localized', parentURL)).toBeUndefined()
dictionary('en', { meta: {} })
expect(readPluginMeta('localized', parentURL)).toBeUndefined()
})
it('reads direct fields, retains per-field translations, and ignores other locale content', () => {
dictionary('en', { meta: { title: 'Team', description: 'Work together', ignored: false }, other: { nested: [1] } })
dictionary('zh', { meta: { title: '团队' } })
dictionary('pt-BR', { meta: { description: 'Trabalhar juntos' } })
file(join(dir, 'locale', 'ignored.txt'), 'not JSON')
expect(readPluginMeta('localized', parentURL)).toEqual({
title: { en: 'Team', zh: '团队' },
description: { en: 'Work together', 'pt-br': 'Trabalhar juntos' },
})
})
it('treats percent markers as literal display text', () => {
dictionary('en', { meta: { title: '%title%', description: 'Plugin %name%' } })
expect(readPluginMeta('localized', parentURL)).toEqual({
title: { en: '%title%' }, description: { en: 'Plugin %name%' },
})
})
it('accepts description-only metadata and supplies the module name when a translated title has no English value', () => {
dictionary('en', { meta: { description: 'About it' } })
expect(readPluginMeta('localized', parentURL)).toEqual({ description: { en: 'About it' } })
dictionary('zh', { meta: { title: '标题' } })
expect(readPluginMeta('localized', parentURL)).toEqual({
title: { en: 'localized', zh: '标题' }, description: { en: 'About it' },
})
})
it('falls back to package fields when locale resources are absent', () => {
manifest({ '.': './index.js', './package.json': './package.json' }, { description: 'Package introduction' })
expect(readPluginMeta('localized', parentURL)).toEqual({ title: 'localized', description: 'Package introduction' })
})
it('falls back per field without replacing available locale translations', () => {
manifest({ './locale/*.json': './locale/*.json', './package.json': './package.json' }, { description: 'Package introduction' })
dictionary('en', { meta: { title: 'English title' } })
dictionary('zh', { meta: { description: '中文介绍' } })
expect(readPluginMeta('localized', parentURL)).toEqual({
title: { en: 'English title' }, description: { en: 'Package introduction', zh: '中文介绍' },
})
dictionary('en', { meta: { description: 'English introduction' } })
dictionary('zh', { meta: { title: '中文标题' } })
expect(readPluginMeta('localized', parentURL)).toEqual({
title: { en: 'localized', zh: '中文标题' }, description: { en: 'English introduction' },
})
dictionary('en', {})
dictionary('zh', {})
expect(readPluginMeta('localized', parentURL)).toEqual({ title: 'localized', description: 'Package introduction' })
})
it('retains non-English descriptions with an empty final English fallback', () => {
dictionary('en', { meta: { title: 'Plugin' } })
dictionary('zh', { meta: { description: '中文介绍' } })
expect(readPluginMeta('localized', parentURL)).toEqual({
title: { en: 'Plugin' }, description: { en: '', zh: '中文介绍' },
})
})
it('uses only the package resource exported for the requested plugin address', () => {
manifest({
'./package.json': './package.json',
'./search/package.json': './search-manifest.json',
}, { description: 'Whole package' })
file(join(dir, 'search-manifest.json'), JSON.stringify({ name: 'Search plugin', description: 'Search introduction' }))
expect(readPluginMeta('localized/search', parentURL)).toEqual({ title: 'Search plugin', description: 'Search introduction' })
expect(readPluginMeta('localized/review', parentURL)).toBeUndefined()
})
it.each([{}, { name: '', description: ' ' }, { name: false, description: null }])('ignores unavailable package text: %j', (fields) => {
manifest({ './feature/package.json': './feature.json' })
file(join(dir, 'feature.json'), JSON.stringify(fields))
expect(readPluginMeta('localized/feature', parentURL)).toBeUndefined()
})
it('reports malformed fallback files', () => {
manifest({ './feature/package.json': './feature.json' })
file(join(dir, 'feature.json'), '{')
expect(readPluginMeta('localized/feature', parentURL)?.error).toContain('feature.json')
})
it('does not hide invalid locale fields behind package text', () => {
manifest({ './locale/*.json': './locale/*.json', './package.json': './package.json' }, { description: 'Package description' })
dictionary('en', { meta: { title: false } })
expect(readPluginMeta('localized', parentURL)?.error).toContain('meta.title must be a non-empty string')
})
it('reads the manifest icon even when both English fields are supplied', () => {
manifest({ './locale/*.json': './locale/*.json', './package.json': './package.json' }, { icon: './art/team.svg' })
file(join(dir, 'art', 'team.svg'), '<svg xmlns="http://www.w3.org/2000/svg"/>')
dictionary('en', { meta: { title: 'Plugin', description: 'Locale introduction' } })
expect(readPluginMeta('localized', parentURL)).toEqual({
title: { en: 'Plugin' }, description: { en: 'Locale introduction' },
icon: `data:image/svg+xml;base64,${Buffer.from('<svg xmlns="http://www.w3.org/2000/svg"/>').toString('base64')}`,
})
})
it.each([
['svg', 'image/svg+xml'], ['png', 'image/png'], ['jpg', 'image/jpeg'],
['jpeg', 'image/jpeg'], ['webp', 'image/webp'], ['SVG', 'image/svg+xml'],
])('encodes a package-local %s icon without executing the plugin', (extension, mediaType) => {
manifest({ '.': './index.js', './package.json': './package.json' }, { icon: `./icon.${extension}` })
const bytes = Buffer.from([0, 1, 127, 128, 255])
writeFileSync(join(dir, `icon.${extension}`), bytes)
expect(readPluginMeta('localized', parentURL)).toEqual({
title: 'localized', icon: `data:${mediaType};base64,${bytes.toString('base64')}`,
})
})
it('reads an icon-only subexport relative to its own manifest without inheriting the package icon', () => {
manifest({ './package.json': './package.json', './search/package.json': './search/manifest.json' }, { icon: './root.png' })
file(join(dir, 'root.png'), 'root')
file(join(dir, 'search', 'manifest.json'), JSON.stringify({ icon: './icon.webp' }))
file(join(dir, 'search', 'icon.webp'), 'search')
expect(readPluginMeta('localized/search', parentURL)).toEqual({
icon: `data:image/webp;base64,${Buffer.from('search').toString('base64')}`,
})
expect(readPluginMeta('localized/review', parentURL)).toBeUndefined()
})
it.each([null, false, 1, {}, [], '', ' '])('retains localized text while rejecting malformed icon %j', (icon) => {
manifest({ './locale/*.json': './locale/*.json', './package.json': './package.json' }, { icon })
dictionary('en', { meta: { title: 'Plugin', description: 'Introduction' } })
dictionary('zh', { meta: { title: '插件' } })
const { error, ...meta } = readPluginMeta('localized', parentURL)!
expect(meta).toEqual({ title: { en: 'Plugin', zh: '插件' }, description: { en: 'Introduction' } })
expect(error).toContain('icon must be a non-empty string')
})
it.each(['/tmp/icon.svg', 'C:\\icon.png', 'C:icon.png', '\\\\server\\share\\icon.png', 'https://example.test/icon.svg', 'data:image/svg+xml,<svg/>'])('rejects non-relative icon %s', (icon) => {
manifest({ './package.json': './package.json' }, { icon, description: 'Introduction' })
const { error, ...meta } = readPluginMeta('localized', parentURL)!
expect(meta).toEqual({ title: 'localized', description: 'Introduction' })
expect(error).toContain('icon must be a relative file path')
})
it.each(['icon.gif', 'icon.html', 'icon', 'icon.svg?query'])('rejects unsupported icon file %s', (icon) => {
manifest({ './package.json': './package.json' }, { icon })
expect(readPluginMeta('localized', parentURL)?.error).toContain('icon must be SVG, PNG, JPEG, or WebP')
})
it('reports missing files, directories, and paths outside the manifest directory', () => {
manifest({ './package.json': './package.json' }, { icon: './missing.svg' })
expect(readPluginMeta('localized', parentURL)?.error).toContain('missing.svg')
mkdirSync(join(dir, 'missing.svg'))
expect(readPluginMeta('localized', parentURL)?.error).toContain('icon must be a regular file')
manifest({ './package.json': './package.json' }, { icon: '../localized-other/icon.svg' })
file(join(root, 'node_modules', 'localized-other', 'icon.svg'), 'outside')
expect(readPluginMeta('localized', parentURL)?.error).toContain('icon must remain inside its manifest directory')
})
it('accepts an icon at the byte limit and rejects one byte more', () => {
manifest({ './package.json': './package.json' }, { icon: './icon.png' })
const bytes = Buffer.alloc(256 * 1024)
writeFileSync(join(dir, 'icon.png'), bytes)
expect(readPluginMeta('localized', parentURL)?.icon).toBe(`data:image/png;base64,${bytes.toString('base64')}`)
writeFileSync(join(dir, 'icon.png'), Buffer.alloc(bytes.length + 1))
expect(readPluginMeta('localized', parentURL)?.error).toContain('icon exceeds 256 KiB')
})
it('rejects an icon that grows beyond the limit after its size check', () => {
manifest({ './package.json': './package.json' }, { icon: './icon.png' })
const icon = join(dir, 'icon.png')
file(icon, 'small')
const original = fs.statSync
const stat = vi.spyOn(fs, 'statSync')
try {
stat.mockImplementation(new Proxy(original, {
apply(target, receiver: unknown, args: unknown[]): unknown {
const result: unknown = Reflect.apply(target, receiver, args)
if (String(args[0]).endsWith(join('localized', 'icon.png'))) writeFileSync(icon, Buffer.alloc(256 * 1024 + 1))
return result
},
}))
syncBuiltinESMExports()
expect(readPluginMeta('localized', parentURL)?.error).toContain('icon exceeds 256 KiB')
} finally {
stat.mockRestore()
syncBuiltinESMExports()
}
})
it('rejects icons reached through an external directory link and accepts internal links', () => {
manifest({ './package.json': './package.json' }, { icon: './art/icon.svg' })
const outside = join(root, 'outside')
file(join(outside, 'icon.svg'), 'outside')
const link = join(dir, 'art')
symlinkSync(outside, link, 'junction')
try {
expect(readPluginMeta('localized', parentURL)?.error).toContain('icon must remain inside its manifest directory')
} finally {
unlinkSync(link)
}
const inside = join(dir, 'images')
file(join(inside, 'icon.svg'), 'inside')
symlinkSync(inside, link, 'junction')
try {
expect(readPluginMeta('localized', parentURL)?.icon).toBe(`data:image/svg+xml;base64,${Buffer.from('inside').toString('base64')}`)
} finally {
unlinkSync(link)
}
})
it('keeps separate plugin exports independent even when their JavaScript entries share a directory', () => {
manifest({
'./search': './lib/search.js', './review': './lib/review.js',
'./search/locale/*.json': './resources/search/*.json',
'./review/locale/*.json': './resources/review/*.json',
'./locale/*.json': './locale/*.json',
})
file(join(dir, 'lib', 'search.js'), 'throw new Error("must not execute search")\n')
file(join(dir, 'lib', 'review.js'), 'throw new Error("must not execute review")\n')
dictionary('en', { meta: { title: 'Whole package' } })
dictionary('en', { meta: { title: 'Search' } }, join(dir, 'resources', 'search'))
dictionary('zh', { meta: { title: '搜索' } }, join(dir, 'resources', 'search'))
dictionary('en', { meta: { title: 'Review' } }, join(dir, 'resources', 'review'))
expect(readPluginMeta('localized/search', parentURL)).toEqual({ title: { en: 'Search', zh: '搜索' } })
expect(readPluginMeta('localized/review', parentURL)).toEqual({ title: { en: 'Review' } })
expect(readPluginMeta('localized/private', parentURL)).toBeUndefined()
})
it('reads scoped package roots and subexports at their complete addresses', () => {
const scoped = join(root, 'node_modules', '@scope', 'localized')
file(join(scoped, 'package.json'), JSON.stringify({
name: '@scope/localized',
exports: { './locale/*.json': './locale/*.json', './search/locale/*.json': './search-locale/*.json' },
}))
dictionary('en', { meta: { title: 'Scoped package' } }, join(scoped, 'locale'))
dictionary('en', { meta: { title: 'Scoped search' } }, join(scoped, 'search-locale'))
expect(readPluginMeta('@scope/localized', parentURL)).toEqual({ title: { en: 'Scoped package' } })
expect(readPluginMeta('@scope/localized/search', parentURL)).toEqual({ title: { en: 'Scoped search' } })
})
it('uses ESM import conditions for locale exports without loading JSON modules', () => {
manifest({ './locale/*.json': { import: './esm/*.json', require: './cjs/*.json' } })
dictionary('en', { meta: { title: 'ESM' } }, join(dir, 'esm'))
dictionary('en', { meta: { title: 'CJS' } }, join(dir, 'cjs'))
expect(readPluginMeta('localized', parentURL)).toEqual({ title: { en: 'ESM' } })
})
it('does not read unexported language files directly from the resolved English directory', () => {
manifest({ './locale/en.json': './locale/en.json' })
dictionary('en', { meta: { title: 'English' } })
dictionary('zh', { meta: { title: '中文' } })
expect(readPluginMeta('localized', parentURL)?.error).toContain('./locale/zh.json')
})
it('reports language resources mapped outside their English directory', () => {
manifest({ './locale/*.json': './locale/*.json', './locale/zh.json': './separate/zh.json' })
dictionary('en', { meta: { title: 'English' } })
dictionary('zh', { meta: { title: 'Local Chinese' } })
dictionary('zh', { meta: { title: 'Mapped Chinese' } }, join(dir, 'separate'))
expect(readPluginMeta('localized', parentURL)?.error).toContain('must share the English locale directory')
})
it('resolves same-named packages independently under each importing parent', () => {
dictionary('en', { meta: { title: 'First' } })
const second = join(root, 'second', 'node_modules', 'localized')
file(join(second, 'package.json'), JSON.stringify({ name: 'localized', exports: { './locale/*.json': './locale/*.json' } }))
dictionary('en', { meta: { title: 'Second' } }, join(second, 'locale'))
const secondParent = pathToFileURL(join(root, 'second', 'entry.mjs')).href
expect(readPluginMeta('localized', parentURL)).toEqual({ title: { en: 'First' } })
expect(readPluginMeta('localized', secondParent)).toEqual({ title: { en: 'Second' } })
})
it.each([null, [], false, 1, 'text'])('reports malformed meta: %j', (meta) => {
dictionary('en', { meta })
expect(readPluginMeta('localized', parentURL)?.error).toContain('meta must be an object')
})
it.each([null, false, 1, {}, '', ' '])('reports a malformed display field: %j', (title) => {
dictionary('en', { meta: { title } })
const localeFile = resolvePluginResource('localized/locale/en.json', parentURL)
expect(readPluginMeta('localized', parentURL)?.error).toContain(localeFile + ': meta.title must be a non-empty string')
})
it.each([[], null, 'text'])('rejects a non-object locale document: %j', (contents) => {
dictionary('en', contents)
expect(readPluginMeta('localized', parentURL)?.error).toContain('en.json must be an object')
})
it('reports malformed JSON and invalid language filenames', () => {
dictionary('en', { meta: { title: 'Title' } })
file(join(dir, 'locale', 'zh.json'), '{')
expect(readPluginMeta('localized', parentURL)?.error).toContain('zh.json')
rmSync(join(dir, 'locale', 'zh.json'))
dictionary('not_a_language', { meta: { title: 'Title' } })
expect(readPluginMeta('localized', parentURL)?.error).toContain('not_a_language.json must use a language id')
})
it('reports invalid export targets instead of treating them as absent metadata', () => {
manifest({ './locale/*.json': '../outside/*.json' })
expect(readPluginMeta('localized', parentURL)?.error).toContain('Invalid "exports" target')
})
it('reports an unavailable Node resolver without changing plugin state', () => {
vi.spyOn(ModuleLoader, 'fromInternal').mockReturnValue(undefined)
expect(readPluginMeta('localized', parentURL)?.error).toContain('requires the Node module resolver')
})
it.each(['v1', 'v2'] as const)('uses the %s Node resolver argument order', (version) => {
const loader = ModuleLoader.fromInternal()!
dictionary('en', { meta: { title: 'Resolved resource', description: 'Resolved introduction' } })
const resolveSync = vi.fn(() => ({ url: pathToFileURL(join(dir, 'locale', 'en.json')).href }))
const adapted = new Proxy(loader, {
get(target, property) {
if (property === 'version') return version
if (property === 'resolveSync') return resolveSync
const value: unknown = Reflect.get(target, property)
return value
},
})
vi.spyOn(ModuleLoader, 'fromInternal').mockReturnValue(adapted)
expect(readPluginMeta('localized', parentURL)).toEqual({
title: { en: 'Resolved resource' }, description: { en: 'Resolved introduction' },
})
const specifier = 'localized/locale/en.json'
expect(resolveSync.mock.calls).toEqual(version === 'v1'
? [[specifier, parentURL, {}], [specifier, parentURL, {}], ['localized/package.json', parentURL, {}]]
: [[parentURL, { specifier, attributes: {} }], [parentURL, { specifier, attributes: {} }],
[parentURL, { specifier: 'localized/package.json', attributes: {} }]])
})
it('rejects case-equivalent language names in a directory listing', () => {
dictionary('en', { meta: { title: 'Title' } })
const localeDir = dirname(resolvePluginResource('localized/locale/en.json', parentURL))
const original = fs.readdirSync
const english = original(localeDir, { withFileTypes: true })[0]!
const duplicate = original(localeDir, { withFileTypes: true })[0]!
duplicate.name = 'EN.json'
// Case-insensitive filesystems cannot store both names in the same directory.
const listing = vi.spyOn(fs, 'readdirSync')
try {
listing.mockImplementation(new Proxy(original, {
apply(target, receiver: unknown, args: unknown[]): unknown {
if (args[0] === localeDir) return [english, duplicate]
return Reflect.apply(target, receiver, args)
},
}))
syncBuiltinESMExports()
expect(fs.readdirSync(dir, { withFileTypes: true })).toEqual(original(dir, { withFileTypes: true }))
expect(readPluginMeta('localized', parentURL)?.error).toContain('localized/locale/EN.json duplicates locale en')
} finally {
listing.mockRestore()
syncBuiltinESMExports()
}
expect(fs.readdirSync).toBe(original)
expect(readdirSync).toBe(original)
expect(readPluginMeta('localized', parentURL)).toEqual({ title: { en: 'Title' } })
})
it('rejects case-equivalent language files on case-sensitive filesystems', (context) => {
dictionary('en', { meta: { title: 'Title' } })
dictionary('EN', { meta: { title: 'Other' } })
// Case-insensitive filesystems cannot hold both filenames.
if (readdirSync(join(dir, 'locale')).length !== 2) context.skip()
expect(readPluginMeta('localized', parentURL)?.error).toContain('duplicates locale en')
})
})