Skip to content
Open
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
166 changes: 118 additions & 48 deletions __build__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ def log_step_info(msg:str, status='info'):
step_infos.append({"msg": f' - {msg}', "type": status})


def build(without_tests = True, fix = False):
def build(without_tests = True, fix = False, quiet_tsc: bool = False, export_frontend_types: bool = False):

THIS_DIR = os.path.dirname(os.path.abspath(__file__))
DIR_SRC_WEB = os.path.abspath(f'{THIS_DIR}/src_web/')
DIR_WEB = os.path.abspath(f'{THIS_DIR}/web/')
DIR_WEB_COMFYUI = os.path.abspath(f'{DIR_WEB}/comfyui/')
DIR_SRC_WEB = os.path.abspath(os.path.join(THIS_DIR, 'src_web'))
DIR_WEB = os.path.abspath(os.path.join(THIS_DIR, 'web'))
DIR_WEB_COMFYUI = os.path.abspath(os.path.join(DIR_WEB, 'comfyui'))

if fix:
tss = glob(os.path.join(DIR_SRC_WEB, "**", "*.ts"), recursive=True)
Expand All @@ -70,42 +70,105 @@ def build(without_tests = True, fix = False):
log_step(status="Done")

log_step(msg='Copying web directory')
rmtree(DIR_WEB)
copytree(DIR_SRC_WEB, DIR_WEB, ignore=ignore_patterns("typings*", "*.ts", "*.scss"))
log_step(status="Done")

ts_version_result = subprocess.run(["node", "./node_modules/typescript/bin/tsc", "-v"],
capture_output=True,
text=True,
check=True)
ts_version = re.sub(r'^.*Version\s*([\d\.]+).*', 'v\\1', ts_version_result.stdout, flags=re.DOTALL)
try:
rmtree(DIR_WEB, ignore_errors=True)
copytree(DIR_SRC_WEB, DIR_WEB, ignore=ignore_patterns("typings*", "*.ts", "*.scss"))
log_step(status="Done")
except Exception as e:
log_step_info(f'Error copying web directory: {e}', 'warn')
log_step(status="Error")
raise

# Resolve cross-platform paths for executables
tsc_path = os.path.join(THIS_DIR, 'node_modules', 'typescript', 'bin', 'tsc')
try:
ts_version_result = subprocess.run(["node", tsc_path, "-v"],
capture_output=True,
text=True,
check=True)
ts_version = re.sub(r'^.*Version\s*([\d\.]+).*', 'v\\1', ts_version_result.stdout, flags=re.DOTALL)
except subprocess.CalledProcessError as e:
log_step_info(f'Failed to get TypeScript version: exit code {e.returncode}', 'warn')
log_step(status="Error")
raise

# Optionally export all declarations in comfyui-frontend-types by prefixing with export (vim-style regex)
if export_frontend_types:
types_file = os.path.join(THIS_DIR, 'node_modules', '@comfyorg', 'comfyui-frontend-types', 'index.d.ts')
log_step(msg='Exporting all declarations in comfyui-frontend-types (index.d.ts)')
try:
if not os.path.exists(types_file):
log_step_info(f'Missing types file: {types_file}', 'warn')
log_step(status='Error')
raise FileNotFoundError(types_file)
with open(types_file, 'r', encoding='utf-8') as f:
content = f.read()
# Vim: s/^(\s*)declare /\1export &/
def repl(m):
indent = m.group(1)
substitute = m.group(2)
replacement=f"{indent}export {m.group(2)}"
log_step_info(msg=f"exporting '{substitute}'")
return replacement
new_content, n = re.subn(r'^(\s*)(declare .*)', repl, content, flags=re.MULTILINE)
if n == 0:
log_step_info('No declare lines found to export. File may already be exported or has different format.', 'warn')
else:
log_step_info(f'Exported {n} declaration line(s).')
with open(types_file, 'w', encoding='utf-8') as f:
f.write(new_content)
log_step(status='Done')
except Exception as e:
log_step_info(f'Failed exporting declarations: {e}', 'warn')
log_step(status='Error')
raise

log_step(msg=f'TypeScript ({ts_version})')
checked = subprocess.run(["node", "./node_modules/typescript/bin/tsc"], check=True)
log_step(status="Done")
try:
if quiet_tsc:
subprocess.run(["node", tsc_path], check=True, capture_output=True, text=True)
else:
subprocess.run(["node", tsc_path], check=True)
log_step(status="Done")
except subprocess.CalledProcessError as e:
if not quiet_tsc:
if e.stdout:
log_step_info(f'tsc stdout:\n{e.stdout}', 'warn')
if e.stderr:
log_step_info(f'tsc stderr:\n{e.stderr}', 'warn')
log_step_info(f'TypeScript compilation failed with exit code {e.returncode}', 'warn')
log_step(status="Error")
raise

if not without_tests:
log_step(msg='Removing directories (KEEPING TESTING)', status="Notice")
else:
log_step(msg='Removing unneeded directories')
test_path = os.path.join(DIR_WEB, 'comfyui', 'tests')
if os.path.exists(test_path):
rmtree(test_path)
rmtree(os.path.join(DIR_WEB, 'comfyui', 'testing'))
rmtree(test_path, ignore_errors=True)
testing_path = os.path.join(DIR_WEB, 'comfyui', 'testing')
rmtree(testing_path, ignore_errors=True)
# Always remove the dummy scripts_comfy directory
rmtree(os.path.join(DIR_WEB, 'scripts_comfy'))
scripts_comfy_path = os.path.join(DIR_WEB, 'scripts_comfy')
rmtree(scripts_comfy_path, ignore_errors=True)
log_step(status="Done")

scsss = glob(os.path.join(DIR_SRC_WEB, "**", "*.scss"), recursive=True)
log_step(msg=f'SASS for {len(scsss)} files')
scsss = [i.replace(THIS_DIR, '.') for i in scsss]
cmds = ["node", "./node_modules/sass/sass"]
scsss = [os.path.relpath(i, THIS_DIR) for i in scsss]
sass_path = os.path.join(THIS_DIR, 'node_modules', 'sass', 'sass')
cmds = ["node", sass_path]
for scss in scsss:
out = scss.replace('src_web', 'web').replace('.scss', '.css')
cmds.append(f'{scss}:{out}')
cmds.append('--no-source-map')
checked = subprocess.run(cmds, check=True)
log_step(status="Done")
try:
subprocess.run(cmds, check=True)
log_step(status="Done")
except subprocess.CalledProcessError as e:
log_step_info(f'SASS compilation failed with exit code {e.returncode}', 'warn')
log_step(status="Error")
raise

# Handle the common directories. Because ComfyUI loads under /extensions/rgthree-comfy we can't
# easily share sources outside of the `DIR_WEB_COMFYUI` _and_ allow typescript to resolve them in
Expand All @@ -115,37 +178,44 @@ def build(without_tests = True, fix = False):
log_step(msg='Cleaning Imports')
js_files = glob(os.path.join(DIR_WEB, '**', '*.js'), recursive=True)
for file in js_files:
rel_path = file.replace(f'{DIR_WEB}/', "")
with open(file, 'r', encoding="utf-8") as f:
filedata = f.read()
num = rel_path.count(os.sep)
if rel_path.startswith('comfyui'):
filedata = re.sub(r'(from\s+["\'])rgthree/', f'\\1{"../" * (num + 1)}rgthree/', filedata)
filedata = re.sub(r'(from\s+["\'])scripts/', f'\\1{"../" * (num + 1)}scripts/', filedata)
# Dynamic Imports
filedata = re.sub(r'(import\(["\'])rgthree/', f'\\1{"../" * (num + 1)}rgthree/', filedata)
else:
filedata = re.sub(r'(from\s+["\'])rgthree/', f'\\1{"../" * num}', filedata)
filedata = re.sub(r'(from\s+["\'])scripts/', f'\\1{"../" * (num + 1)}scripts/', filedata)
# Dynamic Imports
filedata = re.sub(r'(import\(["\'])rgthree/', f'\\1{"../" * num}', filedata)

filedata, n = re.subn(r'(\s*from [\'"](?!.*[.]js[\'"]).*?)([\'"];)', '\\1.js\\2', filedata)
if n > 0:
filename = os.path.basename(file)
log_step_info(
f'{filename} has {n} import{"s" if n > 1 else ""} that do not end in ".js"', 'warn')
with open(file, 'w', encoding="utf-8") as f:
f.write(filedata)
try:
rel_path = os.path.relpath(file, DIR_WEB)
with open(file, 'r', encoding="utf-8") as f:
filedata = f.read()
num = rel_path.count(os.sep)
if rel_path.startswith('comfyui'):
filedata = re.sub(r'(from\s+["\'])rgthree/', f'\\1{"../" * (num + 1)}rgthree/', filedata)
filedata = re.sub(r'(from\s+["\'])scripts/', f'\\1{"../" * (num + 1)}scripts/', filedata)
# Dynamic Imports
filedata = re.sub(r'(import\(["\'])rgthree/', f'\\1{"../" * (num + 1)}rgthree/', filedata)
else:
filedata = re.sub(r'(from\s+["\'])rgthree/', f'\\1{"../" * num}', filedata)
filedata = re.sub(r'(from\s+["\'])scripts/', f'\\1{"../" * (num + 1)}scripts/', filedata)
# Dynamic Imports
filedata = re.sub(r'(import\(["\'])rgthree/', f'\\1{"../" * num}', filedata)

filedata, n = re.subn(r'(\s*from [\'\"](?!.*[.]js[\'\"]).*?)([\'\"];)', '\\1.js\\2', filedata)
if n > 0:
filename = os.path.basename(file)
log_step_info(
f'{filename} has {n} import{"s" if n > 1 else ""} that do not end in ".js"', 'warn')
with open(file, 'w', encoding="utf-8") as f:
f.write(filedata)
except Exception as e:
log_step_info(f'Failed processing JS file {file}: {e}', 'warn')
log_step(status="Error")
raise
log_step(status="Done")


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--no-tests", default=False, action="store_true")
parser.add_argument("-f", "--fix", default=False, action="store_true")
parser.add_argument("-t", "--no-tests", default=False, action="store_true", help="Do not remove test directories from web output")
parser.add_argument("-f", "--fix", default=False, action="store_true", help="Auto-fix .ts import statements to end with .js")
parser.add_argument("-q", "--quiet-tsc", default=False, action="store_true", help="Suppress tsc warnings and errors output")
parser.add_argument("-E", "--export-frontend-types", default=False, action="store_true", help="Prefix all 'declare' statements with 'export' in comfyui-frontend-types index.d.ts")
args = parser.parse_args()

start = time.time()
build(without_tests=args.no_tests, fix=args.fix)
build(without_tests=args.no_tests, fix=args.fix, quiet_tsc=args.quiet_tsc, export_frontend_types=args.export_frontend_types)
print(f'Finished all in {round(time.time() - start, 3)}s')
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"web-tree-sitter": "0.25.6"
},
"scripts": {
"build": "./__build__.py || python .\\__build__.py"
"build": "python __build__.py -E"
}
}