From 32592457806539b95b276735c0844e54f143964d Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Wed, 25 Oct 2023 15:43:36 +0700 Subject: [PATCH 01/44] add create-next-app v13.5.6 --- packages/create-llama/README.md | 84 ++ packages/create-llama/create-app.ts | 268 ++++++ packages/create-llama/helpers/copy.ts | 50 + packages/create-llama/helpers/examples.ts | 131 +++ .../create-llama/helpers/get-pkg-manager.ts | 19 + packages/create-llama/helpers/git.ts | 58 ++ packages/create-llama/helpers/install.ts | 50 + .../create-llama/helpers/is-folder-empty.ts | 62 ++ packages/create-llama/helpers/is-online.ts | 40 + packages/create-llama/helpers/is-writeable.ts | 10 + packages/create-llama/helpers/make-dir.ts | 8 + packages/create-llama/helpers/validate-pkg.ts | 20 + packages/create-llama/index.ts | 511 ++++++++++ packages/create-llama/package.json | 57 ++ packages/create-llama/pnpm-lock.yaml | 882 ++++++++++++++++++ packages/create-llama/tsconfig.json | 11 + pnpm-lock.yaml | 608 +++++++++--- 17 files changed, 2744 insertions(+), 125 deletions(-) create mode 100644 packages/create-llama/README.md create mode 100644 packages/create-llama/create-app.ts create mode 100644 packages/create-llama/helpers/copy.ts create mode 100644 packages/create-llama/helpers/examples.ts create mode 100644 packages/create-llama/helpers/get-pkg-manager.ts create mode 100644 packages/create-llama/helpers/git.ts create mode 100644 packages/create-llama/helpers/install.ts create mode 100644 packages/create-llama/helpers/is-folder-empty.ts create mode 100644 packages/create-llama/helpers/is-online.ts create mode 100644 packages/create-llama/helpers/is-writeable.ts create mode 100644 packages/create-llama/helpers/make-dir.ts create mode 100644 packages/create-llama/helpers/validate-pkg.ts create mode 100644 packages/create-llama/index.ts create mode 100644 packages/create-llama/package.json create mode 100644 packages/create-llama/pnpm-lock.yaml create mode 100644 packages/create-llama/tsconfig.json diff --git a/packages/create-llama/README.md b/packages/create-llama/README.md new file mode 100644 index 0000000000..b50f474249 --- /dev/null +++ b/packages/create-llama/README.md @@ -0,0 +1,84 @@ +# Create Next App + +The easiest way to get started with Next.js is by using `create-next-app`. This CLI tool enables you to quickly start building a new Next.js application, with everything set up for you. You can create a new app using the default Next.js template, or by using one of the [official Next.js examples](https://github.com/vercel/next.js/tree/canary/examples). To get started, use the following command: + +### Interactive + +You can create a new project interactively by running: + +```bash +npx create-next-app@latest +# or +yarn create next-app +# or +pnpm create next-app +# or +bunx create-next-app +``` + +You will be asked for the name of your project, and then whether you want to +create a TypeScript project: + +```bash +✔ Would you like to use TypeScript? … No / Yes +``` + +Select **Yes** to install the necessary types/dependencies and create a new TS project. + +### Non-interactive + +You can also pass command line arguments to set up a new project +non-interactively. See `create-next-app --help`: + +```bash +create-next-app [options] + +Options: + -V, --version output the version number + --ts, --typescript + + Initialize as a TypeScript project. (default) + + --js, --javascript + + Initialize as a JavaScript project. + + --use-npm + + Explicitly tell the CLI to bootstrap the app using npm + + --use-pnpm + + Explicitly tell the CLI to bootstrap the app using pnpm + + --use-yarn + + Explicitly tell the CLI to bootstrap the app using Yarn + + --use-bun + + Explicitly tell the CLI to bootstrap the app using Bun + + -e, --example [name]|[github-url] + + An example to bootstrap the app with. You can use an example name + from the official Next.js repo or a GitHub URL. The URL can use + any branch and/or subdirectory + + --example-path + + In a rare case, your GitHub URL might contain a branch name with + a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). + In this case, you must specify the path to the example separately: + --example-path foo/bar +``` + +### Why use Create Next App? + +`create-next-app` allows you to create a new Next.js app within seconds. It is officially maintained by the creators of Next.js, and includes a number of benefits: + +- **Interactive Experience**: Running `npx create-next-app@latest` (with no arguments) launches an interactive experience that guides you through setting up a project. +- **Zero Dependencies**: Initializing a project is as quick as one second. Create Next App has zero dependencies. +- **Offline Support**: Create Next App will automatically detect if you're offline and bootstrap your project using your local package cache. +- **Support for Examples**: Create Next App can bootstrap your application using an example from the Next.js examples collection (e.g. `npx create-next-app --example api-routes`). +- **Tested**: The package is part of the Next.js monorepo and tested using the same integration test suite as Next.js itself, ensuring it works as expected with every release. diff --git a/packages/create-llama/create-app.ts b/packages/create-llama/create-app.ts new file mode 100644 index 0000000000..89719e48ad --- /dev/null +++ b/packages/create-llama/create-app.ts @@ -0,0 +1,268 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import retry from 'async-retry' +import { red, green, cyan } from 'picocolors' +import fs from 'fs' +import path from 'path' +import type { RepoInfo } from './helpers/examples' +import { + downloadAndExtractExample, + downloadAndExtractRepo, + getRepoInfo, + existsInRepo, + hasRepo, +} from './helpers/examples' +import { makeDir } from './helpers/make-dir' +import { tryGitInit } from './helpers/git' +import { install } from './helpers/install' +import { isFolderEmpty } from './helpers/is-folder-empty' +import { getOnline } from './helpers/is-online' +import { isWriteable } from './helpers/is-writeable' +import type { PackageManager } from './helpers/get-pkg-manager' + +import type { TemplateMode, TemplateType } from './templates' +import { getTemplateFile, installTemplate } from './templates' + +export class DownloadError extends Error {} + +export async function createApp({ + appPath, + packageManager, + example, + examplePath, + typescript, + tailwind, + eslint, + appRouter, + srcDir, + importAlias, +}: { + appPath: string + packageManager: PackageManager + example?: string + examplePath?: string + typescript: boolean + tailwind: boolean + eslint: boolean + appRouter: boolean + srcDir: boolean + importAlias: string +}): Promise { + let repoInfo: RepoInfo | undefined + const mode: TemplateMode = typescript ? 'ts' : 'js' + const template: TemplateType = appRouter + ? tailwind + ? 'app-tw' + : 'app' + : tailwind + ? 'default-tw' + : 'default' + + if (example) { + let repoUrl: URL | undefined + + try { + repoUrl = new URL(example) + } catch (error: any) { + if (error.code !== 'ERR_INVALID_URL') { + console.error(error) + process.exit(1) + } + } + + if (repoUrl) { + if (repoUrl.origin !== 'https://github.com') { + console.error( + `Invalid URL: ${red( + `"${example}"` + )}. Only GitHub repositories are supported. Please use a GitHub URL and try again.` + ) + process.exit(1) + } + + repoInfo = await getRepoInfo(repoUrl, examplePath) + + if (!repoInfo) { + console.error( + `Found invalid GitHub URL: ${red( + `"${example}"` + )}. Please fix the URL and try again.` + ) + process.exit(1) + } + + const found = await hasRepo(repoInfo) + + if (!found) { + console.error( + `Could not locate the repository for ${red( + `"${example}"` + )}. Please check that the repository exists and try again.` + ) + process.exit(1) + } + } else if (example !== '__internal-testing-retry') { + const found = await existsInRepo(example) + + if (!found) { + console.error( + `Could not locate an example named ${red( + `"${example}"` + )}. It could be due to the following:\n`, + `1. Your spelling of example ${red( + `"${example}"` + )} might be incorrect.\n`, + `2. You might not be connected to the internet or you are behind a proxy.` + ) + process.exit(1) + } + } + } + + const root = path.resolve(appPath) + + if (!(await isWriteable(path.dirname(root)))) { + console.error( + 'The application path is not writable, please check folder permissions and try again.' + ) + console.error( + 'It is likely you do not have write permissions for this folder.' + ) + process.exit(1) + } + + const appName = path.basename(root) + + await makeDir(root) + if (!isFolderEmpty(root, appName)) { + process.exit(1) + } + + const useYarn = packageManager === 'yarn' + const isOnline = !useYarn || (await getOnline()) + const originalDirectory = process.cwd() + + console.log(`Creating a new Next.js app in ${green(root)}.`) + console.log() + + process.chdir(root) + + const packageJsonPath = path.join(root, 'package.json') + let hasPackageJson = false + + if (example) { + /** + * If an example repository is provided, clone it. + */ + try { + if (repoInfo) { + const repoInfo2 = repoInfo + console.log( + `Downloading files from repo ${cyan( + example + )}. This might take a moment.` + ) + console.log() + await retry(() => downloadAndExtractRepo(root, repoInfo2), { + retries: 3, + }) + } else { + console.log( + `Downloading files for example ${cyan( + example + )}. This might take a moment.` + ) + console.log() + await retry(() => downloadAndExtractExample(root, example), { + retries: 3, + }) + } + } catch (reason) { + function isErrorLike(err: unknown): err is { message: string } { + return ( + typeof err === 'object' && + err !== null && + typeof (err as { message?: unknown }).message === 'string' + ) + } + throw new DownloadError( + isErrorLike(reason) ? reason.message : reason + '' + ) + } + // Copy `.gitignore` if the application did not provide one + const ignorePath = path.join(root, '.gitignore') + if (!fs.existsSync(ignorePath)) { + fs.copyFileSync( + getTemplateFile({ template, mode, file: 'gitignore' }), + ignorePath + ) + } + + // Copy `next-env.d.ts` to any example that is typescript + const tsconfigPath = path.join(root, 'tsconfig.json') + if (fs.existsSync(tsconfigPath)) { + fs.copyFileSync( + getTemplateFile({ template, mode: 'ts', file: 'next-env.d.ts' }), + path.join(root, 'next-env.d.ts') + ) + } + + hasPackageJson = fs.existsSync(packageJsonPath) + if (hasPackageJson) { + console.log('Installing packages. This might take a couple of minutes.') + console.log() + + await install(packageManager, isOnline) + console.log() + } + } else { + /** + * If an example repository is not provided for cloning, proceed + * by installing from a template. + */ + await installTemplate({ + appName, + root, + template, + mode, + packageManager, + isOnline, + tailwind, + eslint, + srcDir, + importAlias, + }) + } + + if (tryGitInit(root)) { + console.log('Initialized a git repository.') + console.log() + } + + let cdpath: string + if (path.join(originalDirectory, appName) === appPath) { + cdpath = appName + } else { + cdpath = appPath + } + + console.log(`${green('Success!')} Created ${appName} at ${appPath}`) + + if (hasPackageJson) { + console.log('Inside that directory, you can run several commands:') + console.log() + console.log(cyan(` ${packageManager} ${useYarn ? '' : 'run '}dev`)) + console.log(' Starts the development server.') + console.log() + console.log(cyan(` ${packageManager} ${useYarn ? '' : 'run '}build`)) + console.log(' Builds the app for production.') + console.log() + console.log(cyan(` ${packageManager} start`)) + console.log(' Runs the built app in production mode.') + console.log() + console.log('We suggest that you begin by typing:') + console.log() + console.log(cyan(' cd'), cdpath) + console.log(` ${cyan(`${packageManager} ${useYarn ? '' : 'run '}dev`)}`) + } + console.log() +} diff --git a/packages/create-llama/helpers/copy.ts b/packages/create-llama/helpers/copy.ts new file mode 100644 index 0000000000..407b000ab5 --- /dev/null +++ b/packages/create-llama/helpers/copy.ts @@ -0,0 +1,50 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { async as glob } from 'fast-glob' +import path from 'path' +import fs from 'fs' + +interface CopyOption { + cwd?: string + rename?: (basename: string) => string + parents?: boolean +} + +const identity = (x: string) => x + +export const copy = async ( + src: string | string[], + dest: string, + { cwd, rename = identity, parents = true }: CopyOption = {} +) => { + const source = typeof src === 'string' ? [src] : src + + if (source.length === 0 || !dest) { + throw new TypeError('`src` and `dest` are required') + } + + const sourceFiles = await glob(source, { + cwd, + dot: true, + absolute: false, + stats: false, + }) + + const destRelativeToCwd = cwd ? path.resolve(cwd, dest) : dest + + return Promise.all( + sourceFiles.map(async (p) => { + const dirname = path.dirname(p) + const basename = rename(path.basename(p)) + + const from = cwd ? path.resolve(cwd, p) : p + const to = parents + ? path.join(destRelativeToCwd, dirname, basename) + : path.join(destRelativeToCwd, basename) + + // Ensure the destination directory exists + await fs.promises.mkdir(path.dirname(to), { recursive: true }) + + return fs.promises.copyFile(from, to) + }) + ) +} diff --git a/packages/create-llama/helpers/examples.ts b/packages/create-llama/helpers/examples.ts new file mode 100644 index 0000000000..dad9895bd1 --- /dev/null +++ b/packages/create-llama/helpers/examples.ts @@ -0,0 +1,131 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import got from 'got' +import tar from 'tar' +import { Stream } from 'stream' +import { promisify } from 'util' +import { join } from 'path' +import { tmpdir } from 'os' +import { createWriteStream, promises as fs } from 'fs' + +const pipeline = promisify(Stream.pipeline) + +export type RepoInfo = { + username: string + name: string + branch: string + filePath: string +} + +export async function isUrlOk(url: string): Promise { + const res = await got.head(url).catch((e) => e) + return res.statusCode === 200 +} + +export async function getRepoInfo( + url: URL, + examplePath?: string +): Promise { + const [, username, name, t, _branch, ...file] = url.pathname.split('/') + const filePath = examplePath ? examplePath.replace(/^\//, '') : file.join('/') + + if ( + // Support repos whose entire purpose is to be a Next.js example, e.g. + // https://github.com/:username/:my-cool-nextjs-example-repo-name. + t === undefined || + // Support GitHub URL that ends with a trailing slash, e.g. + // https://github.com/:username/:my-cool-nextjs-example-repo-name/ + // In this case "t" will be an empty string while the next part "_branch" will be undefined + (t === '' && _branch === undefined) + ) { + const infoResponse = await got( + `https://api.github.com/repos/${username}/${name}` + ).catch((e) => e) + if (infoResponse.statusCode !== 200) { + return + } + const info = JSON.parse(infoResponse.body) + return { username, name, branch: info['default_branch'], filePath } + } + + // If examplePath is available, the branch name takes the entire path + const branch = examplePath + ? `${_branch}/${file.join('/')}`.replace(new RegExp(`/${filePath}|/$`), '') + : _branch + + if (username && name && branch && t === 'tree') { + return { username, name, branch, filePath } + } +} + +export function hasRepo({ + username, + name, + branch, + filePath, +}: RepoInfo): Promise { + const contentsUrl = `https://api.github.com/repos/${username}/${name}/contents` + const packagePath = `${filePath ? `/${filePath}` : ''}/package.json` + + return isUrlOk(contentsUrl + packagePath + `?ref=${branch}`) +} + +export function existsInRepo(nameOrUrl: string): Promise { + try { + const url = new URL(nameOrUrl) + return isUrlOk(url.href) + } catch { + return isUrlOk( + `https://api.github.com/repos/vercel/next.js/contents/examples/${encodeURIComponent( + nameOrUrl + )}` + ) + } +} + +async function downloadTar(url: string) { + const tempFile = join(tmpdir(), `next.js-cna-example.temp-${Date.now()}`) + await pipeline(got.stream(url), createWriteStream(tempFile)) + return tempFile +} + +export async function downloadAndExtractRepo( + root: string, + { username, name, branch, filePath }: RepoInfo +) { + const tempFile = await downloadTar( + `https://codeload.github.com/${username}/${name}/tar.gz/${branch}` + ) + + await tar.x({ + file: tempFile, + cwd: root, + strip: filePath ? filePath.split('/').length + 1 : 1, + filter: (p) => + p.startsWith( + `${name}-${branch.replace(/\//g, '-')}${ + filePath ? `/${filePath}/` : '/' + }` + ), + }) + + await fs.unlink(tempFile) +} + +export async function downloadAndExtractExample(root: string, name: string) { + if (name === '__internal-testing-retry') { + throw new Error('This is an internal example for testing the CLI.') + } + + const tempFile = await downloadTar( + 'https://codeload.github.com/vercel/next.js/tar.gz/canary' + ) + + await tar.x({ + file: tempFile, + cwd: root, + strip: 2 + name.split('/').length, + filter: (p) => p.includes(`next.js-canary/examples/${name}/`), + }) + + await fs.unlink(tempFile) +} diff --git a/packages/create-llama/helpers/get-pkg-manager.ts b/packages/create-llama/helpers/get-pkg-manager.ts new file mode 100644 index 0000000000..20900ebcb9 --- /dev/null +++ b/packages/create-llama/helpers/get-pkg-manager.ts @@ -0,0 +1,19 @@ +export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun' + +export function getPkgManager(): PackageManager { + const userAgent = process.env.npm_config_user_agent || '' + + if (userAgent.startsWith('yarn')) { + return 'yarn' + } + + if (userAgent.startsWith('pnpm')) { + return 'pnpm' + } + + if (userAgent.startsWith('bun')) { + return 'bun' + } + + return 'npm' +} diff --git a/packages/create-llama/helpers/git.ts b/packages/create-llama/helpers/git.ts new file mode 100644 index 0000000000..79eb9115a1 --- /dev/null +++ b/packages/create-llama/helpers/git.ts @@ -0,0 +1,58 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { execSync } from 'child_process' +import path from 'path' +import fs from 'fs' + +function isInGitRepository(): boolean { + try { + execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }) + return true + } catch (_) {} + return false +} + +function isInMercurialRepository(): boolean { + try { + execSync('hg --cwd . root', { stdio: 'ignore' }) + return true + } catch (_) {} + return false +} + +function isDefaultBranchSet(): boolean { + try { + execSync('git config init.defaultBranch', { stdio: 'ignore' }) + return true + } catch (_) {} + return false +} + +export function tryGitInit(root: string): boolean { + let didInit = false + try { + execSync('git --version', { stdio: 'ignore' }) + if (isInGitRepository() || isInMercurialRepository()) { + return false + } + + execSync('git init', { stdio: 'ignore' }) + didInit = true + + if (!isDefaultBranchSet()) { + execSync('git checkout -b main', { stdio: 'ignore' }) + } + + execSync('git add -A', { stdio: 'ignore' }) + execSync('git commit -m "Initial commit from Create Next App"', { + stdio: 'ignore', + }) + return true + } catch (e) { + if (didInit) { + try { + fs.rmSync(path.join(root, '.git'), { recursive: true, force: true }) + } catch (_) {} + } + return false + } +} diff --git a/packages/create-llama/helpers/install.ts b/packages/create-llama/helpers/install.ts new file mode 100644 index 0000000000..911573b967 --- /dev/null +++ b/packages/create-llama/helpers/install.ts @@ -0,0 +1,50 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { yellow } from 'picocolors' +import spawn from 'cross-spawn' +import type { PackageManager } from './get-pkg-manager' + +/** + * Spawn a package manager installation based on user preference. + * + * @returns A Promise that resolves once the installation is finished. + */ +export async function install( + /** Indicate which package manager to use. */ + packageManager: PackageManager, + /** Indicate whether there is an active Internet connection.*/ + isOnline: boolean +): Promise { + let args: string[] = ['install'] + if (!isOnline) { + console.log( + yellow('You appear to be offline.\nFalling back to the local cache.') + ) + args.push('--offline') + } + /** + * Return a Promise that resolves once the installation is finished. + */ + return new Promise((resolve, reject) => { + /** + * Spawn the installation process. + */ + const child = spawn(packageManager, args, { + stdio: 'inherit', + env: { + ...process.env, + ADBLOCK: '1', + // we set NODE_ENV to development as pnpm skips dev + // dependencies when production + NODE_ENV: 'development', + DISABLE_OPENCOLLECTIVE: '1', + }, + }) + child.on('close', (code) => { + if (code !== 0) { + reject({ command: `${packageManager} ${args.join(' ')}` }) + return + } + resolve() + }) + }) +} diff --git a/packages/create-llama/helpers/is-folder-empty.ts b/packages/create-llama/helpers/is-folder-empty.ts new file mode 100644 index 0000000000..ad89ec22fb --- /dev/null +++ b/packages/create-llama/helpers/is-folder-empty.ts @@ -0,0 +1,62 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { green, blue } from 'picocolors' +import fs from 'fs' +import path from 'path' + +export function isFolderEmpty(root: string, name: string): boolean { + const validFiles = [ + '.DS_Store', + '.git', + '.gitattributes', + '.gitignore', + '.gitlab-ci.yml', + '.hg', + '.hgcheck', + '.hgignore', + '.idea', + '.npmignore', + '.travis.yml', + 'LICENSE', + 'Thumbs.db', + 'docs', + 'mkdocs.yml', + 'npm-debug.log', + 'yarn-debug.log', + 'yarn-error.log', + 'yarnrc.yml', + '.yarn', + ] + + const conflicts = fs + .readdirSync(root) + .filter((file) => !validFiles.includes(file)) + // Support IntelliJ IDEA-based editors + .filter((file) => !/\.iml$/.test(file)) + + if (conflicts.length > 0) { + console.log( + `The directory ${green(name)} contains files that could conflict:` + ) + console.log() + for (const file of conflicts) { + try { + const stats = fs.lstatSync(path.join(root, file)) + if (stats.isDirectory()) { + console.log(` ${blue(file)}/`) + } else { + console.log(` ${file}`) + } + } catch { + console.log(` ${file}`) + } + } + console.log() + console.log( + 'Either try using a new directory name, or remove the files listed above.' + ) + console.log() + return false + } + + return true +} diff --git a/packages/create-llama/helpers/is-online.ts b/packages/create-llama/helpers/is-online.ts new file mode 100644 index 0000000000..c2f3f83a93 --- /dev/null +++ b/packages/create-llama/helpers/is-online.ts @@ -0,0 +1,40 @@ +import { execSync } from 'child_process' +import dns from 'dns' +import url from 'url' + +function getProxy(): string | undefined { + if (process.env.https_proxy) { + return process.env.https_proxy + } + + try { + const httpsProxy = execSync('npm config get https-proxy').toString().trim() + return httpsProxy !== 'null' ? httpsProxy : undefined + } catch (e) { + return + } +} + +export function getOnline(): Promise { + return new Promise((resolve) => { + dns.lookup('registry.yarnpkg.com', (registryErr) => { + if (!registryErr) { + return resolve(true) + } + + const proxy = getProxy() + if (!proxy) { + return resolve(false) + } + + const { hostname } = url.parse(proxy) + if (!hostname) { + return resolve(false) + } + + dns.lookup(hostname, (proxyErr) => { + resolve(proxyErr == null) + }) + }) + }) +} diff --git a/packages/create-llama/helpers/is-writeable.ts b/packages/create-llama/helpers/is-writeable.ts new file mode 100644 index 0000000000..0b9e9abb4f --- /dev/null +++ b/packages/create-llama/helpers/is-writeable.ts @@ -0,0 +1,10 @@ +import fs from 'fs' + +export async function isWriteable(directory: string): Promise { + try { + await fs.promises.access(directory, (fs.constants || fs).W_OK) + return true + } catch (err) { + return false + } +} diff --git a/packages/create-llama/helpers/make-dir.ts b/packages/create-llama/helpers/make-dir.ts new file mode 100644 index 0000000000..5b12b996f0 --- /dev/null +++ b/packages/create-llama/helpers/make-dir.ts @@ -0,0 +1,8 @@ +import fs from 'fs' + +export function makeDir( + root: string, + options = { recursive: true } +): Promise { + return fs.promises.mkdir(root, options) +} diff --git a/packages/create-llama/helpers/validate-pkg.ts b/packages/create-llama/helpers/validate-pkg.ts new file mode 100644 index 0000000000..cff613818f --- /dev/null +++ b/packages/create-llama/helpers/validate-pkg.ts @@ -0,0 +1,20 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import validateProjectName from 'validate-npm-package-name' + +export function validateNpmName(name: string): { + valid: boolean + problems?: string[] +} { + const nameValidation = validateProjectName(name) + if (nameValidation.validForNewPackages) { + return { valid: true } + } + + return { + valid: false, + problems: [ + ...(nameValidation.errors || []), + ...(nameValidation.warnings || []), + ], + } +} diff --git a/packages/create-llama/index.ts b/packages/create-llama/index.ts new file mode 100644 index 0000000000..1d488260ae --- /dev/null +++ b/packages/create-llama/index.ts @@ -0,0 +1,511 @@ +#!/usr/bin/env node +/* eslint-disable import/no-extraneous-dependencies */ +import { cyan, green, red, yellow, bold, blue } from 'picocolors' +import Commander from 'commander' +import Conf from 'conf' +import path from 'path' +import prompts from 'prompts' +import checkForUpdate from 'update-check' +import { createApp, DownloadError } from './create-app' +import { getPkgManager } from './helpers/get-pkg-manager' +import { validateNpmName } from './helpers/validate-pkg' +import packageJson from './package.json' +import ciInfo from 'ci-info' +import { isFolderEmpty } from './helpers/is-folder-empty' +import fs from 'fs' + +let projectPath: string = '' + +const handleSigTerm = () => process.exit(0) + +process.on('SIGINT', handleSigTerm) +process.on('SIGTERM', handleSigTerm) + +const onPromptState = (state: any) => { + if (state.aborted) { + // If we don't re-enable the terminal cursor before exiting + // the program, the cursor will remain hidden + process.stdout.write('\x1B[?25h') + process.stdout.write('\n') + process.exit(1) + } +} + +const program = new Commander.Command(packageJson.name) + .version(packageJson.version) + .arguments('') + .usage(`${green('')} [options]`) + .action((name) => { + projectPath = name + }) + .option( + '--ts, --typescript', + ` + + Initialize as a TypeScript project. (default) +` + ) + .option( + '--js, --javascript', + ` + + Initialize as a JavaScript project. +` + ) + .option( + '--tailwind', + ` + + Initialize with Tailwind CSS config. (default) +` + ) + .option( + '--eslint', + ` + + Initialize with eslint config. +` + ) + .option( + '--app', + ` + + Initialize as an App Router project. +` + ) + .option( + '--src-dir', + ` + + Initialize inside a \`src/\` directory. +` + ) + .option( + '--import-alias ', + ` + + Specify import alias to use (default "@/*"). +` + ) + .option( + '--use-npm', + ` + + Explicitly tell the CLI to bootstrap the application using npm +` + ) + .option( + '--use-pnpm', + ` + + Explicitly tell the CLI to bootstrap the application using pnpm +` + ) + .option( + '--use-yarn', + ` + + Explicitly tell the CLI to bootstrap the application using Yarn +` + ) + .option( + '--use-bun', + ` + + Explicitly tell the CLI to bootstrap the application using Bun +` + ) + .option( + '-e, --example [name]|[github-url]', + ` + + An example to bootstrap the app with. You can use an example name + from the official Next.js repo or a GitHub URL. The URL can use + any branch and/or subdirectory +` + ) + .option( + '--example-path ', + ` + + In a rare case, your GitHub URL might contain a branch name with + a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). + In this case, you must specify the path to the example separately: + --example-path foo/bar +` + ) + .option( + '--reset-preferences', + ` + + Explicitly tell the CLI to reset any stored preferences +` + ) + .allowUnknownOption() + .parse(process.argv) + +const packageManager = !!program.useNpm + ? 'npm' + : !!program.usePnpm + ? 'pnpm' + : !!program.useYarn + ? 'yarn' + : !!program.useBun + ? 'bun' + : getPkgManager() + +async function run(): Promise { + const conf = new Conf({ projectName: 'create-next-app' }) + + if (program.resetPreferences) { + conf.clear() + console.log(`Preferences reset successfully`) + return + } + + if (typeof projectPath === 'string') { + projectPath = projectPath.trim() + } + + if (!projectPath) { + const res = await prompts({ + onState: onPromptState, + type: 'text', + name: 'path', + message: 'What is your project named?', + initial: 'my-app', + validate: (name) => { + const validation = validateNpmName(path.basename(path.resolve(name))) + if (validation.valid) { + return true + } + return 'Invalid project name: ' + validation.problems![0] + }, + }) + + if (typeof res.path === 'string') { + projectPath = res.path.trim() + } + } + + if (!projectPath) { + console.log( + '\nPlease specify the project directory:\n' + + ` ${cyan(program.name())} ${green('')}\n` + + 'For example:\n' + + ` ${cyan(program.name())} ${green('my-next-app')}\n\n` + + `Run ${cyan(`${program.name()} --help`)} to see all options.` + ) + process.exit(1) + } + + const resolvedProjectPath = path.resolve(projectPath) + const projectName = path.basename(resolvedProjectPath) + + const { valid, problems } = validateNpmName(projectName) + if (!valid) { + console.error( + `Could not create a project called ${red( + `"${projectName}"` + )} because of npm naming restrictions:` + ) + + problems!.forEach((p) => console.error(` ${red(bold('*'))} ${p}`)) + process.exit(1) + } + + if (program.example === true) { + console.error( + 'Please provide an example name or url, otherwise remove the example option.' + ) + process.exit(1) + } + + /** + * Verify the project dir is empty or doesn't exist + */ + const root = path.resolve(resolvedProjectPath) + const appName = path.basename(root) + const folderExists = fs.existsSync(root) + + if (folderExists && !isFolderEmpty(root, appName)) { + process.exit(1) + } + + const example = typeof program.example === 'string' && program.example.trim() + const preferences = (conf.get('preferences') || {}) as Record< + string, + boolean | string + > + /** + * If the user does not provide the necessary flags, prompt them for whether + * to use TS or JS. + */ + if (!example) { + const defaults: typeof preferences = { + typescript: true, + eslint: true, + tailwind: true, + app: true, + srcDir: false, + importAlias: '@/*', + customizeImportAlias: false, + } + const getPrefOrDefault = (field: string) => + preferences[field] ?? defaults[field] + + if (!program.typescript && !program.javascript) { + if (ciInfo.isCI) { + // default to TypeScript in CI as we can't prompt to + // prevent breaking setup flows + program.typescript = getPrefOrDefault('typescript') + } else { + const styledTypeScript = blue('TypeScript') + const { typescript } = await prompts( + { + type: 'toggle', + name: 'typescript', + message: `Would you like to use ${styledTypeScript}?`, + initial: getPrefOrDefault('typescript'), + active: 'Yes', + inactive: 'No', + }, + { + /** + * User inputs Ctrl+C or Ctrl+D to exit the prompt. We should close the + * process and not write to the file system. + */ + onCancel: () => { + console.error('Exiting.') + process.exit(1) + }, + } + ) + /** + * Depending on the prompt response, set the appropriate program flags. + */ + program.typescript = Boolean(typescript) + program.javascript = !Boolean(typescript) + preferences.typescript = Boolean(typescript) + } + } + + if ( + !process.argv.includes('--eslint') && + !process.argv.includes('--no-eslint') + ) { + if (ciInfo.isCI) { + program.eslint = getPrefOrDefault('eslint') + } else { + const styledEslint = blue('ESLint') + const { eslint } = await prompts({ + onState: onPromptState, + type: 'toggle', + name: 'eslint', + message: `Would you like to use ${styledEslint}?`, + initial: getPrefOrDefault('eslint'), + active: 'Yes', + inactive: 'No', + }) + program.eslint = Boolean(eslint) + preferences.eslint = Boolean(eslint) + } + } + + if ( + !process.argv.includes('--tailwind') && + !process.argv.includes('--no-tailwind') + ) { + if (ciInfo.isCI) { + program.tailwind = getPrefOrDefault('tailwind') + } else { + const tw = blue('Tailwind CSS') + const { tailwind } = await prompts({ + onState: onPromptState, + type: 'toggle', + name: 'tailwind', + message: `Would you like to use ${tw}?`, + initial: getPrefOrDefault('tailwind'), + active: 'Yes', + inactive: 'No', + }) + program.tailwind = Boolean(tailwind) + preferences.tailwind = Boolean(tailwind) + } + } + + if ( + !process.argv.includes('--src-dir') && + !process.argv.includes('--no-src-dir') + ) { + if (ciInfo.isCI) { + program.srcDir = getPrefOrDefault('srcDir') + } else { + const styledSrcDir = blue('`src/` directory') + const { srcDir } = await prompts({ + onState: onPromptState, + type: 'toggle', + name: 'srcDir', + message: `Would you like to use ${styledSrcDir}?`, + initial: getPrefOrDefault('srcDir'), + active: 'Yes', + inactive: 'No', + }) + program.srcDir = Boolean(srcDir) + preferences.srcDir = Boolean(srcDir) + } + } + + if (!process.argv.includes('--app') && !process.argv.includes('--no-app')) { + if (ciInfo.isCI) { + program.app = getPrefOrDefault('app') + } else { + const styledAppDir = blue('App Router') + const { appRouter } = await prompts({ + onState: onPromptState, + type: 'toggle', + name: 'appRouter', + message: `Would you like to use ${styledAppDir}? (recommended)`, + initial: getPrefOrDefault('app'), + active: 'Yes', + inactive: 'No', + }) + program.app = Boolean(appRouter) + } + } + + if ( + typeof program.importAlias !== 'string' || + !program.importAlias.length + ) { + if (ciInfo.isCI) { + // We don't use preferences here because the default value is @/* regardless of existing preferences + program.importAlias = defaults.importAlias + } else { + const styledImportAlias = blue('import alias') + + const { customizeImportAlias } = await prompts({ + onState: onPromptState, + type: 'toggle', + name: 'customizeImportAlias', + message: `Would you like to customize the default ${styledImportAlias} (${defaults.importAlias})?`, + initial: getPrefOrDefault('customizeImportAlias'), + active: 'Yes', + inactive: 'No', + }) + + if (!customizeImportAlias) { + // We don't use preferences here because the default value is @/* regardless of existing preferences + program.importAlias = defaults.importAlias + } else { + const { importAlias } = await prompts({ + onState: onPromptState, + type: 'text', + name: 'importAlias', + message: `What ${styledImportAlias} would you like configured?`, + initial: getPrefOrDefault('importAlias'), + validate: (value) => + /.+\/\*/.test(value) + ? true + : 'Import alias must follow the pattern /*', + }) + program.importAlias = importAlias + preferences.importAlias = importAlias + } + } + } + } + + try { + await createApp({ + appPath: resolvedProjectPath, + packageManager, + example: example && example !== 'default' ? example : undefined, + examplePath: program.examplePath, + typescript: program.typescript, + tailwind: program.tailwind, + eslint: program.eslint, + appRouter: program.app, + srcDir: program.srcDir, + importAlias: program.importAlias, + }) + } catch (reason) { + if (!(reason instanceof DownloadError)) { + throw reason + } + + const res = await prompts({ + onState: onPromptState, + type: 'confirm', + name: 'builtin', + message: + `Could not download "${example}" because of a connectivity issue between your machine and GitHub.\n` + + `Do you want to use the default template instead?`, + initial: true, + }) + if (!res.builtin) { + throw reason + } + + await createApp({ + appPath: resolvedProjectPath, + packageManager, + typescript: program.typescript, + eslint: program.eslint, + tailwind: program.tailwind, + appRouter: program.app, + srcDir: program.srcDir, + importAlias: program.importAlias, + }) + } + conf.set('preferences', preferences) +} + +const update = checkForUpdate(packageJson).catch(() => null) + +async function notifyUpdate(): Promise { + try { + const res = await update + if (res?.latest) { + const updateMessage = + packageManager === 'yarn' + ? 'yarn global add create-next-app' + : packageManager === 'pnpm' + ? 'pnpm add -g create-next-app' + : packageManager === 'bun' + ? 'bun add -g create-next-app' + : 'npm i -g create-next-app' + + console.log( + yellow(bold('A new version of `create-next-app` is available!')) + + '\n' + + 'You can update by running: ' + + cyan(updateMessage) + + '\n' + ) + } + process.exit() + } catch { + // ignore error + } +} + +run() + .then(notifyUpdate) + .catch(async (reason) => { + console.log() + console.log('Aborting installation.') + if (reason.command) { + console.log(` ${cyan(reason.command)} has failed.`) + } else { + console.log( + red('Unexpected error. Please report it as a bug:') + '\n', + reason + ) + } + console.log() + + await notifyUpdate() + + process.exit(1) + }) diff --git a/packages/create-llama/package.json b/packages/create-llama/package.json new file mode 100644 index 0000000000..be420774bd --- /dev/null +++ b/packages/create-llama/package.json @@ -0,0 +1,57 @@ +{ + "name": "create-next-app", + "version": "13.5.6", + "keywords": [ + "react", + "next", + "next.js" + ], + "description": "Create Next.js-powered React apps with one command", + "repository": { + "type": "git", + "url": "https://github.com/vercel/next.js", + "directory": "packages/create-next-app" + }, + "author": "Next.js Team ", + "license": "MIT", + "bin": { + "create-next-app": "./dist/index.js" + }, + "files": [ + "dist" + ], + "scripts": { + "dev": "ncc build ./index.ts -w -o dist/", + "prerelease": "node ../../scripts/rm.mjs dist", + "release": "ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register", + "prepublishOnly": "cd ../../ && turbo run build", + "build": "pnpm release", + "lint-fix": "pnpm prettier -w --plugin prettier-plugin-tailwindcss 'templates/*-tw/{ts,js}/{app,pages}/**/*.{js,ts,tsx}'" + }, + "devDependencies": { + "@types/async-retry": "1.4.2", + "@types/ci-info": "2.0.0", + "@types/cross-spawn": "6.0.0", + "@types/node": "^20.2.5", + "@types/prompts": "2.0.1", + "@types/tar": "6.1.5", + "@types/validate-npm-package-name": "3.0.0", + "@vercel/ncc": "0.34.0", + "async-retry": "1.3.1", + "ci-info": "watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540", + "commander": "2.20.0", + "conf": "10.2.0", + "cross-spawn": "7.0.3", + "fast-glob": "3.3.1", + "got": "10.7.0", + "picocolors": "1.0.0", + "prettier-plugin-tailwindcss": "0.3.0", + "prompts": "2.1.0", + "tar": "6.1.15", + "update-check": "1.5.4", + "validate-npm-package-name": "3.0.0" + }, + "engines": { + "node": ">=16.14.0" + } +} \ No newline at end of file diff --git a/packages/create-llama/pnpm-lock.yaml b/packages/create-llama/pnpm-lock.yaml new file mode 100644 index 0000000000..4670902d27 --- /dev/null +++ b/packages/create-llama/pnpm-lock.yaml @@ -0,0 +1,882 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +devDependencies: + '@types/async-retry': + specifier: 1.4.2 + version: 1.4.2 + '@types/ci-info': + specifier: 2.0.0 + version: 2.0.0 + '@types/cross-spawn': + specifier: 6.0.0 + version: 6.0.0 + '@types/node': + specifier: ^20.2.5 + version: 20.2.5 + '@types/prompts': + specifier: 2.0.1 + version: 2.0.1 + '@types/tar': + specifier: 6.1.5 + version: 6.1.5 + '@types/validate-npm-package-name': + specifier: 3.0.0 + version: 3.0.0 + '@vercel/ncc': + specifier: 0.34.0 + version: 0.34.0 + async-retry: + specifier: 1.3.1 + version: 1.3.1 + ci-info: + specifier: watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540 + version: github.com/watson/ci-info/f43f6a1cefff47fb361c88cf4b943fdbcaafe540 + commander: + specifier: 2.20.0 + version: 2.20.0 + conf: + specifier: 10.2.0 + version: 10.2.0 + cross-spawn: + specifier: 7.0.3 + version: 7.0.3 + fast-glob: + specifier: 3.3.1 + version: 3.3.1 + got: + specifier: 10.7.0 + version: 10.7.0 + picocolors: + specifier: 1.0.0 + version: 1.0.0 + prettier-plugin-tailwindcss: + specifier: 0.3.0 + version: 0.3.0(prettier@3.0.3) + prompts: + specifier: 2.1.0 + version: 2.1.0 + tar: + specifier: 6.1.15 + version: 6.1.15 + typescript: + specifier: ^5.2.2 + version: 5.2.2 + update-check: + specifier: 1.5.4 + version: 1.5.4 + validate-npm-package-name: + specifier: 3.0.0 + version: 3.0.0 + +packages: + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@sindresorhus/is@2.1.1: + resolution: {integrity: sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==} + engines: {node: '>=10'} + dev: true + + /@szmarczak/http-timer@4.0.6: + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + dependencies: + defer-to-connect: 2.0.1 + dev: true + + /@types/async-retry@1.4.2: + resolution: {integrity: sha512-GUDuJURF0YiJZ+CBjNQA0+vbP/VHlJbB0sFqkzsV7EcOPRfurVonXpXKAt3w8qIjM1TEzpz6hc6POocPvHOS3w==} + dependencies: + '@types/retry': 0.12.4 + dev: true + + /@types/cacheable-request@6.0.3: + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + dependencies: + '@types/http-cache-semantics': 4.0.3 + '@types/keyv': 3.1.4 + '@types/node': 20.2.5 + '@types/responselike': 1.0.2 + dev: true + + /@types/ci-info@2.0.0: + resolution: {integrity: sha512-5R2/MHILQLDCzTuhs1j4Qqq8AaKUf7Ma4KSSkCtc12+fMs47zfa34qhto9goxpyX00tQK1zxB885VCiawZ5Qhg==} + dev: true + + /@types/cross-spawn@6.0.0: + resolution: {integrity: sha512-evp2ZGsFw9YKprDbg8ySgC9NA15g3YgiI8ANkGmKKvvi0P2aDGYLPxQIC5qfeKNUOe3TjABVGuah6omPRpIYhg==} + dependencies: + '@types/node': 20.2.5 + dev: true + + /@types/http-cache-semantics@4.0.3: + resolution: {integrity: sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==} + dev: true + + /@types/keyv@3.1.4: + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + dependencies: + '@types/node': 20.2.5 + dev: true + + /@types/node@20.2.5: + resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==} + dev: true + + /@types/prompts@2.0.1: + resolution: {integrity: sha512-AhtMcmETelF8wFDV1ucbChKhLgsc+ytXZXkNz/nnTAMSDeqsjALknEFxi7ZtLgS/G8bV2rp90LhDW5SGACimIQ==} + dev: true + + /@types/responselike@1.0.2: + resolution: {integrity: sha512-/4YQT5Kp6HxUDb4yhRkm0bJ7TbjvTddqX7PZ5hz6qV3pxSo72f/6YPRo+Mu2DU307tm9IioO69l7uAwn5XNcFA==} + dependencies: + '@types/node': 20.2.5 + dev: true + + /@types/retry@0.12.4: + resolution: {integrity: sha512-l1YzFLj8Y6OhLdt7HKXlz56DoEmksB7qR8KVk+MpFsS4duwnoszLgDlLxJB0vgSqtg/rAS5gmYg5Bjw2sMJ8Ew==} + dev: true + + /@types/tar@6.1.5: + resolution: {integrity: sha512-qm2I/RlZij5RofuY7vohTpYNaYcrSQlN2MyjucQc7ZweDwaEWkdN/EeNh6e9zjK6uEm6PwjdMXkcj05BxZdX1Q==} + dependencies: + '@types/node': 20.2.5 + minipass: 4.2.8 + dev: true + + /@types/validate-npm-package-name@3.0.0: + resolution: {integrity: sha512-iFNNIrEaJH1lbPiyX+O/QyxSbKxrTjdNBVZGckt+iEL9So0hdZNBL68sOfHnt2txuUD8UJXvmKv/1DkgkebgUg==} + dev: true + + /@vercel/ncc@0.34.0: + resolution: {integrity: sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==} + hasBin: true + dev: true + + /ajv-formats@2.1.1(ajv@8.12.0): + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.12.0 + dev: true + + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /async-retry@1.3.1: + resolution: {integrity: sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==} + dependencies: + retry: 0.12.0 + dev: true + + /atomically@1.7.0: + resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==} + engines: {node: '>=10.12.0'} + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /builtins@1.0.3: + resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} + dev: true + + /cacheable-lookup@2.0.1: + resolution: {integrity: sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==} + engines: {node: '>=10'} + dependencies: + '@types/keyv': 3.1.4 + keyv: 4.5.4 + dev: true + + /cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + dev: true + + /chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + dev: true + + /clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + dependencies: + mimic-response: 1.0.1 + dev: true + + /commander@2.20.0: + resolution: {integrity: sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==} + dev: true + + /conf@10.2.0: + resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==} + engines: {node: '>=12'} + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + atomically: 1.7.0 + debounce-fn: 4.0.0 + dot-prop: 6.0.1 + env-paths: 2.2.1 + json-schema-typed: 7.0.3 + onetime: 5.1.2 + pkg-up: 3.1.0 + semver: 7.5.4 + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debounce-fn@4.0.0: + resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==} + engines: {node: '>=10'} + dependencies: + mimic-fn: 3.1.0 + dev: true + + /decompress-response@5.0.0: + resolution: {integrity: sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==} + engines: {node: '>=10'} + dependencies: + mimic-response: 2.1.0 + dev: true + + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: true + + /defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + dev: true + + /dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + dependencies: + is-obj: 2.0.0 + dev: true + + /duplexer3@0.1.5: + resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} + dev: true + + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: true + + /env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: true + + /fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + dev: true + + /get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /got@10.7.0: + resolution: {integrity: sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==} + engines: {node: '>=10'} + dependencies: + '@sindresorhus/is': 2.1.1 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/keyv': 3.1.4 + '@types/responselike': 1.0.2 + cacheable-lookup: 2.0.1 + cacheable-request: 7.0.4 + decompress-response: 5.0.0 + duplexer3: 0.1.5 + get-stream: 5.2.0 + lowercase-keys: 2.0.0 + mimic-response: 2.1.0 + p-cancelable: 2.1.1 + p-event: 4.2.0 + responselike: 2.0.1 + to-readable-stream: 2.1.0 + type-fest: 0.10.0 + dev: true + + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: true + + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /json-schema-typed@7.0.3: + resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==} + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: true + + /locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: true + + /lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn@3.1.0: + resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} + engines: {node: '>=8'} + dev: true + + /mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + dev: true + + /mimic-response@2.1.0: + resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} + engines: {node: '>=8'} + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + dependencies: + yallist: 4.0.0 + dev: true + + /minipass@4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} + dev: true + + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + dev: true + + /minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + dev: true + + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true + + /normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + dev: true + + /p-event@4.2.0: + resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} + engines: {node: '>=8'} + dependencies: + p-timeout: 3.2.0 + dev: true + + /p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + dependencies: + p-finally: 1.0.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pkg-up@3.1.0: + resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} + engines: {node: '>=8'} + dependencies: + find-up: 3.0.0 + dev: true + + /prettier-plugin-tailwindcss@0.3.0(prettier@3.0.3): + resolution: {integrity: sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==} + engines: {node: '>=12.17.0'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@shufo/prettier-plugin-blade': '*' + '@trivago/prettier-plugin-sort-imports': '*' + prettier: '>=2.2.0' + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + prettier-plugin-twig-melody: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@shufo/prettier-plugin-blade': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + prettier-plugin-twig-melody: + optional: true + dependencies: + prettier: 3.0.3 + dev: true + + /prettier@3.0.3: + resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /prompts@2.1.0: + resolution: {integrity: sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: true + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: true + + /registry-auth-token@3.3.2: + resolution: {integrity: sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==} + dependencies: + rc: 1.2.8 + safe-buffer: 5.2.1 + dev: true + + /registry-url@3.1.0: + resolution: {integrity: sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==} + engines: {node: '>=0.10.0'} + dependencies: + rc: 1.2.8 + dev: true + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + dependencies: + lowercase-keys: 2.0.0 + dev: true + + /retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true + + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: true + + /tar@6.1.15: + resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==} + engines: {node: '>=10'} + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + dev: true + + /to-readable-stream@2.1.0: + resolution: {integrity: sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==} + engines: {node: '>=8'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /type-fest@0.10.0: + resolution: {integrity: sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==} + engines: {node: '>=8'} + dev: true + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /update-check@1.5.4: + resolution: {integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==} + dependencies: + registry-auth-token: 3.3.2 + registry-url: 3.1.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /validate-npm-package-name@3.0.0: + resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} + dependencies: + builtins: 1.0.3 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + github.com/watson/ci-info/f43f6a1cefff47fb361c88cf4b943fdbcaafe540: + resolution: {tarball: https://codeload.github.com/watson/ci-info/tar.gz/f43f6a1cefff47fb361c88cf4b943fdbcaafe540} + name: ci-info + version: 2.0.0 + dev: true diff --git a/packages/create-llama/tsconfig.json b/packages/create-llama/tsconfig.json new file mode 100644 index 0000000000..e4edad9e12 --- /dev/null +++ b/packages/create-llama/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2019", + "moduleResolution": "node", + "strict": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "skipLibCheck": false + }, + "exclude": ["templates", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b0bcbb70c..b7f39f8526 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,7 +18,7 @@ importers: devDependencies: '@turbo/gen': specifier: ^1.10.16 - version: 1.10.16(@types/node@20.9.0)(typescript@5.2.2) + version: 1.10.16(@types/node@18.18.8)(typescript@5.2.2) '@types/jest': specifier: ^29.5.6 version: 29.5.6 @@ -33,7 +33,7 @@ importers: version: 8.0.3 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.9.0) + version: 29.7.0(@types/node@18.18.8) prettier: specifier: ^3.0.3 version: 3.0.3 @@ -203,6 +203,72 @@ importers: specifier: ^5.2.2 version: 5.2.2 + packages/create-llama: + devDependencies: + '@types/async-retry': + specifier: 1.4.2 + version: 1.4.2 + '@types/ci-info': + specifier: 2.0.0 + version: 2.0.0 + '@types/cross-spawn': + specifier: 6.0.0 + version: 6.0.0 + '@types/node': + specifier: ^20.2.5 + version: 20.8.10 + '@types/prompts': + specifier: 2.0.1 + version: 2.0.1 + '@types/tar': + specifier: 6.1.5 + version: 6.1.5 + '@types/validate-npm-package-name': + specifier: 3.0.0 + version: 3.0.0 + '@vercel/ncc': + specifier: 0.34.0 + version: 0.34.0 + async-retry: + specifier: 1.3.1 + version: 1.3.1 + ci-info: + specifier: watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540 + version: github.com/watson/ci-info/f43f6a1cefff47fb361c88cf4b943fdbcaafe540 + commander: + specifier: 2.20.0 + version: 2.20.0 + conf: + specifier: 10.2.0 + version: 10.2.0 + cross-spawn: + specifier: 7.0.3 + version: 7.0.3 + fast-glob: + specifier: 3.3.1 + version: 3.3.1 + got: + specifier: 10.7.0 + version: 10.7.0 + picocolors: + specifier: 1.0.0 + version: 1.0.0 + prettier-plugin-tailwindcss: + specifier: 0.3.0 + version: 0.3.0(prettier-plugin-organize-imports@3.2.3)(prettier@3.0.3) + prompts: + specifier: 2.1.0 + version: 2.1.0 + tar: + specifier: 6.1.15 + version: 6.1.15 + update-check: + specifier: 1.5.4 + version: 1.5.4 + validate-npm-package-name: + specifier: 3.0.0 + version: 3.0.0 + packages/eslint-config-custom: dependencies: eslint-config-next: @@ -3127,7 +3193,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.8.10 + '@types/node': 18.18.8 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -3148,14 +3214,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.10 + '@types/node': 18.18.8 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.8.10) + jest-config: 29.7.0(@types/node@18.18.8) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -3183,7 +3249,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.10 + '@types/node': 18.18.8 jest-mock: 29.7.0 dev: true @@ -3210,7 +3276,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.8.10 + '@types/node': 18.18.8 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -3243,7 +3309,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.19 - '@types/node': 20.8.10 + '@types/node': 18.18.8 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -3330,7 +3396,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.2 - '@types/node': 20.8.10 + '@types/node': 18.18.8 '@types/yargs': 17.0.28 chalk: 4.1.2 @@ -3621,6 +3687,11 @@ packages: engines: {node: '>=6'} dev: false + /@sindresorhus/is@2.1.1: + resolution: {integrity: sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==} + engines: {node: '>=10'} + dev: true + /@sinonjs/commons@3.0.0: resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} dependencies: @@ -3808,6 +3879,13 @@ packages: defer-to-connect: 1.1.3 dev: false + /@szmarczak/http-timer@4.0.6: + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + dependencies: + defer-to-connect: 2.0.1 + dev: true + /@tootallnate/quickjs-emscripten@0.23.0: resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} dev: true @@ -3837,7 +3915,7 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@turbo/gen@1.10.16(@types/node@20.9.0)(typescript@5.2.2): + /@turbo/gen@1.10.16(@types/node@18.18.8)(typescript@5.2.2): resolution: {integrity: sha512-PzyluADjVuy5OcIi+/aRcD70OElQpRVRDdfZ9fH8G5Fv75lQcNrjd1bBGKmhjSw+g+eTEkXMGnY7s6gsCYjYTQ==} hasBin: true dependencies: @@ -3849,7 +3927,7 @@ packages: minimatch: 9.0.3 node-plop: 0.26.3 proxy-agent: 6.3.1 - ts-node: 10.9.1(@types/node@20.9.0)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@18.18.8)(typescript@5.2.2) update-check: 1.5.4 validate-npm-package-name: 5.0.0 transitivePeerDependencies: @@ -3878,6 +3956,12 @@ packages: update-check: 1.5.4 dev: true + /@types/async-retry@1.4.2: + resolution: {integrity: sha512-GUDuJURF0YiJZ+CBjNQA0+vbP/VHlJbB0sFqkzsV7EcOPRfurVonXpXKAt3w8qIjM1TEzpz6hc6POocPvHOS3w==} + dependencies: + '@types/retry': 0.12.0 + dev: true + /@types/babel__core@7.20.2: resolution: {integrity: sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==} dependencies: @@ -3911,28 +3995,47 @@ packages: resolution: {integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==} dependencies: '@types/connect': 3.4.36 - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: false /@types/bonjour@3.5.11: resolution: {integrity: sha512-isGhjmBtLIxdHBDl2xGwUzEM8AOyOvWsADWq7rqirdi/ZQoHnLWErHvsThcEzTX8juDRiZtzp2Qkv5bgNh6mAg==} dependencies: - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: false + /@types/cacheable-request@6.0.3: + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + dependencies: + '@types/http-cache-semantics': 4.0.3 + '@types/keyv': 3.1.4 + '@types/node': 18.18.8 + '@types/responselike': 1.0.1 + dev: true + + /@types/ci-info@2.0.0: + resolution: {integrity: sha512-5R2/MHILQLDCzTuhs1j4Qqq8AaKUf7Ma4KSSkCtc12+fMs47zfa34qhto9goxpyX00tQK1zxB885VCiawZ5Qhg==} + dev: true + /@types/connect-history-api-fallback@1.5.1: resolution: {integrity: sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==} dependencies: '@types/express-serve-static-core': 4.17.37 - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: false /@types/connect@3.4.36: resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} dependencies: - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: false + /@types/cross-spawn@6.0.0: + resolution: {integrity: sha512-evp2ZGsFw9YKprDbg8ySgC9NA15g3YgiI8ANkGmKKvvi0P2aDGYLPxQIC5qfeKNUOe3TjABVGuah6omPRpIYhg==} + dependencies: + '@types/node': 18.18.8 + dev: true + /@types/eslint-scope@3.7.5: resolution: {integrity: sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==} dependencies: @@ -3969,7 +4072,7 @@ packages: /@types/express-serve-static-core@4.17.37: resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==} dependencies: - '@types/node': 20.9.0 + '@types/node': 18.18.8 '@types/qs': 6.9.8 '@types/range-parser': 1.2.5 '@types/send': 0.17.2 @@ -3988,13 +4091,13 @@ packages: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: true /@types/graceful-fs@4.1.7: resolution: {integrity: sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==} dependencies: - '@types/node': 20.8.10 + '@types/node': 18.18.8 dev: true /@types/hast@2.3.6: @@ -4010,6 +4113,10 @@ packages: resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} dev: false + /@types/http-cache-semantics@4.0.3: + resolution: {integrity: sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==} + dev: true + /@types/http-errors@2.0.2: resolution: {integrity: sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==} dev: false @@ -4017,7 +4124,7 @@ packages: /@types/http-proxy@1.17.12: resolution: {integrity: sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw==} dependencies: - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: false /@types/inquirer@6.5.0: @@ -4067,8 +4174,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 20.9.0 - dev: false + '@types/node': 18.18.8 /@types/lodash-es@4.17.10: resolution: {integrity: sha512-YJP+w/2khSBwbUSFdGsSqmDvmnN3cCKoPOL7Zjle6s30ZtemkkqhjVfFqGwPN7ASil5VyjE2GtyU/yqYY6mC0A==} @@ -4104,7 +4210,7 @@ packages: /@types/node-fetch@2.6.6: resolution: {integrity: sha512-95X8guJYhfqiuVVhRFxVQcf4hW/2bCuoPwDasMf/531STFoNoWTT7YDnWdXHEZKqAGUigmpG31r2FE70LwnzJw==} dependencies: - '@types/node': 18.18.7 + '@types/node': 18.18.8 form-data: 4.0.0 dev: false @@ -4144,11 +4250,7 @@ packages: resolution: {integrity: sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==} dependencies: undici-types: 5.26.5 - - /@types/node@20.9.0: - resolution: {integrity: sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==} - dependencies: - undici-types: 5.26.5 + dev: true /@types/normalize-package-data@2.4.2: resolution: {integrity: sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==} @@ -4172,6 +4274,10 @@ packages: resolution: {integrity: sha512-+DDIKtFsGMajapzc5A+jL9V1dpLZ5lShAd6Oq0yRu2qFHFr2hhHlZ2rkFiInXOoFSxjxGmyGdCjjHghoHj/x0w==} dev: true + /@types/prompts@2.0.1: + resolution: {integrity: sha512-AhtMcmETelF8wFDV1ucbChKhLgsc+ytXZXkNz/nnTAMSDeqsjALknEFxi7ZtLgS/G8bV2rp90LhDW5SGACimIQ==} + dev: true + /@types/prop-types@15.7.8: resolution: {integrity: sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==} @@ -4213,17 +4319,15 @@ packages: /@types/responselike@1.0.1: resolution: {integrity: sha512-TiGnitEDxj2X0j+98Eqk5lv/Cij8oHd32bU4D/Yw6AOq7vvTk0gSD2GPj0G/HkvhMoVsdlhYF4yqqlyPBTM6Sg==} dependencies: - '@types/node': 20.9.0 - dev: false + '@types/node': 18.18.8 /@types/retry@0.12.0: resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - dev: false /@types/sax@1.2.5: resolution: {integrity: sha512-9jWta97bBVC027/MShr3gLab8gPhKy4l6qpb+UJLF5pDm3501NvA7uvqVCW+REFtx00oTi6Cq9JzLwgq6evVgw==} dependencies: - '@types/node': 17.0.45 + '@types/node': 18.18.8 dev: false /@types/scheduler@0.16.4: @@ -4237,7 +4341,7 @@ packages: resolution: {integrity: sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==} dependencies: '@types/mime': 1.3.3 - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: false /@types/serve-index@1.9.2: @@ -4251,23 +4355,30 @@ packages: dependencies: '@types/http-errors': 2.0.2 '@types/mime': 3.0.2 - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: false /@types/sockjs@0.3.34: resolution: {integrity: sha512-R+n7qBFnm/6jinlteC9DBL5dGiDGjWAvjo4viUanpnc/dG1y7uDoacXPIQ/PQEg1fI912SMHIa014ZjRpvDw4g==} dependencies: - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: false /@types/stack-utils@2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} dev: true + /@types/tar@6.1.5: + resolution: {integrity: sha512-qm2I/RlZij5RofuY7vohTpYNaYcrSQlN2MyjucQc7ZweDwaEWkdN/EeNh6e9zjK6uEm6PwjdMXkcj05BxZdX1Q==} + dependencies: + '@types/node': 18.18.8 + minipass: 4.2.8 + dev: true + /@types/through@0.0.31: resolution: {integrity: sha512-LpKpmb7FGevYgXnBXYs6HWnmiFyVG07Pt1cnbgM1IhEacITTiUaBXXvOR3Y50ksaJWGSfhbEvQFivQEFGCC55w==} dependencies: - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: true /@types/tinycolor2@1.4.4: @@ -4282,6 +4393,10 @@ packages: resolution: {integrity: sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==} dev: true + /@types/validate-npm-package-name@3.0.0: + resolution: {integrity: sha512-iFNNIrEaJH1lbPiyX+O/QyxSbKxrTjdNBVZGckt+iEL9So0hdZNBL68sOfHnt2txuUD8UJXvmKv/1DkgkebgUg==} + dev: true + /@types/webidl-conversions@7.0.2: resolution: {integrity: sha512-uNv6b/uGRLlCVmelat2rA8bcVd3k/42mV2EmjhPh6JLkd35T5bgwR/t6xy7a9MWhd9sixIeBUzhBenvk3NO+DQ==} dev: false @@ -4296,7 +4411,7 @@ packages: /@types/ws@8.5.6: resolution: {integrity: sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg==} dependencies: - '@types/node': 20.9.0 + '@types/node': 18.18.8 dev: false /@types/yargs-parser@21.0.1: @@ -4372,6 +4487,11 @@ packages: /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + /@vercel/ncc@0.34.0: + resolution: {integrity: sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==} + hasBin: true + dev: true + /@webassemblyjs/ast@1.11.6: resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==} dependencies: @@ -4563,7 +4683,6 @@ packages: optional: true dependencies: ajv: 8.12.0 - dev: false /ajv-keywords@3.5.2(ajv@6.12.6): resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} @@ -4596,7 +4715,6 @@ packages: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - dev: false /algoliasearch-helper@3.14.2(algoliasearch@4.20.0): resolution: {integrity: sha512-FjDSrjvQvJT/SKMW74nPgFpsoPUwZCzGbCqbp8HhBFfSk/OvNFxzCaCmuO0p7AWeLy1gD+muFwQEkBwcl5H4pg==} @@ -4839,6 +4957,12 @@ packages: tslib: 2.6.2 dev: true + /async-retry@1.3.1: + resolution: {integrity: sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==} + dependencies: + retry: 0.12.0 + dev: true + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: false @@ -4848,6 +4972,11 @@ packages: engines: {node: '>= 4.0.0'} dev: false + /atomically@1.7.0: + resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==} + engines: {node: '>=10.12.0'} + dev: true + /autoprefixer@10.4.16(postcss@8.4.31): resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==} engines: {node: ^10 || ^12 || >=14} @@ -5313,6 +5442,10 @@ packages: resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} dev: true + /builtins@1.0.3: + resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} + dev: true + /builtins@5.0.1: resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} dependencies: @@ -5358,6 +5491,14 @@ packages: engines: {node: '>=8'} dev: true + /cacheable-lookup@2.0.1: + resolution: {integrity: sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==} + engines: {node: '>=10'} + dependencies: + '@types/keyv': 3.1.4 + keyv: 4.5.4 + dev: true + /cacheable-request@6.1.0: resolution: {integrity: sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==} engines: {node: '>=8'} @@ -5371,6 +5512,19 @@ packages: responselike: 1.0.2 dev: false + /cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + dev: true + /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: @@ -5555,6 +5709,11 @@ packages: optionalDependencies: fsevents: 2.3.3 + /chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + dev: true + /chrome-trace-event@1.0.3: resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} engines: {node: '>=6.0'} @@ -5657,7 +5816,6 @@ packages: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} dependencies: mimic-response: 1.0.1 - dev: false /clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} @@ -5739,8 +5897,8 @@ packages: engines: {node: '>=16'} dev: false - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + /commander@2.20.0: + resolution: {integrity: sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==} /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} @@ -5790,6 +5948,22 @@ packages: /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + /conf@10.2.0: + resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==} + engines: {node: '>=12'} + dependencies: + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + atomically: 1.7.0 + debounce-fn: 4.0.0 + dot-prop: 6.0.1 + env-paths: 2.2.1 + json-schema-typed: 7.0.3 + onetime: 5.1.2 + pkg-up: 3.1.0 + semver: 7.5.4 + dev: true + /configstore@5.0.1: resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} engines: {node: '>=8'} @@ -5964,7 +6138,7 @@ packages: sha.js: 2.4.11 dev: true - /create-jest@29.7.0(@types/node@20.9.0): + /create-jest@29.7.0(@types/node@18.18.8): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -5973,9 +6147,9 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.9.0) + jest-config: 29.7.0(@types/node@18.18.8) jest-util: 29.7.0 - prompts: 2.4.2 + prompts: 2.1.0 transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -6251,6 +6425,13 @@ packages: engines: {node: '>= 14'} dev: true + /debounce-fn@4.0.0: + resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==} + engines: {node: '>=10'} + dependencies: + mimic-fn: 3.1.0 + dev: true + /debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -6304,6 +6485,13 @@ packages: mimic-response: 1.0.1 dev: false + /decompress-response@5.0.0: + resolution: {integrity: sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==} + engines: {node: '>=10'} + dependencies: + mimic-response: 2.1.0 + dev: true + /dedent@1.5.1: resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} peerDependencies: @@ -6381,6 +6569,11 @@ packages: resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==} dev: false + /defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + dev: true + /define-data-property@1.1.0: resolution: {integrity: sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==} engines: {node: '>= 0.4'} @@ -6678,6 +6871,13 @@ packages: is-obj: 2.0.0 dev: false + /dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + dependencies: + is-obj: 2.0.0 + dev: true + /duck@0.1.12: resolution: {integrity: sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==} dependencies: @@ -6686,7 +6886,6 @@ packages: /duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} - dev: false /duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} @@ -6754,7 +6953,6 @@ packages: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 - dev: false /enhanced-resolve@5.13.0: resolution: {integrity: sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==} @@ -6788,6 +6986,11 @@ packages: engines: {node: '>=0.12'} dev: false + /env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + dev: true + /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -7119,7 +7322,7 @@ packages: minimatch: 3.1.2 object.values: 1.1.6 resolve: 1.22.4 - semver: 6.3.0 + semver: 6.3.1 tsconfig-paths: 3.14.2 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -7149,7 +7352,7 @@ packages: minimatch: 3.1.2 object.entries: 1.1.6 object.fromentries: 2.0.6 - semver: 6.3.0 + semver: 6.3.1 dev: false /eslint-plugin-react-hooks@4.6.0(eslint@8.53.0): @@ -7204,7 +7407,7 @@ packages: object.values: 1.1.6 prop-types: 15.8.1 resolve: 2.0.0-next.4 - semver: 6.3.0 + semver: 6.3.1 string.prototype.matchall: 4.0.8 dev: false @@ -7383,7 +7586,7 @@ packages: resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} engines: {node: '>= 0.8'} dependencies: - '@types/node': 20.9.0 + '@types/node': 18.18.8 require-like: 0.1.2 dev: false @@ -7664,7 +7867,6 @@ packages: engines: {node: '>=6'} dependencies: locate-path: 3.0.0 - dev: false /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} @@ -7834,6 +8036,13 @@ packages: universalify: 2.0.0 dev: false + /fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + dev: true + /fs-monkey@1.0.5: resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} dev: false @@ -7940,7 +8149,6 @@ packages: engines: {node: '>=8'} dependencies: pump: 3.0.0 - dev: false /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} @@ -8113,6 +8321,29 @@ packages: dependencies: get-intrinsic: 1.2.2 + /got@10.7.0: + resolution: {integrity: sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==} + engines: {node: '>=10'} + dependencies: + '@sindresorhus/is': 2.1.1 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/keyv': 3.1.4 + '@types/responselike': 1.0.1 + cacheable-lookup: 2.0.1 + cacheable-request: 7.0.4 + decompress-response: 5.0.0 + duplexer3: 0.1.5 + get-stream: 5.2.0 + lowercase-keys: 2.0.0 + mimic-response: 2.1.0 + p-cancelable: 2.1.1 + p-event: 4.2.0 + responselike: 2.0.1 + to-readable-stream: 2.1.0 + type-fest: 0.10.0 + dev: true + /got@9.6.0: resolution: {integrity: sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==} engines: {node: '>=8.6'} @@ -8440,7 +8671,6 @@ packages: /http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - dev: false /http-deceiver@1.2.7: resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} @@ -8948,7 +9178,6 @@ packages: /is-obj@2.0.0: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} - dev: false /is-path-cwd@2.2.0: resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} @@ -9209,7 +9438,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.10 + '@types/node': 18.18.8 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -9230,7 +9459,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@20.9.0): + /jest-cli@29.7.0(@types/node@18.18.8): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -9244,10 +9473,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.9.0) + create-jest: 29.7.0(@types/node@18.18.8) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.9.0) + jest-config: 29.7.0(@types/node@18.18.8) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -9258,47 +9487,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@20.8.10): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.23.0 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.8.10 - babel-jest: 29.7.0(@babel/core@7.23.0) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-config@29.7.0(@types/node@20.9.0): + /jest-config@29.7.0(@types/node@18.18.8): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -9313,7 +9502,7 @@ packages: '@babel/core': 7.23.0 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.9.0 + '@types/node': 18.18.8 babel-jest: 29.7.0(@babel/core@7.23.0) chalk: 4.1.2 ci-info: 3.9.0 @@ -9373,7 +9562,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.10 + '@types/node': 18.18.8 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -9389,7 +9578,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.7 - '@types/node': 20.8.10 + '@types/node': 18.18.8 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -9440,7 +9629,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.8.10 + '@types/node': 18.18.8 jest-util: 29.7.0 dev: true @@ -9495,7 +9684,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.10 + '@types/node': 18.18.8 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -9526,7 +9715,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.10 + '@types/node': 18.18.8 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -9578,7 +9767,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.8.10 + '@types/node': 18.18.8 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -9602,7 +9791,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.8.10 + '@types/node': 18.18.8 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -9614,7 +9803,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 20.9.0 + '@types/node': 18.18.8 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -9622,12 +9811,12 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.9.0 + '@types/node': 18.18.8 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - /jest@29.7.0(@types/node@20.9.0): + /jest@29.7.0(@types/node@18.18.8): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -9640,7 +9829,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.9.0) + jest-cli: 29.7.0(@types/node@18.18.8) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -9714,7 +9903,10 @@ packages: /json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - dev: false + + /json-schema-typed@7.0.3: + resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==} + dev: true /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -9881,7 +10073,6 @@ packages: dependencies: p-locate: 3.0.0 path-exists: 3.0.0 - dev: false /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} @@ -10009,7 +10200,6 @@ packages: /lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} - dev: false /lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -10252,6 +10442,11 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + /mimic-fn@3.1.0: + resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} + engines: {node: '>=8'} + dev: true + /mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} @@ -10260,7 +10455,11 @@ packages: /mimic-response@1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} - dev: false + + /mimic-response@2.1.0: + resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} + engines: {node: '>=8'} + dev: true /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} @@ -10308,6 +10507,31 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + /minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + dependencies: + yallist: 4.0.0 + dev: true + + /minipass@4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} + dev: true + + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + dev: true + + /minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + dev: true + /mixme@0.5.9: resolution: {integrity: sha512-VC5fg6ySUscaWUpI4gxCBTQMH2RdUpNrk+MsbpCYtIvf9SBJdiUey4qE7BXviJsJR4nDQxCZ+3yaYNW3guz/Pw==} engines: {node: '>= 8.0.0'} @@ -10320,6 +10544,12 @@ packages: minimist: 1.2.8 dev: true + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true + /mongodb-connection-string-url@2.6.0: resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==} dependencies: @@ -10587,7 +10817,6 @@ packages: /normalize-url@6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} - dev: false /notion-md-crawler@0.0.2: resolution: {integrity: sha512-lE3/DFMrg7GSbl1sBfDuLVLyxw+yjdarPVm1JGfQ6eONEbNGgO+BdZxpwwZQ1uYeEJurAXMXb/AXT8GKYjKAyg==} @@ -10823,6 +11052,18 @@ packages: engines: {node: '>=6'} dev: false + /p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + dev: true + + /p-event@4.2.0: + resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} + engines: {node: '>=8'} + dependencies: + p-timeout: 3.2.0 + dev: true + /p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -10830,6 +11071,11 @@ packages: p-map: 2.1.0 dev: false + /p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + dev: true + /p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -10847,7 +11093,6 @@ packages: engines: {node: '>=6'} dependencies: p-limit: 2.3.0 - dev: false /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} @@ -10888,6 +11133,13 @@ packages: retry: 0.13.1 dev: false + /p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + dependencies: + p-finally: 1.0.0 + dev: true + /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -11036,7 +11288,6 @@ packages: /path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} - dev: false /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -11136,7 +11387,6 @@ packages: engines: {node: '>=8'} dependencies: find-up: 3.0.0 - dev: false /portkey-ai@0.1.16: resolution: {integrity: sha512-EY4FRp6PZSD75Q1o1qc08DfPNTG9FnkUPN3Z1/lEvaq9iFpSO5UekcagUZaKSVhao311qjBjns+kF0rS9ht7iA==} @@ -11615,6 +11865,62 @@ packages: typescript: 5.2.2 dev: true + /prettier-plugin-tailwindcss@0.3.0(prettier-plugin-organize-imports@3.2.3)(prettier@3.0.3): + resolution: {integrity: sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==} + engines: {node: '>=12.17.0'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@shufo/prettier-plugin-blade': '*' + '@trivago/prettier-plugin-sort-imports': '*' + prettier: '>=2.2.0' + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + prettier-plugin-twig-melody: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@shufo/prettier-plugin-blade': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + prettier-plugin-twig-melody: + optional: true + dependencies: + prettier: 3.0.3 + prettier-plugin-organize-imports: 3.2.3(prettier@3.0.3)(typescript@5.2.2) + dev: true + /prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} @@ -11676,12 +11982,21 @@ packages: asap: 2.0.6 dev: false + /prompts@2.1.0: + resolution: {integrity: sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} dependencies: kleur: 3.0.3 sisteransi: 1.0.5 + dev: false /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -11744,7 +12059,6 @@ packages: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - dev: false /punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -12354,7 +12668,6 @@ packages: /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - dev: false /require-like@0.1.2: resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} @@ -12441,6 +12754,12 @@ packages: lowercase-keys: 1.0.1 dev: false + /responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + dependencies: + lowercase-keys: 2.0.0 + dev: true + /restore-cursor@3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} @@ -12449,6 +12768,11 @@ packages: signal-exit: 3.0.7 dev: true + /retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + dev: true + /retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -13392,6 +13716,18 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} + /tar@6.1.15: + resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==} + engines: {node: '>=10'} + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + dev: true + /term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -13451,7 +13787,7 @@ packages: dependencies: '@jridgewell/source-map': 0.3.5 acorn: 8.10.0 - commander: 2.20.3 + commander: 2.20.0 source-map-support: 0.5.21 /test-exclude@6.0.0: @@ -13542,6 +13878,11 @@ packages: engines: {node: '>=6'} dev: false + /to-readable-stream@2.1.0: + resolution: {integrity: sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==} + engines: {node: '>=8'} + dev: true + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -13626,7 +13967,7 @@ packages: '@babel/core': 7.23.2 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.9.0) + jest: 29.7.0(@types/node@18.18.8) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -13667,7 +14008,7 @@ packages: yn: 3.1.1 dev: true - /ts-node@10.9.1(@types/node@20.9.0)(typescript@5.2.2): + /ts-node@10.9.1(@types/node@18.18.8)(typescript@5.2.2): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -13686,7 +14027,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.9.0 + '@types/node': 18.18.8 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 @@ -13852,6 +14193,11 @@ packages: engines: {node: '>=4'} dev: true + /type-fest@0.10.0: + resolution: {integrity: sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==} + engines: {node: '>=8'} + dev: true + /type-fest@0.13.1: resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} engines: {node: '>=10'} @@ -14312,6 +14658,12 @@ packages: spdx-expression-parse: 3.0.1 dev: false + /validate-npm-package-name@3.0.0: + resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} + dependencies: + builtins: 1.0.3 + dev: true + /validate-npm-package-name@5.0.0: resolution: {integrity: sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -14925,3 +15277,9 @@ packages: /zwitch@1.0.5: resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} dev: false + + github.com/watson/ci-info/f43f6a1cefff47fb361c88cf4b943fdbcaafe540: + resolution: {tarball: https://codeload.github.com/watson/ci-info/tar.gz/f43f6a1cefff47fb361c88cf4b943fdbcaafe540} + name: ci-info + version: 2.0.0 + dev: true From e17c704a4bed03d46e9d93c948cda130583f1294 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Wed, 25 Oct 2023 15:45:39 +0700 Subject: [PATCH 02/44] add async-sema --- packages/create-llama/package.json | 3 ++- pnpm-lock.yaml | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/create-llama/package.json b/packages/create-llama/package.json index be420774bd..e057a8b8fc 100644 --- a/packages/create-llama/package.json +++ b/packages/create-llama/package.json @@ -49,7 +49,8 @@ "prompts": "2.1.0", "tar": "6.1.15", "update-check": "1.5.4", - "validate-npm-package-name": "3.0.0" + "validate-npm-package-name": "3.0.0", + "async-sema": "3.0.1" }, "engines": { "node": ">=16.14.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b7f39f8526..0495abe2eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -232,6 +232,9 @@ importers: async-retry: specifier: 1.3.1 version: 1.3.1 + async-sema: + specifier: 3.0.1 + version: 3.0.1 ci-info: specifier: watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540 version: github.com/watson/ci-info/f43f6a1cefff47fb361c88cf4b943fdbcaafe540 @@ -4963,6 +4966,10 @@ packages: retry: 0.12.0 dev: true + /async-sema@3.0.1: + resolution: {integrity: sha512-fKT2riE8EHAvJEfLJXZiATQWqZttjx1+tfgnVshCDrH8vlw4YC8aECe0B8MU184g+aVRFVgmfxFlKZKaozSrNw==} + dev: true + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: false From b350bb2e7aa88e0a17e8f364deed717af0f049e4 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Wed, 25 Oct 2023 16:23:24 +0700 Subject: [PATCH 03/44] add llama nextjs simple template --- packages/create-llama/README.md | 28 +- packages/create-llama/create-app.ts | 18 +- packages/create-llama/helpers/examples.ts | 2 +- packages/create-llama/index.ts | 151 +---------- packages/create-llama/package.json | 18 +- packages/create-llama/templates/index.ts | 245 ++++++++++++++++++ .../templates/simple/nextjs/.env.example | 3 + .../simple/nextjs/README-template.md | 36 +++ .../simple/nextjs/app/api/route/llm.ts | 52 ++++ .../templates/simple/nextjs/app/favicon.ico | Bin 0 -> 25931 bytes .../templates/simple/nextjs/app/globals.css | 27 ++ .../templates/simple/nextjs/app/layout.tsx | 22 ++ .../templates/simple/nextjs/app/page.tsx | 113 ++++++++ .../templates/simple/nextjs/eslintrc.json | 3 + .../templates/simple/nextjs/gitignore | 35 +++ .../templates/simple/nextjs/next-env.d.ts | 5 + .../templates/simple/nextjs/next.config.js | 4 + .../templates/simple/nextjs/postcss.config.js | 6 + .../templates/simple/nextjs/public/next.svg | 1 + .../templates/simple/nextjs/public/vercel.svg | 1 + .../simple/nextjs/tailwind.config.ts | 20 ++ .../templates/simple/nextjs/tsconfig.json | 27 ++ packages/create-llama/templates/types.ts | 24 ++ 23 files changed, 656 insertions(+), 185 deletions(-) create mode 100644 packages/create-llama/templates/index.ts create mode 100644 packages/create-llama/templates/simple/nextjs/.env.example create mode 100644 packages/create-llama/templates/simple/nextjs/README-template.md create mode 100644 packages/create-llama/templates/simple/nextjs/app/api/route/llm.ts create mode 100644 packages/create-llama/templates/simple/nextjs/app/favicon.ico create mode 100644 packages/create-llama/templates/simple/nextjs/app/globals.css create mode 100644 packages/create-llama/templates/simple/nextjs/app/layout.tsx create mode 100644 packages/create-llama/templates/simple/nextjs/app/page.tsx create mode 100644 packages/create-llama/templates/simple/nextjs/eslintrc.json create mode 100644 packages/create-llama/templates/simple/nextjs/gitignore create mode 100644 packages/create-llama/templates/simple/nextjs/next-env.d.ts create mode 100644 packages/create-llama/templates/simple/nextjs/next.config.js create mode 100644 packages/create-llama/templates/simple/nextjs/postcss.config.js create mode 100644 packages/create-llama/templates/simple/nextjs/public/next.svg create mode 100644 packages/create-llama/templates/simple/nextjs/public/vercel.svg create mode 100644 packages/create-llama/templates/simple/nextjs/tailwind.config.ts create mode 100644 packages/create-llama/templates/simple/nextjs/tsconfig.json create mode 100644 packages/create-llama/templates/types.ts diff --git a/packages/create-llama/README.md b/packages/create-llama/README.md index b50f474249..74134373c4 100644 --- a/packages/create-llama/README.md +++ b/packages/create-llama/README.md @@ -1,19 +1,19 @@ # Create Next App -The easiest way to get started with Next.js is by using `create-next-app`. This CLI tool enables you to quickly start building a new Next.js application, with everything set up for you. You can create a new app using the default Next.js template, or by using one of the [official Next.js examples](https://github.com/vercel/next.js/tree/canary/examples). To get started, use the following command: +The easiest way to get started with LlamaIndex is by using `create-llama`. This CLI tool enables you to quickly start building a new LlamaIndex application, with everything set up for you. You can create a new app using the default LlamaIndex template, or by using one of the [official LlamaIndex examples](https://github.com/vercel/next.js/tree/canary/examples). To get started, use the following command: ### Interactive You can create a new project interactively by running: ```bash -npx create-next-app@latest +npx create-llama@latest # or yarn create next-app # or pnpm create next-app # or -bunx create-next-app +bunx create-llama ``` You will be asked for the name of your project, and then whether you want to @@ -28,20 +28,14 @@ Select **Yes** to install the necessary types/dependencies and create a new TS p ### Non-interactive You can also pass command line arguments to set up a new project -non-interactively. See `create-next-app --help`: +non-interactively. See `create-llama --help`: ```bash -create-next-app [options] +create-llama [options] Options: -V, --version output the version number - --ts, --typescript - - Initialize as a TypeScript project. (default) - - --js, --javascript - - Initialize as a JavaScript project. + --use-npm @@ -62,7 +56,7 @@ Options: -e, --example [name]|[github-url] An example to bootstrap the app with. You can use an example name - from the official Next.js repo or a GitHub URL. The URL can use + from the official LlamaIndex repo or a GitHub URL. The URL can use any branch and/or subdirectory --example-path @@ -75,10 +69,10 @@ Options: ### Why use Create Next App? -`create-next-app` allows you to create a new Next.js app within seconds. It is officially maintained by the creators of Next.js, and includes a number of benefits: +`create-llama` allows you to create a new LlamaIndex app within seconds. It is officially maintained by the creators of LlamaIndex, and includes a number of benefits: -- **Interactive Experience**: Running `npx create-next-app@latest` (with no arguments) launches an interactive experience that guides you through setting up a project. +- **Interactive Experience**: Running `npx create-llama@latest` (with no arguments) launches an interactive experience that guides you through setting up a project. - **Zero Dependencies**: Initializing a project is as quick as one second. Create Next App has zero dependencies. - **Offline Support**: Create Next App will automatically detect if you're offline and bootstrap your project using your local package cache. -- **Support for Examples**: Create Next App can bootstrap your application using an example from the Next.js examples collection (e.g. `npx create-next-app --example api-routes`). -- **Tested**: The package is part of the Next.js monorepo and tested using the same integration test suite as Next.js itself, ensuring it works as expected with every release. +- **Support for Examples**: Create Next App can bootstrap your application using an example from the LlamaIndex examples collection (e.g. `npx create-llama --example api-routes`). +- **Tested**: The package is part of the LlamaIndex monorepo and tested using the same integration test suite as LlamaIndex itself, ensuring it works as expected with every release. diff --git a/packages/create-llama/create-app.ts b/packages/create-llama/create-app.ts index 89719e48ad..64d916078e 100644 --- a/packages/create-llama/create-app.ts +++ b/packages/create-llama/create-app.ts @@ -29,10 +29,8 @@ export async function createApp({ packageManager, example, examplePath, - typescript, tailwind, eslint, - appRouter, srcDir, importAlias, }: { @@ -40,22 +38,14 @@ export async function createApp({ packageManager: PackageManager example?: string examplePath?: string - typescript: boolean tailwind: boolean eslint: boolean - appRouter: boolean srcDir: boolean importAlias: string }): Promise { let repoInfo: RepoInfo | undefined - const mode: TemplateMode = typescript ? 'ts' : 'js' - const template: TemplateType = appRouter - ? tailwind - ? 'app-tw' - : 'app' - : tailwind - ? 'default-tw' - : 'default' + const mode: TemplateMode = 'nextjs'; + const template: TemplateType = 'simple'; if (example) { let repoUrl: URL | undefined @@ -141,7 +131,7 @@ export async function createApp({ const isOnline = !useYarn || (await getOnline()) const originalDirectory = process.cwd() - console.log(`Creating a new Next.js app in ${green(root)}.`) + console.log(`Creating a new LlamaIndex app in ${green(root)}.`) console.log() process.chdir(root) @@ -201,7 +191,7 @@ export async function createApp({ const tsconfigPath = path.join(root, 'tsconfig.json') if (fs.existsSync(tsconfigPath)) { fs.copyFileSync( - getTemplateFile({ template, mode: 'ts', file: 'next-env.d.ts' }), + getTemplateFile({ template, mode: 'nextjs', file: 'next-env.d.ts' }), path.join(root, 'next-env.d.ts') ) } diff --git a/packages/create-llama/helpers/examples.ts b/packages/create-llama/helpers/examples.ts index dad9895bd1..97bbacf27e 100644 --- a/packages/create-llama/helpers/examples.ts +++ b/packages/create-llama/helpers/examples.ts @@ -29,7 +29,7 @@ export async function getRepoInfo( const filePath = examplePath ? examplePath.replace(/^\//, '') : file.join('/') if ( - // Support repos whose entire purpose is to be a Next.js example, e.g. + // Support repos whose entire purpose is to be a LlamaIndex example, e.g. // https://github.com/:username/:my-cool-nextjs-example-repo-name. t === undefined || // Support GitHub URL that ends with a trailing slash, e.g. diff --git a/packages/create-llama/index.ts b/packages/create-llama/index.ts index 1d488260ae..fb0b89a142 100644 --- a/packages/create-llama/index.ts +++ b/packages/create-llama/index.ts @@ -38,46 +38,11 @@ const program = new Commander.Command(packageJson.name) .action((name) => { projectPath = name }) - .option( - '--ts, --typescript', - ` - - Initialize as a TypeScript project. (default) -` - ) - .option( - '--js, --javascript', - ` - - Initialize as a JavaScript project. -` - ) - .option( - '--tailwind', - ` - - Initialize with Tailwind CSS config. (default) -` - ) .option( '--eslint', ` Initialize with eslint config. -` - ) - .option( - '--app', - ` - - Initialize as an App Router project. -` - ) - .option( - '--src-dir', - ` - - Initialize inside a \`src/\` directory. ` ) .option( @@ -120,7 +85,7 @@ const program = new Commander.Command(packageJson.name) ` An example to bootstrap the app with. You can use an example name - from the official Next.js repo or a GitHub URL. The URL can use + from the official LlamaIndex repo or a GitHub URL. The URL can use any branch and/or subdirectory ` ) @@ -155,7 +120,7 @@ const packageManager = !!program.useNpm : getPkgManager() async function run(): Promise { - const conf = new Conf({ projectName: 'create-next-app' }) + const conf = new Conf({ projectName: 'create-llama' }) if (program.resetPreferences) { conf.clear() @@ -254,42 +219,6 @@ async function run(): Promise { const getPrefOrDefault = (field: string) => preferences[field] ?? defaults[field] - if (!program.typescript && !program.javascript) { - if (ciInfo.isCI) { - // default to TypeScript in CI as we can't prompt to - // prevent breaking setup flows - program.typescript = getPrefOrDefault('typescript') - } else { - const styledTypeScript = blue('TypeScript') - const { typescript } = await prompts( - { - type: 'toggle', - name: 'typescript', - message: `Would you like to use ${styledTypeScript}?`, - initial: getPrefOrDefault('typescript'), - active: 'Yes', - inactive: 'No', - }, - { - /** - * User inputs Ctrl+C or Ctrl+D to exit the prompt. We should close the - * process and not write to the file system. - */ - onCancel: () => { - console.error('Exiting.') - process.exit(1) - }, - } - ) - /** - * Depending on the prompt response, set the appropriate program flags. - */ - program.typescript = Boolean(typescript) - program.javascript = !Boolean(typescript) - preferences.typescript = Boolean(typescript) - } - } - if ( !process.argv.includes('--eslint') && !process.argv.includes('--no-eslint') @@ -312,68 +241,6 @@ async function run(): Promise { } } - if ( - !process.argv.includes('--tailwind') && - !process.argv.includes('--no-tailwind') - ) { - if (ciInfo.isCI) { - program.tailwind = getPrefOrDefault('tailwind') - } else { - const tw = blue('Tailwind CSS') - const { tailwind } = await prompts({ - onState: onPromptState, - type: 'toggle', - name: 'tailwind', - message: `Would you like to use ${tw}?`, - initial: getPrefOrDefault('tailwind'), - active: 'Yes', - inactive: 'No', - }) - program.tailwind = Boolean(tailwind) - preferences.tailwind = Boolean(tailwind) - } - } - - if ( - !process.argv.includes('--src-dir') && - !process.argv.includes('--no-src-dir') - ) { - if (ciInfo.isCI) { - program.srcDir = getPrefOrDefault('srcDir') - } else { - const styledSrcDir = blue('`src/` directory') - const { srcDir } = await prompts({ - onState: onPromptState, - type: 'toggle', - name: 'srcDir', - message: `Would you like to use ${styledSrcDir}?`, - initial: getPrefOrDefault('srcDir'), - active: 'Yes', - inactive: 'No', - }) - program.srcDir = Boolean(srcDir) - preferences.srcDir = Boolean(srcDir) - } - } - - if (!process.argv.includes('--app') && !process.argv.includes('--no-app')) { - if (ciInfo.isCI) { - program.app = getPrefOrDefault('app') - } else { - const styledAppDir = blue('App Router') - const { appRouter } = await prompts({ - onState: onPromptState, - type: 'toggle', - name: 'appRouter', - message: `Would you like to use ${styledAppDir}? (recommended)`, - initial: getPrefOrDefault('app'), - active: 'Yes', - inactive: 'No', - }) - program.app = Boolean(appRouter) - } - } - if ( typeof program.importAlias !== 'string' || !program.importAlias.length @@ -422,10 +289,8 @@ async function run(): Promise { packageManager, example: example && example !== 'default' ? example : undefined, examplePath: program.examplePath, - typescript: program.typescript, tailwind: program.tailwind, eslint: program.eslint, - appRouter: program.app, srcDir: program.srcDir, importAlias: program.importAlias, }) @@ -450,10 +315,8 @@ async function run(): Promise { await createApp({ appPath: resolvedProjectPath, packageManager, - typescript: program.typescript, eslint: program.eslint, tailwind: program.tailwind, - appRouter: program.app, srcDir: program.srcDir, importAlias: program.importAlias, }) @@ -469,15 +332,15 @@ async function notifyUpdate(): Promise { if (res?.latest) { const updateMessage = packageManager === 'yarn' - ? 'yarn global add create-next-app' + ? 'yarn global add create-llama' : packageManager === 'pnpm' - ? 'pnpm add -g create-next-app' + ? 'pnpm add -g create-llama' : packageManager === 'bun' - ? 'bun add -g create-next-app' - : 'npm i -g create-next-app' + ? 'bun add -g create-llama' + : 'npm i -g create-llama' console.log( - yellow(bold('A new version of `create-next-app` is available!')) + + yellow(bold('A new version of `create-llama` is available!')) + '\n' + 'You can update by running: ' + cyan(updateMessage) + diff --git a/packages/create-llama/package.json b/packages/create-llama/package.json index e057a8b8fc..cec7693118 100644 --- a/packages/create-llama/package.json +++ b/packages/create-llama/package.json @@ -1,21 +1,21 @@ { - "name": "create-next-app", - "version": "13.5.6", + "name": "create-llama", + "version": "0.0.1", "keywords": [ - "react", - "next", + "rag", + "llamaindex", "next.js" ], - "description": "Create Next.js-powered React apps with one command", + "description": "Create LlamaIndex-powered apps with one command", "repository": { "type": "git", - "url": "https://github.com/vercel/next.js", - "directory": "packages/create-next-app" + "url": "https://github.com/run-llama/LlamaIndexTS", + "directory": "packages/create-llama" }, - "author": "Next.js Team ", + "author": "LlamaIndex Team ", "license": "MIT", "bin": { - "create-next-app": "./dist/index.js" + "create-llama": "./dist/index.js" }, "files": [ "dist" diff --git a/packages/create-llama/templates/index.ts b/packages/create-llama/templates/index.ts new file mode 100644 index 0000000000..9c2b0082f3 --- /dev/null +++ b/packages/create-llama/templates/index.ts @@ -0,0 +1,245 @@ +import { install } from '../helpers/install' +import { makeDir } from '../helpers/make-dir' +import { copy } from '../helpers/copy' + +import { async as glob } from 'fast-glob' +import os from 'os' +import fs from 'fs/promises' +import path from 'path' +import { cyan, bold } from 'picocolors' +import { Sema } from 'async-sema' +import { version } from '../package.json' + +import { GetTemplateFileArgs, InstallTemplateArgs } from './types' + +/** + * Get the file path for a given file in a template, e.g. "next.config.js". + */ +export const getTemplateFile = ({ + template, + mode, + file, +}: GetTemplateFileArgs): string => { + return path.join(__dirname, template, mode, file) +} + +export const SRC_DIR_NAMES = ['app', 'pages', 'styles'] + +/** + * Install a LlamaIndex internal template to a given `root` directory. + */ +export const installTemplate = async ({ + appName, + root, + packageManager, + isOnline, + template, + mode, + tailwind, + eslint, + srcDir, + importAlias, +}: InstallTemplateArgs) => { + console.log(bold(`Using ${packageManager}.`)) + + /** + * Copy the template files to the target directory. + */ + console.log('\nInitializing project with template:', template, '\n') + const templatePath = path.join(__dirname, template, mode) + const copySource = ['**'] + if (!eslint) copySource.push('!eslintrc.json') + if (!tailwind) + copySource.push( + mode == 'nextjs' ? 'tailwind.config.ts' : '!tailwind.config.js', + '!postcss.config.js' + ) + + await copy(copySource, root, { + parents: true, + cwd: templatePath, + rename(name) { + switch (name) { + case 'gitignore': + case 'eslintrc.json': { + return `.${name}` + } + // README.md is ignored by webpack-asset-relocator-loader used by ncc: + // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227 + case 'README-template.md': { + return 'README.md' + } + default: { + return name + } + } + }, + }) + + const tsconfigFile = path.join( + root, + 'tsconfig.json' + ) + await fs.writeFile( + tsconfigFile, + (await fs.readFile(tsconfigFile, 'utf8')) + .replace( + `"@/*": ["./*"]`, + srcDir ? `"@/*": ["./src/*"]` : `"@/*": ["./*"]` + ) + .replace(`"@/*":`, `"${importAlias}":`) + ) + + // update import alias in any files if not using the default + if (importAlias !== '@/*') { + const files = await glob('**/*', { + cwd: root, + dot: true, + stats: false, + }) + const writeSema = new Sema(8, { capacity: files.length }) + await Promise.all( + files.map(async (file) => { + // We don't want to modify compiler options in [ts/js]config.json + if (file === 'tsconfig.json' || file === 'jsconfig.json') return + await writeSema.acquire() + const filePath = path.join(root, file) + if ((await fs.stat(filePath)).isFile()) { + await fs.writeFile( + filePath, + ( + await fs.readFile(filePath, 'utf8') + ).replace(`@/`, `${importAlias.replace(/\*/g, '')}`) + ) + } + await writeSema.release() + }) + ) + } + + if (srcDir) { + await makeDir(path.join(root, 'src')) + await Promise.all( + SRC_DIR_NAMES.map(async (file) => { + await fs + .rename(path.join(root, file), path.join(root, 'src', file)) + .catch((err) => { + if (err.code !== 'ENOENT') { + throw err + } + }) + }) + ) + + const isAppTemplate = template.startsWith('app') + + // Change the `Get started by editing pages/index` / `app/page` to include `src` + const indexPageFile = path.join( + 'src', + isAppTemplate ? 'app' : 'pages', + `${isAppTemplate ? 'page' : 'index'}.tsx` + ) + + await fs.writeFile( + indexPageFile, + ( + await fs.readFile(indexPageFile, 'utf8') + ).replace( + isAppTemplate ? 'app/page' : 'pages/index', + isAppTemplate ? 'src/app/page' : 'src/pages/index' + ) + ) + + if (tailwind) { + const tailwindConfigFile = path.join( + root, + 'tailwind.config.ts' + ) + await fs.writeFile( + tailwindConfigFile, + ( + await fs.readFile(tailwindConfigFile, 'utf8') + ).replace( + /\.\/(\w+)\/\*\*\/\*\.\{js,ts,jsx,tsx,mdx\}/g, + './src/$1/**/*.{js,ts,jsx,tsx,mdx}' + ) + ) + } + } + + /** Create a package.json for the new project and write it to disk. */ + const packageJson: any = { + name: appName, + version: '0.1.0', + private: true, + scripts: { + dev: 'next dev', + build: 'next build', + start: 'next start', + lint: 'next lint', + }, + /** + * Default dependencies. + */ + dependencies: { + react: '^18', + 'react-dom': '^18', + next: process.env.NEXT_PRIVATE_TEST_VERSION ?? version, + }, + devDependencies: {}, + } + + /** + * TypeScript projects will have type definitions and other devDependencies. + */ + packageJson.devDependencies = { + ...packageJson.devDependencies, + typescript: '^5', + '@types/node': '^20', + '@types/react': '^18', + '@types/react-dom': '^18', + } + + /* Add Tailwind CSS dependencies. */ + if (tailwind) { + packageJson.devDependencies = { + ...packageJson.devDependencies, + autoprefixer: '^10', + postcss: '^8', + tailwindcss: '^3', + } + } + + /* Default ESLint dependencies. */ + if (eslint) { + packageJson.devDependencies = { + ...packageJson.devDependencies, + eslint: '^8', + 'eslint-config-next': version, + } + } + + const devDeps = Object.keys(packageJson.devDependencies).length + if (!devDeps) delete packageJson.devDependencies + + await fs.writeFile( + path.join(root, 'package.json'), + JSON.stringify(packageJson, null, 2) + os.EOL + ) + + console.log('\nInstalling dependencies:') + for (const dependency in packageJson.dependencies) + console.log(`- ${cyan(dependency)}`) + + if (devDeps) { + console.log('\nInstalling devDependencies:') + for (const dependency in packageJson.devDependencies) + console.log(`- ${cyan(dependency)}`) + } + + console.log() + + await install(packageManager, isOnline) +} + +export * from './types' diff --git a/packages/create-llama/templates/simple/nextjs/.env.example b/packages/create-llama/templates/simple/nextjs/.env.example new file mode 100644 index 0000000000..7ac0a01551 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/.env.example @@ -0,0 +1,3 @@ +# Rename this file to `.env.local` to use environment variables locally with `next dev` +# https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables +MY_HOST="example.com" diff --git a/packages/create-llama/templates/simple/nextjs/README-template.md b/packages/create-llama/templates/simple/nextjs/README-template.md new file mode 100644 index 0000000000..c4033664f8 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/README-template.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/packages/create-llama/templates/simple/nextjs/app/api/route/llm.ts b/packages/create-llama/templates/simple/nextjs/app/api/route/llm.ts new file mode 100644 index 0000000000..5a56b18449 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/api/route/llm.ts @@ -0,0 +1,52 @@ +import { ChatMessage, OpenAI, SimpleChatEngine } from "llamaindex"; +import { NextRequest, NextResponse } from "next/server"; + +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const { + message, + chatHistory, + }: { + message: string; + chatHistory: ChatMessage[]; + } = body; + if (!message || !chatHistory) { + return NextResponse.json( + { + error: "message, chatHistory are required in the request body", + }, + { status: 400 } + ); + } + + const llm = new OpenAI({ + model: "gpt-3.5-turbo", + }); + + const chatEngine = new SimpleChatEngine({ + llm, + }); + + const response = await chatEngine.chat(message, chatHistory); + const result: ChatMessage = { + role: "assistant", + content: response.response, + }; + + return NextResponse.json({ result }); + } catch (error) { + console.error("[LlamaIndex]", error); + return NextResponse.json( + { + error: (error as Error).message, + }, + { + status: 500, + } + ); + } +} diff --git a/packages/create-llama/templates/simple/nextjs/app/favicon.ico b/packages/create-llama/templates/simple/nextjs/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/packages/create-llama/templates/simple/nextjs/app/globals.css b/packages/create-llama/templates/simple/nextjs/app/globals.css new file mode 100644 index 0000000000..fd81e88583 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/globals.css @@ -0,0 +1,27 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} diff --git a/packages/create-llama/templates/simple/nextjs/app/layout.tsx b/packages/create-llama/templates/simple/nextjs/app/layout.tsx new file mode 100644 index 0000000000..fb09770627 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/layout.tsx @@ -0,0 +1,22 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Create Llama App", + description: "Generated by create-llama", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/packages/create-llama/templates/simple/nextjs/app/page.tsx b/packages/create-llama/templates/simple/nextjs/app/page.tsx new file mode 100644 index 0000000000..7a8286b576 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/page.tsx @@ -0,0 +1,113 @@ +import Image from 'next/image' + +export default function Home() { + return ( +
+
+

+ Get started by editing  + app/page.tsx +

+ +
+ +
+ Next.js Logo +
+ + +
+ ) +} diff --git a/packages/create-llama/templates/simple/nextjs/eslintrc.json b/packages/create-llama/templates/simple/nextjs/eslintrc.json new file mode 100644 index 0000000000..bffb357a71 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/packages/create-llama/templates/simple/nextjs/gitignore b/packages/create-llama/templates/simple/nextjs/gitignore new file mode 100644 index 0000000000..8f322f0d8f --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/create-llama/templates/simple/nextjs/next-env.d.ts b/packages/create-llama/templates/simple/nextjs/next-env.d.ts new file mode 100644 index 0000000000..4f11a03dc6 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/packages/create-llama/templates/simple/nextjs/next.config.js b/packages/create-llama/templates/simple/nextjs/next.config.js new file mode 100644 index 0000000000..767719fc4f --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {} + +module.exports = nextConfig diff --git a/packages/create-llama/templates/simple/nextjs/postcss.config.js b/packages/create-llama/templates/simple/nextjs/postcss.config.js new file mode 100644 index 0000000000..33ad091d26 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/packages/create-llama/templates/simple/nextjs/public/next.svg b/packages/create-llama/templates/simple/nextjs/public/next.svg new file mode 100644 index 0000000000..5174b28c56 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/create-llama/templates/simple/nextjs/public/vercel.svg b/packages/create-llama/templates/simple/nextjs/public/vercel.svg new file mode 100644 index 0000000000..d2f8422273 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/create-llama/templates/simple/nextjs/tailwind.config.ts b/packages/create-llama/templates/simple/nextjs/tailwind.config.ts new file mode 100644 index 0000000000..c7ead80465 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/tailwind.config.ts @@ -0,0 +1,20 @@ +import type { Config } from 'tailwindcss' + +const config: Config = { + content: [ + './pages/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + './app/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: { + backgroundImage: { + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-conic': + 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', + }, + }, + }, + plugins: [], +} +export default config diff --git a/packages/create-llama/templates/simple/nextjs/tsconfig.json b/packages/create-llama/templates/simple/nextjs/tsconfig.json new file mode 100644 index 0000000000..c714696378 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/packages/create-llama/templates/types.ts b/packages/create-llama/templates/types.ts new file mode 100644 index 0000000000..541fa3d31b --- /dev/null +++ b/packages/create-llama/templates/types.ts @@ -0,0 +1,24 @@ +import { PackageManager } from '../helpers/get-pkg-manager' + +export type TemplateType = 'simple' +export type TemplateMode = 'nextjs' + +export interface GetTemplateFileArgs { + template: TemplateType + mode: TemplateMode + file: string +} + +export interface InstallTemplateArgs { + appName: string + root: string + packageManager: PackageManager + isOnline: boolean + + template: TemplateType + mode: TemplateMode + eslint: boolean + tailwind: boolean + srcDir: boolean + importAlias: string +} From 00674686cb632ed2d5d2c6324e3412b4364d2412 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Wed, 25 Oct 2023 17:03:02 +0700 Subject: [PATCH 04/44] add test form for nextjs simple (and make generation work) --- packages/create-llama/index.ts | 4 +- packages/create-llama/templates/index.ts | 9 +- .../simple/nextjs/app/api/llm/route.ts | 52 +++++++ .../simple/nextjs/app/api/route/llm.ts | 52 ------- .../nextjs/app/components/message-form.tsx | 43 ++++++ .../templates/simple/nextjs/app/layout.tsx | 12 +- .../templates/simple/nextjs/app/page.tsx | 141 ++++-------------- .../templates/simple/nextjs/next.config.js | 6 +- 8 files changed, 145 insertions(+), 174 deletions(-) create mode 100644 packages/create-llama/templates/simple/nextjs/app/api/llm/route.ts delete mode 100644 packages/create-llama/templates/simple/nextjs/app/api/route/llm.ts create mode 100644 packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx diff --git a/packages/create-llama/index.ts b/packages/create-llama/index.ts index fb0b89a142..6c5bafeaec 100644 --- a/packages/create-llama/index.ts +++ b/packages/create-llama/index.ts @@ -289,7 +289,7 @@ async function run(): Promise { packageManager, example: example && example !== 'default' ? example : undefined, examplePath: program.examplePath, - tailwind: program.tailwind, + tailwind: true, eslint: program.eslint, srcDir: program.srcDir, importAlias: program.importAlias, @@ -316,7 +316,7 @@ async function run(): Promise { appPath: resolvedProjectPath, packageManager, eslint: program.eslint, - tailwind: program.tailwind, + tailwind: true, srcDir: program.srcDir, importAlias: program.importAlias, }) diff --git a/packages/create-llama/templates/index.ts b/packages/create-llama/templates/index.ts index 9c2b0082f3..df407f7ac6 100644 --- a/packages/create-llama/templates/index.ts +++ b/packages/create-llama/templates/index.ts @@ -8,10 +8,11 @@ import fs from 'fs/promises' import path from 'path' import { cyan, bold } from 'picocolors' import { Sema } from 'async-sema' -import { version } from '../package.json' import { GetTemplateFileArgs, InstallTemplateArgs } from './types' +const NEXT_VERSION = '13.5.6'; + /** * Get the file path for a given file in a template, e.g. "next.config.js". */ @@ -184,7 +185,9 @@ export const installTemplate = async ({ dependencies: { react: '^18', 'react-dom': '^18', - next: process.env.NEXT_PRIVATE_TEST_VERSION ?? version, + next: NEXT_VERSION, + llamaindex: "0.0.0-20231018030303", + encoding: "^0.1.13" }, devDependencies: {}, } @@ -215,7 +218,7 @@ export const installTemplate = async ({ packageJson.devDependencies = { ...packageJson.devDependencies, eslint: '^8', - 'eslint-config-next': version, + 'eslint-config-next': NEXT_VERSION, } } diff --git a/packages/create-llama/templates/simple/nextjs/app/api/llm/route.ts b/packages/create-llama/templates/simple/nextjs/app/api/llm/route.ts new file mode 100644 index 0000000000..bb3a11319e --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/api/llm/route.ts @@ -0,0 +1,52 @@ +import { ChatMessage, OpenAI, SimpleChatEngine } from "llamaindex"; +import { NextRequest, NextResponse } from "next/server"; + +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const { + message, + chatHistory, + }: { + message: string; + chatHistory: ChatMessage[]; + } = body; + if (!message || !chatHistory) { + return NextResponse.json( + { + error: "message, chatHistory are required in the request body", + }, + { status: 400 } + ); + } + + const llm = new OpenAI({ + model: "gpt-3.5-turbo", + }); + + const chatEngine = new SimpleChatEngine({ + llm, + }); + + const response = await chatEngine.chat(message, chatHistory); + const result: ChatMessage = { + role: "assistant", + content: response.response, + }; + + return NextResponse.json({ result }); + } catch (error) { + console.error("[LlamaIndex]", error); + return NextResponse.json( + { + error: (error as Error).message, + }, + { + status: 500, + } + ); + } +} diff --git a/packages/create-llama/templates/simple/nextjs/app/api/route/llm.ts b/packages/create-llama/templates/simple/nextjs/app/api/route/llm.ts deleted file mode 100644 index 5a56b18449..0000000000 --- a/packages/create-llama/templates/simple/nextjs/app/api/route/llm.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { ChatMessage, OpenAI, SimpleChatEngine } from "llamaindex"; -import { NextRequest, NextResponse } from "next/server"; - -export const runtime = "nodejs"; -export const dynamic = "force-dynamic"; - -export async function POST(request: NextRequest) { - try { - const body = await request.json(); - const { - message, - chatHistory, - }: { - message: string; - chatHistory: ChatMessage[]; - } = body; - if (!message || !chatHistory) { - return NextResponse.json( - { - error: "message, chatHistory are required in the request body", - }, - { status: 400 } - ); - } - - const llm = new OpenAI({ - model: "gpt-3.5-turbo", - }); - - const chatEngine = new SimpleChatEngine({ - llm, - }); - - const response = await chatEngine.chat(message, chatHistory); - const result: ChatMessage = { - role: "assistant", - content: response.response, - }; - - return NextResponse.json({ result }); - } catch (error) { - console.error("[LlamaIndex]", error); - return NextResponse.json( - { - error: (error as Error).message, - }, - { - status: 500, - } - ); - } -} diff --git a/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx b/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx new file mode 100644 index 0000000000..0f67f43d3c --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx @@ -0,0 +1,43 @@ +"use client"; +import { ChatMessage } from "llamaindex"; + +export default function MessageForm() { + const testSendMessage = async (message: string) => { + const chatHistory: ChatMessage[] = []; + const apiRoute = "/api/llm"; + const response = await fetch(apiRoute, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ message, chatHistory }), + }); + const data = await response.json(); + alert(JSON.stringify(data)); + }; + + return ( +
{ + e.preventDefault(); + const message = e.currentTarget.message.value; + testSendMessage(message); + }} + className="flex flex-col items-center justify-center w-full max-w-5xl p-4 space-y-4 bg-white rounded-xl shadow-xl" + > + + +
+ ); +} diff --git a/packages/create-llama/templates/simple/nextjs/app/layout.tsx b/packages/create-llama/templates/simple/nextjs/app/layout.tsx index fb09770627..444a7b6eec 100644 --- a/packages/create-llama/templates/simple/nextjs/app/layout.tsx +++ b/packages/create-llama/templates/simple/nextjs/app/layout.tsx @@ -1,8 +1,8 @@ -import type { Metadata } from "next"; -import { Inter } from "next/font/google"; -import "./globals.css"; +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import './globals.css' -const inter = Inter({ subsets: ["latin"] }); +const inter = Inter({ subsets: ['latin'] }) export const metadata: Metadata = { title: "Create Llama App", @@ -12,11 +12,11 @@ export const metadata: Metadata = { export default function RootLayout({ children, }: { - children: React.ReactNode; + children: React.ReactNode }) { return ( {children} - ); + ) } diff --git a/packages/create-llama/templates/simple/nextjs/app/page.tsx b/packages/create-llama/templates/simple/nextjs/app/page.tsx index 7a8286b576..1a73edb1ea 100644 --- a/packages/create-llama/templates/simple/nextjs/app/page.tsx +++ b/packages/create-llama/templates/simple/nextjs/app/page.tsx @@ -1,113 +1,34 @@ -import Image from 'next/image' +import MessageForm from "@/app/components/message-form"; +import Image from "next/image"; export default function Home() { - return ( -
-
-

- Get started by editing  - app/page.tsx -

- -
- -
- Next.js Logo -
- - -
- ) + return ( +
+
+

+ Get started by editing  + app/page.tsx +

+ +
+ +
+ ); } diff --git a/packages/create-llama/templates/simple/nextjs/next.config.js b/packages/create-llama/templates/simple/nextjs/next.config.js index 767719fc4f..0b2c2bf173 100644 --- a/packages/create-llama/templates/simple/nextjs/next.config.js +++ b/packages/create-llama/templates/simple/nextjs/next.config.js @@ -1,4 +1,8 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {} +const nextConfig = { + experimental: { + serverComponentsExternalPackages: ["llamaindex"], + }, +} module.exports = nextConfig From 8e1cb8fb704c733a45659678400c57564ff48317 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Wed, 25 Oct 2023 17:04:07 +0700 Subject: [PATCH 05/44] use prettier --- packages/create-llama/create-app.ts | 246 +++++++------- packages/create-llama/helpers/copy.ts | 42 +-- packages/create-llama/helpers/examples.ts | 112 ++++--- .../create-llama/helpers/get-pkg-manager.ts | 18 +- packages/create-llama/helpers/git.ts | 48 +-- packages/create-llama/helpers/install.ts | 38 +-- .../create-llama/helpers/is-folder-empty.ts | 76 ++--- packages/create-llama/helpers/is-online.ts | 34 +- packages/create-llama/helpers/is-writeable.ts | 8 +- packages/create-llama/helpers/make-dir.ts | 6 +- packages/create-llama/helpers/validate-pkg.ts | 12 +- packages/create-llama/index.ts | 316 +++++++++--------- packages/create-llama/templates/index.ts | 223 ++++++------ .../simple/nextjs/app/api/llm/route.ts | 80 ++--- .../nextjs/app/components/message-form.tsx | 74 ++-- .../templates/simple/nextjs/app/layout.tsx | 12 +- .../templates/simple/nextjs/app/page.tsx | 58 ++-- .../simple/nextjs/tailwind.config.ts | 18 +- packages/create-llama/templates/types.ts | 32 +- 19 files changed, 723 insertions(+), 730 deletions(-) diff --git a/packages/create-llama/create-app.ts b/packages/create-llama/create-app.ts index 64d916078e..fb76bc8a4c 100644 --- a/packages/create-llama/create-app.ts +++ b/packages/create-llama/create-app.ts @@ -1,26 +1,26 @@ /* eslint-disable import/no-extraneous-dependencies */ -import retry from 'async-retry' -import { red, green, cyan } from 'picocolors' -import fs from 'fs' -import path from 'path' -import type { RepoInfo } from './helpers/examples' +import retry from "async-retry"; +import fs from "fs"; +import path from "path"; +import { cyan, green, red } from "picocolors"; +import type { RepoInfo } from "./helpers/examples"; import { downloadAndExtractExample, downloadAndExtractRepo, - getRepoInfo, existsInRepo, + getRepoInfo, hasRepo, -} from './helpers/examples' -import { makeDir } from './helpers/make-dir' -import { tryGitInit } from './helpers/git' -import { install } from './helpers/install' -import { isFolderEmpty } from './helpers/is-folder-empty' -import { getOnline } from './helpers/is-online' -import { isWriteable } from './helpers/is-writeable' -import type { PackageManager } from './helpers/get-pkg-manager' +} from "./helpers/examples"; +import type { PackageManager } from "./helpers/get-pkg-manager"; +import { tryGitInit } from "./helpers/git"; +import { install } from "./helpers/install"; +import { isFolderEmpty } from "./helpers/is-folder-empty"; +import { getOnline } from "./helpers/is-online"; +import { isWriteable } from "./helpers/is-writeable"; +import { makeDir } from "./helpers/make-dir"; -import type { TemplateMode, TemplateType } from './templates' -import { getTemplateFile, installTemplate } from './templates' +import type { TemplateMode, TemplateType } from "./templates"; +import { getTemplateFile, installTemplate } from "./templates"; export class DownloadError extends Error {} @@ -34,110 +34,110 @@ export async function createApp({ srcDir, importAlias, }: { - appPath: string - packageManager: PackageManager - example?: string - examplePath?: string - tailwind: boolean - eslint: boolean - srcDir: boolean - importAlias: string + appPath: string; + packageManager: PackageManager; + example?: string; + examplePath?: string; + tailwind: boolean; + eslint: boolean; + srcDir: boolean; + importAlias: string; }): Promise { - let repoInfo: RepoInfo | undefined - const mode: TemplateMode = 'nextjs'; - const template: TemplateType = 'simple'; + let repoInfo: RepoInfo | undefined; + const mode: TemplateMode = "nextjs"; + const template: TemplateType = "simple"; if (example) { - let repoUrl: URL | undefined + let repoUrl: URL | undefined; try { - repoUrl = new URL(example) + repoUrl = new URL(example); } catch (error: any) { - if (error.code !== 'ERR_INVALID_URL') { - console.error(error) - process.exit(1) + if (error.code !== "ERR_INVALID_URL") { + console.error(error); + process.exit(1); } } if (repoUrl) { - if (repoUrl.origin !== 'https://github.com') { + if (repoUrl.origin !== "https://github.com") { console.error( `Invalid URL: ${red( - `"${example}"` - )}. Only GitHub repositories are supported. Please use a GitHub URL and try again.` - ) - process.exit(1) + `"${example}"`, + )}. Only GitHub repositories are supported. Please use a GitHub URL and try again.`, + ); + process.exit(1); } - repoInfo = await getRepoInfo(repoUrl, examplePath) + repoInfo = await getRepoInfo(repoUrl, examplePath); if (!repoInfo) { console.error( `Found invalid GitHub URL: ${red( - `"${example}"` - )}. Please fix the URL and try again.` - ) - process.exit(1) + `"${example}"`, + )}. Please fix the URL and try again.`, + ); + process.exit(1); } - const found = await hasRepo(repoInfo) + const found = await hasRepo(repoInfo); if (!found) { console.error( `Could not locate the repository for ${red( - `"${example}"` - )}. Please check that the repository exists and try again.` - ) - process.exit(1) + `"${example}"`, + )}. Please check that the repository exists and try again.`, + ); + process.exit(1); } - } else if (example !== '__internal-testing-retry') { - const found = await existsInRepo(example) + } else if (example !== "__internal-testing-retry") { + const found = await existsInRepo(example); if (!found) { console.error( `Could not locate an example named ${red( - `"${example}"` + `"${example}"`, )}. It could be due to the following:\n`, `1. Your spelling of example ${red( - `"${example}"` + `"${example}"`, )} might be incorrect.\n`, - `2. You might not be connected to the internet or you are behind a proxy.` - ) - process.exit(1) + `2. You might not be connected to the internet or you are behind a proxy.`, + ); + process.exit(1); } } } - const root = path.resolve(appPath) + const root = path.resolve(appPath); if (!(await isWriteable(path.dirname(root)))) { console.error( - 'The application path is not writable, please check folder permissions and try again.' - ) + "The application path is not writable, please check folder permissions and try again.", + ); console.error( - 'It is likely you do not have write permissions for this folder.' - ) - process.exit(1) + "It is likely you do not have write permissions for this folder.", + ); + process.exit(1); } - const appName = path.basename(root) + const appName = path.basename(root); - await makeDir(root) + await makeDir(root); if (!isFolderEmpty(root, appName)) { - process.exit(1) + process.exit(1); } - const useYarn = packageManager === 'yarn' - const isOnline = !useYarn || (await getOnline()) - const originalDirectory = process.cwd() + const useYarn = packageManager === "yarn"; + const isOnline = !useYarn || (await getOnline()); + const originalDirectory = process.cwd(); - console.log(`Creating a new LlamaIndex app in ${green(root)}.`) - console.log() + console.log(`Creating a new LlamaIndex app in ${green(root)}.`); + console.log(); - process.chdir(root) + process.chdir(root); - const packageJsonPath = path.join(root, 'package.json') - let hasPackageJson = false + const packageJsonPath = path.join(root, "package.json"); + let hasPackageJson = false; if (example) { /** @@ -145,64 +145,64 @@ export async function createApp({ */ try { if (repoInfo) { - const repoInfo2 = repoInfo + const repoInfo2 = repoInfo; console.log( `Downloading files from repo ${cyan( - example - )}. This might take a moment.` - ) - console.log() + example, + )}. This might take a moment.`, + ); + console.log(); await retry(() => downloadAndExtractRepo(root, repoInfo2), { retries: 3, - }) + }); } else { console.log( `Downloading files for example ${cyan( - example - )}. This might take a moment.` - ) - console.log() + example, + )}. This might take a moment.`, + ); + console.log(); await retry(() => downloadAndExtractExample(root, example), { retries: 3, - }) + }); } } catch (reason) { function isErrorLike(err: unknown): err is { message: string } { return ( - typeof err === 'object' && + typeof err === "object" && err !== null && - typeof (err as { message?: unknown }).message === 'string' - ) + typeof (err as { message?: unknown }).message === "string" + ); } throw new DownloadError( - isErrorLike(reason) ? reason.message : reason + '' - ) + isErrorLike(reason) ? reason.message : reason + "", + ); } // Copy `.gitignore` if the application did not provide one - const ignorePath = path.join(root, '.gitignore') + const ignorePath = path.join(root, ".gitignore"); if (!fs.existsSync(ignorePath)) { fs.copyFileSync( - getTemplateFile({ template, mode, file: 'gitignore' }), - ignorePath - ) + getTemplateFile({ template, mode, file: "gitignore" }), + ignorePath, + ); } // Copy `next-env.d.ts` to any example that is typescript - const tsconfigPath = path.join(root, 'tsconfig.json') + const tsconfigPath = path.join(root, "tsconfig.json"); if (fs.existsSync(tsconfigPath)) { fs.copyFileSync( - getTemplateFile({ template, mode: 'nextjs', file: 'next-env.d.ts' }), - path.join(root, 'next-env.d.ts') - ) + getTemplateFile({ template, mode: "nextjs", file: "next-env.d.ts" }), + path.join(root, "next-env.d.ts"), + ); } - hasPackageJson = fs.existsSync(packageJsonPath) + hasPackageJson = fs.existsSync(packageJsonPath); if (hasPackageJson) { - console.log('Installing packages. This might take a couple of minutes.') - console.log() + console.log("Installing packages. This might take a couple of minutes."); + console.log(); - await install(packageManager, isOnline) - console.log() + await install(packageManager, isOnline); + console.log(); } } else { /** @@ -220,39 +220,39 @@ export async function createApp({ eslint, srcDir, importAlias, - }) + }); } if (tryGitInit(root)) { - console.log('Initialized a git repository.') - console.log() + console.log("Initialized a git repository."); + console.log(); } - let cdpath: string + let cdpath: string; if (path.join(originalDirectory, appName) === appPath) { - cdpath = appName + cdpath = appName; } else { - cdpath = appPath + cdpath = appPath; } - console.log(`${green('Success!')} Created ${appName} at ${appPath}`) + console.log(`${green("Success!")} Created ${appName} at ${appPath}`); if (hasPackageJson) { - console.log('Inside that directory, you can run several commands:') - console.log() - console.log(cyan(` ${packageManager} ${useYarn ? '' : 'run '}dev`)) - console.log(' Starts the development server.') - console.log() - console.log(cyan(` ${packageManager} ${useYarn ? '' : 'run '}build`)) - console.log(' Builds the app for production.') - console.log() - console.log(cyan(` ${packageManager} start`)) - console.log(' Runs the built app in production mode.') - console.log() - console.log('We suggest that you begin by typing:') - console.log() - console.log(cyan(' cd'), cdpath) - console.log(` ${cyan(`${packageManager} ${useYarn ? '' : 'run '}dev`)}`) + console.log("Inside that directory, you can run several commands:"); + console.log(); + console.log(cyan(` ${packageManager} ${useYarn ? "" : "run "}dev`)); + console.log(" Starts the development server."); + console.log(); + console.log(cyan(` ${packageManager} ${useYarn ? "" : "run "}build`)); + console.log(" Builds the app for production."); + console.log(); + console.log(cyan(` ${packageManager} start`)); + console.log(" Runs the built app in production mode."); + console.log(); + console.log("We suggest that you begin by typing:"); + console.log(); + console.log(cyan(" cd"), cdpath); + console.log(` ${cyan(`${packageManager} ${useYarn ? "" : "run "}dev`)}`); } - console.log() + console.log(); } diff --git a/packages/create-llama/helpers/copy.ts b/packages/create-llama/helpers/copy.ts index 407b000ab5..a5b722ba34 100644 --- a/packages/create-llama/helpers/copy.ts +++ b/packages/create-llama/helpers/copy.ts @@ -1,25 +1,25 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { async as glob } from 'fast-glob' -import path from 'path' -import fs from 'fs' +import { async as glob } from "fast-glob"; +import fs from "fs"; +import path from "path"; interface CopyOption { - cwd?: string - rename?: (basename: string) => string - parents?: boolean + cwd?: string; + rename?: (basename: string) => string; + parents?: boolean; } -const identity = (x: string) => x +const identity = (x: string) => x; export const copy = async ( src: string | string[], dest: string, - { cwd, rename = identity, parents = true }: CopyOption = {} + { cwd, rename = identity, parents = true }: CopyOption = {}, ) => { - const source = typeof src === 'string' ? [src] : src + const source = typeof src === "string" ? [src] : src; if (source.length === 0 || !dest) { - throw new TypeError('`src` and `dest` are required') + throw new TypeError("`src` and `dest` are required"); } const sourceFiles = await glob(source, { @@ -27,24 +27,24 @@ export const copy = async ( dot: true, absolute: false, stats: false, - }) + }); - const destRelativeToCwd = cwd ? path.resolve(cwd, dest) : dest + const destRelativeToCwd = cwd ? path.resolve(cwd, dest) : dest; return Promise.all( sourceFiles.map(async (p) => { - const dirname = path.dirname(p) - const basename = rename(path.basename(p)) + const dirname = path.dirname(p); + const basename = rename(path.basename(p)); - const from = cwd ? path.resolve(cwd, p) : p + const from = cwd ? path.resolve(cwd, p) : p; const to = parents ? path.join(destRelativeToCwd, dirname, basename) - : path.join(destRelativeToCwd, basename) + : path.join(destRelativeToCwd, basename); // Ensure the destination directory exists - await fs.promises.mkdir(path.dirname(to), { recursive: true }) + await fs.promises.mkdir(path.dirname(to), { recursive: true }); - return fs.promises.copyFile(from, to) - }) - ) -} + return fs.promises.copyFile(from, to); + }), + ); +}; diff --git a/packages/create-llama/helpers/examples.ts b/packages/create-llama/helpers/examples.ts index 97bbacf27e..b8c74be6e6 100644 --- a/packages/create-llama/helpers/examples.ts +++ b/packages/create-llama/helpers/examples.ts @@ -1,32 +1,34 @@ /* eslint-disable import/no-extraneous-dependencies */ -import got from 'got' -import tar from 'tar' -import { Stream } from 'stream' -import { promisify } from 'util' -import { join } from 'path' -import { tmpdir } from 'os' -import { createWriteStream, promises as fs } from 'fs' +import { createWriteStream, promises as fs } from "fs"; +import got from "got"; +import { tmpdir } from "os"; +import { join } from "path"; +import { Stream } from "stream"; +import tar from "tar"; +import { promisify } from "util"; -const pipeline = promisify(Stream.pipeline) +const pipeline = promisify(Stream.pipeline); export type RepoInfo = { - username: string - name: string - branch: string - filePath: string -} + username: string; + name: string; + branch: string; + filePath: string; +}; export async function isUrlOk(url: string): Promise { - const res = await got.head(url).catch((e) => e) - return res.statusCode === 200 + const res = await got.head(url).catch((e) => e); + return res.statusCode === 200; } export async function getRepoInfo( url: URL, - examplePath?: string + examplePath?: string, ): Promise { - const [, username, name, t, _branch, ...file] = url.pathname.split('/') - const filePath = examplePath ? examplePath.replace(/^\//, '') : file.join('/') + const [, username, name, t, _branch, ...file] = url.pathname.split("/"); + const filePath = examplePath + ? examplePath.replace(/^\//, "") + : file.join("/"); if ( // Support repos whose entire purpose is to be a LlamaIndex example, e.g. @@ -35,25 +37,25 @@ export async function getRepoInfo( // Support GitHub URL that ends with a trailing slash, e.g. // https://github.com/:username/:my-cool-nextjs-example-repo-name/ // In this case "t" will be an empty string while the next part "_branch" will be undefined - (t === '' && _branch === undefined) + (t === "" && _branch === undefined) ) { const infoResponse = await got( - `https://api.github.com/repos/${username}/${name}` - ).catch((e) => e) + `https://api.github.com/repos/${username}/${name}`, + ).catch((e) => e); if (infoResponse.statusCode !== 200) { - return + return; } - const info = JSON.parse(infoResponse.body) - return { username, name, branch: info['default_branch'], filePath } + const info = JSON.parse(infoResponse.body); + return { username, name, branch: info["default_branch"], filePath }; } // If examplePath is available, the branch name takes the entire path const branch = examplePath - ? `${_branch}/${file.join('/')}`.replace(new RegExp(`/${filePath}|/$`), '') - : _branch + ? `${_branch}/${file.join("/")}`.replace(new RegExp(`/${filePath}|/$`), "") + : _branch; - if (username && name && branch && t === 'tree') { - return { username, name, branch, filePath } + if (username && name && branch && t === "tree") { + return { username, name, branch, filePath }; } } @@ -63,69 +65,69 @@ export function hasRepo({ branch, filePath, }: RepoInfo): Promise { - const contentsUrl = `https://api.github.com/repos/${username}/${name}/contents` - const packagePath = `${filePath ? `/${filePath}` : ''}/package.json` + const contentsUrl = `https://api.github.com/repos/${username}/${name}/contents`; + const packagePath = `${filePath ? `/${filePath}` : ""}/package.json`; - return isUrlOk(contentsUrl + packagePath + `?ref=${branch}`) + return isUrlOk(contentsUrl + packagePath + `?ref=${branch}`); } export function existsInRepo(nameOrUrl: string): Promise { try { - const url = new URL(nameOrUrl) - return isUrlOk(url.href) + const url = new URL(nameOrUrl); + return isUrlOk(url.href); } catch { return isUrlOk( `https://api.github.com/repos/vercel/next.js/contents/examples/${encodeURIComponent( - nameOrUrl - )}` - ) + nameOrUrl, + )}`, + ); } } async function downloadTar(url: string) { - const tempFile = join(tmpdir(), `next.js-cna-example.temp-${Date.now()}`) - await pipeline(got.stream(url), createWriteStream(tempFile)) - return tempFile + const tempFile = join(tmpdir(), `next.js-cna-example.temp-${Date.now()}`); + await pipeline(got.stream(url), createWriteStream(tempFile)); + return tempFile; } export async function downloadAndExtractRepo( root: string, - { username, name, branch, filePath }: RepoInfo + { username, name, branch, filePath }: RepoInfo, ) { const tempFile = await downloadTar( - `https://codeload.github.com/${username}/${name}/tar.gz/${branch}` - ) + `https://codeload.github.com/${username}/${name}/tar.gz/${branch}`, + ); await tar.x({ file: tempFile, cwd: root, - strip: filePath ? filePath.split('/').length + 1 : 1, + strip: filePath ? filePath.split("/").length + 1 : 1, filter: (p) => p.startsWith( - `${name}-${branch.replace(/\//g, '-')}${ - filePath ? `/${filePath}/` : '/' - }` + `${name}-${branch.replace(/\//g, "-")}${ + filePath ? `/${filePath}/` : "/" + }`, ), - }) + }); - await fs.unlink(tempFile) + await fs.unlink(tempFile); } export async function downloadAndExtractExample(root: string, name: string) { - if (name === '__internal-testing-retry') { - throw new Error('This is an internal example for testing the CLI.') + if (name === "__internal-testing-retry") { + throw new Error("This is an internal example for testing the CLI."); } const tempFile = await downloadTar( - 'https://codeload.github.com/vercel/next.js/tar.gz/canary' - ) + "https://codeload.github.com/vercel/next.js/tar.gz/canary", + ); await tar.x({ file: tempFile, cwd: root, - strip: 2 + name.split('/').length, + strip: 2 + name.split("/").length, filter: (p) => p.includes(`next.js-canary/examples/${name}/`), - }) + }); - await fs.unlink(tempFile) + await fs.unlink(tempFile); } diff --git a/packages/create-llama/helpers/get-pkg-manager.ts b/packages/create-llama/helpers/get-pkg-manager.ts index 20900ebcb9..acc099f713 100644 --- a/packages/create-llama/helpers/get-pkg-manager.ts +++ b/packages/create-llama/helpers/get-pkg-manager.ts @@ -1,19 +1,19 @@ -export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun' +export type PackageManager = "npm" | "pnpm" | "yarn" | "bun"; export function getPkgManager(): PackageManager { - const userAgent = process.env.npm_config_user_agent || '' + const userAgent = process.env.npm_config_user_agent || ""; - if (userAgent.startsWith('yarn')) { - return 'yarn' + if (userAgent.startsWith("yarn")) { + return "yarn"; } - if (userAgent.startsWith('pnpm')) { - return 'pnpm' + if (userAgent.startsWith("pnpm")) { + return "pnpm"; } - if (userAgent.startsWith('bun')) { - return 'bun' + if (userAgent.startsWith("bun")) { + return "bun"; } - return 'npm' + return "npm"; } diff --git a/packages/create-llama/helpers/git.ts b/packages/create-llama/helpers/git.ts index 79eb9115a1..fc27e60995 100644 --- a/packages/create-llama/helpers/git.ts +++ b/packages/create-llama/helpers/git.ts @@ -1,58 +1,58 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { execSync } from 'child_process' -import path from 'path' -import fs from 'fs' +import { execSync } from "child_process"; +import fs from "fs"; +import path from "path"; function isInGitRepository(): boolean { try { - execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }) - return true + execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" }); + return true; } catch (_) {} - return false + return false; } function isInMercurialRepository(): boolean { try { - execSync('hg --cwd . root', { stdio: 'ignore' }) - return true + execSync("hg --cwd . root", { stdio: "ignore" }); + return true; } catch (_) {} - return false + return false; } function isDefaultBranchSet(): boolean { try { - execSync('git config init.defaultBranch', { stdio: 'ignore' }) - return true + execSync("git config init.defaultBranch", { stdio: "ignore" }); + return true; } catch (_) {} - return false + return false; } export function tryGitInit(root: string): boolean { - let didInit = false + let didInit = false; try { - execSync('git --version', { stdio: 'ignore' }) + execSync("git --version", { stdio: "ignore" }); if (isInGitRepository() || isInMercurialRepository()) { - return false + return false; } - execSync('git init', { stdio: 'ignore' }) - didInit = true + execSync("git init", { stdio: "ignore" }); + didInit = true; if (!isDefaultBranchSet()) { - execSync('git checkout -b main', { stdio: 'ignore' }) + execSync("git checkout -b main", { stdio: "ignore" }); } - execSync('git add -A', { stdio: 'ignore' }) + execSync("git add -A", { stdio: "ignore" }); execSync('git commit -m "Initial commit from Create Next App"', { - stdio: 'ignore', - }) - return true + stdio: "ignore", + }); + return true; } catch (e) { if (didInit) { try { - fs.rmSync(path.join(root, '.git'), { recursive: true, force: true }) + fs.rmSync(path.join(root, ".git"), { recursive: true, force: true }); } catch (_) {} } - return false + return false; } } diff --git a/packages/create-llama/helpers/install.ts b/packages/create-llama/helpers/install.ts index 911573b967..27d8f8132e 100644 --- a/packages/create-llama/helpers/install.ts +++ b/packages/create-llama/helpers/install.ts @@ -1,7 +1,7 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { yellow } from 'picocolors' -import spawn from 'cross-spawn' -import type { PackageManager } from './get-pkg-manager' +import spawn from "cross-spawn"; +import { yellow } from "picocolors"; +import type { PackageManager } from "./get-pkg-manager"; /** * Spawn a package manager installation based on user preference. @@ -12,14 +12,14 @@ export async function install( /** Indicate which package manager to use. */ packageManager: PackageManager, /** Indicate whether there is an active Internet connection.*/ - isOnline: boolean + isOnline: boolean, ): Promise { - let args: string[] = ['install'] + let args: string[] = ["install"]; if (!isOnline) { console.log( - yellow('You appear to be offline.\nFalling back to the local cache.') - ) - args.push('--offline') + yellow("You appear to be offline.\nFalling back to the local cache."), + ); + args.push("--offline"); } /** * Return a Promise that resolves once the installation is finished. @@ -29,22 +29,22 @@ export async function install( * Spawn the installation process. */ const child = spawn(packageManager, args, { - stdio: 'inherit', + stdio: "inherit", env: { ...process.env, - ADBLOCK: '1', + ADBLOCK: "1", // we set NODE_ENV to development as pnpm skips dev // dependencies when production - NODE_ENV: 'development', - DISABLE_OPENCOLLECTIVE: '1', + NODE_ENV: "development", + DISABLE_OPENCOLLECTIVE: "1", }, - }) - child.on('close', (code) => { + }); + child.on("close", (code) => { if (code !== 0) { - reject({ command: `${packageManager} ${args.join(' ')}` }) - return + reject({ command: `${packageManager} ${args.join(" ")}` }); + return; } - resolve() - }) - }) + resolve(); + }); + }); } diff --git a/packages/create-llama/helpers/is-folder-empty.ts b/packages/create-llama/helpers/is-folder-empty.ts index ad89ec22fb..927a344c00 100644 --- a/packages/create-llama/helpers/is-folder-empty.ts +++ b/packages/create-llama/helpers/is-folder-empty.ts @@ -1,62 +1,62 @@ /* eslint-disable import/no-extraneous-dependencies */ -import { green, blue } from 'picocolors' -import fs from 'fs' -import path from 'path' +import fs from "fs"; +import path from "path"; +import { blue, green } from "picocolors"; export function isFolderEmpty(root: string, name: string): boolean { const validFiles = [ - '.DS_Store', - '.git', - '.gitattributes', - '.gitignore', - '.gitlab-ci.yml', - '.hg', - '.hgcheck', - '.hgignore', - '.idea', - '.npmignore', - '.travis.yml', - 'LICENSE', - 'Thumbs.db', - 'docs', - 'mkdocs.yml', - 'npm-debug.log', - 'yarn-debug.log', - 'yarn-error.log', - 'yarnrc.yml', - '.yarn', - ] + ".DS_Store", + ".git", + ".gitattributes", + ".gitignore", + ".gitlab-ci.yml", + ".hg", + ".hgcheck", + ".hgignore", + ".idea", + ".npmignore", + ".travis.yml", + "LICENSE", + "Thumbs.db", + "docs", + "mkdocs.yml", + "npm-debug.log", + "yarn-debug.log", + "yarn-error.log", + "yarnrc.yml", + ".yarn", + ]; const conflicts = fs .readdirSync(root) .filter((file) => !validFiles.includes(file)) // Support IntelliJ IDEA-based editors - .filter((file) => !/\.iml$/.test(file)) + .filter((file) => !/\.iml$/.test(file)); if (conflicts.length > 0) { console.log( - `The directory ${green(name)} contains files that could conflict:` - ) - console.log() + `The directory ${green(name)} contains files that could conflict:`, + ); + console.log(); for (const file of conflicts) { try { - const stats = fs.lstatSync(path.join(root, file)) + const stats = fs.lstatSync(path.join(root, file)); if (stats.isDirectory()) { - console.log(` ${blue(file)}/`) + console.log(` ${blue(file)}/`); } else { - console.log(` ${file}`) + console.log(` ${file}`); } } catch { - console.log(` ${file}`) + console.log(` ${file}`); } } - console.log() + console.log(); console.log( - 'Either try using a new directory name, or remove the files listed above.' - ) - console.log() - return false + "Either try using a new directory name, or remove the files listed above.", + ); + console.log(); + return false; } - return true + return true; } diff --git a/packages/create-llama/helpers/is-online.ts b/packages/create-llama/helpers/is-online.ts index c2f3f83a93..eab6980053 100644 --- a/packages/create-llama/helpers/is-online.ts +++ b/packages/create-llama/helpers/is-online.ts @@ -1,40 +1,40 @@ -import { execSync } from 'child_process' -import dns from 'dns' -import url from 'url' +import { execSync } from "child_process"; +import dns from "dns"; +import url from "url"; function getProxy(): string | undefined { if (process.env.https_proxy) { - return process.env.https_proxy + return process.env.https_proxy; } try { - const httpsProxy = execSync('npm config get https-proxy').toString().trim() - return httpsProxy !== 'null' ? httpsProxy : undefined + const httpsProxy = execSync("npm config get https-proxy").toString().trim(); + return httpsProxy !== "null" ? httpsProxy : undefined; } catch (e) { - return + return; } } export function getOnline(): Promise { return new Promise((resolve) => { - dns.lookup('registry.yarnpkg.com', (registryErr) => { + dns.lookup("registry.yarnpkg.com", (registryErr) => { if (!registryErr) { - return resolve(true) + return resolve(true); } - const proxy = getProxy() + const proxy = getProxy(); if (!proxy) { - return resolve(false) + return resolve(false); } - const { hostname } = url.parse(proxy) + const { hostname } = url.parse(proxy); if (!hostname) { - return resolve(false) + return resolve(false); } dns.lookup(hostname, (proxyErr) => { - resolve(proxyErr == null) - }) - }) - }) + resolve(proxyErr == null); + }); + }); + }); } diff --git a/packages/create-llama/helpers/is-writeable.ts b/packages/create-llama/helpers/is-writeable.ts index 0b9e9abb4f..fa29d60558 100644 --- a/packages/create-llama/helpers/is-writeable.ts +++ b/packages/create-llama/helpers/is-writeable.ts @@ -1,10 +1,10 @@ -import fs from 'fs' +import fs from "fs"; export async function isWriteable(directory: string): Promise { try { - await fs.promises.access(directory, (fs.constants || fs).W_OK) - return true + await fs.promises.access(directory, (fs.constants || fs).W_OK); + return true; } catch (err) { - return false + return false; } } diff --git a/packages/create-llama/helpers/make-dir.ts b/packages/create-llama/helpers/make-dir.ts index 5b12b996f0..2c258fd6b5 100644 --- a/packages/create-llama/helpers/make-dir.ts +++ b/packages/create-llama/helpers/make-dir.ts @@ -1,8 +1,8 @@ -import fs from 'fs' +import fs from "fs"; export function makeDir( root: string, - options = { recursive: true } + options = { recursive: true }, ): Promise { - return fs.promises.mkdir(root, options) + return fs.promises.mkdir(root, options); } diff --git a/packages/create-llama/helpers/validate-pkg.ts b/packages/create-llama/helpers/validate-pkg.ts index cff613818f..68317653c8 100644 --- a/packages/create-llama/helpers/validate-pkg.ts +++ b/packages/create-llama/helpers/validate-pkg.ts @@ -1,13 +1,13 @@ // eslint-disable-next-line import/no-extraneous-dependencies -import validateProjectName from 'validate-npm-package-name' +import validateProjectName from "validate-npm-package-name"; export function validateNpmName(name: string): { - valid: boolean - problems?: string[] + valid: boolean; + problems?: string[]; } { - const nameValidation = validateProjectName(name) + const nameValidation = validateProjectName(name); if (nameValidation.validForNewPackages) { - return { valid: true } + return { valid: true }; } return { @@ -16,5 +16,5 @@ export function validateNpmName(name: string): { ...(nameValidation.errors || []), ...(nameValidation.warnings || []), ], - } + }; } diff --git a/packages/create-llama/index.ts b/packages/create-llama/index.ts index 6c5bafeaec..b74215f98c 100644 --- a/packages/create-llama/index.ts +++ b/packages/create-llama/index.ts @@ -1,207 +1,207 @@ #!/usr/bin/env node /* eslint-disable import/no-extraneous-dependencies */ -import { cyan, green, red, yellow, bold, blue } from 'picocolors' -import Commander from 'commander' -import Conf from 'conf' -import path from 'path' -import prompts from 'prompts' -import checkForUpdate from 'update-check' -import { createApp, DownloadError } from './create-app' -import { getPkgManager } from './helpers/get-pkg-manager' -import { validateNpmName } from './helpers/validate-pkg' -import packageJson from './package.json' -import ciInfo from 'ci-info' -import { isFolderEmpty } from './helpers/is-folder-empty' -import fs from 'fs' - -let projectPath: string = '' - -const handleSigTerm = () => process.exit(0) - -process.on('SIGINT', handleSigTerm) -process.on('SIGTERM', handleSigTerm) +import ciInfo from "ci-info"; +import Commander from "commander"; +import Conf from "conf"; +import fs from "fs"; +import path from "path"; +import { blue, bold, cyan, green, red, yellow } from "picocolors"; +import prompts from "prompts"; +import checkForUpdate from "update-check"; +import { createApp, DownloadError } from "./create-app"; +import { getPkgManager } from "./helpers/get-pkg-manager"; +import { isFolderEmpty } from "./helpers/is-folder-empty"; +import { validateNpmName } from "./helpers/validate-pkg"; +import packageJson from "./package.json"; + +let projectPath: string = ""; + +const handleSigTerm = () => process.exit(0); + +process.on("SIGINT", handleSigTerm); +process.on("SIGTERM", handleSigTerm); const onPromptState = (state: any) => { if (state.aborted) { // If we don't re-enable the terminal cursor before exiting // the program, the cursor will remain hidden - process.stdout.write('\x1B[?25h') - process.stdout.write('\n') - process.exit(1) + process.stdout.write("\x1B[?25h"); + process.stdout.write("\n"); + process.exit(1); } -} +}; const program = new Commander.Command(packageJson.name) .version(packageJson.version) - .arguments('') - .usage(`${green('')} [options]`) + .arguments("") + .usage(`${green("")} [options]`) .action((name) => { - projectPath = name + projectPath = name; }) .option( - '--eslint', + "--eslint", ` Initialize with eslint config. -` +`, ) .option( - '--import-alias ', + "--import-alias ", ` Specify import alias to use (default "@/*"). -` +`, ) .option( - '--use-npm', + "--use-npm", ` Explicitly tell the CLI to bootstrap the application using npm -` +`, ) .option( - '--use-pnpm', + "--use-pnpm", ` Explicitly tell the CLI to bootstrap the application using pnpm -` +`, ) .option( - '--use-yarn', + "--use-yarn", ` Explicitly tell the CLI to bootstrap the application using Yarn -` +`, ) .option( - '--use-bun', + "--use-bun", ` Explicitly tell the CLI to bootstrap the application using Bun -` +`, ) .option( - '-e, --example [name]|[github-url]', + "-e, --example [name]|[github-url]", ` An example to bootstrap the app with. You can use an example name from the official LlamaIndex repo or a GitHub URL. The URL can use any branch and/or subdirectory -` +`, ) .option( - '--example-path ', + "--example-path ", ` In a rare case, your GitHub URL might contain a branch name with a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). In this case, you must specify the path to the example separately: --example-path foo/bar -` +`, ) .option( - '--reset-preferences', + "--reset-preferences", ` Explicitly tell the CLI to reset any stored preferences -` +`, ) .allowUnknownOption() - .parse(process.argv) + .parse(process.argv); const packageManager = !!program.useNpm - ? 'npm' + ? "npm" : !!program.usePnpm - ? 'pnpm' + ? "pnpm" : !!program.useYarn - ? 'yarn' + ? "yarn" : !!program.useBun - ? 'bun' - : getPkgManager() + ? "bun" + : getPkgManager(); async function run(): Promise { - const conf = new Conf({ projectName: 'create-llama' }) + const conf = new Conf({ projectName: "create-llama" }); if (program.resetPreferences) { - conf.clear() - console.log(`Preferences reset successfully`) - return + conf.clear(); + console.log(`Preferences reset successfully`); + return; } - if (typeof projectPath === 'string') { - projectPath = projectPath.trim() + if (typeof projectPath === "string") { + projectPath = projectPath.trim(); } if (!projectPath) { const res = await prompts({ onState: onPromptState, - type: 'text', - name: 'path', - message: 'What is your project named?', - initial: 'my-app', + type: "text", + name: "path", + message: "What is your project named?", + initial: "my-app", validate: (name) => { - const validation = validateNpmName(path.basename(path.resolve(name))) + const validation = validateNpmName(path.basename(path.resolve(name))); if (validation.valid) { - return true + return true; } - return 'Invalid project name: ' + validation.problems![0] + return "Invalid project name: " + validation.problems![0]; }, - }) + }); - if (typeof res.path === 'string') { - projectPath = res.path.trim() + if (typeof res.path === "string") { + projectPath = res.path.trim(); } } if (!projectPath) { console.log( - '\nPlease specify the project directory:\n' + - ` ${cyan(program.name())} ${green('')}\n` + - 'For example:\n' + - ` ${cyan(program.name())} ${green('my-next-app')}\n\n` + - `Run ${cyan(`${program.name()} --help`)} to see all options.` - ) - process.exit(1) + "\nPlease specify the project directory:\n" + + ` ${cyan(program.name())} ${green("")}\n` + + "For example:\n" + + ` ${cyan(program.name())} ${green("my-next-app")}\n\n` + + `Run ${cyan(`${program.name()} --help`)} to see all options.`, + ); + process.exit(1); } - const resolvedProjectPath = path.resolve(projectPath) - const projectName = path.basename(resolvedProjectPath) + const resolvedProjectPath = path.resolve(projectPath); + const projectName = path.basename(resolvedProjectPath); - const { valid, problems } = validateNpmName(projectName) + const { valid, problems } = validateNpmName(projectName); if (!valid) { console.error( `Could not create a project called ${red( - `"${projectName}"` - )} because of npm naming restrictions:` - ) + `"${projectName}"`, + )} because of npm naming restrictions:`, + ); - problems!.forEach((p) => console.error(` ${red(bold('*'))} ${p}`)) - process.exit(1) + problems!.forEach((p) => console.error(` ${red(bold("*"))} ${p}`)); + process.exit(1); } if (program.example === true) { console.error( - 'Please provide an example name or url, otherwise remove the example option.' - ) - process.exit(1) + "Please provide an example name or url, otherwise remove the example option.", + ); + process.exit(1); } /** * Verify the project dir is empty or doesn't exist */ - const root = path.resolve(resolvedProjectPath) - const appName = path.basename(root) - const folderExists = fs.existsSync(root) + const root = path.resolve(resolvedProjectPath); + const appName = path.basename(root); + const folderExists = fs.existsSync(root); if (folderExists && !isFolderEmpty(root, appName)) { - process.exit(1) + process.exit(1); } - const example = typeof program.example === 'string' && program.example.trim() - const preferences = (conf.get('preferences') || {}) as Record< + const example = typeof program.example === "string" && program.example.trim(); + const preferences = (conf.get("preferences") || {}) as Record< string, boolean | string - > + >; /** * If the user does not provide the necessary flags, prompt them for whether * to use TS or JS. @@ -213,71 +213,71 @@ async function run(): Promise { tailwind: true, app: true, srcDir: false, - importAlias: '@/*', + importAlias: "@/*", customizeImportAlias: false, - } + }; const getPrefOrDefault = (field: string) => - preferences[field] ?? defaults[field] + preferences[field] ?? defaults[field]; if ( - !process.argv.includes('--eslint') && - !process.argv.includes('--no-eslint') + !process.argv.includes("--eslint") && + !process.argv.includes("--no-eslint") ) { if (ciInfo.isCI) { - program.eslint = getPrefOrDefault('eslint') + program.eslint = getPrefOrDefault("eslint"); } else { - const styledEslint = blue('ESLint') + const styledEslint = blue("ESLint"); const { eslint } = await prompts({ onState: onPromptState, - type: 'toggle', - name: 'eslint', + type: "toggle", + name: "eslint", message: `Would you like to use ${styledEslint}?`, - initial: getPrefOrDefault('eslint'), - active: 'Yes', - inactive: 'No', - }) - program.eslint = Boolean(eslint) - preferences.eslint = Boolean(eslint) + initial: getPrefOrDefault("eslint"), + active: "Yes", + inactive: "No", + }); + program.eslint = Boolean(eslint); + preferences.eslint = Boolean(eslint); } } if ( - typeof program.importAlias !== 'string' || + typeof program.importAlias !== "string" || !program.importAlias.length ) { if (ciInfo.isCI) { // We don't use preferences here because the default value is @/* regardless of existing preferences - program.importAlias = defaults.importAlias + program.importAlias = defaults.importAlias; } else { - const styledImportAlias = blue('import alias') + const styledImportAlias = blue("import alias"); const { customizeImportAlias } = await prompts({ onState: onPromptState, - type: 'toggle', - name: 'customizeImportAlias', + type: "toggle", + name: "customizeImportAlias", message: `Would you like to customize the default ${styledImportAlias} (${defaults.importAlias})?`, - initial: getPrefOrDefault('customizeImportAlias'), - active: 'Yes', - inactive: 'No', - }) + initial: getPrefOrDefault("customizeImportAlias"), + active: "Yes", + inactive: "No", + }); if (!customizeImportAlias) { // We don't use preferences here because the default value is @/* regardless of existing preferences - program.importAlias = defaults.importAlias + program.importAlias = defaults.importAlias; } else { const { importAlias } = await prompts({ onState: onPromptState, - type: 'text', - name: 'importAlias', + type: "text", + name: "importAlias", message: `What ${styledImportAlias} would you like configured?`, - initial: getPrefOrDefault('importAlias'), + initial: getPrefOrDefault("importAlias"), validate: (value) => /.+\/\*/.test(value) ? true - : 'Import alias must follow the pattern /*', - }) - program.importAlias = importAlias - preferences.importAlias = importAlias + : "Import alias must follow the pattern /*", + }); + program.importAlias = importAlias; + preferences.importAlias = importAlias; } } } @@ -287,29 +287,29 @@ async function run(): Promise { await createApp({ appPath: resolvedProjectPath, packageManager, - example: example && example !== 'default' ? example : undefined, + example: example && example !== "default" ? example : undefined, examplePath: program.examplePath, tailwind: true, eslint: program.eslint, srcDir: program.srcDir, importAlias: program.importAlias, - }) + }); } catch (reason) { if (!(reason instanceof DownloadError)) { - throw reason + throw reason; } const res = await prompts({ onState: onPromptState, - type: 'confirm', - name: 'builtin', + type: "confirm", + name: "builtin", message: `Could not download "${example}" because of a connectivity issue between your machine and GitHub.\n` + `Do you want to use the default template instead?`, initial: true, - }) + }); if (!res.builtin) { - throw reason + throw reason; } await createApp({ @@ -319,35 +319,35 @@ async function run(): Promise { tailwind: true, srcDir: program.srcDir, importAlias: program.importAlias, - }) + }); } - conf.set('preferences', preferences) + conf.set("preferences", preferences); } -const update = checkForUpdate(packageJson).catch(() => null) +const update = checkForUpdate(packageJson).catch(() => null); async function notifyUpdate(): Promise { try { - const res = await update + const res = await update; if (res?.latest) { const updateMessage = - packageManager === 'yarn' - ? 'yarn global add create-llama' - : packageManager === 'pnpm' - ? 'pnpm add -g create-llama' - : packageManager === 'bun' - ? 'bun add -g create-llama' - : 'npm i -g create-llama' + packageManager === "yarn" + ? "yarn global add create-llama" + : packageManager === "pnpm" + ? "pnpm add -g create-llama" + : packageManager === "bun" + ? "bun add -g create-llama" + : "npm i -g create-llama"; console.log( - yellow(bold('A new version of `create-llama` is available!')) + - '\n' + - 'You can update by running: ' + + yellow(bold("A new version of `create-llama` is available!")) + + "\n" + + "You can update by running: " + cyan(updateMessage) + - '\n' - ) + "\n", + ); } - process.exit() + process.exit(); } catch { // ignore error } @@ -356,19 +356,19 @@ async function notifyUpdate(): Promise { run() .then(notifyUpdate) .catch(async (reason) => { - console.log() - console.log('Aborting installation.') + console.log(); + console.log("Aborting installation."); if (reason.command) { - console.log(` ${cyan(reason.command)} has failed.`) + console.log(` ${cyan(reason.command)} has failed.`); } else { console.log( - red('Unexpected error. Please report it as a bug:') + '\n', - reason - ) + red("Unexpected error. Please report it as a bug:") + "\n", + reason, + ); } - console.log() + console.log(); - await notifyUpdate() + await notifyUpdate(); - process.exit(1) - }) + process.exit(1); + }); diff --git a/packages/create-llama/templates/index.ts b/packages/create-llama/templates/index.ts index df407f7ac6..e167f6450f 100644 --- a/packages/create-llama/templates/index.ts +++ b/packages/create-llama/templates/index.ts @@ -1,17 +1,17 @@ -import { install } from '../helpers/install' -import { makeDir } from '../helpers/make-dir' -import { copy } from '../helpers/copy' +import { copy } from "../helpers/copy"; +import { install } from "../helpers/install"; +import { makeDir } from "../helpers/make-dir"; -import { async as glob } from 'fast-glob' -import os from 'os' -import fs from 'fs/promises' -import path from 'path' -import { cyan, bold } from 'picocolors' -import { Sema } from 'async-sema' +import { Sema } from "async-sema"; +import { async as glob } from "fast-glob"; +import fs from "fs/promises"; +import os from "os"; +import path from "path"; +import { bold, cyan } from "picocolors"; -import { GetTemplateFileArgs, InstallTemplateArgs } from './types' +import { GetTemplateFileArgs, InstallTemplateArgs } from "./types"; -const NEXT_VERSION = '13.5.6'; +const NEXT_VERSION = "13.5.6"; /** * Get the file path for a given file in a template, e.g. "next.config.js". @@ -21,10 +21,10 @@ export const getTemplateFile = ({ mode, file, }: GetTemplateFileArgs): string => { - return path.join(__dirname, template, mode, file) -} + return path.join(__dirname, template, mode, file); +}; -export const SRC_DIR_NAMES = ['app', 'pages', 'styles'] +export const SRC_DIR_NAMES = ["app", "pages", "styles"]; /** * Install a LlamaIndex internal template to a given `root` directory. @@ -41,208 +41,199 @@ export const installTemplate = async ({ srcDir, importAlias, }: InstallTemplateArgs) => { - console.log(bold(`Using ${packageManager}.`)) + console.log(bold(`Using ${packageManager}.`)); /** * Copy the template files to the target directory. */ - console.log('\nInitializing project with template:', template, '\n') - const templatePath = path.join(__dirname, template, mode) - const copySource = ['**'] - if (!eslint) copySource.push('!eslintrc.json') + console.log("\nInitializing project with template:", template, "\n"); + const templatePath = path.join(__dirname, template, mode); + const copySource = ["**"]; + if (!eslint) copySource.push("!eslintrc.json"); if (!tailwind) copySource.push( - mode == 'nextjs' ? 'tailwind.config.ts' : '!tailwind.config.js', - '!postcss.config.js' - ) + mode == "nextjs" ? "tailwind.config.ts" : "!tailwind.config.js", + "!postcss.config.js", + ); await copy(copySource, root, { parents: true, cwd: templatePath, rename(name) { switch (name) { - case 'gitignore': - case 'eslintrc.json': { - return `.${name}` + case "gitignore": + case "eslintrc.json": { + return `.${name}`; } // README.md is ignored by webpack-asset-relocator-loader used by ncc: // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227 - case 'README-template.md': { - return 'README.md' + case "README-template.md": { + return "README.md"; } default: { - return name + return name; } } }, - }) + }); - const tsconfigFile = path.join( - root, - 'tsconfig.json' - ) + const tsconfigFile = path.join(root, "tsconfig.json"); await fs.writeFile( tsconfigFile, - (await fs.readFile(tsconfigFile, 'utf8')) + (await fs.readFile(tsconfigFile, "utf8")) .replace( `"@/*": ["./*"]`, - srcDir ? `"@/*": ["./src/*"]` : `"@/*": ["./*"]` + srcDir ? `"@/*": ["./src/*"]` : `"@/*": ["./*"]`, ) - .replace(`"@/*":`, `"${importAlias}":`) - ) + .replace(`"@/*":`, `"${importAlias}":`), + ); // update import alias in any files if not using the default - if (importAlias !== '@/*') { - const files = await glob('**/*', { + if (importAlias !== "@/*") { + const files = await glob("**/*", { cwd: root, dot: true, stats: false, - }) - const writeSema = new Sema(8, { capacity: files.length }) + }); + const writeSema = new Sema(8, { capacity: files.length }); await Promise.all( files.map(async (file) => { // We don't want to modify compiler options in [ts/js]config.json - if (file === 'tsconfig.json' || file === 'jsconfig.json') return - await writeSema.acquire() - const filePath = path.join(root, file) + if (file === "tsconfig.json" || file === "jsconfig.json") return; + await writeSema.acquire(); + const filePath = path.join(root, file); if ((await fs.stat(filePath)).isFile()) { await fs.writeFile( filePath, - ( - await fs.readFile(filePath, 'utf8') - ).replace(`@/`, `${importAlias.replace(/\*/g, '')}`) - ) + (await fs.readFile(filePath, "utf8")).replace( + `@/`, + `${importAlias.replace(/\*/g, "")}`, + ), + ); } - await writeSema.release() - }) - ) + await writeSema.release(); + }), + ); } if (srcDir) { - await makeDir(path.join(root, 'src')) + await makeDir(path.join(root, "src")); await Promise.all( SRC_DIR_NAMES.map(async (file) => { await fs - .rename(path.join(root, file), path.join(root, 'src', file)) + .rename(path.join(root, file), path.join(root, "src", file)) .catch((err) => { - if (err.code !== 'ENOENT') { - throw err + if (err.code !== "ENOENT") { + throw err; } - }) - }) - ) + }); + }), + ); - const isAppTemplate = template.startsWith('app') + const isAppTemplate = template.startsWith("app"); // Change the `Get started by editing pages/index` / `app/page` to include `src` const indexPageFile = path.join( - 'src', - isAppTemplate ? 'app' : 'pages', - `${isAppTemplate ? 'page' : 'index'}.tsx` - ) + "src", + isAppTemplate ? "app" : "pages", + `${isAppTemplate ? "page" : "index"}.tsx`, + ); await fs.writeFile( indexPageFile, - ( - await fs.readFile(indexPageFile, 'utf8') - ).replace( - isAppTemplate ? 'app/page' : 'pages/index', - isAppTemplate ? 'src/app/page' : 'src/pages/index' - ) - ) + (await fs.readFile(indexPageFile, "utf8")).replace( + isAppTemplate ? "app/page" : "pages/index", + isAppTemplate ? "src/app/page" : "src/pages/index", + ), + ); if (tailwind) { - const tailwindConfigFile = path.join( - root, - 'tailwind.config.ts' - ) + const tailwindConfigFile = path.join(root, "tailwind.config.ts"); await fs.writeFile( tailwindConfigFile, - ( - await fs.readFile(tailwindConfigFile, 'utf8') - ).replace( + (await fs.readFile(tailwindConfigFile, "utf8")).replace( /\.\/(\w+)\/\*\*\/\*\.\{js,ts,jsx,tsx,mdx\}/g, - './src/$1/**/*.{js,ts,jsx,tsx,mdx}' - ) - ) + "./src/$1/**/*.{js,ts,jsx,tsx,mdx}", + ), + ); } } /** Create a package.json for the new project and write it to disk. */ const packageJson: any = { name: appName, - version: '0.1.0', + version: "0.1.0", private: true, scripts: { - dev: 'next dev', - build: 'next build', - start: 'next start', - lint: 'next lint', + dev: "next dev", + build: "next build", + start: "next start", + lint: "next lint", }, /** * Default dependencies. */ dependencies: { - react: '^18', - 'react-dom': '^18', + react: "^18", + "react-dom": "^18", next: NEXT_VERSION, llamaindex: "0.0.0-20231018030303", - encoding: "^0.1.13" + encoding: "^0.1.13", }, devDependencies: {}, - } + }; /** * TypeScript projects will have type definitions and other devDependencies. */ - packageJson.devDependencies = { - ...packageJson.devDependencies, - typescript: '^5', - '@types/node': '^20', - '@types/react': '^18', - '@types/react-dom': '^18', - } + packageJson.devDependencies = { + ...packageJson.devDependencies, + typescript: "^5", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + }; /* Add Tailwind CSS dependencies. */ if (tailwind) { packageJson.devDependencies = { ...packageJson.devDependencies, - autoprefixer: '^10', - postcss: '^8', - tailwindcss: '^3', - } + autoprefixer: "^10", + postcss: "^8", + tailwindcss: "^3", + }; } /* Default ESLint dependencies. */ if (eslint) { packageJson.devDependencies = { ...packageJson.devDependencies, - eslint: '^8', - 'eslint-config-next': NEXT_VERSION, - } + eslint: "^8", + "eslint-config-next": NEXT_VERSION, + }; } - const devDeps = Object.keys(packageJson.devDependencies).length - if (!devDeps) delete packageJson.devDependencies + const devDeps = Object.keys(packageJson.devDependencies).length; + if (!devDeps) delete packageJson.devDependencies; await fs.writeFile( - path.join(root, 'package.json'), - JSON.stringify(packageJson, null, 2) + os.EOL - ) + path.join(root, "package.json"), + JSON.stringify(packageJson, null, 2) + os.EOL, + ); - console.log('\nInstalling dependencies:') + console.log("\nInstalling dependencies:"); for (const dependency in packageJson.dependencies) - console.log(`- ${cyan(dependency)}`) + console.log(`- ${cyan(dependency)}`); if (devDeps) { - console.log('\nInstalling devDependencies:') + console.log("\nInstalling devDependencies:"); for (const dependency in packageJson.devDependencies) - console.log(`- ${cyan(dependency)}`) + console.log(`- ${cyan(dependency)}`); } - console.log() + console.log(); - await install(packageManager, isOnline) -} + await install(packageManager, isOnline); +}; -export * from './types' +export * from "./types"; diff --git a/packages/create-llama/templates/simple/nextjs/app/api/llm/route.ts b/packages/create-llama/templates/simple/nextjs/app/api/llm/route.ts index bb3a11319e..b399143b7c 100644 --- a/packages/create-llama/templates/simple/nextjs/app/api/llm/route.ts +++ b/packages/create-llama/templates/simple/nextjs/app/api/llm/route.ts @@ -5,48 +5,48 @@ export const runtime = "nodejs"; export const dynamic = "force-dynamic"; export async function POST(request: NextRequest) { - try { - const body = await request.json(); - const { - message, - chatHistory, - }: { - message: string; - chatHistory: ChatMessage[]; - } = body; - if (!message || !chatHistory) { - return NextResponse.json( - { - error: "message, chatHistory are required in the request body", - }, - { status: 400 } - ); - } + try { + const body = await request.json(); + const { + message, + chatHistory, + }: { + message: string; + chatHistory: ChatMessage[]; + } = body; + if (!message || !chatHistory) { + return NextResponse.json( + { + error: "message, chatHistory are required in the request body", + }, + { status: 400 }, + ); + } - const llm = new OpenAI({ - model: "gpt-3.5-turbo", - }); + const llm = new OpenAI({ + model: "gpt-3.5-turbo", + }); - const chatEngine = new SimpleChatEngine({ - llm, - }); + const chatEngine = new SimpleChatEngine({ + llm, + }); - const response = await chatEngine.chat(message, chatHistory); - const result: ChatMessage = { - role: "assistant", - content: response.response, - }; + const response = await chatEngine.chat(message, chatHistory); + const result: ChatMessage = { + role: "assistant", + content: response.response, + }; - return NextResponse.json({ result }); - } catch (error) { - console.error("[LlamaIndex]", error); - return NextResponse.json( - { - error: (error as Error).message, - }, - { - status: 500, - } - ); - } + return NextResponse.json({ result }); + } catch (error) { + console.error("[LlamaIndex]", error); + return NextResponse.json( + { + error: (error as Error).message, + }, + { + status: 500, + }, + ); + } } diff --git a/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx b/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx index 0f67f43d3c..59f42ce3ae 100644 --- a/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx +++ b/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx @@ -2,42 +2,42 @@ import { ChatMessage } from "llamaindex"; export default function MessageForm() { - const testSendMessage = async (message: string) => { - const chatHistory: ChatMessage[] = []; - const apiRoute = "/api/llm"; - const response = await fetch(apiRoute, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ message, chatHistory }), - }); - const data = await response.json(); - alert(JSON.stringify(data)); - }; + const testSendMessage = async (message: string) => { + const chatHistory: ChatMessage[] = []; + const apiRoute = "/api/llm"; + const response = await fetch(apiRoute, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ message, chatHistory }), + }); + const data = await response.json(); + alert(JSON.stringify(data)); + }; - return ( -
{ - e.preventDefault(); - const message = e.currentTarget.message.value; - testSendMessage(message); - }} - className="flex flex-col items-center justify-center w-full max-w-5xl p-4 space-y-4 bg-white rounded-xl shadow-xl" - > - - -
- ); + return ( +
{ + e.preventDefault(); + const message = e.currentTarget.message.value; + testSendMessage(message); + }} + className="flex flex-col items-center justify-center w-full max-w-5xl p-4 space-y-4 bg-white rounded-xl shadow-xl" + > + + +
+ ); } diff --git a/packages/create-llama/templates/simple/nextjs/app/layout.tsx b/packages/create-llama/templates/simple/nextjs/app/layout.tsx index 444a7b6eec..fb09770627 100644 --- a/packages/create-llama/templates/simple/nextjs/app/layout.tsx +++ b/packages/create-llama/templates/simple/nextjs/app/layout.tsx @@ -1,8 +1,8 @@ -import type { Metadata } from 'next' -import { Inter } from 'next/font/google' -import './globals.css' +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; -const inter = Inter({ subsets: ['latin'] }) +const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "Create Llama App", @@ -12,11 +12,11 @@ export const metadata: Metadata = { export default function RootLayout({ children, }: { - children: React.ReactNode + children: React.ReactNode; }) { return ( {children} - ) + ); } diff --git a/packages/create-llama/templates/simple/nextjs/app/page.tsx b/packages/create-llama/templates/simple/nextjs/app/page.tsx index 1a73edb1ea..a6c7e79f09 100644 --- a/packages/create-llama/templates/simple/nextjs/app/page.tsx +++ b/packages/create-llama/templates/simple/nextjs/app/page.tsx @@ -2,33 +2,33 @@ import MessageForm from "@/app/components/message-form"; import Image from "next/image"; export default function Home() { - return ( -
-
-

- Get started by editing  - app/page.tsx -

- -
- -
- ); + return ( +
+
+

+ Get started by editing  + app/page.tsx +

+ +
+ +
+ ); } diff --git a/packages/create-llama/templates/simple/nextjs/tailwind.config.ts b/packages/create-llama/templates/simple/nextjs/tailwind.config.ts index c7ead80465..7e4bd91a03 100644 --- a/packages/create-llama/templates/simple/nextjs/tailwind.config.ts +++ b/packages/create-llama/templates/simple/nextjs/tailwind.config.ts @@ -1,20 +1,20 @@ -import type { Config } from 'tailwindcss' +import type { Config } from "tailwindcss"; const config: Config = { content: [ - './pages/**/*.{js,ts,jsx,tsx,mdx}', - './components/**/*.{js,ts,jsx,tsx,mdx}', - './app/**/*.{js,ts,jsx,tsx,mdx}', + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./app/**/*.{js,ts,jsx,tsx,mdx}", ], theme: { extend: { backgroundImage: { - 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', - 'gradient-conic': - 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', + "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", + "gradient-conic": + "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", }, }, }, plugins: [], -} -export default config +}; +export default config; diff --git a/packages/create-llama/templates/types.ts b/packages/create-llama/templates/types.ts index 541fa3d31b..5312d80dd9 100644 --- a/packages/create-llama/templates/types.ts +++ b/packages/create-llama/templates/types.ts @@ -1,24 +1,24 @@ -import { PackageManager } from '../helpers/get-pkg-manager' +import { PackageManager } from "../helpers/get-pkg-manager"; -export type TemplateType = 'simple' -export type TemplateMode = 'nextjs' +export type TemplateType = "simple"; +export type TemplateMode = "nextjs"; export interface GetTemplateFileArgs { - template: TemplateType - mode: TemplateMode - file: string + template: TemplateType; + mode: TemplateMode; + file: string; } export interface InstallTemplateArgs { - appName: string - root: string - packageManager: PackageManager - isOnline: boolean + appName: string; + root: string; + packageManager: PackageManager; + isOnline: boolean; - template: TemplateType - mode: TemplateMode - eslint: boolean - tailwind: boolean - srcDir: boolean - importAlias: string + template: TemplateType; + mode: TemplateMode; + eslint: boolean; + tailwind: boolean; + srcDir: boolean; + importAlias: string; } From e6e62fa767ec557d7f47f1e79ee93662667af2a3 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Thu, 26 Oct 2023 11:13:20 +0700 Subject: [PATCH 06/44] removed URL download --- packages/create-llama/README.md | 30 +-- packages/create-llama/create-app.ts | 211 ++-------------------- packages/create-llama/helpers/examples.ts | 133 -------------- packages/create-llama/index.ts | 206 +++++++-------------- 4 files changed, 93 insertions(+), 487 deletions(-) delete mode 100644 packages/create-llama/helpers/examples.ts diff --git a/packages/create-llama/README.md b/packages/create-llama/README.md index 74134373c4..b52ddc0355 100644 --- a/packages/create-llama/README.md +++ b/packages/create-llama/README.md @@ -1,6 +1,7 @@ -# Create Next App +# Create LlamaIndex App -The easiest way to get started with LlamaIndex is by using `create-llama`. This CLI tool enables you to quickly start building a new LlamaIndex application, with everything set up for you. You can create a new app using the default LlamaIndex template, or by using one of the [official LlamaIndex examples](https://github.com/vercel/next.js/tree/canary/examples). To get started, use the following command: +The easiest way to get started with LlamaIndex is by using `create-llama`. This CLI tool enables you to quickly start building a new LlamaIndex application, with everything set up for you. +To get started, use the following command: ### Interactive @@ -9,9 +10,9 @@ You can create a new project interactively by running: ```bash npx create-llama@latest # or -yarn create next-app +yarn create llama-app # or -pnpm create next-app +pnpm create llama-app # or bunx create-llama ``` @@ -53,26 +54,5 @@ Options: Explicitly tell the CLI to bootstrap the app using Bun - -e, --example [name]|[github-url] - - An example to bootstrap the app with. You can use an example name - from the official LlamaIndex repo or a GitHub URL. The URL can use - any branch and/or subdirectory - - --example-path - - In a rare case, your GitHub URL might contain a branch name with - a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). - In this case, you must specify the path to the example separately: - --example-path foo/bar ``` -### Why use Create Next App? - -`create-llama` allows you to create a new LlamaIndex app within seconds. It is officially maintained by the creators of LlamaIndex, and includes a number of benefits: - -- **Interactive Experience**: Running `npx create-llama@latest` (with no arguments) launches an interactive experience that guides you through setting up a project. -- **Zero Dependencies**: Initializing a project is as quick as one second. Create Next App has zero dependencies. -- **Offline Support**: Create Next App will automatically detect if you're offline and bootstrap your project using your local package cache. -- **Support for Examples**: Create Next App can bootstrap your application using an example from the LlamaIndex examples collection (e.g. `npx create-llama --example api-routes`). -- **Tested**: The package is part of the LlamaIndex monorepo and tested using the same integration test suite as LlamaIndex itself, ensuring it works as expected with every release. diff --git a/packages/create-llama/create-app.ts b/packages/create-llama/create-app.ts index fb76bc8a4c..18e1811793 100644 --- a/packages/create-llama/create-app.ts +++ b/packages/create-llama/create-app.ts @@ -1,34 +1,19 @@ /* eslint-disable import/no-extraneous-dependencies */ -import retry from "async-retry"; -import fs from "fs"; import path from "path"; -import { cyan, green, red } from "picocolors"; -import type { RepoInfo } from "./helpers/examples"; -import { - downloadAndExtractExample, - downloadAndExtractRepo, - existsInRepo, - getRepoInfo, - hasRepo, -} from "./helpers/examples"; +import { green } from "picocolors"; import type { PackageManager } from "./helpers/get-pkg-manager"; import { tryGitInit } from "./helpers/git"; -import { install } from "./helpers/install"; import { isFolderEmpty } from "./helpers/is-folder-empty"; import { getOnline } from "./helpers/is-online"; import { isWriteable } from "./helpers/is-writeable"; import { makeDir } from "./helpers/make-dir"; import type { TemplateMode, TemplateType } from "./templates"; -import { getTemplateFile, installTemplate } from "./templates"; - -export class DownloadError extends Error {} +import { installTemplate } from "./templates"; export async function createApp({ appPath, packageManager, - example, - examplePath, tailwind, eslint, srcDir, @@ -36,78 +21,14 @@ export async function createApp({ }: { appPath: string; packageManager: PackageManager; - example?: string; - examplePath?: string; tailwind: boolean; eslint: boolean; srcDir: boolean; importAlias: string; }): Promise { - let repoInfo: RepoInfo | undefined; const mode: TemplateMode = "nextjs"; const template: TemplateType = "simple"; - if (example) { - let repoUrl: URL | undefined; - - try { - repoUrl = new URL(example); - } catch (error: any) { - if (error.code !== "ERR_INVALID_URL") { - console.error(error); - process.exit(1); - } - } - - if (repoUrl) { - if (repoUrl.origin !== "https://github.com") { - console.error( - `Invalid URL: ${red( - `"${example}"`, - )}. Only GitHub repositories are supported. Please use a GitHub URL and try again.`, - ); - process.exit(1); - } - - repoInfo = await getRepoInfo(repoUrl, examplePath); - - if (!repoInfo) { - console.error( - `Found invalid GitHub URL: ${red( - `"${example}"`, - )}. Please fix the URL and try again.`, - ); - process.exit(1); - } - - const found = await hasRepo(repoInfo); - - if (!found) { - console.error( - `Could not locate the repository for ${red( - `"${example}"`, - )}. Please check that the repository exists and try again.`, - ); - process.exit(1); - } - } else if (example !== "__internal-testing-retry") { - const found = await existsInRepo(example); - - if (!found) { - console.error( - `Could not locate an example named ${red( - `"${example}"`, - )}. It could be due to the following:\n`, - `1. Your spelling of example ${red( - `"${example}"`, - )} might be incorrect.\n`, - `2. You might not be connected to the internet or you are behind a proxy.`, - ); - process.exit(1); - } - } - } - const root = path.resolve(appPath); if (!(await isWriteable(path.dirname(root)))) { @@ -129,130 +50,34 @@ export async function createApp({ const useYarn = packageManager === "yarn"; const isOnline = !useYarn || (await getOnline()); - const originalDirectory = process.cwd(); console.log(`Creating a new LlamaIndex app in ${green(root)}.`); console.log(); process.chdir(root); - const packageJsonPath = path.join(root, "package.json"); - let hasPackageJson = false; - - if (example) { - /** - * If an example repository is provided, clone it. - */ - try { - if (repoInfo) { - const repoInfo2 = repoInfo; - console.log( - `Downloading files from repo ${cyan( - example, - )}. This might take a moment.`, - ); - console.log(); - await retry(() => downloadAndExtractRepo(root, repoInfo2), { - retries: 3, - }); - } else { - console.log( - `Downloading files for example ${cyan( - example, - )}. This might take a moment.`, - ); - console.log(); - await retry(() => downloadAndExtractExample(root, example), { - retries: 3, - }); - } - } catch (reason) { - function isErrorLike(err: unknown): err is { message: string } { - return ( - typeof err === "object" && - err !== null && - typeof (err as { message?: unknown }).message === "string" - ); - } - throw new DownloadError( - isErrorLike(reason) ? reason.message : reason + "", - ); - } - // Copy `.gitignore` if the application did not provide one - const ignorePath = path.join(root, ".gitignore"); - if (!fs.existsSync(ignorePath)) { - fs.copyFileSync( - getTemplateFile({ template, mode, file: "gitignore" }), - ignorePath, - ); - } - - // Copy `next-env.d.ts` to any example that is typescript - const tsconfigPath = path.join(root, "tsconfig.json"); - if (fs.existsSync(tsconfigPath)) { - fs.copyFileSync( - getTemplateFile({ template, mode: "nextjs", file: "next-env.d.ts" }), - path.join(root, "next-env.d.ts"), - ); - } - - hasPackageJson = fs.existsSync(packageJsonPath); - if (hasPackageJson) { - console.log("Installing packages. This might take a couple of minutes."); - console.log(); - - await install(packageManager, isOnline); - console.log(); - } - } else { - /** - * If an example repository is not provided for cloning, proceed - * by installing from a template. - */ - await installTemplate({ - appName, - root, - template, - mode, - packageManager, - isOnline, - tailwind, - eslint, - srcDir, - importAlias, - }); - } + /** + * If an example repository is not provided for cloning, proceed + * by installing from a template. + */ + await installTemplate({ + appName, + root, + template, + mode, + packageManager, + isOnline, + tailwind, + eslint, + srcDir, + importAlias, + }); if (tryGitInit(root)) { console.log("Initialized a git repository."); console.log(); } - let cdpath: string; - if (path.join(originalDirectory, appName) === appPath) { - cdpath = appName; - } else { - cdpath = appPath; - } - console.log(`${green("Success!")} Created ${appName} at ${appPath}`); - - if (hasPackageJson) { - console.log("Inside that directory, you can run several commands:"); - console.log(); - console.log(cyan(` ${packageManager} ${useYarn ? "" : "run "}dev`)); - console.log(" Starts the development server."); - console.log(); - console.log(cyan(` ${packageManager} ${useYarn ? "" : "run "}build`)); - console.log(" Builds the app for production."); - console.log(); - console.log(cyan(` ${packageManager} start`)); - console.log(" Runs the built app in production mode."); - console.log(); - console.log("We suggest that you begin by typing:"); - console.log(); - console.log(cyan(" cd"), cdpath); - console.log(` ${cyan(`${packageManager} ${useYarn ? "" : "run "}dev`)}`); - } console.log(); } diff --git a/packages/create-llama/helpers/examples.ts b/packages/create-llama/helpers/examples.ts deleted file mode 100644 index b8c74be6e6..0000000000 --- a/packages/create-llama/helpers/examples.ts +++ /dev/null @@ -1,133 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import { createWriteStream, promises as fs } from "fs"; -import got from "got"; -import { tmpdir } from "os"; -import { join } from "path"; -import { Stream } from "stream"; -import tar from "tar"; -import { promisify } from "util"; - -const pipeline = promisify(Stream.pipeline); - -export type RepoInfo = { - username: string; - name: string; - branch: string; - filePath: string; -}; - -export async function isUrlOk(url: string): Promise { - const res = await got.head(url).catch((e) => e); - return res.statusCode === 200; -} - -export async function getRepoInfo( - url: URL, - examplePath?: string, -): Promise { - const [, username, name, t, _branch, ...file] = url.pathname.split("/"); - const filePath = examplePath - ? examplePath.replace(/^\//, "") - : file.join("/"); - - if ( - // Support repos whose entire purpose is to be a LlamaIndex example, e.g. - // https://github.com/:username/:my-cool-nextjs-example-repo-name. - t === undefined || - // Support GitHub URL that ends with a trailing slash, e.g. - // https://github.com/:username/:my-cool-nextjs-example-repo-name/ - // In this case "t" will be an empty string while the next part "_branch" will be undefined - (t === "" && _branch === undefined) - ) { - const infoResponse = await got( - `https://api.github.com/repos/${username}/${name}`, - ).catch((e) => e); - if (infoResponse.statusCode !== 200) { - return; - } - const info = JSON.parse(infoResponse.body); - return { username, name, branch: info["default_branch"], filePath }; - } - - // If examplePath is available, the branch name takes the entire path - const branch = examplePath - ? `${_branch}/${file.join("/")}`.replace(new RegExp(`/${filePath}|/$`), "") - : _branch; - - if (username && name && branch && t === "tree") { - return { username, name, branch, filePath }; - } -} - -export function hasRepo({ - username, - name, - branch, - filePath, -}: RepoInfo): Promise { - const contentsUrl = `https://api.github.com/repos/${username}/${name}/contents`; - const packagePath = `${filePath ? `/${filePath}` : ""}/package.json`; - - return isUrlOk(contentsUrl + packagePath + `?ref=${branch}`); -} - -export function existsInRepo(nameOrUrl: string): Promise { - try { - const url = new URL(nameOrUrl); - return isUrlOk(url.href); - } catch { - return isUrlOk( - `https://api.github.com/repos/vercel/next.js/contents/examples/${encodeURIComponent( - nameOrUrl, - )}`, - ); - } -} - -async function downloadTar(url: string) { - const tempFile = join(tmpdir(), `next.js-cna-example.temp-${Date.now()}`); - await pipeline(got.stream(url), createWriteStream(tempFile)); - return tempFile; -} - -export async function downloadAndExtractRepo( - root: string, - { username, name, branch, filePath }: RepoInfo, -) { - const tempFile = await downloadTar( - `https://codeload.github.com/${username}/${name}/tar.gz/${branch}`, - ); - - await tar.x({ - file: tempFile, - cwd: root, - strip: filePath ? filePath.split("/").length + 1 : 1, - filter: (p) => - p.startsWith( - `${name}-${branch.replace(/\//g, "-")}${ - filePath ? `/${filePath}/` : "/" - }`, - ), - }); - - await fs.unlink(tempFile); -} - -export async function downloadAndExtractExample(root: string, name: string) { - if (name === "__internal-testing-retry") { - throw new Error("This is an internal example for testing the CLI."); - } - - const tempFile = await downloadTar( - "https://codeload.github.com/vercel/next.js/tar.gz/canary", - ); - - await tar.x({ - file: tempFile, - cwd: root, - strip: 2 + name.split("/").length, - filter: (p) => p.includes(`next.js-canary/examples/${name}/`), - }); - - await fs.unlink(tempFile); -} diff --git a/packages/create-llama/index.ts b/packages/create-llama/index.ts index b74215f98c..01fac2d502 100644 --- a/packages/create-llama/index.ts +++ b/packages/create-llama/index.ts @@ -8,7 +8,7 @@ import path from "path"; import { blue, bold, cyan, green, red, yellow } from "picocolors"; import prompts from "prompts"; import checkForUpdate from "update-check"; -import { createApp, DownloadError } from "./create-app"; +import { createApp } from "./create-app"; import { getPkgManager } from "./helpers/get-pkg-manager"; import { isFolderEmpty } from "./helpers/is-folder-empty"; import { validateNpmName } from "./helpers/validate-pkg"; @@ -78,25 +78,6 @@ const program = new Commander.Command(packageJson.name) ` Explicitly tell the CLI to bootstrap the application using Bun -`, - ) - .option( - "-e, --example [name]|[github-url]", - ` - - An example to bootstrap the app with. You can use an example name - from the official LlamaIndex repo or a GitHub URL. The URL can use - any branch and/or subdirectory -`, - ) - .option( - "--example-path ", - ` - - In a rare case, your GitHub URL might contain a branch name with - a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). - In this case, you must specify the path to the example separately: - --example-path foo/bar `, ) .option( @@ -179,13 +160,6 @@ async function run(): Promise { process.exit(1); } - if (program.example === true) { - console.error( - "Please provide an example name or url, otherwise remove the example option.", - ); - process.exit(1); - } - /** * Verify the project dir is empty or doesn't exist */ @@ -197,130 +171,90 @@ async function run(): Promise { process.exit(1); } - const example = typeof program.example === "string" && program.example.trim(); const preferences = (conf.get("preferences") || {}) as Record< string, boolean | string >; - /** - * If the user does not provide the necessary flags, prompt them for whether - * to use TS or JS. - */ - if (!example) { - const defaults: typeof preferences = { - typescript: true, - eslint: true, - tailwind: true, - app: true, - srcDir: false, - importAlias: "@/*", - customizeImportAlias: false, - }; - const getPrefOrDefault = (field: string) => - preferences[field] ?? defaults[field]; - - if ( - !process.argv.includes("--eslint") && - !process.argv.includes("--no-eslint") - ) { - if (ciInfo.isCI) { - program.eslint = getPrefOrDefault("eslint"); - } else { - const styledEslint = blue("ESLint"); - const { eslint } = await prompts({ - onState: onPromptState, - type: "toggle", - name: "eslint", - message: `Would you like to use ${styledEslint}?`, - initial: getPrefOrDefault("eslint"), - active: "Yes", - inactive: "No", - }); - program.eslint = Boolean(eslint); - preferences.eslint = Boolean(eslint); - } + + const defaults: typeof preferences = { + eslint: true, + tailwind: true, + app: true, + srcDir: false, + importAlias: "@/*", + customizeImportAlias: false, + }; + const getPrefOrDefault = (field: string) => + preferences[field] ?? defaults[field]; + + if ( + !process.argv.includes("--eslint") && + !process.argv.includes("--no-eslint") + ) { + if (ciInfo.isCI) { + program.eslint = getPrefOrDefault("eslint"); + } else { + const styledEslint = blue("ESLint"); + const { eslint } = await prompts({ + onState: onPromptState, + type: "toggle", + name: "eslint", + message: `Would you like to use ${styledEslint}?`, + initial: getPrefOrDefault("eslint"), + active: "Yes", + inactive: "No", + }); + program.eslint = Boolean(eslint); + preferences.eslint = Boolean(eslint); } + } - if ( - typeof program.importAlias !== "string" || - !program.importAlias.length - ) { - if (ciInfo.isCI) { + if (typeof program.importAlias !== "string" || !program.importAlias.length) { + if (ciInfo.isCI) { + // We don't use preferences here because the default value is @/* regardless of existing preferences + program.importAlias = defaults.importAlias; + } else { + const styledImportAlias = blue("import alias"); + + const { customizeImportAlias } = await prompts({ + onState: onPromptState, + type: "toggle", + name: "customizeImportAlias", + message: `Would you like to customize the default ${styledImportAlias} (${defaults.importAlias})?`, + initial: getPrefOrDefault("customizeImportAlias"), + active: "Yes", + inactive: "No", + }); + + if (!customizeImportAlias) { // We don't use preferences here because the default value is @/* regardless of existing preferences program.importAlias = defaults.importAlias; } else { - const styledImportAlias = blue("import alias"); - - const { customizeImportAlias } = await prompts({ + const { importAlias } = await prompts({ onState: onPromptState, - type: "toggle", - name: "customizeImportAlias", - message: `Would you like to customize the default ${styledImportAlias} (${defaults.importAlias})?`, - initial: getPrefOrDefault("customizeImportAlias"), - active: "Yes", - inactive: "No", + type: "text", + name: "importAlias", + message: `What ${styledImportAlias} would you like configured?`, + initial: getPrefOrDefault("importAlias"), + validate: (value) => + /.+\/\*/.test(value) + ? true + : "Import alias must follow the pattern /*", }); - - if (!customizeImportAlias) { - // We don't use preferences here because the default value is @/* regardless of existing preferences - program.importAlias = defaults.importAlias; - } else { - const { importAlias } = await prompts({ - onState: onPromptState, - type: "text", - name: "importAlias", - message: `What ${styledImportAlias} would you like configured?`, - initial: getPrefOrDefault("importAlias"), - validate: (value) => - /.+\/\*/.test(value) - ? true - : "Import alias must follow the pattern /*", - }); - program.importAlias = importAlias; - preferences.importAlias = importAlias; - } + program.importAlias = importAlias; + preferences.importAlias = importAlias; } } } - try { - await createApp({ - appPath: resolvedProjectPath, - packageManager, - example: example && example !== "default" ? example : undefined, - examplePath: program.examplePath, - tailwind: true, - eslint: program.eslint, - srcDir: program.srcDir, - importAlias: program.importAlias, - }); - } catch (reason) { - if (!(reason instanceof DownloadError)) { - throw reason; - } - - const res = await prompts({ - onState: onPromptState, - type: "confirm", - name: "builtin", - message: - `Could not download "${example}" because of a connectivity issue between your machine and GitHub.\n` + - `Do you want to use the default template instead?`, - initial: true, - }); - if (!res.builtin) { - throw reason; - } - - await createApp({ - appPath: resolvedProjectPath, - packageManager, - eslint: program.eslint, - tailwind: true, - srcDir: program.srcDir, - importAlias: program.importAlias, - }); - } + await createApp({ + appPath: resolvedProjectPath, + packageManager, + tailwind: true, + eslint: program.eslint, + srcDir: program.srcDir, + importAlias: program.importAlias, + }); conf.set("preferences", preferences); } From fcf7c1275b9307c75350ac800af1c2807f645b68 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Thu, 26 Oct 2023 11:22:09 +0700 Subject: [PATCH 07/44] use repos package version --- packages/create-llama/package.json | 2 +- packages/create-llama/templates/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/create-llama/package.json b/packages/create-llama/package.json index cec7693118..144a17509e 100644 --- a/packages/create-llama/package.json +++ b/packages/create-llama/package.json @@ -1,6 +1,6 @@ { "name": "create-llama", - "version": "0.0.1", + "version": "0.0.31", "keywords": [ "rag", "llamaindex", diff --git a/packages/create-llama/templates/index.ts b/packages/create-llama/templates/index.ts index e167f6450f..ca013db06d 100644 --- a/packages/create-llama/templates/index.ts +++ b/packages/create-llama/templates/index.ts @@ -8,6 +8,7 @@ import fs from "fs/promises"; import os from "os"; import path from "path"; import { bold, cyan } from "picocolors"; +import { version } from "../package.json" import { GetTemplateFileArgs, InstallTemplateArgs } from "./types"; @@ -177,8 +178,7 @@ export const installTemplate = async ({ react: "^18", "react-dom": "^18", next: NEXT_VERSION, - llamaindex: "0.0.0-20231018030303", - encoding: "^0.1.13", + llamaindex: version, }, devDependencies: {}, }; From 4371c46c4c204468051f9b664238cd566f11e45c Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Thu, 26 Oct 2023 16:33:02 +0700 Subject: [PATCH 08/44] add express example, framework selector and use existing package.json (just update it) --- packages/create-llama/create-app.ts | 10 +- packages/create-llama/index.ts | 31 +++++- packages/create-llama/templates/index.ts | 99 ++++++------------- .../simple/express/README-template.md | 19 ++++ .../templates/simple/express/eslintrc.json | 3 + .../templates/simple/express/index.ts | 17 ++++ .../templates/simple/express/package.json | 22 +++++ .../express/src/controllers/llm.controller.ts | 42 ++++++++ .../simple/express/src/routes/llm.route.ts | 8 ++ .../templates/simple/express/tsconfig.json | 11 +++ .../templates/simple/nextjs/package.json | 27 +++++ packages/create-llama/templates/types.ts | 8 +- 12 files changed, 213 insertions(+), 84 deletions(-) create mode 100644 packages/create-llama/templates/simple/express/README-template.md create mode 100644 packages/create-llama/templates/simple/express/eslintrc.json create mode 100644 packages/create-llama/templates/simple/express/index.ts create mode 100644 packages/create-llama/templates/simple/express/package.json create mode 100644 packages/create-llama/templates/simple/express/src/controllers/llm.controller.ts create mode 100644 packages/create-llama/templates/simple/express/src/routes/llm.route.ts create mode 100644 packages/create-llama/templates/simple/express/tsconfig.json create mode 100644 packages/create-llama/templates/simple/nextjs/package.json diff --git a/packages/create-llama/create-app.ts b/packages/create-llama/create-app.ts index 18e1811793..8efef515f5 100644 --- a/packages/create-llama/create-app.ts +++ b/packages/create-llama/create-app.ts @@ -8,25 +8,24 @@ import { getOnline } from "./helpers/is-online"; import { isWriteable } from "./helpers/is-writeable"; import { makeDir } from "./helpers/make-dir"; -import type { TemplateMode, TemplateType } from "./templates"; +import type { TemplateFramework, TemplateType } from "./templates"; import { installTemplate } from "./templates"; export async function createApp({ + framework, appPath, packageManager, - tailwind, eslint, srcDir, importAlias, }: { + framework: TemplateFramework; appPath: string; packageManager: PackageManager; - tailwind: boolean; eslint: boolean; srcDir: boolean; importAlias: string; }): Promise { - const mode: TemplateMode = "nextjs"; const template: TemplateType = "simple"; const root = path.resolve(appPath); @@ -64,10 +63,9 @@ export async function createApp({ appName, root, template, - mode, + framework, packageManager, isOnline, - tailwind, eslint, srcDir, importAlias, diff --git a/packages/create-llama/index.ts b/packages/create-llama/index.ts index 01fac2d502..9ff2ee6746 100644 --- a/packages/create-llama/index.ts +++ b/packages/create-llama/index.ts @@ -177,8 +177,8 @@ async function run(): Promise { >; const defaults: typeof preferences = { + framework: "nextjs", eslint: true, - tailwind: true, app: true, srcDir: false, importAlias: "@/*", @@ -187,6 +187,33 @@ async function run(): Promise { const getPrefOrDefault = (field: string) => preferences[field] ?? defaults[field]; + if (!program.framework) { + if (ciInfo.isCI) { + program.framework = getPrefOrDefault("framework"); + } else { + const { framework } = await prompts( + { + type: "select", + name: "framework", + message: "Which framework would you like to use?", + choices: [ + { title: "NextJS", value: "nextjs" }, + { title: "Express", value: "express" }, + ], + initial: 0, + }, + { + onCancel: () => { + console.error("Exiting."); + process.exit(1); + }, + }, + ); + program.framework = framework; + preferences.framework = framework; + } + } + if ( !process.argv.includes("--eslint") && !process.argv.includes("--no-eslint") @@ -248,9 +275,9 @@ async function run(): Promise { } await createApp({ + framework: program.framework, appPath: resolvedProjectPath, packageManager, - tailwind: true, eslint: program.eslint, srcDir: program.srcDir, importAlias: program.importAlias, diff --git a/packages/create-llama/templates/index.ts b/packages/create-llama/templates/index.ts index ca013db06d..d9de01f8de 100644 --- a/packages/create-llama/templates/index.ts +++ b/packages/create-llama/templates/index.ts @@ -8,21 +8,19 @@ import fs from "fs/promises"; import os from "os"; import path from "path"; import { bold, cyan } from "picocolors"; -import { version } from "../package.json" +import { version } from "../package.json"; import { GetTemplateFileArgs, InstallTemplateArgs } from "./types"; -const NEXT_VERSION = "13.5.6"; - /** * Get the file path for a given file in a template, e.g. "next.config.js". */ export const getTemplateFile = ({ template, - mode, + framework, file, }: GetTemplateFileArgs): string => { - return path.join(__dirname, template, mode, file); + return path.join(__dirname, template, framework, file); }; export const SRC_DIR_NAMES = ["app", "pages", "styles"]; @@ -36,8 +34,7 @@ export const installTemplate = async ({ packageManager, isOnline, template, - mode, - tailwind, + framework, eslint, srcDir, importAlias, @@ -48,14 +45,9 @@ export const installTemplate = async ({ * Copy the template files to the target directory. */ console.log("\nInitializing project with template:", template, "\n"); - const templatePath = path.join(__dirname, template, mode); + const templatePath = path.join(__dirname, template, framework); const copySource = ["**"]; if (!eslint) copySource.push("!eslintrc.json"); - if (!tailwind) - copySource.push( - mode == "nextjs" ? "tailwind.config.ts" : "!tailwind.config.js", - "!postcss.config.js", - ); await copy(copySource, root, { parents: true, @@ -148,7 +140,7 @@ export const installTemplate = async ({ ), ); - if (tailwind) { + if (framework === "nextjs") { const tailwindConfigFile = path.join(root, "tailwind.config.ts"); await fs.writeFile( tailwindConfigFile, @@ -160,64 +152,31 @@ export const installTemplate = async ({ } } - /** Create a package.json for the new project and write it to disk. */ - const packageJson: any = { - name: appName, - version: "0.1.0", - private: true, - scripts: { - dev: "next dev", - build: "next build", - start: "next start", - lint: "next lint", - }, - /** - * Default dependencies. - */ - dependencies: { - react: "^18", - "react-dom": "^18", - next: NEXT_VERSION, - llamaindex: version, - }, - devDependencies: {}, - }; - /** - * TypeScript projects will have type definitions and other devDependencies. + * Update the package.json scripts. */ - packageJson.devDependencies = { - ...packageJson.devDependencies, - typescript: "^5", - "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", - }; + const packageJsonFile = path.join(root, "package.json"); + const packageJson: any = JSON.parse( + await fs.readFile(packageJsonFile, "utf8"), + ); + packageJson.name = appName; + packageJson.version = "0.1.0"; - /* Add Tailwind CSS dependencies. */ - if (tailwind) { - packageJson.devDependencies = { - ...packageJson.devDependencies, - autoprefixer: "^10", - postcss: "^8", - tailwindcss: "^3", - }; - } + packageJson.dependencies = { + ...packageJson.dependencies, + llamaindex: version, + }; - /* Default ESLint dependencies. */ - if (eslint) { - packageJson.devDependencies = { - ...packageJson.devDependencies, - eslint: "^8", - "eslint-config-next": NEXT_VERSION, - }; + if (!eslint) { + // Remove packages starting with "eslint" from devDependencies + packageJson.devDependencies = Object.fromEntries( + Object.entries(packageJson.devDependencies).filter( + ([key]) => !key.startsWith("eslint"), + ), + ); } - - const devDeps = Object.keys(packageJson.devDependencies).length; - if (!devDeps) delete packageJson.devDependencies; - await fs.writeFile( - path.join(root, "package.json"), + packageJsonFile, JSON.stringify(packageJson, null, 2) + os.EOL, ); @@ -225,11 +184,9 @@ export const installTemplate = async ({ for (const dependency in packageJson.dependencies) console.log(`- ${cyan(dependency)}`); - if (devDeps) { - console.log("\nInstalling devDependencies:"); - for (const dependency in packageJson.devDependencies) - console.log(`- ${cyan(dependency)}`); - } + console.log("\nInstalling devDependencies:"); + for (const dependency in packageJson.devDependencies) + console.log(`- ${cyan(dependency)}`); console.log(); diff --git a/packages/create-llama/templates/simple/express/README-template.md b/packages/create-llama/templates/simple/express/README-template.md new file mode 100644 index 0000000000..b00c2e8d80 --- /dev/null +++ b/packages/create-llama/templates/simple/express/README-template.md @@ -0,0 +1,19 @@ +1. Install the dependencies +``` +pnpm install +``` + +2. Run the server +``` +pnpm run dev +``` + +3. Call the API to LLM Chat +``` +curl --location 'localhost:3000/api/llm' \ +--header 'Content-Type: application/json' \ +--data '{ + "message": "Hello", + "chatHistory": [] +}' +``` \ No newline at end of file diff --git a/packages/create-llama/templates/simple/express/eslintrc.json b/packages/create-llama/templates/simple/express/eslintrc.json new file mode 100644 index 0000000000..c19581799d --- /dev/null +++ b/packages/create-llama/templates/simple/express/eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "eslint:recommended" +} \ No newline at end of file diff --git a/packages/create-llama/templates/simple/express/index.ts b/packages/create-llama/templates/simple/express/index.ts new file mode 100644 index 0000000000..157555572d --- /dev/null +++ b/packages/create-llama/templates/simple/express/index.ts @@ -0,0 +1,17 @@ +import express, { Express, Request, Response } from "express"; +import llmRouter from "./src/routes/llm.route"; + +const app: Express = express(); +const port = 3000; + +app.use(express.json()); + +app.get("/", (req: Request, res: Response) => { + res.send("LlamaIndex Express Server"); +}); + +app.use("/api/llm", llmRouter); + +app.listen(port, () => { + console.log(`⚡️[server]: Server is running at http://localhost:${port}`); +}); diff --git a/packages/create-llama/templates/simple/express/package.json b/packages/create-llama/templates/simple/express/package.json new file mode 100644 index 0000000000..3b57d610ed --- /dev/null +++ b/packages/create-llama/templates/simple/express/package.json @@ -0,0 +1,22 @@ +{ + "name": "llama-index-express", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "build": "tsc", + "start": "node dist/index.js", + "dev": "concurrently \"tsc --watch\" \"nodemon -q dist/index.js\"" + }, + "dependencies": { + "express": "^4", + "llamaindex": "0.0.31" + }, + "devDependencies": { + "@types/express": "^4", + "@types/node": "^20", + "concurrently": "^8", + "nodemon": "^3", + "typescript": "^5", + "eslint": "^8" + } +} \ No newline at end of file diff --git a/packages/create-llama/templates/simple/express/src/controllers/llm.controller.ts b/packages/create-llama/templates/simple/express/src/controllers/llm.controller.ts new file mode 100644 index 0000000000..54fa51b130 --- /dev/null +++ b/packages/create-llama/templates/simple/express/src/controllers/llm.controller.ts @@ -0,0 +1,42 @@ +import { ChatMessage, OpenAI, SimpleChatEngine } from "llamaindex"; +import { NextFunction, Request, Response } from "express"; + +export const chat = async (req: Request, res: Response, next: NextFunction) => { + try { + const { + message, + chatHistory, + }: { + message: string; + chatHistory: ChatMessage[]; + } = req.body; + if (!message || !chatHistory) { + return res.status(400).json({ + error: "message, chatHistory are required in the request body", + }); + } + + const llm = new OpenAI({ + model: "gpt-3.5-turbo", + }); + + const chatEngine = new SimpleChatEngine({ + llm, + }); + + const response = await chatEngine.chat(message, chatHistory); + const result: ChatMessage = { + role: "assistant", + content: response.response, + }; + + return res.status(200).json({ + result, + }); + } catch (error) { + console.error("[LlamaIndex]", error); + return res.status(500).json({ + error: (error as Error).message, + }); + } +}; diff --git a/packages/create-llama/templates/simple/express/src/routes/llm.route.ts b/packages/create-llama/templates/simple/express/src/routes/llm.route.ts new file mode 100644 index 0000000000..3711c71b95 --- /dev/null +++ b/packages/create-llama/templates/simple/express/src/routes/llm.route.ts @@ -0,0 +1,8 @@ +import express from "express"; +import { chat } from "../controllers/llm.controller"; + +const llmRouter = express.Router(); + +llmRouter.route("/").post(chat); + +export default llmRouter; diff --git a/packages/create-llama/templates/simple/express/tsconfig.json b/packages/create-llama/templates/simple/express/tsconfig.json new file mode 100644 index 0000000000..fd70902d6d --- /dev/null +++ b/packages/create-llama/templates/simple/express/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2016", + "module": "commonjs", + "outDir": "./dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +} diff --git a/packages/create-llama/templates/simple/nextjs/package.json b/packages/create-llama/templates/simple/nextjs/package.json new file mode 100644 index 0000000000..afb59904ce --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/package.json @@ -0,0 +1,27 @@ +{ + "name": "llama-index-nextjs", + "version": "1.0.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "react": "^18", + "react-dom": "^18", + "next": "^13", + "llamaindex": "0.0.31" + }, + "devDependencies": { + "typescript": "^5", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "autoprefixer": "^10", + "postcss": "^8", + "tailwindcss": "^3", + "eslint": "^8", + "eslint-config-next": "^13" + } +} \ No newline at end of file diff --git a/packages/create-llama/templates/types.ts b/packages/create-llama/templates/types.ts index 5312d80dd9..9cd44c9686 100644 --- a/packages/create-llama/templates/types.ts +++ b/packages/create-llama/templates/types.ts @@ -1,11 +1,11 @@ import { PackageManager } from "../helpers/get-pkg-manager"; export type TemplateType = "simple"; -export type TemplateMode = "nextjs"; +export type TemplateFramework = "nextjs" | "express"; export interface GetTemplateFileArgs { template: TemplateType; - mode: TemplateMode; + framework: TemplateFramework; file: string; } @@ -14,11 +14,9 @@ export interface InstallTemplateArgs { root: string; packageManager: PackageManager; isOnline: boolean; - template: TemplateType; - mode: TemplateMode; + framework: TemplateFramework; eslint: boolean; - tailwind: boolean; srcDir: boolean; importAlias: string; } From 3df7fd6dd1bb068a91d4e8f882bb2994b8991525 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Thu, 26 Oct 2023 16:49:26 +0700 Subject: [PATCH 09/44] remove import alias and src folder rewrite --- packages/create-llama/create-app.ts | 6 -- packages/create-llama/index.ts | 44 ---------- packages/create-llama/templates/index.ts | 102 +---------------------- packages/create-llama/templates/types.ts | 8 -- 4 files changed, 1 insertion(+), 159 deletions(-) diff --git a/packages/create-llama/create-app.ts b/packages/create-llama/create-app.ts index 8efef515f5..901723640e 100644 --- a/packages/create-llama/create-app.ts +++ b/packages/create-llama/create-app.ts @@ -16,15 +16,11 @@ export async function createApp({ appPath, packageManager, eslint, - srcDir, - importAlias, }: { framework: TemplateFramework; appPath: string; packageManager: PackageManager; eslint: boolean; - srcDir: boolean; - importAlias: string; }): Promise { const template: TemplateType = "simple"; @@ -67,8 +63,6 @@ export async function createApp({ packageManager, isOnline, eslint, - srcDir, - importAlias, }); if (tryGitInit(root)) { diff --git a/packages/create-llama/index.ts b/packages/create-llama/index.ts index 9ff2ee6746..21eda3b7a7 100644 --- a/packages/create-llama/index.ts +++ b/packages/create-llama/index.ts @@ -179,10 +179,6 @@ async function run(): Promise { const defaults: typeof preferences = { framework: "nextjs", eslint: true, - app: true, - srcDir: false, - importAlias: "@/*", - customizeImportAlias: false, }; const getPrefOrDefault = (field: string) => preferences[field] ?? defaults[field]; @@ -236,51 +232,11 @@ async function run(): Promise { } } - if (typeof program.importAlias !== "string" || !program.importAlias.length) { - if (ciInfo.isCI) { - // We don't use preferences here because the default value is @/* regardless of existing preferences - program.importAlias = defaults.importAlias; - } else { - const styledImportAlias = blue("import alias"); - - const { customizeImportAlias } = await prompts({ - onState: onPromptState, - type: "toggle", - name: "customizeImportAlias", - message: `Would you like to customize the default ${styledImportAlias} (${defaults.importAlias})?`, - initial: getPrefOrDefault("customizeImportAlias"), - active: "Yes", - inactive: "No", - }); - - if (!customizeImportAlias) { - // We don't use preferences here because the default value is @/* regardless of existing preferences - program.importAlias = defaults.importAlias; - } else { - const { importAlias } = await prompts({ - onState: onPromptState, - type: "text", - name: "importAlias", - message: `What ${styledImportAlias} would you like configured?`, - initial: getPrefOrDefault("importAlias"), - validate: (value) => - /.+\/\*/.test(value) - ? true - : "Import alias must follow the pattern /*", - }); - program.importAlias = importAlias; - preferences.importAlias = importAlias; - } - } - } - await createApp({ framework: program.framework, appPath: resolvedProjectPath, packageManager, eslint: program.eslint, - srcDir: program.srcDir, - importAlias: program.importAlias, }); conf.set("preferences", preferences); } diff --git a/packages/create-llama/templates/index.ts b/packages/create-llama/templates/index.ts index d9de01f8de..a6cb26d61d 100644 --- a/packages/create-llama/templates/index.ts +++ b/packages/create-llama/templates/index.ts @@ -1,29 +1,13 @@ import { copy } from "../helpers/copy"; import { install } from "../helpers/install"; -import { makeDir } from "../helpers/make-dir"; -import { Sema } from "async-sema"; -import { async as glob } from "fast-glob"; import fs from "fs/promises"; import os from "os"; import path from "path"; import { bold, cyan } from "picocolors"; import { version } from "../package.json"; -import { GetTemplateFileArgs, InstallTemplateArgs } from "./types"; - -/** - * Get the file path for a given file in a template, e.g. "next.config.js". - */ -export const getTemplateFile = ({ - template, - framework, - file, -}: GetTemplateFileArgs): string => { - return path.join(__dirname, template, framework, file); -}; - -export const SRC_DIR_NAMES = ["app", "pages", "styles"]; +import { InstallTemplateArgs } from "./types"; /** * Install a LlamaIndex internal template to a given `root` directory. @@ -36,8 +20,6 @@ export const installTemplate = async ({ template, framework, eslint, - srcDir, - importAlias, }: InstallTemplateArgs) => { console.log(bold(`Using ${packageManager}.`)); @@ -70,88 +52,6 @@ export const installTemplate = async ({ }, }); - const tsconfigFile = path.join(root, "tsconfig.json"); - await fs.writeFile( - tsconfigFile, - (await fs.readFile(tsconfigFile, "utf8")) - .replace( - `"@/*": ["./*"]`, - srcDir ? `"@/*": ["./src/*"]` : `"@/*": ["./*"]`, - ) - .replace(`"@/*":`, `"${importAlias}":`), - ); - - // update import alias in any files if not using the default - if (importAlias !== "@/*") { - const files = await glob("**/*", { - cwd: root, - dot: true, - stats: false, - }); - const writeSema = new Sema(8, { capacity: files.length }); - await Promise.all( - files.map(async (file) => { - // We don't want to modify compiler options in [ts/js]config.json - if (file === "tsconfig.json" || file === "jsconfig.json") return; - await writeSema.acquire(); - const filePath = path.join(root, file); - if ((await fs.stat(filePath)).isFile()) { - await fs.writeFile( - filePath, - (await fs.readFile(filePath, "utf8")).replace( - `@/`, - `${importAlias.replace(/\*/g, "")}`, - ), - ); - } - await writeSema.release(); - }), - ); - } - - if (srcDir) { - await makeDir(path.join(root, "src")); - await Promise.all( - SRC_DIR_NAMES.map(async (file) => { - await fs - .rename(path.join(root, file), path.join(root, "src", file)) - .catch((err) => { - if (err.code !== "ENOENT") { - throw err; - } - }); - }), - ); - - const isAppTemplate = template.startsWith("app"); - - // Change the `Get started by editing pages/index` / `app/page` to include `src` - const indexPageFile = path.join( - "src", - isAppTemplate ? "app" : "pages", - `${isAppTemplate ? "page" : "index"}.tsx`, - ); - - await fs.writeFile( - indexPageFile, - (await fs.readFile(indexPageFile, "utf8")).replace( - isAppTemplate ? "app/page" : "pages/index", - isAppTemplate ? "src/app/page" : "src/pages/index", - ), - ); - - if (framework === "nextjs") { - const tailwindConfigFile = path.join(root, "tailwind.config.ts"); - await fs.writeFile( - tailwindConfigFile, - (await fs.readFile(tailwindConfigFile, "utf8")).replace( - /\.\/(\w+)\/\*\*\/\*\.\{js,ts,jsx,tsx,mdx\}/g, - "./src/$1/**/*.{js,ts,jsx,tsx,mdx}", - ), - ); - } - } - /** * Update the package.json scripts. */ diff --git a/packages/create-llama/templates/types.ts b/packages/create-llama/templates/types.ts index 9cd44c9686..0877310130 100644 --- a/packages/create-llama/templates/types.ts +++ b/packages/create-llama/templates/types.ts @@ -3,12 +3,6 @@ import { PackageManager } from "../helpers/get-pkg-manager"; export type TemplateType = "simple"; export type TemplateFramework = "nextjs" | "express"; -export interface GetTemplateFileArgs { - template: TemplateType; - framework: TemplateFramework; - file: string; -} - export interface InstallTemplateArgs { appName: string; root: string; @@ -17,6 +11,4 @@ export interface InstallTemplateArgs { template: TemplateType; framework: TemplateFramework; eslint: boolean; - srcDir: boolean; - importAlias: string; } From 9e2e5a3f7fd2fe7a47577f0cdc445be787f0713b Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Thu, 26 Oct 2023 17:10:19 +0700 Subject: [PATCH 10/44] doc: update readmes --- packages/create-llama/README.md | 6 ++-- .../simple/express/README-template.md | 30 +++++++++++++++---- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/packages/create-llama/README.md b/packages/create-llama/README.md index b52ddc0355..368826db0b 100644 --- a/packages/create-llama/README.md +++ b/packages/create-llama/README.md @@ -17,14 +17,14 @@ pnpm create llama-app bunx create-llama ``` -You will be asked for the name of your project, and then whether you want to +You will be asked for the name of your project, and then which framework you want to use create a TypeScript project: ```bash -✔ Would you like to use TypeScript? … No / Yes +✔ Which framework would you like to use? › NextJS ``` -Select **Yes** to install the necessary types/dependencies and create a new TS project. +You can choose between NextJS and Express. ### Non-interactive diff --git a/packages/create-llama/templates/simple/express/README-template.md b/packages/create-llama/templates/simple/express/README-template.md index b00c2e8d80..2b4d115316 100644 --- a/packages/create-llama/templates/simple/express/README-template.md +++ b/packages/create-llama/templates/simple/express/README-template.md @@ -1,14 +1,21 @@ -1. Install the dependencies +This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Express](https://expressjs.com/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama). + +## Getting Started + +First, install the dependencies: + ``` -pnpm install +npm install ``` -2. Run the server +Second, run the development server: + ``` -pnpm run dev +npm run dev ``` -3. Call the API to LLM Chat +Then call the express API endpoint `/api/llm` to see the result: + ``` curl --location 'localhost:3000/api/llm' \ --header 'Content-Type: application/json' \ @@ -16,4 +23,15 @@ curl --location 'localhost:3000/api/llm' \ "message": "Hello", "chatHistory": [] }' -``` \ No newline at end of file +``` + +You can start editing the API by modifying `src/controllers/llm.controller.ts`. The endpoint auto-updates as you save the file. + +## Learn More + +To learn more about LlamaIndex, take a look at the following resources: + +- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features). +- [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features). + +You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome! From 18bf710549361f8b24c0c39a13711d83be335247 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Fri, 27 Oct 2023 15:20:51 +0700 Subject: [PATCH 11/44] feat: add simple chat for nextjs template --- .../simple/nextjs/README-template.md | 32 ++++---- .../nextjs/app/components/chat-avatar.tsx | 34 +++++++++ .../nextjs/app/components/chat-history.tsx | 34 +++++++++ .../nextjs/app/components/chat-item.tsx | 13 ++++ .../nextjs/app/components/chat-section.tsx | 45 +++++++++++ .../simple/nextjs/app/components/header.tsx | 28 +++++++ .../nextjs/app/components/message-form.tsx | 72 ++++++++++++++---- .../templates/simple/nextjs/app/favicon.ico | Bin 25931 -> 15406 bytes .../templates/simple/nextjs/app/globals.css | 12 +++ .../templates/simple/nextjs/app/page.tsx | 33 ++------ .../app/services/chatStorage.service.ts | 35 +++++++++ .../templates/simple/nextjs/public/llama.png | Bin 0 -> 36985 bytes .../templates/simple/nextjs/public/next.svg | 1 - .../templates/simple/nextjs/public/vercel.svg | 1 - 14 files changed, 275 insertions(+), 65 deletions(-) create mode 100644 packages/create-llama/templates/simple/nextjs/app/components/chat-avatar.tsx create mode 100644 packages/create-llama/templates/simple/nextjs/app/components/chat-history.tsx create mode 100644 packages/create-llama/templates/simple/nextjs/app/components/chat-item.tsx create mode 100644 packages/create-llama/templates/simple/nextjs/app/components/chat-section.tsx create mode 100644 packages/create-llama/templates/simple/nextjs/app/components/header.tsx create mode 100644 packages/create-llama/templates/simple/nextjs/app/services/chatStorage.service.ts create mode 100644 packages/create-llama/templates/simple/nextjs/public/llama.png delete mode 100644 packages/create-llama/templates/simple/nextjs/public/next.svg delete mode 100644 packages/create-llama/templates/simple/nextjs/public/vercel.svg diff --git a/packages/create-llama/templates/simple/nextjs/README-template.md b/packages/create-llama/templates/simple/nextjs/README-template.md index c4033664f8..1509ded7c3 100644 --- a/packages/create-llama/templates/simple/nextjs/README-template.md +++ b/packages/create-llama/templates/simple/nextjs/README-template.md @@ -1,17 +1,17 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). +This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Next.js](https://nextjs.org/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama). ## Getting Started -First, run the development server: +First, install the dependencies: -```bash +``` +npm install +``` + +Second, run the development server: + +``` npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. @@ -22,15 +22,9 @@ This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-opti ## Learn More -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! - -## Deploy on Vercel +To learn more about LlamaIndex, take a look at the following resources: -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features). +- [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features). -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. +You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome! diff --git a/packages/create-llama/templates/simple/nextjs/app/components/chat-avatar.tsx b/packages/create-llama/templates/simple/nextjs/app/components/chat-avatar.tsx new file mode 100644 index 0000000000..2f79edaea6 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/components/chat-avatar.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { ChatMessage } from "llamaindex"; +import Image from "next/image"; + +export default function ChatAvatar(chatMessage: ChatMessage) { + if (chatMessage.role === "user") { + return ( +
+ + + +
+ ); + } + + return ( +
+ Llama Logo +
+ ); +} diff --git a/packages/create-llama/templates/simple/nextjs/app/components/chat-history.tsx b/packages/create-llama/templates/simple/nextjs/app/components/chat-history.tsx new file mode 100644 index 0000000000..2eafff4c17 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/components/chat-history.tsx @@ -0,0 +1,34 @@ +"use client"; + +import ChatItem from "@/app/components/chat-item"; +import { useChat } from "@/app/components/chat-section"; +import { useEffect, useRef } from "react"; + +export default function ChatHistory() { + const scrollableChatContainerRef = useRef(null); + const { chatHistory } = useChat(); + + const scrollToBottom = () => { + if (scrollableChatContainerRef.current) { + scrollableChatContainerRef.current.scrollTop = + scrollableChatContainerRef.current.scrollHeight; + } + }; + + useEffect(() => { + scrollToBottom(); + }, [chatHistory.length]); + + return ( +
+
+ {chatHistory.map((chatMessage, index) => ( + + ))} +
+
+ ); +} diff --git a/packages/create-llama/templates/simple/nextjs/app/components/chat-item.tsx b/packages/create-llama/templates/simple/nextjs/app/components/chat-item.tsx new file mode 100644 index 0000000000..5d32c64e7d --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/components/chat-item.tsx @@ -0,0 +1,13 @@ +"use client"; + +import ChatAvatar from "@/app/components/chat-avatar"; +import { ChatMessage } from "llamaindex"; + +export default function ChatItem(chatMessage: ChatMessage) { + return ( +
+ +

{chatMessage.content}

+
+ ); +} diff --git a/packages/create-llama/templates/simple/nextjs/app/components/chat-section.tsx b/packages/create-llama/templates/simple/nextjs/app/components/chat-section.tsx new file mode 100644 index 0000000000..416c56cd24 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/components/chat-section.tsx @@ -0,0 +1,45 @@ +"use client"; + +import ChatHistory from "@/app/components/chat-history"; +import MessageForm from "@/app/components/message-form"; +import ChatStorageService from "@/app/services/chatStorage.service"; +import { ChatMessage } from "llamaindex"; +import { createContext, useContext, useEffect, useState } from "react"; + +const ChatSectionContext = createContext<{ + chatHistory: ChatMessage[]; + loadChat: () => void; +}>({ + chatHistory: [], + loadChat: () => {}, +}); + +const ChatSectionContextProvider = (props: { children: JSX.Element[] }) => { + const [chatHistory, setChatHistory] = useState([]); + + const loadChat = () => { + const data = ChatStorageService.getChatHistory(); + setChatHistory(data); + }; + + useEffect(() => { + loadChat(); + }, []); + + return ( + + {props.children} + + ); +}; + +export default function ChatSection() { + return ( + + + + + ); +} + +export const useChat = () => useContext(ChatSectionContext); diff --git a/packages/create-llama/templates/simple/nextjs/app/components/header.tsx b/packages/create-llama/templates/simple/nextjs/app/components/header.tsx new file mode 100644 index 0000000000..2b0e488f76 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/components/header.tsx @@ -0,0 +1,28 @@ +import Image from "next/image"; + +export default function Header() { + return ( +
+

+ Get started by editing  + app/page.tsx +

+ +
+ ); +} diff --git a/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx b/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx index 59f42ce3ae..fff39b9a49 100644 --- a/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx +++ b/packages/create-llama/templates/simple/nextjs/app/components/message-form.tsx @@ -1,40 +1,80 @@ "use client"; +import { useChat } from "@/app/components/chat-section"; +import ChatStorageService from "@/app/services/chatStorage.service"; import { ChatMessage } from "llamaindex"; +import { useState } from "react"; + +const LLM_API_ROUTE = "/api/llm"; export default function MessageForm() { - const testSendMessage = async (message: string) => { - const chatHistory: ChatMessage[] = []; - const apiRoute = "/api/llm"; - const response = await fetch(apiRoute, { + const { loadChat } = useChat(); + const [loading, setLoading] = useState(false); + + const getAssistantMessage = async (message: string) => { + const response = await fetch(LLM_API_ROUTE, { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ message, chatHistory }), + body: JSON.stringify({ + message, + chatHistory: ChatStorageService.getChatHistory(), + }), }); const data = await response.json(); - alert(JSON.stringify(data)); + const assistantMessage = data.result as ChatMessage; + return assistantMessage; + }; + + const sendMessage = async (message: string) => { + if (!message) return; + try { + setLoading(true); + const userMessage: ChatMessage = { content: message, role: "user" }; + ChatStorageService.addChatMessage(userMessage); + loadChat(); + const assistantMessage = await getAssistantMessage(message); + ChatStorageService.addChatMessage(assistantMessage); + setLoading(false); + loadChat(); + } catch (error: any) { + alert(JSON.stringify(error)); + } + }; + + const handleFormSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + const message = e.currentTarget.message.value; + await sendMessage(message); + }; + + const handleKeyDown = async (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + const message = e.currentTarget.value; + if (message !== "") { + await sendMessage(message); + } + } }; return (
{ - e.preventDefault(); - const message = e.currentTarget.message.value; - testSendMessage(message); - }} - className="flex flex-col items-center justify-center w-full max-w-5xl p-4 space-y-4 bg-white rounded-xl shadow-xl" + onSubmit={handleFormSubmit} + className="flex items-start justify-between w-full max-w-5xl p-4 bg-white rounded-xl shadow-xl gap-4" > - diff --git a/packages/create-llama/templates/simple/nextjs/app/favicon.ico b/packages/create-llama/templates/simple/nextjs/app/favicon.ico index 718d6fea4835ec2d246af9800eddb7ffb276240c..a1eaef62f2dfa895f1bbffc6595bb53d9604963e 100644 GIT binary patch literal 15406 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 KfA0a-1OEqKLE94m literal 25931 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 LrJugUA?W`A8`#=m diff --git a/packages/create-llama/templates/simple/nextjs/app/globals.css b/packages/create-llama/templates/simple/nextjs/app/globals.css index fd81e88583..d85e2eec9a 100644 --- a/packages/create-llama/templates/simple/nextjs/app/globals.css +++ b/packages/create-llama/templates/simple/nextjs/app/globals.css @@ -25,3 +25,15 @@ body { ) rgb(var(--background-start-rgb)); } + +.background-gradient { + background-color: #fff; + background-image: radial-gradient( + at 21% 11%, + rgba(186, 186, 233, 0.53) 0, + transparent 50% + ), + radial-gradient(at 85% 0, hsla(46, 57%, 78%, 0.52) 0, transparent 50%), + radial-gradient(at 91% 36%, rgba(194, 213, 255, 0.68) 0, transparent 50%), + radial-gradient(at 8% 40%, rgba(251, 218, 239, 0.46) 0, transparent 50%); +} diff --git a/packages/create-llama/templates/simple/nextjs/app/page.tsx b/packages/create-llama/templates/simple/nextjs/app/page.tsx index a6c7e79f09..31f51facb2 100644 --- a/packages/create-llama/templates/simple/nextjs/app/page.tsx +++ b/packages/create-llama/templates/simple/nextjs/app/page.tsx @@ -1,34 +1,11 @@ -import MessageForm from "@/app/components/message-form"; -import Image from "next/image"; +import ChatSection from "@/app/components/chat-section"; +import Header from "@/app/components/header"; export default function Home() { return ( -
-
-

- Get started by editing  - app/page.tsx -

- -
- +
+
+
); } diff --git a/packages/create-llama/templates/simple/nextjs/app/services/chatStorage.service.ts b/packages/create-llama/templates/simple/nextjs/app/services/chatStorage.service.ts new file mode 100644 index 0000000000..6f92ce86a2 --- /dev/null +++ b/packages/create-llama/templates/simple/nextjs/app/services/chatStorage.service.ts @@ -0,0 +1,35 @@ +import { ChatMessage } from "llamaindex"; + +export const CHAT_STORAGE_KEY = "chatHistory"; + +class _ChatStorageService { + public getChatHistory(): ChatMessage[] { + const chatHistory = localStorage.getItem(CHAT_STORAGE_KEY); + return chatHistory ? JSON.parse(chatHistory) : []; + } + + public saveChatHistory(chatHistory: ChatMessage[]) { + localStorage.setItem(CHAT_STORAGE_KEY, JSON.stringify(chatHistory)); + } + + public clearChatHistory() { + localStorage.removeItem(CHAT_STORAGE_KEY); + } + + public addChatMessage(chatMessage: ChatMessage) { + const chatHistory = this.getChatHistory(); + chatHistory.push(chatMessage); + this.saveChatHistory(chatHistory); + } + + constructor() { + if (typeof window !== "undefined") { + const chatHistory = localStorage.getItem(CHAT_STORAGE_KEY); + if (!chatHistory) this.saveChatHistory([]); + } + } +} + +const ChatStorageService = new _ChatStorageService(); + +export default ChatStorageService; diff --git a/packages/create-llama/templates/simple/nextjs/public/llama.png b/packages/create-llama/templates/simple/nextjs/public/llama.png new file mode 100644 index 0000000000000000000000000000000000000000..d4efba3b816bf765439c6d01b322b02684e946c3 GIT binary patch literal 36985 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 vJhQ^1K}l_EDGGCB?_Esg99ufZln>@+s literal 0 HcmV?d00001 diff --git a/packages/create-llama/templates/simple/nextjs/public/next.svg b/packages/create-llama/templates/simple/nextjs/public/next.svg deleted file mode 100644 index 5174b28c56..0000000000 --- a/packages/create-llama/templates/simple/nextjs/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/create-llama/templates/simple/nextjs/public/vercel.svg b/packages/create-llama/templates/simple/nextjs/public/vercel.svg deleted file mode 100644 index d2f8422273..0000000000 --- a/packages/create-llama/templates/simple/nextjs/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 2244da07e6fea594383dc48d8c5eea8d193f95e6 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Fri, 27 Oct 2023 16:54:21 +0700 Subject: [PATCH 12/44] added first draft of streaming nextjs template --- .../templates/streaming/nextjs/.env.example | 3 + .../streaming/nextjs/README-template.md | 30 +++++++++ .../streaming/nextjs/app/api/chat/route.ts | 62 ++++++++++++++++++ .../nextjs/app/components/chat-avatar.tsx | 34 ++++++++++ .../nextjs/app/components/chat-history.tsx | 33 ++++++++++ .../nextjs/app/components/chat-item.tsx | 13 ++++ .../nextjs/app/components/chat-section.tsx | 16 +++++ .../nextjs/app/components/header.tsx | 28 ++++++++ .../nextjs/app/components/message-form.tsx | 29 ++++++++ .../streaming/nextjs/app/favicon.ico | Bin 0 -> 15406 bytes .../streaming/nextjs/app/globals.css | 39 +++++++++++ .../templates/streaming/nextjs/app/layout.tsx | 22 +++++++ .../templates/streaming/nextjs/app/page.tsx | 11 ++++ .../templates/streaming/nextjs/eslintrc.json | 3 + .../templates/streaming/nextjs/gitignore | 35 ++++++++++ .../templates/streaming/nextjs/next-env.d.ts | 5 ++ .../templates/streaming/nextjs/next.config.js | 8 +++ .../templates/streaming/nextjs/package.json | 29 ++++++++ .../streaming/nextjs/postcss.config.js | 6 ++ .../streaming/nextjs/public/llama.png | Bin 0 -> 36985 bytes .../streaming/nextjs/tailwind.config.ts | 20 ++++++ .../templates/streaming/nextjs/tsconfig.json | 27 ++++++++ 22 files changed, 453 insertions(+) create mode 100644 packages/create-llama/templates/streaming/nextjs/.env.example create mode 100644 packages/create-llama/templates/streaming/nextjs/README-template.md create mode 100644 packages/create-llama/templates/streaming/nextjs/app/api/chat/route.ts create mode 100644 packages/create-llama/templates/streaming/nextjs/app/components/chat-avatar.tsx create mode 100644 packages/create-llama/templates/streaming/nextjs/app/components/chat-history.tsx create mode 100644 packages/create-llama/templates/streaming/nextjs/app/components/chat-item.tsx create mode 100644 packages/create-llama/templates/streaming/nextjs/app/components/chat-section.tsx create mode 100644 packages/create-llama/templates/streaming/nextjs/app/components/header.tsx create mode 100644 packages/create-llama/templates/streaming/nextjs/app/components/message-form.tsx create mode 100644 packages/create-llama/templates/streaming/nextjs/app/favicon.ico create mode 100644 packages/create-llama/templates/streaming/nextjs/app/globals.css create mode 100644 packages/create-llama/templates/streaming/nextjs/app/layout.tsx create mode 100644 packages/create-llama/templates/streaming/nextjs/app/page.tsx create mode 100644 packages/create-llama/templates/streaming/nextjs/eslintrc.json create mode 100644 packages/create-llama/templates/streaming/nextjs/gitignore create mode 100644 packages/create-llama/templates/streaming/nextjs/next-env.d.ts create mode 100644 packages/create-llama/templates/streaming/nextjs/next.config.js create mode 100644 packages/create-llama/templates/streaming/nextjs/package.json create mode 100644 packages/create-llama/templates/streaming/nextjs/postcss.config.js create mode 100644 packages/create-llama/templates/streaming/nextjs/public/llama.png create mode 100644 packages/create-llama/templates/streaming/nextjs/tailwind.config.ts create mode 100644 packages/create-llama/templates/streaming/nextjs/tsconfig.json diff --git a/packages/create-llama/templates/streaming/nextjs/.env.example b/packages/create-llama/templates/streaming/nextjs/.env.example new file mode 100644 index 0000000000..7ac0a01551 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/.env.example @@ -0,0 +1,3 @@ +# Rename this file to `.env.local` to use environment variables locally with `next dev` +# https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables +MY_HOST="example.com" diff --git a/packages/create-llama/templates/streaming/nextjs/README-template.md b/packages/create-llama/templates/streaming/nextjs/README-template.md new file mode 100644 index 0000000000..1509ded7c3 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/README-template.md @@ -0,0 +1,30 @@ +This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Next.js](https://nextjs.org/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama). + +## Getting Started + +First, install the dependencies: + +``` +npm install +``` + +Second, run the development server: + +``` +npm run dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More + +To learn more about LlamaIndex, take a look at the following resources: + +- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features). +- [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features). + +You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome! diff --git a/packages/create-llama/templates/streaming/nextjs/app/api/chat/route.ts b/packages/create-llama/templates/streaming/nextjs/app/api/chat/route.ts new file mode 100644 index 0000000000..06432075c0 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/api/chat/route.ts @@ -0,0 +1,62 @@ +import { OpenAIStream, StreamingTextResponse } from "ai"; +import { NextRequest, NextResponse } from "next/server"; +import OpenAI from "openai"; +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}); + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const { messages } = body; + if (!messages) { + return NextResponse.json( + { + error: "messages are required in the request body", + }, + { status: 400 }, + ); + } + + // const llm = new OpenAI({ + // model: "gpt-3.5-turbo", + // }); + + // const chatEngine = new SimpleChatEngine({ + // llm, + // }); + + // const response = await chatEngine.chat(message, chatHistory); + // const result: ChatMessage = { + // role: "assistant", + // content: response.response, + // }; + + // return NextResponse.json({ result }); + + const response = await openai.chat.completions.create({ + model: "gpt-4", + stream: true, + messages, + }); + + // Transform the response into a readable stream + const stream = OpenAIStream(response); + + // Return a StreamingTextResponse, which can be consumed by the client + return new StreamingTextResponse(stream); + } catch (error) { + console.error("[LlamaIndex]", error); + return NextResponse.json( + { + error: (error as Error).message, + }, + { + status: 500, + }, + ); + } +} diff --git a/packages/create-llama/templates/streaming/nextjs/app/components/chat-avatar.tsx b/packages/create-llama/templates/streaming/nextjs/app/components/chat-avatar.tsx new file mode 100644 index 0000000000..14c7b06f57 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/components/chat-avatar.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { Message } from "ai/react"; +import Image from "next/image"; + +export default function ChatAvatar(chatMessage: Message) { + if (chatMessage.role === "user") { + return ( +
+ + + +
+ ); + } + + return ( +
+ Llama Logo +
+ ); +} diff --git a/packages/create-llama/templates/streaming/nextjs/app/components/chat-history.tsx b/packages/create-llama/templates/streaming/nextjs/app/components/chat-history.tsx new file mode 100644 index 0000000000..80f5e97d9f --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/components/chat-history.tsx @@ -0,0 +1,33 @@ +"use client"; + +import ChatItem from "@/app/components/chat-item"; +import { Message } from "ai/react"; +import { useEffect, useRef } from "react"; + +export default function ChatHistory({ messages }: { messages: Message[] }) { + const scrollableChatContainerRef = useRef(null); + + const scrollToBottom = () => { + if (scrollableChatContainerRef.current) { + scrollableChatContainerRef.current.scrollTop = + scrollableChatContainerRef.current.scrollHeight; + } + }; + + useEffect(() => { + scrollToBottom(); + }, [messages.length]); + + return ( +
+
+ {messages.map((m: Message) => ( + + ))} +
+
+ ); +} diff --git a/packages/create-llama/templates/streaming/nextjs/app/components/chat-item.tsx b/packages/create-llama/templates/streaming/nextjs/app/components/chat-item.tsx new file mode 100644 index 0000000000..dc24fa0af5 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/components/chat-item.tsx @@ -0,0 +1,13 @@ +"use client"; + +import ChatAvatar from "@/app/components/chat-avatar"; +import { Message } from "ai/react"; + +export default function ChatItem(chatMessage: Message) { + return ( +
+ +

{chatMessage.content}

+
+ ); +} diff --git a/packages/create-llama/templates/streaming/nextjs/app/components/chat-section.tsx b/packages/create-llama/templates/streaming/nextjs/app/components/chat-section.tsx new file mode 100644 index 0000000000..3e0b993a5e --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/components/chat-section.tsx @@ -0,0 +1,16 @@ +"use client"; + +import MessageForm from "@/app/components/message-form"; +import { useChat } from "ai/react"; +import ChatHistory from "./chat-history"; + +export default function ChatSection() { + const chat = useChat(); + + return ( + <> + + + + ); +} diff --git a/packages/create-llama/templates/streaming/nextjs/app/components/header.tsx b/packages/create-llama/templates/streaming/nextjs/app/components/header.tsx new file mode 100644 index 0000000000..2b0e488f76 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/components/header.tsx @@ -0,0 +1,28 @@ +import Image from "next/image"; + +export default function Header() { + return ( +
+

+ Get started by editing  + app/page.tsx +

+ +
+ ); +} diff --git a/packages/create-llama/templates/streaming/nextjs/app/components/message-form.tsx b/packages/create-llama/templates/streaming/nextjs/app/components/message-form.tsx new file mode 100644 index 0000000000..ffd89762b5 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/components/message-form.tsx @@ -0,0 +1,29 @@ +"use client"; + +import { UseChatHelpers } from "ai/react"; + +export default function MessageForm({ chat }: { chat: UseChatHelpers }) { + return ( + <> + + + + + + ); +} diff --git a/packages/create-llama/templates/streaming/nextjs/app/favicon.ico b/packages/create-llama/templates/streaming/nextjs/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a1eaef62f2dfa895f1bbffc6595bb53d9604963e GIT binary patch literal 15406 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 KfA0a-1OEqKLE94m literal 0 HcmV?d00001 diff --git a/packages/create-llama/templates/streaming/nextjs/app/globals.css b/packages/create-llama/templates/streaming/nextjs/app/globals.css new file mode 100644 index 0000000000..d85e2eec9a --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/globals.css @@ -0,0 +1,39 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} + +.background-gradient { + background-color: #fff; + background-image: radial-gradient( + at 21% 11%, + rgba(186, 186, 233, 0.53) 0, + transparent 50% + ), + radial-gradient(at 85% 0, hsla(46, 57%, 78%, 0.52) 0, transparent 50%), + radial-gradient(at 91% 36%, rgba(194, 213, 255, 0.68) 0, transparent 50%), + radial-gradient(at 8% 40%, rgba(251, 218, 239, 0.46) 0, transparent 50%); +} diff --git a/packages/create-llama/templates/streaming/nextjs/app/layout.tsx b/packages/create-llama/templates/streaming/nextjs/app/layout.tsx new file mode 100644 index 0000000000..fb09770627 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/layout.tsx @@ -0,0 +1,22 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Create Llama App", + description: "Generated by create-llama", +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/packages/create-llama/templates/streaming/nextjs/app/page.tsx b/packages/create-llama/templates/streaming/nextjs/app/page.tsx new file mode 100644 index 0000000000..ef00262b4a --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/page.tsx @@ -0,0 +1,11 @@ +import Header from "@/app/components/header"; +import ChatSection from "./components/chat-section"; + +export default function Home() { + return ( +
+
+ +
+ ); +} diff --git a/packages/create-llama/templates/streaming/nextjs/eslintrc.json b/packages/create-llama/templates/streaming/nextjs/eslintrc.json new file mode 100644 index 0000000000..bffb357a71 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/packages/create-llama/templates/streaming/nextjs/gitignore b/packages/create-llama/templates/streaming/nextjs/gitignore new file mode 100644 index 0000000000..8f322f0d8f --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/packages/create-llama/templates/streaming/nextjs/next-env.d.ts b/packages/create-llama/templates/streaming/nextjs/next-env.d.ts new file mode 100644 index 0000000000..4f11a03dc6 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/packages/create-llama/templates/streaming/nextjs/next.config.js b/packages/create-llama/templates/streaming/nextjs/next.config.js new file mode 100644 index 0000000000..0b2c2bf173 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/next.config.js @@ -0,0 +1,8 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + experimental: { + serverComponentsExternalPackages: ["llamaindex"], + }, +} + +module.exports = nextConfig diff --git a/packages/create-llama/templates/streaming/nextjs/package.json b/packages/create-llama/templates/streaming/nextjs/package.json new file mode 100644 index 0000000000..399c20bcb7 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/package.json @@ -0,0 +1,29 @@ +{ + "name": "llama-index-nextjs-streaming", + "version": "1.0.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "ai": "^2", + "llamaindex": "0.0.31", + "next": "^13", + "openai": "^4.14.0", + "react": "^18", + "react-dom": "^18" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "autoprefixer": "^10", + "eslint": "^8", + "eslint-config-next": "^13", + "postcss": "^8", + "tailwindcss": "^3", + "typescript": "^5" + } +} \ No newline at end of file diff --git a/packages/create-llama/templates/streaming/nextjs/postcss.config.js b/packages/create-llama/templates/streaming/nextjs/postcss.config.js new file mode 100644 index 0000000000..33ad091d26 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/packages/create-llama/templates/streaming/nextjs/public/llama.png b/packages/create-llama/templates/streaming/nextjs/public/llama.png new file mode 100644 index 0000000000000000000000000000000000000000..d4efba3b816bf765439c6d01b322b02684e946c3 GIT binary patch literal 36985 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 vJhQ^1K}l_EDGGCB?_Esg99ufZln>@+s literal 0 HcmV?d00001 diff --git a/packages/create-llama/templates/streaming/nextjs/tailwind.config.ts b/packages/create-llama/templates/streaming/nextjs/tailwind.config.ts new file mode 100644 index 0000000000..7e4bd91a03 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/tailwind.config.ts @@ -0,0 +1,20 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: [ + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + "./app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + backgroundImage: { + "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", + "gradient-conic": + "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + }, + }, + }, + plugins: [], +}; +export default config; diff --git a/packages/create-llama/templates/streaming/nextjs/tsconfig.json b/packages/create-llama/templates/streaming/nextjs/tsconfig.json new file mode 100644 index 0000000000..c714696378 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} From 8527875f0a27dc9618e81141b9658f07890646c5 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Fri, 27 Oct 2023 17:00:55 +0700 Subject: [PATCH 13/44] added support for generating streaming template --- packages/create-llama/create-app.ts | 4 ++-- packages/create-llama/index.ts | 29 ++++++++++++++++++++++++ packages/create-llama/templates/types.ts | 2 +- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/create-llama/create-app.ts b/packages/create-llama/create-app.ts index 901723640e..29e2d837fb 100644 --- a/packages/create-llama/create-app.ts +++ b/packages/create-llama/create-app.ts @@ -12,18 +12,18 @@ import type { TemplateFramework, TemplateType } from "./templates"; import { installTemplate } from "./templates"; export async function createApp({ + template, framework, appPath, packageManager, eslint, }: { + template: TemplateType; framework: TemplateFramework; appPath: string; packageManager: PackageManager; eslint: boolean; }): Promise { - const template: TemplateType = "simple"; - const root = path.resolve(appPath); if (!(await isWriteable(path.dirname(root)))) { diff --git a/packages/create-llama/index.ts b/packages/create-llama/index.ts index 21eda3b7a7..ad53b6d43c 100644 --- a/packages/create-llama/index.ts +++ b/packages/create-llama/index.ts @@ -177,12 +177,40 @@ async function run(): Promise { >; const defaults: typeof preferences = { + template: "simple", framework: "nextjs", eslint: true, }; const getPrefOrDefault = (field: string) => preferences[field] ?? defaults[field]; + if (!program.template) { + if (ciInfo.isCI) { + program.template = getPrefOrDefault("template"); + } else { + const { template } = await prompts( + { + type: "select", + name: "template", + message: "Which template would you like to use?", + choices: [ + { title: "Simple chat without streaming", value: "simple" }, + { title: "Simple chat with streaming", value: "streaming" }, + ], + initial: 0, + }, + { + onCancel: () => { + console.error("Exiting."); + process.exit(1); + }, + }, + ); + program.template = template; + preferences.template = template; + } + } + if (!program.framework) { if (ciInfo.isCI) { program.framework = getPrefOrDefault("framework"); @@ -233,6 +261,7 @@ async function run(): Promise { } await createApp({ + template: program.template, framework: program.framework, appPath: resolvedProjectPath, packageManager, diff --git a/packages/create-llama/templates/types.ts b/packages/create-llama/templates/types.ts index 0877310130..2847856898 100644 --- a/packages/create-llama/templates/types.ts +++ b/packages/create-llama/templates/types.ts @@ -1,6 +1,6 @@ import { PackageManager } from "../helpers/get-pkg-manager"; -export type TemplateType = "simple"; +export type TemplateType = "simple" | "streaming"; export type TemplateFramework = "nextjs" | "express"; export interface InstallTemplateArgs { From 69a7ef063d52d20721b866f8e8273f62b68f41f3 Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Fri, 27 Oct 2023 17:49:21 +0700 Subject: [PATCH 14/44] added streaming for llamaindex --- .vscode/launch.json | 32 +++++++++++++ .../nextjs/app/api/chat/llamaindex-stream.ts | 35 ++++++++++++++ .../streaming/nextjs/app/api/chat/route.ts | 46 +++++++------------ .../templates/streaming/nextjs/package.json | 1 - 4 files changed, 84 insertions(+), 30 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 packages/create-llama/templates/streaming/nextjs/app/api/chat/llamaindex-stream.ts diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..50537f0566 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js: debug server-side", + "type": "node-terminal", + "request": "launch", + "cwd": "${workspaceFolder}/packages/create-llama/templates/streaming/nextjs", // Add this line to set the current working directory to the desired folder + "command": "pnpm run dev" + }, + { + "name": "Next.js: debug client-side", + "type": "chrome", + "request": "launch", + "url": "http://localhost:3000" + }, + { + "name": "Next.js: debug full stack", + "type": "node-terminal", + "request": "launch", + "command": "pnpm run dev", + "serverReadyAction": { + "pattern": "started server on .+, url: (https?://.+)", + "uriFormat": "%s", + "action": "debugWithChrome" + } + } + ], + "runtimeArgs": [ + "--preserve-symlinks" + ] +} \ No newline at end of file diff --git a/packages/create-llama/templates/streaming/nextjs/app/api/chat/llamaindex-stream.ts b/packages/create-llama/templates/streaming/nextjs/app/api/chat/llamaindex-stream.ts new file mode 100644 index 0000000000..12328de875 --- /dev/null +++ b/packages/create-llama/templates/streaming/nextjs/app/api/chat/llamaindex-stream.ts @@ -0,0 +1,35 @@ +import { + createCallbacksTransformer, + createStreamDataTransformer, + trimStartOfStreamHelper, + type AIStreamCallbacksAndOptions, +} from "ai"; + +function createParser(res: AsyncGenerator) { + const trimStartOfStream = trimStartOfStreamHelper(); + return new ReadableStream({ + async pull(controller): Promise { + const { value, done } = await res.next(); + if (done) { + controller.close(); + return; + } + + const text = trimStartOfStream(value ?? ""); + if (text) { + controller.enqueue(text); + } + }, + }); +} + +export function LlamaIndexStream( + res: AsyncGenerator, + callbacks?: AIStreamCallbacksAndOptions, +): ReadableStream { + return createParser(res) + .pipeThrough(createCallbacksTransformer(callbacks)) + .pipeThrough( + createStreamDataTransformer(callbacks?.experimental_streamData), + ); +} diff --git a/packages/create-llama/templates/streaming/nextjs/app/api/chat/route.ts b/packages/create-llama/templates/streaming/nextjs/app/api/chat/route.ts index 06432075c0..fb54dbc803 100644 --- a/packages/create-llama/templates/streaming/nextjs/app/api/chat/route.ts +++ b/packages/create-llama/templates/streaming/nextjs/app/api/chat/route.ts @@ -1,50 +1,38 @@ -import { OpenAIStream, StreamingTextResponse } from "ai"; +import { Message, StreamingTextResponse } from "ai"; +import { OpenAI, SimpleChatEngine } from "llamaindex"; import { NextRequest, NextResponse } from "next/server"; -import OpenAI from "openai"; +import { LlamaIndexStream } from "./llamaindex-stream"; + export const runtime = "nodejs"; export const dynamic = "force-dynamic"; -const openai = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY, -}); - export async function POST(request: NextRequest) { try { const body = await request.json(); - const { messages } = body; - if (!messages) { + const { messages }: { messages: Message[] } = body; + const lastMessage = messages.pop(); + if (!messages || !lastMessage || lastMessage.role !== "user") { return NextResponse.json( { - error: "messages are required in the request body", + error: + "messages are required in the request body and the last message must be from the user", }, { status: 400 }, ); } - // const llm = new OpenAI({ - // model: "gpt-3.5-turbo", - // }); - - // const chatEngine = new SimpleChatEngine({ - // llm, - // }); - - // const response = await chatEngine.chat(message, chatHistory); - // const result: ChatMessage = { - // role: "assistant", - // content: response.response, - // }; - - // return NextResponse.json({ result }); + const llm = new OpenAI({ + model: "gpt-3.5-turbo", + }); - const response = await openai.chat.completions.create({ - model: "gpt-4", - stream: true, - messages, + const chatEngine = new SimpleChatEngine({ + llm, }); + const response = await chatEngine.chat(lastMessage.content, messages, true); + // Transform the response into a readable stream - const stream = OpenAIStream(response); + const stream = LlamaIndexStream(response); // Return a StreamingTextResponse, which can be consumed by the client return new StreamingTextResponse(stream); diff --git a/packages/create-llama/templates/streaming/nextjs/package.json b/packages/create-llama/templates/streaming/nextjs/package.json index 399c20bcb7..e9f23201d8 100644 --- a/packages/create-llama/templates/streaming/nextjs/package.json +++ b/packages/create-llama/templates/streaming/nextjs/package.json @@ -11,7 +11,6 @@ "ai": "^2", "llamaindex": "0.0.31", "next": "^13", - "openai": "^4.14.0", "react": "^18", "react-dom": "^18" }, From 9db22674459fb0641c0182757bb99078faab361a Mon Sep 17 00:00:00 2001 From: Marcus Schiesser Date: Mon, 30 Oct 2023 09:07:12 +0700 Subject: [PATCH 15/44] moved components to ui folder (shadcn structure) --- .../nextjs/app/components/chat-section.tsx | 14 +++++++++----- .../nextjs/app/components/{ => ui}/chat-avatar.tsx | 0 .../{message-form.tsx => ui/chat-input.tsx} | 8 ++++---- .../nextjs/app/components/{ => ui}/chat-item.tsx | 2 +- .../{chat-history.tsx => ui/chat-messages.tsx} | 4 ++-- 5 files changed, 16 insertions(+), 12 deletions(-) rename packages/create-llama/templates/streaming/nextjs/app/components/{ => ui}/chat-avatar.tsx (100%) rename packages/create-llama/templates/streaming/nextjs/app/components/{message-form.tsx => ui/chat-input.tsx} (77%) rename packages/create-llama/templates/streaming/nextjs/app/components/{ => ui}/chat-item.tsx (83%) rename packages/create-llama/templates/streaming/nextjs/app/components/{chat-history.tsx => ui/chat-messages.tsx} (85%) diff --git a/packages/create-llama/templates/streaming/nextjs/app/components/chat-section.tsx b/packages/create-llama/templates/streaming/nextjs/app/components/chat-section.tsx index 3e0b993a5e..a48e021416 100644 --- a/packages/create-llama/templates/streaming/nextjs/app/components/chat-section.tsx +++ b/packages/create-llama/templates/streaming/nextjs/app/components/chat-section.tsx @@ -1,16 +1,20 @@ "use client"; -import MessageForm from "@/app/components/message-form"; +import ChatInput from "@/app/components/ui/chat-input"; import { useChat } from "ai/react"; -import ChatHistory from "./chat-history"; +import ChatMessages from "./ui/chat-messages"; export default function ChatSection() { - const chat = useChat(); + const { messages, input, handleSubmit, handleInputChange } = useChat(); return ( <> - - + + ); } diff --git a/packages/create-llama/templates/streaming/nextjs/app/components/chat-avatar.tsx b/packages/create-llama/templates/streaming/nextjs/app/components/ui/chat-avatar.tsx similarity index 100% rename from packages/create-llama/templates/streaming/nextjs/app/components/chat-avatar.tsx rename to packages/create-llama/templates/streaming/nextjs/app/components/ui/chat-avatar.tsx diff --git a/packages/create-llama/templates/streaming/nextjs/app/components/message-form.tsx b/packages/create-llama/templates/streaming/nextjs/app/components/ui/chat-input.tsx similarity index 77% rename from packages/create-llama/templates/streaming/nextjs/app/components/message-form.tsx rename to packages/create-llama/templates/streaming/nextjs/app/components/ui/chat-input.tsx index ffd89762b5..73ccb1698f 100644 --- a/packages/create-llama/templates/streaming/nextjs/app/components/message-form.tsx +++ b/packages/create-llama/templates/streaming/nextjs/app/components/ui/chat-input.tsx @@ -2,11 +2,11 @@ import { UseChatHelpers } from "ai/react"; -export default function MessageForm({ chat }: { chat: UseChatHelpers }) { +export default function ChatInput(props: Partial) { return ( <>