Skip to content

Commit

Permalink
feat(registry): add manifest typings
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Apr 29, 2024
1 parent 37fd46a commit ccf6cfb
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 60 deletions.
14 changes: 6 additions & 8 deletions packages/registry/src/local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import { readdir, readFile } from 'node:fs/promises'
import { PackageJson, SearchObject, SearchResult } from './types'
import { conclude } from './utils'

interface LocalObject extends Pick<SearchObject, 'shortname' | 'workspace' | 'manifest'> {
package: Pick<PackageJson, 'name' | 'version' | 'peerDependencies' | 'peerDependenciesMeta'>
const LocalKeys = ['name', 'version', 'peerDependencies', 'peerDependenciesMeta'] as const
type LocalKeys = typeof LocalKeys[number]

interface LocalObject extends Pick<SearchObject, 'shortname' | 'ecosystem' | 'workspace' | 'manifest'> {
package: Pick<PackageJson, LocalKeys>
}

export interface LocalScanner extends SearchResult<LocalObject>, LocalScanner.Options {}
Expand Down Expand Up @@ -88,12 +91,7 @@ export class LocalScanner {
workspace,
manifest: conclude(meta),
shortname: meta.name.replace(/(cordis-|^@cordisjs\/)plugin-/, ''),
package: pick(meta, [
'name',
'version',
'peerDependencies',
'peerDependenciesMeta',
]),
package: pick(meta, LocalKeys),
}
this.cache[name] = object
await this.onSuccess?.(object)
Expand Down
31 changes: 21 additions & 10 deletions packages/registry/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface PackageJson extends BasePackage, Partial<Record<DependencyKey,
bin?: string | Dict<string>
scripts?: Dict<string>
exports?: PackageJson.Exports
cordis?: Partial<Manifest>
cordis?: Manifest
keywords: string[]
engines?: Dict<string>
os?: string[]
Expand All @@ -47,25 +47,35 @@ export interface IconSvg {
pathData: string
}

export interface Manifest {
export interface Manifest extends Manifest.Base {
icon?: IconSvg
hidden?: boolean
preview?: boolean
insecure?: boolean
browser?: boolean
category?: string
public?: string[]
exports?: Dict<string>
description: string | Dict<string>
service: Manifest.Service
locales: string[]
exports?: Dict<Manifest.Base | null | undefined>
ecosystem?: Manifest.Ecosystem
}

export namespace Manifest {
export interface Base {
browser?: boolean
description?: string | Dict<string>
service?: Manifest.Service
resources?: Dict
}

export interface Service {
required: string[]
optional: string[]
implements: string[]
required?: string[]
optional?: string[]
implements?: string[]
}

export interface Ecosystem {
inject?: string[]
pattern?: string[]
keywords?: string[]
}
}

Expand Down Expand Up @@ -115,6 +125,7 @@ export interface SearchPackage extends DatedPackage, Pick<RemotePackage, Depende

export interface SearchObject {
shortname: string
ecosystem?: string
package: SearchPackage
searchScore: number
score: Score
Expand Down
87 changes: 47 additions & 40 deletions packages/registry/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,28 @@ import { Manifest, PackageJson } from './types'

interface Ensure<T> {
(value: any): T | undefined
(value: any, fallback: T): T
}

export namespace Ensure {
export const array: Ensure<string[]> = (value: any, fallback?: any) => {
if (!Array.isArray(value)) return fallback
export const array: Ensure<string[]> = (value: any) => {
if (!Array.isArray(value)) return
return value.filter(x => typeof x === 'string')
}

export const dict: Ensure<Dict<string>> = (value: any, fallback?: any) => {
if (typeof value !== 'object' || value === null) return fallback
return Object.entries(value).reduce<Dict<string>>((dict, [key, value]) => {
if (typeof value === 'string') dict[key] = value
export const dict = <T>(value: any, callback?: (value: T) => T): Dict<T> | undefined => {
if (typeof value !== 'object' || value === null) return
return Object.entries(value).reduce((dict, [key, value]: [string, any]) => {
value = callback ? callback(value) : value
if (value !== undefined) dict[key] = value
return dict
}, {})
}

export const object = <T>(value: any, callback?: (value: T) => T): T | undefined => {
if (typeof value !== 'object' || value === null) return
return callback ? callback(value) : value
}

// https://github.com/microsoft/TypeScript/issues/15713#issuecomment-499474386
const primitive = <T, >(type: string): Ensure<T> => (value: any, fallback?: T) => {
if (typeof value !== type) return fallback
Expand All @@ -31,45 +36,47 @@ export namespace Ensure {
export const string = primitive<string>('string')
}

function concludeBase(base?: Manifest.Base | null, description?: string) {
if (typeof base !== 'object') return
if (!base) return null

const result: Manifest.Base = {
browser: Ensure.boolean(base.browser),
description: Ensure.dict(base.description) ?? Ensure.string(description),
service: Ensure.object(base.service, (service) => ({
required: Ensure.array(service.required),
optional: Ensure.array(service.optional),
implements: Ensure.array(service.implements),
})),
resources: Ensure.dict(base.resources),
}

if (typeof result.description === 'string') {
result.description = result.description.slice(0, 1024)
} else if (result.description) {
for (const key in result.description) {
result.description[key] = result.description[key].slice(0, 1024)
}
}

return result
}

export function conclude(meta: PackageJson) {
const manifest: Manifest = {
const result: Manifest = {
...concludeBase(meta.cordis, meta.description),
hidden: Ensure.boolean(meta.cordis?.hidden),
preview: Ensure.boolean(meta.cordis?.preview),
insecure: Ensure.boolean(meta.cordis?.insecure),
browser: Ensure.boolean(meta.cordis?.browser),
category: Ensure.string(meta.cordis?.category),
public: Ensure.array(meta.cordis?.public),
description: Ensure.dict(meta.cordis?.description) || Ensure.string(meta.description, ''),
locales: Ensure.array(meta.cordis?.locales, []),
service: {
required: Ensure.array(meta.cordis?.service?.required, []),
optional: Ensure.array(meta.cordis?.service?.optional, []),
implements: Ensure.array(meta.cordis?.service?.implements, []),
},
ecosystem: Ensure.object(meta.cordis?.ecosystem, (ecosystem) => ({
inject: Ensure.array(ecosystem.inject),
pattern: Ensure.array(ecosystem.pattern),
keywords: Ensure.array(ecosystem.keywords),
})),
exports: Ensure.dict(meta.cordis?.exports, concludeBase),
}

if (typeof manifest.description === 'string') {
manifest.description = manifest.description.slice(0, 1024)
} else if (manifest.description) {
for (const key in manifest.description) {
manifest.description[key] = manifest.description[key].slice(0, 1024)
}
}

meta.keywords = Ensure.array(meta.keywords, []).filter((keyword) => {
if (!keyword.includes(':')) return true
if (keyword === 'market:hidden') {
manifest.hidden = true
} else if (keyword.startsWith('required:')) {
manifest.service.required.push(keyword.slice(9))
} else if (keyword.startsWith('optional:')) {
manifest.service.optional.push(keyword.slice(9))
} else if (keyword.startsWith('impl:')) {
manifest.service.implements.push(keyword.slice(5))
} else if (keyword.startsWith('locale:')) {
manifest.locales.push(keyword.slice(7))
}
})

return manifest
return result
}
3 changes: 1 addition & 2 deletions plugins/config/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,7 @@ export default class Manager extends Service {
}

// check implementations
for (const name of local.manifest.service.implements) {
if (name === 'adapter') continue
for (const name of local.manifest.service?.implements || []) {
result.impl.push(name)
}

Expand Down

0 comments on commit ccf6cfb

Please sign in to comment.