@majordigital/create-acorn 1.2.1 → 1.3.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/bin/create-acorn.mjs +212 -0
- package/package.json +1 -1
package/bin/create-acorn.mjs
CHANGED
|
@@ -220,6 +220,216 @@ async function setupPrismic(projectName) {
|
|
|
220
220
|
console.log('');
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
async function setupStoryblok(projectName) {
|
|
224
|
+
console.log('Setting up Storyblok...');
|
|
225
|
+
console.log('');
|
|
226
|
+
|
|
227
|
+
// Install Storyblok SDK
|
|
228
|
+
console.log('Installing @storyblok/react...');
|
|
229
|
+
await runCommand('npm', ['install', '--legacy-peer-deps', '@storyblok/react']);
|
|
230
|
+
console.log('');
|
|
231
|
+
|
|
232
|
+
// Update next.config.ts — replace Prismic image patterns with Storyblok
|
|
233
|
+
const nextConfigPath = join(process.cwd(), 'next.config.ts');
|
|
234
|
+
try {
|
|
235
|
+
let config = readFileSync(nextConfigPath, 'utf-8');
|
|
236
|
+
config = config.replace(
|
|
237
|
+
/remotePatterns:\s*\[[\s\S]*?\],/,
|
|
238
|
+
`remotePatterns: [
|
|
239
|
+
{
|
|
240
|
+
protocol: 'https',
|
|
241
|
+
hostname: 'a.storyblok.com',
|
|
242
|
+
port: '',
|
|
243
|
+
pathname: '/**',
|
|
244
|
+
},
|
|
245
|
+
],`
|
|
246
|
+
);
|
|
247
|
+
writeFileSync(nextConfigPath, config);
|
|
248
|
+
console.log('Updated next.config.ts with Storyblok image domain.');
|
|
249
|
+
} catch {
|
|
250
|
+
console.log('Warning: Could not update next.config.ts image patterns.');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Create src/lib/storyblok.ts
|
|
254
|
+
const libDir = join(process.cwd(), 'src', 'lib');
|
|
255
|
+
writeFileSync(join(libDir, 'storyblok.ts'), `import { apiPlugin, storyblokInit } from '@storyblok/react/rsc';
|
|
256
|
+
|
|
257
|
+
import Page from '@/components/storyblok/Page';
|
|
258
|
+
import Feature from '@/components/storyblok/Feature';
|
|
259
|
+
import Grid from '@/components/storyblok/Grid';
|
|
260
|
+
import Teaser from '@/components/storyblok/Teaser';
|
|
261
|
+
|
|
262
|
+
export const getStoryblokApi = storyblokInit({
|
|
263
|
+
accessToken: process.env.STORYBLOK_PREVIEW_TOKEN,
|
|
264
|
+
use: [apiPlugin],
|
|
265
|
+
components: {
|
|
266
|
+
page: Page,
|
|
267
|
+
feature: Feature,
|
|
268
|
+
grid: Grid,
|
|
269
|
+
teaser: Teaser,
|
|
270
|
+
},
|
|
271
|
+
apiOptions: {
|
|
272
|
+
region: 'eu',
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
`);
|
|
276
|
+
console.log('Created src/lib/storyblok.ts');
|
|
277
|
+
|
|
278
|
+
// Create StoryblokProvider
|
|
279
|
+
const componentsDir = join(process.cwd(), 'src', 'components');
|
|
280
|
+
mkdirSync(componentsDir, { recursive: true });
|
|
281
|
+
writeFileSync(join(componentsDir, 'StoryblokProvider.tsx'), `'use client';
|
|
282
|
+
|
|
283
|
+
import { getStoryblokApi } from '@/lib/storyblok';
|
|
284
|
+
import type { PropsWithChildren } from 'react';
|
|
285
|
+
|
|
286
|
+
export default function StoryblokProvider({ children }: PropsWithChildren) {
|
|
287
|
+
getStoryblokApi();
|
|
288
|
+
return children;
|
|
289
|
+
}
|
|
290
|
+
`);
|
|
291
|
+
console.log('Created src/components/StoryblokProvider.tsx');
|
|
292
|
+
|
|
293
|
+
// Create Storyblok component directory and base components
|
|
294
|
+
const sbComponentsDir = join(componentsDir, 'storyblok');
|
|
295
|
+
mkdirSync(sbComponentsDir, { recursive: true });
|
|
296
|
+
|
|
297
|
+
writeFileSync(join(sbComponentsDir, 'Page.tsx'), `import { storyblokEditable, StoryblokServerComponent } from '@storyblok/react/rsc';
|
|
298
|
+
import type { SbBlokData } from '@storyblok/react/rsc';
|
|
299
|
+
|
|
300
|
+
interface PageBlok extends SbBlokData {
|
|
301
|
+
body?: SbBlokData[];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export default function Page({ blok }: { blok: PageBlok }) {
|
|
305
|
+
return (
|
|
306
|
+
<main {...storyblokEditable(blok)}>
|
|
307
|
+
{blok.body?.map((nestedBlok) => (
|
|
308
|
+
<StoryblokServerComponent blok={nestedBlok} key={nestedBlok._uid} />
|
|
309
|
+
))}
|
|
310
|
+
</main>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
`);
|
|
314
|
+
|
|
315
|
+
writeFileSync(join(sbComponentsDir, 'Grid.tsx'), `import { storyblokEditable, StoryblokServerComponent } from '@storyblok/react/rsc';
|
|
316
|
+
import type { SbBlokData } from '@storyblok/react/rsc';
|
|
317
|
+
|
|
318
|
+
interface GridBlok extends SbBlokData {
|
|
319
|
+
columns?: SbBlokData[];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export default function Grid({ blok }: { blok: GridBlok }) {
|
|
323
|
+
return (
|
|
324
|
+
<div {...storyblokEditable(blok)} className="grid grid-cols-3 gap-6">
|
|
325
|
+
{blok.columns?.map((nestedBlok) => (
|
|
326
|
+
<StoryblokServerComponent blok={nestedBlok} key={nestedBlok._uid} />
|
|
327
|
+
))}
|
|
328
|
+
</div>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
`);
|
|
332
|
+
|
|
333
|
+
writeFileSync(join(sbComponentsDir, 'Feature.tsx'), `import { storyblokEditable } from '@storyblok/react/rsc';
|
|
334
|
+
import type { SbBlokData } from '@storyblok/react/rsc';
|
|
335
|
+
|
|
336
|
+
interface FeatureBlok extends SbBlokData {
|
|
337
|
+
name?: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export default function Feature({ blok }: { blok: FeatureBlok }) {
|
|
341
|
+
return (
|
|
342
|
+
<div {...storyblokEditable(blok)} className="feature">
|
|
343
|
+
<span>{blok.name}</span>
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
`);
|
|
348
|
+
|
|
349
|
+
writeFileSync(join(sbComponentsDir, 'Teaser.tsx'), `import { storyblokEditable } from '@storyblok/react/rsc';
|
|
350
|
+
import type { SbBlokData } from '@storyblok/react/rsc';
|
|
351
|
+
|
|
352
|
+
interface TeaserBlok extends SbBlokData {
|
|
353
|
+
headline?: string;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export default function Teaser({ blok }: { blok: TeaserBlok }) {
|
|
357
|
+
return (
|
|
358
|
+
<div {...storyblokEditable(blok)} className="teaser">
|
|
359
|
+
<h2>{blok.headline}</h2>
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
`);
|
|
364
|
+
console.log('Created Storyblok component boilerplate (Page, Grid, Feature, Teaser).');
|
|
365
|
+
|
|
366
|
+
// Update layout.tsx to wrap with StoryblokProvider
|
|
367
|
+
const layoutPath = join(process.cwd(), 'src', 'app', 'layout.tsx');
|
|
368
|
+
try {
|
|
369
|
+
let layout = readFileSync(layoutPath, 'utf-8');
|
|
370
|
+
// Add import
|
|
371
|
+
layout = layout.replace(
|
|
372
|
+
"import '@/styles/globals.css';",
|
|
373
|
+
"import '@/styles/globals.css';\n\nimport StoryblokProvider from '@/components/StoryblokProvider';"
|
|
374
|
+
);
|
|
375
|
+
// Wrap <html> with StoryblokProvider
|
|
376
|
+
layout = layout.replace(
|
|
377
|
+
'<html lang="en"',
|
|
378
|
+
'<StoryblokProvider>\n\t\t<html lang="en"'
|
|
379
|
+
);
|
|
380
|
+
layout = layout.replace(
|
|
381
|
+
'</html>',
|
|
382
|
+
'</html>\n\t\t</StoryblokProvider>'
|
|
383
|
+
);
|
|
384
|
+
writeFileSync(layoutPath, layout);
|
|
385
|
+
console.log('Updated layout.tsx with StoryblokProvider.');
|
|
386
|
+
} catch {
|
|
387
|
+
console.log('Warning: Could not update layout.tsx with StoryblokProvider.');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Create a sample home page that fetches from Storyblok
|
|
391
|
+
writeFileSync(join(process.cwd(), 'src', 'app', 'page.tsx'), `import { getStoryblokApi } from '@/lib/storyblok';
|
|
392
|
+
import { StoryblokStory } from '@storyblok/react/rsc';
|
|
393
|
+
|
|
394
|
+
async function fetchData() {
|
|
395
|
+
const storyblokApi = getStoryblokApi();
|
|
396
|
+
return storyblokApi.get('cdn/stories/home', {
|
|
397
|
+
version: 'draft',
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export default async function Home() {
|
|
402
|
+
const { data } = await fetchData();
|
|
403
|
+
|
|
404
|
+
return (
|
|
405
|
+
<div>
|
|
406
|
+
<StoryblokStory story={data.story} />
|
|
407
|
+
</div>
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
`);
|
|
411
|
+
console.log('Created sample home page with Storyblok data fetching.');
|
|
412
|
+
|
|
413
|
+
console.log('');
|
|
414
|
+
console.log('=== Storyblok Setup Complete ===');
|
|
415
|
+
console.log('');
|
|
416
|
+
console.log('=== Next Steps ===');
|
|
417
|
+
console.log('');
|
|
418
|
+
console.log(' 1. Create a Storyblok space at https://app.storyblok.com');
|
|
419
|
+
console.log('');
|
|
420
|
+
console.log(' 2. Copy your Preview token and add it to .env.local:');
|
|
421
|
+
console.log(' STORYBLOK_PREVIEW_TOKEN=your_preview_token_here');
|
|
422
|
+
console.log('');
|
|
423
|
+
console.log(' 3. Make sure you have a "home" story in your Storyblok space');
|
|
424
|
+
console.log(' with a content type of "page"');
|
|
425
|
+
console.log('');
|
|
426
|
+
console.log(' 4. Set the Visual Editor URL to https://localhost:3000/');
|
|
427
|
+
console.log('');
|
|
428
|
+
console.log(' 5. Start your dev server:');
|
|
429
|
+
console.log(' npm run dev');
|
|
430
|
+
console.log('');
|
|
431
|
+
}
|
|
432
|
+
|
|
223
433
|
function generateReadme(projectName, cms) {
|
|
224
434
|
const title = projectName || 'Project Title';
|
|
225
435
|
|
|
@@ -431,6 +641,8 @@ SITE_URL=
|
|
|
431
641
|
|
|
432
642
|
if (selection.key === 'prismic') {
|
|
433
643
|
await setupPrismic(projectDir);
|
|
644
|
+
} else if (selection.key === 'storyblok') {
|
|
645
|
+
await setupStoryblok(projectDir);
|
|
434
646
|
} else {
|
|
435
647
|
console.log(`CMS preset ${selection.label} scaffolding is coming next.`);
|
|
436
648
|
console.log('This run only confirms selection for non-Prismic options.');
|
package/package.json
CHANGED