diff --git a/.changeset/few-chicken-exercise.md b/.changeset/few-chicken-exercise.md new file mode 100644 index 0000000000..9ddded96a7 --- /dev/null +++ b/.changeset/few-chicken-exercise.md @@ -0,0 +1,5 @@ +--- +"create-llama": patch +--- + +Add "Start in VSCode" option to postInstallAction diff --git a/.changeset/plenty-onions-relate.md b/.changeset/plenty-onions-relate.md new file mode 100644 index 0000000000..8b940e8a29 --- /dev/null +++ b/.changeset/plenty-onions-relate.md @@ -0,0 +1,5 @@ +--- +"create-llama": patch +--- + +Add devcontainers to generated code diff --git a/packages/create-llama/create-app.ts b/packages/create-llama/create-app.ts index 1b5ed303d0..2f7db13a7f 100644 --- a/packages/create-llama/create-app.ts +++ b/packages/create-llama/create-app.ts @@ -11,6 +11,7 @@ import fs from "fs"; import terminalLink from "terminal-link"; import type { InstallTemplateArgs } from "./helpers"; import { installTemplate } from "./helpers"; +import { writeDevcontainer } from "./helpers/devcontainer"; import { templatesDir } from "./helpers/dir"; import { toolsRequireConfig } from "./helpers/tools"; @@ -121,6 +122,8 @@ export async function createApp({ console.log(); } + await writeDevcontainer(root, templatesDir, framework, frontend); + if (toolsRequireConfig(tools)) { console.log( yellow( diff --git a/packages/create-llama/helpers/devcontainer.ts b/packages/create-llama/helpers/devcontainer.ts new file mode 100644 index 0000000000..cb008b9773 --- /dev/null +++ b/packages/create-llama/helpers/devcontainer.ts @@ -0,0 +1,61 @@ +import fs from "fs"; +import path from "path"; +import { TemplateFramework } from "./types"; + +function renderDevcontainerContent( + templatesDir: string, + framework: TemplateFramework, + frontend: boolean, +) { + const devcontainerJson: any = JSON.parse( + fs.readFileSync(path.join(templatesDir, "devcontainer.json"), "utf8"), + ); + + // Modify postCreateCommand + if (frontend) { + devcontainerJson.postCreateCommand = + framework === "fastapi" + ? "cd backend && poetry install && cd ../frontend && npm install" + : "cd backend && npm install && cd ../frontend && npm install"; + } else { + devcontainerJson.postCreateCommand = + framework === "fastapi" ? "poetry install" : "npm install"; + } + + // Modify containerEnv + if (framework === "fastapi") { + if (frontend) { + devcontainerJson.containerEnv = { + ...devcontainerJson.containerEnv, + PYTHONPATH: "${PYTHONPATH}:${workspaceFolder}/backend", + }; + } else { + devcontainerJson.containerEnv = { + ...devcontainerJson.containerEnv, + PYTHONPATH: "${PYTHONPATH}:${workspaceFolder}", + }; + } + } + + return JSON.stringify(devcontainerJson, null, 2); +} + +export const writeDevcontainer = async ( + root: string, + templatesDir: string, + framework: TemplateFramework, + frontend: boolean, +) => { + console.log("Adding .devcontainer"); + const devcontainerContent = renderDevcontainerContent( + templatesDir, + framework, + frontend, + ); + const devcontainerDir = path.join(root, ".devcontainer"); + fs.mkdirSync(devcontainerDir); + await fs.promises.writeFile( + path.join(devcontainerDir, "devcontainer.json"), + devcontainerContent, + ); +}; diff --git a/packages/create-llama/helpers/llama-pack.ts b/packages/create-llama/helpers/llama-pack.ts index 16cd801fa3..887201d9fc 100644 --- a/packages/create-llama/helpers/llama-pack.ts +++ b/packages/create-llama/helpers/llama-pack.ts @@ -142,7 +142,7 @@ export const installLlamapackProject = async ({ await copyLlamapackEmptyProject({ root }); await copyData({ root }); await installLlamapackExample({ root, llamapack }); - if (postInstallAction !== "none") { + if (postInstallAction === "runApp" || postInstallAction === "dependencies") { installPythonDependencies({ noRoot: true }); } }; diff --git a/packages/create-llama/helpers/python.ts b/packages/create-llama/helpers/python.ts index 3ebde783b1..285beb9a8b 100644 --- a/packages/create-llama/helpers/python.ts +++ b/packages/create-llama/helpers/python.ts @@ -266,7 +266,7 @@ export const installPythonTemplate = async ({ ); await addDependencies(root, addOnDependencies); - if (postInstallAction !== "none") { + if (postInstallAction === "runApp" || postInstallAction === "dependencies") { installPythonDependencies(); } }; diff --git a/packages/create-llama/helpers/types.ts b/packages/create-llama/helpers/types.ts index 52ef94b914..7422773731 100644 --- a/packages/create-llama/helpers/types.ts +++ b/packages/create-llama/helpers/types.ts @@ -6,7 +6,11 @@ export type TemplateFramework = "nextjs" | "express" | "fastapi"; export type TemplateEngine = "simple" | "context"; export type TemplateUI = "html" | "shadcn"; export type TemplateVectorDB = "none" | "mongo" | "pg" | "pinecone"; -export type TemplatePostInstallAction = "none" | "dependencies" | "runApp"; +export type TemplatePostInstallAction = + | "none" + | "VSCode" + | "dependencies" + | "runApp"; export type TemplateDataSource = { type: TemplateDataSourceType; config: TemplateDataSourceConfig; diff --git a/packages/create-llama/helpers/typescript.ts b/packages/create-llama/helpers/typescript.ts index cfadc67b1f..bab6812f76 100644 --- a/packages/create-llama/helpers/typescript.ts +++ b/packages/create-llama/helpers/typescript.ts @@ -228,7 +228,7 @@ export const installTSTemplate = async ({ JSON.stringify(packageJson, null, 2) + os.EOL, ); - if (postInstallAction !== "none") { + if (postInstallAction === "runApp" || postInstallAction === "dependencies") { await installTSDependencies(packageJson, packageManager, isOnline); } }; diff --git a/packages/create-llama/index.ts b/packages/create-llama/index.ts index 804eae6cce..47e78c26a4 100644 --- a/packages/create-llama/index.ts +++ b/packages/create-llama/index.ts @@ -1,11 +1,13 @@ #!/usr/bin/env node /* eslint-disable import/no-extraneous-dependencies */ +import { execSync } from "child_process"; import Commander from "commander"; import Conf from "conf"; import fs from "fs"; import path from "path"; import { bold, cyan, green, red, yellow } from "picocolors"; import prompts from "prompts"; +import terminalLink from "terminal-link"; import checkForUpdate from "update-check"; import { createApp } from "./create-app"; import { getPkgManager } from "./helpers/get-pkg-manager"; @@ -298,7 +300,31 @@ async function run(): Promise { }); conf.set("preferences", preferences); - if (program.postInstallAction === "runApp") { + if (program.postInstallAction === "VSCode") { + console.log(`Starting VSCode in ${root}...`); + try { + execSync(`code . --new-window --goto README.md`, { + stdio: "inherit", + cwd: root, + }); + } catch (error) { + console.log( + red( + `Failed to start VSCode in ${root}. +Got error: ${(error as Error).message}.\n`, + ), + ); + console.log( + `Make sure you have VSCode installed and added to your PATH. +Please check ${cyan( + terminalLink( + "This documentation", + `https://code.visualstudio.com/docs/setup/setup-overview`, + ), + )} for more information.`, + ); + } + } else if (program.postInstallAction === "runApp") { console.log(`Running app in ${root}...`); await runApp( root, diff --git a/packages/create-llama/questions.ts b/packages/create-llama/questions.ts index 6835bb3635..af5f17d69f 100644 --- a/packages/create-llama/questions.ts +++ b/packages/create-llama/questions.ts @@ -215,6 +215,10 @@ export const askQuestions = async ( title: "Just generate code (~1 sec)", value: "none", }, + { + title: "Start in VSCode (~1 sec)", + value: "VSCode", + }, { title: "Generate code and install dependencies (~2 min)", value: "dependencies", diff --git a/packages/create-llama/templates/devcontainer.json b/packages/create-llama/templates/devcontainer.json new file mode 100644 index 0000000000..f87545ffba --- /dev/null +++ b/packages/create-llama/templates/devcontainer.json @@ -0,0 +1,35 @@ +{ + "image": "mcr.microsoft.com/vscode/devcontainers/typescript-node:dev-20-bullseye", + "features": { + "ghcr.io/devcontainers-contrib/features/turborepo-npm:1": {}, + "ghcr.io/devcontainers-contrib/features/typescript:2": {}, + "ghcr.io/devcontainers/features/python:1": { + "version": "3.11", + "toolsToInstall": ["flake8", "black", "mypy", "poetry"] + } + }, + "customizations": { + "codespaces": { + "openFiles": ["README.md"] + }, + "vscode": { + "extensions": [ + "ms-vscode.typescript-language-features", + "esbenp.prettier-vscode", + "ms-python.python", + "ms-python.black-formatter", + "ms-python.vscode-flake8", + "ms-python.vscode-pylance" + ], + "settings": { + "python.formatting.provider": "black", + "python.languageServer": "Pylance", + "python.analysis.typeCheckingMode": "basic" + } + } + }, + "containerEnv": { + "POETRY_VIRTUALENVS_CREATE": "false" + }, + "forwardPorts": [3000, 8000] +}