Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚀 Feature: Separate templating engine from options parsing #76

Open
2 tasks done
JoshuaKGoldberg opened this issue Dec 20, 2024 · 2 comments
Open
2 tasks done
Assignees
Labels
status: blocked Waiting for something else to be resolved type: feature New enhancement or request
Milestone

Comments

@JoshuaKGoldberg
Copy link
Owner

JoshuaKGoldberg commented Dec 20, 2024

Bug Report Checklist

Overview

Right now, create joins roughly two big engine-y areas:

  • Options: defining in a schema, parsing from disk + user input, passing to a Template
  • Templating: the Blocks approach

But, the two don't have to be linked. And long-term I don't think they should be. Most users are going to want to start with very basic templating. A full system of Blocks and Presets is very heavyweight.

Long term I want to split the two. A Template should be able to define its Options as a Zod schema, then once create tells it to run, it does whatever it needs to. I think there are at least two kinds of templating engines that I can see implementers wanting to choose from:

  • Handlebars: essentially what most existing solutions such as Plop and Yeoman do
  • The current Blocks approach

The concept of a "Preset" might end up being completely unique to the latter. Maybe the two templating approaches might even be their own separate projects and docs sites, if not just separate packages in the monorepo. TBD.

Additional Info

This is not going to happen until after milestone/2 Blocks Launch. I'm going to get the create engine working for create-typescript-app, then expand to other use cases.

💖

@JoshuaKGoldberg JoshuaKGoldberg added the type: feature New enhancement or request label Dec 20, 2024
@JoshuaKGoldberg JoshuaKGoldberg added this to the Broad Launch milestone Dec 20, 2024
@JoshuaKGoldberg JoshuaKGoldberg self-assigned this Dec 20, 2024
@JoshuaKGoldberg JoshuaKGoldberg added the status: blocked Waiting for something else to be resolved label Dec 20, 2024
@JoshuaKGoldberg
Copy link
Owner Author

I had a think on this and I think it's absolutely the right way to go. Three axioms:

  • create itself should be as straightforward to get started with as possible
  • The Creation engine of outputting files, requests, scripts, and suggestions should be the core way Templates express themselves
  • Blocks are the best way to build complex Templates like CTA

So I'd like to rework the docs and packages structure to reflect that. The core root docs and packages for building Templates won't include the Blocks system at all. To start users off, they'll just show a straightforward Template:

import { createTemplate } from "create";

export default createTemplate({
  about: { name: "My Template" },
  produce() {
    return {
      files: {
        "tsconfig.json": JSON.stringify(
          {
            compilerOptions: {
              module: "NodeNext",
              moduleResolution: "NodeNext",
              strict: true,
            },
            include: ["src"],
          },
          null,
          "\t"
        ),
      },
    };
  },
});

Templates should also be able to have their own Options. That'll allow systems like Presets, but can be more flexible and just directly allow different settings.

For example, a Template might let you choose your package.json "type":

import { createTemplate } from "create";
import { z } from "zod";

export default createTemplate({
  about: { name: "My Package Template" },
  options: {
    name: z.string(),
    owner: z.string(),
    type: z.union([z.literal("module"), z.literal("script")]).default("module"),
  },
  produce({ options }) {
    return {
      files: {
        "package.json": JSON.stringify(
          {
            type,
            name: options.name,
            type: options.type,
            repository: {
              type: "git",
              url: `https://github.com/${options.owner}/${options.name}`,
            },
          },
          null,
          "\t"
        ),
      },
    };
  },
});

Here we see a change in terminology. Previously I'd been calling create an "engine" rather than a "framework" or "library". But now the term "engine" is really superseded by a Templating Engine.

Anyway, that's just the 'getting started' side.
For real-world usage I expect most users will want to choose a Templating Engine.
Two such Engine possibilities have been top of mind for me:

  • Blocks: the whole reason I made create in the first place!
  • Handlebars-from-disk: much closer to how existing solutions like Plop and Yeoman

Either way, the Templating Engine can generally provide folks a way to build their Template.
Here's how the Handlebars one might roughly look:

import { createTemplateHandlebars } from "create-template-handlebars";
import path from "node:path";

export default createTemplateHandlebars({
  about: { name: "My Package Template" },
  options: {
    description: z.string(),
    owner: z.string(),
    repository: z.string(),
    title: z.string(),
  },
  source: path.join(import.meta.dirname, "src"),
});

Here's a similarly revamped version of Blocks:

import { createTemplateBlocks } from "create-template-blocks";
import path from "node:path";

export default createTemplateBlocks({
  about: { name: "My Package Template" },
  presets: [presetMinimal, presetCommon, presetEverything],
  suggested: presetCommon,
});

The one unresolved sticking point in that design work is Options.
I'm not sure yet how I want them to be declared.
Especially given #71:

  • Who defines which Options along with how to fill in defaults?
  • How will the core create CLI prompt for Options defined by a Template?

TBD.

@johnnyreilly
Copy link

Feel like I'm jacked into Joshtrix 😅

I like this. The work I'm doing on Avalanche is likely to lean on Handlebars for templating initially as it's simple. I can see occasions where I'd want something more complex - I wouldn't mind the ability to mix and match perhaps? But I probably won't know how useful that would be until later

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: blocked Waiting for something else to be resolved type: feature New enhancement or request
Projects
None yet
Development

No branches or pull requests

2 participants