Plugins

A Go tool that copies the plugins declared in the merged per-platform configuration files into build/<platform>/sources/. Reads the plugins map from build/xcode/config/config.jsonc and build/android/config/config.jsonc, resolves each plugin to its source directory and copies it over.

Requirements

  • Go 1.26 or newer (only for compilation. dist/ directory contains binaries).

  • go-task (optional, only for the tasks below).

  • The merged config files, produced by task jsonc.

Usage

From tools/plugins/src:

go run . --xcode build/xcode/config/config.jsonc \
  --android build/android/config/config.jsonc

Both --xcode and --android are required.

Flags

Flag Required Description

--xcode

Yes

Path to the merged Xcode config file (build/xcode/config/config.jsonc).

--android

Yes

Path to the merged Android config file (build/android/config/config.jsonc).

--ignore-platform

No

Keep other-platform plugin entries in the config files instead of deleting them. They are still skipped when copying.

--xcode-template

No

Path to the Plugins.swift template. Default: sources/xcode/Application/Application/Plugins.swift.

--android-template

No

Path to the Plugins.kt template. Default: sources/android/Application/src/main/java/com/jasonelle/application/Plugins.kt.

Plugin path references

Each plugin in the config plugins map points to a path reference. The reference selects the platform when the second segment is xcode or android; otherwise the plugin targets both platforms. When the reference does not include a plugin name (e.g. @jasonelle or @jasonelle/xcode), the config map key is used as the name.

Reference Resolves to Platforms

@jasonelle/JLPluginHello

sources/xcode/JLPluginHello + sources/android/JLPluginHello

Both

@jasonelle/xcode/JLPluginAppleSignIn

sources/xcode/JLPluginAppleSignIn

Xcode

@jasonelle/android/JLPluginFoo

sources/android/JLPluginFoo

Android

@lib/JLPluginCustom

lib/common/sources/plugins/JLPluginCustom/xcode + …​/android

Both

@lib/xcode/JLPluginCustom

lib/common/sources/plugins/JLPluginCustom/xcode

Xcode

@lib/android/JLPluginCustom

lib/common/sources/plugins/JLPluginCustom/android

Android

Plugins that target the other platform (e.g. @jasonelle/android/* inside the Xcode config) are ignored for that build and, unless --ignore-platform is passed, removed from the config file so each build/<platform>/config/config.jsonc only lists the plugins that apply to that platform.

How it works

For each platform:

  1. Reads the platform config file and its plugins map.

  2. Resolves every plugin reference to a source directory (see above), skipping references for the other platform.

  3. Cleans build/<platform>/sources/ so plugins removed from the config do not linger.

  4. Copies each plugin directory into build/<platform>/sources/<name>, where <name> is the last path segment of the reference.

  5. Generates the platform’s plugin composition file from its template, keeping only the plugins that resolved for that platform. Used plugins with no template block get their registration lines synthesized (see below).

  6. Unless --ignore-platform is set, rewrites the config file without the other-platform entries (all other keys are preserved).

Plugin composition files

Plugins.swift (Xcode) and Plugins.kt (Android) register the available plugins in the app. They are generated by filtering a template that lives under sources/<platform>/:

  • Xcode: sources/xcode/Application/Application/Plugins.swift

  • Android: sources/android/Application/src/main/java/com/jasonelle/application/Plugins.kt

Each plugin block in the template is wrapped in markers keyed by the config plugins key:

// PLUGIN:JLPluginHello
import JLPluginHello
// ENDPLUGIN

A block whose key is used is kept verbatim and its markers stripped; a block whose key is not used (for example a plugin removed from the config) is dropped together with its markers. Non-marker lines pass through unchanged. The tool fails loudly on unmatched or nested markers and on an unterminated block.

Plugins without a template block

A used plugin that has no // PLUGIN: block is synthesized instead of failing. Each template carries EXTRA markers where the generated lines are injected (markers with no plugins emit nothing):

  • // PLUGINS.IMPORT.EXTRA (Xcode): import <Name> per plugin.

  • // PLUGINS.INIT.EXTRA (Xcode): <Name>.Plugin.id: <Name>.Plugin() per plugin. The name is the resolved plugin name from the reference.

  • // PLUGINS.INIT.EXTRA.VARS (Android): val <name> = <pkg>.Plugin(context) per plugin.

  • // PLUGINS.INIT.EXTRA.MAP (Android): <name>.id to <name> per plugin.

The Android lines are read from the plugin’s own Plugin.kt (copied into build/android/sources/<name>/): the package declaration provides the fully-qualified class and the override val name property provides the variable and JS registration key. Explicit template blocks always win over synthesis.

A template that lacks its required EXTRA markers while a used plugin has no block still fails, so a plugin can never silently disappear from the generated file.

The filtered files are written mirroring the template path under build/<platform>/sources/, next to the copied plugin directories.

Output

  • Xcode: build/xcode/sources/<plugin>/ and build/xcode/sources/Application/Application/Plugins.swift

  • Android: build/android/sources/<plugin>/ and build/android/sources/Application/src/main/java/com/jasonelle/application/Plugins.kt

Tasks

From the repository root:

  • task plugins (pl): copy plugins for both Xcode and Android.

  • task plugins.build (pb): build the tools/plugins binary.

From tools/plugins/src:

  • task build (b): cross-compile binaries into ../dist/ for macOS (amd64/arm64), Linux (amd64) and Windows (amd64).

  • task test (t): run the test suite.

Release binaries

dist/ contains prebuilt binaries for each platform, named plugins-<os>-<arch> (Windows uses .exe).