Apply new code format standard (#217)

This commit is contained in:
Chenlei Hu
2024-07-25 10:10:18 -04:00
committed by GitHub
parent 19c70d95d3
commit e179f75387
121 changed files with 11898 additions and 11983 deletions

View File

@@ -1,4 +1,4 @@
import type { LiteGraph, LGraphCanvas } from "@comfyorg/litegraph";
import type { LiteGraph, LGraphCanvas } from '@comfyorg/litegraph'
/**
* @typedef { import("./src/scripts/app")["app"] } app
@@ -11,34 +11,28 @@ import type { LiteGraph, LGraphCanvas } from "@comfyorg/litegraph";
* @typedef { (...args: EzOutput[] | [...EzOutput[], Record<string, unknown>]) => EzNode } EzNodeFactory
*/
export type EzNameSpace = Record<string, (...args) => EzNode>;
export type EzNameSpace = Record<string, (...args) => EzNode>
export class EzConnection {
/** @type { app } */
app;
app
/** @type { InstanceType<LG["LLink"]> } */
link;
link
get originNode() {
return new EzNode(
this.app,
this.app.graph.getNodeById(this.link.origin_id)
);
return new EzNode(this.app, this.app.graph.getNodeById(this.link.origin_id))
}
get originOutput() {
return this.originNode.outputs[this.link.origin_slot];
return this.originNode.outputs[this.link.origin_slot]
}
get targetNode() {
return new EzNode(
this.app,
this.app.graph.getNodeById(this.link.target_id)
);
return new EzNode(this.app, this.app.graph.getNodeById(this.link.target_id))
}
get targetInput() {
return this.targetNode.inputs[this.link.target_slot];
return this.targetNode.inputs[this.link.target_slot]
}
/**
@@ -46,34 +40,34 @@ export class EzConnection {
* @param { InstanceType<LG["LLink"]> } link
*/
constructor(app, link) {
this.app = app;
this.link = link;
this.app = app
this.link = link
}
disconnect() {
this.targetInput.disconnect();
this.targetInput.disconnect()
}
}
export class EzSlot {
/** @type { EzNode } */
node;
node
/** @type { number } */
index;
index
/**
* @param { EzNode } node
* @param { number } index
*/
constructor(node, index) {
this.node = node;
this.index = index;
this.node = node
this.index = index
}
}
export class EzInput extends EzSlot {
/** @type { INodeInputSlot } */
input;
input
/**
* @param { EzNode } node
@@ -81,26 +75,26 @@ export class EzInput extends EzSlot {
* @param { INodeInputSlot } input
*/
constructor(node, index, input) {
super(node, index);
this.input = input;
super(node, index)
this.input = input
}
get connection() {
const link = this.node.node.inputs?.[this.index]?.link;
const link = this.node.node.inputs?.[this.index]?.link
if (link == null) {
return null;
return null
}
return new EzConnection(this.node.app, this.node.app.graph.links[link]);
return new EzConnection(this.node.app, this.node.app.graph.links[link])
}
disconnect() {
this.node.node.disconnectInput(this.index);
this.node.node.disconnectInput(this.index)
}
}
export class EzOutput extends EzSlot {
/** @type { INodeOutputSlot } */
output;
output
/**
* @param { EzNode } node
@@ -108,21 +102,21 @@ export class EzOutput extends EzSlot {
* @param { INodeOutputSlot } output
*/
constructor(node, index, output) {
super(node, index);
this.output = output;
super(node, index)
this.output = output
}
get connections() {
return (this.node.node.outputs?.[this.index]?.links ?? []).map(
(l) => new EzConnection(this.node.app, this.node.app.graph.links[l])
);
)
}
/**
* @param { EzInput } input
*/
connectTo(input) {
if (!input) throw new Error("Invalid input");
if (!input) throw new Error('Invalid input')
/**
* @type { LG["LLink"] | null }
@@ -131,27 +125,27 @@ export class EzOutput extends EzSlot {
this.index,
input.node.node,
input.index
);
)
if (!link) {
const inp = input.input;
const inName = inp.name || inp.label || inp.type;
const inp = input.input
const inName = inp.name || inp.label || inp.type
throw new Error(
`Connecting from ${input.node.node.type}#${input.node.id}[${inName}#${input.index}] -> ${this.node.node.type}#${this.node.id}[${
this.output.name ?? this.output.type
}#${this.index}] failed.`
);
)
}
return link;
return link
}
}
export class EzNodeMenuItem {
/** @type { EzNode } */
node;
node
/** @type { number } */
index;
index
/** @type { ContextMenuItem } */
item;
item
/**
* @param { EzNode } node
@@ -159,18 +153,18 @@ export class EzNodeMenuItem {
* @param { ContextMenuItem } item
*/
constructor(node, index, item) {
this.node = node;
this.index = index;
this.item = item;
this.node = node
this.index = index
this.item = item
}
call(selectNode = true) {
if (!this.item?.callback)
throw new Error(
`Menu Item ${this.item?.content ?? "[null]"} has no callback.`
);
`Menu Item ${this.item?.content ?? '[null]'} has no callback.`
)
if (selectNode) {
this.node.select();
this.node.select()
}
return this.item.callback.call(
this.node.node,
@@ -179,17 +173,17 @@ export class EzNodeMenuItem {
undefined,
undefined,
this.node.node
);
)
}
}
export class EzWidget {
/** @type { EzNode } */
node;
node
/** @type { number } */
index;
index
/** @type { IWidget } */
widget;
widget
/**
* @param { EzNode } node
@@ -197,104 +191,104 @@ export class EzWidget {
* @param { IWidget } widget
*/
constructor(node, index, widget) {
this.node = node;
this.index = index;
this.widget = widget;
this.node = node
this.index = index
this.widget = widget
}
get value() {
return this.widget.value;
return this.widget.value
}
set value(v) {
this.widget.value = v;
this.widget.callback?.call?.(this.widget, v);
this.widget.value = v
this.widget.callback?.call?.(this.widget, v)
}
get isConvertedToInput() {
// @ts-ignore : this type is valid for converted widgets
return this.widget.type === "converted-widget";
return this.widget.type === 'converted-widget'
}
getConvertedInput() {
if (!this.isConvertedToInput)
throw new Error(`Widget ${this.widget.name} is not converted to input.`);
throw new Error(`Widget ${this.widget.name} is not converted to input.`)
return this.node.inputs.find(
(inp) => inp.input["widget"]?.name === this.widget.name
);
(inp) => inp.input['widget']?.name === this.widget.name
)
}
convertToWidget() {
if (!this.isConvertedToInput)
throw new Error(
`Widget ${this.widget.name} cannot be converted as it is already a widget.`
);
var menu = this.node.menu["Convert Input to Widget"].item.submenu.options;
)
var menu = this.node.menu['Convert Input to Widget'].item.submenu.options
var index = menu.findIndex(
(a) => a.content == `Convert ${this.widget.name} to widget`
);
menu[index].callback.call();
)
menu[index].callback.call()
}
convertToInput() {
if (this.isConvertedToInput)
throw new Error(
`Widget ${this.widget.name} cannot be converted as it is already an input.`
);
var menu = this.node.menu["Convert Widget to Input"].item.submenu.options;
)
var menu = this.node.menu['Convert Widget to Input'].item.submenu.options
var index = menu.findIndex(
(a) => a.content == `Convert ${this.widget.name} to input`
);
menu[index].callback.call();
)
menu[index].callback.call()
}
}
export class EzNode {
/** @type { app } */
app;
app
/** @type { LGNode } */
node;
node
/**
* @param { app } app
* @param { LGNode } node
*/
constructor(app, node) {
this.app = app;
this.node = node;
this.app = app
this.node = node
}
get id() {
return this.node.id;
return this.node.id
}
get inputs() {
return this.#makeLookupArray("inputs", "name", EzInput);
return this.#makeLookupArray('inputs', 'name', EzInput)
}
get outputs() {
return this.#makeLookupArray("outputs", "name", EzOutput);
return this.#makeLookupArray('outputs', 'name', EzOutput)
}
get widgets() {
return this.#makeLookupArray("widgets", "name", EzWidget);
return this.#makeLookupArray('widgets', 'name', EzWidget)
}
get menu() {
return this.#makeLookupArray(
() => this.app.canvas.getNodeMenuOptions(this.node),
"content",
'content',
EzNodeMenuItem
);
)
}
get isRemoved() {
return !this.app.graph.getNodeById(this.id);
return !this.app.graph.getNodeById(this.id)
}
select(addToSelection = false) {
this.app.canvas.selectNode(this.node, addToSelection);
this.app.canvas.selectNode(this.node, addToSelection)
}
// /**
@@ -323,60 +317,60 @@ export class EzNode {
*/
#makeLookupArray(nodeProperty, nameProperty, ctor) {
const items =
typeof nodeProperty === "function"
typeof nodeProperty === 'function'
? nodeProperty()
: this.node[nodeProperty];
: this.node[nodeProperty]
// @ts-ignore
return (items ?? []).reduce(
(p, s, i) => {
if (!s) return p;
if (!s) return p
const name = s[nameProperty];
const item = new ctor(this, i, s);
const name = s[nameProperty]
const item = new ctor(this, i, s)
// @ts-ignore
p.push(item);
p.push(item)
if (name) {
// @ts-ignore
if (name in p) {
throw new Error(
`Unable to store ${nodeProperty} ${name} on array as name conflicts.`
);
)
}
}
// @ts-ignore
p[name] = item;
return p;
p[name] = item
return p
},
Object.assign([], { $: this })
);
)
}
}
export class EzGraph {
/** @type { app } */
app;
app
/**
* @param { app } app
*/
constructor(app) {
this.app = app;
this.app = app
}
get nodes() {
return this.app.graph._nodes.map((n) => new EzNode(this.app, n));
return this.app.graph._nodes.map((n) => new EzNode(this.app, n))
}
clear() {
this.app.graph.clear();
this.app.graph.clear()
}
arrange() {
this.app.graph.arrange();
this.app.graph.arrange()
}
stringify() {
return JSON.stringify(this.app.graph.serialize(), undefined);
return JSON.stringify(this.app.graph.serialize(), undefined)
}
/**
@@ -384,36 +378,36 @@ export class EzGraph {
* @returns { EzNode }
*/
find(obj) {
let match;
let id;
if (typeof obj === "number") {
id = obj;
let match
let id
if (typeof obj === 'number') {
id = obj
} else {
id = obj.id;
id = obj.id
}
match = this.app.graph.getNodeById(id);
match = this.app.graph.getNodeById(id)
if (!match) {
throw new Error(`Unable to find node with ID ${id}.`);
throw new Error(`Unable to find node with ID ${id}.`)
}
return new EzNode(this.app, match);
return new EzNode(this.app, match)
}
/**
* @returns { Promise<void> }
*/
reload() {
const graph = JSON.parse(JSON.stringify(this.app.graph.serialize()));
const graph = JSON.parse(JSON.stringify(this.app.graph.serialize()))
return new Promise((r) => {
this.app.graph.clear();
this.app.graph.clear()
setTimeout(async () => {
await this.app.loadGraphData(graph);
await this.app.loadGraphData(graph)
// @ts-ignore
r();
}, 10);
});
r()
}, 10)
})
}
/**
@@ -426,7 +420,7 @@ export class EzGraph {
*/
toPrompt() {
// @ts-ignore
return this.app.graphToPrompt();
return this.app.graphToPrompt()
}
}
@@ -452,10 +446,10 @@ export const Ez = {
*/
graph(app, LiteGraph, LGraphCanvas, clearGraph = true) {
// Always set the active canvas so things work
LGraphCanvas.active_canvas = app.canvas;
LGraphCanvas.active_canvas = app.canvas
if (clearGraph) {
app.graph.clear();
app.graph.clear()
}
// @ts-ignore : this proxy handles utility methods & node creation
@@ -463,35 +457,35 @@ export const Ez = {
{},
{
get(_, p) {
if (typeof p !== "string") throw new Error("Invalid node");
const node = LiteGraph.createNode(p);
if (!node) throw new Error(`Unknown node "${p}"`);
app.graph.add(node);
if (typeof p !== 'string') throw new Error('Invalid node')
const node = LiteGraph.createNode(p)
if (!node) throw new Error(`Unknown node "${p}"`)
app.graph.add(node)
/**
* @param {Parameters<EzNodeFactory>} args
*/
return function (...args) {
const ezNode = new EzNode(app, node);
const inputs = ezNode.inputs;
const ezNode = new EzNode(app, node)
const inputs = ezNode.inputs
let slot = 0;
let slot = 0
for (const arg of args) {
if (arg instanceof EzOutput) {
arg.connectTo(inputs[slot++]);
arg.connectTo(inputs[slot++])
} else {
for (const k in arg) {
ezNode.widgets[k].value = arg[k];
ezNode.widgets[k].value = arg[k]
}
}
}
return ezNode;
};
},
return ezNode
}
}
}
);
)
return { graph: new EzGraph(app), ez: factory };
},
};
return { graph: new EzGraph(app), ez: factory }
}
}

View File

@@ -1,21 +1,21 @@
import { APIConfig, mockApi } from "./setup";
import { Ez, EzGraph, EzNameSpace } from "./ezgraph";
import lg from "./litegraph";
import fs from "fs";
import path from "path";
import { APIConfig, mockApi } from './setup'
import { Ez, EzGraph, EzNameSpace } from './ezgraph'
import lg from './litegraph'
import fs from 'fs'
import path from 'path'
const html = fs.readFileSync(path.resolve(__dirname, "../../index.html"));
const html = fs.readFileSync(path.resolve(__dirname, '../../index.html'))
interface StartConfig extends APIConfig {
resetEnv?: boolean;
preSetup?(app): Promise<void>;
localStorage?: Record<string, string>;
resetEnv?: boolean
preSetup?(app): Promise<void>
localStorage?: Record<string, string>
}
interface StartResult {
app: any;
graph: EzGraph;
ez: EzNameSpace;
app: any
graph: EzGraph
ez: EzNameSpace
}
/**
@@ -29,24 +29,24 @@ interface StartResult {
*/
export async function start(config: StartConfig = {}): Promise<StartResult> {
if (config.resetEnv) {
jest.resetModules();
jest.resetAllMocks();
lg.setup(global);
localStorage.clear();
sessionStorage.clear();
jest.resetModules()
jest.resetAllMocks()
lg.setup(global)
localStorage.clear()
sessionStorage.clear()
}
Object.assign(localStorage, config.localStorage ?? {});
document.body.innerHTML = html.toString();
Object.assign(localStorage, config.localStorage ?? {})
document.body.innerHTML = html.toString()
mockApi(config);
const { app } = await import("../../src/scripts/app");
const { LiteGraph, LGraphCanvas } = await import("@comfyorg/litegraph");
config.preSetup?.(app);
await app.setup();
mockApi(config)
const { app } = await import('../../src/scripts/app')
const { LiteGraph, LGraphCanvas } = await import('@comfyorg/litegraph')
config.preSetup?.(app)
await app.setup()
// @ts-ignore
return { ...Ez.graph(app, LiteGraph, LGraphCanvas), app };
return { ...Ez.graph(app, LiteGraph, LGraphCanvas), app }
}
/**
@@ -54,9 +54,9 @@ export async function start(config: StartConfig = {}): Promise<StartResult> {
* @param { (hasReloaded: boolean) => (Promise<void> | void) } cb
*/
export async function checkBeforeAndAfterReload(graph, cb) {
await cb(false);
await graph.reload();
await cb(true);
await cb(false)
await graph.reload()
await cb(true)
}
/**
@@ -68,34 +68,34 @@ export async function checkBeforeAndAfterReload(graph, cb) {
export function makeNodeDef(name, input, output = {}) {
const nodeDef = {
name,
category: "test",
category: 'test',
output: [],
output_name: [],
output_is_list: [],
input: {
required: {},
},
};
required: {}
}
}
for (const k in input) {
nodeDef.input.required[k] =
typeof input[k] === "string" ? [input[k], {}] : [...input[k]];
typeof input[k] === 'string' ? [input[k], {}] : [...input[k]]
}
if (output instanceof Array) {
output = output.reduce((p, c) => {
p[c] = c;
return p;
}, {});
p[c] = c
return p
}, {})
}
for (const k in output) {
// @ts-ignore
nodeDef.output.push(output[k]);
nodeDef.output.push(output[k])
// @ts-ignore
nodeDef.output_name.push(k);
nodeDef.output_name.push(k)
// @ts-ignore
nodeDef.output_is_list.push(false);
nodeDef.output_is_list.push(false)
}
return { [name]: nodeDef };
return { [name]: nodeDef }
}
/**
@@ -105,9 +105,9 @@ export function makeNodeDef(name, input, output = {}) {
* @returns { x is Exclude<T, null | undefined> }
*/
export function assertNotNullOrUndefined(x) {
expect(x).not.toEqual(null);
expect(x).not.toEqual(undefined);
return true;
expect(x).not.toEqual(null)
expect(x).not.toEqual(undefined)
return true
}
/**
@@ -116,32 +116,32 @@ export function assertNotNullOrUndefined(x) {
* @param { ReturnType<Ez["graph"]>["graph"] } graph
*/
export function createDefaultWorkflow(ez, graph) {
graph.clear();
const ckpt = ez.CheckpointLoaderSimple();
graph.clear()
const ckpt = ez.CheckpointLoaderSimple()
const pos = ez.CLIPTextEncode(ckpt.outputs.CLIP, { text: "positive" });
const neg = ez.CLIPTextEncode(ckpt.outputs.CLIP, { text: "negative" });
const pos = ez.CLIPTextEncode(ckpt.outputs.CLIP, { text: 'positive' })
const neg = ez.CLIPTextEncode(ckpt.outputs.CLIP, { text: 'negative' })
const empty = ez.EmptyLatentImage();
const empty = ez.EmptyLatentImage()
const sampler = ez.KSampler(
ckpt.outputs.MODEL,
pos.outputs.CONDITIONING,
neg.outputs.CONDITIONING,
empty.outputs.LATENT
);
)
const decode = ez.VAEDecode(sampler.outputs.LATENT, ckpt.outputs.VAE);
const save = ez.SaveImage(decode.outputs.IMAGE);
graph.arrange();
const decode = ez.VAEDecode(sampler.outputs.LATENT, ckpt.outputs.VAE)
const save = ez.SaveImage(decode.outputs.IMAGE)
graph.arrange()
return { ckpt, pos, neg, empty, sampler, decode, save };
return { ckpt, pos, neg, empty, sampler, decode, save }
}
export async function getNodeDefs() {
const { api } = await import("../../src/scripts/api");
return api.getNodeDefs();
const { api } = await import('../../src/scripts/api')
return api.getNodeDefs()
}
export async function getNodeDef(nodeId) {
return (await getNodeDefs())[nodeId];
return (await getNodeDefs())[nodeId]
}

View File

@@ -1,19 +1,19 @@
import fs from "fs";
import path from "path";
import { nop } from "../utils/nopProxy";
import fs from 'fs'
import path from 'path'
import { nop } from '../utils/nopProxy'
function forEachKey(cb) {
for (const k of [
"LiteGraph",
"LGraph",
"LLink",
"LGraphNode",
"LGraphGroup",
"DragAndScale",
"LGraphCanvas",
"ContextMenu",
'LiteGraph',
'LGraph',
'LLink',
'LGraphNode',
'LGraphGroup',
'DragAndScale',
'LGraphCanvas',
'ContextMenu'
]) {
cb(k);
cb(k)
}
}
@@ -24,6 +24,6 @@ export default {
// forEachKey((k) => delete ctx[k]);
// Clear document after each run
document.getElementsByTagName("html")[0].innerHTML = "";
},
};
document.getElementsByTagName('html')[0].innerHTML = ''
}
}

View File

@@ -2,5 +2,5 @@ export const nop = new Proxy(function () {}, {
get: () => nop,
set: () => true,
apply: () => nop,
construct: () => nop,
});
construct: () => nop
})

View File

@@ -1,28 +1,28 @@
import "../../src/scripts/api";
import '../../src/scripts/api'
const fs = require("fs");
const path = require("path");
const fs = require('fs')
const path = require('path')
function* walkSync(dir: string): Generator<string> {
const files = fs.readdirSync(dir, { withFileTypes: true });
const files = fs.readdirSync(dir, { withFileTypes: true })
for (const file of files) {
if (file.isDirectory()) {
yield* walkSync(path.join(dir, file.name));
yield* walkSync(path.join(dir, file.name))
} else {
yield path.join(dir, file.name);
yield path.join(dir, file.name)
}
}
}
export interface APIConfig {
mockExtensions?: string[];
mockNodeDefs?: Record<string, any>;
settings?: Record<string, string>;
mockExtensions?: string[]
mockNodeDefs?: Record<string, any>
settings?: Record<string, string>
userConfig?: {
storage: "server" | "browser";
users?: Record<string, any>;
migrated?: boolean;
};
userData?: Record<string, any>;
storage: 'server' | 'browser'
users?: Record<string, any>
migrated?: boolean
}
userData?: Record<string, any>
}
/**
@@ -42,20 +42,20 @@ export function mockApi(config: APIConfig = {}) {
let { mockExtensions, mockNodeDefs, userConfig, settings, userData } = {
settings: {},
userData: {},
...config,
};
...config
}
if (!mockExtensions) {
mockExtensions = Array.from(walkSync(path.resolve("./src/extensions/core")))
.filter((x) => x.endsWith(".js"))
.map((x) => path.relative(path.resolve("./src/"), x).replace(/\\/g, "/"));
mockExtensions = Array.from(walkSync(path.resolve('./src/extensions/core')))
.filter((x) => x.endsWith('.js'))
.map((x) => path.relative(path.resolve('./src/'), x).replace(/\\/g, '/'))
}
if (!mockNodeDefs) {
mockNodeDefs = JSON.parse(
fs.readFileSync(path.resolve("./tests-ui/data/object_info.json"))
);
fs.readFileSync(path.resolve('./tests-ui/data/object_info.json'))
)
}
const events = new EventTarget();
const events = new EventTarget()
const mockApi = {
addEventListener: events.addEventListener.bind(events),
removeEventListener: events.removeEventListener.bind(events),
@@ -64,37 +64,37 @@ export function mockApi(config: APIConfig = {}) {
getExtensions: jest.fn(() => mockExtensions),
getNodeDefs: jest.fn(() => mockNodeDefs),
init: jest.fn(),
apiURL: jest.fn((x) => "src/" + x),
fileURL: jest.fn((x) => "src/" + x),
apiURL: jest.fn((x) => 'src/' + x),
fileURL: jest.fn((x) => 'src/' + x),
createUser: jest.fn((username) => {
// @ts-ignore
if (username in userConfig.users) {
return { status: 400, json: () => "Duplicate" };
return { status: 400, json: () => 'Duplicate' }
}
// @ts-ignore
userConfig.users[username + "!"] = username;
return { status: 200, json: () => username + "!" };
userConfig.users[username + '!'] = username
return { status: 200, json: () => username + '!' }
}),
getUserConfig: jest.fn(
() => userConfig ?? { storage: "browser", migrated: false }
() => userConfig ?? { storage: 'browser', migrated: false }
),
getSettings: jest.fn(() => settings),
storeSettings: jest.fn((v) => Object.assign(settings, v)),
getUserData: jest.fn((f) => {
if (f in userData) {
return { status: 200, json: () => userData[f] };
return { status: 200, json: () => userData[f] }
} else {
return { status: 404 };
return { status: 404 }
}
}),
storeUserData: jest.fn((file, data) => {
userData[file] = data;
userData[file] = data
}),
listUserData: jest.fn(() => []),
};
jest.mock("../../src/scripts/api", () => ({
listUserData: jest.fn(() => [])
}
jest.mock('../../src/scripts/api', () => ({
get api() {
return mockApi;
},
}));
return mockApi
}
}))
}