@stackable-labs/cli-app-extension 1.1.0 → 1.2.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/dist/index.js +287 -83
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6,8 +6,8 @@ import { render } from "ink";
|
|
|
6
6
|
|
|
7
7
|
// src/App.tsx
|
|
8
8
|
import { join as join2 } from "path";
|
|
9
|
-
import { Box as
|
|
10
|
-
import { useCallback, useState as
|
|
9
|
+
import { Box as Box13, Text as Text13, useApp } from "ink";
|
|
10
|
+
import { useCallback, useState as useState9 } from "react";
|
|
11
11
|
|
|
12
12
|
// src/components/Confirm.tsx
|
|
13
13
|
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
@@ -319,7 +319,7 @@ var stepIcon = (status) => {
|
|
|
319
319
|
}
|
|
320
320
|
};
|
|
321
321
|
var ScaffoldProgress = ({ steps }) => /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, children: [
|
|
322
|
-
/* @__PURE__ */ jsx8(Text8, { bold: true, children: "Scaffolding your
|
|
322
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, children: "Scaffolding your Extension\u2026" }),
|
|
323
323
|
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: steps.map((step) => /* @__PURE__ */ jsxs8(Box8, { gap: 2, children: [
|
|
324
324
|
stepIcon(step.status),
|
|
325
325
|
/* @__PURE__ */ jsx8(Text8, { dimColor: step.status === "pending", color: step.status === "running" ? "cyan" : void 0, children: step.label })
|
|
@@ -336,11 +336,11 @@ var TARGET_DESCRIPTIONS = {
|
|
|
336
336
|
"slot.footer": "Renders a footer bar at the bottom of the panel",
|
|
337
337
|
"slot.footer-links": "Renders a link row in the global footer"
|
|
338
338
|
};
|
|
339
|
-
var TargetSelect = ({ availableTargets, onSubmit, onBack }) => {
|
|
339
|
+
var TargetSelect = ({ availableTargets, preSelected, onSubmit, onBack }) => {
|
|
340
340
|
const [cursor, setCursor] = useState6(0);
|
|
341
341
|
const [backFocused, setBackFocused] = useState6(false);
|
|
342
342
|
const [selected, setSelected] = useState6(
|
|
343
|
-
new Set(availableTargets.includes("slot.content") ? ["slot.content"] : [])
|
|
343
|
+
new Set(preSelected ?? (availableTargets.includes("slot.content") ? ["slot.content"] : []))
|
|
344
344
|
);
|
|
345
345
|
const [error, setError] = useState6();
|
|
346
346
|
useInput3((input, key) => {
|
|
@@ -414,18 +414,41 @@ var TargetSelect = ({ availableTargets, onSubmit, onBack }) => {
|
|
|
414
414
|
};
|
|
415
415
|
|
|
416
416
|
// src/components/AppSelect.tsx
|
|
417
|
-
import { Box as Box11, Text as Text11 } from "ink";
|
|
418
|
-
import SelectInput from "ink-select-input";
|
|
417
|
+
import { Box as Box11, Text as Text11, useInput as useInput4 } from "ink";
|
|
419
418
|
import Spinner2 from "ink-spinner";
|
|
420
419
|
import { useEffect, useState as useState7 } from "react";
|
|
421
420
|
|
|
422
421
|
// src/lib/api.ts
|
|
423
|
-
var
|
|
422
|
+
var DEFAULT_API_URL = "https://api.stackablelabs.io/app-extension/latest";
|
|
423
|
+
var DEFAULT_ADMIN_API_URL = "https://api-use1.stackablelabs.io/admin";
|
|
424
|
+
var getApiBaseUrl = () => process.env.API_BASE_URL ?? DEFAULT_API_URL;
|
|
425
|
+
var getAdminApiBaseUrl = () => process.env.ADMIN_API_BASE_URL ?? DEFAULT_ADMIN_API_URL;
|
|
424
426
|
var fetchApps = async () => {
|
|
425
|
-
const baseURL = `${
|
|
427
|
+
const baseURL = `${getApiBaseUrl()}/apps`;
|
|
426
428
|
const res = await fetch(baseURL);
|
|
427
429
|
if (!res.ok) {
|
|
428
|
-
throw new Error(`Failed to fetch apps
|
|
430
|
+
throw new Error(`Failed to fetch apps: ${res.status} ${res.statusText}`);
|
|
431
|
+
}
|
|
432
|
+
return res.json();
|
|
433
|
+
};
|
|
434
|
+
var createExtensionRemote = async (appId, payload) => {
|
|
435
|
+
const baseUrl = getAdminApiBaseUrl();
|
|
436
|
+
const res = await fetch(`${baseUrl}/app-extension/${appId}/extensions`, {
|
|
437
|
+
method: "POST",
|
|
438
|
+
headers: { "content-type": "application/json" },
|
|
439
|
+
body: JSON.stringify(payload)
|
|
440
|
+
});
|
|
441
|
+
if (!res.ok) {
|
|
442
|
+
const body = await res.text();
|
|
443
|
+
throw new Error(`Failed to create extension: ${res.status} ${body}`);
|
|
444
|
+
}
|
|
445
|
+
return res.json();
|
|
446
|
+
};
|
|
447
|
+
var fetchExtensions = async (appId) => {
|
|
448
|
+
const baseUrl = getApiBaseUrl();
|
|
449
|
+
const res = await fetch(`${baseUrl}/extensions/${appId}`);
|
|
450
|
+
if (!res.ok) {
|
|
451
|
+
throw new Error(`Failed to fetch extensions: ${res.status} ${res.statusText}`);
|
|
429
452
|
}
|
|
430
453
|
return res.json();
|
|
431
454
|
};
|
|
@@ -480,15 +503,26 @@ var AppSelect = ({ onSubmit }) => {
|
|
|
480
503
|
const [apps, setApps] = useState7([]);
|
|
481
504
|
const [loading, setLoading] = useState7(true);
|
|
482
505
|
const [error, setError] = useState7();
|
|
506
|
+
const [cursor, setCursor] = useState7(0);
|
|
483
507
|
useEffect(() => {
|
|
484
508
|
fetchApps().then(setApps).catch((err) => setError(err instanceof Error ? err.message : String(err))).finally(() => setLoading(false));
|
|
485
509
|
}, []);
|
|
510
|
+
useInput4((_, key) => {
|
|
511
|
+
if (loading || error || apps.length === 0) return;
|
|
512
|
+
if (key.upArrow) {
|
|
513
|
+
setCursor((c) => Math.max(0, c - 1));
|
|
514
|
+
} else if (key.downArrow) {
|
|
515
|
+
setCursor((c) => Math.min(apps.length - 1, c + 1));
|
|
516
|
+
} else if (key.return) {
|
|
517
|
+
onSubmit(apps[cursor]);
|
|
518
|
+
}
|
|
519
|
+
});
|
|
486
520
|
if (loading) {
|
|
487
521
|
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
488
522
|
/* @__PURE__ */ jsx11(Banner, {}),
|
|
489
523
|
/* @__PURE__ */ jsxs11(Box11, { gap: 2, paddingX: 1, children: [
|
|
490
524
|
/* @__PURE__ */ jsx11(Spinner2, { type: "dots" }),
|
|
491
|
-
/* @__PURE__ */ jsx11(Text11, { children: "Loading available
|
|
525
|
+
/* @__PURE__ */ jsx11(Text11, { children: "Loading available Apps\u2026" })
|
|
492
526
|
] })
|
|
493
527
|
] });
|
|
494
528
|
}
|
|
@@ -501,20 +535,103 @@ var AppSelect = ({ onSubmit }) => {
|
|
|
501
535
|
if (apps.length === 0) {
|
|
502
536
|
return /* @__PURE__ */ jsx11(Text11, { color: "yellow", children: "No apps available. Contact your administrator." });
|
|
503
537
|
}
|
|
504
|
-
const items = apps.map((app) => ({
|
|
505
|
-
label: app.name,
|
|
506
|
-
value: app.id
|
|
507
|
-
}));
|
|
508
|
-
const handleSelect = (item) => {
|
|
509
|
-
const app = apps.find((a) => a.id === item.value);
|
|
510
|
-
if (app) onSubmit(app);
|
|
511
|
-
};
|
|
512
538
|
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
513
539
|
/* @__PURE__ */ jsx11(Banner, {}),
|
|
514
|
-
/* @__PURE__ */ jsx11(StepShell, { title: "Select the App you are building an Extension for:", children: /* @__PURE__ */ jsx11(
|
|
540
|
+
/* @__PURE__ */ jsx11(StepShell, { title: "Select the App you are building an Extension for:", children: /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: apps.map((app, i) => {
|
|
541
|
+
const isCursor = i === cursor;
|
|
542
|
+
return /* @__PURE__ */ jsxs11(Box11, { gap: 1, children: [
|
|
543
|
+
/* @__PURE__ */ jsx11(Text11, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u276F" : " " }),
|
|
544
|
+
/* @__PURE__ */ jsx11(Text11, { bold: isCursor, children: app.name }),
|
|
545
|
+
/* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
|
|
546
|
+
"(",
|
|
547
|
+
app.id,
|
|
548
|
+
")"
|
|
549
|
+
] })
|
|
550
|
+
] }, app.id);
|
|
551
|
+
}) }) })
|
|
515
552
|
] });
|
|
516
553
|
};
|
|
517
554
|
|
|
555
|
+
// src/components/ExtensionSelect.tsx
|
|
556
|
+
import { Box as Box12, Text as Text12, useInput as useInput5 } from "ink";
|
|
557
|
+
import Spinner3 from "ink-spinner";
|
|
558
|
+
import { useEffect as useEffect2, useState as useState8 } from "react";
|
|
559
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
560
|
+
var ExtensionSelect = ({ appId, onSubmit, onBack }) => {
|
|
561
|
+
const [extensions, setExtensions] = useState8([]);
|
|
562
|
+
const [loading, setLoading] = useState8(true);
|
|
563
|
+
const [error, setError] = useState8();
|
|
564
|
+
const [cursor, setCursor] = useState8(0);
|
|
565
|
+
const [backFocused, setBackFocused] = useState8(false);
|
|
566
|
+
useEffect2(() => {
|
|
567
|
+
fetchExtensions(appId).then((byId) => setExtensions(Object.values(byId))).catch((err) => setError(err instanceof Error ? err.message : String(err))).finally(() => setLoading(false));
|
|
568
|
+
}, [appId]);
|
|
569
|
+
useInput5((_, key) => {
|
|
570
|
+
if (loading || error || extensions.length === 0) return;
|
|
571
|
+
if (key.upArrow) {
|
|
572
|
+
if (cursor === 0 && onBack) {
|
|
573
|
+
setBackFocused(true);
|
|
574
|
+
} else {
|
|
575
|
+
setBackFocused(false);
|
|
576
|
+
setCursor((c) => Math.max(0, c - 1));
|
|
577
|
+
}
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
if (key.downArrow) {
|
|
581
|
+
if (backFocused) {
|
|
582
|
+
setBackFocused(false);
|
|
583
|
+
} else {
|
|
584
|
+
setCursor((c) => Math.min(extensions.length - 1, c + 1));
|
|
585
|
+
}
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (key.return) {
|
|
589
|
+
if (backFocused) {
|
|
590
|
+
onBack?.();
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
onSubmit(extensions[cursor]);
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
if (loading) {
|
|
597
|
+
return /* @__PURE__ */ jsxs12(Box12, { gap: 2, paddingX: 1, children: [
|
|
598
|
+
/* @__PURE__ */ jsx12(Spinner3, { type: "dots" }),
|
|
599
|
+
/* @__PURE__ */ jsx12(Text12, { children: "Loading Extensions\u2026" })
|
|
600
|
+
] });
|
|
601
|
+
}
|
|
602
|
+
if (error) {
|
|
603
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", gap: 1, children: [
|
|
604
|
+
/* @__PURE__ */ jsx12(Text12, { color: "red", bold: true, children: "Failed to load Extensions" }),
|
|
605
|
+
/* @__PURE__ */ jsx12(Text12, { color: "red", children: error })
|
|
606
|
+
] });
|
|
607
|
+
}
|
|
608
|
+
if (extensions.length === 0) {
|
|
609
|
+
return /* @__PURE__ */ jsx12(Text12, { color: "yellow", children: "No Extensions found for this App." });
|
|
610
|
+
}
|
|
611
|
+
return /* @__PURE__ */ jsx12(StepShell, { title: "Select an existing Extension to scaffold:", onBack, backFocused, children: /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: extensions.map((ext, i) => {
|
|
612
|
+
const isCursor = i === cursor && !backFocused;
|
|
613
|
+
return /* @__PURE__ */ jsxs12(Box12, { gap: 1, children: [
|
|
614
|
+
/* @__PURE__ */ jsx12(Text12, { color: isCursor ? "cyan" : void 0, children: isCursor ? "\u276F" : " " }),
|
|
615
|
+
/* @__PURE__ */ jsx12(Text12, { bold: isCursor, children: ext.manifest.name }),
|
|
616
|
+
/* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
617
|
+
"(",
|
|
618
|
+
ext.id,
|
|
619
|
+
")"
|
|
620
|
+
] })
|
|
621
|
+
] }, ext.id);
|
|
622
|
+
}) }) });
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
// src/constants.ts
|
|
626
|
+
var TEMPLATE_SOURCE = "github:stackable-labs/templates/app-extension";
|
|
627
|
+
var DEFAULT_PERMISSIONS = ["context:read"];
|
|
628
|
+
var TARGET_PERMISSION_MAP = {
|
|
629
|
+
"slot.header": ["context:read"],
|
|
630
|
+
"slot.content": ["context:read", "data:query", "actions:toast", "actions:invoke"],
|
|
631
|
+
"slot.footer": [],
|
|
632
|
+
"slot.footer-links": []
|
|
633
|
+
};
|
|
634
|
+
|
|
518
635
|
// src/lib/postScaffold.ts
|
|
519
636
|
import { execFile } from "child_process";
|
|
520
637
|
import { promisify } from "util";
|
|
@@ -541,18 +658,6 @@ var postScaffold = async (options) => {
|
|
|
541
658
|
import { readFile, readdir, rm, writeFile } from "fs/promises";
|
|
542
659
|
import { join } from "path";
|
|
543
660
|
import { downloadTemplate } from "giget";
|
|
544
|
-
|
|
545
|
-
// src/constants.ts
|
|
546
|
-
var TEMPLATE_SOURCE = "github:stackable-labs/templates/app-extension";
|
|
547
|
-
var DEFAULT_PERMISSIONS = ["context:read"];
|
|
548
|
-
var TARGET_PERMISSION_MAP = {
|
|
549
|
-
"slot.header": ["context:read"],
|
|
550
|
-
"slot.content": ["context:read", "data:query", "actions:toast", "actions:invoke"],
|
|
551
|
-
"slot.footer": [],
|
|
552
|
-
"slot.footer-links": []
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
// src/lib/scaffold.ts
|
|
556
661
|
var normalizeTargets = (targets) => Array.from(new Set(targets));
|
|
557
662
|
var derivePermissions = (targets) => {
|
|
558
663
|
const permissions = /* @__PURE__ */ new Set();
|
|
@@ -737,7 +842,7 @@ var rewritePreviewApp = async (rootDir, targets, permissions) => {
|
|
|
737
842
|
}
|
|
738
843
|
const appContent = `import { ExtensionProvider, ExtensionSlot } from '@stackable-labs/sdk-extension-host'
|
|
739
844
|
import type { CapabilityHandlers } from '@stackable-labs/sdk-extension-host'
|
|
740
|
-
import
|
|
845
|
+
import hostComponents from '@stackable-labs/embeddables/components'
|
|
741
846
|
import type {
|
|
742
847
|
${typeImports.join(",\n ")}
|
|
743
848
|
} from '@stackable-labs/sdk-extension-contracts'
|
|
@@ -851,47 +956,93 @@ var scaffold = async (options) => {
|
|
|
851
956
|
};
|
|
852
957
|
|
|
853
958
|
// src/App.tsx
|
|
854
|
-
import { jsx as
|
|
855
|
-
var
|
|
856
|
-
var
|
|
959
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
960
|
+
var STEP_ORDER_CREATE = ["app", "name", "targets", "ports", "dir", "confirm"];
|
|
961
|
+
var STEP_ORDER_SCAFFOLD = ["app", "extensionSelect", "confirmName", "confirmTargets", "ports", "dir", "confirm"];
|
|
962
|
+
var CREATE_STEPS = [
|
|
963
|
+
{ label: "Registering extension", status: "pending" },
|
|
964
|
+
{ label: "Fetching template", status: "pending" },
|
|
965
|
+
{ label: "Generating files", status: "pending" },
|
|
966
|
+
{ label: "Installing dependencies", status: "pending" }
|
|
967
|
+
];
|
|
968
|
+
var SCAFFOLD_STEPS = [
|
|
857
969
|
{ label: "Fetching template", status: "pending" },
|
|
858
970
|
{ label: "Generating files", status: "pending" },
|
|
859
971
|
{ label: "Installing dependencies", status: "pending" }
|
|
860
972
|
];
|
|
861
973
|
var toKebabCase2 = (value) => value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
862
|
-
var
|
|
974
|
+
var derivePermissions2 = (targets) => {
|
|
975
|
+
const permissions = /* @__PURE__ */ new Set();
|
|
976
|
+
for (const target of targets) {
|
|
977
|
+
const mapped = TARGET_PERMISSION_MAP[target];
|
|
978
|
+
if (mapped && mapped.length > 0) {
|
|
979
|
+
for (const permission of mapped) {
|
|
980
|
+
permissions.add(permission);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
if (permissions.size === 0) {
|
|
985
|
+
for (const permission of DEFAULT_PERMISSIONS) {
|
|
986
|
+
permissions.add(permission);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
return [...permissions];
|
|
990
|
+
};
|
|
991
|
+
var App = ({ mode, initialName, options }) => {
|
|
863
992
|
const { exit } = useApp();
|
|
864
|
-
const [step, setStep] =
|
|
865
|
-
const [name, setName] =
|
|
866
|
-
const [extensionId, setExtensionId] =
|
|
867
|
-
const [selectedApp, setSelectedApp] =
|
|
868
|
-
const [extensionPort, setExtensionPort] =
|
|
993
|
+
const [step, setStep] = useState9("app");
|
|
994
|
+
const [name, setName] = useState9(initialName ?? "");
|
|
995
|
+
const [extensionId, setExtensionId] = useState9("");
|
|
996
|
+
const [selectedApp, setSelectedApp] = useState9(null);
|
|
997
|
+
const [extensionPort, setExtensionPort] = useState9(
|
|
869
998
|
options?.extensionPort ? parseInt(options.extensionPort, 10) : 5173
|
|
870
999
|
);
|
|
871
|
-
const [previewPort, setPreviewPort] =
|
|
1000
|
+
const [previewPort, setPreviewPort] = useState9(
|
|
872
1001
|
options?.previewPort ? parseInt(options.previewPort, 10) : 5174
|
|
873
1002
|
);
|
|
874
|
-
const [targets, setTargets] =
|
|
875
|
-
const [outputDir, setOutputDir] =
|
|
876
|
-
const [progressSteps, setProgressSteps] =
|
|
877
|
-
const [errorMessage, setErrorMessage] =
|
|
1003
|
+
const [targets, setTargets] = useState9([]);
|
|
1004
|
+
const [outputDir, setOutputDir] = useState9("");
|
|
1005
|
+
const [progressSteps, setProgressSteps] = useState9(mode === "create" ? CREATE_STEPS : SCAFFOLD_STEPS);
|
|
1006
|
+
const [errorMessage, setErrorMessage] = useState9();
|
|
878
1007
|
const updateStep = useCallback((index, status) => {
|
|
879
1008
|
setProgressSteps((prev) => prev.map((s, i) => i === index ? { ...s, status } : s));
|
|
880
1009
|
}, []);
|
|
1010
|
+
const activeSteps = useCallback(() => {
|
|
1011
|
+
const base = mode === "create" ? STEP_ORDER_CREATE : STEP_ORDER_SCAFFOLD;
|
|
1012
|
+
const skipped = /* @__PURE__ */ new Set();
|
|
1013
|
+
if (mode === "create" && initialName) skipped.add("name");
|
|
1014
|
+
if (options?.extensionPort || options?.previewPort) skipped.add("ports");
|
|
1015
|
+
return base.filter((s) => !skipped.has(s));
|
|
1016
|
+
}, [mode, initialName, options?.extensionPort, options?.previewPort]);
|
|
881
1017
|
const goBack = useCallback(() => {
|
|
882
|
-
const skippedSteps = /* @__PURE__ */ new Set();
|
|
883
|
-
if (initialName) skippedSteps.add("name");
|
|
884
|
-
if (options?.extensionPort || options?.previewPort) skippedSteps.add("ports");
|
|
885
|
-
const activeSteps = STEP_ORDER.filter((s) => !skippedSteps.has(s));
|
|
886
1018
|
setStep((prev) => {
|
|
887
|
-
const
|
|
888
|
-
|
|
1019
|
+
const steps = activeSteps();
|
|
1020
|
+
const idx = steps.indexOf(prev);
|
|
1021
|
+
return idx > 0 ? steps[idx - 1] : prev;
|
|
889
1022
|
});
|
|
890
|
-
}, [
|
|
1023
|
+
}, [activeSteps]);
|
|
891
1024
|
const handleAppSelect = (app) => {
|
|
892
1025
|
setSelectedApp(app);
|
|
1026
|
+
if (mode === "scaffold") {
|
|
1027
|
+
setStep("extensionSelect");
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
893
1030
|
setStep(initialName ? "targets" : "name");
|
|
894
1031
|
};
|
|
1032
|
+
const handleExtensionSelect = (ext) => {
|
|
1033
|
+
setName(ext.manifest.name);
|
|
1034
|
+
setExtensionId(ext.id);
|
|
1035
|
+
setTargets(ext.manifest.targets);
|
|
1036
|
+
setStep("confirmName");
|
|
1037
|
+
};
|
|
1038
|
+
const handleConfirmName = (value) => {
|
|
1039
|
+
setName(value);
|
|
1040
|
+
setStep("confirmTargets");
|
|
1041
|
+
};
|
|
1042
|
+
const handleConfirmTargets = (value) => {
|
|
1043
|
+
setTargets(value);
|
|
1044
|
+
setStep(options?.extensionPort || options?.previewPort ? "dir" : "ports");
|
|
1045
|
+
};
|
|
895
1046
|
const handleName = (value) => {
|
|
896
1047
|
setName(value);
|
|
897
1048
|
setExtensionId(toKebabCase2(value));
|
|
@@ -912,32 +1063,46 @@ var App = ({ initialName, options }) => {
|
|
|
912
1063
|
};
|
|
913
1064
|
const handleConfirm = async () => {
|
|
914
1065
|
setStep("scaffolding");
|
|
915
|
-
setProgressSteps(
|
|
916
|
-
{ label: "Fetching template", status: "running" },
|
|
917
|
-
{ label: "Generating files", status: "pending" },
|
|
918
|
-
{ label: "Installing dependencies", status: "pending" }
|
|
919
|
-
]);
|
|
1066
|
+
setProgressSteps(mode === "create" ? CREATE_STEPS : SCAFFOLD_STEPS);
|
|
920
1067
|
try {
|
|
921
|
-
|
|
1068
|
+
let resolvedExtensionId = extensionId || toKebabCase2(name);
|
|
1069
|
+
let scaffoldStepOffset = 0;
|
|
1070
|
+
if (mode === "create") {
|
|
1071
|
+
scaffoldStepOffset = 1;
|
|
1072
|
+
updateStep(0, "running");
|
|
1073
|
+
const created = await createExtensionRemote(selectedApp.id, {
|
|
1074
|
+
manifest: {
|
|
1075
|
+
name,
|
|
1076
|
+
version: "0.0.0",
|
|
1077
|
+
targets,
|
|
1078
|
+
permissions: derivePermissions2(targets)
|
|
1079
|
+
},
|
|
1080
|
+
bundleUrl: `http://localhost:${extensionPort}`
|
|
1081
|
+
});
|
|
1082
|
+
resolvedExtensionId = created.id;
|
|
1083
|
+
setExtensionId(created.id);
|
|
1084
|
+
updateStep(0, "done");
|
|
1085
|
+
}
|
|
1086
|
+
updateStep(scaffoldStepOffset + 0, "running");
|
|
922
1087
|
await scaffold({
|
|
923
1088
|
appId: selectedApp.id,
|
|
924
1089
|
name,
|
|
925
|
-
extensionId,
|
|
1090
|
+
extensionId: resolvedExtensionId,
|
|
926
1091
|
targets,
|
|
927
1092
|
outputDir,
|
|
928
1093
|
extensionPort,
|
|
929
1094
|
previewPort
|
|
930
1095
|
});
|
|
931
|
-
updateStep(0, "done");
|
|
932
|
-
updateStep(1, "running");
|
|
1096
|
+
updateStep(scaffoldStepOffset + 0, "done");
|
|
1097
|
+
updateStep(scaffoldStepOffset + 1, "running");
|
|
933
1098
|
await new Promise((r) => setTimeout(r, 200));
|
|
934
|
-
updateStep(1, "done");
|
|
1099
|
+
updateStep(scaffoldStepOffset + 1, "done");
|
|
935
1100
|
if (!options?.skipInstall) {
|
|
936
|
-
updateStep(2, "running");
|
|
1101
|
+
updateStep(scaffoldStepOffset + 2, "running");
|
|
937
1102
|
await postScaffold({ outputDir, skipInstall: false, skipGit: options?.skipGit });
|
|
938
|
-
updateStep(2, "done");
|
|
1103
|
+
updateStep(scaffoldStepOffset + 2, "done");
|
|
939
1104
|
} else {
|
|
940
|
-
updateStep(2, "done");
|
|
1105
|
+
updateStep(scaffoldStepOffset + 2, "done");
|
|
941
1106
|
}
|
|
942
1107
|
setStep("done");
|
|
943
1108
|
} catch (err) {
|
|
@@ -951,10 +1116,41 @@ var App = ({ initialName, options }) => {
|
|
|
951
1116
|
};
|
|
952
1117
|
switch (step) {
|
|
953
1118
|
case "app": {
|
|
954
|
-
return /* @__PURE__ */
|
|
1119
|
+
return /* @__PURE__ */ jsx13(AppSelect, { onSubmit: handleAppSelect });
|
|
1120
|
+
}
|
|
1121
|
+
case "extensionSelect": {
|
|
1122
|
+
return /* @__PURE__ */ jsx13(
|
|
1123
|
+
ExtensionSelect,
|
|
1124
|
+
{
|
|
1125
|
+
appId: selectedApp.id,
|
|
1126
|
+
onSubmit: handleExtensionSelect,
|
|
1127
|
+
onBack: goBack
|
|
1128
|
+
}
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
case "confirmName": {
|
|
1132
|
+
return /* @__PURE__ */ jsx13(
|
|
1133
|
+
NamePrompt,
|
|
1134
|
+
{
|
|
1135
|
+
initialValue: name,
|
|
1136
|
+
onSubmit: handleConfirmName,
|
|
1137
|
+
onBack: goBack
|
|
1138
|
+
}
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
case "confirmTargets": {
|
|
1142
|
+
return /* @__PURE__ */ jsx13(
|
|
1143
|
+
TargetSelect,
|
|
1144
|
+
{
|
|
1145
|
+
availableTargets: selectedApp?.targets ?? [],
|
|
1146
|
+
preSelected: targets,
|
|
1147
|
+
onSubmit: handleConfirmTargets,
|
|
1148
|
+
onBack: goBack
|
|
1149
|
+
}
|
|
1150
|
+
);
|
|
955
1151
|
}
|
|
956
1152
|
case "name": {
|
|
957
|
-
return /* @__PURE__ */
|
|
1153
|
+
return /* @__PURE__ */ jsx13(
|
|
958
1154
|
NamePrompt,
|
|
959
1155
|
{
|
|
960
1156
|
initialValue: name,
|
|
@@ -964,7 +1160,7 @@ var App = ({ initialName, options }) => {
|
|
|
964
1160
|
);
|
|
965
1161
|
}
|
|
966
1162
|
case "targets": {
|
|
967
|
-
return /* @__PURE__ */
|
|
1163
|
+
return /* @__PURE__ */ jsx13(
|
|
968
1164
|
TargetSelect,
|
|
969
1165
|
{
|
|
970
1166
|
availableTargets: selectedApp?.targets ?? [],
|
|
@@ -974,7 +1170,7 @@ var App = ({ initialName, options }) => {
|
|
|
974
1170
|
);
|
|
975
1171
|
}
|
|
976
1172
|
case "ports": {
|
|
977
|
-
return /* @__PURE__ */
|
|
1173
|
+
return /* @__PURE__ */ jsx13(
|
|
978
1174
|
PortsPrompt,
|
|
979
1175
|
{
|
|
980
1176
|
onSubmit: handlePorts,
|
|
@@ -983,17 +1179,17 @@ var App = ({ initialName, options }) => {
|
|
|
983
1179
|
);
|
|
984
1180
|
}
|
|
985
1181
|
case "dir": {
|
|
986
|
-
return /* @__PURE__ */
|
|
1182
|
+
return /* @__PURE__ */ jsx13(
|
|
987
1183
|
DirPrompt,
|
|
988
1184
|
{
|
|
989
|
-
defaultDir: join2(process.cwd(), toKebabCase2(name)),
|
|
1185
|
+
defaultDir: join2(process.cwd(), toKebabCase2(extensionId || name)),
|
|
990
1186
|
onSubmit: handleDir,
|
|
991
1187
|
onBack: goBack
|
|
992
1188
|
}
|
|
993
1189
|
);
|
|
994
1190
|
}
|
|
995
1191
|
case "confirm": {
|
|
996
|
-
return /* @__PURE__ */
|
|
1192
|
+
return /* @__PURE__ */ jsx13(
|
|
997
1193
|
Confirm,
|
|
998
1194
|
{
|
|
999
1195
|
name,
|
|
@@ -1008,23 +1204,31 @@ var App = ({ initialName, options }) => {
|
|
|
1008
1204
|
);
|
|
1009
1205
|
}
|
|
1010
1206
|
case "scaffolding": {
|
|
1011
|
-
return /* @__PURE__ */
|
|
1207
|
+
return /* @__PURE__ */ jsx13(ScaffoldProgress, { steps: progressSteps });
|
|
1012
1208
|
}
|
|
1013
1209
|
case "done": {
|
|
1014
|
-
return /* @__PURE__ */
|
|
1210
|
+
return /* @__PURE__ */ jsx13(Done, { name, outputDir });
|
|
1015
1211
|
}
|
|
1016
1212
|
default: {
|
|
1017
|
-
return /* @__PURE__ */
|
|
1018
|
-
/* @__PURE__ */
|
|
1019
|
-
errorMessage && /* @__PURE__ */
|
|
1213
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", gap: 1, children: [
|
|
1214
|
+
/* @__PURE__ */ jsx13(Text13, { color: "red", bold: true, children: "Scaffold failed" }),
|
|
1215
|
+
errorMessage && /* @__PURE__ */ jsx13(Text13, { color: "red", children: errorMessage })
|
|
1020
1216
|
] });
|
|
1021
1217
|
}
|
|
1022
1218
|
}
|
|
1023
1219
|
};
|
|
1024
1220
|
|
|
1025
1221
|
// src/index.tsx
|
|
1026
|
-
import { jsx as
|
|
1027
|
-
program.name("
|
|
1028
|
-
|
|
1222
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1223
|
+
program.name("stackable-extension").description("Stackable extension developer CLI");
|
|
1224
|
+
program.command("create").description("Create a new extension project").argument("[name]", "Extension project name").option("--targets <targets>", "Comma-separated target slots").option("--extension-port <port>", "Extension dev server port (default: 5173)").option("--preview-port <port>", "Preview host dev server port").option("--skip-install", "Skip package manager install").option("--skip-git", "Skip git initialization").action((name, options) => {
|
|
1225
|
+
render(/* @__PURE__ */ jsx14(App, { mode: "create", initialName: name, options }));
|
|
1226
|
+
});
|
|
1227
|
+
program.command("scaffold").description("Scaffold a local project from an existing extension").option("--extension-port <port>", "Extension dev server port (default: 5173)").option("--preview-port <port>", "Preview host dev server port").option("--skip-install", "Skip package manager install").option("--skip-git", "Skip git initialization").action((options) => {
|
|
1228
|
+
render(/* @__PURE__ */ jsx14(App, { mode: "scaffold", options }));
|
|
1029
1229
|
});
|
|
1030
|
-
|
|
1230
|
+
if (process.argv[1]?.endsWith("create-extension")) {
|
|
1231
|
+
program.parse(["node", "stackable-extension", "create", ...process.argv.slice(2).filter((arg) => arg !== "--")]);
|
|
1232
|
+
} else {
|
|
1233
|
+
program.parse(process.argv.filter((arg) => arg !== "--"));
|
|
1234
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackable-labs/cli-app-extension",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
7
7
|
"create-extension": "./dist/index.js",
|
|
8
|
-
"
|
|
8
|
+
"stackable-extension": "./dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist/",
|