devin 0.0.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/.vscode/settings.json +16 -0
- package/LICENSE +21 -0
- package/README.md +36 -0
- package/biome.jsonc +74 -0
- package/dist/index.js +3 -0
- package/package.json +51 -0
- package/reset.d.ts +1 -0
- package/src/index.ts +121 -0
- package/src/utils/get-user-package-manager.ts +15 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +13 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"editor.codeActionsOnSave": {
|
|
3
|
+
"source.addMissingImports.ts": "always",
|
|
4
|
+
"source.fixAll": "explicit",
|
|
5
|
+
"source.fixAll.format": "always",
|
|
6
|
+
"source.organizeImports": "always",
|
|
7
|
+
"source.organizeImports.biome": "always"
|
|
8
|
+
},
|
|
9
|
+
"editor.defaultFormatter": "biomejs.biome",
|
|
10
|
+
"editor.formatOnPaste": false,
|
|
11
|
+
"editor.formatOnSave": true,
|
|
12
|
+
"prettier.requireConfig": true,
|
|
13
|
+
"[markdown]": {
|
|
14
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Devin Metivier
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
```sh
|
|
2
|
+
npx devin
|
|
3
|
+
```
|
|
4
|
+
|
|
5
|
+
Deterministic, effortless scaffolding for modern web application. Choose your stack, tools, and configuration in seconds.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Scaffold projects with:
|
|
10
|
+
- Next.js
|
|
11
|
+
- TypeScript (or JavaScript - if you dare...)
|
|
12
|
+
- Tailwind CSS
|
|
13
|
+
- tRPC or Hono
|
|
14
|
+
- Authentication (Stytch)
|
|
15
|
+
- Database ORM (Drizzle, Prisma)
|
|
16
|
+
- Database provider (SQLite, MySQL, PlanetScale)
|
|
17
|
+
- Linting/formatting (Biome or ESLint + Prettier)
|
|
18
|
+
- Git initialization
|
|
19
|
+
- Dependency installation
|
|
20
|
+
- Custom import alias
|
|
21
|
+
- Modern, minimal UX—navigate with arrows, select with space/enter
|
|
22
|
+
- Sensible defaults
|
|
23
|
+
|
|
24
|
+
You'll be guided through a series of prompts to configure your new project.
|
|
25
|
+
|
|
26
|
+
## Contributing
|
|
27
|
+
|
|
28
|
+
Contributions are welcome! Please open issues or pull requests for improvements, bug fixes, or new features.
|
|
29
|
+
|
|
30
|
+
## License
|
|
31
|
+
|
|
32
|
+
MIT
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx devin
|
|
36
|
+
```
|
package/biome.jsonc
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
3
|
+
"css": {
|
|
4
|
+
"formatter": {
|
|
5
|
+
"enabled": true,
|
|
6
|
+
"indentStyle": "space",
|
|
7
|
+
"indentWidth": 2,
|
|
8
|
+
"lineWidth": 120
|
|
9
|
+
},
|
|
10
|
+
"linter": {
|
|
11
|
+
"enabled": true
|
|
12
|
+
},
|
|
13
|
+
"parser": {
|
|
14
|
+
"cssModules": true
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": {
|
|
18
|
+
"maxSize": 20000000
|
|
19
|
+
},
|
|
20
|
+
"formatter": {
|
|
21
|
+
"attributePosition": "auto",
|
|
22
|
+
"enabled": true,
|
|
23
|
+
"formatWithErrors": false,
|
|
24
|
+
"ignore": ["**/dist/**", "**/node_modules/**", "**/pnpm-lock.yaml"],
|
|
25
|
+
"indentStyle": "space",
|
|
26
|
+
"indentWidth": 2,
|
|
27
|
+
"lineEnding": "lf",
|
|
28
|
+
"lineWidth": 120
|
|
29
|
+
},
|
|
30
|
+
"javascript": {
|
|
31
|
+
"formatter": {
|
|
32
|
+
"arrowParentheses": "always",
|
|
33
|
+
"attributePosition": "auto",
|
|
34
|
+
"bracketSameLine": false,
|
|
35
|
+
"bracketSpacing": true,
|
|
36
|
+
"jsxQuoteStyle": "single",
|
|
37
|
+
"quoteProperties": "asNeeded",
|
|
38
|
+
"quoteStyle": "single",
|
|
39
|
+
"semicolons": "always",
|
|
40
|
+
"trailingCommas": "all"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"linter": {
|
|
44
|
+
"enabled": true,
|
|
45
|
+
"ignore": ["**/dist/**", "**/node_modules/**", "**/pnpm-lock.yaml"],
|
|
46
|
+
"rules": {
|
|
47
|
+
"a11y": {
|
|
48
|
+
"noSvgWithoutTitle": "off",
|
|
49
|
+
"useKeyWithClickEvents": "off"
|
|
50
|
+
},
|
|
51
|
+
"nursery": {
|
|
52
|
+
"useSortedClasses": {
|
|
53
|
+
"fix": "safe",
|
|
54
|
+
"level": "error",
|
|
55
|
+
"options": {
|
|
56
|
+
"attributes": ["className"],
|
|
57
|
+
"functions": ["cn"]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"recommended": true,
|
|
62
|
+
"style": {
|
|
63
|
+
"noNonNullAssertion": "warn"
|
|
64
|
+
},
|
|
65
|
+
"suspicious": {
|
|
66
|
+
"noArrayIndexKey": "warn",
|
|
67
|
+
"noExplicitAny": "warn"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"organizeImports": {
|
|
72
|
+
"enabled": true
|
|
73
|
+
}
|
|
74
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
import{cancel as a,confirm as r,intro as v,isCancel as l,note as w,outro as h,select as t,text as f}from"@clack/prompts";import e from"chalk";async function O(){v(e.magenta.bold("Welcome to Devin's App Bootstrapper CLI!"));let c=await f({message:"What will your project be called?",validate:s=>s?void 0:"Project name cannot be empty"});if(l(c))return a("Operation cancelled.");let o=await t({message:"Will you be using Typescript or Javascript?",options:[{value:"typescript",label:"Typescript"},{value:"javascript",label:"Javascript"}]});if(l(o))return a("Operation cancelled.");o==="javascript"&&w(e.redBright("Wrong answer, using TypeScript instead"));let u=await r({message:"Will you be using Tailwind CSS for styling?",initialValue:!0});if(l(u))return a("Operation cancelled.");let p=await t({message:"Which of these RPC libraries would you like to use?",options:[{value:"trpc",label:"tRPC"},{value:"hono",label:"Hono"},{value:"none",label:"None"}]});if(l(p))return a("Operation cancelled.");let d=await t({message:"What authentication provider would you like to use?",options:[{value:"none",label:"None"},{value:"stytch",label:"Stytch"}]});if(l(d))return a("Operation cancelled.");let n=await t({message:"What databse ORM would you like to use?",options:[{value:"drizzle",label:"Drizzle"},{value:"prisma",label:"Prisma"},{value:"none",label:"None"}]});if(l(n))return a("Operation cancelled.");let i;n!=="none"&&(i=await t({message:"What database provider would you like to use?",options:[{value:"sqlite",label:"SQLite (LibSQL)"},{value:"mysql",label:"MySQL"},{value:"planetscale",label:"PlanetScale"}]}));let g=await t({message:"Would you like to use Biome or Eslint/Prettier for linting/formatting?",options:[{value:"biome",label:"Biome"},{value:"eslint-prettier",label:"Eslint and Prettier"}]});if(l(g))return a("Operation cancelled.");let m=await r({message:"Do you want to initialize a Git repository and stage the changes?",initialValue:!0});if(l(m))return a("Operation cancelled.");let y=await r({message:"Should we install dependencies for you?",initialValue:!0});if(l(y))return a("Operation cancelled.");let b=await f({message:"What import alias would you like to use?",placeholder:"~/",initialValue:"~/",validate:s=>s?void 0:"Import alias cannot be empty"});if(l(b))return a("Operation cancelled.");console.log(`
|
|
3
|
+
${e.green("Your selections:")}`),console.log(` Project name: ${e.cyan(c)}`),console.log(` Language: ${e.cyan(o)}`),console.log(` Tailwind CSS: ${u?e.green("Yes"):e.red("No")}`),console.log(` RPC: ${e.cyan(p)}`),console.log(` Auth: ${e.cyan(d)}`),console.log(` ORM: ${e.cyan(n)}`),i&&console.log(` Database: ${e.cyan(i)}`),console.log(` Linter: ${e.cyan(g)}`),console.log(` Git: ${m?e.green("Yes"):e.red("No")}`),console.log(` Install deps: ${y?e.green("Yes"):e.red("No")}`),console.log(` Import alias: ${e.cyan(b)}`),h(e.yellow("Scaffolding will begin here in the next step..."))}O();
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": {
|
|
3
|
+
"name": "Devin Metivier",
|
|
4
|
+
"url": "https://devinmetivier.com"
|
|
5
|
+
},
|
|
6
|
+
"bin": {
|
|
7
|
+
"devin": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/devjmetivier/devin/issues"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@clack/prompts": "^0.10.1",
|
|
14
|
+
"chalk": "^5.0.1"
|
|
15
|
+
},
|
|
16
|
+
"description": "Devin Metivier",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@biomejs/biome": "^1.9.4",
|
|
19
|
+
"@total-typescript/ts-reset": "^0.6.1",
|
|
20
|
+
"@types/node": "^22.15.18",
|
|
21
|
+
"@types/open": "^6.2.1",
|
|
22
|
+
"prettier": "^3.5.3",
|
|
23
|
+
"tsup": "^8.5.0",
|
|
24
|
+
"typescript": "^5.8.3"
|
|
25
|
+
},
|
|
26
|
+
"exports": "./dist/index.js",
|
|
27
|
+
"homepage": "https://github.com/devjmetivier/devin#readme",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"main": "./src/index.js",
|
|
30
|
+
"name": "devin",
|
|
31
|
+
"packageManager": "pnpm@10.5.2+sha512.da9dc28cd3ff40d0592188235ab25d3202add8a207afbedc682220e4a0029ffbff4562102b9e6e46b4e3f9e8bd53e6d05de48544b0c57d4b0179e22c76d1199b",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/devjmetivier/devin.git"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup",
|
|
38
|
+
"clean": "rm -rf dist .turbo node_modules",
|
|
39
|
+
"dev": "tsup --watch",
|
|
40
|
+
"format": "biome check . --write --unsafe --diagnostic-level=error && biome format --diagnostic-level=error --write .",
|
|
41
|
+
"go": "node src/index.js",
|
|
42
|
+
"lint": "pnpm biome lint --write --unsafe --diagnostic-level=error",
|
|
43
|
+
"pub:beta": "pnpm build && npm publish --tag beta",
|
|
44
|
+
"pub:next": "pnpm build && npm publish --tag next",
|
|
45
|
+
"pub:release": "pnpm build && npm publish",
|
|
46
|
+
"start": "node dist/index.js",
|
|
47
|
+
"typecheck": "tsc"
|
|
48
|
+
},
|
|
49
|
+
"type": "module",
|
|
50
|
+
"version": "0.0.0"
|
|
51
|
+
}
|
package/reset.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@total-typescript/ts-reset';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { cancel, confirm, intro, isCancel, note, outro, select, text } from '@clack/prompts';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
async function main() {
|
|
7
|
+
intro(chalk.magenta.bold("Welcome to Devin's App Bootstrapper CLI!"));
|
|
8
|
+
|
|
9
|
+
const projectName = await text({
|
|
10
|
+
message: 'What will your project be called?',
|
|
11
|
+
validate: (input: string) => (input ? undefined : 'Project name cannot be empty'),
|
|
12
|
+
});
|
|
13
|
+
if (isCancel(projectName)) return cancel('Operation cancelled.');
|
|
14
|
+
|
|
15
|
+
const language = await select({
|
|
16
|
+
message: 'Will you be using Typescript or Javascript?',
|
|
17
|
+
options: [
|
|
18
|
+
{ value: 'typescript', label: 'Typescript' },
|
|
19
|
+
{ value: 'javascript', label: 'Javascript' },
|
|
20
|
+
],
|
|
21
|
+
});
|
|
22
|
+
if (isCancel(language)) return cancel('Operation cancelled.');
|
|
23
|
+
|
|
24
|
+
if (language === 'javascript') {
|
|
25
|
+
note(chalk.redBright('Wrong answer, using TypeScript instead'));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const tailwind = await confirm({
|
|
29
|
+
message: 'Will you be using Tailwind CSS for styling?',
|
|
30
|
+
initialValue: true,
|
|
31
|
+
});
|
|
32
|
+
if (isCancel(tailwind)) return cancel('Operation cancelled.');
|
|
33
|
+
|
|
34
|
+
const rpc = await select({
|
|
35
|
+
message: 'Which of these RPC libraries would you like to use?',
|
|
36
|
+
options: [
|
|
37
|
+
{ value: 'trpc', label: 'tRPC' },
|
|
38
|
+
{ value: 'hono', label: 'Hono' },
|
|
39
|
+
{ value: 'none', label: 'None' },
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
if (isCancel(rpc)) return cancel('Operation cancelled.');
|
|
43
|
+
|
|
44
|
+
const auth = await select({
|
|
45
|
+
message: 'What authentication provider would you like to use?',
|
|
46
|
+
options: [
|
|
47
|
+
{ value: 'none', label: 'None' },
|
|
48
|
+
{ value: 'stytch', label: 'Stytch' },
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
if (isCancel(auth)) return cancel('Operation cancelled.');
|
|
52
|
+
|
|
53
|
+
const orm = await select({
|
|
54
|
+
message: 'What databse ORM would you like to use?',
|
|
55
|
+
options: [
|
|
56
|
+
{ value: 'drizzle', label: 'Drizzle' },
|
|
57
|
+
{ value: 'prisma', label: 'Prisma' },
|
|
58
|
+
{ value: 'none', label: 'None' },
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
if (isCancel(orm)) return cancel('Operation cancelled.');
|
|
62
|
+
|
|
63
|
+
let databaseProvider: symbol | 'postgres' | 'sqlite' | 'mysql' | 'planetscale' | undefined;
|
|
64
|
+
if (orm !== 'none') {
|
|
65
|
+
databaseProvider = await select({
|
|
66
|
+
message: 'What database provider would you like to use?',
|
|
67
|
+
options: [
|
|
68
|
+
{ value: 'sqlite', label: 'SQLite (LibSQL)' },
|
|
69
|
+
{ value: 'mysql', label: 'MySQL' },
|
|
70
|
+
{ value: 'planetscale', label: 'PlanetScale' },
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const linter = await select({
|
|
76
|
+
message: 'Would you like to use Biome or Eslint/Prettier for linting/formatting?',
|
|
77
|
+
options: [
|
|
78
|
+
{ value: 'biome', label: 'Biome' },
|
|
79
|
+
{ value: 'eslint-prettier', label: 'Eslint and Prettier' },
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
if (isCancel(linter)) return cancel('Operation cancelled.');
|
|
83
|
+
|
|
84
|
+
const git = await confirm({
|
|
85
|
+
message: 'Do you want to initialize a Git repository and stage the changes?',
|
|
86
|
+
initialValue: true,
|
|
87
|
+
});
|
|
88
|
+
if (isCancel(git)) return cancel('Operation cancelled.');
|
|
89
|
+
|
|
90
|
+
const install = await confirm({
|
|
91
|
+
message: 'Should we install dependencies for you?',
|
|
92
|
+
initialValue: true,
|
|
93
|
+
});
|
|
94
|
+
if (isCancel(install)) return cancel('Operation cancelled.');
|
|
95
|
+
|
|
96
|
+
const importAlias = await text({
|
|
97
|
+
message: 'What import alias would you like to use?',
|
|
98
|
+
placeholder: '~/',
|
|
99
|
+
initialValue: '~/',
|
|
100
|
+
validate: (input: string) => (input ? undefined : 'Import alias cannot be empty'),
|
|
101
|
+
});
|
|
102
|
+
if (isCancel(importAlias)) return cancel('Operation cancelled.');
|
|
103
|
+
|
|
104
|
+
// Print summary
|
|
105
|
+
console.log(`\n${chalk.green('Your selections:')}`);
|
|
106
|
+
console.log(` Project name: ${chalk.cyan(projectName)}`);
|
|
107
|
+
console.log(` Language: ${chalk.cyan(language)}`);
|
|
108
|
+
console.log(` Tailwind CSS: ${tailwind ? chalk.green('Yes') : chalk.red('No')}`);
|
|
109
|
+
console.log(` RPC: ${chalk.cyan(rpc)}`);
|
|
110
|
+
console.log(` Auth: ${chalk.cyan(auth)}`);
|
|
111
|
+
console.log(` ORM: ${chalk.cyan(orm)}`);
|
|
112
|
+
if (databaseProvider) console.log(` Database: ${chalk.cyan(databaseProvider)}`);
|
|
113
|
+
console.log(` Linter: ${chalk.cyan(linter)}`);
|
|
114
|
+
console.log(` Git: ${git ? chalk.green('Yes') : chalk.red('No')}`);
|
|
115
|
+
console.log(` Install deps: ${install ? chalk.green('Yes') : chalk.red('No')}`);
|
|
116
|
+
console.log(` Import alias: ${chalk.cyan(importAlias)}`);
|
|
117
|
+
|
|
118
|
+
outro(chalk.yellow('Scaffolding will begin here in the next step...'));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
main();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
|
|
2
|
+
|
|
3
|
+
export function getUserPkgManager(): PackageManager {
|
|
4
|
+
// This environment variable is set by npm and yarn but pnpm seems less consistent
|
|
5
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
6
|
+
|
|
7
|
+
if (userAgent) {
|
|
8
|
+
if (userAgent.startsWith('yarn')) return 'yarn';
|
|
9
|
+
if (userAgent.startsWith('pnpm')) return 'pnpm';
|
|
10
|
+
if (userAgent.startsWith('bun')) return 'bun';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// If no user agent is set, assume npm
|
|
14
|
+
return 'npm';
|
|
15
|
+
}
|
package/tsconfig.json
ADDED
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
const isDev = process.env.npm_lifecycle_event === 'dev';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
clean: true,
|
|
7
|
+
entry: ['src/index.ts'],
|
|
8
|
+
format: ['esm'],
|
|
9
|
+
minify: !isDev,
|
|
10
|
+
target: 'esnext',
|
|
11
|
+
outDir: 'dist',
|
|
12
|
+
onSuccess: isDev ? 'node dist/index.js' : undefined,
|
|
13
|
+
});
|