Skip to content

Commit 9812fee

Browse files
committed
feat: refactor, wip scaffolding
1 parent 3c9ed6e commit 9812fee

File tree

7 files changed

+206
-77
lines changed

7 files changed

+206
-77
lines changed

commands/init.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import { input, confirm, select, checkbox } from "@inquirer/prompts"
44
import * as y from "yoctocolors"
55
import { action as secretAction } from "./secret.js"
66

7-
import { install } from "../lib/pkg-manager.js"
7+
import { getPkgManager, install } from "../lib/pkg-manager.js"
88
import * as meta from "../lib/meta.js"
9+
import { updateEnvFile } from "../lib/write-env.js"
10+
import { mkdir } from "node:fs/promises"
911

1012
/**
1113
* @param {string} framework
@@ -89,6 +91,26 @@ async function createFromExample(framework, dir) {
8991
* @param {{framework: string, dir: string, providers: string[], adapter?: string}} options
9092
*/
9193
async function scaffoldProject(options) {
92-
console.log(options)
93-
throw new Error("Not implemented")
94+
const { framework, providers, dir, adapter } = options
95+
const pkgManager = getPkgManager()
96+
console.log(
97+
`Scaffolding ${y.bold(framework)} project with ${y.bold(
98+
pkgManager
99+
)} at ${y.bold(dir)}...`
100+
)
101+
throw new Error("Scaffolding not implemented. Please use `init --example`")
102+
console.log(`Create directory ${dir}`)
103+
await mkdir(dir)
104+
console.log(`Change directory to ${dir}`)
105+
execSync(`cd ${dir}`)
106+
console.log(`Initialize ${pkgManager} project`)
107+
execSync(`${pkgManager} init`)
108+
// console.log(`Add ${framework} to ${pkgManager}`)
109+
// execSync(`${pkgManager} install ${framework}`)
110+
for (const provider of providers) {
111+
const id = `AUTH_${provider.toUpperCase()}_ID`
112+
const secret = `AUTH_${provider.toUpperCase()}_SECRET`
113+
await updateEnvFile(dir, id, "")
114+
await updateEnvFile(dir, secret, "")
115+
}
94116
}

commands/secret.js

Lines changed: 11 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import * as y from "yoctocolors"
44
import { write } from "../lib/clipboard/index.js"
55
import { detectFramework } from "../lib/detect.js"
6-
import { readFile, writeFile } from "node:fs/promises"
76
import { join } from "node:path"
8-
import prompt from "prompts"
7+
import { updateEnvFile } from "../lib/write-env.js"
8+
import { frameworks } from "../lib/meta.js"
99

1010
/** Web compatible method to create a random string of a given length */
1111
function randomString(size = 32) {
@@ -14,13 +14,6 @@ function randomString(size = 32) {
1414
return Buffer.from(bytes, "base64").toString("base64")
1515
}
1616

17-
/** @type {Record<import("../lib/detect.js").SupportedFramework, string>} */
18-
export const frameworkDotEnvFile = {
19-
nextjs: ".env.local",
20-
express: ".env",
21-
sveltekit: ".env",
22-
}
23-
2417
/**
2518
* @param {{
2619
* copy?: boolean
@@ -56,63 +49,18 @@ export async function action(options) {
5649
}
5750
}
5851

59-
if (options.write) {
60-
try {
61-
const framework = await detectFramework(options.path)
62-
if (framework === "unknown") {
63-
return console.log(
64-
`No framework detected. Currently supported frameworks are: ${y.bold(
65-
Object.keys(frameworkDotEnvFile).join(", ")
66-
)}`
67-
)
68-
}
69-
const dotEnvFile = frameworkDotEnvFile[framework]
70-
await updateEnvFile(
71-
dotEnvFile,
72-
join(process.cwd(), options.path ?? "", dotEnvFile),
73-
key,
74-
value
52+
try {
53+
const framework = await detectFramework(options.path)
54+
if (framework === "unknown") {
55+
return console.log(
56+
`No framework detected. Currently supported frameworks are: ${y.bold(
57+
Object.keys(frameworks).join(", ")
58+
)}`
7559
)
76-
} catch (error) {
77-
console.error(y.red(error))
7860
}
79-
}
80-
}
8161

82-
/**
83-
* Update a key-value pair to a .env file
84-
* @param {string} file
85-
* @param {string} envPath
86-
* @param {string} key
87-
* @param {string} value
88-
*/
89-
async function updateEnvFile(file, envPath, key, value) {
90-
let content = ""
91-
const line = `${key}="${value}" # Added by \`npx auth\`. Read more: https://cli.authjs.dev`
92-
try {
93-
await readFile(envPath, "utf-8")
94-
content = await readFile(envPath, "utf-8")
95-
if (!content.includes(`${key}=`)) {
96-
console.log(`➕ Added ${key} to ${y.italic(file)}.`)
97-
content = `${line}\n${content}`
98-
} else {
99-
const { overwrite } = await prompt({
100-
type: "confirm",
101-
name: "overwrite",
102-
message: `Overwrite existing ${key}?`,
103-
initial: false,
104-
})
105-
if (!overwrite) return
106-
console.log(`✨ Updated ${key} in ${y.italic(file)}.`)
107-
content = content.replace(new RegExp(`${key}=(.*)`), `${line}`)
108-
}
62+
await updateEnvFile(options.path, key, value)
10963
} catch (error) {
110-
if (error.code === "ENOENT") {
111-
console.log(`📝 Created ${y.italic(file)} with ${key}.`)
112-
content = line
113-
} else {
114-
throw error
115-
}
64+
console.error(y.red(error))
11665
}
117-
if (content) await writeFile(envPath, content)
11866
}

index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ program
4949
.command("secret")
5050
.option("--raw", "Output the string without any formatting.")
5151
.option("--copy", 'Copy AUTH_SECRET="value".')
52-
.option("--write", 'Write AUTH_SECRET="value" to the .env file.')
53-
.description("Generate a random string.")
52+
.description("Generate a random string and add it to the .env file.")
5453
.action(secret.action)
5554

5655
program.parse()

lib/detect.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,22 @@
22
import { readFile } from "node:fs/promises"
33
import { join } from "node:path"
44

5-
/**
6-
* @typedef {"nextjs" | "express" | "sveltekit"} SupportedFramework
7-
*/
8-
95
/**
106
* When this function runs in a framework directory we support,
117
* it will return the framework's name
128
* @param {string} path
13-
* @returns {Promise<SupportedFramework | "unknown">}
9+
* @returns {Promise<import("./meta").SupportedFramework | "unknown">}
1410
*/
1511
export async function detectFramework(path = "") {
1612
const dir = process.cwd()
1713
const packageJsonPath = join(dir, path, "package.json")
1814
try {
1915
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"))
2016

21-
/** @type {SupportedFramework[]} */
17+
/** @type {import("./meta").SupportedFramework[]} */
2218
const foundFrameworks = []
2319

24-
if (packageJson.dependencies["next"]) foundFrameworks.push("nextjs")
20+
if (packageJson.dependencies["next"]) foundFrameworks.push("next")
2521
if (packageJson.dependencies["express"]) foundFrameworks.push("express")
2622
if (packageJson.devDependencies["@sveltejs/kit"])
2723
foundFrameworks.push("sveltekit")
@@ -35,7 +31,7 @@ export async function detectFramework(path = "") {
3531
return "unknown"
3632
}
3733
return "unknown"
38-
} catch (error) {
34+
} catch {
3935
return "unknown"
4036
}
4137
}

lib/meta.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,140 @@
22

33
// TODO: Get these programmatically
44

5+
/**
6+
* @typedef {"next" | "express" | "sveltekit"} SupportedFramework
7+
*/
8+
59
export const frameworks = {
610
next: {
711
name: "Next.js",
812
src: "https://github.com/nextauthjs/next-auth-example",
913
demo: "https://next-auth-example.vercel.app",
14+
envFile: ".env.local",
1015
},
1116
sveltekit: {
1217
name: "SvelteKit",
1318
src: "https://github.com/nextauthjs/sveltekit-auth-example",
1419
demo: "https://sveltekit-auth-example.vercel.app",
20+
envFile: ".env",
1521
},
1622
express: {
1723
name: "Express",
1824
src: "https://github.com/nextauthjs/express-auth-example",
1925
demo: "https://express-auth-example.vercel.app",
26+
envFile: ".env",
2027
},
2128
}
2229

2330
export const providers = {
31+
"42-school": "42 School",
32+
apple: "Apple",
33+
asgardeo: "Asgardeo",
34+
auth0: "Auth0",
35+
authentik: "Authentik",
36+
"azure-ad-b2c": "Azure AD B2C",
37+
"azure-ad": "Azure AD",
38+
"azure-devops": "Azure DevOps",
39+
battlenet: "Battlenet",
40+
beyondidentity: "Beyond Identity",
41+
box: "Box",
42+
"boxyhq-saml": "Boxyhq SAML",
43+
bungie: "Bungie",
44+
"click-up": "Click-up",
45+
cognito: "Cognito",
46+
coinbase: "Coinbase",
47+
credentials: "Credentials",
48+
descope: "Descope",
49+
discord: "Discord",
50+
dribbble: "Dribbble",
51+
dropbox: "Dropbox",
52+
"duende-identity-server6": "Duende Identity Server 6",
53+
email: "Email",
54+
eveonline: "Eveonline",
55+
facebook: "Facebook",
56+
faceit: "Faceit",
57+
foursquare: "Foursquare",
58+
freshbooks: "Freshbooks",
59+
fusionauth: "FusionAuth",
2460
github: "GitHub",
61+
gitlab: "GitLab",
2562
google: "Google",
63+
hubspot: "Hubspot",
64+
"identity-server4": "Identity Server 4",
65+
instagram: "Instagram",
66+
kakao: "Kakao",
67+
keycloak: "Keycloak",
68+
line: "Line",
69+
linkedin: "LinkedIn",
70+
mailchimp: "Mailchimp",
71+
mailru: "Mail.ru",
72+
mastodon: "Mastodon",
73+
mattermost: "Mattermost",
74+
medium: "Medium",
75+
"microsoft-entra-id": "Microsoft Entra ID",
76+
naver: "Naver",
77+
netlify: "Netlify",
78+
netsuite: "Netsuite",
79+
nodemailer: "Nodemailer",
80+
notion: "Notion",
81+
okta: "Okta",
82+
onelogin: "Onelogin",
83+
"ory-hydra": "Ory Hydra",
84+
osso: "Osso",
85+
osu: "Osu",
86+
passage: "Passage",
87+
passkey: "Passkey",
88+
patreon: "Patreon",
89+
pinterest: "Pinterest",
90+
pipedrive: "Pipedrive",
91+
postmark: "Postmark",
92+
reddit: "Reddit",
2693
resend: "Resend",
94+
salesforce: "Salesforce",
95+
sendgrid: "Sendgrid",
96+
slack: "Slack",
97+
spotify: "Spotify",
98+
strava: "Strava",
99+
tiktok: "Tiktok",
100+
todoist: "Todoist",
101+
trakt: "Trakt",
102+
twitch: "Twitch",
27103
twitter: "Twitter",
104+
"united-effects": "United Effects",
105+
vk: "Vk",
106+
webex: "Webex",
107+
wikimedia: "Wikimedia",
108+
wordpress: "Wordpress",
109+
workos: "WorkOS",
110+
yandex: "Yandex",
111+
zitadel: "Zitadel",
112+
zoho: "Zoho",
113+
zoom: "Zoom",
28114
}
29115

30116
export const adapters = {
31117
none: "None",
118+
"adapter-azure-tables": "Azure Tables",
119+
d1: "d1",
120+
dgraph: "Dgraph",
32121
drizzle: "Drizzle",
122+
dynamodb: "DynamoDB",
123+
edgedb: "EdgeDB",
124+
fauna: "Fauna",
125+
firebase: "Firebase",
126+
hasura: "Hasura",
127+
kysely: "Kysely",
128+
"mikro-orm": "MikroORM",
129+
mongodb: "MongoDB",
130+
neo4j: "Neo4j",
131+
pg: "PostgreSQL",
132+
pouchdb: "PouchDB",
33133
prisma: "Prisma",
134+
sequelize: "Sequelize",
135+
supabase: "Supabase",
136+
surrealdb: "SurrealDB",
137+
typeorm: "TypeORM",
138+
unstorage: "Unstorage",
139+
"upstash-redis": "Upstash Redis",
140+
xata: "Xata",
34141
}

lib/write-env.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// @ts-check
2+
3+
import * as y from "yoctocolors"
4+
import { readFile, writeFile } from "node:fs/promises"
5+
import prompt from "prompts"
6+
import { join } from "node:path"
7+
import { frameworks } from "./meta.js"
8+
import { detectFramework } from "./detect.js"
9+
10+
/**
11+
* Update a key-value pair to a .env file
12+
* @param {string|undefined} envPath
13+
* @param {string} key
14+
* @param {string} value
15+
*/
16+
export async function updateEnvFile(envPath = "", key, value) {
17+
const framework = await detectFramework(envPath)
18+
const dotEnvFile = frameworks[framework]?.envFile
19+
let content = ""
20+
const line = `${key}="${value}" # Added by \`npx auth\`. Read more: https://cli.authjs.dev`
21+
const file = join(process.cwd(), envPath, dotEnvFile)
22+
try {
23+
content = await readFile(file, "utf-8")
24+
if (!content.includes(`${key}=`)) {
25+
console.log(`➕ Added ${key} to ${y.italic(file)}.`)
26+
content = `${line}\n${content}`
27+
} else {
28+
const { overwrite } = await prompt({
29+
type: "confirm",
30+
name: "overwrite",
31+
message: `Overwrite existing ${key}?`,
32+
initial: false,
33+
})
34+
if (!overwrite) return
35+
console.log(`✨ Updated ${key} in ${y.italic(file)}.`)
36+
content = content.replace(new RegExp(`${key}=(.*)`), `${line}`)
37+
}
38+
} catch (error) {
39+
if (error.code === "ENOENT") {
40+
console.log(`📝 Created ${y.italic(file)} with ${key}.`)
41+
content = line
42+
} else {
43+
throw error
44+
}
45+
}
46+
if (content) await writeFile(file, content)
47+
}

0 commit comments

Comments
 (0)