Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/figma

src/get-file-nodes.ts

86 lines · 2.9 KB · TypeScript
import { figmaRequest } from './client.ts'
import { mapNodeBundle } from './models.ts'
import { normalizeNodeIds, parseFileKey } from './setup.ts'
import type { FigmaAuthInput, FigmaNodeBundle, JsonRecord } from './types.ts'
import {
	clampInt,
	compactRecord,
	optionalString,
	requireRecord,
	requireStringArray,
} from './types.ts'
import { requireAuthMode } from './auth.ts'

export type GetFileNodesInput = FigmaAuthInput & {
	/** File key, or a figma.com file/design/board URL. */
	fileKey: string
	/** Node ids. Hyphenated URL ids (`1-2`) are accepted. */
	ids: Array<string> | string
	depth?: number
	version?: string
	geometry?: 'paths'
}

export type GetFileNodesResult = {
	fileKey: string
	name: string | null
	lastModified: string | null
	nodes: Array<FigmaNodeBundle>
}

/**
 * Load slim JSON for specific nodes in a Figma file.
 * @example
 * import getFileNodes from 'kody:@kody/figma/get-file-nodes'
 * const { nodes } = await getFileNodes({ fileKey: 'abc123', ids: ['12:34'] })
 */
export async function getFileNodes(input: GetFileNodesInput): Promise<GetFileNodesResult> {
	const fileKey = parseFileKey(input.fileKey)
	const ids = normalizeNodeIds(requireStringArray(input.ids, 'ids'))
	const result = await figmaRequest<{
		name?: string
		lastModified?: string
		nodes?: JsonRecord
	}>({
		...input,
		operation: 'files.read',
		path: `/v1/files/${encodeURIComponent(fileKey)}/nodes`,
		query: compactRecord({
			ids: ids.join(','),
			depth: input.depth === undefined ? undefined : clampInt(input.depth, 1, 8, 1, 'depth'),
			version: optionalString(input.version, 'version'),
			geometry: optionalString(input.geometry, 'geometry'),
		}),
	})
	const nodes = result.body.nodes ?? {}
	return {
		fileKey,
		name: typeof result.body.name === 'string' ? result.body.name : null,
		lastModified: typeof result.body.lastModified === 'string' ? result.body.lastModified : null,
		nodes: Object.entries(nodes).map(([id, bundle]) => mapNodeBundle(id, bundle)),
	}
}

/**
 * Load slim JSON for specific nodes in a Figma file.
 * @example
 * import getFileNodes from 'kody:@kody/figma/get-file-nodes'
 * const result = await getFileNodes({ fileKey: 'abc123', ids: '1-2' })
 */
export default async function getFileNodesEntrypoint(
	params: Partial<GetFileNodesInput> & Record<string, unknown> = {},
) {
	const input = requireRecord(params, 'get-file-nodes')
	return getFileNodes({
		fileKey: String(input.fileKey ?? input.fileUrl ?? ''),
		ids: input.ids as GetFileNodesInput['ids'],
		depth: input.depth as number | undefined,
		version: optionalString(input.version, 'version'),
		geometry: optionalString(input.geometry, 'geometry') as GetFileNodesInput['geometry'],
		integrationName: optionalString(input.integrationName, 'integrationName'),
		integration: optionalString(input.integration, 'integration'),
		account: optionalString(input.account, 'account'),
		secretName: optionalString(input.secretName, 'secretName'),
		auth: requireAuthMode(input.auth),
	})
}