Skip to content

Commit

Permalink
Merge pull request #143 from framesjs/feat/cli-tool
Browse files Browse the repository at this point in the history
feat: basic create-frames cli tool
  • Loading branch information
stephancill authored Mar 26, 2024
2 parents dee5dcb + e1cc938 commit 7ba28f0
Show file tree
Hide file tree
Showing 141 changed files with 8,269 additions and 83 deletions.
5 changes: 5 additions & 0 deletions .changeset/smooth-bugs-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-frames": minor
---

feat: introduce cli tool to bootstrap projects from templates
3 changes: 3 additions & 0 deletions .scripts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
33 changes: 33 additions & 0 deletions .scripts/prepare-create-frames.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* This script is used to prepare create-frames package for publishing.
*
* It copies the templates directory to the root of the package so that it can be used by the create-frames package.
*/

import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import glob from "fast-glob";
import { cpSync, renameSync, rmSync } from 'node:fs';

const __dirname = dirname(fileURLToPath(import.meta.url));


rmSync(resolve(__dirname, "../packages/create-frames/templates"), { force: true, recursive: true });

cpSync(
resolve(__dirname, "../templates"),
resolve(__dirname, "../packages/create-frames/templates"),
{
recursive: true,
filter: (src) => !/(node_modules|\.turbo|\.next|next-env\.d\.ts)/.test(src),
}
);

const dotfiles = await glob(
resolve(__dirname, "../packages/create-frames/templates/**/.*")
);

// rename dotfiles to use underscore instead of dot so we can publish them to npm as is
for (const doftile of dotfiles) {
renameSync(doftile, doftile.replace("/.", "/_"));
}
33 changes: 27 additions & 6 deletions docs/pages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ import { HomePage } from "vocs/components";
<HomePage.Button href="https://github.com/framesjs/frames.js">
GitHub
</HomePage.Button>
<HomePage.Button href="#quickstart" variant="accent">
Quickstart
</HomePage.Button>
<HomePage.Button href="https://github.com/framesjs/frames.js">
GitHub
</HomePage.Button>
</HomePage.Buttons>
</HomePage.Root>

Expand All @@ -31,15 +37,25 @@ import { HomePage } from "vocs/components";

# Quickstart

## 1. Clone the frames.js starter template (with local debugger)
## Bootstrap the project from template using the CLI tool

Run to clone the starter into a new folder called `framesjs-starter`
Run one of the commands below based on your preferred package manager and then follow the steps in the terminal.

```bash
npx degit github:framesjs/frames.js/examples/framesjs-starter#main framesjs-starter
:::code-group

```bash [npm]
npm init frames
```

```bash [yarn]
yarn create frames
```

```bash [pnpm]
pnpm create frames
```

or [clone from github](https://github.com/framesjs/frames.js/tree/main/examples/framesjs-starter)
:::

## Alternatively, add frames.js to your existing project manually

Expand All @@ -58,7 +74,12 @@ export async function generateMetadata() {
return {
title: "My page",
other: await fetchMetadata(
new URL("/frames", process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : "http://localhost:3000")
new URL(
"/frames",
process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: "http://localhost:3000"
)
),
};
}
Expand Down
6 changes: 2 additions & 4 deletions examples/framesjs-starter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
"dev:monorepo": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"update-debugger": "npx degit github:framesjs/frames.js/examples/framesjs-starter/app/debug#main app/debug --force",
"update-framesjs": "yarn upgrade frames.js@latest && yarn run update-debugger"
"lint": "next lint"
},
"dependencies": {
"@farcaster/core": "^0.14.3",
Expand Down Expand Up @@ -43,4 +41,4 @@
"tailwindcss": "^3.3.0",
"typescript": "^5.3.3"
}
}
}
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
"name": "framesjs-monorepo",
"private": true,
"scripts": {
"build": "turbo build",
"build:ci": "turbo build --filter=!debugger --filter=!framesjs-starter --filter=!utils-starter --filter=!docs",
"build": "turbo build --filter=!./templates/* && node ./.scripts/prepare-create-frames.js",
"build:ci": "turbo build --filter=!debugger --filter=!framesjs-starter --filter=!utils-starter --filter=!docs --filter=!template-*",
"dev": "FJS_MONOREPO=true turbo dev --filter=framesjs-starter... --filter=debugger...",
"dev:custom-redirects": "turbo dev --filter=custom-redirects...",
"dev:utils-starter": "turbo dev --filter=utils-starter...",
"dev:all": "turbo dev",
"lint": "turbo lint",
"lint": "turbo lint --filter=!template-*",
"test:ci": "jest --ci",
"test": "cd ./packages/frames.js && npm run test:watch",
"publish-packages": "turbo run build lint && changeset version && changeset publish && git push --follow-tags origin main",
Expand All @@ -23,9 +23,11 @@
"@framesjs/typescript-config": "*",
"@jest/globals": "^29.7.0",
"@types/jest": "^29.5.11",
"fast-glob": "^3.3.2",
"jest": "^29.7.0",
"nock": "beta",
"prettier": "^3.1.1",
"rimraf": "^5.0.5",
"turbo": "^1.12.2"
},
"engines": {
Expand All @@ -35,7 +37,8 @@
"workspaces": [
"docs",
"examples/*",
"packages/*"
"packages/*",
"templates/*"
],
"version": "0.3.0-canary.0"
}
1 change: 1 addition & 0 deletions packages/create-frames/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
templates
1 change: 1 addition & 0 deletions packages/create-frames/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!templates
48 changes: 48 additions & 0 deletions packages/create-frames/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Create Frames.js app

Create a new Frames.js app with a single command using predefined templates.

## Creating a project

### Using npm

```sh
npm init frames
```

### Using yarn

```sh
yarn create frames
```

### Using pnpm

```sh
pnpm create frames
```

## Installing globally (optional) and running from terminal

You can also install this package globally and run it from your terminal.

### Using npm

```sh
npm install -g create-frames
create-frames
```

### Using yarn

```sh
yarn global add create-frames
create-frames
```

### Using pnpm

```sh
pnpm add -g create-frames
create-frames
```
30 changes: 30 additions & 0 deletions packages/create-frames/bin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env node
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { getTemplates } from "./utils/getTemplates.js";
import { create } from "./create.js";

// use only default command
yargs(hideBin(process.argv))
.scriptName("create-frames")
.usage("Usage: $0 [options]")
.command(
"$0",
"Create a new project",
{
name: {
alias: "n",
describe: "Name of the project",
type: "string",
},
template: {
alias: "t",
describe: "Choose a template for the project",
choices: getTemplates(),
},
},
(args) => {
create(args);
}
)
.parse();
135 changes: 135 additions & 0 deletions packages/create-frames/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { intro, log, outro, select, confirm, text } from "@clack/prompts";
import { getTemplates } from "./utils/getTemplates.js";
import { dirname, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { spawnSync } from "node:child_process";
import ignore from "ignore";
import pc from "picocolors";
import { detect as detectPackageManager } from "detect-package-manager";
import {
cpSync,
readFileSync,
readdirSync,
renameSync,
writeFileSync,
} from "node:fs";
import { packageManagerRunCommand } from "./utils/packageManagerRunCommand.js";

const __dirname = dirname(fileURLToPath(import.meta.url));

/**
*
* @param {import('yargs').ArgumentsCamelCase<{ t?: string, n?: string }>} params
*/
export async function create(params) {
if (params.$0 !== "create-frames") {
throw new Error("Invalid command");
}

intro("Welcome to frames.js");

// if there is no -n argument (name, ask for the name of new project)
let projectName =
params.n ||
(await text({
message: "Enter the name of your project",
placeholder: "my-frames-app",
validate(input) {
if (!input) {
return "Project name is required";
}

return;
},
}));

const destDir = resolve(process.cwd(), projectName);

const template =
params.t ||
(await select({
message: "Choose a template for the project",
options: getTemplates().map((template) => ({
name: template,
value: template,
})),
defaultValue: "next",
validate(input) {
if (!input) {
return "Template is required";
}

return;
},
}));

const templateDir = resolve(__dirname, "./templates", template);
const ignoredPatterns = readFileSync(
resolve(templateDir, "_gitignore"),
"utf-8"
);
const ignored = ignore().add(ignoredPatterns);

cpSync(templateDir, destDir, {
force: true,
recursive: true,
filter(src) {
const path = relative(templateDir, src);

return !path || !ignored.ignores(path);
},
});

for (const file of readdirSync(destDir)) {
if (!file.startsWith("_")) continue;

renameSync(
resolve(destDir, file),
resolve(destDir, file.replace("_", "."))
);
}

const pkgJsonPath = resolve(destDir, "package.json");
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
pkgJson.name = projectName;
writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));

log.success(`Project successfully scaffolded in ${pc.blue(destDir)}!`);

const pkgManager = await detectPackageManager();

const wantsToInstallDependencies = await confirm({
message: `Do you want to install the dependencies using ${pkgManager}?`,
initialValue: true,
});

if (wantsToInstallDependencies) {
log.message(`Installing the dependencies...`);
const result = spawnSync(pkgManager, ["install"], {
cwd: destDir,
stdio: "ignore",
});

if (result.status !== 0) {
log.error(
`Failed to install the dependencies, please install them manually.`
);
process.exit(1);
}

log.success(`Dependencies installed!`);
}

log.message("Next steps:");
log.step(
`1. Go to the project directory by running: ${pc.blue(`cd ./${projectName}`)}`
);
log.step(
`2. Start the development server and run the app in debugger by running: ${pc.blue(await packageManagerRunCommand("dev"))}`
);
log.step(
`3. Open your browser and go to ${pc.blue(`http://localhost:3010`)} to see your app running in the debugger`
);

outro("Done! Your project has been set up! 🎉");
}
Loading

0 comments on commit 7ba28f0

Please sign in to comment.