mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 10:59:53 +00:00
Note for reviewers: the code changes in src/* is because I've upgraded prettier to latest. the @prettier/plugin-oxc it self only improve performance and doesnt affect format rules ## Summary Integrates `@prettier/plugin-oxc` to improve Prettier performance by ~20%. The oxc plugin provides a faster parser written in Rust, significantly speeding up formatting operations across the codebase. ## Changes - Added `@prettier/plugin-oxc` as dev dependency - Updated `.prettierrc` to use oxc plugin alongside existing sort-imports plugin - Added `scripts/benchmark-prettier.js` to measure performance improvements - Updated `knip.config.ts` to ignore the oxc plugin - Updated `eslint.config.ts` to ignore the benchmark script ## Benchmark Results Ran 3 benchmarks comparing formatting performance on the entire codebase: **Without oxc:** - Median: 32.76s - Average: 32.89s - Min: 32.49s - Max: 33.43s **With oxc:** - Median: 26.13s - Average: 26.35s - Min: 25.24s - Max: 27.69s **Improvement: 20.26% faster (6.64s saved)** ## Testing The benchmark script can be run with: ```bash node scripts/benchmark-prettier.js ``` This will: 1. Test formatting performance without oxc plugin 2. Test formatting performance with oxc plugin 3. Display comparison results 4. Restore original configuration 🤖 Generated with [Claude Code](https://claude.com/claude-code) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6088-feat-Add-prettier-plugin-oxc-for-faster-formatting-28e6d73d365081aabb24d3af98c11bb0) by [Unito](https://www.unito.io) --------- Co-authored-by: DrJKL <DrJKL0424@gmail.com> Co-authored-by: GitHub Action <action@github.com>
374 lines
11 KiB
TypeScript
374 lines
11 KiB
TypeScript
import { liteClient as algoliasearch } from 'algoliasearch/dist/lite/builds/browser'
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import { useAlgoliaSearchProvider } from '@/services/providers/algoliaSearchProvider'
|
|
import { SortableAlgoliaField } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
|
|
|
// Mock global Algolia constants
|
|
;(global as any).__ALGOLIA_APP_ID__ = 'test-app-id'
|
|
;(global as any).__ALGOLIA_API_KEY__ = 'test-api-key'
|
|
|
|
// Mock algoliasearch
|
|
vi.mock('algoliasearch/dist/lite/builds/browser', () => ({
|
|
liteClient: vi.fn()
|
|
}))
|
|
|
|
describe('useAlgoliaSearchProvider', () => {
|
|
let mockSearchClient: any
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
|
|
// Create mock search client
|
|
mockSearchClient = {
|
|
search: vi.fn()
|
|
}
|
|
|
|
vi.mocked(algoliasearch).mockReturnValue(mockSearchClient)
|
|
})
|
|
|
|
afterEach(() => {
|
|
// Clear the module-level cache between tests
|
|
const provider = useAlgoliaSearchProvider()
|
|
provider.clearSearchCache()
|
|
})
|
|
|
|
describe('searchPacks', () => {
|
|
it('should search for packs and convert results', async () => {
|
|
const mockAlgoliaResults = {
|
|
results: [
|
|
{
|
|
hits: [
|
|
{
|
|
objectID: 'algolia-1',
|
|
id: 'pack-1',
|
|
name: 'Test Pack',
|
|
description: 'A test pack',
|
|
publisher_id: 'publisher-1',
|
|
total_install: 500,
|
|
create_time: '2024-01-01T00:00:00Z',
|
|
update_time: '2024-01-15T00:00:00Z',
|
|
repository_url: 'https://github.com/test/pack',
|
|
license: 'MIT',
|
|
status: 'active',
|
|
latest_version: '1.0.0',
|
|
latest_version_status: 'published',
|
|
icon_url: 'https://example.com/icon.png',
|
|
comfy_nodes: ['LoadImage', 'SaveImage']
|
|
}
|
|
]
|
|
},
|
|
{ hits: [] } // Query suggestions
|
|
]
|
|
}
|
|
|
|
mockSearchClient.search.mockResolvedValue(mockAlgoliaResults)
|
|
|
|
const provider = useAlgoliaSearchProvider()
|
|
const result = await provider.searchPacks('test', {
|
|
pageSize: 10,
|
|
pageNumber: 0
|
|
})
|
|
|
|
expect(mockSearchClient.search).toHaveBeenCalledWith({
|
|
requests: [
|
|
{
|
|
query: 'test',
|
|
indexName: 'nodes_index',
|
|
attributesToRetrieve: expect.any(Array),
|
|
hitsPerPage: 10,
|
|
page: 0
|
|
},
|
|
{
|
|
query: 'test',
|
|
indexName: 'nodes_index_query_suggestions'
|
|
}
|
|
],
|
|
strategy: 'none'
|
|
})
|
|
|
|
expect(result.nodePacks).toHaveLength(1)
|
|
expect(result.nodePacks[0]).toEqual({
|
|
id: 'pack-1',
|
|
name: 'Test Pack',
|
|
description: 'A test pack',
|
|
repository: 'https://github.com/test/pack',
|
|
license: 'MIT',
|
|
downloads: 500,
|
|
status: 'active',
|
|
icon: 'https://example.com/icon.png',
|
|
latest_version: {
|
|
version: '1.0.0',
|
|
createdAt: '2024-01-15T00:00:00Z',
|
|
status: 'published',
|
|
comfy_node_extract_status: undefined
|
|
},
|
|
publisher: {
|
|
id: 'publisher-1',
|
|
name: 'publisher-1'
|
|
},
|
|
created_at: '2024-01-01T00:00:00Z',
|
|
comfy_nodes: ['LoadImage', 'SaveImage'],
|
|
category: undefined,
|
|
author: undefined,
|
|
tags: undefined,
|
|
github_stars: undefined,
|
|
supported_os: undefined,
|
|
supported_comfyui_version: undefined,
|
|
supported_comfyui_frontend_version: undefined,
|
|
supported_accelerators: undefined,
|
|
banner_url: undefined
|
|
})
|
|
})
|
|
|
|
it('should include query suggestions when query is long enough', async () => {
|
|
const mockAlgoliaResults = {
|
|
results: [
|
|
{ hits: [] }, // Main results
|
|
{
|
|
hits: [
|
|
{ query: 'test query', popularity: 10 },
|
|
{ query: 'test pack', popularity: 5 }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
|
|
mockSearchClient.search.mockResolvedValue(mockAlgoliaResults)
|
|
|
|
const provider = useAlgoliaSearchProvider()
|
|
const result = await provider.searchPacks('test', {
|
|
pageSize: 10,
|
|
pageNumber: 0
|
|
})
|
|
|
|
// Should make 2 requests (main + suggestions)
|
|
expect(mockSearchClient.search).toHaveBeenCalledWith({
|
|
requests: [
|
|
expect.objectContaining({ indexName: 'nodes_index' }),
|
|
expect.objectContaining({
|
|
indexName: 'nodes_index_query_suggestions'
|
|
})
|
|
],
|
|
strategy: 'none'
|
|
})
|
|
|
|
expect(result.querySuggestions).toEqual([
|
|
{ query: 'test query', popularity: 10 },
|
|
{ query: 'test pack', popularity: 5 }
|
|
])
|
|
})
|
|
|
|
it('should not query suggestions for short queries', async () => {
|
|
mockSearchClient.search.mockResolvedValue({
|
|
results: [{ hits: [] }]
|
|
})
|
|
|
|
const provider = useAlgoliaSearchProvider()
|
|
await provider.searchPacks('a', {
|
|
pageSize: 10,
|
|
pageNumber: 0
|
|
})
|
|
|
|
// Should only make 1 request (no suggestions)
|
|
expect(mockSearchClient.search).toHaveBeenCalledWith({
|
|
requests: [expect.objectContaining({ indexName: 'nodes_index' })],
|
|
strategy: 'none'
|
|
})
|
|
})
|
|
|
|
it('should cache search results', async () => {
|
|
mockSearchClient.search.mockResolvedValue({
|
|
results: [{ hits: [] }, { hits: [] }]
|
|
})
|
|
|
|
const provider = useAlgoliaSearchProvider()
|
|
const params = { pageSize: 10, pageNumber: 0 }
|
|
|
|
// First call
|
|
await provider.searchPacks('test', params)
|
|
expect(mockSearchClient.search).toHaveBeenCalledTimes(1)
|
|
|
|
// Second call with same params should use cache
|
|
await provider.searchPacks('test', params)
|
|
expect(mockSearchClient.search).toHaveBeenCalledTimes(1)
|
|
|
|
// Different params should make new request
|
|
await provider.searchPacks('test', { ...params, pageNumber: 1 })
|
|
expect(mockSearchClient.search).toHaveBeenCalledTimes(2)
|
|
})
|
|
|
|
it('should handle missing objectID by using id field', async () => {
|
|
const mockAlgoliaResults = {
|
|
results: [
|
|
{
|
|
hits: [
|
|
{
|
|
id: 'pack-id-only',
|
|
name: 'Pack without objectID',
|
|
// ... other required fields
|
|
publisher_id: 'pub',
|
|
total_install: 0,
|
|
comfy_nodes: []
|
|
}
|
|
]
|
|
},
|
|
{ hits: [] }
|
|
]
|
|
}
|
|
|
|
mockSearchClient.search.mockResolvedValue(mockAlgoliaResults)
|
|
|
|
const provider = useAlgoliaSearchProvider()
|
|
const result = await provider.searchPacks('test', {
|
|
pageSize: 10,
|
|
pageNumber: 0
|
|
})
|
|
|
|
expect(result.nodePacks[0].id).toBe('pack-id-only')
|
|
})
|
|
})
|
|
|
|
describe('clearSearchCache', () => {
|
|
it('should clear the cache', async () => {
|
|
mockSearchClient.search.mockResolvedValue({
|
|
results: [{ hits: [] }, { hits: [] }]
|
|
})
|
|
|
|
const provider = useAlgoliaSearchProvider()
|
|
const params = { pageSize: 10, pageNumber: 0 }
|
|
|
|
// Populate cache
|
|
await provider.searchPacks('test', params)
|
|
expect(mockSearchClient.search).toHaveBeenCalledTimes(1)
|
|
|
|
// Clear cache
|
|
provider.clearSearchCache()
|
|
|
|
// Same search should hit API again
|
|
await provider.searchPacks('test', params)
|
|
expect(mockSearchClient.search).toHaveBeenCalledTimes(2)
|
|
})
|
|
})
|
|
|
|
describe('getSortValue', () => {
|
|
const testPack = {
|
|
id: '1',
|
|
name: 'Test Pack',
|
|
downloads: 100,
|
|
publisher: { id: 'pub1', name: 'Publisher One' },
|
|
latest_version: {
|
|
version: '1.0.0',
|
|
createdAt: '2024-01-15T10:00:00Z'
|
|
},
|
|
created_at: '2024-01-01T10:00:00Z'
|
|
}
|
|
|
|
it('should return correct values for each sort field', () => {
|
|
const provider = useAlgoliaSearchProvider()
|
|
|
|
expect(
|
|
provider.getSortValue(testPack, SortableAlgoliaField.Downloads)
|
|
).toBe(100)
|
|
expect(provider.getSortValue(testPack, SortableAlgoliaField.Name)).toBe(
|
|
'Test Pack'
|
|
)
|
|
expect(
|
|
provider.getSortValue(testPack, SortableAlgoliaField.Publisher)
|
|
).toBe('Publisher One')
|
|
|
|
const createdTimestamp = new Date('2024-01-01T10:00:00Z').getTime()
|
|
expect(
|
|
provider.getSortValue(testPack as any, SortableAlgoliaField.Created)
|
|
).toBe(createdTimestamp)
|
|
|
|
const updatedTimestamp = new Date('2024-01-15T10:00:00Z').getTime()
|
|
expect(
|
|
provider.getSortValue(testPack, SortableAlgoliaField.Updated)
|
|
).toBe(updatedTimestamp)
|
|
})
|
|
|
|
it('should handle missing values', () => {
|
|
const incompletePack = { id: '1', name: 'Incomplete' }
|
|
const provider = useAlgoliaSearchProvider()
|
|
|
|
expect(
|
|
provider.getSortValue(incompletePack, SortableAlgoliaField.Downloads)
|
|
).toBe(0)
|
|
expect(
|
|
provider.getSortValue(incompletePack, SortableAlgoliaField.Publisher)
|
|
).toBe('')
|
|
expect(
|
|
provider.getSortValue(
|
|
incompletePack as any,
|
|
SortableAlgoliaField.Created
|
|
)
|
|
).toBe(0)
|
|
expect(
|
|
provider.getSortValue(incompletePack, SortableAlgoliaField.Updated)
|
|
).toBe(0)
|
|
})
|
|
})
|
|
|
|
describe('getSortableFields', () => {
|
|
it('should return all Algolia sort fields', () => {
|
|
const provider = useAlgoliaSearchProvider()
|
|
const fields = provider.getSortableFields()
|
|
|
|
expect(fields).toEqual([
|
|
{
|
|
id: SortableAlgoliaField.Downloads,
|
|
label: 'Downloads',
|
|
direction: 'desc'
|
|
},
|
|
{
|
|
id: SortableAlgoliaField.Created,
|
|
label: 'Created',
|
|
direction: 'desc'
|
|
},
|
|
{
|
|
id: SortableAlgoliaField.Updated,
|
|
label: 'Updated',
|
|
direction: 'desc'
|
|
},
|
|
{
|
|
id: SortableAlgoliaField.Publisher,
|
|
label: 'Publisher',
|
|
direction: 'asc'
|
|
},
|
|
{ id: SortableAlgoliaField.Name, label: 'Name', direction: 'asc' }
|
|
])
|
|
})
|
|
})
|
|
|
|
describe('memoization', () => {
|
|
it('should memoize toRegistryPack conversions', async () => {
|
|
const mockHit = {
|
|
objectID: 'algolia-1',
|
|
id: 'pack-1',
|
|
name: 'Test Pack',
|
|
publisher_id: 'pub1',
|
|
total_install: 100,
|
|
comfy_nodes: []
|
|
}
|
|
|
|
mockSearchClient.search.mockResolvedValue({
|
|
results: [
|
|
{ hits: [mockHit, mockHit, mockHit] }, // Same object 3 times
|
|
{ hits: [] }
|
|
]
|
|
})
|
|
|
|
const provider = useAlgoliaSearchProvider()
|
|
const result = await provider.searchPacks('test', {
|
|
pageSize: 10,
|
|
pageNumber: 0
|
|
})
|
|
|
|
// All 3 results should be the same object reference due to memoization
|
|
expect(result.nodePacks[0]).toBe(result.nodePacks[1])
|
|
expect(result.nodePacks[1]).toBe(result.nodePacks[2])
|
|
})
|
|
})
|
|
})
|