@majordigital/create-acorn 1.5.6 → 1.5.8
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 +6 -6
- package/bin/create-acorn.mjs +18 -17
- package/dato/dato-api/scripts/setupDatoModels.mjs +401 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @majordigital/create-acorn
|
|
2
2
|
|
|
3
|
-
**Create Acorn** is Major Digital's production-ready
|
|
3
|
+
**Create Acorn** is Major Digital's production-ready starter CLI for building headless CMS websites with Next.js 15 and the Acorn component system.
|
|
4
4
|
|
|
5
5
|
It eliminates the repetitive setup that comes with every new project — framework configuration, CMS integration, linting, commit conventions, and a full UI component library — so your team can skip straight to building.
|
|
6
6
|
|
|
@@ -10,9 +10,9 @@ It eliminates the repetitive setup that comes with every new project — framewo
|
|
|
10
10
|
npx @majordigital/create-acorn@latest
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
> **Note:** Always use `npx` to run the CLI. Do not use `npm install` — this is a
|
|
13
|
+
> **Note:** Always use `npx` to run the CLI. Do not use `npm install` — this is a starter tool, not a library dependency.
|
|
14
14
|
|
|
15
|
-
The CLI prompts you for a project name, creates the directory, then walks you through selecting a headless CMS —
|
|
15
|
+
The CLI prompts you for a project name, creates the directory, then walks you through selecting a headless CMS — generating a complete, opinionated project in seconds.
|
|
16
16
|
|
|
17
17
|
```
|
|
18
18
|
? What is the name of your project? (my-acorn-app)
|
|
@@ -59,8 +59,8 @@ src/
|
|
|
59
59
|
| CMS | Status | Setup |
|
|
60
60
|
|-----|--------|-------|
|
|
61
61
|
| **Prismic** | Full integration | Slice Machine init, repository connection, slicemachine script |
|
|
62
|
-
| **Storyblok** |
|
|
63
|
-
| **DatoCMS** |
|
|
62
|
+
| **Storyblok** | Full integration | Environment template, component generation support |
|
|
63
|
+
| **DatoCMS** | Full integration | Environment template, API token configuration |
|
|
64
64
|
|
|
65
65
|
## Non-Interactive Usage
|
|
66
66
|
|
|
@@ -74,7 +74,7 @@ npx @majordigital/create-acorn@latest --name my-project --cms dato
|
|
|
74
74
|
|
|
75
75
|
## Generated Project Files
|
|
76
76
|
|
|
77
|
-
Beyond the component library, every
|
|
77
|
+
Beyond the component library, every project includes:
|
|
78
78
|
|
|
79
79
|
| File | Purpose |
|
|
80
80
|
|------|---------|
|
package/bin/create-acorn.mjs
CHANGED
|
@@ -79,8 +79,8 @@ function runCommand(cmd, args, options = {}) {
|
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
async function
|
|
83
|
-
console.log('
|
|
82
|
+
async function setupNextApp() {
|
|
83
|
+
console.log('Setting up Next.js 15 project...');
|
|
84
84
|
console.log('');
|
|
85
85
|
await runCommand('npx', [
|
|
86
86
|
'create-next-app@latest',
|
|
@@ -96,7 +96,7 @@ async function scaffoldNextApp() {
|
|
|
96
96
|
'--yes'
|
|
97
97
|
]);
|
|
98
98
|
console.log('');
|
|
99
|
-
console.log('Next.js project
|
|
99
|
+
console.log('Next.js project created successfully.');
|
|
100
100
|
console.log('');
|
|
101
101
|
|
|
102
102
|
// Add .npmrc with legacy-peer-deps so all npm installs (including Slice Machine) work with React 19
|
|
@@ -107,7 +107,7 @@ async function scaffoldNextApp() {
|
|
|
107
107
|
try { rmSync(join(process.cwd(), 'next.config.ts')); } catch {}
|
|
108
108
|
try { rmSync(join(process.cwd(), 'next.config.js')); } catch {}
|
|
109
109
|
|
|
110
|
-
// Copy Acorn template files (src/, public/, config files) over the Next.js
|
|
110
|
+
// Copy Acorn template files (src/, public/, config files) over the Next.js starter
|
|
111
111
|
console.log('Copying Acorn boilerplate...');
|
|
112
112
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
113
113
|
const templateDir = join(__dirname, '..', 'template');
|
|
@@ -343,7 +343,7 @@ async function setupDato() {
|
|
|
343
343
|
console.log(' https://www.datocms.com/dashboard');
|
|
344
344
|
console.log('');
|
|
345
345
|
console.log('Once your project is ready, provide the Full-access API token below');
|
|
346
|
-
console.log('and we will automatically
|
|
346
|
+
console.log('and we will automatically create all models (Page, Hero, Button, Layout, etc.).');
|
|
347
347
|
console.log('');
|
|
348
348
|
console.log('Token location: Settings > API Tokens > Full-access API token');
|
|
349
349
|
console.log('Make sure "Access the Content Management API" is enabled and the role is Admin.');
|
|
@@ -368,11 +368,11 @@ async function setupDato() {
|
|
|
368
368
|
|
|
369
369
|
if (apiToken) {
|
|
370
370
|
console.log('');
|
|
371
|
-
console.log('
|
|
371
|
+
console.log('Creating DatoCMS models...');
|
|
372
372
|
console.log('');
|
|
373
373
|
|
|
374
374
|
try {
|
|
375
|
-
await
|
|
375
|
+
await createDatoModels(apiToken);
|
|
376
376
|
console.log('');
|
|
377
377
|
console.log('DatoCMS models created successfully!');
|
|
378
378
|
console.log('');
|
|
@@ -405,17 +405,16 @@ NEXT_DATOCMS_ENVIRONMENT=draft
|
|
|
405
405
|
}
|
|
406
406
|
} catch (err) {
|
|
407
407
|
console.log('');
|
|
408
|
-
console.log('Warning: Could not
|
|
408
|
+
console.log('Warning: Could not create DatoCMS models automatically.');
|
|
409
409
|
console.log(` Error: ${err.message}`);
|
|
410
410
|
console.log('');
|
|
411
|
-
console.log('You can create models
|
|
412
|
-
console.log('
|
|
413
|
-
console.log('Layout (singleton), MenuItem (block), CaseStudy, LegalPage, Post,');
|
|
414
|
-
console.log('CaseStudiesListing (singleton), PostListing (singleton).');
|
|
411
|
+
console.log('You can create models later by running:');
|
|
412
|
+
console.log(' npm run setup-dato-models');
|
|
415
413
|
}
|
|
416
414
|
} else {
|
|
417
415
|
console.log('');
|
|
418
|
-
console.log('Skipping model
|
|
416
|
+
console.log('Skipping model creation — you can run this later inside your project:');
|
|
417
|
+
console.log(' npm run setup-dato-models');
|
|
419
418
|
}
|
|
420
419
|
|
|
421
420
|
// Update next.config.ts — replace Prismic image patterns with DatoCMS
|
|
@@ -474,6 +473,7 @@ NEXT_DATOCMS_ENVIRONMENT=draft
|
|
|
474
473
|
const targetScripts = join(process.cwd(), 'scripts');
|
|
475
474
|
mkdirSync(targetScripts, { recursive: true });
|
|
476
475
|
cpSync(join(datoSrcDir, 'scripts', 'buildLinkIndex.ts'), join(targetScripts, 'buildLinkIndex.ts'));
|
|
476
|
+
cpSync(join(datoSrcDir, 'scripts', 'setupDatoModels.mjs'), join(targetScripts, 'setupDatoModels.mjs'));
|
|
477
477
|
console.log('Copied build scripts.');
|
|
478
478
|
|
|
479
479
|
// Update package.json scripts for DatoCMS
|
|
@@ -494,6 +494,7 @@ NEXT_DATOCMS_ENVIRONMENT=draft
|
|
|
494
494
|
pkg.scripts['fix:prod'] = 'biome check . --write';
|
|
495
495
|
pkg.scripts['generate-types'] = 'graphql-codegen --config graphql.config.yml';
|
|
496
496
|
pkg.scripts['check-types'] = 'tsc --noEmit';
|
|
497
|
+
pkg.scripts['setup-dato-models'] = 'node scripts/setupDatoModels.mjs';
|
|
497
498
|
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
498
499
|
console.log('Updated package.json scripts for DatoCMS.');
|
|
499
500
|
} catch {
|
|
@@ -547,10 +548,10 @@ NEXT_DATOCMS_ENVIRONMENT=draft
|
|
|
547
548
|
}
|
|
548
549
|
|
|
549
550
|
/**
|
|
550
|
-
*
|
|
551
|
-
*
|
|
551
|
+
* Create default DatoCMS models using the Management API.
|
|
552
|
+
* Models match the GraphQL queries in the dato-api layer.
|
|
552
553
|
*/
|
|
553
|
-
async function
|
|
554
|
+
async function createDatoModels(apiToken) {
|
|
554
555
|
const { buildClient } = await import('@datocms/cma-client-node');
|
|
555
556
|
const client = buildClient({ apiToken });
|
|
556
557
|
|
|
@@ -1089,7 +1090,7 @@ async function main() {
|
|
|
1089
1090
|
console.log(`Created directory: ${projectDir}`);
|
|
1090
1091
|
console.log('');
|
|
1091
1092
|
|
|
1092
|
-
await
|
|
1093
|
+
await setupNextApp();
|
|
1093
1094
|
|
|
1094
1095
|
// Generate .env.example and README before CMS setup (CMS setup may block if user starts Slice Machine)
|
|
1095
1096
|
const envExamples = {
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Standalone script to create DatoCMS models via the Management API.
|
|
4
|
+
* Run with: npm run setup-dato-models
|
|
5
|
+
*
|
|
6
|
+
* This is useful if you:
|
|
7
|
+
* - Skipped the token during initial CLI setup
|
|
8
|
+
* - Created your DatoCMS project after running the CLI
|
|
9
|
+
* - Need to re-run model creation on a fresh DatoCMS project
|
|
10
|
+
*
|
|
11
|
+
* Requires: DATO_API_TOKEN in .env.local (Full-access token with Admin role + CMA enabled)
|
|
12
|
+
* Or pass it when prompted.
|
|
13
|
+
*/
|
|
14
|
+
import readline from 'node:readline';
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { buildClient } from '@datocms/cma-client-node';
|
|
18
|
+
|
|
19
|
+
function ask(question, defaultValue) {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
22
|
+
const prompt = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
|
|
23
|
+
rl.question(prompt, (answer) => {
|
|
24
|
+
rl.close();
|
|
25
|
+
resolve(answer && answer.trim() ? answer.trim() : defaultValue);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Try to read token from .env.local
|
|
31
|
+
function readTokenFromEnv() {
|
|
32
|
+
try {
|
|
33
|
+
const envPath = join(process.cwd(), '.env.local');
|
|
34
|
+
const content = readFileSync(envPath, 'utf-8');
|
|
35
|
+
const match = content.match(/^DATO_API_TOKEN=(.+)$/m);
|
|
36
|
+
return match?.[1]?.trim() || '';
|
|
37
|
+
} catch {
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function findOrCreateItemType(client, existingTypes, config) {
|
|
43
|
+
const existing = existingTypes.find(t => t.api_key === config.api_key);
|
|
44
|
+
if (existing) {
|
|
45
|
+
console.log(` Skipping ${config.name} (already exists)...`);
|
|
46
|
+
return existing;
|
|
47
|
+
}
|
|
48
|
+
return client.itemTypes.create(config);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function findOrCreateField(client, modelId, config) {
|
|
52
|
+
const existingFields = await client.fields.list(modelId);
|
|
53
|
+
const existing = existingFields.find(f => f.api_key === config.api_key);
|
|
54
|
+
if (existing) return existing;
|
|
55
|
+
return client.fields.create(modelId, config);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function main() {
|
|
59
|
+
console.log('');
|
|
60
|
+
console.log('=== DatoCMS Model Setup ===');
|
|
61
|
+
console.log('');
|
|
62
|
+
console.log('This will create all required models, blocks, and fields');
|
|
63
|
+
console.log('in your DatoCMS project via the Management API.');
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log('You need a Full-access API token with Admin role');
|
|
66
|
+
console.log('and "Access the Content Management API" enabled.');
|
|
67
|
+
console.log('');
|
|
68
|
+
|
|
69
|
+
const envToken = readTokenFromEnv();
|
|
70
|
+
const apiToken = await ask('Paste your Full-access API token', envToken || undefined);
|
|
71
|
+
|
|
72
|
+
if (!apiToken) {
|
|
73
|
+
console.log('No token provided. Exiting.');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log('');
|
|
78
|
+
console.log('Creating DatoCMS models...');
|
|
79
|
+
console.log('');
|
|
80
|
+
|
|
81
|
+
const client = buildClient({ apiToken });
|
|
82
|
+
|
|
83
|
+
// Fetch existing models so we can skip duplicates
|
|
84
|
+
const existingTypes = await client.itemTypes.list();
|
|
85
|
+
|
|
86
|
+
// --- Block models ---
|
|
87
|
+
|
|
88
|
+
console.log(' Creating Button block...');
|
|
89
|
+
const buttonBlock = await findOrCreateItemType(client, existingTypes, {
|
|
90
|
+
name: 'Button',
|
|
91
|
+
api_key: 'button',
|
|
92
|
+
modular_block: true,
|
|
93
|
+
});
|
|
94
|
+
await findOrCreateField(client, buttonBlock.id, {
|
|
95
|
+
label: 'Text',
|
|
96
|
+
field_type: 'string',
|
|
97
|
+
api_key: 'text',
|
|
98
|
+
validators: { required: {} },
|
|
99
|
+
});
|
|
100
|
+
await findOrCreateField(client, buttonBlock.id, {
|
|
101
|
+
label: 'Variant',
|
|
102
|
+
field_type: 'string',
|
|
103
|
+
api_key: 'variant',
|
|
104
|
+
validators: { enum: { values: ['primary', 'secondary', 'outline'] } },
|
|
105
|
+
appearance: {
|
|
106
|
+
addons: [],
|
|
107
|
+
editor: 'string_select',
|
|
108
|
+
parameters: { options: [
|
|
109
|
+
{ hint: '', label: 'Primary', value: 'primary' },
|
|
110
|
+
{ hint: '', label: 'Secondary', value: 'secondary' },
|
|
111
|
+
{ hint: '', label: 'Outline', value: 'outline' },
|
|
112
|
+
]},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
await findOrCreateField(client, buttonBlock.id, {
|
|
116
|
+
label: 'Link',
|
|
117
|
+
field_type: 'json',
|
|
118
|
+
api_key: 'link',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
console.log(' Creating Hero block...');
|
|
122
|
+
const heroBlock = await findOrCreateItemType(client, existingTypes, {
|
|
123
|
+
name: 'Hero',
|
|
124
|
+
api_key: 'hero',
|
|
125
|
+
modular_block: true,
|
|
126
|
+
});
|
|
127
|
+
await findOrCreateField(client, heroBlock.id, {
|
|
128
|
+
label: 'Heading',
|
|
129
|
+
field_type: 'string',
|
|
130
|
+
api_key: 'heading',
|
|
131
|
+
validators: { required: {} },
|
|
132
|
+
});
|
|
133
|
+
await findOrCreateField(client, heroBlock.id, {
|
|
134
|
+
label: 'Eyebrow',
|
|
135
|
+
field_type: 'string',
|
|
136
|
+
api_key: 'eyebrow',
|
|
137
|
+
});
|
|
138
|
+
await findOrCreateField(client, heroBlock.id, {
|
|
139
|
+
label: 'Content',
|
|
140
|
+
field_type: 'text',
|
|
141
|
+
api_key: 'content',
|
|
142
|
+
appearance: {
|
|
143
|
+
addons: [],
|
|
144
|
+
editor: 'textarea',
|
|
145
|
+
parameters: {},
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
await findOrCreateField(client, heroBlock.id, {
|
|
149
|
+
label: 'Layout',
|
|
150
|
+
field_type: 'string',
|
|
151
|
+
api_key: 'layout',
|
|
152
|
+
validators: { enum: { values: ['default', 'centered', 'split'] } },
|
|
153
|
+
appearance: {
|
|
154
|
+
addons: [],
|
|
155
|
+
editor: 'string_select',
|
|
156
|
+
parameters: { options: [
|
|
157
|
+
{ hint: '', label: 'Default', value: 'default' },
|
|
158
|
+
{ hint: '', label: 'Centered', value: 'centered' },
|
|
159
|
+
{ hint: '', label: 'Split', value: 'split' },
|
|
160
|
+
]},
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
await findOrCreateField(client, heroBlock.id, {
|
|
164
|
+
label: 'Theme',
|
|
165
|
+
field_type: 'string',
|
|
166
|
+
api_key: 'theme',
|
|
167
|
+
validators: { enum: { values: ['light', 'dark'] } },
|
|
168
|
+
appearance: {
|
|
169
|
+
addons: [],
|
|
170
|
+
editor: 'string_select',
|
|
171
|
+
parameters: { options: [
|
|
172
|
+
{ hint: '', label: 'Light', value: 'light' },
|
|
173
|
+
{ hint: '', label: 'Dark', value: 'dark' },
|
|
174
|
+
]},
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
await findOrCreateField(client, heroBlock.id, {
|
|
178
|
+
label: 'Image',
|
|
179
|
+
field_type: 'file',
|
|
180
|
+
api_key: 'image',
|
|
181
|
+
});
|
|
182
|
+
await findOrCreateField(client, heroBlock.id, {
|
|
183
|
+
label: 'Actions',
|
|
184
|
+
field_type: 'rich_text',
|
|
185
|
+
api_key: 'actions',
|
|
186
|
+
validators: {
|
|
187
|
+
rich_text_blocks: { item_types: [buttonBlock.id] },
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
console.log(' Creating MenuItem block...');
|
|
192
|
+
const menuItemBlock = await findOrCreateItemType(client, existingTypes, {
|
|
193
|
+
name: 'Menu Item',
|
|
194
|
+
api_key: 'menu_item',
|
|
195
|
+
modular_block: true,
|
|
196
|
+
});
|
|
197
|
+
await findOrCreateField(client, menuItemBlock.id, {
|
|
198
|
+
label: 'Heading',
|
|
199
|
+
field_type: 'string',
|
|
200
|
+
api_key: 'heading',
|
|
201
|
+
validators: { required: {} },
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// --- Regular models ---
|
|
205
|
+
|
|
206
|
+
console.log(' Creating Page model...');
|
|
207
|
+
const pageModel = await findOrCreateItemType(client, existingTypes, {
|
|
208
|
+
name: 'Page',
|
|
209
|
+
api_key: 'page',
|
|
210
|
+
draft_mode_active: true,
|
|
211
|
+
});
|
|
212
|
+
const pageHeadingField = await findOrCreateField(client, pageModel.id, {
|
|
213
|
+
label: 'Heading',
|
|
214
|
+
field_type: 'string',
|
|
215
|
+
api_key: 'heading',
|
|
216
|
+
validators: { required: {} },
|
|
217
|
+
});
|
|
218
|
+
await findOrCreateField(client, pageModel.id, {
|
|
219
|
+
label: 'Description',
|
|
220
|
+
field_type: 'text',
|
|
221
|
+
api_key: 'description',
|
|
222
|
+
});
|
|
223
|
+
await findOrCreateField(client, pageModel.id, {
|
|
224
|
+
label: 'Slug',
|
|
225
|
+
field_type: 'slug',
|
|
226
|
+
api_key: 'slug',
|
|
227
|
+
validators: {
|
|
228
|
+
required: {},
|
|
229
|
+
slug_title_field: { title_field_id: pageHeadingField.id },
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
await findOrCreateField(client, pageModel.id, {
|
|
233
|
+
label: 'Parent Page',
|
|
234
|
+
field_type: 'link',
|
|
235
|
+
api_key: 'parent_page',
|
|
236
|
+
validators: {
|
|
237
|
+
item_item_type: { item_types: [pageModel.id] },
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
await findOrCreateField(client, pageModel.id, {
|
|
241
|
+
label: 'Body',
|
|
242
|
+
field_type: 'rich_text',
|
|
243
|
+
api_key: 'body',
|
|
244
|
+
validators: {
|
|
245
|
+
rich_text_blocks: { item_types: [heroBlock.id] },
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Add target link to MenuItem
|
|
250
|
+
await findOrCreateField(client, menuItemBlock.id, {
|
|
251
|
+
label: 'Target',
|
|
252
|
+
field_type: 'link',
|
|
253
|
+
api_key: 'target',
|
|
254
|
+
validators: {
|
|
255
|
+
item_item_type: { item_types: [pageModel.id] },
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
console.log(' Creating Case Study model...');
|
|
260
|
+
const caseStudyModel = await findOrCreateItemType(client, existingTypes, {
|
|
261
|
+
name: 'Case Study',
|
|
262
|
+
api_key: 'case_study',
|
|
263
|
+
draft_mode_active: true,
|
|
264
|
+
});
|
|
265
|
+
const caseStudyTitleField = await findOrCreateField(client, caseStudyModel.id, {
|
|
266
|
+
label: 'Title',
|
|
267
|
+
field_type: 'string',
|
|
268
|
+
api_key: 'title',
|
|
269
|
+
validators: { required: {} },
|
|
270
|
+
});
|
|
271
|
+
await findOrCreateField(client, caseStudyModel.id, {
|
|
272
|
+
label: 'Slug',
|
|
273
|
+
field_type: 'slug',
|
|
274
|
+
api_key: 'slug',
|
|
275
|
+
validators: {
|
|
276
|
+
required: {},
|
|
277
|
+
slug_title_field: { title_field_id: caseStudyTitleField.id },
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
console.log(' Creating Legal Page model...');
|
|
282
|
+
const legalPageModel = await findOrCreateItemType(client, existingTypes, {
|
|
283
|
+
name: 'Legal Page',
|
|
284
|
+
api_key: 'legal_page',
|
|
285
|
+
draft_mode_active: true,
|
|
286
|
+
});
|
|
287
|
+
const legalTitleField = await findOrCreateField(client, legalPageModel.id, {
|
|
288
|
+
label: 'Title',
|
|
289
|
+
field_type: 'string',
|
|
290
|
+
api_key: 'title',
|
|
291
|
+
validators: { required: {} },
|
|
292
|
+
});
|
|
293
|
+
await findOrCreateField(client, legalPageModel.id, {
|
|
294
|
+
label: 'Slug',
|
|
295
|
+
field_type: 'slug',
|
|
296
|
+
api_key: 'slug',
|
|
297
|
+
validators: {
|
|
298
|
+
required: {},
|
|
299
|
+
slug_title_field: { title_field_id: legalTitleField.id },
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
await findOrCreateField(client, legalPageModel.id, {
|
|
303
|
+
label: 'Download',
|
|
304
|
+
field_type: 'file',
|
|
305
|
+
api_key: 'download',
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
console.log(' Creating Post model...');
|
|
309
|
+
const postModel = await findOrCreateItemType(client, existingTypes, {
|
|
310
|
+
name: 'Post',
|
|
311
|
+
api_key: 'post',
|
|
312
|
+
draft_mode_active: true,
|
|
313
|
+
});
|
|
314
|
+
const postTitleField = await findOrCreateField(client, postModel.id, {
|
|
315
|
+
label: 'Title',
|
|
316
|
+
field_type: 'string',
|
|
317
|
+
api_key: 'title',
|
|
318
|
+
validators: { required: {} },
|
|
319
|
+
});
|
|
320
|
+
await findOrCreateField(client, postModel.id, {
|
|
321
|
+
label: 'Slug',
|
|
322
|
+
field_type: 'slug',
|
|
323
|
+
api_key: 'slug',
|
|
324
|
+
validators: {
|
|
325
|
+
required: {},
|
|
326
|
+
slug_title_field: { title_field_id: postTitleField.id },
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
console.log(' Creating Case Studies Listing singleton...');
|
|
331
|
+
const caseStudiesListing = await findOrCreateItemType(client, existingTypes, {
|
|
332
|
+
name: 'Case Studies Listing',
|
|
333
|
+
api_key: 'case_studies_listing',
|
|
334
|
+
singleton: true,
|
|
335
|
+
});
|
|
336
|
+
await findOrCreateField(client, caseStudiesListing.id, {
|
|
337
|
+
label: 'Slug',
|
|
338
|
+
field_type: 'string',
|
|
339
|
+
api_key: 'slug',
|
|
340
|
+
validators: { required: {} },
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
console.log(' Creating Post Listing singleton...');
|
|
344
|
+
const postListing = await findOrCreateItemType(client, existingTypes, {
|
|
345
|
+
name: 'Post Listing',
|
|
346
|
+
api_key: 'post_listing',
|
|
347
|
+
singleton: true,
|
|
348
|
+
});
|
|
349
|
+
await findOrCreateField(client, postListing.id, {
|
|
350
|
+
label: 'Slug',
|
|
351
|
+
field_type: 'string',
|
|
352
|
+
api_key: 'slug',
|
|
353
|
+
validators: { required: {} },
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
console.log(' Creating Layout singleton...');
|
|
357
|
+
const layoutModel = await findOrCreateItemType(client, existingTypes, {
|
|
358
|
+
name: 'Layout',
|
|
359
|
+
api_key: 'layout',
|
|
360
|
+
singleton: true,
|
|
361
|
+
});
|
|
362
|
+
await findOrCreateField(client, layoutModel.id, {
|
|
363
|
+
label: 'Navigation',
|
|
364
|
+
field_type: 'rich_text',
|
|
365
|
+
api_key: 'navigation',
|
|
366
|
+
validators: {
|
|
367
|
+
rich_text_blocks: { item_types: [menuItemBlock.id] },
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
await findOrCreateField(client, layoutModel.id, {
|
|
371
|
+
label: 'Footer Navigation',
|
|
372
|
+
field_type: 'rich_text',
|
|
373
|
+
api_key: 'footer_navigation',
|
|
374
|
+
validators: {
|
|
375
|
+
rich_text_blocks: { item_types: [menuItemBlock.id] },
|
|
376
|
+
},
|
|
377
|
+
});
|
|
378
|
+
await findOrCreateField(client, layoutModel.id, {
|
|
379
|
+
label: 'Copyright',
|
|
380
|
+
field_type: 'string',
|
|
381
|
+
api_key: 'copyright',
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
console.log('');
|
|
385
|
+
console.log(' Models created: Page, Case Study, Legal Page, Post');
|
|
386
|
+
console.log(' Singletons created: Layout, Case Studies Listing, Post Listing');
|
|
387
|
+
console.log(' Blocks created: Hero, Button, Menu Item');
|
|
388
|
+
console.log('');
|
|
389
|
+
console.log('Done! You can now run:');
|
|
390
|
+
console.log(' npm run generate-types');
|
|
391
|
+
console.log(' npm run dev');
|
|
392
|
+
console.log('');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
main().catch((err) => {
|
|
396
|
+
console.error('');
|
|
397
|
+
console.error('Failed to create DatoCMS models.');
|
|
398
|
+
console.error(` Error: ${err.message}`);
|
|
399
|
+
console.error('');
|
|
400
|
+
process.exit(1);
|
|
401
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@majordigital/create-acorn",
|
|
3
|
-
"version": "1.5.
|
|
4
|
-
"description": "Interactive
|
|
3
|
+
"version": "1.5.8",
|
|
4
|
+
"description": "Interactive starter CLI for Acorn with Storyblok/Prismic/DatoCMS, TypeScript, and Tailwind.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-acorn": "bin/create-acorn.mjs",
|
|
7
7
|
"major-acorn": "bin/create-acorn.mjs"
|