create-ej-app 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # create-ej-app
2
2
 
3
3
  ```
4
- npx create-ej-app
4
+ npx create-ej-app@latest
5
5
  ```
package/cli.js CHANGED
@@ -1,88 +1,76 @@
1
1
  #!/usr/bin/env node
2
-
3
- const { program } = require('commander')
4
- const fs = require('fs')
5
- const path = require('path')
6
- const { prompt } = require('enquirer')
7
- const copydir = require('copy-dir')
8
-
9
- program
10
- .version('2.1.0')
11
- .description('CLI tool to bootstrap a Next.js App')
12
- .action(async () => {
13
- const response = await prompt([
14
- {
15
- type: 'input',
16
- name: 'projectName',
17
- message: 'Enter the project name:',
18
- initial: 'ej-app'
19
- },
20
- {
21
- type: 'input',
22
- name: 'description',
23
- message: 'Enter a description for the project:',
24
- initial: 'A Legendary Next.js App'
25
- },
26
- {
27
- type: 'select',
28
- name: 'template',
29
- message: 'Select a template:',
30
- choices: [
31
- { name: 'base', message: 'Base' },
32
- {
33
- name: 'advanced',
34
- message:
35
- 'Advanced setup with User Types and ACLs (roles & permissions)'
36
- }
37
- ],
38
- initial: 0
39
- }
40
- ])
41
-
42
- const { projectName, description, template } = response
43
-
44
- // Create a directory with the project name
45
- const projectDir = path.join(process.cwd(), projectName)
46
-
47
- if (fs.existsSync(projectDir)) {
48
- console.error(`Error: Directory '${projectName}' already exists.`)
49
- process.exit(1)
2
+ "use strict";
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ var __importDefault = (this && this.__importDefault) || function (mod) {
13
+ return (mod && mod.__esModule) ? mod : { "default": mod };
14
+ };
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ const commander_1 = require("commander");
17
+ const enquirer_1 = require("enquirer");
18
+ const fs_1 = __importDefault(require("fs"));
19
+ const path_1 = __importDefault(require("path"));
20
+ //@ts-ignore
21
+ const copy_dir_1 = __importDefault(require("copy-dir"));
22
+ commander_1.program.action(() => __awaiter(void 0, void 0, void 0, function* () {
23
+ const response = yield (0, enquirer_1.prompt)([
24
+ {
25
+ type: 'input',
26
+ name: 'projectName',
27
+ message: 'Enter the project name:',
28
+ initial: 'my-app'
29
+ },
30
+ {
31
+ type: 'input',
32
+ name: 'description',
33
+ message: 'Enter a description for the project:',
34
+ initial: 'A Legendary App'
35
+ },
36
+ {
37
+ type: 'select',
38
+ name: 'template',
39
+ message: 'Select a template:',
40
+ choices: [
41
+ {
42
+ name: 'base',
43
+ message: 'Base setup with ts, shadcn, prisma, auth, editor.js'
44
+ },
45
+ {
46
+ name: 'advanced',
47
+ message: 'Advanced setup with User Types and ACLs (roles & permissions)'
48
+ }
49
+ ]
50
+ }
51
+ ]);
52
+ const { projectName, description, template } = response;
53
+ const projectDir = path_1.default.join(process.cwd(), projectName);
54
+ if (fs_1.default.existsSync(projectDir)) {
55
+ console.error(`Error: Directory '${projectName}' already exists.`);
56
+ process.exit(1);
50
57
  }
51
-
52
- fs.mkdirSync(projectDir)
53
-
54
- // Copy all files and subdirectories from your template folder to the new project directory
55
- const templateDir = path.join(__dirname, 'template', template)
56
- copydir.sync(templateDir, projectDir)
57
-
58
- // Modify package.json in the new project directory to set the project name and description
59
- const packageJsonFile = path.join(projectDir, 'package.json')
60
- const packageJson = JSON.parse(fs.readFileSync(packageJsonFile, 'utf-8'))
61
- packageJson.name = projectName
62
- packageJson.description = description
63
- fs.writeFileSync(packageJsonFile, JSON.stringify(packageJson, null, 2))
64
-
65
- // Modify src/lib/siteConfig.ts in the new project directory to set the project name and description
66
- const siteConfigFile = path.join(projectDir, 'src', 'lib', 'siteConfig.ts')
67
- let siteConfig = fs.readFileSync(siteConfigFile, 'utf-8')
68
- siteConfig = siteConfig.replace('Project Name', projectName)
69
- siteConfig = siteConfig.replace('Project Description', description)
70
- fs.writeFileSync(siteConfigFile, siteConfig)
71
-
72
- // Copy .env.example to .env
73
- const envExampleFile = path.join(projectDir, '.env.example')
74
- const envExample = fs.readFileSync(envExampleFile, 'utf-8')
75
- fs.writeFileSync(path.join(projectDir, '.env'), envExample)
76
-
77
- console.log(`Created a new Next.js app called '${projectName}'
78
-
79
-
80
- To get started, run the following commands:
81
-
82
- cd ${projectName}
83
- npm install
84
- npm run dev
85
- `)
86
- })
87
-
88
- program.parse(process.argv)
58
+ fs_1.default.mkdirSync(projectDir);
59
+ const templateDir = path_1.default.join(__dirname, 'template', template);
60
+ copy_dir_1.default.sync(templateDir, projectDir);
61
+ const packageJsonFile = path_1.default.join(projectDir, 'package.json');
62
+ const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonFile, 'utf-8'));
63
+ packageJson.name = projectName;
64
+ packageJson.description = description;
65
+ fs_1.default.writeFileSync(packageJsonFile, JSON.stringify(packageJson, null, 2));
66
+ const siteConfigFile = path_1.default.join(projectDir, 'src', 'lib', 'siteConfig.ts');
67
+ let siteConfig = fs_1.default.readFileSync(siteConfigFile, 'utf-8');
68
+ siteConfig = siteConfig.replace('Project Name', projectName);
69
+ siteConfig = siteConfig.replace('Project Description', description);
70
+ fs_1.default.writeFileSync(siteConfigFile, siteConfig);
71
+ const envExampleFile = path_1.default.join(projectDir, '.env.example');
72
+ const envExample = fs_1.default.readFileSync(envExampleFile, 'utf-8');
73
+ fs_1.default.writeFileSync(path_1.default.join(projectDir, '.env'), envExample);
74
+ console.log(`Created a new app called '${projectName}' 🎉\n\nTo get started, run the following commands:\n\ncd ${projectName}\nnpm install\nnpm run dev\n`);
75
+ }));
76
+ commander_1.program.parse(process.argv);
package/cli.ts ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { program } from 'commander';
4
+ import { prompt } from 'enquirer';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ //@ts-ignore
8
+ import copydir from 'copy-dir';
9
+
10
+ interface Response {
11
+ projectName: string;
12
+ description: string;
13
+ template: string;
14
+ }
15
+
16
+ program.action(async () => {
17
+ const response: Response = await prompt([
18
+ {
19
+ type: 'input',
20
+ name: 'projectName',
21
+ message: 'Enter the project name:',
22
+ initial: 'my-app'
23
+ },
24
+ {
25
+ type: 'input',
26
+ name: 'description',
27
+ message: 'Enter a description for the project:',
28
+ initial: 'A Legendary App'
29
+ },
30
+ {
31
+ type: 'select',
32
+ name: 'template',
33
+ message: 'Select a template:',
34
+ choices: [
35
+ {
36
+ name: 'base',
37
+ message: 'Base setup with ts, shadcn, prisma, auth, editor.js'
38
+ },
39
+ {
40
+ name: 'advanced',
41
+ message:
42
+ 'Advanced setup with User Types and ACLs (roles & permissions)'
43
+ }
44
+ ]
45
+ }
46
+ ]);
47
+
48
+ const { projectName, description, template } = response;
49
+
50
+ const projectDir = path.join(process.cwd(), projectName);
51
+
52
+ if (fs.existsSync(projectDir)) {
53
+ console.error(`Error: Directory '${projectName}' already exists.`);
54
+ process.exit(1);
55
+ }
56
+
57
+ fs.mkdirSync(projectDir);
58
+
59
+ const templateDir = path.join(__dirname, 'template', template);
60
+ copydir.sync(templateDir, projectDir);
61
+
62
+ const packageJsonFile = path.join(projectDir, 'package.json');
63
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonFile, 'utf-8'));
64
+ packageJson.name = projectName;
65
+ packageJson.description = description;
66
+ fs.writeFileSync(packageJsonFile, JSON.stringify(packageJson, null, 2));
67
+
68
+ const siteConfigFile = path.join(projectDir, 'src', 'lib', 'siteConfig.ts');
69
+ let siteConfig = fs.readFileSync(siteConfigFile, 'utf-8');
70
+ siteConfig = siteConfig.replace('Project Name', projectName);
71
+ siteConfig = siteConfig.replace('Project Description', description);
72
+ fs.writeFileSync(siteConfigFile, siteConfig);
73
+
74
+ const envExampleFile = path.join(projectDir, '.env.example');
75
+ const envExample = fs.readFileSync(envExampleFile, 'utf-8');
76
+ fs.writeFileSync(path.join(projectDir, '.env'), envExample);
77
+
78
+ console.log(`Created a new app called '${projectName}' 🎉\n\nTo get started, run the following commands:\n\ncd ${projectName}\nnpm install\nnpm run dev\n`);
79
+ });
80
+
81
+ program.parse(process.argv);
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "create-ej-app",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "Bootstrap a next.js app with my preferred setup",
5
5
  "main": "cli.js",
6
6
  "scripts": {
7
+ "build": "tsc",
7
8
  "start": "node ."
8
9
  },
9
10
  "bin": {
@@ -32,5 +33,9 @@
32
33
  "commander": "^11.0.0",
33
34
  "copy-dir": "^1.3.0",
34
35
  "enquirer": "^2.4.1"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^20.8.0",
39
+ "typescript": "^5.2.2"
35
40
  }
36
41
  }
@@ -149,5 +149,6 @@ model Resource {
149
149
  createdAt DateTime @default(now()) @db.Timestamptz()
150
150
  updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz()
151
151
 
152
+ @@index([taskId])
152
153
  @@map("Resources")
153
154
  }
@@ -5,8 +5,41 @@ import { revalidatePath } from 'next/cache'
5
5
 
6
6
  import { authGuard } from '@/lib/auth'
7
7
  import { prisma } from '@/lib/db'
8
+ import { storageClient } from '@/lib/storageClient'
8
9
  import { UnwrapPromise } from '@/types/UnwrapPromise'
9
10
 
11
+ const handleImages = async (imageIds: string[], taskId: string) => {
12
+ const prevImages = await prisma.resource.findMany({
13
+ where: {
14
+ taskId
15
+ }
16
+ })
17
+
18
+ for (const p of prevImages) {
19
+ if (imageIds.includes(p.id)) continue
20
+ await Promise.all([
21
+ storageClient.deleteFile(p.newFilename),
22
+ prisma.resource.delete({
23
+ where: {
24
+ id: p.id
25
+ }
26
+ })
27
+ ])
28
+ }
29
+
30
+ if (imageIds.length)
31
+ await prisma.resource.updateMany({
32
+ where: {
33
+ id: {
34
+ in: imageIds
35
+ }
36
+ },
37
+ data: {
38
+ taskId
39
+ }
40
+ })
41
+ }
42
+
10
43
  export const getTask = async (id: string) => {
11
44
  const session = await authGuard(['Root', 'Normal'], 'TaskView')
12
45
  if (!session) throw new Error('Unauthorized')
@@ -57,25 +90,7 @@ export const createTask = async ({
57
90
  updatedById: session.user.id
58
91
  }
59
92
  })
60
- await prisma.resource.updateMany({
61
- where: {
62
- taskId: id
63
- },
64
- data: {
65
- taskId: null
66
- }
67
- })
68
- if (imageIds.length)
69
- await prisma.resource.updateMany({
70
- where: {
71
- id: {
72
- in: imageIds
73
- }
74
- },
75
- data: {
76
- taskId: id
77
- }
78
- })
93
+ await handleImages(imageIds, id)
79
94
 
80
95
  revalidatePath('/tasks')
81
96
  revalidatePath(`/tasks/${id}`)
@@ -110,25 +125,7 @@ export const updateTask = async ({
110
125
  updatedById: session.user.id
111
126
  }
112
127
  })
113
- await prisma.resource.updateMany({
114
- where: {
115
- taskId: id
116
- },
117
- data: {
118
- taskId: null
119
- }
120
- })
121
- if (imageIds.length)
122
- await prisma.resource.updateMany({
123
- where: {
124
- id: {
125
- in: imageIds
126
- }
127
- },
128
- data: {
129
- taskId: id
130
- }
131
- })
128
+ await handleImages(imageIds, id)
132
129
 
133
130
  revalidatePath('/tasks')
134
131
  revalidatePath(`/tasks/${id}`)
@@ -138,6 +135,7 @@ export const deleteTask = async (id: string) => {
138
135
  const session = await authGuard(['Root', 'Normal'], 'TaskDelete')
139
136
  if (!session) throw new Error('Unauthorized')
140
137
 
138
+ await handleImages([], id)
141
139
  await prisma.task.delete({
142
140
  where: {
143
141
  id
@@ -1,17 +1,8 @@
1
- import { GCPStorageClient } from '@ejekanshjain/cloud-storage'
2
1
  import { NextRequest, NextResponse } from 'next/server'
3
2
 
4
- import { env } from '@/env.mjs'
5
3
  import { getAuthSession } from '@/lib/auth'
6
4
  import { prisma } from '@/lib/db'
7
-
8
- const storageClient = GCPStorageClient({
9
- projectId: env.FIREBASE_PROJECT_ID!,
10
- privateKey: env.FIREBASE_PRIVATE_KEY!,
11
- clientEmail: env.FIREBASE_CLIENT_EMAIL!,
12
- bucket: env.FIREBASE_STORAGE_BUCKET!,
13
- defaultMediaPublic: true
14
- })
5
+ import { storageClient } from '@/lib/storageClient'
15
6
 
16
7
  export async function POST(request: NextRequest) {
17
8
  const session = await getAuthSession()
@@ -0,0 +1,11 @@
1
+ import { GCPStorageClient } from '@ejekanshjain/cloud-storage'
2
+
3
+ import { env } from '@/env.mjs'
4
+
5
+ export const storageClient = GCPStorageClient({
6
+ projectId: env.FIREBASE_PROJECT_ID!,
7
+ privateKey: env.FIREBASE_PRIVATE_KEY!,
8
+ clientEmail: env.FIREBASE_CLIENT_EMAIL!,
9
+ bucket: env.FIREBASE_STORAGE_BUCKET!,
10
+ defaultMediaPublic: true
11
+ })
@@ -111,5 +111,6 @@ model Resource {
111
111
  createdAt DateTime @default(now()) @db.Timestamptz()
112
112
  updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz()
113
113
 
114
+ @@index([taskId])
114
115
  @@map("Resources")
115
116
  }
@@ -5,8 +5,41 @@ import { revalidatePath } from 'next/cache'
5
5
 
6
6
  import { getAuthSession } from '@/lib/auth'
7
7
  import { prisma } from '@/lib/db'
8
+ import { storageClient } from '@/lib/storageClient'
8
9
  import { UnwrapPromise } from '@/types/UnwrapPromise'
9
10
 
11
+ const handleImages = async (imageIds: string[], taskId: string) => {
12
+ const prevImages = await prisma.resource.findMany({
13
+ where: {
14
+ taskId
15
+ }
16
+ })
17
+
18
+ for (const p of prevImages) {
19
+ if (imageIds.includes(p.id)) continue
20
+ await Promise.all([
21
+ storageClient.deleteFile(p.newFilename),
22
+ prisma.resource.delete({
23
+ where: {
24
+ id: p.id
25
+ }
26
+ })
27
+ ])
28
+ }
29
+
30
+ if (imageIds.length)
31
+ await prisma.resource.updateMany({
32
+ where: {
33
+ id: {
34
+ in: imageIds
35
+ }
36
+ },
37
+ data: {
38
+ taskId
39
+ }
40
+ })
41
+ }
42
+
10
43
  export const getTask = async (id: string) => {
11
44
  const session = await getAuthSession()
12
45
  if (!session) throw new Error('Unauthorized')
@@ -57,25 +90,7 @@ export const createTask = async ({
57
90
  updatedById: session.user.id
58
91
  }
59
92
  })
60
- await prisma.resource.updateMany({
61
- where: {
62
- taskId: id
63
- },
64
- data: {
65
- taskId: null
66
- }
67
- })
68
- if (imageIds.length)
69
- await prisma.resource.updateMany({
70
- where: {
71
- id: {
72
- in: imageIds
73
- }
74
- },
75
- data: {
76
- taskId: id
77
- }
78
- })
93
+ await handleImages(imageIds, id)
79
94
 
80
95
  revalidatePath('/tasks')
81
96
  revalidatePath(`/tasks/${id}`)
@@ -110,25 +125,7 @@ export const updateTask = async ({
110
125
  updatedById: session.user.id
111
126
  }
112
127
  })
113
- await prisma.resource.updateMany({
114
- where: {
115
- taskId: id
116
- },
117
- data: {
118
- taskId: null
119
- }
120
- })
121
- if (imageIds.length)
122
- await prisma.resource.updateMany({
123
- where: {
124
- id: {
125
- in: imageIds
126
- }
127
- },
128
- data: {
129
- taskId: id
130
- }
131
- })
128
+ await handleImages(imageIds, id)
132
129
 
133
130
  revalidatePath('/tasks')
134
131
  revalidatePath(`/tasks/${id}`)
@@ -138,6 +135,7 @@ export const deleteTask = async (id: string) => {
138
135
  const session = await getAuthSession()
139
136
  if (!session) throw new Error('Unauthorized')
140
137
 
138
+ await handleImages([], id)
141
139
  await prisma.task.delete({
142
140
  where: {
143
141
  id
@@ -1,17 +1,8 @@
1
- import { GCPStorageClient } from '@ejekanshjain/cloud-storage'
2
1
  import { NextRequest, NextResponse } from 'next/server'
3
2
 
4
- import { env } from '@/env.mjs'
5
3
  import { getAuthSession } from '@/lib/auth'
6
4
  import { prisma } from '@/lib/db'
7
-
8
- const storageClient = GCPStorageClient({
9
- projectId: env.FIREBASE_PROJECT_ID!,
10
- privateKey: env.FIREBASE_PRIVATE_KEY!,
11
- clientEmail: env.FIREBASE_CLIENT_EMAIL!,
12
- bucket: env.FIREBASE_STORAGE_BUCKET!,
13
- defaultMediaPublic: true
14
- })
5
+ import { storageClient } from '@/lib/storageClient'
15
6
 
16
7
  export async function POST(request: NextRequest) {
17
8
  const session = await getAuthSession()
@@ -0,0 +1,11 @@
1
+ import { GCPStorageClient } from '@ejekanshjain/cloud-storage'
2
+
3
+ import { env } from '@/env.mjs'
4
+
5
+ export const storageClient = GCPStorageClient({
6
+ projectId: env.FIREBASE_PROJECT_ID!,
7
+ privateKey: env.FIREBASE_PRIVATE_KEY!,
8
+ clientEmail: env.FIREBASE_CLIENT_EMAIL!,
9
+ bucket: env.FIREBASE_STORAGE_BUCKET!,
10
+ defaultMediaPublic: true
11
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "./",
4
+ "lib": ["ESNext", "DOM"],
5
+ "target": "es2016",
6
+ "module": "commonjs",
7
+ "esModuleInterop": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "strict": true,
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["**/*.ts"],
13
+ "exclude": ["node_modules", "template"]
14
+ }