Skip to content

Create composable create-udl-app CLI #44

Description

@dawidurbanski

Summary

Create a CLI tool that scaffolds UDL projects with composable framework + data source combinations.

MVP Scope

For initial release, support:

  • Framework: Next.js only (14+)
  • Source: Contentful only

Usage

# Interactive mode (prompts for missing options)
npx create-udl-app my-app

# Explicit flags
npx create-udl-app my-app --framework=nextjs --source=contentful

# Future: Multiple sources
npx create-udl-app my-app --framework=nextjs --source=contentful,shopify

Interactive Flow

? What is your project named? › my-app
? Which framework? › Next.js (more coming soon)
? Which data sources? › ☑ Contentful (more coming soon)

Next.js Adapter Package

The generated app uses @udl/adapter-nextjs which provides a unified CLI:

{
  "scripts": {
    "dev": "udl-next dev",
    "build": "udl-next build", 
    "start": "udl-next start"
  }
}

How udl-next works

  1. Starts UDL server (runs sourceNodes, exposes GraphQL endpoint)
  2. Spawns the corresponding next command (dev, build, start)
  3. Manages lifecycle (graceful shutdown, signal forwarding)

Adapter package structure

packages/adapter-nextjs/
├── package.json
├── src/
│   └── cli.ts    # Entry point for udl-next command
└── README.md
// packages/adapter-nextjs/package.json
{
  "name": "@udl/adapter-nextjs",
  "bin": {
    "udl-next": "./dist/cli.js"
  },
  "peerDependencies": {
    "next": ">=14.0.0"
  },
  "dependencies": {
    "universal-data-layer": "workspace:*"
  }
}

The adapter uses the user's installed next (peer dep), so one package works for Next.js 14, 15, 16+.

Generated Output

package.json

{
  "name": "my-app",
  "scripts": {
    "dev": "udl-next dev",
    "build": "udl-next build",
    "start": "udl-next start"
  },
  "dependencies": {
    "next": "^15.0.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "universal-data-layer": "^1.0.0",
    "@udl/adapter-nextjs": "^1.0.0",
    "@udl/plugin-source-contentful": "^1.0.0"
  }
}

udl.config.ts

import { defineConfig } from 'universal-data-layer';

export const { config } = defineConfig({
  config: {
    plugins: [
      {
        name: '@udl/plugin-source-contentful',
        options: {
          spaceId: process.env.CONTENTFUL_SPACE_ID,
          accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
        },
      },
    ],
  },
});

.env.example

# Contentful
CONTENTFUL_SPACE_ID=
CONTENTFUL_ACCESS_TOKEN=
CONTENTFUL_ENVIRONMENT=master

Package Structure

packages/create-udl-app/
├── package.json
├── src/
│   ├── index.ts              # CLI entry point
│   ├── prompts.ts            # Interactive prompts
│   ├── generator.ts          # File generation logic
│   └── sources/
│       └── contentful.ts     # Source plugin definition
└── templates/
    └── frameworks/
        └── nextjs/           # Base Next.js skeleton

Acceptance Criteria

  • npx create-udl-app my-app works interactively
  • npx create-udl-app my-app --framework=nextjs --source=contentful works
  • Generated app runs with npm install && npm run dev
  • @udl/adapter-nextjs package created with udl-next CLI
  • Adapter works with Next.js 14, 15, and 16

Future Enhancements

  • More frameworks: Nuxt, Express, Astro
  • More sources: Shopify, Okendo (as plugins are published)

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions