Skip to content

Commit ec84e7d

Browse files
committed
Introducing publish example
1 parent a4f7122 commit ec84e7d

5 files changed

Lines changed: 667 additions & 2 deletions

File tree

examples/publish/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Publish CLI
2+
3+
A CLI tool that continuously monitors a Framer project for changes and automatically publishes and deploys them at a set interval.
4+
5+
## How it works
6+
7+
1. Connects to your Framer project
8+
2. Checks for unpublished changes
9+
3. If changes exist, publishes and deploys them
10+
4. Waits for the configured interval
11+
5. Repeats
12+
13+
Each check creates a fresh connection to avoid long-running sessions.
14+
15+
## Usage
16+
17+
Interactive mode (prompts for interval and confirmation):
18+
19+
```bash
20+
node --env-file=../../.env index.ts
21+
22+
bun --env-file=../../.env run index.ts
23+
24+
deno --env-file=../../.env run index.ts
25+
```
26+
27+
Automated mode (no prompts):
28+
29+
```bash
30+
INTERVAL_MINUTES=10 node --env-file=../../.env index.ts
31+
```
32+
33+
## Environment Variables
34+
35+
| Variable | Required | Description |
36+
|----------|----------|-------------|
37+
| `EXAMPLE_PROJECT_URL` | Yes | Your Framer project URL |
38+
| `INTERVAL_MINUTES` | No | Interval in minutes. If set, skips all prompts |

examples/publish/index.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import assert from "node:assert";
2+
import { confirm, input } from "@inquirer/prompts";
3+
import { connect, type Framer } from "framer-api";
4+
5+
// --- Configuration ---
6+
7+
const projectUrl = process.env["EXAMPLE_PROJECT_URL"];
8+
assert(projectUrl, "EXAMPLE_PROJECT_URL environment variable is required");
9+
10+
const intervalFromEnv = process.env["INTERVAL_MINUTES"];
11+
12+
// --- Main ---
13+
14+
const minutes = intervalFromEnv
15+
? parseInt(intervalFromEnv, 10)
16+
: parseInt(await input({ message: "Interval in minutes:", default: "10" }), 10);
17+
18+
const shouldStart =
19+
intervalFromEnv ||
20+
(await confirm({
21+
message: `🔄 This will publish and deploy every ${minutes} minute(s). Start?`,
22+
default: true,
23+
}));
24+
25+
if (!shouldStart) {
26+
console.log("🛑 Cancelled.");
27+
process.exit(0);
28+
}
29+
30+
console.log(`\n🔄 Will check for changes and publish every ${minutes} minute(s)`);
31+
console.log(` Press Ctrl+C to stop\n`);
32+
33+
const checkAndPublish = async () => {
34+
using framer = await connect(projectUrl);
35+
36+
const timestamp = new Date().toLocaleTimeString();
37+
const hasChanges = await showChanges(framer);
38+
39+
if (hasChanges) {
40+
console.log(`[${timestamp}] Publishing...`);
41+
await publishAndDeploy(framer);
42+
} else {
43+
console.log(`[${timestamp}] ⏳ No changes`);
44+
}
45+
};
46+
47+
await checkAndPublish();
48+
setInterval(checkAndPublish, minutes * 60 * 1000);
49+
50+
// --- Helper Functions ---
51+
52+
async function showChanges(framer: Framer): Promise<boolean> {
53+
const changedPaths = await framer.getChangedPaths();
54+
const entries = Object.entries(changedPaths);
55+
const totalChanges = entries.reduce((sum, [, paths]) => sum + paths.length, 0);
56+
57+
if (totalChanges === 0) return false;
58+
59+
console.log(`📄 ${totalChanges} change(s):`);
60+
for (const [type, paths] of entries) {
61+
for (const path of paths) {
62+
console.log(` ${type}: ${path}`);
63+
}
64+
}
65+
66+
return true;
67+
}
68+
69+
async function publishAndDeploy(framer: Framer) {
70+
const { deployment } = await framer.publish();
71+
console.log(`🚀 Published deployment ${deployment.id}`);
72+
73+
const deployedHostnames = await framer.deploy(deployment.id);
74+
console.log(`✅ Deployed to:`);
75+
for (const hostname of deployedHostnames) {
76+
console.log(` https://${hostname.hostname}`);
77+
}
78+
}

examples/publish/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "publish",
3+
"version": "0.0.1",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"typecheck": "tsc --noEmit"
8+
},
9+
"dependencies": {
10+
"@inquirer/prompts": "^7.2.1",
11+
"framer-api": "^0.0.1-beta.0",
12+
"typescript": "^5.9.3"
13+
},
14+
"devDependencies": {
15+
"@types/node": "^22.10.2"
16+
}
17+
}

examples/publish/tsconfig.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../tsconfig.base.json",
3+
"include": ["index.ts"]
4+
}

0 commit comments

Comments
 (0)