@owncast/plugin-sdk 0.3.1 → 0.4.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/bin/owncast-plugin.js +47 -41
- package/package.json +1 -1
package/bin/owncast-plugin.js
CHANGED
|
@@ -238,50 +238,18 @@ module.exports = { register, on_event, on_filter, on_http_request };
|
|
|
238
238
|
env,
|
|
239
239
|
});
|
|
240
240
|
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
// without
|
|
244
|
-
//
|
|
245
|
-
const assetsSrc = path.join(cwd, "assets");
|
|
246
|
-
if (fs.existsSync(assetsSrc) && fs.statSync(assetsSrc).isDirectory()) {
|
|
247
|
-
const assetsDest = path.join(cwd, `${slug}-assets`);
|
|
248
|
-
let needsLink = true;
|
|
249
|
-
// Use lstatSync (not existsSync), existsSync follows symlinks and
|
|
250
|
-
// returns false for a dangling link, but the link's inode is still
|
|
251
|
-
// there and would make symlinkSync below fail with EEXIST. lstatSync
|
|
252
|
-
// sees the link itself regardless of whether its target resolves.
|
|
253
|
-
let st;
|
|
254
|
-
try {
|
|
255
|
-
st = fs.lstatSync(assetsDest);
|
|
256
|
-
} catch {
|
|
257
|
-
// path doesn't exist at all, fall through to create it.
|
|
258
|
-
}
|
|
259
|
-
if (st) {
|
|
260
|
-
let target;
|
|
261
|
-
if (st.isSymbolicLink()) {
|
|
262
|
-
// realpathSync throws on dangling links; treat that as "doesn't
|
|
263
|
-
// match, replace it" rather than letting it abort the build.
|
|
264
|
-
try {
|
|
265
|
-
target = fs.realpathSync(assetsDest);
|
|
266
|
-
} catch {}
|
|
267
|
-
}
|
|
268
|
-
if (target && target === fs.realpathSync(assetsSrc)) {
|
|
269
|
-
needsLink = false;
|
|
270
|
-
} else {
|
|
271
|
-
fs.rmSync(assetsDest, { recursive: true, force: true });
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
if (needsLink) {
|
|
275
|
-
fs.symlinkSync(path.resolve(assetsSrc), assetsDest, "dir");
|
|
276
|
-
}
|
|
277
|
-
}
|
|
241
|
+
// public/ and assets/ live at the source root; the host's
|
|
242
|
+
// loose-files loader picks them up as siblings of the built
|
|
243
|
+
// <slug>.wasm without any rename, so the build CLI doesn't need to
|
|
244
|
+
// create or mirror anything for them.
|
|
278
245
|
|
|
279
246
|
console.log(`built ${path.relative(cwd, wasmOut)}`);
|
|
280
247
|
}
|
|
281
248
|
|
|
282
249
|
// `owncast-plugin package`, bundle the project into a single .ocpkg file
|
|
283
|
-
// (zip archive with plugin.manifest.json, plugin.wasm, and optional
|
|
284
|
-
// Builds the wasm first if it
|
|
250
|
+
// (zip archive with plugin.manifest.json, plugin.wasm, and optional
|
|
251
|
+
// public/ and assets/ directories). Builds the wasm first if it
|
|
252
|
+
// doesn't exist.
|
|
285
253
|
async function packageMain() {
|
|
286
254
|
const cwd = process.cwd();
|
|
287
255
|
const manifestPath = path.join(cwd, "plugin.manifest.json");
|
|
@@ -296,6 +264,7 @@ async function packageMain() {
|
|
|
296
264
|
await buildMain();
|
|
297
265
|
}
|
|
298
266
|
|
|
267
|
+
const publicDir = path.join(cwd, "public");
|
|
299
268
|
const assetsDir = path.join(cwd, "assets");
|
|
300
269
|
const zip = new JSZip();
|
|
301
270
|
zip.file("plugin.manifest.json", fs.readFileSync(manifestPath));
|
|
@@ -310,6 +279,26 @@ async function packageMain() {
|
|
|
310
279
|
zip.file("icon.png", fs.readFileSync(iconPath));
|
|
311
280
|
fileCount++;
|
|
312
281
|
}
|
|
282
|
+
// Bundle a top-level INSTRUCTIONS.md if the plugin source root has one.
|
|
283
|
+
// The host serves it to the admin (which renders it as markdown in a
|
|
284
|
+
// details tab); like icon.png it needs no manifest field and no
|
|
285
|
+
// http.serve permission. The filename is fixed for simplicity.
|
|
286
|
+
const instructionsPath = path.join(cwd, "INSTRUCTIONS.md");
|
|
287
|
+
if (fs.existsSync(instructionsPath) && fs.statSync(instructionsPath).isFile()) {
|
|
288
|
+
zip.file("INSTRUCTIONS.md", fs.readFileSync(instructionsPath));
|
|
289
|
+
fileCount++;
|
|
290
|
+
}
|
|
291
|
+
// public/ → /plugins/<slug>/<path>, served by the host.
|
|
292
|
+
if (fs.existsSync(publicDir) && fs.statSync(publicDir).isDirectory()) {
|
|
293
|
+
for (const file of walkFiles(publicDir)) {
|
|
294
|
+
const rel = path.relative(publicDir, file).split(path.sep).join("/");
|
|
295
|
+
zip.file(`public/${rel}`, fs.readFileSync(file));
|
|
296
|
+
fileCount++;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// assets/ → host reads internally for manifest fields that inline
|
|
300
|
+
// file contents (styles, scripts, extraPageContent). Not served at
|
|
301
|
+
// a URL.
|
|
313
302
|
if (fs.existsSync(assetsDir) && fs.statSync(assetsDir).isDirectory()) {
|
|
314
303
|
for (const file of walkFiles(assetsDir)) {
|
|
315
304
|
const rel = path.relative(assetsDir, file).split(path.sep).join("/");
|
|
@@ -329,12 +318,29 @@ async function packageMain() {
|
|
|
329
318
|
console.log(
|
|
330
319
|
`packaged ${path.relative(cwd, outPath)} (${sizeKb} KB, ${fileCount} files)`,
|
|
331
320
|
);
|
|
321
|
+
|
|
322
|
+
// Drop the intermediate <slug>.wasm now that it's bundled inside the
|
|
323
|
+
// .ocpkg. The .ocpkg is the only artifact authors care about: leaving
|
|
324
|
+
// the loose .wasm next to it just confuses "what do I ship". Only
|
|
325
|
+
// runs on a successful package so a mid-pipeline failure leaves the
|
|
326
|
+
// last good build in place for debugging.
|
|
327
|
+
try {
|
|
328
|
+
fs.unlinkSync(wasmPath);
|
|
329
|
+
} catch (e) {
|
|
330
|
+
// Don't fail the package step over a cleanup miss. The .ocpkg is
|
|
331
|
+
// already written; surface the warning so the author notices the
|
|
332
|
+
// straggler but treat the run as successful.
|
|
333
|
+
if (e.code !== "ENOENT") {
|
|
334
|
+
console.warn(`warning: could not clean up ${path.relative(cwd, wasmPath)}: ${e.message}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
332
337
|
}
|
|
333
338
|
|
|
334
339
|
function* walkFiles(dir) {
|
|
335
340
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
336
|
-
//
|
|
337
|
-
//
|
|
341
|
+
// statSync (not lstatSync) so a symlinked file or directory in
|
|
342
|
+
// the source tree resolves to its target and we read its contents
|
|
343
|
+
// rather than skipping it.
|
|
338
344
|
const full = path.join(dir, entry.name);
|
|
339
345
|
let info;
|
|
340
346
|
try {
|