Part of #1097.
Current state
crates/perry/src/commands/compile.rs is 9,007 lines. The function run_with_parse_cache spans lines 1171 → 8765 (~7.6k lines) — that is, almost the entire file is one function: the full compile pipeline (parse → HIR → transform → link → bundle → sign → audit manifest) in a single linear scope.
There are some helpers around it (target_bundle_section, package_name_for_path, inject_ios_deeplinks, lookup_bundle_id_from_info_plist, emit_harmonyos_arkts_stubs, read_app_metadata, write_audit_manifest, collect_native_archives_for_lock, for_each_native_library_package, derive_target_key, rust_target_triple) but they're a small fraction.
The pipeline isn't conceptually one thing — it's parse, then HIR-lower, then transform, then codegen, then link, then bundle/sign per target. Each phase has its own concerns that today share one stack frame and one set of let-bound locals.
Proposed split
This one is higher-effort than the other seven because it requires extracting state into a CompileState struct (or threading the existing CompilationContext through more aggressively) so each phase can be a function. The payoff is large — making the compile pipeline navigable is worth a focused effort — but the work isn't pure file-moves the way the others can be.
Suggested phasing:
-
PR 1 — pure helper extraction (no API change to run_with_parse_cache): pull the obvious sub-routines (TypeScript-checking pass, source-map emission, audit-manifest assembly, dependency-lock writing, native-archive scanning) out into private functions still defined in compile.rs. This is mechanical and reviewable.
-
PR 2 — phase functions: define a struct CompilePipeline { ... } holding the long-lived state (parse cache, HIR modules, codegen settings, target info, etc.) and convert each phase to impl CompilePipeline { fn parse(&mut self), fn lower(&mut self), fn transform(&mut self), fn codegen(&mut self), fn link(&mut self), fn bundle(&mut self) }. run_with_parse_cache becomes a ~50-line orchestrator.
-
PR 3 — split into directory module crates/perry/src/commands/compile/:
compile/mod.rs — pub fn run, pub fn run_with_parse_cache, the CompilePipeline orchestrator
compile/parse.rs — parsing + parse-cache integration
compile/lower.rs — driving HIR lowering across modules
compile/transform.rs — running transform passes
compile/codegen.rs — driving codegen + the rayon parallel chunking
compile/link.rs — invoking cc/ld
compile/bundle_ios.rs / compile/bundle_macos.rs / compile/bundle_android.rs / compile/bundle_harmonyos.rs — per-target bundling
compile/sign.rs — codesigning, entitlements, provisioning
compile/audit.rs — write_audit_manifest
compile/metadata.rs — read_app_metadata, lookup_bundle_id_from_info_plist, package_bundle_id_from_input, target_bundle_section, toml_string, toml_build_number, package_name_for_path, rust_target_triple
compile/native_libs.rs — collect_native_archives_for_lock, for_each_native_library_package, derive_target_key
compile/ios_deeplinks.rs — inject_ios_deeplinks
compile/harmonyos_arkts.rs — emit_harmonyos_arkts_stubs
Acceptance
Per-PR conventions from #1097. Plus, because this is the CLI entry point, smoke-test each major target before merging the final PR:
cargo run --release -- some.ts -o out && ./out
cargo run --release -- some.ts --target ios-simulator -o out
- Optionally android/harmonyos if the local toolchain is set up
Part of #1097.
Current state
crates/perry/src/commands/compile.rsis 9,007 lines. The functionrun_with_parse_cachespans lines 1171 → 8765 (~7.6k lines) — that is, almost the entire file is one function: the full compile pipeline (parse → HIR → transform → link → bundle → sign → audit manifest) in a single linear scope.There are some helpers around it (
target_bundle_section,package_name_for_path,inject_ios_deeplinks,lookup_bundle_id_from_info_plist,emit_harmonyos_arkts_stubs,read_app_metadata,write_audit_manifest,collect_native_archives_for_lock,for_each_native_library_package,derive_target_key,rust_target_triple) but they're a small fraction.The pipeline isn't conceptually one thing — it's parse, then HIR-lower, then transform, then codegen, then link, then bundle/sign per target. Each phase has its own concerns that today share one stack frame and one set of
let-bound locals.Proposed split
This one is higher-effort than the other seven because it requires extracting state into a
CompileStatestruct (or threading the existingCompilationContextthrough more aggressively) so each phase can be a function. The payoff is large — making the compile pipeline navigable is worth a focused effort — but the work isn't pure file-moves the way the others can be.Suggested phasing:
PR 1 — pure helper extraction (no API change to
run_with_parse_cache): pull the obvious sub-routines (TypeScript-checking pass, source-map emission, audit-manifest assembly, dependency-lock writing, native-archive scanning) out into private functions still defined incompile.rs. This is mechanical and reviewable.PR 2 — phase functions: define a
struct CompilePipeline { ... }holding the long-lived state (parse cache, HIR modules, codegen settings, target info, etc.) and convert each phase toimpl CompilePipeline { fn parse(&mut self), fn lower(&mut self), fn transform(&mut self), fn codegen(&mut self), fn link(&mut self), fn bundle(&mut self) }.run_with_parse_cachebecomes a ~50-line orchestrator.PR 3 — split into directory module
crates/perry/src/commands/compile/:compile/mod.rs—pub fn run,pub fn run_with_parse_cache, theCompilePipelineorchestratorcompile/parse.rs— parsing + parse-cache integrationcompile/lower.rs— driving HIR lowering across modulescompile/transform.rs— running transform passescompile/codegen.rs— driving codegen + the rayon parallel chunkingcompile/link.rs— invokingcc/ldcompile/bundle_ios.rs/compile/bundle_macos.rs/compile/bundle_android.rs/compile/bundle_harmonyos.rs— per-target bundlingcompile/sign.rs— codesigning, entitlements, provisioningcompile/audit.rs—write_audit_manifestcompile/metadata.rs—read_app_metadata,lookup_bundle_id_from_info_plist,package_bundle_id_from_input,target_bundle_section,toml_string,toml_build_number,package_name_for_path,rust_target_triplecompile/native_libs.rs—collect_native_archives_for_lock,for_each_native_library_package,derive_target_keycompile/ios_deeplinks.rs—inject_ios_deeplinkscompile/harmonyos_arkts.rs—emit_harmonyos_arkts_stubsAcceptance
Per-PR conventions from #1097. Plus, because this is the CLI entry point, smoke-test each major target before merging the final PR:
cargo run --release -- some.ts -o out && ./outcargo run --release -- some.ts --target ios-simulator -o out