From 7f54bdc20f0d7649144b6da89fbbfc077067bc5c Mon Sep 17 00:00:00 2001 From: Denis Kralj Date: Mon, 3 Jun 2024 15:51:28 +0200 Subject: [PATCH 1/4] feat(root): include create-novu-app to monorepo --- packages/create-novu-app/.gitattributes | 2 + packages/create-novu-app/.gitignore | 132 +++++++ packages/create-novu-app/README.md | 7 + packages/create-novu-app/create-app.ts | 231 ++++++++++++ packages/create-novu-app/helpers/copy.ts | 48 +++ packages/create-novu-app/helpers/examples.ts | 106 ++++++ .../helpers/get-pkg-manager.ts | 20 ++ packages/create-novu-app/helpers/git.ts | 58 +++ packages/create-novu-app/helpers/install.ts | 48 +++ .../helpers/is-folder-empty.ts | 59 +++ packages/create-novu-app/helpers/is-online.ts | 46 +++ .../create-novu-app/helpers/is-writeable.ts | 11 + .../create-novu-app/helpers/validate-pkg.ts | 23 ++ packages/create-novu-app/index.ts | 339 ++++++++++++++++++ packages/create-novu-app/package.json | 56 +++ .../templates/.prettierrc.json | 5 + .../templates/app-react-email/ts/.env.example | 3 + .../app-react-email/ts/README-template.md | 36 ++ .../app-react-email/ts/app/api/echo/route.ts | 4 + .../app-react-email/ts/app/echo/client.ts | 64 ++++ .../ts/app/echo/emails/vercel.tsx | 173 +++++++++ .../app-react-email/ts/app/favicon.ico | Bin 0 -> 25931 bytes .../app-react-email/ts/app/globals.css | 33 ++ .../app-react-email/ts/app/layout.tsx | 23 ++ .../templates/app-react-email/ts/app/page.tsx | 114 ++++++ .../app-react-email/ts/eslintrc.json | 3 + .../templates/app-react-email/ts/gitignore | 36 ++ .../app-react-email/ts/next-env.d.ts | 5 + .../app-react-email/ts/next.config.mjs | 4 + .../app-react-email/ts/postcss.config.cjs | 5 + .../app-react-email/ts/public/next.svg | 1 + .../app-react-email/ts/public/vercel.svg | 1 + .../app-react-email/ts/tailwind.config.ts | 20 ++ .../app-react-email/ts/tsconfig.json | 26 ++ .../templates/app/ts/.env.example | 3 + .../templates/app/ts/README-template.md | 36 ++ .../templates/app/ts/app/api/echo/route.ts | 4 + .../templates/app/ts/app/echo/client.ts | 63 ++++ .../templates/app/ts/app/favicon.ico | Bin 0 -> 25931 bytes .../templates/app/ts/app/globals.css | 107 ++++++ .../templates/app/ts/app/layout.tsx | 23 ++ .../templates/app/ts/app/page.module.css | 230 ++++++++++++ .../templates/app/ts/app/page.tsx | 96 +++++ .../templates/app/ts/eslintrc.json | 3 + .../templates/app/ts/gitignore | 36 ++ .../templates/app/ts/next-env.d.ts | 5 + .../templates/app/ts/next.config.mjs | 4 + .../templates/app/ts/public/next.svg | 1 + .../templates/app/ts/public/vercel.svg | 1 + .../templates/app/ts/tsconfig.json | 26 ++ packages/create-novu-app/templates/index.ts | 256 +++++++++++++ packages/create-novu-app/templates/types.ts | 29 ++ packages/create-novu-app/tsconfig.json | 12 + pnpm-lock.yaml | 315 ++++++++++++++-- 54 files changed, 2960 insertions(+), 32 deletions(-) create mode 100644 packages/create-novu-app/.gitattributes create mode 100644 packages/create-novu-app/.gitignore create mode 100644 packages/create-novu-app/README.md create mode 100644 packages/create-novu-app/create-app.ts create mode 100644 packages/create-novu-app/helpers/copy.ts create mode 100644 packages/create-novu-app/helpers/examples.ts create mode 100644 packages/create-novu-app/helpers/get-pkg-manager.ts create mode 100644 packages/create-novu-app/helpers/git.ts create mode 100644 packages/create-novu-app/helpers/install.ts create mode 100644 packages/create-novu-app/helpers/is-folder-empty.ts create mode 100644 packages/create-novu-app/helpers/is-online.ts create mode 100644 packages/create-novu-app/helpers/is-writeable.ts create mode 100644 packages/create-novu-app/helpers/validate-pkg.ts create mode 100644 packages/create-novu-app/index.ts create mode 100644 packages/create-novu-app/package.json create mode 100644 packages/create-novu-app/templates/.prettierrc.json create mode 100644 packages/create-novu-app/templates/app-react-email/ts/.env.example create mode 100644 packages/create-novu-app/templates/app-react-email/ts/README-template.md create mode 100644 packages/create-novu-app/templates/app-react-email/ts/app/api/echo/route.ts create mode 100644 packages/create-novu-app/templates/app-react-email/ts/app/echo/client.ts create mode 100644 packages/create-novu-app/templates/app-react-email/ts/app/echo/emails/vercel.tsx create mode 100644 packages/create-novu-app/templates/app-react-email/ts/app/favicon.ico create mode 100644 packages/create-novu-app/templates/app-react-email/ts/app/globals.css create mode 100644 packages/create-novu-app/templates/app-react-email/ts/app/layout.tsx create mode 100644 packages/create-novu-app/templates/app-react-email/ts/app/page.tsx create mode 100644 packages/create-novu-app/templates/app-react-email/ts/eslintrc.json create mode 100644 packages/create-novu-app/templates/app-react-email/ts/gitignore create mode 100644 packages/create-novu-app/templates/app-react-email/ts/next-env.d.ts create mode 100644 packages/create-novu-app/templates/app-react-email/ts/next.config.mjs create mode 100644 packages/create-novu-app/templates/app-react-email/ts/postcss.config.cjs create mode 100644 packages/create-novu-app/templates/app-react-email/ts/public/next.svg create mode 100644 packages/create-novu-app/templates/app-react-email/ts/public/vercel.svg create mode 100644 packages/create-novu-app/templates/app-react-email/ts/tailwind.config.ts create mode 100644 packages/create-novu-app/templates/app-react-email/ts/tsconfig.json create mode 100644 packages/create-novu-app/templates/app/ts/.env.example create mode 100644 packages/create-novu-app/templates/app/ts/README-template.md create mode 100644 packages/create-novu-app/templates/app/ts/app/api/echo/route.ts create mode 100644 packages/create-novu-app/templates/app/ts/app/echo/client.ts create mode 100644 packages/create-novu-app/templates/app/ts/app/favicon.ico create mode 100644 packages/create-novu-app/templates/app/ts/app/globals.css create mode 100644 packages/create-novu-app/templates/app/ts/app/layout.tsx create mode 100644 packages/create-novu-app/templates/app/ts/app/page.module.css create mode 100644 packages/create-novu-app/templates/app/ts/app/page.tsx create mode 100644 packages/create-novu-app/templates/app/ts/eslintrc.json create mode 100644 packages/create-novu-app/templates/app/ts/gitignore create mode 100644 packages/create-novu-app/templates/app/ts/next-env.d.ts create mode 100644 packages/create-novu-app/templates/app/ts/next.config.mjs create mode 100644 packages/create-novu-app/templates/app/ts/public/next.svg create mode 100644 packages/create-novu-app/templates/app/ts/public/vercel.svg create mode 100644 packages/create-novu-app/templates/app/ts/tsconfig.json create mode 100644 packages/create-novu-app/templates/index.ts create mode 100644 packages/create-novu-app/templates/types.ts create mode 100644 packages/create-novu-app/tsconfig.json diff --git a/packages/create-novu-app/.gitattributes b/packages/create-novu-app/.gitattributes new file mode 100644 index 00000000000..dfe0770424b --- /dev/null +++ b/packages/create-novu-app/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/packages/create-novu-app/.gitignore b/packages/create-novu-app/.gitignore new file mode 100644 index 00000000000..b295889cf32 --- /dev/null +++ b/packages/create-novu-app/.gitignore @@ -0,0 +1,132 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +.DS_Store diff --git a/packages/create-novu-app/README.md b/packages/create-novu-app/README.md new file mode 100644 index 00000000000..30563414930 --- /dev/null +++ b/packages/create-novu-app/README.md @@ -0,0 +1,7 @@ +# Create Novu App + +The easiest way to get started with Novu workflows is to call `create-novu-app`. The command will ask you for a project name as well as if you want to use react email. + +### Why use Create Novu App? + +`create-novu-app` allows you to create a new Novu app within seconds. diff --git a/packages/create-novu-app/create-app.ts b/packages/create-novu-app/create-app.ts new file mode 100644 index 00000000000..a6b55a8ca5f --- /dev/null +++ b/packages/create-novu-app/create-app.ts @@ -0,0 +1,231 @@ +/* eslint-disable */ +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 { 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, + reactEmail, + eslint, + srcDir, + importAlias, +}: { + appPath: string; + packageManager: PackageManager; + example?: string; + examplePath?: string; + typescript: boolean; + reactEmail: boolean; + eslint: boolean; + srcDir: boolean; + importAlias: string; +}): Promise { + let repoInfo: RepoInfo | undefined; + const mode: TemplateMode = typescript ? 'ts' : 'js'; + const template: TemplateType = reactEmail ? 'app-react-email' : 'app'; + + if (example) { + let repoUrl: URL | undefined; + + try { + repoUrl = new URL(example); + } catch (error: unknown) { + const err = error as Error & { code: string | undefined }; + if (err.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); + + fs.mkdirSync(root, { recursive: true }); + 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 Echo 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, + } as any); + } else { + console.log(`Downloading files for example ${cyan(example)}. This might take a moment.`); + console.log(); + await retry(() => downloadAndExtractExample(root, example), { + retries: 3, + } as any); + } + } 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, + reactEmail, + 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-novu-app/helpers/copy.ts b/packages/create-novu-app/helpers/copy.ts new file mode 100644 index 00000000000..050c96a37e0 --- /dev/null +++ b/packages/create-novu-app/helpers/copy.ts @@ -0,0 +1,48 @@ +/* eslint-disable */ +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-novu-app/helpers/examples.ts b/packages/create-novu-app/helpers/examples.ts new file mode 100644 index 00000000000..f32605e30e6 --- /dev/null +++ b/packages/create-novu-app/helpers/examples.ts @@ -0,0 +1,106 @@ +/* eslint-disable */ +import tar from 'tar'; +import { Readable } from 'stream'; +import { pipeline } from 'stream/promises'; + +export type RepoInfo = { + username: string; + name: string; + branch: string; + filePath: string; +}; + +export async function isUrlOk(url: string): Promise { + try { + const res = await fetch(url, { method: 'HEAD' }); + return res.status === 200; + } catch { + return false; + } +} + +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) + ) { + try { + const infoResponse = await fetch(`https://api.github.com/repos/${username}/${name}`); + if (infoResponse.status !== 200) { + return; + } + + const info = await infoResponse.json(); + return { username, name, branch: info['default_branch'], filePath }; + } catch { + return; + } + } + + // 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 downloadTarStream(url: string) { + const res = await fetch(url); + + if (!res.body) { + throw new Error(`Failed to download: ${url}`); + } + + return Readable.fromWeb(res.body as import('stream/web').ReadableStream); +} + +export async function downloadAndExtractRepo(root: string, { username, name, branch, filePath }: RepoInfo) { + await pipeline( + await downloadTarStream(`https://codeload.github.com/${username}/${name}/tar.gz/${branch}`), + tar.x({ + cwd: root, + strip: filePath ? filePath.split('/').length + 1 : 1, + filter: (p) => p.startsWith(`${name}-${branch.replace(/\//g, '-')}${filePath ? `/${filePath}/` : '/'}`), + }) + ); +} + +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.'); + } + + await pipeline( + await downloadTarStream('https://codeload.github.com/vercel/next.js/tar.gz/canary'), + tar.x({ + cwd: root, + strip: 2 + name.split('/').length, + filter: (p) => p.includes(`next.js-canary/examples/${name}/`), + }) + ); +} diff --git a/packages/create-novu-app/helpers/get-pkg-manager.ts b/packages/create-novu-app/helpers/get-pkg-manager.ts new file mode 100644 index 00000000000..b457bde2522 --- /dev/null +++ b/packages/create-novu-app/helpers/get-pkg-manager.ts @@ -0,0 +1,20 @@ +/* eslint-disable */ +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-novu-app/helpers/git.ts b/packages/create-novu-app/helpers/git.ts new file mode 100644 index 00000000000..e115a637701 --- /dev/null +++ b/packages/create-novu-app/helpers/git.ts @@ -0,0 +1,58 @@ +/* eslint-disable */ +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-novu-app/helpers/install.ts b/packages/create-novu-app/helpers/install.ts new file mode 100644 index 00000000000..fe2c570f0ff --- /dev/null +++ b/packages/create-novu-app/helpers/install.ts @@ -0,0 +1,48 @@ +/* eslint-disable */ +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 { + const 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-novu-app/helpers/is-folder-empty.ts b/packages/create-novu-app/helpers/is-folder-empty.ts new file mode 100644 index 00000000000..003c75449de --- /dev/null +++ b/packages/create-novu-app/helpers/is-folder-empty.ts @@ -0,0 +1,59 @@ +/* eslint-disable */ +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 + !/\.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-novu-app/helpers/is-online.ts b/packages/create-novu-app/helpers/is-online.ts new file mode 100644 index 00000000000..5fd49c0d2e6 --- /dev/null +++ b/packages/create-novu-app/helpers/is-online.ts @@ -0,0 +1,46 @@ +/* eslint-disable */ +import { execSync } from 'child_process'; +import dns from 'dns/promises'; +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 async function getOnline(): Promise { + try { + await dns.lookup('registry.yarnpkg.com'); + // If DNS lookup succeeds, we are online + return true; + } catch { + // The DNS lookup failed, but we are still fine as long as a proxy has been set + const proxy = getProxy(); + if (!proxy) { + return false; + } + + const { hostname } = url.parse(proxy); + if (!hostname) { + // Invalid proxy URL + return false; + } + + try { + await dns.lookup(hostname); + // If DNS lookup succeeds for the proxy server, we are online + return true; + } catch { + // The DNS lookup for the proxy server also failed, so we are offline + return false; + } + } +} diff --git a/packages/create-novu-app/helpers/is-writeable.ts b/packages/create-novu-app/helpers/is-writeable.ts new file mode 100644 index 00000000000..ba00c1a450c --- /dev/null +++ b/packages/create-novu-app/helpers/is-writeable.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +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-novu-app/helpers/validate-pkg.ts b/packages/create-novu-app/helpers/validate-pkg.ts new file mode 100644 index 00000000000..9e60ec2e5d4 --- /dev/null +++ b/packages/create-novu-app/helpers/validate-pkg.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ +import validateProjectName from 'validate-npm-package-name'; + +type ValidateNpmNameResult = + | { + valid: true; + } + | { + valid: false; + problems: string[]; + }; + +export function validateNpmName(name: string): ValidateNpmNameResult { + const nameValidation = validateProjectName(name); + if (nameValidation.validForNewPackages) { + return { valid: true }; + } + + return { + valid: false, + problems: [...(nameValidation.errors || []), ...(nameValidation.warnings || [])], + }; +} diff --git a/packages/create-novu-app/index.ts b/packages/create-novu-app/index.ts new file mode 100644 index 00000000000..cb12a466247 --- /dev/null +++ b/packages/create-novu-app/index.ts @@ -0,0 +1,339 @@ +#!/usr/bin/env node +/* eslint-disable */ +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 type { InitialReturnValue } 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 = ''; + +const handleSigTerm = () => process.exit(0); + +process.on('SIGINT', handleSigTerm); +process.on('SIGTERM', handleSigTerm); + +const onPromptState = (state: { value: InitialReturnValue; aborted: boolean; exited: boolean }) => { + 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( + '--react-email', + ` + + Initialize with React Email config. (default) +` + ) + .option( + '--src-dir', + ` + + Initialize inside a \`src/\` directory. +` + ) + .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 Echo repo or a GitHub URL. The URL can use + any branch and/or subdirectory +` + ) + .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-echo-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-echo-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-echo-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 validation = validateNpmName(projectName); + if (!validation.valid) { + console.error(`Could not create a project called ${red(`"${projectName}"`)} because of npm naming restrictions:`); + + validation.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; + /** + * 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, + reactEmail: true, + app: true, + srcDir: false, + importAlias: '@/*', + customizeImportAlias: false, + }; + const getPrefOrDefault = (field: string) => preferences[field] ?? defaults[field]; + + if (!program.typescript && !program.javascript) { + /* + * default to TypeScript in CI as we can't prompt to + * prevent breaking setup flows + */ + program.typescript = getPrefOrDefault('typescript'); + } + + if (!process.argv.includes('--eslint') && !process.argv.includes('--no-eslint')) { + program.eslint = getPrefOrDefault('eslint'); + } + + if (!process.argv.includes('--react-email') && !process.argv.includes('--no-react-email')) { + if (ciInfo.isCI) { + program.tailwind = getPrefOrDefault('tailwind'); + } else { + const tw = blue('React E-mail'); + const { reactEmail } = await prompts({ + onState: onPromptState, + type: 'toggle', + name: 'reactEmail', + message: `Would you like to use ${tw}?`, + initial: getPrefOrDefault('reactEmail'), + active: 'Yes', + inactive: 'No', + }); + program.reactEmail = Boolean(reactEmail); + preferences.reactEmail = Boolean(reactEmail); + } + } + + if (!process.argv.includes('--src-dir') && !process.argv.includes('--no-src-dir')) { + program.srcDir = getPrefOrDefault('srcDir'); + } + + if (!process.argv.includes('--app') && !process.argv.includes('--no-app')) { + program.app = getPrefOrDefault('app'); + } + + if (typeof program.importAlias !== 'string' || !program.importAlias.length) { + // We don't use preferences here because the default value is @/* regardless of existing preferences + program.importAlias = defaults.importAlias; + } + } + + try { + await createApp({ + appPath: resolvedProjectPath, + packageManager, + example: example && example !== 'default' ? example : undefined, + examplePath: program.examplePath, + typescript: program.typescript, + reactEmail: program.reactEmail, + 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, + typescript: program.typescript, + eslint: program.eslint, + reactEmail: program.reactEmail, + 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-echo-app' + : packageManager === 'pnpm' + ? 'pnpm add -g create-echo-app' + : packageManager === 'bun' + ? 'bun add -g create-echo-app' + : 'npm i -g create-echo-app'; + + console.log( + yellow(bold('A new version of `create-echo-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-novu-app/package.json b/packages/create-novu-app/package.json new file mode 100644 index 00000000000..a4a0f3918c3 --- /dev/null +++ b/packages/create-novu-app/package.json @@ -0,0 +1,56 @@ +{ + "name": "create-novu-app", + "version": "0.0.1-alpha.4", + "keywords": [ + "novu" + ], + "description": "Create Novu-powered apps with one command", + "repository": { + "type": "git", + "url": "https://github.com/novuhq/novu" + }, + "author": "Novu Team ", + "license": "MIT", + "bin": { + "create-novu-app": "./dist/index.js" + }, + "files": [ + "dist" + ], + "scripts": { + "start": "node dist/index.js", + "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", + "build": "pnpm release", + "lint-fix": "pnpm prettier -w --plugin prettier-plugin-tailwindcss 'templates/*-tw/{ts,js}/{app,pages}/**/*.{js,ts,tsx}'", + "release:alpha": "npm i && npm run build && npm version prerelease --preid=alpha && npm publish" + }, + "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.4.2", + "@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", + "picocolors": "1.0.0", + "prettier-plugin-tailwindcss": "0.3.0", + "prompts": "2.4.2", + "tar": "6.1.15", + "update-check": "1.5.4", + "validate-npm-package-name": "3.0.0", + "typescript": "5.2.2", + "async-sema": "3.0.1" + }, + "engines": { + "node": ">=18.17.0" + } +} diff --git a/packages/create-novu-app/templates/.prettierrc.json b/packages/create-novu-app/templates/.prettierrc.json new file mode 100644 index 00000000000..a7d8824a651 --- /dev/null +++ b/packages/create-novu-app/templates/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "trailingComma": "all", + "singleQuote": false, + "semi": true +} diff --git a/packages/create-novu-app/templates/app-react-email/ts/.env.example b/packages/create-novu-app/templates/app-react-email/ts/.env.example new file mode 100644 index 00000000000..ce586fad292 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/.env.example @@ -0,0 +1,3 @@ +# Rename this file to `.env.local` to use environment variables locally with `next dev` +# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables +MY_HOST="example.com" diff --git a/packages/create-novu-app/templates/app-react-email/ts/README-template.md b/packages/create-novu-app/templates/app-react-email/ts/README-template.md new file mode 100644 index 00000000000..c4033664f80 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/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-novu-app/templates/app-react-email/ts/app/api/echo/route.ts b/packages/create-novu-app/templates/app-react-email/ts/app/api/echo/route.ts new file mode 100644 index 00000000000..be1cc4b3ea9 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/app/api/echo/route.ts @@ -0,0 +1,4 @@ +import { serve } from "@novu/echo/next"; +import { echo } from "../../echo/client"; + +export const { GET, POST, PUT } = serve({ client: echo }); diff --git a/packages/create-novu-app/templates/app-react-email/ts/app/echo/client.ts b/packages/create-novu-app/templates/app-react-email/ts/app/echo/client.ts new file mode 100644 index 00000000000..29e5b68fd14 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/app/echo/client.ts @@ -0,0 +1,64 @@ +import { Echo } from "@novu/echo"; +import { renderReactEmail } from "./emails/vercel"; + +export const echo = new Echo({ + /** + * Enable this flag only during local development + */ + devModeBypassAuthentication: process.env.NODE_ENV === "development", +}); + +echo.workflow( + "hello-world", + async ({ step }) => { + await step.email( + "send-email", + async (inputs) => { + return { + subject: "This is an email subject", + body: renderReactEmail(inputs), + }; + }, + { + inputSchema: { + type: "object", + + properties: { + showButton: { type: "boolean", default: true }, + username: { type: "string", default: "alanturing" }, + userImage: { + type: "string", + default: + "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-user.png", + format: "uri", + }, + invitedByUsername: { type: "string", default: "Alan" }, + invitedByEmail: { + type: "string", + default: "alan.turing@example.com", + format: "email", + }, + teamName: { type: "string", default: "Team Awesome" }, + teamImage: { + type: "string", + default: + "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-team.png", + format: "uri", + }, + inviteLink: { + type: "string", + default: "https://vercel.com/teams/invite/foo", + format: "uri", + }, + inviteFromIp: { type: "string", default: "204.13.186.218" }, + inviteFromLocation: { + type: "string", + default: "São Paulo, Brazil", + }, + }, + }, + }, + ); + }, + { payloadSchema: { type: "object", properties: {} } }, +); diff --git a/packages/create-novu-app/templates/app-react-email/ts/app/echo/emails/vercel.tsx b/packages/create-novu-app/templates/app-react-email/ts/app/echo/emails/vercel.tsx new file mode 100644 index 00000000000..486f011e2c3 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/app/echo/emails/vercel.tsx @@ -0,0 +1,173 @@ +import { + Body, + Button, + Container, + Column, + Head, + Heading, + Hr, + Html, + Img, + Link, + Preview, + Row, + Section, + Text, + render, +} from "@react-email/components"; +import { Tailwind } from "@react-email/tailwind"; +import * as React from "react"; + +const baseUrl = process.env.VERCEL_URL + ? `https://react-email-demo-bdj5iju9r-resend.vercel.app` + : "https://react-email-demo-bdj5iju9r-resend.vercel.app"; + +// eslint-disable-next-line @typescript-eslint/naming-convention +export const VercelInviteUserEmail = ({ + username, + showButton, + userImage, + invitedByUsername, + invitedByEmail, + teamName, + teamImage, + inviteLink, + inviteFromIp, + inviteFromLocation, + listItems, +}: any) => { + const previewText = `Join ${invitedByUsername} on Vercel`; + + return ( + + + {previewText} + + + +
+ Vercel +
+ + Joined {teamName} on Vercel + + + Hello {username}, + + + {invitedByUsername} ( + + {invitedByEmail} + + ) has invited you to the {teamName} team on{" "} + Vercel. + +
+ + + + + + invited you to + + + + + +
+ + {listItems?.map((item: string) => { + return ( + + {item === "component1" ? ( + + + + ) : null} + {item === "component2" ? ( + + + {item} + + + ) : null} + {item === "component3" ? ( + + {" "} +
+
+ ) : null} + {item === "component4" ? IMAGE : null} +
+ ); + })} + {showButton && ( +
+ +
+ )} + +
+ + or copy and paste this URL into your browser:{" "} + + {inviteLink} + + +
+ + This invitation was intended for{" "} + {username}. This invite was + sent from {inviteFromIp}{" "} + located in{" "} + {inviteFromLocation}. If you + were not expecting this invitation, you can ignore this email. If + you are concerned about your account's safety, please reply + to this email to get in touch with us. + +
+ +
+ + ); +}; + +export default VercelInviteUserEmail; + +export function renderReactEmail(inputs: any) { + return render(); +} diff --git a/packages/create-novu-app/templates/app-react-email/ts/app/favicon.ico b/packages/create-novu-app/templates/app-react-email/ts/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-novu-app/templates/app-react-email/ts/app/globals.css b/packages/create-novu-app/templates/app-react-email/ts/app/globals.css new file mode 100644 index 00000000000..875c01e819b --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/app/globals.css @@ -0,0 +1,33 @@ +@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)); +} + +@layer utilities { + .text-balance { + text-wrap: balance; + } +} diff --git a/packages/create-novu-app/templates/app-react-email/ts/app/layout.tsx b/packages/create-novu-app/templates/app-react-email/ts/app/layout.tsx new file mode 100644 index 00000000000..b03e2f83d8e --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/app/layout.tsx @@ -0,0 +1,23 @@ +/* eslint-disable */ +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 Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/packages/create-novu-app/templates/app-react-email/ts/app/page.tsx b/packages/create-novu-app/templates/app-react-email/ts/app/page.tsx new file mode 100644 index 00000000000..8c766cfbc1a --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/app/page.tsx @@ -0,0 +1,114 @@ +import Image from "next/image"; + +// eslint-disable-next-line @typescript-eslint/naming-convention +export default function Home() { + return ( +
+
+

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

+ +
+ +
+ Next.js Logo +
+ + +
+ ); +} diff --git a/packages/create-novu-app/templates/app-react-email/ts/eslintrc.json b/packages/create-novu-app/templates/app-react-email/ts/eslintrc.json new file mode 100644 index 00000000000..bffb357a712 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/packages/create-novu-app/templates/app-react-email/ts/gitignore b/packages/create-novu-app/templates/app-react-email/ts/gitignore new file mode 100644 index 00000000000..fd3dbb571a1 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# 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-novu-app/templates/app-react-email/ts/next-env.d.ts b/packages/create-novu-app/templates/app-react-email/ts/next-env.d.ts new file mode 100644 index 00000000000..4f11a03dc6c --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/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-novu-app/templates/app-react-email/ts/next.config.mjs b/packages/create-novu-app/templates/app-react-email/ts/next.config.mjs new file mode 100644 index 00000000000..4678774e6d6 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/packages/create-novu-app/templates/app-react-email/ts/postcss.config.cjs b/packages/create-novu-app/templates/app-react-email/ts/postcss.config.cjs new file mode 100644 index 00000000000..ee5f90b3090 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/postcss.config.cjs @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + tailwindcss: {}, + }, +}; diff --git a/packages/create-novu-app/templates/app-react-email/ts/public/next.svg b/packages/create-novu-app/templates/app-react-email/ts/public/next.svg new file mode 100644 index 00000000000..5174b28c565 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/create-novu-app/templates/app-react-email/ts/public/vercel.svg b/packages/create-novu-app/templates/app-react-email/ts/public/vercel.svg new file mode 100644 index 00000000000..d2f84222734 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/create-novu-app/templates/app-react-email/ts/tailwind.config.ts b/packages/create-novu-app/templates/app-react-email/ts/tailwind.config.ts new file mode 100644 index 00000000000..7e4bd91a034 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/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-novu-app/templates/app-react-email/ts/tsconfig.json b/packages/create-novu-app/templates/app-react-email/ts/tsconfig.json new file mode 100644 index 00000000000..e7ff90fd276 --- /dev/null +++ b/packages/create-novu-app/templates/app-react-email/ts/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "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-novu-app/templates/app/ts/.env.example b/packages/create-novu-app/templates/app/ts/.env.example new file mode 100644 index 00000000000..ce586fad292 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/.env.example @@ -0,0 +1,3 @@ +# Rename this file to `.env.local` to use environment variables locally with `next dev` +# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables +MY_HOST="example.com" diff --git a/packages/create-novu-app/templates/app/ts/README-template.md b/packages/create-novu-app/templates/app/ts/README-template.md new file mode 100644 index 00000000000..c4033664f80 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/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-novu-app/templates/app/ts/app/api/echo/route.ts b/packages/create-novu-app/templates/app/ts/app/api/echo/route.ts new file mode 100644 index 00000000000..be1cc4b3ea9 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/app/api/echo/route.ts @@ -0,0 +1,4 @@ +import { serve } from "@novu/echo/next"; +import { echo } from "../../echo/client"; + +export const { GET, POST, PUT } = serve({ client: echo }); diff --git a/packages/create-novu-app/templates/app/ts/app/echo/client.ts b/packages/create-novu-app/templates/app/ts/app/echo/client.ts new file mode 100644 index 00000000000..d23ae77d8ed --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/app/echo/client.ts @@ -0,0 +1,63 @@ +import { Echo } from "@novu/echo"; + +export const echo = new Echo({ + /** + * Enable this flag only during local development + */ + devModeBypassAuthentication: process.env.NODE_ENV === "development", +}); + +echo.workflow( + "hello-world", + async ({ step }) => { + await step.email( + "send-email", + async (inputs) => { + return { + subject: "This is an email subject", + body: "Test Email " + inputs.showButton, + }; + }, + { + inputSchema: { + type: "object", + + properties: { + showButton: { type: "boolean", default: true }, + username: { type: "string", default: "alanturing" }, + userImage: { + type: "string", + default: + "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-user.png", + format: "uri", + }, + invitedByUsername: { type: "string", default: "Alan" }, + invitedByEmail: { + type: "string", + default: "alan.turing@example.com", + format: "email", + }, + teamName: { type: "string", default: "Team Tomer" }, + teamImage: { + type: "string", + default: + "https://react-email-demo-bdj5iju9r-resend.vercel.app/static/vercel-team.png", + format: "uri", + }, + inviteLink: { + type: "string", + default: "https://vercel.com/teams/invite/foo", + format: "uri", + }, + inviteFromIp: { type: "string", default: "204.13.186.218" }, + inviteFromLocation: { + type: "string", + default: "São Paulo, Brazil", + }, + }, + }, + }, + ); + }, + { payloadSchema: { type: "object", properties: {} } }, +); diff --git a/packages/create-novu-app/templates/app/ts/app/favicon.ico b/packages/create-novu-app/templates/app/ts/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-novu-app/templates/app/ts/app/globals.css b/packages/create-novu-app/templates/app/ts/app/globals.css new file mode 100644 index 00000000000..f4bd77c0cca --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/app/globals.css @@ -0,0 +1,107 @@ +:root { + --max-width: 1100px; + --border-radius: 12px; + --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", + "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", + "Fira Mono", "Droid Sans Mono", "Courier New", monospace; + + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; + + --primary-glow: conic-gradient( + from 180deg at 50% 50%, + #16abff33 0deg, + #0885ff33 55deg, + #54d6ff33 120deg, + #0071ff33 160deg, + transparent 360deg + ); + --secondary-glow: radial-gradient( + rgba(255, 255, 255, 1), + rgba(255, 255, 255, 0) + ); + + --tile-start-rgb: 239, 245, 249; + --tile-end-rgb: 228, 232, 233; + --tile-border: conic-gradient( + #00000080, + #00000040, + #00000030, + #00000020, + #00000010, + #00000010, + #00000080 + ); + + --callout-rgb: 238, 240, 241; + --callout-border-rgb: 172, 175, 176; + --card-rgb: 180, 185, 188; + --card-border-rgb: 131, 134, 135; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + + --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); + --secondary-glow: linear-gradient( + to bottom right, + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0.3) + ); + + --tile-start-rgb: 2, 13, 46; + --tile-end-rgb: 2, 5, 19; + --tile-border: conic-gradient( + #ffffff80, + #ffffff40, + #ffffff30, + #ffffff20, + #ffffff10, + #ffffff10, + #ffffff80 + ); + + --callout-rgb: 20, 20, 20; + --callout-border-rgb: 108, 108, 108; + --card-rgb: 100, 100, 100; + --card-border-rgb: 200, 200, 200; + } +} + +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} + +html, +body { + max-width: 100vw; + overflow-x: hidden; +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} + +a { + color: inherit; + text-decoration: none; +} + +@media (prefers-color-scheme: dark) { + html { + color-scheme: dark; + } +} diff --git a/packages/create-novu-app/templates/app/ts/app/layout.tsx b/packages/create-novu-app/templates/app/ts/app/layout.tsx new file mode 100644 index 00000000000..37c0b3f083d --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/app/layout.tsx @@ -0,0 +1,23 @@ +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 Next App", + description: "Generated by create next app", +}; + +// eslint-disable-next-line @typescript-eslint/naming-convention +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/packages/create-novu-app/templates/app/ts/app/page.module.css b/packages/create-novu-app/templates/app/ts/app/page.module.css new file mode 100644 index 00000000000..5c4b1e6a2c6 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/app/page.module.css @@ -0,0 +1,230 @@ +.main { + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + padding: 6rem; + min-height: 100vh; +} + +.description { + display: inherit; + justify-content: inherit; + align-items: inherit; + font-size: 0.85rem; + max-width: var(--max-width); + width: 100%; + z-index: 2; + font-family: var(--font-mono); +} + +.description a { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; +} + +.description p { + position: relative; + margin: 0; + padding: 1rem; + background-color: rgba(var(--callout-rgb), 0.5); + border: 1px solid rgba(var(--callout-border-rgb), 0.3); + border-radius: var(--border-radius); +} + +.code { + font-weight: 700; + font-family: var(--font-mono); +} + +.grid { + display: grid; + grid-template-columns: repeat(4, minmax(25%, auto)); + max-width: 100%; + width: var(--max-width); +} + +.card { + padding: 1rem 1.2rem; + border-radius: var(--border-radius); + background: rgba(var(--card-rgb), 0); + border: 1px solid rgba(var(--card-border-rgb), 0); + transition: background 200ms, border 200ms; +} + +.card span { + display: inline-block; + transition: transform 200ms; +} + +.card h2 { + font-weight: 600; + margin-bottom: 0.7rem; +} + +.card p { + margin: 0; + opacity: 0.6; + font-size: 0.9rem; + line-height: 1.5; + max-width: 30ch; + text-wrap: balance; +} + +.center { + display: flex; + justify-content: center; + align-items: center; + position: relative; + padding: 4rem 0; +} + +.center::before { + background: var(--secondary-glow); + border-radius: 50%; + width: 480px; + height: 360px; + margin-left: -400px; +} + +.center::after { + background: var(--primary-glow); + width: 240px; + height: 180px; + z-index: -1; +} + +.center::before, +.center::after { + content: ""; + left: 50%; + position: absolute; + filter: blur(45px); + transform: translateZ(0); +} + +.logo { + position: relative; +} +/* Enable hover only on non-touch devices */ +@media (hover: hover) and (pointer: fine) { + .card:hover { + background: rgba(var(--card-rgb), 0.1); + border: 1px solid rgba(var(--card-border-rgb), 0.15); + } + + .card:hover span { + transform: translateX(4px); + } +} + +@media (prefers-reduced-motion) { + .card:hover span { + transform: none; + } +} + +/* Mobile */ +@media (max-width: 700px) { + .content { + padding: 4rem; + } + + .grid { + grid-template-columns: 1fr; + margin-bottom: 120px; + max-width: 320px; + text-align: center; + } + + .card { + padding: 1rem 2.5rem; + } + + .card h2 { + margin-bottom: 0.5rem; + } + + .center { + padding: 8rem 0 6rem; + } + + .center::before { + transform: none; + height: 300px; + } + + .description { + font-size: 0.8rem; + } + + .description a { + padding: 1rem; + } + + .description p, + .description div { + display: flex; + justify-content: center; + position: fixed; + width: 100%; + } + + .description p { + align-items: center; + inset: 0 0 auto; + padding: 2rem 1rem 1.4rem; + border-radius: 0; + border: none; + border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); + background: linear-gradient( + to bottom, + rgba(var(--background-start-rgb), 1), + rgba(var(--callout-rgb), 0.5) + ); + background-clip: padding-box; + backdrop-filter: blur(24px); + } + + .description div { + align-items: flex-end; + pointer-events: none; + inset: auto 0 0; + padding: 2rem; + height: 200px; + background: linear-gradient( + to bottom, + transparent 0%, + rgb(var(--background-end-rgb)) 40% + ); + z-index: 1; + } +} + +/* Tablet and Smaller Desktop */ +@media (min-width: 701px) and (max-width: 1120px) { + .grid { + grid-template-columns: repeat(2, 50%); + } +} + +@media (prefers-color-scheme: dark) { + .vercelLogo { + filter: invert(1); + } + + .logo { + filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); + } +} + +@keyframes rotate { + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } +} diff --git a/packages/create-novu-app/templates/app/ts/app/page.tsx b/packages/create-novu-app/templates/app/ts/app/page.tsx new file mode 100644 index 00000000000..03182f325c4 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/app/page.tsx @@ -0,0 +1,96 @@ +import Image from "next/image"; +import styles from "./page.module.css"; + +// eslint-disable-next-line @typescript-eslint/naming-convention +export default function Home() { + return ( +
+
+

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

+ +
+ +
+ Next.js Logo +
+ + +
+ ); +} diff --git a/packages/create-novu-app/templates/app/ts/eslintrc.json b/packages/create-novu-app/templates/app/ts/eslintrc.json new file mode 100644 index 00000000000..bffb357a712 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/packages/create-novu-app/templates/app/ts/gitignore b/packages/create-novu-app/templates/app/ts/gitignore new file mode 100644 index 00000000000..fd3dbb571a1 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# 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-novu-app/templates/app/ts/next-env.d.ts b/packages/create-novu-app/templates/app/ts/next-env.d.ts new file mode 100644 index 00000000000..4f11a03dc6c --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/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-novu-app/templates/app/ts/next.config.mjs b/packages/create-novu-app/templates/app/ts/next.config.mjs new file mode 100644 index 00000000000..4678774e6d6 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/packages/create-novu-app/templates/app/ts/public/next.svg b/packages/create-novu-app/templates/app/ts/public/next.svg new file mode 100644 index 00000000000..5174b28c565 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/create-novu-app/templates/app/ts/public/vercel.svg b/packages/create-novu-app/templates/app/ts/public/vercel.svg new file mode 100644 index 00000000000..d2f84222734 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/create-novu-app/templates/app/ts/tsconfig.json b/packages/create-novu-app/templates/app/ts/tsconfig.json new file mode 100644 index 00000000000..e7ff90fd276 --- /dev/null +++ b/packages/create-novu-app/templates/app/ts/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "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-novu-app/templates/index.ts b/packages/create-novu-app/templates/index.ts new file mode 100644 index 00000000000..6cc6526f2af --- /dev/null +++ b/packages/create-novu-app/templates/index.ts @@ -0,0 +1,256 @@ +/* eslint-disable */ +import { install } from "../helpers/install"; +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 pkg 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 Next.js internal template to a given `root` directory. + */ +export const installTemplate = async ({ + appName, + root, + packageManager, + isOnline, + template, + mode, + reactEmail, + 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 (!reactEmail) { + copySource.push( + mode == "ts" ? "tailwind.config.ts" : "!tailwind.config.js", + "!postcss.config.cjs", + ); + } + + 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, + // We don't want to modify compiler options in [ts/js]config.json + // and none of the files in the .git folder + ignore: ["tsconfig.json", "jsconfig.json", ".git/**/*"], + }); + const writeSema = new Sema(8, { capacity: files.length }); + await Promise.all( + files.map(async (file) => { + 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, "")}`), + ); + } + writeSema.release(); + }), + ); + } + + if (srcDir) { + await fs.mkdir(path.join(root, "src"), { recursive: true }); + 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"}.${mode === "ts" ? "tsx" : "js"}`, + ); + + await fs.writeFile( + indexPageFile, + ( + await fs.readFile(indexPageFile, "utf8") + ).replace( + isAppTemplate ? "app/page" : "pages/index", + isAppTemplate ? "src/app/page" : "src/pages/index", + ), + ); + + if (reactEmail) { + const tailwindConfigFile = path.join( + root, + mode === "ts" ? "tailwind.config.ts" : "tailwind.config.js", + ); + await fs.writeFile( + tailwindConfigFile, + ( + await fs.readFile(tailwindConfigFile, "utf8") + ).replace( + /\.\/(\w+)\/\*\*\/\*\.\{js,ts,jsx,tsx,mdx\}/g, + "./src/$1/**/*.{js,ts,jsx,tsx,mdx}", + ), + ); + } + } + + /** Copy the version from package.json or override for tests. */ + const version = "14.2.3"; + + /** 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 --port=4000", + build: "next build", + start: "next start", + lint: "next lint", + }, + /** + * Default dependencies. + */ + dependencies: { + react: "^18", + "react-dom": "^18", + next: version, + "@novu/echo": "latest", + }, + devDependencies: {}, + }; + + /** + * TypeScript projects will have type definitions and other devDependencies. + */ + if (mode === "ts") { + packageJson.devDependencies = { + ...packageJson.devDependencies, + typescript: "^5", + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + }; + } + + /* Add Tailwind CSS dependencies. */ + if (reactEmail) { + packageJson.devDependencies = { + ...packageJson.devDependencies, + postcss: "^8", + tailwindcss: "^3.4.1", + }; + + packageJson.dependencies = { + ...packageJson.dependencies, + "@react-email/components": "0.0.17", + "@react-email/tailwind": "0.0.16", + "react-email": "2.1.2", + }; + } + + /* 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-novu-app/templates/types.ts b/packages/create-novu-app/templates/types.ts new file mode 100644 index 00000000000..ce92fb357b2 --- /dev/null +++ b/packages/create-novu-app/templates/types.ts @@ -0,0 +1,29 @@ +/* eslint-disable */ +import { PackageManager } from "../helpers/get-pkg-manager"; + +export type TemplateType = + | "default" + | "app" + | "default-react-email" + | "app-react-email"; +export type TemplateMode = "js" | "ts"; + +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; + reactEmail: boolean; + srcDir: boolean; + importAlias: string; +} diff --git a/packages/create-novu-app/tsconfig.json b/packages/create-novu-app/tsconfig.json new file mode 100644 index 00000000000..5b7524c0271 --- /dev/null +++ b/packages/create-novu-app/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2019", + "moduleResolution": "node", + "strict": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "skipLibCheck": false, + "jsx": "preserve" + }, + "exclude": ["templates", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db0144ec102..7abb701ae95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3674,6 +3674,75 @@ importers: specifier: 4.9.5 version: 4.9.5 + packages/create-novu-app: + 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.12.12 + '@types/prompts': + specifier: 2.4.2 + version: 2.4.2 + '@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 + async-sema: + specifier: 3.0.1 + version: 3.0.1 + ci-info: + specifier: watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540 + version: https://codeload.github.com/watson/ci-info/tar.gz/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 + picocolors: + specifier: 1.0.0 + version: 1.0.0 + prettier-plugin-tailwindcss: + specifier: 0.3.0 + version: 0.3.0(prettier@3.2.5) + prompts: + specifier: 2.4.2 + version: 2.4.2 + tar: + specifier: 7.1.0 + version: 7.1.0 + 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/echo: dependencies: ajv: @@ -14581,6 +14650,9 @@ packages: '@types/aria-query@5.0.2': resolution: {integrity: sha512-PHKZuMN+K5qgKIWhBodXzQslTo5P+K/6LqeKXS6O/4liIDdZqaX5RXrCK++LAw+y/nptN48YmUMFiQHRSWYwtQ==} + '@types/async-retry@1.4.2': + resolution: {integrity: sha512-GUDuJURF0YiJZ+CBjNQA0+vbP/VHlJbB0sFqkzsV7EcOPRfurVonXpXKAt3w8qIjM1TEzpz6hc6POocPvHOS3w==} + '@types/async@3.2.18': resolution: {integrity: sha512-/IsuXp3B9R//uRLi40VlIYoMp7OzhkunPe2fDu7jGfQXI9y3CDCx6FC4juRLSqrpmLst3vgsiK536AAGJFl4Ww==} @@ -14635,6 +14707,9 @@ packages: '@types/chai@4.3.4': resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} + '@types/ci-info@2.0.0': + resolution: {integrity: sha512-5R2/MHILQLDCzTuhs1j4Qqq8AaKUf7Ma4KSSkCtc12+fMs47zfa34qhto9goxpyX00tQK1zxB885VCiawZ5Qhg==} + '@types/command-line-args@5.2.0': resolution: {integrity: sha512-UuKzKpJJ/Ief6ufIaIzr3A/0XnluX7RvFgwkV89Yzvm77wCh1kFaFmqN8XEnGcN62EuHdedQjEMb8mYxFLGPyA==} @@ -14671,6 +14746,9 @@ packages: '@types/cors@2.8.13': resolution: {integrity: sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==} + '@types/cross-spawn@6.0.0': + resolution: {integrity: sha512-evp2ZGsFw9YKprDbg8ySgC9NA15g3YgiI8ANkGmKKvvi0P2aDGYLPxQIC5qfeKNUOe3TjABVGuah6omPRpIYhg==} + '@types/cross-spawn@6.0.3': resolution: {integrity: sha512-BDAkU7WHHRHnvBf5z89lcvACsvkz/n7Tv+HyD/uW76O29HoH1Tk/W6iQrepaZVbisvlEek4ygwT8IW7ow9XLAA==} @@ -15116,6 +15194,9 @@ packages: '@types/pretty-hrtime@1.0.1': resolution: {integrity: sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==} + '@types/prompts@2.4.2': + resolution: {integrity: sha512-TwNx7qsjvRIUv/BCx583tqF5IINEVjCNqg9ofKHRlSoUHE62WBHrem4B1HGXcIrG511v29d1kJ9a/t2Esz7MIg==} + '@types/prop-types@15.7.5': resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} @@ -15246,6 +15327,9 @@ packages: '@types/tapable@1.0.9': resolution: {integrity: sha512-fOHIwZua0sRltqWzODGUM6b4ffZrf/vzGUmNXdR+4DzuJP42PMbM5dLKcdzlYvv8bMJ3GALOzkk1q7cDm2zPyA==} + '@types/tar@6.1.5': + resolution: {integrity: sha512-qm2I/RlZij5RofuY7vohTpYNaYcrSQlN2MyjucQc7ZweDwaEWkdN/EeNh6e9zjK6uEm6PwjdMXkcj05BxZdX1Q==} + '@types/tedious@4.0.14': resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} @@ -15291,6 +15375,9 @@ packages: '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + '@types/validate-npm-package-name@3.0.0': + resolution: {integrity: sha512-iFNNIrEaJH1lbPiyX+O/QyxSbKxrTjdNBVZGckt+iEL9So0hdZNBL68sOfHnt2txuUD8UJXvmKv/1DkgkebgUg==} + '@types/validator@13.7.14': resolution: {integrity: sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g==} @@ -15574,6 +15661,10 @@ packages: '@vercel/error-utils@1.0.10': resolution: {integrity: sha512-nsKy2sy+pjUWyKI1V/XXKspVzHMYgSalmj5+EsKWFXZbnNZicqxNtMR94J8Hs7SB4TQxh0s4KhczJtL59AVGMg==} + '@vercel/ncc@0.34.0': + resolution: {integrity: sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==} + hasBin: true + '@vercel/node@2.15.10': resolution: {integrity: sha512-IfnqnKAJlL1+0FSDJgxoe9J3kfYAgPGDjz4aO/H5FSjvqP7cKJnns1F9GsQq4pM499+TY8T8mKAdos7/m+WOEw==} @@ -16637,9 +16728,15 @@ packages: resolution: {integrity: sha512-V+SsTpDqkrWTimiotsyl33ePSjA5/KrithwupuvJ6ztsqPvGv6ge4OredFhPffVXiLN/QUWvE0XcqJaYgt6fOg==} engines: {node: '>= 14'} + async-retry@1.3.1: + resolution: {integrity: sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==} + async-retry@1.3.3: resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + async-sema@3.0.1: + resolution: {integrity: sha512-fKT2riE8EHAvJEfLJXZiATQWqZttjx1+tfgnVshCDrH8vlw4YC8aECe0B8MU184g+aVRFVgmfxFlKZKaozSrNw==} + async-validator@4.2.5: resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} @@ -16671,6 +16768,10 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} + atomically@1.7.0: + resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==} + engines: {node: '>=10.12.0'} + attr-accept@2.2.2: resolution: {integrity: sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==} engines: {node: '>=4'} @@ -17507,6 +17608,10 @@ packages: resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} engines: {node: '>=8'} + ci-info@https://codeload.github.com/watson/ci-info/tar.gz/f43f6a1cefff47fb361c88cf4b943fdbcaafe540: + resolution: {tarball: https://codeload.github.com/watson/ci-info/tar.gz/f43f6a1cefff47fb361c88cf4b943fdbcaafe540} + version: 2.0.0 + cjs-module-lexer@1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} @@ -17784,8 +17889,8 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} - 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==} @@ -17890,6 +17995,10 @@ packages: resolution: {integrity: sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==} engines: {node: '>=0.10.0'} + conf@10.2.0: + resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==} + engines: {node: '>=12'} + config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -18542,6 +18651,10 @@ packages: de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + debounce-fn@4.0.0: + resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==} + engines: {node: '>=10'} + debounce@1.2.1: resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} @@ -20835,7 +20948,6 @@ packages: glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} @@ -22808,6 +22920,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@7.0.3: + resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -24227,10 +24342,6 @@ packages: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - minipass@7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} - engines: {node: '>=16 || 14 >=14.17'} - minipass@7.1.1: resolution: {integrity: sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==} engines: {node: '>=16 || 14 >=14.17'} @@ -26405,6 +26516,58 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} + prettier-plugin-tailwindcss@0.3.0: + 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 + prettier@2.8.7: resolution: {integrity: sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==} engines: {node: '>=10.13.0'} @@ -27630,10 +27793,17 @@ packages: resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} + registry-auth-token@3.3.2: + resolution: {integrity: sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==} + registry-auth-token@5.0.2: resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} engines: {node: '>=14'} + registry-url@3.1.0: + resolution: {integrity: sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==} + engines: {node: '>=0.10.0'} + registry-url@5.1.0: resolution: {integrity: sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==} engines: {node: '>=8'} @@ -27916,10 +28086,12 @@ packages: rimraf@2.4.5: resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@2.7.1: @@ -29990,6 +30162,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} @@ -30230,6 +30407,9 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-check@1.5.4: + resolution: {integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==} + upper-case-first@1.1.2: resolution: {integrity: sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==} @@ -31883,7 +32063,7 @@ snapshots: endent: 2.1.0 enquirer: 2.3.6 env-ci: 5.5.0 - fast-glob: 3.3.2 + fast-glob: 3.3.1 fp-ts: 2.13.1 fromentries: 1.3.2 gitlog: 4.0.4 @@ -44356,7 +44536,7 @@ snapshots: '@pandacss/types': 0.31.0 browserslist: 4.23.0 chokidar: 3.6.0 - fast-glob: 3.3.2 + fast-glob: 3.3.1 file-size: 1.0.0 filesize: 10.1.0 fs-extra: 11.1.1 @@ -44855,7 +45035,7 @@ snapshots: '@pnpm/read-project-manifest': 5.0.0 '@pnpm/types': 9.0.0 '@pnpm/util.lex-comparator': 1.0.0 - fast-glob: 3.3.2 + fast-glob: 3.3.1 p-filter: 2.1.0 '@pnpm/git-utils@1.0.0': @@ -50547,14 +50727,14 @@ snapshots: '@ts-morph/common@0.11.1': dependencies: - fast-glob: 3.3.2 + fast-glob: 3.3.1 minimatch: 3.1.2 mkdirp: 1.0.4 path-browserify: 1.0.1 '@ts-morph/common@0.20.0': dependencies: - fast-glob: 3.3.2 + fast-glob: 3.3.1 minimatch: 7.4.6 mkdirp: 2.1.6 path-browserify: 1.0.1 @@ -50593,6 +50773,10 @@ snapshots: '@types/aria-query@5.0.2': {} + '@types/async-retry@1.4.2': + dependencies: + '@types/retry': 0.12.0 + '@types/async@3.2.18': {} '@types/aws-lambda@8.10.122': {} @@ -50671,6 +50855,8 @@ snapshots: '@types/chai@4.3.4': {} + '@types/ci-info@2.0.0': {} + '@types/command-line-args@5.2.0': {} '@types/command-line-usage@5.0.2': {} @@ -50709,6 +50895,10 @@ snapshots: dependencies: '@types/node': 14.18.42 + '@types/cross-spawn@6.0.0': + dependencies: + '@types/node': 14.18.42 + '@types/cross-spawn@6.0.3': dependencies: '@types/node': 14.18.42 @@ -51234,6 +51424,11 @@ snapshots: '@types/pretty-hrtime@1.0.1': {} + '@types/prompts@2.4.2': + dependencies: + '@types/node': 14.18.42 + kleur: 3.0.3 + '@types/prop-types@15.7.5': {} '@types/q@1.5.5': {} @@ -51391,6 +51586,11 @@ snapshots: '@types/tapable@1.0.9': optional: true + '@types/tar@6.1.5': + dependencies: + '@types/node': 14.18.42 + minipass: 4.2.7 + '@types/tedious@4.0.14': dependencies: '@types/node': 14.18.42 @@ -51434,6 +51634,8 @@ snapshots: '@types/uuid@9.0.8': {} + '@types/validate-npm-package-name@3.0.0': {} + '@types/validator@13.7.14': {} '@types/warning@3.0.3': {} @@ -51888,6 +52090,8 @@ snapshots: '@vercel/error-utils@1.0.10': {} + '@vercel/ncc@0.34.0': {} + '@vercel/node@2.15.10(@swc/core@1.3.107)(encoding@0.1.13)': dependencies: '@edge-runtime/node-utils': 2.0.3 @@ -53638,10 +53842,16 @@ snapshots: async-listen@3.0.0: {} + async-retry@1.3.1: + dependencies: + retry: 0.12.0 + async-retry@1.3.3: dependencies: retry: 0.13.1 + async-sema@3.0.1: {} + async-validator@4.2.5: {} async@2.6.4: @@ -53662,6 +53872,8 @@ snapshots: atomic-sleep@1.0.0: {} + atomically@1.7.0: {} + attr-accept@2.2.2: {} author-regex@1.0.0: {} @@ -54656,7 +54868,7 @@ snapshots: fs-minipass: 3.0.1 glob: 10.3.10 lru-cache: 10.0.1 - minipass: 7.0.4 + minipass: 7.1.1 minipass-collect: 1.0.2 minipass-flush: 1.0.5 minipass-pipeline: 1.2.4 @@ -55012,6 +55224,8 @@ snapshots: ci-info@4.0.0: {} + ci-info@https://codeload.github.com/watson/ci-info/tar.gz/f43f6a1cefff47fb361c88cf4b943fdbcaafe540: {} + cjs-module-lexer@1.2.2: {} class-transformer@0.5.1: {} @@ -55288,7 +55502,7 @@ snapshots: commander@11.1.0: {} - commander@2.20.3: {} + commander@2.20.0: {} commander@4.1.1: {} @@ -55411,6 +55625,19 @@ snapshots: is-whitespace: 0.3.0 kind-of: 3.2.2 + conf@10.2.0: + 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.6.2 + config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -55556,7 +55783,7 @@ snapshots: copy-webpack-plugin@11.0.0(webpack@5.88.2(@swc/core@1.3.107)(esbuild@0.18.17)): dependencies: - fast-glob: 3.3.2 + fast-glob: 3.3.1 glob-parent: 6.0.2 globby: 13.1.3 normalize-path: 3.0.0 @@ -56470,6 +56697,10 @@ snapshots: de-indent@1.0.2: {} + debounce-fn@4.0.0: + dependencies: + mimic-fn: 3.1.0 + debounce@1.2.1: {} debug@2.6.9: @@ -56928,7 +57159,7 @@ snapshots: editorconfig@0.15.3: dependencies: - commander: 2.20.3 + commander: 2.20.0 lru-cache: 4.1.5 semver: 5.7.2 sigmund: 1.0.1 @@ -59459,7 +59690,7 @@ snapshots: gh-pages@5.0.0: dependencies: async: 3.2.4 - commander: 2.20.3 + commander: 2.20.0 email-addresses: 5.0.0 filenamify: 4.3.0 find-cache-dir: 3.3.2 @@ -59561,7 +59792,7 @@ snapshots: foreground-child: 3.1.1 jackspeak: 2.3.6 minimatch: 9.0.4 - minipass: 7.0.4 + minipass: 7.1.1 path-scurry: 1.10.1 glob@6.0.4: @@ -59675,7 +59906,7 @@ snapshots: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.2 + fast-glob: 3.3.1 ignore: 5.2.4 merge2: 1.4.1 slash: 3.0.0 @@ -59684,7 +59915,7 @@ snapshots: dependencies: array-union: 3.0.1 dir-glob: 3.0.1 - fast-glob: 3.2.12 + fast-glob: 3.3.1 ignore: 5.2.4 merge2: 1.4.1 slash: 4.0.0 @@ -59692,7 +59923,7 @@ snapshots: globby@13.1.3: dependencies: dir-glob: 3.0.1 - fast-glob: 3.3.2 + fast-glob: 3.3.1 ignore: 5.2.4 merge2: 1.4.1 slash: 4.0.0 @@ -63725,6 +63956,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema-typed@7.0.3: {} + json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -65740,8 +65973,6 @@ snapshots: minipass@5.0.0: {} - minipass@7.0.4: {} - minipass@7.1.1: {} minizlib@2.1.2: @@ -67602,7 +67833,7 @@ snapshots: path-scurry@1.10.1: dependencies: lru-cache: 10.0.1 - minipass: 7.0.4 + minipass: 7.1.1 path-scurry@1.6.4: dependencies: @@ -68211,7 +68442,7 @@ snapshots: postcss-mixins@9.0.4(postcss@8.4.38): dependencies: - fast-glob: 3.3.2 + fast-glob: 3.3.1 postcss: 8.4.38 postcss-js: 4.0.1(postcss@8.4.38) postcss-simple-vars: 7.0.1(postcss@8.4.38) @@ -68567,6 +68798,10 @@ snapshots: dependencies: fast-diff: 1.2.0 + prettier-plugin-tailwindcss@0.3.0(prettier@3.2.5): + dependencies: + prettier: 3.2.5 + prettier@2.8.7: {} prettier@2.8.8: {} @@ -69802,7 +70037,7 @@ snapshots: '@babel/generator': 7.23.0 '@babel/runtime': 7.23.2 ast-types: 0.14.2 - commander: 2.20.3 + commander: 2.20.0 doctrine: 3.0.0 estree-to-babel: 3.2.1 neo-async: 2.6.2 @@ -70770,10 +71005,19 @@ snapshots: unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.1.0 + registry-auth-token@3.3.2: + dependencies: + rc: 1.2.8 + safe-buffer: 5.2.1 + registry-auth-token@5.0.2: dependencies: '@pnpm/npm-conf': 2.1.1 + registry-url@3.1.0: + dependencies: + rc: 1.2.8 + registry-url@5.1.0: dependencies: rc: 1.2.8 @@ -72760,7 +73004,7 @@ snapshots: color-name: 1.1.4 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.3.2 + fast-glob: 3.3.1 glob-parent: 6.0.2 is-glob: 4.0.3 jiti: 1.18.2 @@ -72789,7 +73033,7 @@ snapshots: color-name: 1.1.4 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.3.2 + fast-glob: 3.3.1 glob-parent: 6.0.2 is-glob: 4.0.3 jiti: 1.18.2 @@ -72818,7 +73062,7 @@ snapshots: color-name: 1.1.4 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.3.2 + fast-glob: 3.3.1 glob-parent: 6.0.2 is-glob: 4.0.3 jiti: 1.18.2 @@ -73063,21 +73307,21 @@ snapshots: dependencies: '@jridgewell/source-map': 0.3.3 acorn: 8.11.3 - commander: 2.20.3 + commander: 2.20.0 source-map-support: 0.5.21 terser@5.19.2: dependencies: '@jridgewell/source-map': 0.3.3 acorn: 8.11.3 - commander: 2.20.3 + commander: 2.20.0 source-map-support: 0.5.21 terser@5.22.0: dependencies: '@jridgewell/source-map': 0.3.3 acorn: 8.11.3 - commander: 2.20.3 + commander: 2.20.0 source-map-support: 0.5.21 test-exclude@6.0.0: @@ -74119,6 +74363,8 @@ snapshots: typescript@5.1.6: {} + typescript@5.2.2: {} + typescript@5.3.3: {} typescript@5.4.5: {} @@ -74370,6 +74616,11 @@ snapshots: escalade: 3.1.2 picocolors: 1.0.0 + update-check@1.5.4: + dependencies: + registry-auth-token: 3.3.2 + registry-url: 3.1.0 + upper-case-first@1.1.2: dependencies: upper-case: 1.1.3 From ff5c78b9e461978b06ecdc721c2a6c3e7e92417b Mon Sep 17 00:00:00 2001 From: Denis Kralj Date: Mon, 3 Jun 2024 16:12:47 +0200 Subject: [PATCH 2/4] feat(root): include create-novu-app to monorepo Fix cspell errors --- .cspell.json | 14 ++++++++++++-- packages/create-novu-app/create-app.ts | 8 ++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.cspell.json b/.cspell.json index a6aa0e92ed3..7f007e622b0 100644 --- a/.cspell.json +++ b/.cspell.json @@ -8,6 +8,7 @@ "adresses", "africas", "africastalking", + "alanturing", "alexjoverm", "alist", "aliyun", @@ -64,6 +65,7 @@ "bzrignore", "cacheable", "cafebabe", + "callout", "canonicalization", "canonicalized", "chartjs", @@ -205,6 +207,7 @@ "Hewgill", "HEXTET", "hextets", + "hgcheck", "hgignore", "hmac", "hokify", @@ -301,6 +304,7 @@ "MITM", "MJML", "mkdir", + "mkdocs", "mlen", "moby", "modlen", @@ -352,6 +356,7 @@ "novuhq", "novui", "novutest", + "npmignore", "npmjs", "npmrc", "nrwl", @@ -369,6 +374,7 @@ "opcodemap", "opcodestr", "openapi", + "OPENCOLLECTIVE", "opentelemetry", "orcid", "Otel", @@ -385,6 +391,7 @@ "permerrors", "personalizations", "PGID", + "picocolors", "pino", "PKCS", "pkdata", @@ -400,6 +407,7 @@ "prefixlen", "preheader", "prepopulating", + "prepush", "presigner", "prettierignore", "printjson", @@ -471,6 +479,7 @@ "scopsy", "Scriptable", "Segoe", + "sema", "sendchamp", "Sendchamp", "sendgrid", @@ -563,6 +572,7 @@ "TRANSID", "transpiled", "trunc", + "tsbuildinfo", "TSIG", "tspan", "tsup", @@ -587,6 +597,7 @@ "Usergroup", "userid", "uuidv", + "vercel", "verifyspf", "Vonage", "WaitList", @@ -598,8 +609,7 @@ "whatsappbusiness", "xkeysib", "zulip", - "zwnj", - "prepush" + "zwnj" ], "flagWords": [], "patterns": [ diff --git a/packages/create-novu-app/create-app.ts b/packages/create-novu-app/create-app.ts index a6b55a8ca5f..3e18f727643 100644 --- a/packages/create-novu-app/create-app.ts +++ b/packages/create-novu-app/create-app.ts @@ -201,11 +201,11 @@ export async function createApp({ 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}`); @@ -224,7 +224,7 @@ export async function createApp({ console.log(); console.log('We suggest that you begin by typing:'); console.log(); - console.log(cyan(' cd'), cdpath); + console.log(cyan(' cd'), cdPath); console.log(` ${cyan(`${packageManager} ${useYarn ? '' : 'run '}dev`)}`); } console.log(); From 13b82e96fa22e5a1268d8e395d99ab71cfcf0288 Mon Sep 17 00:00:00 2001 From: Denis Kralj Date: Mon, 3 Jun 2024 16:40:49 +0200 Subject: [PATCH 3/4] feat(root): include create-novu-app to monorepo Fix one more cspell error --- .cspell.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.cspell.json b/.cspell.json index 7f007e622b0..9ea97aabf14 100644 --- a/.cspell.json +++ b/.cspell.json @@ -453,6 +453,7 @@ "Relabelings", "releasewards", "RELEASLY", + "relocator", "replayable", "replstate", "reshard", From 85dc4707de696ec6552491858a32f510e87cd77f Mon Sep 17 00:00:00 2001 From: Denis Kralj Date: Mon, 3 Jun 2024 17:07:53 +0200 Subject: [PATCH 4/4] feat(root): include create-novu-app to monorepo Update per MR comments --- packages/create-novu-app/package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/create-novu-app/package.json b/packages/create-novu-app/package.json index a4a0f3918c3..f1b9fc989c1 100644 --- a/packages/create-novu-app/package.json +++ b/packages/create-novu-app/package.json @@ -1,6 +1,6 @@ { "name": "create-novu-app", - "version": "0.0.1-alpha.4", + "version": "0.24.2", "keywords": [ "novu" ], @@ -49,8 +49,5 @@ "validate-npm-package-name": "3.0.0", "typescript": "5.2.2", "async-sema": "3.0.1" - }, - "engines": { - "node": ">=18.17.0" } }