1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
use Regex;
use OnceLock;
use ensure_loader_imports;
use cratetemplate_env;
/// Idempotency marker injected into `RustLib.init` by
/// [`rewrite_frb_external_library_loader`]. Presence of this token means the
/// loader override has already been applied.
const ALEF_LOADER_MARKER: &str = "_alefResolveExternalLibrary";
/// Sentinel present ONLY in the current loader template (the versioned-cache
/// resolution step calls `nativeCachedLibPath()`). A file that carries
/// [`ALEF_LOADER_MARKER`] but NOT this sentinel was injected by an older alef
/// and must be upgraded in place — otherwise the marker-based idempotency check
/// would freeze a stale loader forever, even across regenerations. (This is the
/// exact failure that shipped a broken cache-unaware loader in a released
/// binding: the download script populated the versioned cache, but the frozen
/// loader never looked there.)
const ALEF_LOADER_CURRENT_SENTINEL: &str = "nativeCachedLibPath()";
/// Inject a published-package-aware native-library loader into the
/// flutter_rust_bridge-generated `frb_generated.dart`.
///
/// # Why
///
/// flutter_rust_bridge's default loader (`kDefaultExternalLibraryLoaderConfig`)
/// uses a build-tree-relative `ioDirectory` (e.g. `rust/target/release/`) that
/// is resolved against the *consumer's* current working directory and is NOT
/// shipped in the published pub tarball. When the package is consumed from
/// pub.dev the default loader fails to find the library at that path and falls
/// back to opening a relative framework path (`<stem>.framework/<stem>` on
/// macOS), which a hardened runtime rejects with
/// "Failed to load dynamic library ... (relative path not allowed)".
///
/// # Fix
///
/// This rewrite makes `RustLib.init` resolve the prebuilt native library from
/// the package's *own* installed location (`lib/src/<module>_bridge_generated/`,
/// resolved at runtime via `Isolate.resolvePackageUri`) as an **absolute** path
/// before delegating to flutter_rust_bridge. The publish pipeline ships the
/// prebuilt library alongside the generated bridge sources there. When the
/// package-relative library cannot be found (e.g. local development where the
/// library lives under `rust/target/<profile>/`), the override returns `null`
/// and flutter_rust_bridge falls back to its default loader unchanged — so this
/// is safe in both published and source-tree builds.
///
/// The transform is **idempotent**: a source that already contains the injected
/// helper is returned verbatim. It is also a no-op on any source that does not
/// contain the canonical FRB `RustLib.init` prologue (e.g. `lib.dart`), so it is
/// safe to apply unconditionally to any frb-generated file.
///
/// `package_name` is the pub package name (used to build the `package:` URI),
/// `module_name` is the bridge module stem (the `<module>_bridge_generated`
/// directory), and `stem` is the native library file stem
/// (`kDefaultExternalLibraryLoaderConfig.stem`, e.g. `sample_project_dart`).
/// Match the entire previously-injected loader region: from the helper's leading
/// doc comment (`/// Resolve the prebuilt native library`, stable across every
/// template version) through the injected `externalLibrary ??= await
/// _alefResolveExternalLibrary();` line inside `init`. Non-greedy so it stops at
/// the first such assignment. The original `init` body follows the match and is
/// left untouched.
/// Return the exact FRB-generated `RustLib.init` prologue present in `source`,
/// up to and including the `async {` that opens the method body, or `None` if
/// the canonical signature is absent.
///
/// Matches the prologue with flexible indentation, since flutter_rust_bridge
/// emits different indentation in different versions.
/// Build the patched `RustLib.init` prologue: the original signature plus a
/// `externalLibrary ??= ...` resolution line, followed by the
/// `_alefResolveExternalLibrary` helper method.
///
/// Renders `dart_init_prologue_replacement.jinja`, the single source of truth for the
/// injected prologue also used to build the `patch_published_loader` fallback embedded in
/// the generated dart-bridge crate's `build.rs` (see
/// `gen_rust_crate::cargo::dart_init_prologue_replacement`). Both call sites must stay on
/// this one template — a second, hand-written copy previously drifted and shipped a
/// version that couldn't reach `nativeDownloadAndCacheLibrary()`, breaking cold-cache
/// installs.
pub
/// Extract the native-library stem from the FRB-generated
/// `kDefaultExternalLibraryLoaderConfig` (the `stem: '<name>'` field), or `None`
/// if the config block is absent (e.g. for `lib.dart`).
/// Apply the published-package loader fix to a frb-generated file, deriving the
/// bridge-module and library stem from the file's own
/// `kDefaultExternalLibraryLoaderConfig`.
///
/// alef's dart backend names the bridge cdylib `<crate>_dart` (the FRB `stem`)
/// and emits its bridge sources under `lib/src/<crate>_bridge_generated/`, so
/// the module name is recovered by stripping the trailing `_dart` from the stem.
///
/// `package_name` must be the resolved `[dart] pubspec_name` — it is only a
/// coincidence that it equals the crate base when `pubspec_name` is
/// unconfigured. Deriving it from the stem emitted
/// `package:<crate>/src/native_loader.dart` into every renamed package, an
/// import that resolves nowhere and takes the whole bridge down with it.
///
/// No-op when no loader config is present (returns `source` unchanged), so this
/// is safe to call on `lib.dart` as well as `frb_generated.dart`.
pub