Syntax highlighting silently fails (and `Bundle.module` crashes) in SwiftPM CLI-built app bundles
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 65/100
Research direction
Start in CodeLanguage.swift, especially resourceURL and queryURL(for:), then reproduce with swift build and a hand-assembled .app containing the named resource bundle. Verify the bundle is found under Contents/Resources, both query layouts work, .scm files load for highlighting, and Xcode-built consumers retain their current behavior; run SwiftLint.
Written by the indexing model from the issue text.
Description
Summary
When CodeEditLanguages is consumed by an app that is built and packaged
with swift build (plus a hand-rolled bundle-assembly step) rather than
Xcode, two resource-resolution problems surface:
- Distributed app crashes on the first highlight lookup, because the
SwiftPM-generatedBundle.moduleaccessor cannot locate the resource
bundle inside a.appand callsfatalError. - Even with the bundle located, every tree-sitter query file fails to
load becauseCodeLanguage.queryURL(for:)hardcodes a path layout that
only matches the Xcode-produced bundle. The editor renders as plain gray
text with no error.
Both stem from the same assumption — that the package is always built and
embedded by Xcode. Dev builds on the build machine work, which is why this is
easy to miss.
Environment
- Package:
CodeEditLanguages0.1.20 (pulled transitively via
CodeEditSourceEditor0.15.2). - Build:
swift buildfrom the command line. No.xcodeproj. The app is
assembled into a.appby a custom script that copies the SwiftPM resource
bundle (CodeEditLanguages_CodeEditLanguages.bundle) into
MyApp.app/Contents/Resources/. - macOS, Apple Silicon, current Swift toolchain.
Defect 1 — Bundle.module fatalErrors in a distributed .app
Root cause
The SwiftPM-generated accessor
(.build/…/CodeEditLanguages.build/DerivedSources/resource_bundle_accessor.swift)
probes exactly two locations:
let mainPath = Bundle.main.bundleURL.appendingPathComponent("CodeEditLanguages_CodeEditLanguages.bundle").path
let buildPath = "/Users/<build-machine>/…/.build/arm64-apple-macosx/debug/CodeEditLanguages_CodeEditLanguages.bundle"
let preferredBundle = Bundle(path: mainPath)
guard let bundle = preferredBundle ?? Bundle(path: buildPath) else {
Swift.fatalError("could not load resource bundle: from \(mainPath) or \(buildPath)")
}
mainPathusesBundle.main.**bundleURL**— i.e. the.appwrapper root,
notContents/Resources. A correctly-formed.appkeeps resources in
Contents/Resources, so this candidate never matches.buildPathis a hardcoded absolute path on the build machine. It
resolves during local development, but obviously not on any other machine.
Result on an end user's machine: neither candidate exists →
Swift.fatalError → the app crashes the first time a CodeLanguage's
resourceURL / query is accessed.
Bundle.module (CodeLanguage.resourceURL) is used here:
// CodeLanguage.swift
internal var resourceURL: URL? = Bundle.module.resourceURL
Repro
- Depend on
CodeEditLanguages(directly or viaCodeEditSourceEditor). swift build(no Xcode project).- Assemble a
.appand copyCodeEditLanguages_CodeEditLanguages.bundle
intoContents/Resources/. - Run the
.app. First access to a language'squeryURL→ crash at the
accessor'sfatalError.
- Expected: the bundle is found under
Contents/Resources; no crash. - Actual:
fatalError("could not load resource bundle …").
Defect 2 — query path assumes the Xcode bundle layout
Root cause
// CodeLanguage.swift
internal func queryURL(for highlights: String = "highlights") -> URL? {
return resourceURL?
.appendingPathComponent("Resources/tree-sitter-\(tsName)/\(highlights).scm")
}
The "Resources/" segment is unconditionally prepended. That matches the
directory nesting Xcode produces, but under the SwiftPM command-line build the
grammar files do not live at that relative path from the resolved
resourceURL, so queryURL points at a file that does not exist. The lookup
returns a URL that never resolves, no error is thrown, and highlighting
silently degrades to plain text.
Repro
- With Defect 1 worked around (bundle located), open any source file in an
editor backed byCodeEditSourceEditor. - Observe: no syntax colors; text is uniform gray.
- Log
CodeLanguage.swift.queryURLandFileManager.fileExistson the
returned path — the file is not there.
- Expected: the
.scmquery loads; tokens are highlighted. - Actual:
queryURLresolves to a non-existent path; queries silently
fail.
Proposed fix
Both are small and, importantly, change nothing for Xcode consumers — the
new code paths only engage when the current lookup fails.
-
Bundle resolution — before falling back to
Bundle.module, probe
Bundle.main.resourceURL(i.e.Contents/Resources), where a
CLI-assembled app copies the SwiftPM resource bundle:private static let queryBundleResourceURL: URL? = { if let bundled = Bundle.main.resourceURL? .appendingPathComponent("CodeEditLanguages_CodeEditLanguages.bundle"), let bundle = Bundle(url: bundled) { return bundle.resourceURL } return Bundle.module.resourceURL }() internal var resourceURL: URL? = CodeLanguage.queryBundleResourceURL -
Layout-aware query path — resolve against whichever layout exists on
disk, probing the nested (Resources/…) layout first so Xcode behavior is
byte-for-byte unchanged, then the flat layout:guard let base = resourceURL else { return nil } let relativePath = "tree-sitter-\(tsName)/\(highlights).scm" let nested = base.appendingPathComponent("Resources/\(relativePath)") if FileManager.default.fileExists(atPath: nested.path) { return nested } return base.appendingPathComponent(relativePath)
Offer to contribute
We are already running these two changes as a local patch against 0.1.20 and
they resolve both issues with no observed regression for Xcode-built consumers.
Per the CodeEdit contribution convention (open an issue first, ask to be
assigned), we'd be glad to submit the PR — could you assign this to us? The
patch passes SwiftLint and keeps the existing code paths as the primary
lookup. Happy to adjust the approach if you'd prefer a different shape.
Thanks for maintaining CodeEditLanguages.
- Dominant language
- Swift
- Stars
- 133
- Forks
- 56
- PR merge metrics
- No merged PRs in 30d
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from CodeEditApp/CodeEditLanguages
-
Difficulty 1/5 Under an hour Newbie friendliness 78/100
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
CodeEditApp/CodeEditLanguages#94 · 1 comment · 1 reaction ·
-
Difficulty 3/5 Half a day Newbie friendliness 45/100
CodeEditApp/CodeEditLanguages#93 · 1 comment ·
-
Difficulty 3/5 1-2 days Newbie friendliness 35/100
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 35/100
All issues in CodeEditApp/CodeEditLanguages
Similar issues
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
type: docs
Difficulty 1/5 Under an hour Newbie friendliness 95/100
googleapis/google-cloud-swift#971 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
bitcoindevkit/bdk-ffi#1125 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
mozilla-mobile/firefox-ios#35743 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
manaflow-ai/cmux#13417 ·