create-appraisejs 0.2.0-alpha.3 → 0.2.0-alpha.5
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/package.json +1 -2
- package/templates/default/.appraise-template-meta.json +1 -1
- package/templates/default/prisma/dev.db +0 -0
- package/templates/default/src/app/layout.tsx +1 -2
- package/templates/default/src/config/db-config.ts +67 -6
- package/templates/default/src/lib/automation/projection-service.ts +0 -5
- package/templates/default/src/lib/utils/template-step-file-generator.ts +41 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-appraisejs",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.5",
|
|
4
4
|
"description": "Scaffold a new AppraiseJS app in your directory",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Hasnat Jamil",
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
"sync-templates": "tsx scripts/sync-templates.ts",
|
|
45
45
|
"test": "vitest run",
|
|
46
46
|
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
47
|
-
"publish": "npm publish",
|
|
48
47
|
"publish:alpha": "npm publish --tag alpha",
|
|
49
48
|
"publish:beta": "npm publish --tag beta",
|
|
50
49
|
"bump:alpha": "npm version prerelease --preid alpha",
|
|
Binary file
|
|
@@ -156,10 +156,9 @@ export default function RootLayout({
|
|
|
156
156
|
},
|
|
157
157
|
]}
|
|
158
158
|
/>
|
|
159
|
-
|
|
159
|
+
<NavLink href="/settings" icon={<Settings2 className="h-5 w-5 text-primary" />}>
|
|
160
160
|
Settings
|
|
161
161
|
</NavLink>
|
|
162
|
-
*/}
|
|
163
162
|
<NavCommand className="ml-auto" />
|
|
164
163
|
</div>
|
|
165
164
|
</nav>
|
|
@@ -1,9 +1,70 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import { createRequire } from 'module'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
|
|
5
|
+
function readProjectDatabaseUrl(): string | null {
|
|
6
|
+
const envPath = path.join(process.cwd(), '.env')
|
|
7
|
+
if (!fs.existsSync(envPath)) {
|
|
8
|
+
return null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const envContent = fs.readFileSync(envPath, 'utf8')
|
|
12
|
+
const match = envContent.match(/^\s*DATABASE_URL\s*=\s*(?:"([^"]*)"|'([^']*)'|([^#\r\n]+))\s*$/m)
|
|
13
|
+
const rawValue = match?.[1] ?? match?.[2] ?? match?.[3]
|
|
14
|
+
const normalizedValue = rawValue?.trim()
|
|
15
|
+
|
|
16
|
+
return normalizedValue ? normalizedValue : null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function normalizeDatabaseUrl(databaseUrl: string): string {
|
|
20
|
+
if (!databaseUrl.startsWith('file:')) {
|
|
21
|
+
return databaseUrl
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const sqlitePathWithQuery = databaseUrl.slice('file:'.length)
|
|
25
|
+
if (!sqlitePathWithQuery || sqlitePathWithQuery === ':memory:') {
|
|
26
|
+
return databaseUrl
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const queryStartIndex = sqlitePathWithQuery.indexOf('?')
|
|
30
|
+
const sqlitePath = queryStartIndex >= 0 ? sqlitePathWithQuery.slice(0, queryStartIndex) : sqlitePathWithQuery
|
|
31
|
+
const query = queryStartIndex >= 0 ? sqlitePathWithQuery.slice(queryStartIndex) : ''
|
|
32
|
+
|
|
33
|
+
if (path.isAbsolute(sqlitePath)) {
|
|
34
|
+
return databaseUrl
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Prisma resolves relative SQLite paths from the schema directory. The app's schema
|
|
38
|
+
// lives in `<project>/prisma`, so normalize local file URLs to that location.
|
|
39
|
+
const absolutePath = path.resolve(process.cwd(), 'prisma', sqlitePath)
|
|
40
|
+
return `file:${absolutePath}${query}`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function ensureProjectDatabaseUrl(): void {
|
|
44
|
+
if (process.env.DATABASE_URL) {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const projectDatabaseUrl = readProjectDatabaseUrl()
|
|
49
|
+
if (!projectDatabaseUrl) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
process.env.DATABASE_URL = normalizeDatabaseUrl(projectDatabaseUrl)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
ensureProjectDatabaseUrl()
|
|
57
|
+
|
|
58
|
+
type PrismaClientInstance = import('@prisma/client').PrismaClient
|
|
59
|
+
const require = createRequire(import.meta.url)
|
|
60
|
+
const { PrismaClient } = require('@prisma/client') as {
|
|
61
|
+
PrismaClient: new () => PrismaClientInstance
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const globalForPrisma = global as unknown as {
|
|
65
|
+
prisma: PrismaClientInstance | undefined
|
|
66
|
+
}
|
|
67
|
+
const prisma = globalForPrisma.prisma ?? new PrismaClient()
|
|
7
68
|
|
|
8
69
|
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
|
9
70
|
|
|
@@ -214,14 +214,9 @@ class AutomationProjectionService {
|
|
|
214
214
|
select: { id: true },
|
|
215
215
|
})
|
|
216
216
|
|
|
217
|
-
const templateStepGroups = await prisma.templateStepGroup.findMany({
|
|
218
|
-
select: { id: true },
|
|
219
|
-
})
|
|
220
|
-
|
|
221
217
|
await Promise.all([
|
|
222
218
|
this.syncEnvironments(),
|
|
223
219
|
...locatorGroups.map(locatorGroup => this.syncLocatorGroup(locatorGroup.id)),
|
|
224
|
-
...templateStepGroups.map(group => this.syncTemplateStepGroup(group.id)),
|
|
225
220
|
])
|
|
226
221
|
|
|
227
222
|
await this.regenerateAllFeatures()
|
|
@@ -10,29 +10,53 @@ import {
|
|
|
10
10
|
} from '@/lib/automation/paths'
|
|
11
11
|
|
|
12
12
|
const RUNTIME_IMPORT = '../../../packages/cucumber-runtime/src/index.js'
|
|
13
|
-
const REQUIRED_RUNTIME_IMPORT =
|
|
14
|
-
`import { When, Then, CustomWorld, expect, SelectorName, resolveLocator, getEnvironment, generateRandomData, RandomDataType } from '${RUNTIME_IMPORT}';\n\n`
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const REQUIRED_RUNTIME_IMPORT =
|
|
14
|
+
`import { When, Then, CustomWorld, expect, SelectorName, resolveLocator, getEnvironment, generateRandomData, RandomDataType } from '${RUNTIME_IMPORT}';\n\n`
|
|
15
|
+
|
|
16
|
+
function generateStepJSDoc(templateStep: Pick<TemplateStep, 'name' | 'description' | 'icon'>): string {
|
|
17
|
+
const lines = ['/**']
|
|
18
|
+
lines.push(` * @name ${templateStep.name}`)
|
|
19
|
+
if (templateStep.description) {
|
|
20
|
+
lines.push(` * @description ${templateStep.description}`)
|
|
21
|
+
}
|
|
22
|
+
lines.push(` * @icon ${templateStep.icon}`)
|
|
23
|
+
lines.push(' */')
|
|
24
|
+
return lines.join('\n')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function stripLeadingJSDoc(functionDefinition: string): string {
|
|
28
|
+
return functionDefinition.replace(/^\s*\/\*\*[\s\S]*?\*\/\s*/u, '').trim()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function generateStepDefinition(templateStep: TemplateStep): string | null {
|
|
32
|
+
const functionDefinition = templateStep.functionDefinition?.trim()
|
|
33
|
+
if (!functionDefinition) {
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return `${generateStepJSDoc(templateStep)}\n${stripLeadingJSDoc(functionDefinition)}`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function sanitizeFileName(groupName: string): string {
|
|
41
|
+
return groupName
|
|
18
42
|
.toLowerCase()
|
|
19
43
|
.trim()
|
|
20
44
|
.replace(/\s+/g, '_')
|
|
21
45
|
.replace(/[^a-z0-9_]/g, '')
|
|
22
46
|
}
|
|
23
47
|
|
|
24
|
-
export function generateFileContent(templateSteps: TemplateStep[]): string {
|
|
25
|
-
if (!templateSteps || templateSteps.length === 0) {
|
|
26
|
-
return REQUIRED_RUNTIME_IMPORT + '// This file is generated automatically. Add template steps to this group to generate content.'
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const functionDefinitions = templateSteps
|
|
30
|
-
.map(
|
|
31
|
-
.filter(Boolean)
|
|
32
|
-
.join('\n\n')
|
|
33
|
-
|
|
34
|
-
return REQUIRED_RUNTIME_IMPORT + functionDefinitions
|
|
35
|
-
}
|
|
48
|
+
export function generateFileContent(templateSteps: TemplateStep[]): string {
|
|
49
|
+
if (!templateSteps || templateSteps.length === 0) {
|
|
50
|
+
return REQUIRED_RUNTIME_IMPORT + '// This file is generated automatically. Add template steps to this group to generate content.'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const functionDefinitions = templateSteps
|
|
54
|
+
.map(generateStepDefinition)
|
|
55
|
+
.filter((definition): definition is string => Boolean(definition))
|
|
56
|
+
.join('\n\n')
|
|
57
|
+
|
|
58
|
+
return REQUIRED_RUNTIME_IMPORT + functionDefinitions
|
|
59
|
+
}
|
|
36
60
|
|
|
37
61
|
export async function formatFileContent(content: string): Promise<string> {
|
|
38
62
|
try {
|