@sleekcms/cli 1.0.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.
Files changed (2) hide show
  1. package/index.js +157 -0
  2. package/package.json +20 -0
package/index.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs-extra");
4
+ const axios = require("axios");
5
+ const chokidar = require("chokidar");
6
+ const { program } = require("commander");
7
+
8
+ const API_BASE_URLS = {
9
+ localhost: "http://localhost:9000/api/template",
10
+ development: "https://app.sleekcms.net/api/template",
11
+ production: "https://app.sleekcms.com/api/template",
12
+ }
13
+
14
+ const PORT = 8080;
15
+ const DEBOUNCE_DELAY = 1000; // 2 seconds delay
16
+ let isShuttingDown = false;
17
+ const pendingUpdates = {};
18
+ let fileMap = {};
19
+
20
+ // CLI Setup to take `--token=<token>`
21
+ program
22
+ .option("--token <token>", "API authentication token")
23
+ .option("--env <env>", "Environment (localhost, development, production)", "production")
24
+ .parse(process.argv);
25
+
26
+ const options = program.opts();
27
+ const AUTH_TOKEN = options.token;
28
+ const ENV = options.env.toLowerCase();
29
+
30
+ if (!AUTH_TOKEN) {
31
+ console.error("โŒ Missing required --token parameter.");
32
+ process.exit(1);
33
+ }
34
+
35
+ const API_BASE_URL = API_BASE_URLS[ENV] || API_BASE_URLS.production;
36
+
37
+ const VIEWS_DIR = AUTH_TOKEN.split('-')[0] + "-views/";
38
+
39
+ // Axios instance with authorization
40
+ const apiClient = axios.create({
41
+ baseURL: API_BASE_URL,
42
+ headers: { Authorization: `Bearer ${AUTH_TOKEN}` },
43
+ });
44
+
45
+
46
+ // Function to fetch and save files
47
+ async function fetchFiles() {
48
+ try {
49
+ console.log("๐Ÿ“ฅ Fetching files from API...");
50
+ const response = await apiClient.get("/");
51
+
52
+ await fs.ensureDir(`./${VIEWS_DIR}`);
53
+
54
+ for (const file of response.data) {
55
+ if (file.file_path) {
56
+ const filePath = `./${VIEWS_DIR}${file.file_path}`;
57
+ await fs.outputFile(filePath, file.code);
58
+ fileMap[file.file_path] = file.id;
59
+ console.log(`โœ… Created: ${filePath}`);
60
+ }
61
+ }
62
+
63
+ console.log("โœ”๏ธ All files downloaded. They will be deleted on exit.");
64
+ } catch (error) {
65
+ console.error("โŒ Error fetching files:", error.response?.data || error.message);
66
+ }
67
+ }
68
+
69
+ // Function to clean up views directory
70
+ async function cleanupFiles() {
71
+ console.log("๐Ÿงน Cleaning up files...");
72
+ try {
73
+ await fs.remove(`./${VIEWS_DIR}`);
74
+ console.log("โœ… Cleanup complete. Exiting...");
75
+ } catch (error) {
76
+ console.error("โŒ Error during cleanup:", error.message);
77
+ }
78
+ }
79
+
80
+
81
+ // Function to handle debounced updates
82
+ function scheduleUpdate(filePath) {
83
+ if (isShuttingDown) return;
84
+
85
+ const relativePath = filePath.replace(VIEWS_DIR, ""); // Extract relative file path
86
+ const fileId = fileMap[relativePath];
87
+
88
+ // Clear previous timeout if it exists
89
+ if (pendingUpdates[fileId]) {
90
+ clearTimeout(pendingUpdates[fileId]);
91
+ }
92
+
93
+ // Schedule a new update after the debounce delay
94
+ pendingUpdates[fileId] = setTimeout(async () => {
95
+ try {
96
+ const code = await fs.readFile(filePath, "utf-8");
97
+
98
+ await apiClient.patch(`/${fileId}`, { code });
99
+ console.log("โœ… Updated template for:", relativePath);
100
+
101
+ delete pendingUpdates[fileId]; // Cleanup
102
+ } catch (error) {
103
+ console.error("โŒ Error updating API:", error.response?.data || error.message);
104
+ }
105
+ }, DEBOUNCE_DELAY);
106
+ }
107
+
108
+ async function createSchema(filePath) {
109
+ if (isShuttingDown) return;
110
+
111
+ const relativePath = filePath.replace(VIEWS_DIR, ""); // Extract relative file path
112
+ const resp = await apiClient.post("/cli", { file_path: relativePath});
113
+ const schema = resp.data;
114
+ fileMap[relativePath] = schema.tmpl_main_id;
115
+ console.log("โœ… Created model for:", relativePath);
116
+ }
117
+
118
+ // Function to monitor file changes
119
+ function monitorFiles() {
120
+ console.log("๐Ÿ‘€ Watching for file changes...");
121
+
122
+ chokidar.watch(`./${VIEWS_DIR}`, { persistent: true, ignoreInitial: true })
123
+ .on("change", scheduleUpdate)
124
+ .on("add", createSchema);
125
+ }
126
+
127
+ // Graceful shutdown handler
128
+ async function handleExit() {
129
+ if (isShuttingDown) return;
130
+ isShuttingDown = true;
131
+ console.log("\nโš ๏ธ Shutting down...");
132
+
133
+ //await finalSync();
134
+ await cleanupFiles();
135
+
136
+ process.exit(0);
137
+ }
138
+
139
+ // Main function
140
+ async function main() {
141
+ await fetchFiles();
142
+ monitorFiles();
143
+ //const server = startServer();
144
+
145
+ process.on("SIGINT", async () => {
146
+ console.log("\n๐Ÿ›‘ Caught interrupt signal (Ctrl+C)");
147
+ await handleExit();
148
+ });
149
+
150
+ process.on("SIGTERM", async () => {
151
+ console.log("\n๐Ÿ›‘ Caught termination signal");
152
+ await handleExit();
153
+ });
154
+ }
155
+
156
+ // Execute when script runs
157
+ main();
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@sleekcms/cli",
3
+ "version": "1.0.0",
4
+ "description": "SleekCMS CLI for locally editing SleekCMS site template code",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "sleekcms": "index.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "author": "Yusuf Bhabhrawala",
13
+ "license": "ISC",
14
+ "dependencies": {
15
+ "axios": "^1.7.9",
16
+ "chokidar": "^4.0.3",
17
+ "commander": "^13.1.0",
18
+ "fs-extra": "^11.3.0"
19
+ }
20
+ }