atproto blogging
1#!/usr/bin/env bash
2set -euo pipefail
3
4SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5cd "$SCRIPT_DIR"
6
7PKG_NAME="@weaver.sh/editor"
8PKG_VERSION="0.1.1"
9
10# Targets to build
11TARGETS=(bundler web nodejs deno)
12
13COMMAND="${1:-build}"
14shift || true
15
16# Feature variants
17declare -A VARIANTS=(
18 ["core"]=""
19 ["collab"]="collab"
20)
21
22build() {
23 local target="$1"
24 local variant="$2"
25 local features="$3"
26 local out_dir="pkg/${variant}/${target}"
27
28 echo "Building ${variant}/${target}..."
29
30 local feature_args="--no-default-features"
31 if [[ -n "$features" ]]; then
32 feature_args="$feature_args --features $features"
33 fi
34
35 wasm-pack build \
36 --out-name weaver_editor \
37 --out-dir "$out_dir" \
38 --target "$target" \
39 $feature_args
40
41 # Report size
42 local wasm_file="${out_dir}/weaver_editor_bg.wasm"
43 if [[ -f "$wasm_file" ]]; then
44 local size=$(ls -lh "$wasm_file" | awk '{print $5}')
45 echo " → ${size}"
46 fi
47}
48
49generate_package_json() {
50 local variant="$1"
51 local out_dir="pkg/${variant}"
52 local pkg_suffix=""
53 local description=""
54
55 if [[ "$variant" == "collab" ]]; then
56 pkg_suffix="-collab"
57 description="Weaver markdown editor with collaborative editing (Loro CRDT + iroh P2P)"
58 else
59 pkg_suffix="-core"
60 description="Weaver markdown editor (local editing, lightweight)"
61 fi
62
63 # Worker export only for collab variant
64 local worker_export=""
65 local worker_files=""
66 if [[ "$variant" == "collab" ]]; then
67 worker_export=',
68 "./worker": {
69 "import": "./worker/editor_worker.js"
70 }'
71 worker_files=',
72 "worker/"'
73 fi
74
75 cat > "${out_dir}/package.json" << EOF
76{
77 "name": "${PKG_NAME}${pkg_suffix}",
78 "version": "${PKG_VERSION}",
79 "description": "${description}",
80 "license": "MPL-2.0",
81 "repository": {
82 "type": "git",
83 "url": "https://tangled.org/nonbinary.computer/weaver"
84 },
85 "keywords": ["atproto", "markdown", "editor", "wasm", "weaver"],
86 "main": "index.js",
87 "module": "index.js",
88 "types": "index.d.ts",
89 "exports": {
90 ".": {
91 "import": "./index.js",
92 "types": "./index.d.ts"
93 },
94 "./types": {
95 "import": "./types.js",
96 "types": "./types.d.ts"
97 },
98 "./wasm/bundler": {
99 "import": "./bundler/weaver_editor.js",
100 "types": "./bundler/weaver_editor.d.ts"
101 },
102 "./wasm/web": {
103 "import": "./web/weaver_editor.js",
104 "types": "./web/weaver_editor.d.ts"
105 },
106 "./wasm/nodejs": {
107 "import": "./nodejs/weaver_editor.js",
108 "require": "./nodejs/weaver_editor.js",
109 "types": "./nodejs/weaver_editor.d.ts"
110 },
111 "./wasm/deno": {
112 "import": "./deno/weaver_editor.js",
113 "types": "./deno/weaver_editor.d.ts"
114 },
115 "./weaver-editor.css": "./weaver-editor.css"${worker_export}
116 },
117 "files": [
118 "index.js",
119 "index.d.ts",
120 "types.js",
121 "types.d.ts",
122 "weaver-editor.css",
123 "bundler/",
124 "web/",
125 "nodejs/",
126 "deno/",
127 "README.md"${worker_files}
128 ]
129}
130EOF
131}
132
133generate_readme() {
134 local variant="$1"
135 local out_dir="pkg/${variant}"
136
137 cat > "${out_dir}/README.md" << 'EOF'
138# @weaver.sh/editor
139
140WASM-based markdown editor for weaver.sh.
141
142## Installation
143
144```bash
145npm install @weaver.sh/editor-core # Local editing only
146npm install @weaver.sh/editor-collab # With collaborative editing
147```
148
149## Usage
150
151### With a bundler (webpack, vite, etc.)
152
153```javascript
154import init, { JsEditor } from '@weaver.sh/editor-core';
155
156await init();
157
158const editor = JsEditor.fromMarkdown('# Hello\n\nWorld');
159console.log(editor.getMarkdown());
160```
161
162### Direct browser usage (no bundler)
163
164```html
165<script type="module">
166 import init, { JsEditor } from '@weaver.sh/editor-core/web';
167 await init();
168 // ...
169</script>
170```
171
172### Node.js
173
174```javascript
175const { JsEditor } = require('@weaver.sh/editor-core/nodejs');
176```
177
178## API
179
180See the TypeScript definitions for full API documentation.
181
182### Core
183
184- `JsEditor.new()` - Create empty editor
185- `JsEditor.fromMarkdown(content)` - Create from markdown
186- `JsEditor.fromSnapshot(entry)` - Create from EntryJson snapshot
187- `editor.getMarkdown()` - Get markdown content
188- `editor.getSnapshot()` - Get EntryJson for drafts
189- `editor.toEntry()` - Get validated EntryJson for publishing
190- `editor.executeAction(action)` - Execute an EditorAction
191- `editor.setTitle(title)` / `editor.setPath(path)` / `editor.setTags(tags)`
192
193### Images
194
195- `editor.addPendingImage(image)` - Track pending upload
196- `editor.finalizeImage(localId, finalized)` - Mark upload complete
197- `editor.getPendingImages()` - Get images awaiting upload
198- `editor.getStagingUris()` - Get staging record URIs for cleanup
199
200### Collab (editor-collab only)
201
202- `JsCollabEditor` - Collaborative editor with Loro CRDT
203- `editor.exportUpdates()` / `editor.importUpdates(bytes)`
204- `editor.addPeer(nodeId)` / `editor.removePeer(nodeId)`
205EOF
206}
207
208build_worker() {
209 echo "Building editor worker WASM..."
210
211 # Build the worker binary from weaver-editor-crdt
212 # Must be in workspace root for cargo to find the crate
213 local workspace_root="$(cd ../.. && pwd)"
214
215 export RUSTFLAGS='--cfg getrandom_backend="wasm_js"'
216
217 (cd "$workspace_root" && cargo build \
218 -p weaver-editor-crdt \
219 --bin editor_worker \
220 --target wasm32-unknown-unknown \
221 --release \
222 --features collab)
223
224 # Create worker output directory
225 local worker_out="pkg/collab/worker"
226 mkdir -p "$worker_out"
227
228 # Run wasm-bindgen with no-modules target for web worker compatibility
229 wasm-bindgen \
230 "$workspace_root/target/wasm32-unknown-unknown/release/editor_worker.wasm" \
231 --out-dir "$worker_out" \
232 --target no-modules \
233 --no-typescript
234
235 # Report size
236 local wasm_file="${worker_out}/editor_worker_bg.wasm"
237 if [[ -f "$wasm_file" ]]; then
238 local size=$(ls -lh "$wasm_file" | awk '{print $5}')
239 echo " → Worker WASM: ${size}"
240 fi
241}
242
243build_typescript() {
244 echo "Building TypeScript wrapper..."
245
246 # Install deps if needed
247 if [[ ! -d "ts/node_modules" ]]; then
248 (cd ts && npm install)
249 fi
250
251 # Link WASM output so TypeScript can find it during compilation
252 # Use collab/bundler as source - it has all exports (JsCollabEditor + JsEditor)
253 # Core variant users who import collab will get runtime error, which is expected
254 rm -rf ts/bundler
255 ln -s ../pkg/collab/bundler ts/bundler
256
257 # Compile TypeScript
258 (cd ts && npm run build)
259
260 # Copy to pkg variants
261 for variant in "${!VARIANTS[@]}"; do
262 local out_dir="pkg/${variant}"
263
264 # Copy compiled JS/TS
265 cp -r ts/dist/* "${out_dir}/"
266
267 # Copy CSS
268 cp ts/weaver-editor.css "${out_dir}/"
269 done
270
271 echo " → TypeScript wrapper built"
272}
273
274do_build() {
275 # Clean previous builds
276 rm -rf pkg
277
278 # Build all combinations
279 for variant in "${!VARIANTS[@]}"; do
280 features="${VARIANTS[$variant]}"
281
282 for target in "${TARGETS[@]}"; do
283 build "$target" "$variant" "$features"
284 done
285
286 generate_package_json "$variant"
287 generate_readme "$variant"
288
289 # Clean up wasm-pack artifacts we don't need
290 find "pkg/${variant}" -name ".gitignore" -delete
291 find "pkg/${variant}" -name "package.json" -path "*/bundler/*" -delete
292 find "pkg/${variant}" -name "package.json" -path "*/web/*" -delete
293 find "pkg/${variant}" -name "package.json" -path "*/nodejs/*" -delete
294 find "pkg/${variant}" -name "package.json" -path "*/deno/*" -delete
295 done
296
297 # Build worker WASM for collab variant
298 build_worker
299
300 # Build TypeScript wrapper
301 build_typescript
302
303 echo ""
304 echo "Build complete!"
305 echo ""
306 echo "Editor WASM:"
307 ls -lh pkg/core/web/*.wasm pkg/collab/web/*.wasm 2>/dev/null || true
308 echo ""
309 echo "Worker WASM (collab only):"
310 ls -lh pkg/collab/worker/*.wasm 2>/dev/null || true
311 echo ""
312 echo "Packages:"
313 echo " pkg/core/ - @weaver.sh/editor-core (local editing)"
314 echo " pkg/collab/ - @weaver.sh/editor-collab (with CRDT collab)"
315}
316
317do_pack() {
318 echo "Packing..."
319 for variant in "${!VARIANTS[@]}"; do
320 echo " ${variant}..."
321 (cd "pkg/${variant}" && npm pack)
322 done
323 echo ""
324 echo "Tarballs created:"
325 ls -lh pkg/*/*.tgz 2>/dev/null || true
326}
327
328do_publish() {
329 local tag="${1:-}"
330 local tag_arg=""
331 if [[ -n "$tag" ]]; then
332 tag_arg="--tag $tag"
333 fi
334
335 echo "Publishing..."
336 for variant in "${!VARIANTS[@]}"; do
337 echo " ${variant}..."
338 (cd "pkg/${variant}" && npm publish --access public $tag_arg)
339 done
340 echo ""
341 echo "Published!"
342}
343
344usage() {
345 echo "Usage: $0 [command]"
346 echo ""
347 echo "Commands:"
348 echo " build Build all variants and targets (default)"
349 echo " pack Create npm tarballs"
350 echo " publish Publish to npm registry"
351 echo " all Build, pack, and publish"
352 echo ""
353 echo "Options for publish:"
354 echo " --tag <tag> Publish with a specific tag (e.g., 'next', 'beta')"
355}
356
357case "$COMMAND" in
358 build)
359 do_build
360 ;;
361 pack)
362 do_pack
363 ;;
364 publish)
365 do_publish "$@"
366 ;;
367 all)
368 do_build
369 do_pack
370 do_publish "$@"
371 ;;
372 -h|--help|help)
373 usage
374 ;;
375 *)
376 echo "Unknown command: $COMMAND"
377 usage
378 exit 1
379 ;;
380esac