Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions scripts/measure-icon-resolution.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env node

import { build } from 'esbuild';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { performance } from 'node:perf_hooks';
import { fileURLToPath, pathToFileURL } from 'node:url';

const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const targetFile = path.join(root, 'src/renderer/src/raycast-api/icon-runtime-phosphor.tsx');
const outputFile = path.join(os.tmpdir(), `supercmd-icon-resolution-${process.pid}-${Date.now()}.mjs`);

const exactInputs = [
'AddPerson',
'Icon.ArrowLeftCircleFilled',
'MagnifyingGlass',
'Stopwatch',
'Temperature',
'Dot',
'XMarkCircleFilled',
'Folder',
];

const unknownAndFuzzyInputs = [
'TotallyMissingIconName',
'HyperSpecificSparkleClockWidget',
'SuperCmdTimerStopwatchShape',
'UnmappedTerminalCommandLineIcon',
'MysteryNetworkCloudBolt',
'Noise___No_Such_Icon_999',
];

function exposeResolverPlugin() {
const normalizedTarget = path.normalize(targetFile);
return {
name: 'expose-icon-resolution-for-measurement',
setup(buildApi) {
buildApi.onLoad({ filter: /icon-runtime-phosphor\.tsx$/ }, async (args) => {
if (path.normalize(args.path) !== normalizedTarget) return undefined;
const source = await fs.readFile(args.path, 'utf8');
return {
contents: `${source}\nexport const __measureResolvePhosphorIconFromRaycast = resolvePhosphorIconFromRaycast;\n`,
loader: 'tsx',
resolveDir: path.dirname(args.path),
};
});
},
};
}

function stubServerRenderingPlugin() {
return {
name: 'stub-server-rendering-for-measurement',
setup(buildApi) {
buildApi.onResolve({ filter: /^react-dom\/server$/ }, () => ({
path: 'react-dom-server-measurement-stub',
namespace: 'measurement-stub',
}));
buildApi.onLoad({ filter: /.*/, namespace: 'measurement-stub' }, () => ({
contents: 'export function renderToStaticMarkup() { return ""; }',
loader: 'js',
}));
},
};
}

function measureCase(resolveIcon, { name, inputs, iterations }) {
const calls = inputs.length * iterations;
const start = performance.now();
let resolved = 0;

for (let iteration = 0; iteration < iterations; iteration += 1) {
for (const input of inputs) {
if (resolveIcon(input)?.icon) resolved += 1;
}
}

const durationMs = performance.now() - start;
return {
name,
calls,
resolved,
durationMs: Number(durationMs.toFixed(3)),
callsPerMs: Number((calls / Math.max(durationMs, 0.001)).toFixed(3)),
};
}

async function main() {
await build({
entryPoints: [targetFile],
outfile: outputFile,
bundle: true,
format: 'esm',
platform: 'node',
target: 'es2022',
jsx: 'automatic',
logLevel: 'silent',
plugins: [exposeResolverPlugin(), stubServerRenderingPlugin()],
});

const moduleUrl = `${pathToFileURL(outputFile).href}?t=${Date.now()}`;
const runtime = await import(moduleUrl);
const resolveIcon = runtime.__measureResolvePhosphorIconFromRaycast;
if (typeof resolveIcon !== 'function') {
throw new Error('Failed to expose resolvePhosphorIconFromRaycast for measurement.');
}

const results = [
measureCase(resolveIcon, {
name: 'exact/repeated',
inputs: exactInputs,
iterations: 2500,
}),
measureCase(resolveIcon, {
name: 'unknown-fuzzy/repeated',
inputs: unknownAndFuzzyInputs,
iterations: 250,
}),
];

console.log('Icon resolution measurement');
for (const result of results) {
console.log(
`${result.name}: ${result.calls} calls, ${result.durationMs} ms, ${result.callsPerMs} calls/ms, resolved ${result.resolved}`
);
}
console.log(JSON.stringify({ results }, null, 2));
}

try {
await main();
} finally {
await fs.unlink(outputFile).catch(() => {});
}
147 changes: 147 additions & 0 deletions scripts/test-icon-runtime-phosphor-cache.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#!/usr/bin/env node

import test from 'node:test';
import assert from 'node:assert/strict';
import { build } from 'esbuild';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';

const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const targetFile = path.join(root, 'src/renderer/src/raycast-api/icon-runtime-phosphor.tsx');

async function importIconRuntimeForTest() {
const outputFile = path.join(os.tmpdir(), `supercmd-icon-runtime-test-${process.pid}-${Date.now()}.mjs`);
const normalizedTarget = path.normalize(targetFile);

try {
await build({
entryPoints: [targetFile],
outfile: outputFile,
bundle: true,
format: 'esm',
platform: 'node',
target: 'es2022',
jsx: 'automatic',
logLevel: 'silent',
plugins: [
{
name: 'expose-icon-runtime-test-hooks',
setup(buildApi) {
buildApi.onLoad({ filter: /icon-runtime-phosphor\.tsx$/ }, async (args) => {
if (path.normalize(args.path) !== normalizedTarget) return undefined;
const source = await fs.readFile(args.path, 'utf8');
return {
contents: `${source}
export const __testIconRuntimePhosphor = {
resolvePhosphorIconFromRaycast,
tryResolvePhosphorByName,
clearIconResolutionCaches: () => {
phosphorIconResolutionCache.clear();
phosphorNameResolutionCache.clear();
raycastIconNameResolutionCache.clear();
},
caches: {
phosphorIconResolutionCache,
phosphorNameResolutionCache,
raycastIconNameResolutionCache,
},
};
`,
loader: 'tsx',
resolveDir: path.dirname(args.path),
};
});
},
},
{
name: 'stub-server-rendering-for-icon-runtime-tests',
setup(buildApi) {
buildApi.onResolve({ filter: /^react-dom\/server$/ }, () => ({
path: 'react-dom-server-test-stub',
namespace: 'test-stub',
}));
buildApi.onLoad({ filter: /.*/, namespace: 'test-stub' }, () => ({
contents: 'export function renderToStaticMarkup() { return ""; }',
loader: 'js',
}));
},
},
],
});

const runtime = await import(`${pathToFileURL(outputFile).href}?t=${Date.now()}`);
return runtime.__testIconRuntimePhosphor;
} finally {
await fs.unlink(outputFile).catch(() => {});
}
}

test('Phosphor icon runtime resolution cache', async (t) => {
const iconRuntime = await importIconRuntimeForTest();
const {
resolvePhosphorIconFromRaycast,
tryResolvePhosphorByName,
clearIconResolutionCaches,
caches,
} = iconRuntime;

await t.test('resolves direct Raycast icon names without changing weight', () => {
clearIconResolutionCaches();

const expected = tryResolvePhosphorByName('MagnifyingGlass');
const resolved = resolvePhosphorIconFromRaycast('MagnifyingGlass');

assert.ok(expected, 'expected Phosphor MagnifyingGlass export to exist');
assert.equal(resolved?.icon, expected);
assert.equal(resolved?.weight, 'regular');
});

await t.test('preserves explicit Raycast aliases and filled weight compatibility', () => {
clearIconResolutionCaches();

const stopwatch = resolvePhosphorIconFromRaycast('Stopwatch');
assert.equal(stopwatch?.icon, tryResolvePhosphorByName('Timer'));
assert.equal(stopwatch?.weight, 'regular');

const filled = resolvePhosphorIconFromRaycast('XMarkCircleFilled');
assert.equal(filled?.icon, tryResolvePhosphorByName('XCircle'));
assert.equal(filled?.weight, 'fill');
});

await t.test('keeps unknown icons on the existing fallback glyph', () => {
clearIconResolutionCaches();

const fallback = tryResolvePhosphorByName('Question') || tryResolvePhosphorByName('Circle');
const resolved = resolvePhosphorIconFromRaycast('QzxvPqmn999');

assert.ok(fallback, 'expected a Phosphor fallback glyph to exist');
assert.equal(resolved?.icon, fallback);
assert.equal(resolved?.weight, 'regular');
});

await t.test('caches successful and failed resolutions while allowing whole-cache clear', () => {
clearIconResolutionCaches();

const first = resolvePhosphorIconFromRaycast('TotallyMissingIconName');
const second = resolvePhosphorIconFromRaycast('TotallyMissingIconName');
assert.equal(second, first, 'repeated final resolution should return the cached result object');

assert.equal(tryResolvePhosphorByName('DefinitelyNotAPhosphorIcon'), undefined);
assert.equal(
caches.phosphorNameResolutionCache.get('DefinitelyNotAPhosphorIcon'),
null,
'failed Phosphor export lookups are memoized as misses',
);

clearIconResolutionCaches();
assert.equal(caches.phosphorIconResolutionCache.size, 0);
assert.equal(caches.phosphorNameResolutionCache.size, 0);
assert.equal(caches.raycastIconNameResolutionCache.size, 0);

const afterClear = resolvePhosphorIconFromRaycast('TotallyMissingIconName');
assert.equal(afterClear?.icon, first?.icon);
assert.equal(afterClear?.weight, first?.weight);
});
});
Loading
Loading