@scenar/cli 0.3.0 → 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/package.json +4 -9
- package/src/__tests__/embed-snippet.test.ts +1 -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/serve.d.ts.map +1 -1
- package/src/commands/serve.js +1 -2
- package/src/commands/serve.js.map +1 -1
- package/src/commands/serve.ts +1 -2
- package/src/commands/try.js +1 -1
- package/src/commands/try.ts +1 -1
- package/src/embed/embed-snippet.d.ts.map +1 -0
- package/src/embed/embed-snippet.js.map +1 -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/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/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.js +1 -1
- package/src/publish/run-publish.js.map +1 -1
- package/src/publish/run-publish.ts +1 -1
- 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.js +2 -2
- package/src/serve/run-serve.js.map +1 -1
- package/src/serve/run-serve.ts +2 -2
- 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
- /package/src/{deploy → embed}/embed-snippet.d.ts +0 -0
- /package/src/{deploy → embed}/embed-snippet.js +0 -0
- /package/src/{deploy → embed}/embed-snippet.ts +0 -0
package/src/commands/pack.ts
CHANGED
|
@@ -20,11 +20,11 @@ export function registerPackCommand(program: Command): void {
|
|
|
20
20
|
.command("pack")
|
|
21
21
|
.description(
|
|
22
22
|
"Bundle a scenario into a self-contained, hosted embed.\n\n" +
|
|
23
|
-
"Accepts the same scenario directory as `render
|
|
23
|
+
"Accepts the same scenario directory as `render`\n" +
|
|
24
24
|
"(steps.ts + an index.tsx that exports renderStep, plus an\n" +
|
|
25
25
|
"optional .scenar/providers.tsx and narration/). Produces a static\n" +
|
|
26
26
|
"bundle that boots ScenarioPlayer in the browser, ready for\n" +
|
|
27
|
-
"`scenar serve
|
|
27
|
+
"`scenar serve` or `scenar publish`.\n\n" +
|
|
28
28
|
"The bundle contains index.html, hashed JS/CSS, a scenario.json\n" +
|
|
29
29
|
"descriptor, and a pack-manifest.json listing every file with its\n" +
|
|
30
30
|
"lowercase-hex sha256 and content type. Output stays within the\n" +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../../src/commands/serve.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../../src/commands/serve.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+D3D"}
|
package/src/commands/serve.js
CHANGED
|
@@ -14,8 +14,7 @@ export function registerServeCommand(program) {
|
|
|
14
14
|
"Prints the URL plus a ready-to-paste responsive <iframe> snippet, then\n" +
|
|
15
15
|
"stays running until you press Ctrl+C.\n\n" +
|
|
16
16
|
"This is the zero-infrastructure way to view an embed: no backend, no\n" +
|
|
17
|
-
"account. For a permanent public URL, use `scenar publish` (GitHub
|
|
18
|
-
"Pages) or `scenar deploy` (Scenar Cloud).")
|
|
17
|
+
"account. For a permanent public URL, use `scenar publish` (GitHub Pages).")
|
|
19
18
|
.argument("<bundleDir>", "path to a packed bundle directory (from scenar pack)")
|
|
20
19
|
.option("--port <number>", `port to listen on (default: ${DEFAULT_PORT})`, String(DEFAULT_PORT))
|
|
21
20
|
.option("--host <host>", `host/interface to bind (default: ${DEFAULT_HOST})`, DEFAULT_HOST)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,wEAAwE;AACxE,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,yEAAyE;AACzE,MAAM,YAAY,GAAG,WAAW,CAAC;AAQjC,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CACV,4DAA4D;QAC1D,yEAAyE;QACzE,uEAAuE;QACvE,0EAA0E;QAC1E,2CAA2C;QAC3C,wEAAwE;QACxE,
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/commands/serve.ts"],"names":[],"mappings":"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"}
|
package/src/commands/serve.ts
CHANGED
|
@@ -26,8 +26,7 @@ export function registerServeCommand(program: Command): void {
|
|
|
26
26
|
"Prints the URL plus a ready-to-paste responsive <iframe> snippet, then\n" +
|
|
27
27
|
"stays running until you press Ctrl+C.\n\n" +
|
|
28
28
|
"This is the zero-infrastructure way to view an embed: no backend, no\n" +
|
|
29
|
-
"account. For a permanent public URL, use `scenar publish` (GitHub
|
|
30
|
-
"Pages) or `scenar deploy` (Scenar Cloud).",
|
|
29
|
+
"account. For a permanent public URL, use `scenar publish` (GitHub Pages).",
|
|
31
30
|
)
|
|
32
31
|
.argument("<bundleDir>", "path to a packed bundle directory (from scenar pack)")
|
|
33
32
|
.option("--port <number>", `port to listen on (default: ${DEFAULT_PORT})`, String(DEFAULT_PORT))
|
package/src/commands/try.js
CHANGED
|
@@ -24,7 +24,7 @@ export function registerTryCommand(program) {
|
|
|
24
24
|
"ready-to-paste <iframe> snippet, then stays running until Ctrl+C.\n\n" +
|
|
25
25
|
"This is the fastest way to see what a Scenar embed looks like before\n" +
|
|
26
26
|
"wiring up your own app. To build a tour from your components, see\n" +
|
|
27
|
-
"`scenar
|
|
27
|
+
"`scenar install`; to host one, see `scenar publish`.")
|
|
28
28
|
.option("--port <number>", `port to listen on (default: ${DEFAULT_PORT})`, String(DEFAULT_PORT))
|
|
29
29
|
.option("--host <host>", `host/interface to bind (default: ${DEFAULT_HOST})`, DEFAULT_HOST)
|
|
30
30
|
.option("--open", "open the embed in your default browser")
|
package/src/commands/try.ts
CHANGED
|
@@ -37,7 +37,7 @@ export function registerTryCommand(program: Command): void {
|
|
|
37
37
|
"ready-to-paste <iframe> snippet, then stays running until Ctrl+C.\n\n" +
|
|
38
38
|
"This is the fastest way to see what a Scenar embed looks like before\n" +
|
|
39
39
|
"wiring up your own app. To build a tour from your components, see\n" +
|
|
40
|
-
"`scenar
|
|
40
|
+
"`scenar install`; to host one, see `scenar publish`.",
|
|
41
41
|
)
|
|
42
42
|
.option("--port <number>", `port to listen on (default: ${DEFAULT_PORT})`, String(DEFAULT_PORT))
|
|
43
43
|
.option("--host <host>", `host/interface to bind (default: ${DEFAULT_HOST})`, DEFAULT_HOST)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed-snippet.d.ts","sourceRoot":"","sources":["../../../src/embed/embed-snippet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAelE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAW1E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed-snippet.js","sourceRoot":"","sources":["../../../src/embed/embed-snippet.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAwB;IACxD,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC;IAC5C,OAAO;QACL,sDAAsD,QAAQ,CAAC,KAAK,mBAAmB,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,MAAM,IAAI;QAC5H,WAAW;QACX,YAAY,UAAU,CAAC,QAAQ,CAAC,GAAG;QACnC,cAAc,UAAU,CAAC,KAAK,CAAC,GAAG;QAClC,oBAAoB;QACpB,uEAAuE;QACvE,kCAAkC;QAClC,qBAAqB;QACrB,cAAc;QACd,QAAQ;KACT,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAwB;IAChE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC3D,OAAO;QACL,8BAA8B,UAAU,CAAC,SAAS,CAAC,aAAa;QAChE,eAAe;QACf,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG;QACjC,YAAY,UAAU,CAAC,KAAK,CAAC,GAAG;QAChC,kBAAkB;KACnB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,kFAAkF;AAClF,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3B,CAAC"}
|
package/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,aAAa,IAAI,OAAO,CAkBvC;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAGxC"}
|
package/src/index.js
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { registerValidateCommand } from "./commands/validate.js";
|
|
3
3
|
import { registerNarrateCommand } from "./commands/narrate.js";
|
|
4
|
-
import {
|
|
4
|
+
import { registerInstallCommand } from "./commands/install.js";
|
|
5
5
|
import { registerRenderCommand } from "./commands/render.js";
|
|
6
6
|
import { registerPackCommand } from "./commands/pack.js";
|
|
7
7
|
import { registerServeCommand } from "./commands/serve.js";
|
|
8
8
|
import { registerTryCommand } from "./commands/try.js";
|
|
9
9
|
import { registerPublishCommand } from "./commands/publish.js";
|
|
10
|
-
import { registerDeployCommand } from "./commands/deploy.js";
|
|
11
10
|
export function createProgram() {
|
|
12
11
|
const program = new Command();
|
|
13
12
|
program
|
|
14
13
|
.name("scenar")
|
|
15
|
-
.description("Scenar CLI —
|
|
14
|
+
.description("Scenar CLI — install a demos project, author scenarios, then pack, serve, and publish embeds.")
|
|
16
15
|
.version("0.0.1");
|
|
17
16
|
registerValidateCommand(program);
|
|
18
17
|
registerNarrateCommand(program);
|
|
19
18
|
registerRenderCommand(program);
|
|
20
|
-
|
|
19
|
+
registerInstallCommand(program);
|
|
21
20
|
registerPackCommand(program);
|
|
22
21
|
registerServeCommand(program);
|
|
23
22
|
registerTryCommand(program);
|
|
24
23
|
registerPublishCommand(program);
|
|
25
|
-
registerDeployCommand(program);
|
|
26
24
|
return program;
|
|
27
25
|
}
|
|
28
26
|
export function run(argv) {
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,QAAQ,CAAC;SACd,WAAW,CAAC,+FAA+F,CAAC;SAC5G,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjC,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAEhC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAAc;IAChC,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC"}
|
package/src/index.ts
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { registerValidateCommand } from "./commands/validate.js";
|
|
3
3
|
import { registerNarrateCommand } from "./commands/narrate.js";
|
|
4
|
-
import {
|
|
4
|
+
import { registerInstallCommand } from "./commands/install.js";
|
|
5
5
|
import { registerRenderCommand } from "./commands/render.js";
|
|
6
6
|
import { registerPackCommand } from "./commands/pack.js";
|
|
7
7
|
import { registerServeCommand } from "./commands/serve.js";
|
|
8
8
|
import { registerTryCommand } from "./commands/try.js";
|
|
9
9
|
import { registerPublishCommand } from "./commands/publish.js";
|
|
10
|
-
import { registerDeployCommand } from "./commands/deploy.js";
|
|
11
10
|
|
|
12
11
|
export function createProgram(): Command {
|
|
13
12
|
const program = new Command();
|
|
14
13
|
|
|
15
14
|
program
|
|
16
15
|
.name("scenar")
|
|
17
|
-
.description("Scenar CLI —
|
|
16
|
+
.description("Scenar CLI — install a demos project, author scenarios, then pack, serve, and publish embeds.")
|
|
18
17
|
.version("0.0.1");
|
|
19
18
|
|
|
20
19
|
registerValidateCommand(program);
|
|
21
20
|
registerNarrateCommand(program);
|
|
22
21
|
registerRenderCommand(program);
|
|
23
|
-
|
|
22
|
+
registerInstallCommand(program);
|
|
24
23
|
registerPackCommand(program);
|
|
25
24
|
registerServeCommand(program);
|
|
26
25
|
registerTryCommand(program);
|
|
27
26
|
registerPublishCommand(program);
|
|
28
|
-
registerDeployCommand(program);
|
|
29
27
|
|
|
30
28
|
return program;
|
|
31
29
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Supported package managers, detected by lockfile / workspace manifest. */
|
|
2
|
+
export type PackageManager = "npm" | "yarn" | "pnpm";
|
|
3
|
+
/** A dependency parsed from a positional install argument. */
|
|
4
|
+
export interface ParsedSpec {
|
|
5
|
+
/** The package name (registry key written into `dependencies`). */
|
|
6
|
+
readonly name: string;
|
|
7
|
+
/**
|
|
8
|
+
* The version range / locator to record. Empty string means "bare" — the
|
|
9
|
+
* concrete value is decided at merge time (`workspace:*` for a workspace
|
|
10
|
+
* member, otherwise `*`, later pinned to the installed version).
|
|
11
|
+
*/
|
|
12
|
+
readonly range: string;
|
|
13
|
+
}
|
|
14
|
+
export interface RunInstallOptions {
|
|
15
|
+
/** Project directory to operate in (absolute or relative to cwd). */
|
|
16
|
+
readonly cwd: string;
|
|
17
|
+
/** Positional dependency specs to add (names, ranges, file:/git/workspace). */
|
|
18
|
+
readonly packages: readonly string[];
|
|
19
|
+
/** Line-oriented log sink (the CLI wires this to stderr). */
|
|
20
|
+
readonly onLog: (line: string) => void;
|
|
21
|
+
/** Scaffold and record deps but skip the package-manager subprocess. */
|
|
22
|
+
readonly skipInstall?: boolean;
|
|
23
|
+
/** Force a package manager instead of detecting one. */
|
|
24
|
+
readonly packageManager?: PackageManager;
|
|
25
|
+
}
|
|
26
|
+
export interface RunInstallResult {
|
|
27
|
+
/** Whether a fresh project was scaffolded (no prior `package.json`). */
|
|
28
|
+
readonly scaffolded: boolean;
|
|
29
|
+
/** Files created by scaffolding, relative to the project directory. */
|
|
30
|
+
readonly scaffoldCreated: readonly string[];
|
|
31
|
+
/** Dependencies added (or already present) from the positional specs. */
|
|
32
|
+
readonly added: readonly ParsedSpec[];
|
|
33
|
+
/** The package manager used. */
|
|
34
|
+
readonly packageManager: PackageManager;
|
|
35
|
+
/** Workspace root if the project is a workspace member, else null. */
|
|
36
|
+
readonly workspaceRoot: string | null;
|
|
37
|
+
/** Whether the package-manager install actually ran. */
|
|
38
|
+
readonly installRan: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Bootstrap a Scenar demos project: scaffold (if empty), record the requested
|
|
42
|
+
* component packages as ordinary dependencies, and run the package manager.
|
|
43
|
+
*
|
|
44
|
+
* The command is intentionally resolver-agnostic — it only writes dependency
|
|
45
|
+
* entries and delegates resolution to the package manager — so a positional
|
|
46
|
+
* argument can be a registry version, a `workspace:*` spec, a `file:`/`link:`
|
|
47
|
+
* path, or a git URL. Dependency resolution is the package manager's job, not
|
|
48
|
+
* ours.
|
|
49
|
+
*/
|
|
50
|
+
export declare function runInstall(options: RunInstallOptions): RunInstallResult;
|
|
51
|
+
/**
|
|
52
|
+
* Parse one positional install argument into a `{ name, range }` pair.
|
|
53
|
+
*
|
|
54
|
+
* Handles the common forms (`name`, `name@range`, `@scope/name`,
|
|
55
|
+
* `@scope/name@range`), resolves `file:`/`link:` names from the target's
|
|
56
|
+
* `package.json`, and rejects git/URL specs that carry no package name (the
|
|
57
|
+
* user should add those to `package.json` directly, then re-run).
|
|
58
|
+
*/
|
|
59
|
+
export declare function parseSpec(raw: string, cwd: string): ParsedSpec;
|
|
60
|
+
/**
|
|
61
|
+
* Walk up from `startDir` to find the nearest workspace root that actually
|
|
62
|
+
* contains `startDir` as a member — a directory with a `pnpm-workspace.yaml`
|
|
63
|
+
* or a `package.json` `workspaces` field whose globs match `startDir`. Returns
|
|
64
|
+
* null when the project is standalone.
|
|
65
|
+
*/
|
|
66
|
+
export declare function findWorkspaceRoot(startDir: string): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* Detect the package manager by the nearest lockfile, walking up from `dir`.
|
|
69
|
+
* Defaults to npm when nothing is found.
|
|
70
|
+
*/
|
|
71
|
+
export declare function detectPackageManager(dir: string): PackageManager;
|
|
72
|
+
//# sourceMappingURL=run-install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-install.d.ts","sourceRoot":"","sources":["../../../src/install/run-install.ts"],"names":[],"mappings":"AAMA,6EAA6E;AAC7E,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,wEAAwE;IACxE,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,wDAAwD;IACxD,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,gCAAgC;IAChC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,sEAAsE;IACtE,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,wDAAwD;IACxD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAyDvE;AAUD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CA4B9D;AAyED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcjE;AA8GD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAUhE"}
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { parse as parseYaml } from "yaml";
|
|
5
|
+
import { scaffoldProject } from "./scaffold.js";
|
|
6
|
+
/**
|
|
7
|
+
* Bootstrap a Scenar demos project: scaffold (if empty), record the requested
|
|
8
|
+
* component packages as ordinary dependencies, and run the package manager.
|
|
9
|
+
*
|
|
10
|
+
* The command is intentionally resolver-agnostic — it only writes dependency
|
|
11
|
+
* entries and delegates resolution to the package manager — so a positional
|
|
12
|
+
* argument can be a registry version, a `workspace:*` spec, a `file:`/`link:`
|
|
13
|
+
* path, or a git URL. Dependency resolution is the package manager's job, not
|
|
14
|
+
* ours.
|
|
15
|
+
*/
|
|
16
|
+
export function runInstall(options) {
|
|
17
|
+
const cwd = path.resolve(options.cwd);
|
|
18
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
19
|
+
// --- 1. Scaffold a fresh project if there's no package.json yet. ---
|
|
20
|
+
let scaffolded = false;
|
|
21
|
+
let scaffold = { created: [] };
|
|
22
|
+
if (!fs.existsSync(pkgPath)) {
|
|
23
|
+
scaffold = scaffoldProject(cwd);
|
|
24
|
+
scaffolded = true;
|
|
25
|
+
options.onLog(`\x1b[36m●\x1b[0m Scaffolded a new Scenar demos project`);
|
|
26
|
+
for (const file of scaffold.created) {
|
|
27
|
+
options.onLog(` \x1b[32m✓\x1b[0m ${file}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// --- 2. Detect workspace + package manager. ---
|
|
31
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
32
|
+
const packageManager = options.packageManager ?? detectPackageManager(workspaceRoot ?? cwd);
|
|
33
|
+
const installDir = workspaceRoot ?? cwd;
|
|
34
|
+
// --- 3. Record the requested packages as dependencies. ---
|
|
35
|
+
const added = options.packages.map((raw) => parseSpec(raw, cwd));
|
|
36
|
+
if (added.length > 0) {
|
|
37
|
+
const memberNames = workspaceRoot
|
|
38
|
+
? workspaceMemberNames(workspaceRoot)
|
|
39
|
+
: new Set();
|
|
40
|
+
mergeDependencies(pkgPath, added, { packageManager, memberNames });
|
|
41
|
+
options.onLog(`\x1b[36m●\x1b[0m Added dependencies`);
|
|
42
|
+
for (const spec of added) {
|
|
43
|
+
options.onLog(` \x1b[32m✓\x1b[0m ${spec.name}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// --- 4. Install. ---
|
|
47
|
+
let installRan = false;
|
|
48
|
+
if (!options.skipInstall) {
|
|
49
|
+
options.onLog(`\n\x1b[36m●\x1b[0m Installing with ${packageManager}` +
|
|
50
|
+
(workspaceRoot ? ` (workspace root: ${path.relative(cwd, installDir) || "."})` : ""));
|
|
51
|
+
runPackageManagerInstall(packageManager, installDir);
|
|
52
|
+
installRan = true;
|
|
53
|
+
// Bare registry specs were written as "*"; pin them to what actually
|
|
54
|
+
// resolved so the project gets a clean, reproducible package.json.
|
|
55
|
+
pinResolvedRanges(pkgPath, added);
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
scaffolded,
|
|
59
|
+
scaffoldCreated: scaffold.created,
|
|
60
|
+
added,
|
|
61
|
+
packageManager,
|
|
62
|
+
workspaceRoot,
|
|
63
|
+
installRan,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Dependency-spec parsing
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
/** Locator-prefixed specs whose name lives in the target, not the string. */
|
|
70
|
+
const PATH_SPEC_RE = /^(file:|link:)(.+)$/;
|
|
71
|
+
const URL_SPEC_RE = /^(git\+|git:|https?:|github:|bitbucket:|gitlab:)/;
|
|
72
|
+
/**
|
|
73
|
+
* Parse one positional install argument into a `{ name, range }` pair.
|
|
74
|
+
*
|
|
75
|
+
* Handles the common forms (`name`, `name@range`, `@scope/name`,
|
|
76
|
+
* `@scope/name@range`), resolves `file:`/`link:` names from the target's
|
|
77
|
+
* `package.json`, and rejects git/URL specs that carry no package name (the
|
|
78
|
+
* user should add those to `package.json` directly, then re-run).
|
|
79
|
+
*/
|
|
80
|
+
export function parseSpec(raw, cwd) {
|
|
81
|
+
const spec = raw.trim();
|
|
82
|
+
const pathMatch = PATH_SPEC_RE.exec(spec);
|
|
83
|
+
if (pathMatch) {
|
|
84
|
+
const target = path.resolve(cwd, pathMatch[2]);
|
|
85
|
+
const name = readPackageName(target);
|
|
86
|
+
if (!name) {
|
|
87
|
+
throw new Error(`could not read a package name from "${spec}". Make sure the path ` +
|
|
88
|
+
`points at a directory containing a package.json.`);
|
|
89
|
+
}
|
|
90
|
+
return { name, range: spec };
|
|
91
|
+
}
|
|
92
|
+
if (URL_SPEC_RE.test(spec)) {
|
|
93
|
+
throw new Error(`git/URL dependency "${spec}" has no package name. Add it to ` +
|
|
94
|
+
`package.json (e.g. "your-pkg": "${spec}") and run \`scenar install\`.`);
|
|
95
|
+
}
|
|
96
|
+
// name | name@range | @scope/name | @scope/name@range
|
|
97
|
+
const scoped = spec.startsWith("@");
|
|
98
|
+
const at = spec.indexOf("@", scoped ? 1 : 0);
|
|
99
|
+
if (at === -1)
|
|
100
|
+
return { name: spec, range: "" };
|
|
101
|
+
return { name: spec.slice(0, at), range: spec.slice(at + 1) };
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Merge parsed specs into `dependencies`, idempotently. Existing pins are kept
|
|
105
|
+
* when the user passed a bare name; an explicit range always wins. Bare names
|
|
106
|
+
* that match a workspace member become `workspace:*` (pnpm/yarn) or `*` (npm),
|
|
107
|
+
* and other bare names default to `*` (pinned post-install).
|
|
108
|
+
*/
|
|
109
|
+
function mergeDependencies(pkgPath, specs, ctx) {
|
|
110
|
+
const pkg = readJson(pkgPath);
|
|
111
|
+
const deps = { ...(pkg.dependencies ?? {}) };
|
|
112
|
+
for (const { name, range } of specs) {
|
|
113
|
+
if (range === "" && deps[name])
|
|
114
|
+
continue; // keep existing pin
|
|
115
|
+
if (range !== "") {
|
|
116
|
+
deps[name] = range;
|
|
117
|
+
}
|
|
118
|
+
else if (ctx.memberNames.has(name)) {
|
|
119
|
+
deps[name] =
|
|
120
|
+
ctx.packageManager === "npm" ? "*" : "workspace:*";
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
deps[name] = "*";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
pkg.dependencies = sortKeys(deps);
|
|
127
|
+
writeJson(pkgPath, pkg);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Replace any `"*"` dependency ranges with `^<installed version>` after a
|
|
131
|
+
* successful install, so a bare `scenar install @foo/bar` yields a clean,
|
|
132
|
+
* reproducible pin. Best-effort: workspace specs and unresolved packages are
|
|
133
|
+
* left untouched.
|
|
134
|
+
*/
|
|
135
|
+
function pinResolvedRanges(pkgPath, specs) {
|
|
136
|
+
const pkg = readJson(pkgPath);
|
|
137
|
+
const deps = pkg.dependencies ?? {};
|
|
138
|
+
const nodeModules = path.join(path.dirname(pkgPath), "node_modules");
|
|
139
|
+
let changed = false;
|
|
140
|
+
for (const name of new Set(specs.map((s) => s.name))) {
|
|
141
|
+
if (deps[name] !== "*")
|
|
142
|
+
continue;
|
|
143
|
+
const version = readPackageVersion(path.join(nodeModules, name));
|
|
144
|
+
if (version) {
|
|
145
|
+
deps[name] = `^${version}`;
|
|
146
|
+
changed = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (changed) {
|
|
150
|
+
pkg.dependencies = deps;
|
|
151
|
+
writeJson(pkgPath, pkg);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// Workspace + package-manager detection
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
/**
|
|
158
|
+
* Walk up from `startDir` to find the nearest workspace root that actually
|
|
159
|
+
* contains `startDir` as a member — a directory with a `pnpm-workspace.yaml`
|
|
160
|
+
* or a `package.json` `workspaces` field whose globs match `startDir`. Returns
|
|
161
|
+
* null when the project is standalone.
|
|
162
|
+
*/
|
|
163
|
+
export function findWorkspaceRoot(startDir) {
|
|
164
|
+
let dir = path.resolve(startDir);
|
|
165
|
+
// The project's own package.json is never its workspace root, so start above.
|
|
166
|
+
let current = path.dirname(dir);
|
|
167
|
+
while (true) {
|
|
168
|
+
const globs = readWorkspaceGlobs(current);
|
|
169
|
+
if (globs && matchesAnyGlob(path.relative(current, dir), globs)) {
|
|
170
|
+
return current;
|
|
171
|
+
}
|
|
172
|
+
const parent = path.dirname(current);
|
|
173
|
+
if (parent === current)
|
|
174
|
+
return null;
|
|
175
|
+
current = parent;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** Read workspace package globs from pnpm-workspace.yaml or package.json. */
|
|
179
|
+
function readWorkspaceGlobs(dir) {
|
|
180
|
+
const pnpmManifest = path.join(dir, "pnpm-workspace.yaml");
|
|
181
|
+
if (fs.existsSync(pnpmManifest)) {
|
|
182
|
+
try {
|
|
183
|
+
const parsed = parseYaml(fs.readFileSync(pnpmManifest, "utf-8"));
|
|
184
|
+
if (Array.isArray(parsed?.packages))
|
|
185
|
+
return parsed.packages;
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// Malformed manifest — fall through to package.json.
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const pkgPath = path.join(dir, "package.json");
|
|
192
|
+
if (fs.existsSync(pkgPath)) {
|
|
193
|
+
try {
|
|
194
|
+
const pkg = readJson(pkgPath);
|
|
195
|
+
const ws = pkg.workspaces;
|
|
196
|
+
if (Array.isArray(ws))
|
|
197
|
+
return ws;
|
|
198
|
+
if (ws && Array.isArray(ws.packages)) {
|
|
199
|
+
return ws.packages;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// Ignore unreadable package.json.
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
/** Test a member path (relative to the workspace root) against workspace globs. */
|
|
209
|
+
function matchesAnyGlob(relMember, globs) {
|
|
210
|
+
const normalized = relMember.split(path.sep).join("/");
|
|
211
|
+
return globs.some((glob) => globToRegExp(glob).test(normalized));
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Convert a workspace glob (e.g. `packages/*`, `apps/**`) to a RegExp. Supports
|
|
215
|
+
* the `*` (one segment) and `**` (any depth) wildcards that workspace manifests
|
|
216
|
+
* use; everything else is matched literally.
|
|
217
|
+
*/
|
|
218
|
+
function globToRegExp(glob) {
|
|
219
|
+
const trimmed = glob.replace(/\/+$/, "");
|
|
220
|
+
let re = "";
|
|
221
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
222
|
+
const c = trimmed[i];
|
|
223
|
+
if (c === "*") {
|
|
224
|
+
if (trimmed[i + 1] === "*") {
|
|
225
|
+
re += ".*";
|
|
226
|
+
i++;
|
|
227
|
+
if (trimmed[i + 1] === "/")
|
|
228
|
+
i++;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
re += "[^/]*";
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else if (/[.+?^${}()|[\]\\]/.test(c)) {
|
|
235
|
+
re += "\\" + c;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
re += c;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return new RegExp(`^${re}$`);
|
|
242
|
+
}
|
|
243
|
+
/** Collect the package names of all workspace members under `root`. */
|
|
244
|
+
function workspaceMemberNames(root) {
|
|
245
|
+
const names = new Set();
|
|
246
|
+
const globs = readWorkspaceGlobs(root);
|
|
247
|
+
if (!globs)
|
|
248
|
+
return names;
|
|
249
|
+
for (const glob of globs) {
|
|
250
|
+
for (const memberDir of expandGlobDirs(root, glob)) {
|
|
251
|
+
const name = readPackageName(memberDir);
|
|
252
|
+
if (name)
|
|
253
|
+
names.add(name);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return names;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Expand a single workspace glob into existing member directories. Only the
|
|
260
|
+
* `pkgs/*` and `pkgs/**` shapes that workspace manifests use are supported;
|
|
261
|
+
* each candidate is kept only if it contains a `package.json`.
|
|
262
|
+
*/
|
|
263
|
+
function expandGlobDirs(root, glob) {
|
|
264
|
+
const trimmed = glob.replace(/\/+$/, "");
|
|
265
|
+
const parts = trimmed.split("/");
|
|
266
|
+
let dirs = [root];
|
|
267
|
+
for (const part of parts) {
|
|
268
|
+
const next = [];
|
|
269
|
+
for (const dir of dirs) {
|
|
270
|
+
if (part === "*" || part === "**") {
|
|
271
|
+
for (const entry of safeReaddir(dir)) {
|
|
272
|
+
const full = path.join(dir, entry.name);
|
|
273
|
+
if (entry.isDirectory())
|
|
274
|
+
next.push(full);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
const full = path.join(dir, part);
|
|
279
|
+
if (fs.existsSync(full))
|
|
280
|
+
next.push(full);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
dirs = next;
|
|
284
|
+
}
|
|
285
|
+
return dirs.filter((dir) => fs.existsSync(path.join(dir, "package.json")));
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Detect the package manager by the nearest lockfile, walking up from `dir`.
|
|
289
|
+
* Defaults to npm when nothing is found.
|
|
290
|
+
*/
|
|
291
|
+
export function detectPackageManager(dir) {
|
|
292
|
+
let current = path.resolve(dir);
|
|
293
|
+
while (true) {
|
|
294
|
+
if (fs.existsSync(path.join(current, "pnpm-lock.yaml")))
|
|
295
|
+
return "pnpm";
|
|
296
|
+
if (fs.existsSync(path.join(current, "yarn.lock")))
|
|
297
|
+
return "yarn";
|
|
298
|
+
if (fs.existsSync(path.join(current, "package-lock.json")))
|
|
299
|
+
return "npm";
|
|
300
|
+
const parent = path.dirname(current);
|
|
301
|
+
if (parent === current)
|
|
302
|
+
return "npm";
|
|
303
|
+
current = parent;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// ---------------------------------------------------------------------------
|
|
307
|
+
// Subprocess
|
|
308
|
+
// ---------------------------------------------------------------------------
|
|
309
|
+
/** Run `<pm> install` in `dir`, throwing a clear error on failure. */
|
|
310
|
+
function runPackageManagerInstall(pm, dir) {
|
|
311
|
+
const result = spawnSync(pm, ["install"], {
|
|
312
|
+
cwd: dir,
|
|
313
|
+
stdio: "inherit",
|
|
314
|
+
// Windows resolves npm/yarn/pnpm via the shell.
|
|
315
|
+
shell: process.platform === "win32",
|
|
316
|
+
});
|
|
317
|
+
if (result.error) {
|
|
318
|
+
const code = result.error.code;
|
|
319
|
+
if (code === "ENOENT") {
|
|
320
|
+
throw new Error(`${pm} was not found on your PATH. Install it, or pass ` +
|
|
321
|
+
`--package-manager <npm|yarn|pnpm>.`);
|
|
322
|
+
}
|
|
323
|
+
throw result.error;
|
|
324
|
+
}
|
|
325
|
+
if (typeof result.status === "number" && result.status !== 0) {
|
|
326
|
+
throw new Error(`${pm} install failed (exit code ${result.status}). If a package is ` +
|
|
327
|
+
`private or unpublished, check your registry access and authentication.`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// ---------------------------------------------------------------------------
|
|
331
|
+
// JSON / fs helpers
|
|
332
|
+
// ---------------------------------------------------------------------------
|
|
333
|
+
function readJson(file) {
|
|
334
|
+
return JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
335
|
+
}
|
|
336
|
+
function writeJson(file, value) {
|
|
337
|
+
fs.writeFileSync(file, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
338
|
+
}
|
|
339
|
+
function readPackageName(dir) {
|
|
340
|
+
const pkgPath = path.join(dir, "package.json");
|
|
341
|
+
if (!fs.existsSync(pkgPath))
|
|
342
|
+
return null;
|
|
343
|
+
try {
|
|
344
|
+
const name = readJson(pkgPath).name;
|
|
345
|
+
return typeof name === "string" && name.length > 0 ? name : null;
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function readPackageVersion(dir) {
|
|
352
|
+
const pkgPath = path.join(dir, "package.json");
|
|
353
|
+
if (!fs.existsSync(pkgPath))
|
|
354
|
+
return null;
|
|
355
|
+
try {
|
|
356
|
+
const version = readJson(pkgPath).version;
|
|
357
|
+
return typeof version === "string" && version.length > 0 ? version : null;
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function safeReaddir(dir) {
|
|
364
|
+
try {
|
|
365
|
+
return fs.readdirSync(dir, { withFileTypes: true });
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
return [];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/** Sort object keys so generated package.json deps stay stable across runs. */
|
|
372
|
+
function sortKeys(obj) {
|
|
373
|
+
const sorted = {};
|
|
374
|
+
for (const key of Object.keys(obj).sort())
|
|
375
|
+
sorted[key] = obj[key];
|
|
376
|
+
return sorted;
|
|
377
|
+
}
|
|
378
|
+
//# sourceMappingURL=run-install.js.map
|