-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #291 from palmaresHQ:create-db-command-line
Create Command Line and Templates for quick starting
- Loading branch information
Showing
113 changed files
with
2,699 additions
and
524 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -115,3 +115,5 @@ next-env.d.ts | |
|
||
# macos files | ||
.DS_Store | ||
|
||
/bin/templates/ |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
#!/usr/bin/env node | ||
|
||
import { ConsoleLogging } from '@palmares/console-logging'; | ||
import { Commands, CoreDomain, defineSettings, domain, getDefaultStd } from '@palmares/core'; | ||
import { Logger, loggingDomain } from '@palmares/logging'; | ||
import { NodeStd } from '@palmares/node-std'; | ||
|
||
import { recursivelyCopyFilesFromTemplate } from './utils'; | ||
|
||
import type { ExtractCommandsType } from '@palmares/core'; | ||
|
||
type TemplatesJson = { | ||
name: string; | ||
templates: { | ||
name: string; | ||
location: string; | ||
messages: { | ||
onStart: string; | ||
onDoing: string; | ||
onFinish: { | ||
message: string; | ||
commands: string[]; | ||
}; | ||
}; | ||
}[]; | ||
}; | ||
|
||
const logger = new Logger({ domainName: 'palmares' }); | ||
const cpaDomain = domain('palmares', '', { | ||
commands: { | ||
new: { | ||
description: 'Create a new Palmares app', | ||
positionalArgs: { | ||
appType: { | ||
description: 'The type of app you want to create', | ||
type: 'string', | ||
required: true | ||
}, | ||
name: { | ||
description: 'The name of the app you are creating', | ||
type: 'string', | ||
required: true | ||
} | ||
}, | ||
keywordArgs: { | ||
template: { | ||
description: 'The template to use for the database app', | ||
type: 'string', | ||
required: true | ||
} | ||
}, | ||
handler: async (args) => { | ||
const std = getDefaultStd(); | ||
// @ts-ignore Trust me bro | ||
const basePath = import.meta.dirname; | ||
const fullPath = await std.files.join(basePath, '..', 'templates'); | ||
const allApps = await std.files.readDirectory(fullPath); | ||
const commandLineArgs = args.commandLineArgs as ExtractCommandsType<typeof cpaDomain, 'new'>; | ||
let appType = commandLineArgs.positionalArgs.appType; | ||
let name = commandLineArgs.positionalArgs.name; | ||
if (!appType) { | ||
const appNameOptions = allApps.reduce( | ||
(accumulator, app, index) => { | ||
accumulator[index] = app; | ||
return accumulator; | ||
}, | ||
{} as Record<string, string> | ||
); | ||
const appTypeOption = await std.asker.askClearingScreen( | ||
[ | ||
`\x1b[1mWhich app you want to create?\x1b[0m`, | ||
...Object.entries(appNameOptions).map(([key, value]) => `${key}. ${value}`) | ||
], | ||
(answer) => `\x1b[1mWhich app you want to create?\x1b[0m ` + `\x1b[32m${appNameOptions[answer]}\x1b[0m` | ||
); | ||
appType = appNameOptions[appTypeOption]; | ||
} | ||
if (!appType) | ||
throw new Error( | ||
'You must provide a type for the app.\n\nOptions:\n\n' + allApps.map((app) => `- ${app}`).join('\n') | ||
); | ||
|
||
if (!name) { | ||
name = await std.asker.askClearingScreen( | ||
[`\x1b[1mWhat is the name of your project?\x1b[0m`], | ||
(answer) => `\x1b[1mWhat is the name of your project?\x1b[0m \x1b[32m${answer}\x1b[0m` | ||
); | ||
} | ||
|
||
const packageManagerOptions = { | ||
'1': 'npm', | ||
'2': 'yarn', | ||
'3': 'pnpm', | ||
'4': 'bun' | ||
}; | ||
const packageManagerOption = await std.asker.askClearingScreen( | ||
[ | ||
`\x1b[1mWhich package manager would you like to use?\x1b[0m`, | ||
...Object.entries(packageManagerOptions).map(([key, value]) => `${key}. ${value}`) | ||
], | ||
(answer) => | ||
`\x1b[1mWhich package manager would you like to use?\x1b[0m ` + | ||
`\x1b[32m${packageManagerOptions[answer as keyof typeof packageManagerOptions]}\x1b[0m` | ||
); | ||
const packageManager = packageManagerOptions[packageManagerOption as keyof typeof packageManagerOptions]; | ||
const path = await std.files.join(fullPath, appType); | ||
try { | ||
const templatesJson = JSON.parse(await std.files.readFile(await std.files.join(path, 'templates.json'))) as | ||
| TemplatesJson | ||
| undefined; | ||
|
||
if (!templatesJson) throw new Error('Could not find templates.json in the template directory'); | ||
|
||
const templateToUse = commandLineArgs.keywordArgs.template; | ||
let template = templateToUse | ||
? templatesJson.templates.find((template) => template.name === templateToUse) | ||
: undefined; | ||
|
||
if (!template) { | ||
const question = | ||
typeof templateToUse === 'string' | ||
? `Template '${templateToUse}' not found. Choose one from the list` | ||
: 'Which template would you like to use?'; | ||
const templateOptions = templatesJson.templates.reduce( | ||
(accumulator, template, index) => { | ||
accumulator[index + 1] = template.name; | ||
return accumulator; | ||
}, | ||
{} as Record<string, string> | ||
); | ||
const answer = await std.asker.askClearingScreen( | ||
[ | ||
`\x1b[1m${question}\x1b[0m`, | ||
...Object.entries(templateOptions).map(([key, value]) => `${key}. ${value}`) | ||
], | ||
(answer) => `\x1b[1m${question}\x1b[0m ` + `\x1b[32m${templateOptions[answer]}\x1b[0m` | ||
); | ||
|
||
template = templatesJson.templates.find((template) => template.name === templateOptions[answer]); | ||
} | ||
|
||
if (template?.messages['onStart']) | ||
logger.log( | ||
template.messages['onStart'] | ||
.replaceAll('${appName}', name) | ||
.replaceAll('${packageManager}', packageManager) | ||
); | ||
|
||
const templatePath = await std.files.join(path, ...(template?.location.split('/') || [])); | ||
await recursivelyCopyFilesFromTemplate(std, name, templatePath); | ||
|
||
logger.log(`\x1b[1mInstalling dependencies on '${name}' using '${packageManager}'...\x1b[0m`); | ||
|
||
const done = { | ||
done: false | ||
}; | ||
|
||
const message1 = setTimeout(() => { | ||
if (!done.done) logger.log(`It might take a few minutes, hang in there...`); | ||
}, 10000); | ||
|
||
const message2 = setTimeout(() => { | ||
if (!done.done) logger.log(`Just a little more time...`); | ||
}, 30000); | ||
|
||
const message3 = setTimeout(() => { | ||
if (!done.done) | ||
logger.log( | ||
`Use the millions you will earn with your '${name}' application to buy ` + | ||
`yourself a really good internet connection...\n` | ||
); | ||
}, 60000); | ||
|
||
const message4 = setTimeout(() => { | ||
if (!done.done) logger.log(`Probably using Bun will speed it up, you should check it out!\n`); | ||
}, 60000 * 2); | ||
|
||
const message5 = setTimeout(() => { | ||
if (!done.done) | ||
logger.log(`You are still here huh? I like your perseverance, but you should give up, it's over\n`); | ||
}, 60000 * 5); | ||
|
||
std.childProcess.executeAndOutput(`cd ${name} && ${packageManager} i`).then(async () => { | ||
done.done = true; | ||
clearTimeout(message1); | ||
clearTimeout(message2); | ||
clearTimeout(message3); | ||
clearTimeout(message4); | ||
clearTimeout(message5); | ||
|
||
if (template?.messages['onFinish']) { | ||
for (const command of template.messages['onFinish'].commands) | ||
console.log( | ||
await std.childProcess.executeAndOutput( | ||
`cd ${name} && ${command.replaceAll('${packageManager}', packageManager)}` | ||
) | ||
); | ||
logger.log( | ||
template.messages['onFinish'].message | ||
.replaceAll('${appName}', name) | ||
.replaceAll('${packageManager}', packageManager) | ||
); | ||
} | ||
}); | ||
} catch (e) { | ||
console.log(e); | ||
throw new Error('Could not find templates.json in the template directory'); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
|
||
Commands.handleCommands( | ||
defineSettings({ | ||
basePath: '', | ||
settingsLocation: '', | ||
std: NodeStd, | ||
installedDomains: [ | ||
[CoreDomain, {}], | ||
[ | ||
loggingDomain, | ||
{ | ||
logger: ConsoleLogging | ||
} | ||
], | ||
cpaDomain | ||
] | ||
}), | ||
process.argv.slice(2) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import type { Std } from '@palmares/core'; | ||
|
||
export async function recursivelyCopyFilesFromTemplate(std: Std, projectName: string, templateDirectoryPath: string) { | ||
const recursivelyCopyFiles = async (directoryToCreate: string, path: string) => { | ||
const templateFiles = await std.files.readDirectory(path); | ||
await std.files.makeDirectory(directoryToCreate); | ||
|
||
await Promise.all( | ||
templateFiles.map(async (fileOrFolder) => { | ||
const fileNameRenamed = fileOrFolder.replace(/^\$/g, '.').replace(/^_/g, ''); | ||
const locationPathToCopyFrom = await std.files.join(path, fileOrFolder); | ||
try { | ||
await std.files.readDirectory(locationPathToCopyFrom); | ||
const newDirectoryPath = await std.files.join(directoryToCreate, fileNameRenamed); | ||
recursivelyCopyFiles(newDirectoryPath, locationPathToCopyFrom); | ||
} catch (e) { | ||
//console.log(locationPathToCopyFrom); | ||
let fileContent = await std.files.readFile(locationPathToCopyFrom); | ||
const fileName = await std.files.join(directoryToCreate, fileNameRenamed); | ||
//console.log('Creating file from', locationPathToCopyFrom, 'to', fileName); | ||
const isPackageJson = fileNameRenamed === 'package.json'; | ||
if (isPackageJson) { | ||
const fileContentAsJson = JSON.parse(fileContent); | ||
fileContentAsJson.name = projectName; | ||
const dependenciesAsEntries = Object.entries(fileContentAsJson.dependencies).concat( | ||
Object.entries(fileContentAsJson.devDependencies) | ||
); | ||
await Promise.all( | ||
dependenciesAsEntries.map(async ([key, value]) => { | ||
if (value === '${version}') { | ||
const allVersions = await std.childProcess.executeAndOutput(`npm view ${key} versions --json`); | ||
const allVersionsAsArray = JSON.parse(allVersions) as string[]; | ||
let latestVersion = allVersionsAsArray.pop(); | ||
while (allVersionsAsArray.length > 0 && latestVersion && latestVersion.includes('-')) { | ||
latestVersion = allVersionsAsArray.pop(); | ||
} | ||
if (fileContentAsJson.dependencies[key]) fileContentAsJson.dependencies[key] = `^${latestVersion}`; | ||
else fileContentAsJson.devDependencies[key] = `^${latestVersion}`; | ||
} | ||
}) | ||
); | ||
fileContent = JSON.stringify(fileContentAsJson, null, 2); | ||
} | ||
await std.files.writeFile( | ||
fileName, | ||
fileContent.replace(`// eslint-disable-next-line ts/ban-ts-comment\n// @ts-nocheck\n`, '') | ||
); | ||
} | ||
}) | ||
); | ||
}; | ||
|
||
await recursivelyCopyFiles(projectName, templateDirectoryPath); | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# @palmares/drizzle-template | ||
|
||
To install dependencies: | ||
|
||
```bash | ||
bun install | ||
``` | ||
|
||
To run: | ||
|
||
```bash | ||
bun run index.ts | ||
``` | ||
|
||
This project was created using `bun init` in bun v1.1.33. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { setDatabaseConfig } from '@palmares/databases'; | ||
import { DrizzleDatabaseAdapter } from '@palmares/drizzle-engine'; | ||
import { drizzle as drizzleBetterSqlite3 } from '@palmares/drizzle-engine/better-sqlite3'; | ||
import { NodeStd } from '@palmares/node-std'; | ||
import Database from 'better-sqlite3'; | ||
|
||
import { Company, User } from './src/models'; | ||
|
||
const database = new Database('sqlite.db'); | ||
|
||
const newEngine = DrizzleDatabaseAdapter.new({ | ||
output: './.drizzle/schema.ts', | ||
type: 'better-sqlite3', | ||
drizzle: drizzleBetterSqlite3(database) | ||
}); | ||
|
||
export const db = newEngine[1]().instance.instance; | ||
|
||
export default setDatabaseConfig({ | ||
databases: { | ||
default: { | ||
engine: newEngine | ||
} | ||
}, | ||
locations: [ | ||
{ | ||
name: 'default', | ||
path: import.meta.dirname, | ||
getMigrations: () => [], | ||
getModels: () => [Company, User] | ||
} | ||
], | ||
std: new NodeStd() | ||
}); |
Oops, something went wrong.