@scenar/cli 0.2.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/example-bundle/embed.js +1 -0
- package/example-bundle/pack-manifest.json +6 -0
- package/package.json +4 -8
- package/src/__tests__/embed-loader.test.ts +29 -0
- package/src/__tests__/embed-snippet.test.ts +48 -1
- package/src/__tests__/install-command.test.ts +97 -0
- package/src/__tests__/local-url.test.ts +17 -0
- package/src/__tests__/run-install.test.ts +222 -0
- package/src/api.d.ts +3 -1
- package/src/api.d.ts.map +1 -1
- package/src/api.js +3 -1
- package/src/api.js.map +1 -1
- package/src/api.ts +10 -1
- package/src/commands/install.d.ts +3 -0
- package/src/commands/install.d.ts.map +1 -0
- package/src/commands/install.js +67 -0
- package/src/commands/install.js.map +1 -0
- package/src/commands/install.ts +89 -0
- package/src/commands/pack.js +2 -2
- package/src/commands/pack.js.map +1 -1
- package/src/commands/pack.ts +2 -2
- package/src/commands/publish.d.ts.map +1 -1
- package/src/commands/publish.js +3 -1
- package/src/commands/publish.js.map +1 -1
- package/src/commands/publish.ts +5 -1
- package/src/commands/serve.d.ts.map +1 -1
- package/src/commands/serve.js +3 -2
- package/src/commands/serve.js.map +1 -1
- package/src/commands/serve.ts +5 -2
- package/src/commands/try.js +1 -1
- package/src/commands/try.ts +1 -1
- package/src/{deploy → embed}/embed-snippet.d.ts +16 -0
- package/src/embed/embed-snippet.d.ts.map +1 -0
- package/src/{deploy → embed}/embed-snippet.js +27 -0
- package/src/embed/embed-snippet.js.map +1 -0
- package/src/{deploy → embed}/embed-snippet.ts +28 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +3 -5
- package/src/index.js.map +1 -1
- package/src/index.ts +3 -5
- package/src/install/run-install.d.ts +72 -0
- package/src/install/run-install.d.ts.map +1 -0
- package/src/install/run-install.js +378 -0
- package/src/install/run-install.js.map +1 -0
- package/src/install/run-install.ts +461 -0
- package/src/install/scaffold.d.ts +29 -0
- package/src/install/scaffold.d.ts.map +1 -0
- package/src/install/scaffold.js +233 -0
- package/src/install/scaffold.js.map +1 -0
- package/src/install/scaffold.ts +255 -0
- package/src/pack/embed-loader.d.ts +18 -0
- package/src/pack/embed-loader.d.ts.map +1 -0
- package/src/pack/embed-loader.js +25 -0
- package/src/pack/embed-loader.js.map +1 -0
- package/src/pack/embed-loader.ts +27 -0
- package/src/pack/pack-manifest.d.ts +1 -1
- package/src/pack/pack-manifest.d.ts.map +1 -1
- package/src/pack/pack-manifest.ts +1 -1
- package/src/pack/run-pack.d.ts.map +1 -1
- package/src/pack/run-pack.js +6 -0
- package/src/pack/run-pack.js.map +1 -1
- package/src/pack/run-pack.ts +7 -0
- package/src/publish/publish-flow.d.ts +3 -3
- package/src/publish/publish-flow.js +3 -3
- package/src/publish/publish-flow.ts +3 -3
- package/src/publish/run-publish.d.ts +2 -0
- package/src/publish/run-publish.d.ts.map +1 -1
- package/src/publish/run-publish.js +3 -2
- package/src/publish/run-publish.js.map +1 -1
- package/src/publish/run-publish.ts +5 -2
- package/src/render/detect-render-export.d.ts +2 -2
- package/src/render/detect-render-export.js +2 -2
- package/src/render/detect-render-export.ts +2 -2
- package/src/serve/local-url.d.ts +8 -0
- package/src/serve/local-url.d.ts.map +1 -0
- package/src/serve/local-url.js +21 -0
- package/src/serve/local-url.js.map +1 -0
- package/src/serve/local-url.ts +20 -0
- package/src/serve/run-serve.d.ts +2 -0
- package/src/serve/run-serve.d.ts.map +1 -1
- package/src/serve/run-serve.js +4 -3
- package/src/serve/run-serve.js.map +1 -1
- package/src/serve/run-serve.ts +6 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/src/__tests__/deploy-command.test.ts +0 -45
- package/src/__tests__/deploy-flow.test.ts +0 -125
- package/src/__tests__/deploy-upload.test.ts +0 -39
- package/src/commands/deploy.d.ts +0 -3
- package/src/commands/deploy.d.ts.map +0 -1
- package/src/commands/deploy.js +0 -150
- package/src/commands/deploy.js.map +0 -1
- package/src/commands/deploy.ts +0 -186
- package/src/commands/preview.d.ts +0 -3
- package/src/commands/preview.d.ts.map +0 -1
- package/src/commands/preview.js +0 -93
- package/src/commands/preview.js.map +0 -1
- package/src/commands/preview.ts +0 -138
- package/src/deploy/client.d.ts +0 -20
- package/src/deploy/client.d.ts.map +0 -1
- package/src/deploy/client.js +0 -22
- package/src/deploy/client.js.map +0 -1
- package/src/deploy/client.ts +0 -28
- package/src/deploy/deploy-flow.d.ts +0 -78
- package/src/deploy/deploy-flow.d.ts.map +0 -1
- package/src/deploy/deploy-flow.js +0 -53
- package/src/deploy/deploy-flow.js.map +0 -1
- package/src/deploy/deploy-flow.ts +0 -115
- package/src/deploy/embed-snippet.d.ts.map +0 -1
- package/src/deploy/embed-snippet.js.map +0 -1
- package/src/deploy/upload.d.ts +0 -10
- package/src/deploy/upload.d.ts.map +0 -1
- package/src/deploy/upload.js +0 -23
- package/src/deploy/upload.js.map +0 -1
- package/src/deploy/upload.ts +0 -29
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var ScenarEmbed=function(o){"use strict";var I=Object.defineProperty;var N=(o,u,d)=>u in o?I(o,u,{enumerable:!0,configurable:!0,writable:!0,value:d}):o[u]=d;var E=(o,u,d)=>N(o,typeof u!="symbol"?u+"":u,d);const u="scenar-embed";function g(e){return{source:u,v:1,...e}}function S(e){if(typeof e!="object"||e===null)return!1;const s=e;return s.source===u&&s.v===1}function l(e){return typeof e=="number"&&Number.isFinite(e)}function A(e){if(!S(e))return null;const s=e.type;switch(s){case"started":case"paused":case"completed":case"audioBlocked":return{type:s};case"ready":return l(e.totalSteps)&&typeof e.hasNarration=="boolean"?{type:s,totalSteps:e.totalSteps,hasNarration:e.hasNarration}:null;case"resize":return l(e.widthPx)&&l(e.heightPx)?{type:s,widthPx:e.widthPx,heightPx:e.heightPx}:null;case"stepchange":return l(e.stepIndex)&&l(e.totalSteps)?{type:s,stepIndex:e.stepIndex,totalSteps:e.totalSteps}:null;case"progress":return l(e.stepIndex)&&l(e.totalSteps)&&l(e.fraction)?{type:s,stepIndex:e.stepIndex,totalSteps:e.totalSteps,fraction:e.fraction}:null;case"error":return typeof e.message=="string"?{type:s,message:e.message}:null;default:return null}}function w(e,s={}){const{iframe:t,origin:n}=e,{onEvent:r}=s,a=typeof window<"u",c=i=>{var h;(h=t.contentWindow)==null||h.postMessage(g(i),n)},f=i=>{if(i.source!==t.contentWindow||i.origin!==n)return;const h=A(i.data);h&&(r==null||r(h))};return a&&window.addEventListener("message",f),{play:()=>c({type:"play"}),pause:()=>c({type:"pause"}),seek:i=>c({type:"seek",timeMs:i}),setMuted:i=>c({type:"setMuted",muted:i}),setVolume:i=>c({type:"setVolume",volume:i}),prefetch:()=>c({type:"prefetch"}),destroy:()=>{c({type:"destroy"}),a&&window.removeEventListener("message",f)}}}const T=896,C=480;function M(e,s){return e==="light"?"light":e==="dark"||s?"dark":"light"}function _(e,s){const t=new URL(e);return t.searchParams.set("theme",s),t.toString()}function P(e){return new URL(e).origin}function R(){return typeof document<"u"&&document.documentElement.classList.contains("dark")}function O(e,s){const{src:t,theme:n="auto",onAspectRatio:r,onEvent:a}=s,c=P(t);let f=null;const i=()=>{const m=M(n,R());m!==f&&(f=m,e.src=_(t,m))},h=w({iframe:e,origin:c},{onEvent:m=>{m.type==="resize"&&(r==null||r({widthPx:m.widthPx,heightPx:m.heightPx})),a==null||a(m)}});let p;return n==="auto"&&typeof MutationObserver<"u"&&typeof document<"u"&&(p=new MutationObserver(i),p.observe(document.documentElement,{attributes:!0,attributeFilter:["class"]})),i(),{controller:h,destroy(){p==null||p.disconnect(),h.destroy()}}}const k="scenar-embed",x="Interactive product tour",L=typeof HTMLElement<"u"?HTMLElement:class{};class y extends L{constructor(){super(...arguments);E(this,"iframe",null);E(this,"mount",null)}static get observedAttributes(){return["src","title","theme"]}connectedCallback(){this.iframe||this.render(),this.attach()}disconnectedCallback(){var t;(t=this.mount)==null||t.destroy(),this.mount=null}attributeChangedCallback(t,n,r){if(n!==r){if(t==="title"){this.iframe&&this.iframe.setAttribute("title",this.titleAttr());return}this.isConnected&&this.attach()}}play(){var t;(t=this.mount)==null||t.controller.play()}pause(){var t;(t=this.mount)==null||t.controller.pause()}seek(t){var n;(n=this.mount)==null||n.controller.seek(t)}setMuted(t){var n;(n=this.mount)==null||n.controller.setMuted(t)}setVolume(t){var n;(n=this.mount)==null||n.controller.setVolume(t)}titleAttr(){return this.getAttribute("title")??x}themeAttr(){const t=this.getAttribute("theme");return t==="light"||t==="dark"?t:"auto"}setAspectRatio(t,n){this.style.aspectRatio=`${t} / ${n}`}render(){this.style.display="block",this.style.position="relative",this.style.width="100%",this.setAspectRatio(T,C);const t=document.createElement("iframe");t.setAttribute("title",this.titleAttr()),t.setAttribute("loading","lazy"),t.setAttribute("allow","autoplay; fullscreen"),t.setAttribute("allowfullscreen",""),t.style.cssText="position:absolute;inset:0;width:100%;height:100%;border:0;border-radius:inherit",this.appendChild(t),this.iframe=t}attach(){var n;const t=this.getAttribute("src");!t||!this.iframe||((n=this.mount)==null||n.destroy(),this.mount=O(this.iframe,{src:t,theme:this.themeAttr(),onAspectRatio:({widthPx:r,heightPx:a})=>this.setAspectRatio(r,a),onEvent:r=>this.dispatchEvent(new CustomEvent(`scenar:${r.type}`,{detail:r}))}))}}function b(e=k){typeof customElements>"u"||customElements.get(e)||customElements.define(e,y)}return b(),o.ScenarEmbedElement=y,o.defineScenarEmbed=b,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"}),o}({});
|
|
@@ -20,6 +20,12 @@
|
|
|
20
20
|
"sizeBytes": 30458,
|
|
21
21
|
"contentType": "text/css"
|
|
22
22
|
},
|
|
23
|
+
{
|
|
24
|
+
"path": "embed.js",
|
|
25
|
+
"sha256": "87ebc6301237b7b99b1f7eb8ab5e4e89362888b7d03a289af592e0cd38922c99",
|
|
26
|
+
"sizeBytes": 4574,
|
|
27
|
+
"contentType": "text/javascript"
|
|
28
|
+
},
|
|
23
29
|
{
|
|
24
30
|
"path": "index.html",
|
|
25
31
|
"sha256": "242a0d2b71f1cc01a1c61a75fb605ae35997d0d943a9b4bd71fd982c7aef51e3",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scenar/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Scenar CLI — validate scenarios, generate narration, and render videos.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -39,13 +39,9 @@
|
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"@
|
|
44
|
-
"@
|
|
45
|
-
"@scenar/core": "0.2.1",
|
|
46
|
-
"@scenar/preview": "0.2.1",
|
|
47
|
-
"@scenar/sdk": "0.2.1",
|
|
48
|
-
"@scenar/stubs": "0.2.1",
|
|
42
|
+
"@scenar/core": "0.4.0",
|
|
43
|
+
"@scenar/embed": "0.4.0",
|
|
44
|
+
"@scenar/sdk": "0.4.0",
|
|
49
45
|
"commander": "^13.0.0",
|
|
50
46
|
"yaml": "^2.7.0"
|
|
51
47
|
},
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { mkdtemp, readFile, rm, stat } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { copyEmbedLoader, EMBED_LOADER_FILE } from "../pack/embed-loader.js";
|
|
6
|
+
|
|
7
|
+
describe("copyEmbedLoader", () => {
|
|
8
|
+
it("copies @scenar/embed's loader into the bundle as embed.js (idempotent)", async () => {
|
|
9
|
+
const dir = await mkdtemp(join(tmpdir(), "scenar-loader-"));
|
|
10
|
+
try {
|
|
11
|
+
await copyEmbedLoader(dir);
|
|
12
|
+
|
|
13
|
+
const dest = join(dir, EMBED_LOADER_FILE);
|
|
14
|
+
const info = await stat(dest);
|
|
15
|
+
expect(info.isFile()).toBe(true);
|
|
16
|
+
expect(info.size).toBeGreaterThan(0);
|
|
17
|
+
|
|
18
|
+
// It is the IIFE loader that registers the custom element.
|
|
19
|
+
const js = await readFile(dest, "utf-8");
|
|
20
|
+
expect(js).toContain("scenar-embed");
|
|
21
|
+
|
|
22
|
+
// Copying again overwrites cleanly (same bytes) — pack can re-run safely.
|
|
23
|
+
await copyEmbedLoader(dir);
|
|
24
|
+
expect(await readFile(dest, "utf-8")).toBe(js);
|
|
25
|
+
} finally {
|
|
26
|
+
await rm(dir, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { buildEmbedSnippet } from "../
|
|
2
|
+
import { buildEmbedSnippet, buildEnhancedEmbedSnippet } from "../embed/embed-snippet.js";
|
|
3
3
|
|
|
4
4
|
describe("buildEmbedSnippet", () => {
|
|
5
5
|
const embedUrl = "https://d-dep1.scenarusercontent.net/";
|
|
@@ -54,3 +54,50 @@ describe("buildEmbedSnippet", () => {
|
|
|
54
54
|
expect(snippet).toContain("max-width:1280px");
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
|
+
|
|
58
|
+
describe("buildEnhancedEmbedSnippet", () => {
|
|
59
|
+
const viewport = { width: 896, height: 480 };
|
|
60
|
+
|
|
61
|
+
it("loads the bundle's sibling embed.js and renders <scenar-embed>", () => {
|
|
62
|
+
const snippet = buildEnhancedEmbedSnippet({
|
|
63
|
+
embedUrl: "https://you.github.io/scenar-embeds/welcome/",
|
|
64
|
+
viewport,
|
|
65
|
+
});
|
|
66
|
+
expect(snippet).toContain(
|
|
67
|
+
'<script type="module" src="https://you.github.io/scenar-embeds/welcome/embed.js"></script>',
|
|
68
|
+
);
|
|
69
|
+
expect(snippet).toContain('<scenar-embed');
|
|
70
|
+
expect(snippet).toContain('src="https://you.github.io/scenar-embeds/welcome/"');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("resolves embed.js relative to the embed URL on a local serve", () => {
|
|
74
|
+
const snippet = buildEnhancedEmbedSnippet({
|
|
75
|
+
embedUrl: "http://localhost:4173/",
|
|
76
|
+
viewport,
|
|
77
|
+
});
|
|
78
|
+
expect(snippet).toContain('src="http://localhost:4173/embed.js"');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("uses a default title and honors an explicit one", () => {
|
|
82
|
+
const def = buildEnhancedEmbedSnippet({ embedUrl: "https://x.test/", viewport });
|
|
83
|
+
expect(def).toContain('title="Scenar embed"');
|
|
84
|
+
|
|
85
|
+
const named = buildEnhancedEmbedSnippet({
|
|
86
|
+
embedUrl: "https://x.test/",
|
|
87
|
+
viewport,
|
|
88
|
+
title: "Welcome tour",
|
|
89
|
+
});
|
|
90
|
+
expect(named).toContain('title="Welcome tour"');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("escapes attribute-breaking characters", () => {
|
|
94
|
+
const snippet = buildEnhancedEmbedSnippet({
|
|
95
|
+
embedUrl: 'https://x.test/?a="1"/',
|
|
96
|
+
viewport,
|
|
97
|
+
title: '<b>"&',
|
|
98
|
+
});
|
|
99
|
+
expect(snippet).not.toContain('"1"');
|
|
100
|
+
expect(snippet).not.toContain("<b>");
|
|
101
|
+
expect(snippet).toContain(""");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtemp, rm, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { createProgram } from "../index.js";
|
|
7
|
+
|
|
8
|
+
describe("scenar install (command surface)", () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
process.exitCode = undefined;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("registers the install command", () => {
|
|
14
|
+
const program = createProgram();
|
|
15
|
+
const cmd = program.commands.find((c) => c.name() === "install");
|
|
16
|
+
expect(cmd).toBeDefined();
|
|
17
|
+
expect(cmd!.description()).toContain("Bootstrap a Scenar demos project");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("takes variadic packages and exposes its options", () => {
|
|
21
|
+
const program = createProgram();
|
|
22
|
+
const cmd = program.commands.find((c) => c.name() === "install");
|
|
23
|
+
expect(cmd!.registeredArguments).toHaveLength(1);
|
|
24
|
+
expect(cmd!.registeredArguments[0]!.variadic).toBe(true);
|
|
25
|
+
const opts = cmd!.options.map((o) => o.long);
|
|
26
|
+
expect(opts).toContain("--dir");
|
|
27
|
+
expect(opts).toContain("--no-install");
|
|
28
|
+
expect(opts).toContain("--package-manager");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("scenar install (end-to-end, --no-install)", () => {
|
|
33
|
+
let dir: string;
|
|
34
|
+
|
|
35
|
+
beforeEach(async () => {
|
|
36
|
+
dir = await mkdtemp(join(tmpdir(), "scenar-install-cmd-"));
|
|
37
|
+
process.exitCode = undefined;
|
|
38
|
+
vi.spyOn(process.stderr, "write").mockImplementation(() => true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
afterEach(async () => {
|
|
42
|
+
vi.restoreAllMocks();
|
|
43
|
+
await rm(dir, { recursive: true, force: true });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
async function runInstallCommand(args: string[]): Promise<void> {
|
|
47
|
+
const program = createProgram();
|
|
48
|
+
program.exitOverride();
|
|
49
|
+
try {
|
|
50
|
+
await program.parseAsync(["node", "scenar", "install", ...args]);
|
|
51
|
+
} catch {
|
|
52
|
+
// commander throws under exitOverride; assertions cover the outcome
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
it("scaffolds a runnable starter tour on first run", async () => {
|
|
57
|
+
await runInstallCommand(["--no-install", "--dir", dir]);
|
|
58
|
+
|
|
59
|
+
expect(process.exitCode).not.toBe(1);
|
|
60
|
+
expect(existsSync(join(dir, "package.json"))).toBe(true);
|
|
61
|
+
expect(existsSync(join(dir, "tsconfig.json"))).toBe(true);
|
|
62
|
+
expect(existsSync(join(dir, ".gitignore"))).toBe(true);
|
|
63
|
+
expect(existsSync(join(dir, "tours/example-tour/steps.ts"))).toBe(true);
|
|
64
|
+
expect(existsSync(join(dir, "tours/example-tour/index.tsx"))).toBe(true);
|
|
65
|
+
expect(existsSync(join(dir, "tours/example-tour/.scenar/providers.tsx"))).toBe(true);
|
|
66
|
+
|
|
67
|
+
const index = await readFile(join(dir, "tours/example-tour/index.tsx"), "utf-8");
|
|
68
|
+
expect(index).toContain("renderStep");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("never overwrites authored files on re-run", async () => {
|
|
72
|
+
await runInstallCommand(["--no-install", "--dir", dir]);
|
|
73
|
+
|
|
74
|
+
// Every scaffolded file is author-owned, so edits must survive a re-run.
|
|
75
|
+
const indexPath = join(dir, "tours/example-tour/index.tsx");
|
|
76
|
+
const marker = "// EDITED BY AUTHOR — must survive re-run\n";
|
|
77
|
+
const original = await readFile(indexPath, "utf-8");
|
|
78
|
+
await writeFile(indexPath, marker + original, "utf-8");
|
|
79
|
+
|
|
80
|
+
await runInstallCommand(["--no-install", "--dir", dir]);
|
|
81
|
+
|
|
82
|
+
const after = await readFile(indexPath, "utf-8");
|
|
83
|
+
expect(after.startsWith(marker)).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("records a named dependency in package.json", async () => {
|
|
87
|
+
await runInstallCommand(["--no-install", "--dir", dir, "@stigmer/react@^1.0.0"]);
|
|
88
|
+
|
|
89
|
+
const pkg = JSON.parse(await readFile(join(dir, "package.json"), "utf-8"));
|
|
90
|
+
expect(pkg.dependencies["@stigmer/react"]).toBe("^1.0.0");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("rejects an invalid --package-manager", async () => {
|
|
94
|
+
await runInstallCommand(["--no-install", "--dir", dir, "--package-manager", "bun"]);
|
|
95
|
+
expect(process.exitCode).toBe(1);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { localViewUrl } from "../serve/local-url.js";
|
|
3
|
+
|
|
4
|
+
describe("localViewUrl", () => {
|
|
5
|
+
it("downgrades https to http for *.localhost hosts (local edges serve http)", () => {
|
|
6
|
+
expect(localViewUrl("https://d-abc.localhost:8787/")).toBe("http://d-abc.localhost:8787/");
|
|
7
|
+
expect(localViewUrl("https://localhost:8787/")).toBe("http://localhost:8787/");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("leaves production https URLs untouched", () => {
|
|
11
|
+
expect(localViewUrl("https://d-abc.scenar.ai/")).toBe("https://d-abc.scenar.ai/");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("returns non-URL input unchanged", () => {
|
|
15
|
+
expect(localViewUrl("not a url")).toBe("not a url");
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtemp, rm, mkdir, writeFile, readFile } from "node:fs/promises";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
runInstall,
|
|
8
|
+
parseSpec,
|
|
9
|
+
detectPackageManager,
|
|
10
|
+
findWorkspaceRoot,
|
|
11
|
+
} from "../install/run-install.js";
|
|
12
|
+
|
|
13
|
+
const silent = () => {};
|
|
14
|
+
|
|
15
|
+
describe("parseSpec", () => {
|
|
16
|
+
const cwd = "/tmp/does-not-matter";
|
|
17
|
+
|
|
18
|
+
it("parses a bare name", () => {
|
|
19
|
+
expect(parseSpec("react", cwd)).toEqual({ name: "react", range: "" });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("parses a scoped bare name", () => {
|
|
23
|
+
expect(parseSpec("@stigmer/react", cwd)).toEqual({
|
|
24
|
+
name: "@stigmer/react",
|
|
25
|
+
range: "",
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("parses name@range", () => {
|
|
30
|
+
expect(parseSpec("react@^19.0.0", cwd)).toEqual({
|
|
31
|
+
name: "react",
|
|
32
|
+
range: "^19.0.0",
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("parses @scope/name@range", () => {
|
|
37
|
+
expect(parseSpec("@stigmer/react@1.2.0", cwd)).toEqual({
|
|
38
|
+
name: "@stigmer/react",
|
|
39
|
+
range: "1.2.0",
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("rejects git/URL specs without a name", () => {
|
|
44
|
+
expect(() => parseSpec("git+https://example.com/x.git", cwd)).toThrow(
|
|
45
|
+
/no package name/,
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("detectPackageManager", () => {
|
|
51
|
+
let dir: string;
|
|
52
|
+
|
|
53
|
+
beforeEach(async () => {
|
|
54
|
+
dir = await mkdtemp(join(tmpdir(), "scenar-pm-"));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterEach(async () => {
|
|
58
|
+
await rm(dir, { recursive: true, force: true });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("defaults to npm when no lockfile is present", () => {
|
|
62
|
+
expect(detectPackageManager(dir)).toBe("npm");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("detects pnpm", async () => {
|
|
66
|
+
await writeFile(join(dir, "pnpm-lock.yaml"), "", "utf-8");
|
|
67
|
+
expect(detectPackageManager(dir)).toBe("pnpm");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("detects yarn", async () => {
|
|
71
|
+
await writeFile(join(dir, "yarn.lock"), "", "utf-8");
|
|
72
|
+
expect(detectPackageManager(dir)).toBe("yarn");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("detects npm from package-lock.json", async () => {
|
|
76
|
+
await writeFile(join(dir, "package-lock.json"), "{}", "utf-8");
|
|
77
|
+
expect(detectPackageManager(dir)).toBe("npm");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("findWorkspaceRoot", () => {
|
|
82
|
+
let root: string;
|
|
83
|
+
|
|
84
|
+
beforeEach(async () => {
|
|
85
|
+
root = await mkdtemp(join(tmpdir(), "scenar-ws-"));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
afterEach(async () => {
|
|
89
|
+
await rm(root, { recursive: true, force: true });
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("detects a member via pnpm-workspace.yaml globs", async () => {
|
|
93
|
+
await writeFile(
|
|
94
|
+
join(root, "pnpm-workspace.yaml"),
|
|
95
|
+
"packages:\n - 'packages/*'\n",
|
|
96
|
+
"utf-8",
|
|
97
|
+
);
|
|
98
|
+
const member = join(root, "packages", "demos");
|
|
99
|
+
await mkdir(member, { recursive: true });
|
|
100
|
+
expect(findWorkspaceRoot(member)).toBe(root);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("detects a member via package.json workspaces", async () => {
|
|
104
|
+
await writeFile(
|
|
105
|
+
join(root, "package.json"),
|
|
106
|
+
JSON.stringify({ name: "root", workspaces: ["apps/*"] }),
|
|
107
|
+
"utf-8",
|
|
108
|
+
);
|
|
109
|
+
const member = join(root, "apps", "web");
|
|
110
|
+
await mkdir(member, { recursive: true });
|
|
111
|
+
expect(findWorkspaceRoot(member)).toBe(root);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("returns null for a standalone directory", async () => {
|
|
115
|
+
const standalone = join(root, "standalone");
|
|
116
|
+
await mkdir(standalone, { recursive: true });
|
|
117
|
+
expect(findWorkspaceRoot(standalone)).toBeNull();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("runInstall (skipInstall)", () => {
|
|
122
|
+
let dir: string;
|
|
123
|
+
|
|
124
|
+
beforeEach(async () => {
|
|
125
|
+
dir = await mkdtemp(join(tmpdir(), "scenar-install-"));
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
afterEach(async () => {
|
|
129
|
+
await rm(dir, { recursive: true, force: true });
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
async function readPkg(d: string) {
|
|
133
|
+
return JSON.parse(await readFile(join(d, "package.json"), "utf-8"));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
it("scaffolds a fresh project with a runnable starter tour", () => {
|
|
137
|
+
const result = runInstall({ cwd: dir, packages: [], onLog: silent, skipInstall: true });
|
|
138
|
+
expect(result.scaffolded).toBe(true);
|
|
139
|
+
expect(existsSync(join(dir, "package.json"))).toBe(true);
|
|
140
|
+
expect(existsSync(join(dir, ".gitignore"))).toBe(true);
|
|
141
|
+
expect(existsSync(join(dir, "tsconfig.json"))).toBe(true);
|
|
142
|
+
expect(existsSync(join(dir, "tours/example-tour/steps.ts"))).toBe(true);
|
|
143
|
+
expect(existsSync(join(dir, "tours/example-tour/index.tsx"))).toBe(true);
|
|
144
|
+
expect(existsSync(join(dir, "tours/example-tour/.scenar/providers.tsx"))).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("records a bare package as '*'", async () => {
|
|
148
|
+
runInstall({ cwd: dir, packages: ["@stigmer/react"], onLog: silent, skipInstall: true });
|
|
149
|
+
const pkg = await readPkg(dir);
|
|
150
|
+
expect(pkg.dependencies["@stigmer/react"]).toBe("*");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("records an explicit range", async () => {
|
|
154
|
+
runInstall({ cwd: dir, packages: ["@stigmer/react@^1.2.0"], onLog: silent, skipInstall: true });
|
|
155
|
+
const pkg = await readPkg(dir);
|
|
156
|
+
expect(pkg.dependencies["@stigmer/react"]).toBe("^1.2.0");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("does not clobber an existing pin when given a bare name", async () => {
|
|
160
|
+
runInstall({ cwd: dir, packages: ["@stigmer/react@^1.2.0"], onLog: silent, skipInstall: true });
|
|
161
|
+
runInstall({ cwd: dir, packages: ["@stigmer/react"], onLog: silent, skipInstall: true });
|
|
162
|
+
const pkg = await readPkg(dir);
|
|
163
|
+
expect(pkg.dependencies["@stigmer/react"]).toBe("^1.2.0");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("resolves a file: spec's name from its target package.json", async () => {
|
|
167
|
+
const lib = join(dir, "..", `lib-${Date.now()}`);
|
|
168
|
+
await mkdir(lib, { recursive: true });
|
|
169
|
+
await writeFile(
|
|
170
|
+
join(lib, "package.json"),
|
|
171
|
+
JSON.stringify({ name: "@local/lib", version: "0.0.0" }),
|
|
172
|
+
"utf-8",
|
|
173
|
+
);
|
|
174
|
+
try {
|
|
175
|
+
runInstall({
|
|
176
|
+
cwd: dir,
|
|
177
|
+
packages: [`file:${lib}`],
|
|
178
|
+
onLog: silent,
|
|
179
|
+
skipInstall: true,
|
|
180
|
+
});
|
|
181
|
+
const pkg = await readPkg(dir);
|
|
182
|
+
expect(pkg.dependencies["@local/lib"]).toBe(`file:${lib}`);
|
|
183
|
+
} finally {
|
|
184
|
+
await rm(lib, { recursive: true, force: true });
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("uses workspace:* for a workspace member under pnpm", async () => {
|
|
189
|
+
const root = await mkdtemp(join(tmpdir(), "scenar-ws-install-"));
|
|
190
|
+
try {
|
|
191
|
+
await writeFile(join(root, "pnpm-lock.yaml"), "", "utf-8");
|
|
192
|
+
await writeFile(
|
|
193
|
+
join(root, "pnpm-workspace.yaml"),
|
|
194
|
+
"packages:\n - 'packages/*'\n",
|
|
195
|
+
"utf-8",
|
|
196
|
+
);
|
|
197
|
+
const reactPkg = join(root, "packages", "react");
|
|
198
|
+
await mkdir(reactPkg, { recursive: true });
|
|
199
|
+
await writeFile(
|
|
200
|
+
join(reactPkg, "package.json"),
|
|
201
|
+
JSON.stringify({ name: "@stigmer/react", version: "0.0.0" }),
|
|
202
|
+
"utf-8",
|
|
203
|
+
);
|
|
204
|
+
const demos = join(root, "packages", "demos");
|
|
205
|
+
await mkdir(demos, { recursive: true });
|
|
206
|
+
|
|
207
|
+
const result = runInstall({
|
|
208
|
+
cwd: demos,
|
|
209
|
+
packages: ["@stigmer/react"],
|
|
210
|
+
onLog: silent,
|
|
211
|
+
skipInstall: true,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
expect(result.workspaceRoot).toBe(root);
|
|
215
|
+
expect(result.packageManager).toBe("pnpm");
|
|
216
|
+
const pkg = await readPkg(demos);
|
|
217
|
+
expect(pkg.dependencies["@stigmer/react"]).toBe("workspace:*");
|
|
218
|
+
} finally {
|
|
219
|
+
await rm(root, { recursive: true, force: true });
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
});
|
package/src/api.d.ts
CHANGED
|
@@ -21,11 +21,13 @@ export type { RunPublishOptions, PublishRunResult } from "./publish/run-publish.
|
|
|
21
21
|
export { runPublishFlow, validateRepoName, validatePublishPath, normalizePath, pagesUrl, repoUrl, } from "./publish/publish-flow.js";
|
|
22
22
|
export type { PublishFlowDeps, PublishOptions, PublishResult, } from "./publish/publish-flow.js";
|
|
23
23
|
export { createGithubPublisher } from "./publish/github.js";
|
|
24
|
+
export { runInstall } from "./install/run-install.js";
|
|
25
|
+
export type { RunInstallOptions, RunInstallResult, ParsedSpec, PackageManager, } from "./install/run-install.js";
|
|
24
26
|
export { validateScenario } from "./validate/scenario-validator.js";
|
|
25
27
|
export { loadScenarioYaml } from "./util/load-yaml.js";
|
|
26
28
|
export { readBundleViewport } from "./bundle/read-viewport.js";
|
|
27
29
|
export type { BundleViewport } from "./bundle/read-viewport.js";
|
|
28
|
-
export { buildEmbedSnippet } from "./
|
|
30
|
+
export { buildEmbedSnippet } from "./embed/embed-snippet.js";
|
|
29
31
|
export { DEFAULT_VIEWPORT, parseViewport } from "./pack/viewport.js";
|
|
30
32
|
export type { Viewport } from "./pack/viewport.js";
|
|
31
33
|
//# sourceMappingURL=api.d.ts.map
|
package/src/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrE,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAGnG,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG7E,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACjF,YAAY,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAG7F,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACpF,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,QAAQ,EACR,OAAO,GACR,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,eAAe,EACf,cAAc,EACd,aAAa,GACd,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrE,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAGnG,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG7E,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACjF,YAAY,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAG7F,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACpF,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,QAAQ,EACR,OAAO,GACR,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,eAAe,EACf,cAAc,EACd,aAAa,GACd,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,YAAY,EACV,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,cAAc,GACf,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACrE,YAAY,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC"}
|
package/src/api.js
CHANGED
|
@@ -18,11 +18,13 @@ export { startBundleServer, createBundleServer } from "./serve/static-server.js"
|
|
|
18
18
|
export { runPublish } from "./publish/run-publish.js";
|
|
19
19
|
export { runPublishFlow, validateRepoName, validatePublishPath, normalizePath, pagesUrl, repoUrl, } from "./publish/publish-flow.js";
|
|
20
20
|
export { createGithubPublisher } from "./publish/github.js";
|
|
21
|
+
// Install (bootstrap a demos project: scaffold + add deps + run the package manager)
|
|
22
|
+
export { runInstall } from "./install/run-install.js";
|
|
21
23
|
// Validate
|
|
22
24
|
export { validateScenario } from "./validate/scenario-validator.js";
|
|
23
25
|
export { loadScenarioYaml } from "./util/load-yaml.js";
|
|
24
26
|
// Bundle helpers (viewport + embed snippet)
|
|
25
27
|
export { readBundleViewport } from "./bundle/read-viewport.js";
|
|
26
|
-
export { buildEmbedSnippet } from "./
|
|
28
|
+
export { buildEmbedSnippet } from "./embed/embed-snippet.js";
|
|
27
29
|
export { DEFAULT_VIEWPORT, parseViewport } from "./pack/viewport.js";
|
|
28
30
|
//# sourceMappingURL=api.js.map
|
package/src/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO;AACP,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAI7C,UAAU;AACV,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGtD,SAAS;AACT,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGnD,QAAQ;AACR,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGjF,yBAAyB;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,QAAQ,EACR,OAAO,GACR,MAAM,2BAA2B,CAAC;AAMnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,WAAW;AACX,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,4CAA4C;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO;AACP,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAI7C,UAAU;AACV,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGtD,SAAS;AACT,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGnD,QAAQ;AACR,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGjF,yBAAyB;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,QAAQ,EACR,OAAO,GACR,MAAM,2BAA2B,CAAC;AAMnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,qFAAqF;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAQtD,WAAW;AACX,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,4CAA4C;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
|
package/src/api.ts
CHANGED
|
@@ -43,6 +43,15 @@ export type {
|
|
|
43
43
|
} from "./publish/publish-flow.js";
|
|
44
44
|
export { createGithubPublisher } from "./publish/github.js";
|
|
45
45
|
|
|
46
|
+
// Install (bootstrap a demos project: scaffold + add deps + run the package manager)
|
|
47
|
+
export { runInstall } from "./install/run-install.js";
|
|
48
|
+
export type {
|
|
49
|
+
RunInstallOptions,
|
|
50
|
+
RunInstallResult,
|
|
51
|
+
ParsedSpec,
|
|
52
|
+
PackageManager,
|
|
53
|
+
} from "./install/run-install.js";
|
|
54
|
+
|
|
46
55
|
// Validate
|
|
47
56
|
export { validateScenario } from "./validate/scenario-validator.js";
|
|
48
57
|
export { loadScenarioYaml } from "./util/load-yaml.js";
|
|
@@ -50,6 +59,6 @@ export { loadScenarioYaml } from "./util/load-yaml.js";
|
|
|
50
59
|
// Bundle helpers (viewport + embed snippet)
|
|
51
60
|
export { readBundleViewport } from "./bundle/read-viewport.js";
|
|
52
61
|
export type { BundleViewport } from "./bundle/read-viewport.js";
|
|
53
|
-
export { buildEmbedSnippet } from "./
|
|
62
|
+
export { buildEmbedSnippet } from "./embed/embed-snippet.js";
|
|
54
63
|
export { DEFAULT_VIEWPORT, parseViewport } from "./pack/viewport.js";
|
|
55
64
|
export type { Viewport } from "./pack/viewport.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/commands/install.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+C7D"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { runInstall } from "../install/run-install.js";
|
|
3
|
+
const PACKAGE_MANAGERS = ["npm", "yarn", "pnpm"];
|
|
4
|
+
export function registerInstallCommand(program) {
|
|
5
|
+
program
|
|
6
|
+
.command("install")
|
|
7
|
+
.description("Bootstrap a Scenar demos project in one step.\n\n" +
|
|
8
|
+
"Scaffolds a package.json and a runnable starter tour under\n" +
|
|
9
|
+
"tours/example-tour/ if the directory is empty, adds the given\n" +
|
|
10
|
+
"component packages as ordinary dependencies, and runs your package\n" +
|
|
11
|
+
"manager. Re-run any time — existing files are never overwritten, so\n" +
|
|
12
|
+
"your authored tours are always preserved.\n\n" +
|
|
13
|
+
"Component packages are consumed as normal dependencies — import and\n" +
|
|
14
|
+
"compose them into the tours you author under tours/. Specs are\n" +
|
|
15
|
+
"resolver-agnostic: a registry version (@stigmer/react@1.2.0),\n" +
|
|
16
|
+
"workspace:*, file:../pkg, or a git URL all work; resolution is your\n" +
|
|
17
|
+
"package manager's job.\n\n" +
|
|
18
|
+
"Inside a monorepo, the project is detected as a workspace member and\n" +
|
|
19
|
+
"the install runs at the workspace root.")
|
|
20
|
+
.argument("[packages...]", "component packages to add as dependencies (e.g. @stigmer/react)")
|
|
21
|
+
.option("--dir <path>", "project directory (default: current directory)")
|
|
22
|
+
.option("--no-install", "scaffold and record dependencies but skip the install")
|
|
23
|
+
.option("--package-manager <pm>", "force npm | yarn | pnpm instead of auto-detecting")
|
|
24
|
+
.action(async (packages, options) => {
|
|
25
|
+
try {
|
|
26
|
+
const cwd = path.resolve(options.dir ?? process.cwd());
|
|
27
|
+
const installResult = runInstall({
|
|
28
|
+
cwd,
|
|
29
|
+
packages: packages ?? [],
|
|
30
|
+
onLog: (line) => process.stderr.write(`${line}\n`),
|
|
31
|
+
skipInstall: options.install === false,
|
|
32
|
+
packageManager: parsePackageManager(options.packageManager),
|
|
33
|
+
});
|
|
34
|
+
printNextSteps(cwd, installResult.installRan);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
38
|
+
process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function parsePackageManager(value) {
|
|
44
|
+
if (value === undefined)
|
|
45
|
+
return undefined;
|
|
46
|
+
if (PACKAGE_MANAGERS.includes(value)) {
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`invalid --package-manager "${value}"; expected one of ${PACKAGE_MANAGERS.join(", ")}.`);
|
|
50
|
+
}
|
|
51
|
+
function printNextSteps(cwd, installRan) {
|
|
52
|
+
const rel = path.relative(process.cwd(), cwd);
|
|
53
|
+
// Prefer a short relative path, but fall back to the absolute path when the
|
|
54
|
+
// target is outside the current directory (avoids a wall of "../").
|
|
55
|
+
const where = rel === "" ? "." : rel.startsWith("..") ? cwd : rel;
|
|
56
|
+
process.stderr.write(`\n\x1b[32m✓\x1b[0m Scenar demos project ready in ${where}/\n`);
|
|
57
|
+
if (!installRan) {
|
|
58
|
+
process.stderr.write(` \x1b[33m●\x1b[0m Install skipped — run your package manager before packing.\n`);
|
|
59
|
+
}
|
|
60
|
+
process.stderr.write(` Next:\n`);
|
|
61
|
+
process.stderr.write(` 1. \`scenar pack tours/example-tour\` to preview the starter tour.\n`);
|
|
62
|
+
process.stderr.write(` 2. Edit tours/example-tour/index.tsx — swap the shells for your\n`);
|
|
63
|
+
process.stderr.write(` real components (add them with \`scenar install <your-pkg>\`).\n`);
|
|
64
|
+
process.stderr.write(` 3. Wire any data they fetch in tours/example-tour/.scenar/providers.tsx.\n`);
|
|
65
|
+
process.stderr.write(` 4. \`scenar pack\` then \`scenar publish\` to ship the embed.\n`);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/commands/install.ts"],"names":[],"mappings":"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"}
|