@maker-or/opencms 0.1.1 → 0.1.3
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/README.md +2 -0
- package/dist/index.js +154 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,4 +19,6 @@ npx @maker-or/opencms deploy
|
|
|
19
19
|
|
|
20
20
|
`opencms create` authenticates you, creates an OpenCMS project, pulls the Next.js template, writes the project configuration, and installs dependencies.
|
|
21
21
|
|
|
22
|
+
The generated `cms/schema.json` file defines the project's content types and allowed blocks. The CLI syncs it to the development environment when `dev` or `deploy` runs.
|
|
23
|
+
|
|
22
24
|
The CLI stores its local login configuration in `~/.config/opencms/config.json` (or `$XDG_CONFIG_HOME/opencms/config.json` when configured).
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,51 @@ import { spawn } from "node:child_process";
|
|
|
10
10
|
import { createInterface } from "node:readline/promises";
|
|
11
11
|
import process from "node:process";
|
|
12
12
|
|
|
13
|
+
// ../../packages/sdk/src/schema.ts
|
|
14
|
+
var emptyPageContent = { version: 1, blocks: [] };
|
|
15
|
+
var defaultSchema = {
|
|
16
|
+
version: 1,
|
|
17
|
+
blocks: {
|
|
18
|
+
heading: {
|
|
19
|
+
label: "Heading",
|
|
20
|
+
fields: {
|
|
21
|
+
text: { type: "text", label: "Text", required: true },
|
|
22
|
+
level: { type: "number", label: "Level", required: true }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
text: {
|
|
26
|
+
label: "Text",
|
|
27
|
+
fields: {
|
|
28
|
+
text: { type: "text", label: "Text", required: true }
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
quote: {
|
|
32
|
+
label: "Quote",
|
|
33
|
+
fields: {
|
|
34
|
+
text: { type: "text", label: "Quote", required: true },
|
|
35
|
+
author: { type: "text", label: "Author" }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"feature-list": {
|
|
39
|
+
label: "Feature list",
|
|
40
|
+
fields: {
|
|
41
|
+
title: { type: "text", label: "Title", required: true },
|
|
42
|
+
items: { type: "text", label: "Items", required: true }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
contentTypes: {
|
|
47
|
+
page: {
|
|
48
|
+
label: "Page",
|
|
49
|
+
fields: {
|
|
50
|
+
title: { type: "text", label: "Title", required: true },
|
|
51
|
+
slug: { type: "slug", label: "Slug", required: true, unique: true }
|
|
52
|
+
},
|
|
53
|
+
blocks: ["heading", "text", "quote", "feature-list"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
13
58
|
// ../../packages/sdk/src/index.ts
|
|
14
59
|
class OpenCmsApiError extends Error {
|
|
15
60
|
status;
|
|
@@ -55,6 +100,22 @@ function createSdk(options = {}) {
|
|
|
55
100
|
body: JSON.stringify(input)
|
|
56
101
|
})
|
|
57
102
|
},
|
|
103
|
+
schema: {
|
|
104
|
+
get: () => {
|
|
105
|
+
if (!projectId)
|
|
106
|
+
throw new Error("projectId is required to get the schema");
|
|
107
|
+
return request(`/api/projects/${projectId}/schema`);
|
|
108
|
+
},
|
|
109
|
+
update: (schema) => {
|
|
110
|
+
if (!projectId)
|
|
111
|
+
throw new Error("projectId is required to update the schema");
|
|
112
|
+
return request(`/api/projects/${projectId}/schema`, {
|
|
113
|
+
method: "PUT",
|
|
114
|
+
headers: { "Content-Type": "application/json" },
|
|
115
|
+
body: JSON.stringify(schema)
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
},
|
|
58
119
|
pages: {
|
|
59
120
|
list: () => {
|
|
60
121
|
if (!projectId)
|
|
@@ -67,8 +128,29 @@ function createSdk(options = {}) {
|
|
|
67
128
|
return request(`/api/projects/${projectId}/pages`, {
|
|
68
129
|
method: "POST",
|
|
69
130
|
headers: { "Content-Type": "application/json" },
|
|
131
|
+
body: JSON.stringify({ ...input, environment, content: input.content ?? emptyPageContent })
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
get: (documentId) => {
|
|
135
|
+
if (!projectId)
|
|
136
|
+
throw new Error("projectId is required to get pages");
|
|
137
|
+
return request(`/api/projects/${projectId}/pages/${documentId}?environment=${environment}`);
|
|
138
|
+
},
|
|
139
|
+
update: (documentId, input) => {
|
|
140
|
+
if (!projectId)
|
|
141
|
+
throw new Error("projectId is required to update pages");
|
|
142
|
+
return request(`/api/projects/${projectId}/pages/${documentId}`, {
|
|
143
|
+
method: "PATCH",
|
|
144
|
+
headers: { "Content-Type": "application/json" },
|
|
70
145
|
body: JSON.stringify({ ...input, environment })
|
|
71
146
|
});
|
|
147
|
+
},
|
|
148
|
+
delete: (documentId) => {
|
|
149
|
+
if (!projectId)
|
|
150
|
+
throw new Error("projectId is required to delete pages");
|
|
151
|
+
return request(`/api/projects/${projectId}/pages/${documentId}?environment=${environment}`, {
|
|
152
|
+
method: "DELETE"
|
|
153
|
+
});
|
|
72
154
|
}
|
|
73
155
|
},
|
|
74
156
|
deploy: (targetProjectId = projectId) => {
|
|
@@ -84,11 +166,36 @@ function createSdk(options = {}) {
|
|
|
84
166
|
throw new Error("projectId is required to list documents");
|
|
85
167
|
return request(`/api/projects/${projectId}/pages?environment=${environment}`);
|
|
86
168
|
},
|
|
87
|
-
create: (input) =>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
169
|
+
create: (input) => {
|
|
170
|
+
if (!projectId)
|
|
171
|
+
throw new Error("projectId is required to create documents");
|
|
172
|
+
return request(`/api/projects/${projectId}/pages`, {
|
|
173
|
+
method: "POST",
|
|
174
|
+
headers: { "Content-Type": "application/json" },
|
|
175
|
+
body: JSON.stringify({ ...input, environment, content: input.content ?? emptyPageContent })
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
get: (documentId) => {
|
|
179
|
+
if (!projectId)
|
|
180
|
+
throw new Error("projectId is required to get documents");
|
|
181
|
+
return request(`/api/projects/${projectId}/pages/${documentId}?environment=${environment}`);
|
|
182
|
+
},
|
|
183
|
+
update: (documentId, input) => {
|
|
184
|
+
if (!projectId)
|
|
185
|
+
throw new Error("projectId is required to update documents");
|
|
186
|
+
return request(`/api/projects/${projectId}/pages/${documentId}`, {
|
|
187
|
+
method: "PATCH",
|
|
188
|
+
headers: { "Content-Type": "application/json" },
|
|
189
|
+
body: JSON.stringify({ ...input, environment })
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
delete: (documentId) => {
|
|
193
|
+
if (!projectId)
|
|
194
|
+
throw new Error("projectId is required to delete documents");
|
|
195
|
+
return request(`/api/projects/${projectId}/pages/${documentId}?environment=${environment}`, {
|
|
196
|
+
method: "DELETE"
|
|
197
|
+
});
|
|
198
|
+
}
|
|
92
199
|
}
|
|
93
200
|
};
|
|
94
201
|
}
|
|
@@ -207,6 +314,12 @@ async function ensureToken(config) {
|
|
|
207
314
|
await writeConfig({ ...config, token: loggedInToken, apiUrl: config.apiUrl ?? apiUrl });
|
|
208
315
|
return loggedInToken;
|
|
209
316
|
}
|
|
317
|
+
async function reauthenticate(config) {
|
|
318
|
+
console.log("Your OpenCMS session has expired. Opening browser login…");
|
|
319
|
+
const loggedInToken = await browserLogin();
|
|
320
|
+
await writeConfig({ ...config, token: loggedInToken, apiUrl: config.apiUrl ?? apiUrl });
|
|
321
|
+
return loggedInToken;
|
|
322
|
+
}
|
|
210
323
|
async function login() {
|
|
211
324
|
const config = await readConfig();
|
|
212
325
|
if (process.env.OPENCMS_CLERK_TOKEN) {
|
|
@@ -254,6 +367,11 @@ async function ensureCmsDirectory(destination, project, baseUrl) {
|
|
|
254
367
|
apiUrl: process.env.OPENCMS_API_URL ?? "${baseUrl}",
|
|
255
368
|
environment: process.env.OPENCMS_ENVIRONMENT ?? "development",
|
|
256
369
|
} as const;
|
|
370
|
+
`, "utf8");
|
|
371
|
+
}
|
|
372
|
+
const schemaFile = join(cmsDirectory, "schema.json");
|
|
373
|
+
if (!await fileExists(schemaFile)) {
|
|
374
|
+
await writeFile(schemaFile, `${JSON.stringify(defaultSchema, null, 2)}
|
|
257
375
|
`, "utf8");
|
|
258
376
|
}
|
|
259
377
|
}
|
|
@@ -277,7 +395,7 @@ async function installDependencies(destination) {
|
|
|
277
395
|
}
|
|
278
396
|
}
|
|
279
397
|
async function pullTemplate(destination) {
|
|
280
|
-
const repository = process.env.OPENCMS_TEMPLATE_REPO ?? "https://github.com/
|
|
398
|
+
const repository = process.env.OPENCMS_TEMPLATE_REPO ?? "https://github.com/maker-or/nextjs-template.git";
|
|
281
399
|
if (await fileExists(destination))
|
|
282
400
|
throw new Error(`Destination already exists: ${destination}`);
|
|
283
401
|
console.log("Pulling the OpenCMS Next.js template…");
|
|
@@ -292,8 +410,19 @@ async function createProject() {
|
|
|
292
410
|
const name = await ask("Project name: ");
|
|
293
411
|
if (!name)
|
|
294
412
|
throw new Error("A project name is required.");
|
|
295
|
-
|
|
296
|
-
|
|
413
|
+
let currentConfig = await readConfig();
|
|
414
|
+
let client = sdk(currentConfig);
|
|
415
|
+
let project;
|
|
416
|
+
try {
|
|
417
|
+
project = await client.projects.create({ name });
|
|
418
|
+
} catch (error) {
|
|
419
|
+
if (!(error instanceof OpenCmsApiError) || error.status !== 401)
|
|
420
|
+
throw error;
|
|
421
|
+
await reauthenticate(currentConfig);
|
|
422
|
+
currentConfig = await readConfig();
|
|
423
|
+
client = sdk(currentConfig);
|
|
424
|
+
project = await client.projects.create({ name });
|
|
425
|
+
}
|
|
297
426
|
const destination = resolve(process.cwd(), slugify(project.name));
|
|
298
427
|
await pullTemplate(destination);
|
|
299
428
|
const baseUrl = process.env.OPENCMS_API_URL ?? config.apiUrl ?? apiUrl;
|
|
@@ -314,10 +443,26 @@ Next steps:
|
|
|
314
443
|
async function runDev() {
|
|
315
444
|
const config = await readConfig();
|
|
316
445
|
await ensureToken(config);
|
|
446
|
+
await syncLocalSchema(await projectIdFromEnv(), await readConfig());
|
|
317
447
|
const manager = await packageManager(process.cwd());
|
|
318
448
|
const command = manager[0] === "npm" ? ["npm", "run", "dev"] : manager[0] === "pnpm" ? ["pnpm", "dev"] : manager[0] === "yarn" ? ["yarn", "dev"] : ["bun", "run", "dev"];
|
|
319
449
|
process.exit(await runCommand(command[0], command.slice(1), { cwd: process.cwd(), env: { ...process.env, OPENCMS_ENVIRONMENT: "development" }, inherit: true }));
|
|
320
450
|
}
|
|
451
|
+
async function syncLocalSchema(projectId, config) {
|
|
452
|
+
if (!projectId)
|
|
453
|
+
return;
|
|
454
|
+
const schemaPath = join(process.cwd(), "cms", "schema.json");
|
|
455
|
+
if (!await fileExists(schemaPath))
|
|
456
|
+
return;
|
|
457
|
+
let schema;
|
|
458
|
+
try {
|
|
459
|
+
schema = JSON.parse(await readFile(schemaPath, "utf8"));
|
|
460
|
+
} catch {
|
|
461
|
+
throw new Error("cms/schema.json is not valid JSON.");
|
|
462
|
+
}
|
|
463
|
+
console.log("Syncing the OpenCMS schema to development…");
|
|
464
|
+
await sdk(config, projectId).schema.update(schema);
|
|
465
|
+
}
|
|
321
466
|
function projectIdFromEnv() {
|
|
322
467
|
const envPath = join(process.cwd(), ".env.local");
|
|
323
468
|
return fileExists(envPath).then(async (exists) => {
|
|
@@ -333,6 +478,7 @@ async function deploy() {
|
|
|
333
478
|
const projectId = await projectIdFromEnv() ?? config.projectId;
|
|
334
479
|
if (!projectId)
|
|
335
480
|
throw new Error("No OpenCMS project is configured in this directory.");
|
|
481
|
+
await syncLocalSchema(projectId, await readConfig());
|
|
336
482
|
const deployment = await sdk(await readConfig()).deploy(projectId);
|
|
337
483
|
console.log(`Deployed ${deployment.sourceEnvironment} content to ${deployment.targetEnvironment}.`);
|
|
338
484
|
if (process.env.VERCEL_TOKEN) {
|