You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
manifestEntries lets a user-authored external plugin (fallow-plugin-*.jsonc) seed entry points DERIVED from framework manifest files: find manifests by a recursive glob, parse JSON/JSONC, gate on a dotted-field when (strict equality), and resolve each entries[].path (with ${dotted.field} string/array fan-out) relative to the manifest's directory. This makes manifest-driven frameworks self-serviceable in a config file instead of requiring a built-in plugin.
That raises a natural question: can existing built-in plugins move to the declarative format, shrinking the compiled-Rust plugin surface? A survey of the built-in plugins splits them into three tiers, and the answer is "a few, once the format grows a little."
Survey of the built-in plugins
AST config parsers (~62): parse arbitrary JS/TS config through the Oxc parser (resolve.alias, entry, plugins, path aliases, referenced deps). Declarative data cannot execute or parse JavaScript, so these are not expressible. Out of scope; they stay in Rust.
JSON/JSONC manifest entry seeders (a handful): browser-extension, obsidian, commit-and-tag-version. This is manifestEntries' family, but they use capabilities v1 deliberately deferred (below).
Static-only (~40): enablers plus a couple of entryPoints / alwaysUsed globs plus toolingDependencies (ava, mocha, karma, husky, cspell, svgo, and similar). These are already expressible with the existing external-plugin static fields; they never needed manifestEntries.
Format extensions needed to cover the Tier-2 seeders
Object-array iteration (content_scripts[*].js). ${field} currently fans out over SCALAR arrays only. browser-extension seeds content_scripts[].js (an array of objects, each carrying a js array), which needs iteration over an object array plus a sub-field select. Size: M.
Presence / exists operator on when. obsidian activates on a manifest SHAPE (id / name / version / minAppVersion present) rather than a specific value. v1 when is strict-equality only; a { "field": { "exists": true } } form was reserved for exactly this. Size: S.
Declarative pathAliases / referencedDependencies outputs. Several plugins credit dependencies or path aliases derived from a manifest, not just entries. The external format seeds entries / always-used / tooling only today. Size: M.
The ~40 static-only plugins -> expressible with existing fields today, but see the recommendation.
Recommendation
Do NOT mass-migrate the built-ins. They are compiled, individually tested (activation checks plus negative-check fixtures), and each is tiny. Moving them to data trades away compile-time safety and their per-plugin fixtures for a modest maintenance gain, with real regression risk. manifestEntries' value is new and unsupported manifest-driven frameworks plus adopter self-serve, not replacing plugins that already work.
If shrinking the Rust plugin surface becomes a goal, the honest sequence is:
Migrate obsidian and browser-extension as a proof of value, keeping their existing fixtures as parity tests (the migrated declarative plugin must produce byte-identical results on the fixture before the Rust version is removed).
Reassess before going wider.
A complementary author-experience item (already noted separately): a fallow plugin check dry-run that shows which manifests matched, which passed each when, and which entries were seeded, would make authoring and migrating declarative plugins far less blind.
Non-goals
The ~62 AST-config plugins stay in Rust.
No wholesale migration of working static plugins without a byte-identical parity-test guarantee per plugin.
Background
manifestEntrieslets a user-authored external plugin (fallow-plugin-*.jsonc) seed entry points DERIVED from framework manifest files: find manifests by a recursive glob, parse JSON/JSONC, gate on a dotted-fieldwhen(strict equality), and resolve eachentries[].path(with${dotted.field}string/array fan-out) relative to the manifest's directory. This makes manifest-driven frameworks self-serviceable in a config file instead of requiring a built-in plugin.That raises a natural question: can existing built-in plugins move to the declarative format, shrinking the compiled-Rust plugin surface? A survey of the built-in plugins splits them into three tiers, and the answer is "a few, once the format grows a little."
Survey of the built-in plugins
resolve.alias,entry,plugins, path aliases, referenced deps). Declarative data cannot execute or parse JavaScript, so these are not expressible. Out of scope; they stay in Rust.browser-extension,obsidian,commit-and-tag-version. This ismanifestEntries' family, but they use capabilities v1 deliberately deferred (below).enablersplus a couple ofentryPoints/alwaysUsedglobs plustoolingDependencies(ava, mocha, karma, husky, cspell, svgo, and similar). These are already expressible with the existing external-plugin static fields; they never neededmanifestEntries.Format extensions needed to cover the Tier-2 seeders
content_scripts[*].js).${field}currently fans out over SCALAR arrays only.browser-extensionseedscontent_scripts[].js(an array of objects, each carrying ajsarray), which needs iteration over an object array plus a sub-field select. Size: M.existsoperator onwhen.obsidianactivates on a manifest SHAPE (id/name/version/minAppVersionpresent) rather than a specific value. v1whenis strict-equality only; a{ "field": { "exists": true } }form was reserved for exactly this. Size: S.pathAliases/referencedDependenciesoutputs. Several plugins credit dependencies or path aliases derived from a manifest, not just entries. The external format seeds entries / always-used / tooling only today. Size: M.role(entries currently inherit the plugin's singleentryPointRole). Additive-safe. Size: S.Migration candidates (each gated on an extension above)
obsidian-> needs chore(deps): bump actions/download-artifact from 4 to 8 #2 (presencewhen); its entries are static siblings, so it is otherwise a clean fit.browser-extension-> needs chore(deps): bump actions/setup-node from 4 to 6 #1 (object-array iteration).commit-and-tag-version-> needs chore(deps): bump actions/setup-node from 4 to 6 #1.Recommendation
Do NOT mass-migrate the built-ins. They are compiled, individually tested (activation checks plus negative-check fixtures), and each is tiny. Moving them to data trades away compile-time safety and their per-plugin fixtures for a modest maintenance gain, with real regression risk.
manifestEntries' value is new and unsupported manifest-driven frameworks plus adopter self-serve, not replacing plugins that already work.If shrinking the Rust plugin surface becomes a goal, the honest sequence is:
when) - the two that unlock real Tier-2 candidates.obsidianandbrowser-extensionas a proof of value, keeping their existing fixtures as parity tests (the migrated declarative plugin must produce byte-identical results on the fixture before the Rust version is removed).A complementary author-experience item (already noted separately): a
fallow plugin checkdry-run that shows which manifests matched, which passed eachwhen, and which entries were seeded, would make authoring and migrating declarative plugins far less blind.Non-goals