Skip to content

Commit

Permalink
Merge pull request #203 from siguici/main
Browse files Browse the repository at this point in the history
✨ Feat(CLI): Allow use of Astro template
  • Loading branch information
siguici authored Dec 31, 2024
2 parents f6ffd41 + f8ac679 commit f2f85ec
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 47 deletions.
23 changes: 13 additions & 10 deletions libs/create-qwikdev-astro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,17 @@

| Name | Description |
| :--------------------------------------| :----------------------------------------|
| `--help` (`-h`) | Display available flags. |
| `--dry-run` | Walk through steps without executing. |
| `--force` / `--no-force` (`-f` / `--no-f`) | Overwrite target directory if it exists. |
| `--add` / `--no-add` (`-a` / `--no-a`) | Add QwikDev/astro to existing project. |
| `--install` / `--no-install` (`-i` / `--no-i`) | Install dependencies. |
| `--help` (`-h`) | Display available flags. |
| `--template` (`-t`) | Start from an Astro template. |
| `--biome` / `--no-biome` | Prefer Biome to ESLint/Prettier. |
| `--install` / `--no-install` (`-i` / `--no-i`) | Install dependencies. |
| `--git` / `--no-git` | Initialize Git repository. |
| `--ci` / `--no-ci` | Add CI workflow. |
| `--yes` (`-y`) | Skip all prompts by accepting defaults. |
| `--no` (`-n`) | Skip all prompts by declining defaults. |
| `--add` / `--no-add` (`-a` / `--no-a`) | Add QwikDev/astro to existing project. |
| `--force` / `--no-force` (`-f` / `--no-f`) | Overwrite target directory if it exists. |
| `--dry-run` | Walk through steps without executing. |

### 📦 API

Expand All @@ -78,14 +79,15 @@
export type Definition = {
destination: string;
adapter?: "deno" | "node" | "none";
force?: boolean;
add?: boolean;
install?: boolean;
template?: string;
biome?: boolean;
install?: boolean;
git?: boolean;
ci?: boolean;
yes?: boolean;
no?: boolean;
add?: boolean;
force?: boolean;
dryRun?: boolean;
};
```
Expand All @@ -96,14 +98,15 @@
export const defaultDefinition = {
destination: "./qwik-astro-app",
adapter: "none",
force: undefined,
add: undefined,
template: "",
install: undefined,
biome: undefined,
git: undefined,
ci: undefined,
yes: undefined,
no: undefined,
add: undefined,
force: undefined,
dryRun: undefined
} as const;
```
Expand Down
146 changes: 122 additions & 24 deletions libs/create-qwikdev-astro/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import fs, { cpSync } from "node:fs";
import path from "node:path";
import { copySync, ensureDirSync } from "fs-extra/esm";
import { copySync, ensureDirSync, pathExistsSync } from "fs-extra/esm";
import pkg from "../package.json";
import { ensureString } from "./console";
import { type Definition as BaseDefinition, Program } from "./core";
import { $, $pmInstall, $pmX } from "./process";
import { $, $pmCreate, $pmInstall, $pmX } from "./process";
import {
__dirname,
clearDir,
deepMergeJsonFile,
getPackageJson,
getPackageManager,
notEmptyDir,
Expand All @@ -22,12 +23,13 @@ import {
export type Definition = BaseDefinition & {
destination: string;
adapter: Adapter;
force?: boolean;
template?: string;
install?: boolean;
biome?: boolean;
git?: boolean;
ci?: boolean;
add?: boolean;
force?: boolean;
dryRun?: boolean;
};

Expand All @@ -37,15 +39,16 @@ export type UserDefinition = Partial<Definition>;
export const defaultDefinition = {
destination: "./qwik-astro-app",
adapter: "none",
force: undefined,
install: undefined,
template: undefined,
biome: undefined,
install: undefined,
git: undefined,
ci: undefined,
yes: undefined,
no: undefined,
dryRun: undefined,
add: undefined
add: undefined,
force: undefined,
dryRun: undefined
} as const;

export type Adapter = "node" | "deno" | "none";
Expand Down Expand Up @@ -84,6 +87,12 @@ export class Application extends Program<Definition, Input> {
desc: "Server adapter",
choices: ["deno", "node", "none"]
})
.argument("template", {
alias: "t",
type: "string",
default: defaultDefinition.template,
desc: "Start from an Astro template"
})
.option("add", {
alias: "a",
type: "boolean",
Expand Down Expand Up @@ -148,6 +157,7 @@ export class Application extends Program<Definition, Input> {
return {
destination: definition.destination,
adapter: definition.adapter,
template: definition.template ?? "",
force:
definition.force ?? (definition.add ? false : !!definition.yes && !definition.no),
add: !!definition.add,
Expand All @@ -166,7 +176,7 @@ export class Application extends Program<Definition, Input> {
definition.destination === defaultDefinition.destination
? await this.scanString(
`Where would you like to create your new project? ${this.gray(
`(Use '.' or 'qwik-astro-app' for current directory)`
`(Use '.' for current directory)`
)}`,
definition.destination
)
Expand All @@ -176,14 +186,13 @@ export class Application extends Program<Definition, Input> {
const outDir = resolveAbsoluteDir(destination.trim());
const exists = notEmptyDir(outDir);

const add =
definition.add === undefined
? exists &&
!!(await this.scanBoolean(
definition,
"Do you want to add @QwikDev/astro to your existing project?"
))
: definition.add;
const add = !!(definition.add === undefined && !definition.force
? exists &&
(await this.scanBoolean(
definition,
"Do you want to add @QwikDev/astro to your existing project?"
))
: definition.add);

const force =
definition.force === undefined
Expand All @@ -196,13 +205,24 @@ export class Application extends Program<Definition, Input> {
)}" already exists and is not empty. What would you like to overwrite it?`,
false
))
: false;
: definition.force;

const template: string =
definition.template === undefined &&
(await this.scanBoolean(definition, "Would you like to use a template?"))
? await this.scanString("What template would you like to use?", "")
: (definition.template ?? "");

const ask = !exists || add || force;

let adapter: Adapter;

if (ask && (!add || force) && definition.adapter === defaultDefinition.adapter) {
if (
!template &&
ask &&
(!add || force) &&
definition.adapter === defaultDefinition.adapter
) {
const adapterInput =
((await this.scanBoolean(
definition,
Expand Down Expand Up @@ -270,6 +290,7 @@ export class Application extends Program<Definition, Input> {
return {
destination,
adapter,
template,
biome,
ci,
install,
Expand Down Expand Up @@ -305,7 +326,7 @@ export class Application extends Program<Definition, Input> {
}
}

async runCreate(input: Input) {
async prepareDir(input: Input) {
const outDir = input.outDir;

if (notEmptyDir(outDir)) {
Expand All @@ -323,6 +344,12 @@ export class Application extends Program<Definition, Input> {
process.exit(1);
}
}
}

async runCreate(input: Input) {
const outDir = input.outDir;

await this.prepareDir(input);

let starterKit = input.adapter;

Expand All @@ -337,12 +364,83 @@ export class Application extends Program<Definition, Input> {
this.copyGitignore(input);
}

async runTemplate(input: Input) {
const args = [
"astro",
input.destination,
"--",
"--skip-houston",
"--template",
input.template,
"--add",
"@qwikdev/astro",
input.install ? "--install" : "--no-install",
input.git ? "--git" : "--no-git"
];

if (input.dryRun) {
args.push("--dry-run");
}

await this.prepareDir(input);
await $pmCreate(args.join(" "), process.cwd());

const outDir = input.outDir;
const stubPath = path.join(
__dirname,
"..",
"stubs",
"templates",
`none${input.biome ? "-biome" : ""}`
);

const configFiles = input.biome
? ["biome.json"]
: [".eslintignore", ".eslintrc.cjs", ".prettierignore", "prettier.config.cjs"];

const vscodeDir = path.join(stubPath, ".vscode");
const vscodeFiles = ["extensions.json", "launch.json"];

const projectPackageJsonFile = path.join(outDir, "package.json");
const projectTsconfigJsonFile = path.join(outDir, "tsconfig.json");
const templatePackageJsonFile = path.join(stubPath, "package.json");
const templateTsconfigJsonFile = path.join(stubPath, "tsconfig.json");

this.step(`Copying template files into ${this.bgBlue(` ${outDir} `)} ... 🐇`);

for (const vscodeFile of vscodeFiles) {
const vscodeFilePath = path.join(vscodeDir, vscodeFile);
const projectVscodeFilePath = path.join(outDir, ".vscode", vscodeFile);

pathExistsSync(projectVscodeFilePath)
? deepMergeJsonFile(projectVscodeFilePath, vscodeFilePath, true)
: cpSync(vscodeFilePath, projectVscodeFilePath, {
force: true
});
}

for (const configFile of configFiles) {
cpSync(path.join(stubPath, configFile), path.join(outDir, configFile), {
force: true
});
}

deepMergeJsonFile(projectPackageJsonFile, templatePackageJsonFile, true);
deepMergeJsonFile(projectTsconfigJsonFile, templateTsconfigJsonFile, true);

return input.install;
}

async start(input: Input): Promise<boolean> {
this.intro(`Let's create a ${this.bgBlue(" QwikDev/astro App ")} ✨`);
this.intro(
`Let's create a ${this.bgBlue(" QwikDev")}${this.bgMagenta("Astro")} App ✨`
);

let ranInstall: boolean;

if (input.add) {
if (input.template) {
ranInstall = await this.runTemplate(input);
} else if (input.add) {
ranInstall = await this.runInstall(input);
await this.runAdd(input);
} else {
Expand Down Expand Up @@ -445,10 +543,10 @@ export class Application extends Program<Definition, Input> {
try {
if (!input.dryRun) {
const res = [];
res.push(await $("git", ["init"], outDir).install);
res.push(await $("git", ["add", "-A"], outDir).install);
res.push(await $("git", ["init"], outDir).process);
res.push(await $("git", ["add", "-A"], outDir).process);
res.push(
await $("git", ["commit", "-m", "Initial commit 🎉"], outDir).install
await $("git", ["commit", "-m", "Initial commit 🎉"], outDir).process
);

if (res.some((r) => r === false)) {
Expand Down
18 changes: 15 additions & 3 deletions libs/create-qwikdev-astro/src/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const isPackageManagerInstalled = (packageManager: string) => {
export function $(cmd: string, args: string[], cwd: string) {
let child: ChildProcess;

const install = new Promise<boolean>((resolve) => {
const process = new Promise<boolean>((resolve) => {
try {
child = spawn(cmd, args, {
cwd,
Expand Down Expand Up @@ -42,7 +42,7 @@ export function $(cmd: string, args: string[], cwd: string) {
}
};

return { abort, install };
return { abort, process };
}

export const $pm = async (
Expand All @@ -52,7 +52,15 @@ export const $pm = async (
) => {
const packageManager = getPackageManager();
args = Array.isArray(args) ? args : [args];
if (["exec", "dlx"].includes(args[0])) {

if (args[0] === "create" && packageManager === "deno") {
const packageName = args[1];
const parts = packageName.split("/", 2);
const createCommand = parts[1]
? `npm:${parts[0]}/create-${parts[1]}`
: `npm:create-${parts[0]}`;
args = ["run", "-A", createCommand, ...args.slice(2)];
} else if (["exec", "dlx"].includes(args[0])) {
switch (packageManager) {
case "pnpm":
case "yarn":
Expand Down Expand Up @@ -97,6 +105,10 @@ export const $pmRun = async (script: string, cwd: string) => {
await $pm(["run", ...script.split(/\s+/)], cwd);
};

export const $pmCreate = async (script: string, cwd: string) => {
await $pm(["create", ...script.split(/\s+/)], cwd);
};

export const $pmExec = async (command: string, cwd: string) => {
await $pm(["exec", ...command.split(/\s+/)], cwd);
};
Expand Down
Loading

0 comments on commit f2f85ec

Please sign in to comment.