@archastro/astroshot 0.1.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/LICENSE +21 -0
- package/README.md +57 -0
- package/bin/astroshot.mjs +215 -0
- package/bin/templates.mjs +186 -0
- package/ink.d.ts +5 -0
- package/ink.js +1 -0
- package/package.json +74 -0
- package/pty.d.ts +8 -0
- package/pty.js +1 -0
- package/react.d.ts +1 -0
- package/react.js +1 -0
- package/tui.d.ts +1 -0
- package/tui.js +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ArchAstro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# `@archastro/astroshot`
|
|
2
|
+
|
|
3
|
+
One command for deterministic React, Ink, and arbitrary terminal screenshots.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx --@archastro:registry=https://registry.npmjs.org \
|
|
7
|
+
@archastro/astroshot init react
|
|
8
|
+
npx --@archastro:registry=https://registry.npmjs.org \
|
|
9
|
+
@archastro/astroshot init ink
|
|
10
|
+
npx --@archastro:registry=https://registry.npmjs.org \
|
|
11
|
+
@archastro/astroshot init pty
|
|
12
|
+
|
|
13
|
+
npx --@archastro:registry=https://registry.npmjs.org \
|
|
14
|
+
@archastro/astroshot react ./react.shot.tsx -o ./react.png
|
|
15
|
+
npx --@archastro:registry=https://registry.npmjs.org \
|
|
16
|
+
@archastro/astroshot ink ./ink.shot.tsx -o ./ink.png
|
|
17
|
+
npx --@archastro:registry=https://registry.npmjs.org \
|
|
18
|
+
@archastro/astroshot pty ./pty.shot.yaml -o ./terminal.png
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Use `react` for isolated browser components, `ink` for in-process Ink fixture
|
|
22
|
+
trees, and `pty` for executable terminal applications such as Ratatui, Bubble
|
|
23
|
+
Tea, Textual, and curses programs. `tui` remains an alias for `ink` for
|
|
24
|
+
compatibility.
|
|
25
|
+
|
|
26
|
+
Ink fixtures require project-local peers:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install --save-dev ink@^7.1 react@^19
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Install the shared Chromium runtime once:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx --@archastro:registry=https://registry.npmjs.org \
|
|
36
|
+
@archastro/astroshot install-browser
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Use `react batch <manifest>` or `ink batch <manifest>` for maintained fixture
|
|
40
|
+
sets. Run `<mode> --help` for mode-specific options.
|
|
41
|
+
|
|
42
|
+
Fixture types are available from the unified package:
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import type { ReactShotFixture } from "@archastro/astroshot/react";
|
|
46
|
+
import type { InkShotFixture } from "@archastro/astroshot/ink";
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
PTY YAML and JSON files launch `command` directly, without a shell. Their
|
|
50
|
+
`actions` can wait for visible text, send named keys or literal text, and
|
|
51
|
+
pause for bounded durations. Paths in `cwd` are relative to the fixture.
|
|
52
|
+
On Windows, point `command` at an `.exe`; `.cmd` and `.bat` files are rejected
|
|
53
|
+
because running them would introduce an implicit shell.
|
|
54
|
+
|
|
55
|
+
Fixtures, imported modules, and PTY commands execute with the current user's
|
|
56
|
+
permissions. Review untrusted files before capture and never include
|
|
57
|
+
credentials or private customer data in fixtures or generated images.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
import { writeFixtureTemplate } from "./templates.mjs";
|
|
9
|
+
|
|
10
|
+
function help() {
|
|
11
|
+
console.log(`astroshot — one CLI for React, Ink, and PTY screenshots
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
astroshot init react [fixture.tsx] [--force]
|
|
15
|
+
astroshot init ink [fixture.tsx] [--force]
|
|
16
|
+
astroshot init pty [fixture.yaml] [--force]
|
|
17
|
+
astroshot react <fixture.tsx> -o <out.png> [options]
|
|
18
|
+
astroshot react shot <fixture.tsx> -o <out.png> [options]
|
|
19
|
+
astroshot react batch <manifest.yaml|json> [options]
|
|
20
|
+
astroshot ink <fixture.tsx> -o <out.png> [options]
|
|
21
|
+
astroshot ink batch <manifest.yaml|json> [options]
|
|
22
|
+
astroshot pty <fixture.yaml|json> -o <out.png> [options]
|
|
23
|
+
astroshot install-browser [--with-deps]
|
|
24
|
+
|
|
25
|
+
Commands:
|
|
26
|
+
react Capture an isolated React component
|
|
27
|
+
ink Capture an Ink component fixture
|
|
28
|
+
pty Capture any executable in a pseudoterminal
|
|
29
|
+
init Generate a React, Ink, or PTY fixture template
|
|
30
|
+
install-browser Install the shared Chromium runtime
|
|
31
|
+
|
|
32
|
+
Compatibility: "astroshot tui" remains an alias for "astroshot ink".
|
|
33
|
+
Run "astroshot <mode> --help" for mode options.`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function modeHelp(mode) {
|
|
37
|
+
if (mode === "react") {
|
|
38
|
+
console.log(`astroshot react — deterministic React component screenshots
|
|
39
|
+
|
|
40
|
+
Usage:
|
|
41
|
+
astroshot init react [fixture.tsx] [--force]
|
|
42
|
+
astroshot react <fixture.tsx> -o <out.png> [options]
|
|
43
|
+
astroshot react batch <manifest.yaml|json> [options]
|
|
44
|
+
|
|
45
|
+
Options:
|
|
46
|
+
-o, --out <path> Output PNG path
|
|
47
|
+
--root <dir> Package root for imports and aliases
|
|
48
|
+
--config <path> Fixture configuration path
|
|
49
|
+
--width <px> Override viewport width
|
|
50
|
+
--height <px> Override viewport height
|
|
51
|
+
--headed Show Chromium for debugging
|
|
52
|
+
-h, --help Show this help`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (mode === "ink") {
|
|
57
|
+
console.log(`astroshot ink — deterministic Ink component screenshots
|
|
58
|
+
|
|
59
|
+
Usage:
|
|
60
|
+
astroshot init ink [fixture.tsx] [--force]
|
|
61
|
+
astroshot ink <fixture.tsx> -o <out.png> [options]
|
|
62
|
+
astroshot ink batch <manifest.yaml|json> [options]
|
|
63
|
+
|
|
64
|
+
Options:
|
|
65
|
+
-o, --out <path> Output PNG path
|
|
66
|
+
--cols <count> Override terminal columns
|
|
67
|
+
--rows <count> Override terminal rows
|
|
68
|
+
--scale <factor> Override PNG device scale factor
|
|
69
|
+
--out-dir <path> Override batch output directory
|
|
70
|
+
--headed Show Chromium for debugging
|
|
71
|
+
-h, --help Show this help`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`astroshot pty — screenshots of arbitrary terminal programs
|
|
76
|
+
|
|
77
|
+
Usage:
|
|
78
|
+
astroshot init pty [fixture.yaml] [--force]
|
|
79
|
+
astroshot pty <fixture.yaml|json> -o <out.png> [options]
|
|
80
|
+
|
|
81
|
+
Options:
|
|
82
|
+
-o, --out <path> Output PNG path
|
|
83
|
+
--cols <count> Override terminal columns
|
|
84
|
+
--rows <count> Override terminal rows
|
|
85
|
+
--scale <factor> Override PNG device scale factor
|
|
86
|
+
--headed Show Chromium for debugging
|
|
87
|
+
-h, --help Show this help`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function engineBin(mode) {
|
|
91
|
+
const packageName =
|
|
92
|
+
mode === "react" ? "@archastro/react-shot" : "@archastro/tui-shot";
|
|
93
|
+
const executable = mode === "react" ? "react-shot.mjs" : "tui-shot.mjs";
|
|
94
|
+
const entry = fileURLToPath(import.meta.resolve(packageName));
|
|
95
|
+
const packageRoot = path.dirname(path.dirname(entry));
|
|
96
|
+
return path.join(packageRoot, "bin", executable);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function runEngine(mode, arguments_) {
|
|
100
|
+
const engineMode = mode === "react" ? "react" : "tui";
|
|
101
|
+
const engineArguments =
|
|
102
|
+
mode === "pty" ? ["pty", ...arguments_] : arguments_;
|
|
103
|
+
const normalized =
|
|
104
|
+
mode !== "pty" &&
|
|
105
|
+
engineArguments[0] &&
|
|
106
|
+
/\.[cm]?tsx?$/.test(engineArguments[0])
|
|
107
|
+
? ["shot", ...engineArguments]
|
|
108
|
+
: engineArguments;
|
|
109
|
+
const result = spawnSync(
|
|
110
|
+
process.execPath,
|
|
111
|
+
[engineBin(engineMode), ...normalized],
|
|
112
|
+
{ stdio: "inherit" },
|
|
113
|
+
);
|
|
114
|
+
if (result.error) {
|
|
115
|
+
console.error(`astroshot could not start ${mode} capture: ${result.error.message}`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
if (result.signal) process.kill(process.pid, result.signal);
|
|
119
|
+
process.exit(result.status ?? 1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function initHelp() {
|
|
123
|
+
console.log(`astroshot init — generate a fixture template
|
|
124
|
+
|
|
125
|
+
Usage:
|
|
126
|
+
astroshot init react [fixture.tsx] [--force]
|
|
127
|
+
astroshot init ink [fixture.tsx] [--force]
|
|
128
|
+
astroshot init pty [fixture.yaml] [--force]
|
|
129
|
+
|
|
130
|
+
Defaults:
|
|
131
|
+
react.shot.tsx
|
|
132
|
+
ink.shot.tsx
|
|
133
|
+
pty.shot.yaml
|
|
134
|
+
|
|
135
|
+
Existing files are never replaced unless --force is passed.`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function runInit(arguments_) {
|
|
139
|
+
if (
|
|
140
|
+
arguments_.length === 0 ||
|
|
141
|
+
arguments_[0] === "help" ||
|
|
142
|
+
arguments_[0] === "-h" ||
|
|
143
|
+
arguments_[0] === "--help"
|
|
144
|
+
) {
|
|
145
|
+
initHelp();
|
|
146
|
+
process.exit(arguments_.length === 0 ? 1 : 0);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const mode = arguments_[0];
|
|
150
|
+
const rest = arguments_.slice(1);
|
|
151
|
+
const force = rest.includes("--force") || rest.includes("-f");
|
|
152
|
+
const unknownFlags = rest.filter(
|
|
153
|
+
(value) => value.startsWith("-") && value !== "--force" && value !== "-f",
|
|
154
|
+
);
|
|
155
|
+
if (unknownFlags.length) {
|
|
156
|
+
throw new Error(`Unknown init flag: ${unknownFlags[0]}`);
|
|
157
|
+
}
|
|
158
|
+
const positionals = rest.filter((value) => !value.startsWith("-"));
|
|
159
|
+
if (positionals.length > 1) {
|
|
160
|
+
throw new Error("init accepts at most one output path");
|
|
161
|
+
}
|
|
162
|
+
const result = writeFixtureTemplate({
|
|
163
|
+
mode,
|
|
164
|
+
outputPath: positionals[0],
|
|
165
|
+
force,
|
|
166
|
+
});
|
|
167
|
+
console.log(`Created ${result.label} fixture: ${result.absolutePath}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const [command, ...arguments_] = process.argv.slice(2);
|
|
171
|
+
|
|
172
|
+
if (command === "-v" || command === "--version") {
|
|
173
|
+
const packageJSON = JSON.parse(
|
|
174
|
+
fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"),
|
|
175
|
+
);
|
|
176
|
+
console.log(packageJSON.version);
|
|
177
|
+
process.exit(0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!command || command === "help" || command === "-h" || command === "--help") {
|
|
181
|
+
help();
|
|
182
|
+
process.exit(command ? 0 : 1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (command === "install-browser") {
|
|
186
|
+
runEngine("react", ["install-browser", ...arguments_]);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (command === "init") {
|
|
190
|
+
try {
|
|
191
|
+
runInit(arguments_);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error(error instanceof Error ? error.message : error);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
process.exit(0);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (command === "react" || command === "ink" || command === "tui" || command === "pty") {
|
|
200
|
+
const canonicalMode = command === "tui" ? "ink" : command;
|
|
201
|
+
if (
|
|
202
|
+
arguments_.length === 0 ||
|
|
203
|
+
arguments_[0] === "help" ||
|
|
204
|
+
arguments_[0] === "-h" ||
|
|
205
|
+
arguments_[0] === "--help"
|
|
206
|
+
) {
|
|
207
|
+
modeHelp(canonicalMode);
|
|
208
|
+
process.exit(arguments_.length === 0 ? 1 : 0);
|
|
209
|
+
}
|
|
210
|
+
runEngine(canonicalMode, arguments_);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.error(`Unknown command: ${command}`);
|
|
214
|
+
help();
|
|
215
|
+
process.exit(1);
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const REACT_TEMPLATE = `import type { ReactShotFixture } from "@archastro/astroshot/react";
|
|
5
|
+
|
|
6
|
+
function ExampleCard() {
|
|
7
|
+
return (
|
|
8
|
+
<article
|
|
9
|
+
data-astroshot
|
|
10
|
+
style={{
|
|
11
|
+
width: 420,
|
|
12
|
+
padding: 24,
|
|
13
|
+
borderRadius: 16,
|
|
14
|
+
background: "#f8fafc",
|
|
15
|
+
color: "#0f172a",
|
|
16
|
+
fontFamily: "system-ui, sans-serif",
|
|
17
|
+
boxShadow: "0 20px 50px rgba(15, 23, 42, 0.16)",
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
20
|
+
<h1 style={{ margin: "0 0 8px", fontSize: 24 }}>React fixture</h1>
|
|
21
|
+
<p style={{ margin: 0 }}>Replace this component with the state you want to capture.</p>
|
|
22
|
+
</article>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
width: 720,
|
|
28
|
+
height: 420,
|
|
29
|
+
background: "#e2e8f0",
|
|
30
|
+
selector: "[data-astroshot]",
|
|
31
|
+
waitFor: "text=React fixture",
|
|
32
|
+
component: <ExampleCard />,
|
|
33
|
+
} satisfies ReactShotFixture;
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const INK_TEMPLATE = `import React from "react";
|
|
37
|
+
import { Box, Text } from "ink";
|
|
38
|
+
import type { InkShotFixture } from "@archastro/astroshot/ink";
|
|
39
|
+
|
|
40
|
+
export default {
|
|
41
|
+
cols: 64,
|
|
42
|
+
rows: 12,
|
|
43
|
+
expectText: ["Ink fixture", "Ready to capture"],
|
|
44
|
+
component: (
|
|
45
|
+
<Box borderStyle="round" borderColor="cyan" paddingX={1} flexDirection="column">
|
|
46
|
+
<Text bold color="cyan">Ink fixture</Text>
|
|
47
|
+
<Text>Ready to capture</Text>
|
|
48
|
+
</Box>
|
|
49
|
+
),
|
|
50
|
+
} satisfies InkShotFixture;
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const PTY_TEMPLATE = `version: 1
|
|
54
|
+
command: ./target/debug/my-tui
|
|
55
|
+
args: []
|
|
56
|
+
cwd: .
|
|
57
|
+
cols: 100
|
|
58
|
+
rows: 30
|
|
59
|
+
timeoutMs: 15000
|
|
60
|
+
actions:
|
|
61
|
+
- waitFor: "Choose an option"
|
|
62
|
+
- key: down
|
|
63
|
+
- key: enter
|
|
64
|
+
- waitFor: "Ready"
|
|
65
|
+
expectText:
|
|
66
|
+
- "Ready"
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const PTY_JSON_TEMPLATE = `${JSON.stringify(
|
|
70
|
+
{
|
|
71
|
+
version: 1,
|
|
72
|
+
command: "./target/debug/my-tui",
|
|
73
|
+
args: [],
|
|
74
|
+
cwd: ".",
|
|
75
|
+
cols: 100,
|
|
76
|
+
rows: 30,
|
|
77
|
+
timeoutMs: 15000,
|
|
78
|
+
actions: [
|
|
79
|
+
{ waitFor: "Choose an option" },
|
|
80
|
+
{ key: "down" },
|
|
81
|
+
{ key: "enter" },
|
|
82
|
+
{ waitFor: "Ready" },
|
|
83
|
+
],
|
|
84
|
+
expectText: ["Ready"],
|
|
85
|
+
},
|
|
86
|
+
null,
|
|
87
|
+
2,
|
|
88
|
+
)}\n`;
|
|
89
|
+
|
|
90
|
+
const TEMPLATES = {
|
|
91
|
+
react: {
|
|
92
|
+
defaultPath: "react.shot.tsx",
|
|
93
|
+
label: "React",
|
|
94
|
+
source: REACT_TEMPLATE,
|
|
95
|
+
},
|
|
96
|
+
ink: {
|
|
97
|
+
defaultPath: "ink.shot.tsx",
|
|
98
|
+
label: "Ink",
|
|
99
|
+
source: INK_TEMPLATE,
|
|
100
|
+
},
|
|
101
|
+
pty: {
|
|
102
|
+
defaultPath: "pty.shot.yaml",
|
|
103
|
+
label: "PTY",
|
|
104
|
+
source: PTY_TEMPLATE,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export function canonicalTemplateMode(mode) {
|
|
109
|
+
return mode === "tui" ? "ink" : mode;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function writeFixtureTemplate({
|
|
113
|
+
mode,
|
|
114
|
+
outputPath,
|
|
115
|
+
force = false,
|
|
116
|
+
cwd = process.cwd(),
|
|
117
|
+
}) {
|
|
118
|
+
const canonicalMode = canonicalTemplateMode(mode);
|
|
119
|
+
const template = TEMPLATES[canonicalMode];
|
|
120
|
+
if (!template) {
|
|
121
|
+
throw new Error("init requires one of: react, ink, pty");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const requestedPath = outputPath ?? template.defaultPath;
|
|
125
|
+
const absolutePath = path.resolve(cwd, requestedPath);
|
|
126
|
+
const expectedExtension = canonicalMode === "pty" ? /\.(?:ya?ml|json)$/i : /\.tsx$/i;
|
|
127
|
+
if (!expectedExtension.test(absolutePath)) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
canonicalMode === "pty"
|
|
130
|
+
? "PTY fixture templates must use .yaml, .yml, or .json"
|
|
131
|
+
: `${template.label} fixture templates must use .tsx`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const parentDirectory = path.dirname(absolutePath);
|
|
136
|
+
fs.mkdirSync(parentDirectory, { recursive: true });
|
|
137
|
+
const source =
|
|
138
|
+
canonicalMode === "pty" && path.extname(absolutePath).toLowerCase() === ".json"
|
|
139
|
+
? PTY_JSON_TEMPLATE
|
|
140
|
+
: template.source;
|
|
141
|
+
if (!force) {
|
|
142
|
+
try {
|
|
143
|
+
fs.writeFileSync(absolutePath, source, {
|
|
144
|
+
encoding: "utf8",
|
|
145
|
+
flag: "wx",
|
|
146
|
+
});
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (error?.code === "EEXIST") {
|
|
149
|
+
throw new Error(`Refusing to overwrite ${absolutePath}; pass --force to replace it`);
|
|
150
|
+
}
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
let existing;
|
|
155
|
+
try {
|
|
156
|
+
existing = fs.lstatSync(absolutePath);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (error?.code !== "ENOENT") throw error;
|
|
159
|
+
}
|
|
160
|
+
if (existing?.isSymbolicLink()) {
|
|
161
|
+
throw new Error(`Refusing to replace symbolic link ${absolutePath}`);
|
|
162
|
+
}
|
|
163
|
+
if (existing?.isDirectory()) {
|
|
164
|
+
throw new Error(`Refusing to replace directory ${absolutePath}`);
|
|
165
|
+
}
|
|
166
|
+
const temporaryPath = path.join(
|
|
167
|
+
parentDirectory,
|
|
168
|
+
`.${path.basename(absolutePath)}.${process.pid}.${Date.now()}.tmp`,
|
|
169
|
+
);
|
|
170
|
+
try {
|
|
171
|
+
fs.writeFileSync(temporaryPath, source, {
|
|
172
|
+
encoding: "utf8",
|
|
173
|
+
flag: "wx",
|
|
174
|
+
});
|
|
175
|
+
fs.renameSync(temporaryPath, absolutePath);
|
|
176
|
+
} finally {
|
|
177
|
+
fs.rmSync(temporaryPath, { force: true });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
absolutePath,
|
|
183
|
+
label: template.label,
|
|
184
|
+
mode: canonicalMode,
|
|
185
|
+
};
|
|
186
|
+
}
|
package/ink.d.ts
ADDED
package/ink.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@archastro/tui-shot";
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@archastro/astroshot",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "One CLI for deterministic React and terminal UI screenshots",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"ink",
|
|
8
|
+
"pty",
|
|
9
|
+
"ratatui",
|
|
10
|
+
"terminal",
|
|
11
|
+
"screenshot",
|
|
12
|
+
"visual-testing"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/ArchAstro/astroshots.git",
|
|
18
|
+
"directory": "packages/astroshot"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/ArchAstro/astroshots#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/ArchAstro/astroshots/issues"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"bin": {
|
|
26
|
+
"astroshot": "bin/astroshot.mjs"
|
|
27
|
+
},
|
|
28
|
+
"exports": {
|
|
29
|
+
"./react": {
|
|
30
|
+
"types": "./react.d.ts",
|
|
31
|
+
"import": "./react.js"
|
|
32
|
+
},
|
|
33
|
+
"./ink": {
|
|
34
|
+
"types": "./ink.d.ts",
|
|
35
|
+
"import": "./ink.js"
|
|
36
|
+
},
|
|
37
|
+
"./pty": {
|
|
38
|
+
"types": "./pty.d.ts",
|
|
39
|
+
"import": "./pty.js"
|
|
40
|
+
},
|
|
41
|
+
"./tui": {
|
|
42
|
+
"types": "./tui.d.ts",
|
|
43
|
+
"import": "./tui.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"bin",
|
|
48
|
+
"react.d.ts",
|
|
49
|
+
"react.js",
|
|
50
|
+
"ink.d.ts",
|
|
51
|
+
"ink.js",
|
|
52
|
+
"pty.d.ts",
|
|
53
|
+
"pty.js",
|
|
54
|
+
"tui.d.ts",
|
|
55
|
+
"tui.js",
|
|
56
|
+
"README.md"
|
|
57
|
+
],
|
|
58
|
+
"scripts": {
|
|
59
|
+
"pretest": "npm run build --workspace @archastro/react-shot && npm run build --workspace @archastro/tui-shot",
|
|
60
|
+
"test": "node --test test/*.test.mjs"
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=22.14.0"
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public",
|
|
67
|
+
"provenance": true,
|
|
68
|
+
"registry": "https://registry.npmjs.org/"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@archastro/react-shot": "0.1.0",
|
|
72
|
+
"@archastro/tui-shot": "0.1.0"
|
|
73
|
+
}
|
|
74
|
+
}
|
package/pty.d.ts
ADDED
package/pty.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { closeSharedBrowser, takePtyShot } from "@archastro/tui-shot";
|
package/react.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@archastro/react-shot";
|
package/react.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@archastro/react-shot";
|
package/tui.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@archastro/tui-shot";
|
package/tui.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@archastro/tui-shot";
|