@effindomv2/fui-as 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +7 -0
- package/browser/src/common-harness/host-imports.ts +430 -0
- package/browser/src/common-harness/interop.ts +39 -0
- package/browser/src/common-harness/managed-harness-bitmap-host.ts +92 -0
- package/browser/src/common-harness/managed-harness-fetch-host.ts +201 -0
- package/browser/src/common-harness/managed-harness-file-host.ts +1101 -0
- package/browser/src/common-harness/managed-harness-file-payloads.ts +143 -0
- package/browser/src/common-harness/managed-harness-file-types.ts +106 -0
- package/browser/src/common-harness/managed-harness-session.ts +15 -0
- package/browser/src/common-harness/managed-harness.ts +1323 -0
- package/browser/src/common-harness/managed-history.ts +168 -0
- package/browser/src/common-harness/persisted-restore-policy.ts +50 -0
- package/browser/src/common-harness/persisted-ui-state-controller.ts +309 -0
- package/browser/src/common-harness/text-session-bridge.ts +452 -0
- package/browser/src/common-harness/types.ts +205 -0
- package/browser/src/common-harness/ui-chrome.ts +191 -0
- package/browser/src/common-harness/ui-imports.ts +529 -0
- package/browser/src/common-harness/wasm-module-cache.ts +47 -0
- package/browser/src/common-harness.ts +27 -0
- package/browser/src/file-processing-worker.ts +89 -0
- package/browser/src/host-events.ts +97 -0
- package/browser/src/host-services.ts +203 -0
- package/browser/src/index.ts +62 -0
- package/browser/src/persisted-ui-state.ts +206 -0
- package/browser/src/routed-harness.ts +198 -0
- package/browser/src/worker-bootstrap.ts +483 -0
- package/browser/src/worker-manager.ts +230 -0
- package/browser/src/worker-types.ts +50 -0
- package/package.json +89 -0
- package/scripts/build-demo-as.sh +91 -0
- package/scripts/build.sh +325 -0
- package/scripts/generate-host-events.ts +175 -0
- package/scripts/generate-host-services.ts +157 -0
- package/src/Fui.ts +205 -0
- package/src/FuiExports.ts +55 -0
- package/src/FuiPrimitives.ts +15 -0
- package/src/FuiWorker.ts +3 -0
- package/src/FuiWorkerExports.ts +6 -0
- package/src/bindings/ui.ts +531 -0
- package/src/color.ts +86 -0
- package/src/controls/AntiSelectionArea.ts +23 -0
- package/src/controls/Button.ts +750 -0
- package/src/controls/Checkbox.ts +181 -0
- package/src/controls/ContextMenu.ts +885 -0
- package/src/controls/ControlTemplateSet.ts +37 -0
- package/src/controls/Dialog.ts +355 -0
- package/src/controls/Dropdown.ts +856 -0
- package/src/controls/Form.ts +110 -0
- package/src/controls/NavLink.ts +211 -0
- package/src/controls/Popup.ts +129 -0
- package/src/controls/ProgressBar.ts +180 -0
- package/src/controls/RadioButton.ts +135 -0
- package/src/controls/RadioGroup.ts +244 -0
- package/src/controls/SelectionArea.ts +75 -0
- package/src/controls/Slider.ts +471 -0
- package/src/controls/Switch.ts +132 -0
- package/src/controls/TextArea.ts +20 -0
- package/src/controls/TextInput.ts +7 -0
- package/src/controls/index.ts +18 -0
- package/src/controls/internal/ButtonPresenter.ts +95 -0
- package/src/controls/internal/CheckboxIndicatorPresenter.ts +93 -0
- package/src/controls/internal/DropdownChevronPresenter.ts +67 -0
- package/src/controls/internal/DropdownFieldPresenter.ts +110 -0
- package/src/controls/internal/DropdownOptionRowPresenter.ts +82 -0
- package/src/controls/internal/PopupPresenter.ts +198 -0
- package/src/controls/internal/PressableIndicatorPresenter.ts +32 -0
- package/src/controls/internal/PressableLabeledControl.ts +221 -0
- package/src/controls/internal/RadioIndicatorPresenter.ts +73 -0
- package/src/controls/internal/SliderPresenter.ts +157 -0
- package/src/controls/internal/SwitchIndicatorPresenter.ts +72 -0
- package/src/controls/internal/TextInputCore.ts +695 -0
- package/src/controls/internal/TextInputPresenter.ts +72 -0
- package/src/controls/templating.ts +54 -0
- package/src/core/Action.ts +94 -0
- package/src/core/Actions.ts +37 -0
- package/src/core/Animation.ts +412 -0
- package/src/core/Application.ts +328 -0
- package/src/core/Assets.ts +264 -0
- package/src/core/AttachedProperties.ts +32 -0
- package/src/core/Bitmap.ts +70 -0
- package/src/core/BoundCallback.ts +104 -0
- package/src/core/Callbacks.ts +17 -0
- package/src/core/ContextMenuManager.ts +466 -0
- package/src/core/DebugApi.ts +30 -0
- package/src/core/Disposable.ts +10 -0
- package/src/core/DragDropManager.ts +179 -0
- package/src/core/DragGesture.ts +184 -0
- package/src/core/DynamicAssetIds.ts +24 -0
- package/src/core/Errors.ts +48 -0
- package/src/core/EventRouter.ts +408 -0
- package/src/core/ExternalDropManager.ts +122 -0
- package/src/core/Fetch.ts +264 -0
- package/src/core/FetchFfi.ts +15 -0
- package/src/core/File.ts +1002 -0
- package/src/core/FocusAdornerManager.ts +263 -0
- package/src/core/FocusVisibility.ts +36 -0
- package/src/core/FrameScheduler.ts +28 -0
- package/src/core/KeyboardScroll.ts +161 -0
- package/src/core/KeyboardScrollTracker.ts +386 -0
- package/src/core/Logger.ts +80 -0
- package/src/core/Navigation.ts +13 -0
- package/src/core/Node.ts +1708 -0
- package/src/core/PersistedState.ts +102 -0
- package/src/core/PersistedUiState.ts +142 -0
- package/src/core/Platform.ts +219 -0
- package/src/core/Signal.ts +89 -0
- package/src/core/Theme.ts +365 -0
- package/src/core/Timers.ts +129 -0
- package/src/core/ToolTip.ts +122 -0
- package/src/core/ToolTipManager.ts +459 -0
- package/src/core/Transitions.ts +34 -0
- package/src/core/Typography.ts +204 -0
- package/src/core/Worker.ts +196 -0
- package/src/core/bind.ts +37 -0
- package/src/core/event_exports.ts +596 -0
- package/src/core/ffi.ts +728 -0
- package/src/host-services/runtime.ts +25 -0
- package/src/nodes/FlexBox.ts +789 -0
- package/src/nodes/GradientStop.ts +9 -0
- package/src/nodes/Grid.ts +183 -0
- package/src/nodes/Image.ts +189 -0
- package/src/nodes/Portal.ts +14 -0
- package/src/nodes/RichText.ts +312 -0
- package/src/nodes/ScrollBar.ts +570 -0
- package/src/nodes/ScrollBox.ts +415 -0
- package/src/nodes/ScrollState.ts +10 -0
- package/src/nodes/ScrollView.ts +511 -0
- package/src/nodes/Svg.ts +142 -0
- package/src/nodes/Text.ts +145 -0
- package/src/nodes/TextCore.ts +558 -0
- package/src/nodes/VirtualList.ts +431 -0
- package/src/nodes/helpers.ts +25 -0
- package/src/nodes/index.ts +14 -0
- package/src/tsconfig.json +7 -0
- package/src/worker/Worker.ts +169 -0
- package/src/worker/WorkerJob.ts +65 -0
- package/src/worker/ffi.ts +23 -0
package/scripts/build.sh
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
PACKAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
6
|
+
REPO_ROOT="$(cd "${PACKAGE_DIR}/../.." && pwd)"
|
|
7
|
+
BROWSER_SRC_DIR="${PACKAGE_DIR}/browser/src"
|
|
8
|
+
SMOKE_FIXTURE_DIR="${PACKAGE_DIR}/tests/fixtures/smoke"
|
|
9
|
+
OUT_DIR="${REPO_ROOT}/public/v2/fui-as"
|
|
10
|
+
DEMO_OUT_DIR="${OUT_DIR}/demo"
|
|
11
|
+
HELLO_OUT_DIR="${OUT_DIR}/demo-hello-world"
|
|
12
|
+
WORKER_BUILD_DIR="${PACKAGE_DIR}/build/workers"
|
|
13
|
+
WORKER_BOOTSTRAP_BUILD="${PACKAGE_DIR}/build/worker-bootstrap.js"
|
|
14
|
+
WORKER_BOOTSTRAP_MAP_BUILD="${PACKAGE_DIR}/build/worker-bootstrap.js.map"
|
|
15
|
+
WORKER_HOST_SERVICES_BUILD="${PACKAGE_DIR}/build/worker-host-services.js"
|
|
16
|
+
WORKER_HOST_SERVICES_MAP_BUILD="${PACKAGE_DIR}/build/worker-host-services.js.map"
|
|
17
|
+
WORKER_MANIFEST_BUILD="${PACKAGE_DIR}/build/worker-manifest.json"
|
|
18
|
+
FILE_PROCESSING_WORKER_BUILD="${PACKAGE_DIR}/build/file-processing-worker.js"
|
|
19
|
+
FILE_PROCESSING_WORKER_MAP_BUILD="${PACKAGE_DIR}/build/file-processing-worker.js.map"
|
|
20
|
+
HOST_SERVICE_GENERATOR_BUILD="${PACKAGE_DIR}/build/generate-host-services.mjs"
|
|
21
|
+
HOST_EVENT_GENERATOR_BUILD="${PACKAGE_DIR}/build/generate-host-events.mjs"
|
|
22
|
+
RUNTIME_CONFIG_FILE="effindom-runtime-config.js"
|
|
23
|
+
DEFAULT_MANIFEST_PATH="./runtime/dist/effindom.v2.manifest.json"
|
|
24
|
+
|
|
25
|
+
rm -rf "${OUT_DIR}"
|
|
26
|
+
mkdir -p "${PACKAGE_DIR}/build" "${OUT_DIR}" "${DEMO_OUT_DIR}" "${HELLO_OUT_DIR}" "${WORKER_BUILD_DIR}"
|
|
27
|
+
|
|
28
|
+
cd "${PACKAGE_DIR}"
|
|
29
|
+
|
|
30
|
+
if [ -x "${PACKAGE_DIR}/node_modules/.bin/tsc" ]; then
|
|
31
|
+
TSC_BIN="${PACKAGE_DIR}/node_modules/.bin/tsc"
|
|
32
|
+
elif [ -x "${REPO_ROOT}/node_modules/.bin/tsc" ]; then
|
|
33
|
+
TSC_BIN="${REPO_ROOT}/node_modules/.bin/tsc"
|
|
34
|
+
else
|
|
35
|
+
echo "Could not locate tsc in node_modules/.bin." >&2
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
"${TSC_BIN}" -p tsconfig.json --noEmit
|
|
40
|
+
|
|
41
|
+
build_app() {
|
|
42
|
+
local entry_file="$1"
|
|
43
|
+
local wasm_out="$2"
|
|
44
|
+
|
|
45
|
+
npx asc "${entry_file}" --config asconfig.json --target release
|
|
46
|
+
cp "${PACKAGE_DIR}/build/app.wasm" "${wasm_out}"
|
|
47
|
+
if [ -f "${PACKAGE_DIR}/build/app.wasm.map" ]; then
|
|
48
|
+
cp "${PACKAGE_DIR}/build/app.wasm.map" "${wasm_out}.map"
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
build_worker() {
|
|
53
|
+
local entry_file="$1"
|
|
54
|
+
local wasm_out="$2"
|
|
55
|
+
|
|
56
|
+
npx asc "${entry_file}" --config asconfig.json --target release
|
|
57
|
+
cp "${PACKAGE_DIR}/build/app.wasm" "${wasm_out}"
|
|
58
|
+
if [ -f "${PACKAGE_DIR}/build/app.wasm.map" ]; then
|
|
59
|
+
cp "${PACKAGE_DIR}/build/app.wasm.map" "${wasm_out}.map"
|
|
60
|
+
fi
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
generate_host_services() {
|
|
64
|
+
local definition_file="$1"
|
|
65
|
+
local export_name="$2"
|
|
66
|
+
local output_file="$3"
|
|
67
|
+
|
|
68
|
+
npx esbuild "${PACKAGE_DIR}/scripts/generate-host-services.ts" \
|
|
69
|
+
--bundle \
|
|
70
|
+
--format=esm \
|
|
71
|
+
--platform=node \
|
|
72
|
+
--target=node20 \
|
|
73
|
+
--packages=external \
|
|
74
|
+
--outfile="${HOST_SERVICE_GENERATOR_BUILD}"
|
|
75
|
+
|
|
76
|
+
node "${HOST_SERVICE_GENERATOR_BUILD}" "${definition_file}" "${export_name}" "${output_file}"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
generate_host_events() {
|
|
80
|
+
local definition_file="$1"
|
|
81
|
+
local export_name="$2"
|
|
82
|
+
local output_file="$3"
|
|
83
|
+
|
|
84
|
+
npx esbuild "${PACKAGE_DIR}/scripts/generate-host-events.ts" \
|
|
85
|
+
--bundle \
|
|
86
|
+
--format=esm \
|
|
87
|
+
--platform=node \
|
|
88
|
+
--target=node20 \
|
|
89
|
+
--packages=external \
|
|
90
|
+
--outfile="${HOST_EVENT_GENERATOR_BUILD}"
|
|
91
|
+
|
|
92
|
+
node "${HOST_EVENT_GENERATOR_BUILD}" "${definition_file}" "${export_name}" "${output_file}"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
find_worker_entries() {
|
|
96
|
+
find \
|
|
97
|
+
"${PACKAGE_DIR}/src/workers" \
|
|
98
|
+
"${PACKAGE_DIR}/demo/src/workers" \
|
|
99
|
+
"${SMOKE_FIXTURE_DIR}/workers" \
|
|
100
|
+
-maxdepth 1 -type f -name '*.ts' 2>/dev/null | sort
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
resolve_runtime_dist_dir() {
|
|
104
|
+
local candidate=""
|
|
105
|
+
local candidates=()
|
|
106
|
+
|
|
107
|
+
if [ -n "${EFFINDOM_RUNTIME_DIST_DIR:-}" ]; then
|
|
108
|
+
candidates+=("${EFFINDOM_RUNTIME_DIST_DIR}")
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
candidates+=(
|
|
112
|
+
"${PACKAGE_DIR}/node_modules/@effindomv2/runtime/dist"
|
|
113
|
+
"${REPO_ROOT}/node_modules/@effindomv2/runtime/dist"
|
|
114
|
+
"${REPO_ROOT}/v2/browser-bridge/dist"
|
|
115
|
+
"${REPO_ROOT}/public/v2/browser-bridge"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
for candidate in "${candidates[@]}"; do
|
|
119
|
+
if [ -f "${candidate}/bridge.js" ] && [ -f "${candidate}/effindom.v2.manifest.json" ] && [ -d "${candidate}/runtime" ]; then
|
|
120
|
+
printf '%s\n' "${candidate}"
|
|
121
|
+
return 0
|
|
122
|
+
fi
|
|
123
|
+
done
|
|
124
|
+
|
|
125
|
+
echo "Could not locate runtime dist assets." >&2
|
|
126
|
+
echo "Expected one of:" >&2
|
|
127
|
+
echo " - \$EFFINDOM_RUNTIME_DIST_DIR" >&2
|
|
128
|
+
echo " - ${PACKAGE_DIR}/node_modules/@effindomv2/runtime/dist" >&2
|
|
129
|
+
echo " - ${REPO_ROOT}/node_modules/@effindomv2/runtime/dist" >&2
|
|
130
|
+
echo " - ${REPO_ROOT}/v2/browser-bridge/dist" >&2
|
|
131
|
+
echo " - ${REPO_ROOT}/public/v2/browser-bridge" >&2
|
|
132
|
+
echo "Install @effindomv2/runtime or build runtime assets first." >&2
|
|
133
|
+
exit 1
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
RUNTIME_DIST_DIR="$(resolve_runtime_dist_dir)"
|
|
137
|
+
|
|
138
|
+
write_runtime_config() {
|
|
139
|
+
local destination="$1"
|
|
140
|
+
local manifest_url="$2"
|
|
141
|
+
|
|
142
|
+
cat > "${destination}/${RUNTIME_CONFIG_FILE}" <<EOF
|
|
143
|
+
window.__effindomRuntime = Object.assign({}, window.__effindomRuntime, {
|
|
144
|
+
manifestUrl: '${manifest_url}',
|
|
145
|
+
});
|
|
146
|
+
EOF
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
copy_runtime_assets() {
|
|
150
|
+
local destination="$1"
|
|
151
|
+
cp "${RUNTIME_DIST_DIR}/bridge.js" "${destination}/bridge.js"
|
|
152
|
+
if [ -f "${RUNTIME_DIST_DIR}/bridge.js.map" ]; then
|
|
153
|
+
cp "${RUNTIME_DIST_DIR}/bridge.js.map" "${destination}/bridge.js.map"
|
|
154
|
+
else
|
|
155
|
+
rm -f "${destination}/bridge.js.map"
|
|
156
|
+
fi
|
|
157
|
+
rm -f "${destination}/effindom.v2.manifest.json" "${destination}/icu-asset.json"
|
|
158
|
+
rm -rf "${destination}/runtime"
|
|
159
|
+
mkdir -p "${destination}/runtime/dist"
|
|
160
|
+
cp "${RUNTIME_DIST_DIR}/effindom.v2.manifest.json" "${destination}/runtime/dist/effindom.v2.manifest.json"
|
|
161
|
+
if [ -f "${RUNTIME_DIST_DIR}/icu-asset.json" ]; then
|
|
162
|
+
cp "${RUNTIME_DIST_DIR}/icu-asset.json" "${destination}/runtime/dist/icu-asset.json"
|
|
163
|
+
fi
|
|
164
|
+
cp -R "${RUNTIME_DIST_DIR}/runtime" "${destination}/runtime/dist/runtime"
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
build_workers() {
|
|
168
|
+
rm -rf "${WORKER_BUILD_DIR}"
|
|
169
|
+
mkdir -p "${WORKER_BUILD_DIR}"
|
|
170
|
+
|
|
171
|
+
local worker_entry=""
|
|
172
|
+
while IFS= read -r worker_entry; do
|
|
173
|
+
[ -n "${worker_entry}" ] || continue
|
|
174
|
+
local worker_name
|
|
175
|
+
worker_name="$(basename "${worker_entry}" .ts)"
|
|
176
|
+
build_worker "${worker_entry}" "${WORKER_BUILD_DIR}/${worker_name}.wasm"
|
|
177
|
+
done < <(find_worker_entries)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
write_worker_manifest() {
|
|
181
|
+
declare -A worker_entries=()
|
|
182
|
+
local worker_entry=""
|
|
183
|
+
while IFS= read -r worker_entry; do
|
|
184
|
+
[ -n "${worker_entry}" ] || continue
|
|
185
|
+
local worker_name
|
|
186
|
+
worker_name="$(basename "${worker_entry}" .ts)"
|
|
187
|
+
local export_name=""
|
|
188
|
+
while IFS= read -r export_name; do
|
|
189
|
+
[ -n "${export_name}" ] || continue
|
|
190
|
+
if [ -n "${worker_entries[$export_name]:-}" ]; then
|
|
191
|
+
echo "Duplicate worker export name: ${export_name}" >&2
|
|
192
|
+
exit 1
|
|
193
|
+
fi
|
|
194
|
+
worker_entries["${export_name}"]="./workers/${worker_name}.wasm"
|
|
195
|
+
done < <(rg 'export function ([A-Za-z_][A-Za-z0-9_]*)\s*\(' "${worker_entry}" -or '$1')
|
|
196
|
+
done < <(find_worker_entries)
|
|
197
|
+
|
|
198
|
+
{
|
|
199
|
+
printf '{\n "version": 1,\n "entries": {\n'
|
|
200
|
+
local first=1
|
|
201
|
+
local entry_name=""
|
|
202
|
+
while IFS= read -r entry_name; do
|
|
203
|
+
[ -n "${entry_name}" ] || continue
|
|
204
|
+
if [ "${first}" -eq 0 ]; then
|
|
205
|
+
printf ',\n'
|
|
206
|
+
fi
|
|
207
|
+
first=0
|
|
208
|
+
printf ' "%s": "%s"' "${entry_name}" "${worker_entries[$entry_name]}"
|
|
209
|
+
done < <(printf '%s\n' "${!worker_entries[@]}" | sort)
|
|
210
|
+
printf '\n }\n}\n'
|
|
211
|
+
} > "${WORKER_MANIFEST_BUILD}"
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
copy_worker_assets() {
|
|
215
|
+
local destination="$1"
|
|
216
|
+
rm -rf "${destination}/workers"
|
|
217
|
+
mkdir -p "${destination}/workers"
|
|
218
|
+
shopt -s nullglob
|
|
219
|
+
local worker_asset=""
|
|
220
|
+
for worker_asset in "${WORKER_BUILD_DIR}"/*; do
|
|
221
|
+
cp "${worker_asset}" "${destination}/workers/$(basename "${worker_asset}")"
|
|
222
|
+
done
|
|
223
|
+
shopt -u nullglob
|
|
224
|
+
cp "${WORKER_BOOTSTRAP_BUILD}" "${destination}/worker-bootstrap.js"
|
|
225
|
+
cp "${WORKER_BOOTSTRAP_MAP_BUILD}" "${destination}/worker-bootstrap.js.map"
|
|
226
|
+
cp "${WORKER_HOST_SERVICES_BUILD}" "${destination}/worker-host-services.js"
|
|
227
|
+
cp "${WORKER_HOST_SERVICES_MAP_BUILD}" "${destination}/worker-host-services.js.map"
|
|
228
|
+
cp "${WORKER_MANIFEST_BUILD}" "${destination}/worker-manifest.json"
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
generate_host_services "demo/src/host-services.ts" "demoHostServices" "demo/src/generated/HostServices.ts"
|
|
232
|
+
generate_host_events "demo/src/host-events.ts" "demoHostEvents" "demo/src/generated/HostEvents.ts"
|
|
233
|
+
generate_host_services "demo/src/worker-host-services.ts" "demoWorkerHostServices" "demo/src/generated/WorkerHostServices.ts"
|
|
234
|
+
|
|
235
|
+
build_app "tests/fixtures/smoke/app.ts" "${OUT_DIR}/app.wasm"
|
|
236
|
+
build_app "demo/src/dashboard.ts" "${DEMO_OUT_DIR}/demo.wasm"
|
|
237
|
+
build_app "demo/src/routes/demo_home.ts" "${DEMO_OUT_DIR}/home.wasm"
|
|
238
|
+
build_app "demo/src/routes/demo_advanced_controls.ts" "${DEMO_OUT_DIR}/advanced-controls.wasm"
|
|
239
|
+
build_app "demo/src/routes/templated-controls.ts" "${DEMO_OUT_DIR}/templated-controls.wasm"
|
|
240
|
+
build_app "demo-hello-world/src/App.ts" "${HELLO_OUT_DIR}/app.wasm"
|
|
241
|
+
build_workers
|
|
242
|
+
write_worker_manifest
|
|
243
|
+
|
|
244
|
+
npx esbuild "${SMOKE_FIXTURE_DIR}/harness.ts" \
|
|
245
|
+
--bundle \
|
|
246
|
+
--format=esm \
|
|
247
|
+
--platform=browser \
|
|
248
|
+
--target=es2020 \
|
|
249
|
+
--minify \
|
|
250
|
+
--outfile="${OUT_DIR}/harness.js" \
|
|
251
|
+
--sourcemap
|
|
252
|
+
|
|
253
|
+
npx esbuild "${PACKAGE_DIR}/demo/harness.ts" \
|
|
254
|
+
--bundle \
|
|
255
|
+
--format=esm \
|
|
256
|
+
--platform=browser \
|
|
257
|
+
--target=es2020 \
|
|
258
|
+
--minify \
|
|
259
|
+
--outfile="${DEMO_OUT_DIR}/harness.js" \
|
|
260
|
+
--sourcemap
|
|
261
|
+
|
|
262
|
+
npx esbuild "${PACKAGE_DIR}/demo-hello-world/harness.ts" \
|
|
263
|
+
--bundle \
|
|
264
|
+
--format=esm \
|
|
265
|
+
--platform=browser \
|
|
266
|
+
--target=es2020 \
|
|
267
|
+
--minify \
|
|
268
|
+
--outfile="${HELLO_OUT_DIR}/harness.js" \
|
|
269
|
+
--sourcemap
|
|
270
|
+
|
|
271
|
+
npx esbuild "${BROWSER_SRC_DIR}/file-processing-worker.ts" \
|
|
272
|
+
--bundle \
|
|
273
|
+
--format=iife \
|
|
274
|
+
--platform=browser \
|
|
275
|
+
--target=es2020 \
|
|
276
|
+
--minify \
|
|
277
|
+
--outfile="${FILE_PROCESSING_WORKER_BUILD}" \
|
|
278
|
+
--sourcemap
|
|
279
|
+
|
|
280
|
+
cp "${FILE_PROCESSING_WORKER_BUILD}" "${OUT_DIR}/file-processing-worker.js"
|
|
281
|
+
cp "${FILE_PROCESSING_WORKER_MAP_BUILD}" "${OUT_DIR}/file-processing-worker.js.map"
|
|
282
|
+
cp "${FILE_PROCESSING_WORKER_BUILD}" "${DEMO_OUT_DIR}/file-processing-worker.js"
|
|
283
|
+
cp "${FILE_PROCESSING_WORKER_MAP_BUILD}" "${DEMO_OUT_DIR}/file-processing-worker.js.map"
|
|
284
|
+
cp "${FILE_PROCESSING_WORKER_BUILD}" "${HELLO_OUT_DIR}/file-processing-worker.js"
|
|
285
|
+
cp "${FILE_PROCESSING_WORKER_MAP_BUILD}" "${HELLO_OUT_DIR}/file-processing-worker.js.map"
|
|
286
|
+
|
|
287
|
+
npx esbuild "${BROWSER_SRC_DIR}/worker-bootstrap.ts" \
|
|
288
|
+
--bundle \
|
|
289
|
+
--format=iife \
|
|
290
|
+
--platform=browser \
|
|
291
|
+
--target=es2020 \
|
|
292
|
+
--minify \
|
|
293
|
+
--outfile="${WORKER_BOOTSTRAP_BUILD}" \
|
|
294
|
+
--sourcemap
|
|
295
|
+
|
|
296
|
+
npx esbuild "${PACKAGE_DIR}/demo/worker-host-services.ts" \
|
|
297
|
+
--bundle \
|
|
298
|
+
--format=iife \
|
|
299
|
+
--platform=browser \
|
|
300
|
+
--target=es2020 \
|
|
301
|
+
--minify \
|
|
302
|
+
--outfile="${WORKER_HOST_SERVICES_BUILD}" \
|
|
303
|
+
--sourcemap
|
|
304
|
+
|
|
305
|
+
cp "${SMOKE_FIXTURE_DIR}/index.html" "${OUT_DIR}/index.html"
|
|
306
|
+
cp "${PACKAGE_DIR}/demo/index.html" "${DEMO_OUT_DIR}/index.html"
|
|
307
|
+
cp "${PACKAGE_DIR}/demo-hello-world/index.html" "${HELLO_OUT_DIR}/index.html"
|
|
308
|
+
cp "${PACKAGE_DIR}/demo/demo-texture.png" "${DEMO_OUT_DIR}/demo-texture.png"
|
|
309
|
+
cp "${PACKAGE_DIR}/demo/demo-secondary-texture.png" "${DEMO_OUT_DIR}/demo-secondary-texture.png"
|
|
310
|
+
|
|
311
|
+
mkdir -p "${DEMO_OUT_DIR}/advanced-controls" "${DEMO_OUT_DIR}/templated-controls"
|
|
312
|
+
cp "${PACKAGE_DIR}/demo/route-shell.html" "${DEMO_OUT_DIR}/advanced-controls/index.html"
|
|
313
|
+
cp "${PACKAGE_DIR}/demo/route-shell.html" "${DEMO_OUT_DIR}/templated-controls/index.html"
|
|
314
|
+
|
|
315
|
+
copy_runtime_assets "${OUT_DIR}"
|
|
316
|
+
copy_runtime_assets "${DEMO_OUT_DIR}"
|
|
317
|
+
copy_runtime_assets "${HELLO_OUT_DIR}"
|
|
318
|
+
copy_worker_assets "${OUT_DIR}"
|
|
319
|
+
copy_worker_assets "${DEMO_OUT_DIR}"
|
|
320
|
+
copy_worker_assets "${HELLO_OUT_DIR}"
|
|
321
|
+
write_runtime_config "${OUT_DIR}" "${DEFAULT_MANIFEST_PATH}"
|
|
322
|
+
write_runtime_config "${DEMO_OUT_DIR}" "${DEFAULT_MANIFEST_PATH}"
|
|
323
|
+
write_runtime_config "${HELLO_OUT_DIR}" "${DEFAULT_MANIFEST_PATH}"
|
|
324
|
+
write_runtime_config "${DEMO_OUT_DIR}/advanced-controls" "../runtime/dist/effindom.v2.manifest.json"
|
|
325
|
+
write_runtime_config "${DEMO_OUT_DIR}/templated-controls" "../runtime/dist/effindom.v2.manifest.json"
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { build } from "esbuild";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
+
import { listHostEventMethods, type NormalizedHostEventMethod } from "../browser/src/host-events";
|
|
7
|
+
import type { HostServiceTypeName } from "../browser/src/host-services";
|
|
8
|
+
|
|
9
|
+
const SCRIPT_PATH = fileURLToPath(import.meta.url);
|
|
10
|
+
const PACKAGE_DIR = path.resolve(path.dirname(SCRIPT_PATH), "..");
|
|
11
|
+
|
|
12
|
+
function toPosix(value: string): string {
|
|
13
|
+
return value.split(path.sep).join("/");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function relativeImport(fromFile: string, targetFile: string): string {
|
|
17
|
+
let relative = path.relative(path.dirname(fromFile), targetFile);
|
|
18
|
+
relative = relative.replace(/\.[^.]+$/, "");
|
|
19
|
+
if (!relative.startsWith(".")) {
|
|
20
|
+
relative = `./${relative}`;
|
|
21
|
+
}
|
|
22
|
+
return toPosix(relative);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function asTypeName(type: HostServiceTypeName): string {
|
|
26
|
+
switch (type) {
|
|
27
|
+
case "string":
|
|
28
|
+
return "string";
|
|
29
|
+
case "bool":
|
|
30
|
+
return "bool";
|
|
31
|
+
case "i32":
|
|
32
|
+
return "i32";
|
|
33
|
+
case "f64":
|
|
34
|
+
return "f64";
|
|
35
|
+
case "void":
|
|
36
|
+
return "void";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function callbackTypeFor(method: NormalizedHostEventMethod): string {
|
|
41
|
+
if (method.args.length == 0) {
|
|
42
|
+
return "Callback0";
|
|
43
|
+
}
|
|
44
|
+
if (method.args.length == 1) {
|
|
45
|
+
return `Callback1<${asTypeName(method.args[0])}>`;
|
|
46
|
+
}
|
|
47
|
+
if (method.args.length == 2) {
|
|
48
|
+
return `Callback2<${asTypeName(method.args[0])}, ${asTypeName(method.args[1])}>`;
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`Host event ${method.serviceName}.${method.methodName} uses ${String(method.args.length)} args; only 0-2 are supported right now.`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function emitExportArgs(method: NormalizedHostEventMethod): string {
|
|
54
|
+
const parts: Array<string> = [];
|
|
55
|
+
method.args.forEach((type, index) => {
|
|
56
|
+
if (type === "string") {
|
|
57
|
+
parts.push(`arg${String(index)}Ptr: usize`, `arg${String(index)}Len: u32`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
parts.push(`arg${String(index)}: ${asTypeName(type)}`);
|
|
61
|
+
});
|
|
62
|
+
return parts.join(", ");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function emitDecodedArgs(method: NormalizedHostEventMethod): Array<string> {
|
|
66
|
+
const lines: Array<string> = [];
|
|
67
|
+
method.args.forEach((type, index) => {
|
|
68
|
+
if (type !== "string") {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
lines.push(
|
|
72
|
+
` const arg${String(index)} = arg${String(index)}Len == 0 ? "" : String.UTF8.decodeUnsafe(arg${String(index)}Ptr, <usize>arg${String(index)}Len, false);`,
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
return lines;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function emitCallbackInvokeArgs(method: NormalizedHostEventMethod): string {
|
|
79
|
+
return method.args
|
|
80
|
+
.map((_type, index) => method.args[index] === "string" ? `arg${String(index)}` : `arg${String(index)}`)
|
|
81
|
+
.join(", ");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function emitHandlerBlock(method: NormalizedHostEventMethod): string {
|
|
85
|
+
const eventName = method.eventName;
|
|
86
|
+
const publicName = eventName.length == 0 ? eventName : `${eventName[0].toUpperCase()}${eventName.slice(1)}`;
|
|
87
|
+
const callbackType = callbackTypeFor(method);
|
|
88
|
+
const exportArgs = emitExportArgs(method);
|
|
89
|
+
const decodedArgs = emitDecodedArgs(method);
|
|
90
|
+
const invokeArgs = emitCallbackInvokeArgs(method);
|
|
91
|
+
const directSignature = `${callbackType} | null`;
|
|
92
|
+
return [
|
|
93
|
+
`let __${eventName}Handler: ${directSignature} = null;`,
|
|
94
|
+
"",
|
|
95
|
+
`export function on${publicName}(callback: ${directSignature}): void {`,
|
|
96
|
+
` __${eventName}Handler = callback;`,
|
|
97
|
+
`}`,
|
|
98
|
+
"",
|
|
99
|
+
`export function clear${publicName}(): void {`,
|
|
100
|
+
` __${eventName}Handler = null;`,
|
|
101
|
+
`}`,
|
|
102
|
+
"",
|
|
103
|
+
`export function ${method.exportName}(${exportArgs}): void {`,
|
|
104
|
+
` const callback = __${eventName}Handler;`,
|
|
105
|
+
` if (callback === null) {`,
|
|
106
|
+
` return;`,
|
|
107
|
+
` }`,
|
|
108
|
+
...decodedArgs,
|
|
109
|
+
method.args.length == 0
|
|
110
|
+
? ` callback.invoke();`
|
|
111
|
+
: ` callback.invoke(${invokeArgs});`,
|
|
112
|
+
`}`,
|
|
113
|
+
].join("\n");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function loadHostEvents(modulePath: string, exportName: string): Promise<unknown> {
|
|
117
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "fui-host-events-"));
|
|
118
|
+
const bundledFile = path.join(tempDir, "host-events.mjs");
|
|
119
|
+
try {
|
|
120
|
+
await build({
|
|
121
|
+
entryPoints: [modulePath],
|
|
122
|
+
outfile: bundledFile,
|
|
123
|
+
bundle: true,
|
|
124
|
+
format: "esm",
|
|
125
|
+
platform: "node",
|
|
126
|
+
target: "node20",
|
|
127
|
+
logLevel: "silent",
|
|
128
|
+
});
|
|
129
|
+
const loaded = await import(pathToFileURL(bundledFile).href);
|
|
130
|
+
if (!(exportName in loaded)) {
|
|
131
|
+
throw new Error(`Host-events module does not export "${exportName}".`);
|
|
132
|
+
}
|
|
133
|
+
return loaded[exportName as keyof typeof loaded];
|
|
134
|
+
} finally {
|
|
135
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function emitBindingsFile(
|
|
140
|
+
sourceModulePath: string,
|
|
141
|
+
exportName: string,
|
|
142
|
+
outputPath: string,
|
|
143
|
+
methods: ReturnType<typeof listHostEventMethods>,
|
|
144
|
+
): string {
|
|
145
|
+
const callbackImport = relativeImport(outputPath, path.resolve(PACKAGE_DIR, "src/FuiPrimitives.ts"));
|
|
146
|
+
const blocks: Array<string> = [
|
|
147
|
+
`// Generated by scripts/generate-host-events.ts from ${toPosix(sourceModulePath)}#${exportName}.`,
|
|
148
|
+
`import { Callback0, Callback1, Callback2 } from "${callbackImport}";`,
|
|
149
|
+
"",
|
|
150
|
+
];
|
|
151
|
+
methods.forEach((method, index) => {
|
|
152
|
+
blocks.push(emitHandlerBlock(method));
|
|
153
|
+
if (index + 1 < methods.length) {
|
|
154
|
+
blocks.push("");
|
|
155
|
+
blocks.push("");
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
return `${blocks.join("\n")}\n`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function main(): Promise<void> {
|
|
162
|
+
const [moduleArg, exportName, outputArg] = process.argv.slice(2);
|
|
163
|
+
if (moduleArg === undefined || exportName === undefined || outputArg === undefined) {
|
|
164
|
+
throw new Error("Usage: generate-host-events <module-path> <export-name> <output-path>");
|
|
165
|
+
}
|
|
166
|
+
const modulePath = path.resolve(process.cwd(), moduleArg);
|
|
167
|
+
const outputPath = path.resolve(process.cwd(), outputArg);
|
|
168
|
+
const registry = await loadHostEvents(modulePath, exportName);
|
|
169
|
+
const methods = listHostEventMethods(registry as never);
|
|
170
|
+
const content = emitBindingsFile(modulePath, exportName, outputPath, methods);
|
|
171
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
172
|
+
await fs.writeFile(outputPath, content, "utf8");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
await main();
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { build } from "esbuild";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
+
import { listHostServiceMethods, type HostServiceTypeName } from "../browser/src/host-services";
|
|
7
|
+
|
|
8
|
+
const SCRIPT_PATH = fileURLToPath(import.meta.url);
|
|
9
|
+
const PACKAGE_DIR = path.resolve(path.dirname(SCRIPT_PATH), "..");
|
|
10
|
+
|
|
11
|
+
function toPosix(value: string): string {
|
|
12
|
+
return value.split(path.sep).join("/");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function relativeImport(fromFile: string, targetFile: string): string {
|
|
16
|
+
let relative = path.relative(path.dirname(fromFile), targetFile);
|
|
17
|
+
relative = relative.replace(/\.[^.]+$/, "");
|
|
18
|
+
if (!relative.startsWith(".")) {
|
|
19
|
+
relative = `./${relative}`;
|
|
20
|
+
}
|
|
21
|
+
return toPosix(relative);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function asTypeName(type: HostServiceTypeName): string {
|
|
25
|
+
switch (type) {
|
|
26
|
+
case "string":
|
|
27
|
+
return "string";
|
|
28
|
+
case "bool":
|
|
29
|
+
return "bool";
|
|
30
|
+
case "i32":
|
|
31
|
+
return "i32";
|
|
32
|
+
case "f64":
|
|
33
|
+
return "f64";
|
|
34
|
+
case "void":
|
|
35
|
+
return "void";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function emitExternalSignature(importName: string, args: readonly HostServiceTypeName[], returns: HostServiceTypeName): string {
|
|
40
|
+
const signatureParts: Array<string> = [];
|
|
41
|
+
args.forEach((type, index) => {
|
|
42
|
+
if (type === "string") {
|
|
43
|
+
signatureParts.push(`arg${String(index)}Ptr: usize`, `arg${String(index)}Len: u32`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
signatureParts.push(`arg${String(index)}: ${asTypeName(type)}`);
|
|
47
|
+
});
|
|
48
|
+
if (returns === "string") {
|
|
49
|
+
signatureParts.push("resultPtr: usize", "resultCap: u32");
|
|
50
|
+
}
|
|
51
|
+
const returnType = returns === "string" ? "u32" : asTypeName(returns);
|
|
52
|
+
return [
|
|
53
|
+
`@external("fui_host_service", "${importName}")`,
|
|
54
|
+
`declare function __host_${importName}(${signatureParts.join(", ")}): ${returnType};`,
|
|
55
|
+
].join("\n");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function emitWrapper(importName: string, args: readonly HostServiceTypeName[], returns: HostServiceTypeName): string {
|
|
59
|
+
const wrapperArgs = args.map((type, index) => `arg${String(index)}: ${asTypeName(type)}`);
|
|
60
|
+
const lines: Array<string> = [];
|
|
61
|
+
args.forEach((type, index) => {
|
|
62
|
+
if (type === "string") {
|
|
63
|
+
lines.push(` const arg${String(index)}Bytes = Uint8Array.wrap(String.UTF8.encode(arg${String(index)}, false));`);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const callArgs: Array<string> = [];
|
|
67
|
+
args.forEach((type, index) => {
|
|
68
|
+
if (type === "string") {
|
|
69
|
+
callArgs.push(`arg${String(index)}Bytes.length > 0 ? arg${String(index)}Bytes.dataStart : 0`);
|
|
70
|
+
callArgs.push(`<u32>arg${String(index)}Bytes.length`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
callArgs.push(`arg${String(index)}`);
|
|
74
|
+
});
|
|
75
|
+
if (returns === "string") {
|
|
76
|
+
lines.push(" const resultPtr = hostServiceResultBufferPtr();");
|
|
77
|
+
lines.push(" const resultCap = hostServiceResultBufferSize();");
|
|
78
|
+
callArgs.push("resultPtr", "resultCap");
|
|
79
|
+
lines.push(` const resultLen = __host_${importName}(${callArgs.join(", ")});`);
|
|
80
|
+
lines.push(` return decodeHostServiceStringResult(resultPtr, resultLen, "${importName}");`);
|
|
81
|
+
} else if (returns === "void") {
|
|
82
|
+
lines.push(` __host_${importName}(${callArgs.join(", ")});`);
|
|
83
|
+
} else {
|
|
84
|
+
lines.push(` return __host_${importName}(${callArgs.join(", ")});`);
|
|
85
|
+
}
|
|
86
|
+
return [
|
|
87
|
+
`export function ${importName}(${wrapperArgs.join(", ")}): ${returns === "string" ? "string" : asTypeName(returns)} {`,
|
|
88
|
+
...lines,
|
|
89
|
+
"}",
|
|
90
|
+
].join("\n");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function loadHostServices(modulePath: string, exportName: string): Promise<unknown> {
|
|
94
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "fui-host-services-"));
|
|
95
|
+
const bundledFile = path.join(tempDir, "host-services.mjs");
|
|
96
|
+
try {
|
|
97
|
+
await build({
|
|
98
|
+
entryPoints: [modulePath],
|
|
99
|
+
outfile: bundledFile,
|
|
100
|
+
bundle: true,
|
|
101
|
+
format: "esm",
|
|
102
|
+
platform: "node",
|
|
103
|
+
target: "node20",
|
|
104
|
+
logLevel: "silent",
|
|
105
|
+
});
|
|
106
|
+
const loaded = await import(pathToFileURL(bundledFile).href);
|
|
107
|
+
if (!(exportName in loaded)) {
|
|
108
|
+
throw new Error(`Host-services module does not export "${exportName}".`);
|
|
109
|
+
}
|
|
110
|
+
return loaded[exportName as keyof typeof loaded];
|
|
111
|
+
} finally {
|
|
112
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function emitBindingsFile(
|
|
117
|
+
sourceModulePath: string,
|
|
118
|
+
exportName: string,
|
|
119
|
+
outputPath: string,
|
|
120
|
+
methods: ReturnType<typeof listHostServiceMethods>,
|
|
121
|
+
): string {
|
|
122
|
+
const runtimeImport = relativeImport(outputPath, path.resolve(PACKAGE_DIR, "src/FuiPrimitives.ts"));
|
|
123
|
+
const blocks: Array<string> = [
|
|
124
|
+
`// Generated by scripts/generate-host-services.ts from ${toPosix(sourceModulePath)}#${exportName}.`,
|
|
125
|
+
];
|
|
126
|
+
if (methods.some((method) => method.returns === "string")) {
|
|
127
|
+
blocks.push(`import { decodeHostServiceStringResult, hostServiceResultBufferPtr, hostServiceResultBufferSize } from "${runtimeImport}";`);
|
|
128
|
+
blocks.push("");
|
|
129
|
+
} else {
|
|
130
|
+
blocks.push("");
|
|
131
|
+
}
|
|
132
|
+
methods.forEach((method, index) => {
|
|
133
|
+
blocks.push(emitExternalSignature(method.importName, method.args, method.returns));
|
|
134
|
+
blocks.push("");
|
|
135
|
+
blocks.push(emitWrapper(method.importName, method.args, method.returns));
|
|
136
|
+
if (index + 1 < methods.length) {
|
|
137
|
+
blocks.push("");
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return `${blocks.join("\n")}\n`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function main(): Promise<void> {
|
|
144
|
+
const [moduleArg, exportName, outputArg] = process.argv.slice(2);
|
|
145
|
+
if (moduleArg === undefined || exportName === undefined || outputArg === undefined) {
|
|
146
|
+
throw new Error("Usage: generate-host-services <module-path> <export-name> <output-path>");
|
|
147
|
+
}
|
|
148
|
+
const modulePath = path.resolve(process.cwd(), moduleArg);
|
|
149
|
+
const outputPath = path.resolve(process.cwd(), outputArg);
|
|
150
|
+
const registry = await loadHostServices(modulePath, exportName);
|
|
151
|
+
const methods = listHostServiceMethods(registry as never);
|
|
152
|
+
const content = emitBindingsFile(modulePath, exportName, outputPath, methods);
|
|
153
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
154
|
+
await fs.writeFile(outputPath, content, "utf8");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
await main();
|