create-lightning-scaffold 1.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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +335 -0
  5. package/package.json +55 -0
  6. package/templates/backend/firebase/index.ts +25 -0
  7. package/templates/backend/firebase/package.json.ejs +8 -0
  8. package/templates/backend/supabase/index.ts +16 -0
  9. package/templates/backend/supabase/package.json.ejs +8 -0
  10. package/templates/base/.env.example.ejs +18 -0
  11. package/templates/components/nativewind-ui/button.tsx +25 -0
  12. package/templates/components/nativewind-ui/card.tsx +17 -0
  13. package/templates/components/nativewind-ui/index.ts +2 -0
  14. package/templates/components/shadcn/button.tsx +15 -0
  15. package/templates/components/shadcn/card.tsx +19 -0
  16. package/templates/components/shadcn/index.ts +2 -0
  17. package/templates/examples/mobile/biometric-onboard.tsx +104 -0
  18. package/templates/examples/mobile/gasless-transfer.tsx +72 -0
  19. package/templates/examples/mobile/index.tsx +30 -0
  20. package/templates/examples/mobile/passkey-login.tsx +55 -0
  21. package/templates/examples/web/biometric-onboard/page.tsx +70 -0
  22. package/templates/examples/web/gasless-transfer/page.tsx +85 -0
  23. package/templates/examples/web/page.tsx +27 -0
  24. package/templates/examples/web/passkey-login/page.tsx +50 -0
  25. package/templates/lib/lazorkit/mobile/index.ts +53 -0
  26. package/templates/lib/lazorkit/web/index.ts +67 -0
  27. package/templates/mobile/.vscode/extensions.json +1 -0
  28. package/templates/mobile/.vscode/settings.json +7 -0
  29. package/templates/mobile/app/(tabs)/_layout.tsx +59 -0
  30. package/templates/mobile/app/(tabs)/index.tsx +31 -0
  31. package/templates/mobile/app/(tabs)/two.tsx +31 -0
  32. package/templates/mobile/app/+html.tsx +38 -0
  33. package/templates/mobile/app/+not-found.tsx +40 -0
  34. package/templates/mobile/app/_layout.tsx +59 -0
  35. package/templates/mobile/app/modal.tsx +35 -0
  36. package/templates/mobile/app.json.ejs +38 -0
  37. package/templates/mobile/assets/fonts/SpaceMono-Regular.ttf +0 -0
  38. package/templates/mobile/assets/images/adaptive-icon.png +0 -0
  39. package/templates/mobile/assets/images/favicon.png +0 -0
  40. package/templates/mobile/assets/images/icon.png +0 -0
  41. package/templates/mobile/assets/images/splash-icon.png +0 -0
  42. package/templates/mobile/components/EditScreenInfo.tsx +77 -0
  43. package/templates/mobile/components/ExternalLink.tsx +25 -0
  44. package/templates/mobile/components/StyledText.tsx +5 -0
  45. package/templates/mobile/components/Themed.tsx +45 -0
  46. package/templates/mobile/components/__tests__/StyledText-test.js +10 -0
  47. package/templates/mobile/components/useClientOnlyValue.ts +4 -0
  48. package/templates/mobile/components/useClientOnlyValue.web.ts +12 -0
  49. package/templates/mobile/components/useColorScheme.ts +1 -0
  50. package/templates/mobile/components/useColorScheme.web.ts +8 -0
  51. package/templates/mobile/constants/Colors.ts +19 -0
  52. package/templates/mobile/lib/lazorkit/index.ts +53 -0
  53. package/templates/mobile/package.json.ejs +40 -0
  54. package/templates/mobile/tsconfig.json +17 -0
  55. package/templates/state/redux/index.ts +30 -0
  56. package/templates/state/zustand/index.ts +16 -0
  57. package/templates/styling/nativewind/global.css +3 -0
  58. package/templates/styling/nativewind/tailwind.config.js +7 -0
  59. package/templates/web/README.md +36 -0
  60. package/templates/web/app/favicon.ico +0 -0
  61. package/templates/web/app/globals.css +26 -0
  62. package/templates/web/app/layout.tsx.ejs +32 -0
  63. package/templates/web/app/page.tsx +65 -0
  64. package/templates/web/eslint.config.mjs +18 -0
  65. package/templates/web/lib/lazorkit/index.ts +67 -0
  66. package/templates/web/next.config.ts +7 -0
  67. package/templates/web/package.json.ejs +28 -0
  68. package/templates/web/postcss.config.mjs +7 -0
  69. package/templates/web/public/file.svg +1 -0
  70. package/templates/web/public/globe.svg +1 -0
  71. package/templates/web/public/next.svg +1 -0
  72. package/templates/web/public/vercel.svg +1 -0
  73. package/templates/web/public/window.svg +1 -0
  74. package/templates/web/tailwind.config.ts +14 -0
  75. package/templates/web/tsconfig.json +34 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 create-lightning-scaffold
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,65 @@
1
+ # create-lightning-scaffold
2
+
3
+ CLI to scaffold projects with LazorKit SDK integration. Generate React Native (Expo) or Next.js projects with passkey authentication, gasless transactions, and biometric onboarding built-in.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx create-lightning-scaffold
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - 🚀 **5 Presets**: Mobile App, Web App, Full-Stack Mobile, Full-Stack Web, Monorepo
14
+ - 🔐 **LazorKit SDK**: Passkey auth, gasless transactions, smart wallets
15
+ - 📱 **React Native + Expo**: Official `create-expo-app` under the hood
16
+ - 🌐 **Next.js**: Official `create-next-app` under the hood
17
+ - 🎨 **Styling**: TailwindCSS (web) / NativeWind (mobile)
18
+ - 📦 **State**: Zustand or Redux Toolkit
19
+ - 🗄️ **Backend**: Supabase or Firebase integration
20
+ - 📦 **Package Managers**: npm, pnpm, yarn, bun
21
+
22
+ ## Included Examples
23
+
24
+ Every generated project includes 3 working LazorKit examples:
25
+
26
+ 1. **Passkey Login** - WebAuthn-based authentication with smart wallet
27
+ 2. **Gasless Transfer** - Send SOL without paying gas fees
28
+ 3. **Biometric Onboarding** - Mobile-first onboarding with FaceID/TouchID
29
+
30
+ ## Usage
31
+
32
+ ```bash
33
+ # Interactive mode
34
+ npx create-lightning-scaffold
35
+
36
+ # You'll be prompted for:
37
+ # - Project name
38
+ # - Preset (Mobile, Web, Full-Stack, Monorepo)
39
+ # - Customization options (styling, state, components, backend)
40
+ # - Package manager
41
+ ```
42
+
43
+ ## Presets
44
+
45
+ | Preset | Description | Structure |
46
+ |--------|-------------|-----------|
47
+ | Mobile App | React Native + Expo | Flat |
48
+ | Web App | Next.js | Flat |
49
+ | Full-Stack Mobile | React Native + Backend | Monorepo |
50
+ | Full-Stack Web | Next.js + Backend | Monorepo |
51
+ | Monorepo | Mobile + Web + Backend | Monorepo |
52
+
53
+ ## Configuration
54
+
55
+ After scaffolding, copy `.env.example` to `.env` and add your LazorKit API key:
56
+
57
+ ```bash
58
+ cp .env.example .env
59
+ ```
60
+
61
+ Get your API key from [portal.lazor.sh](https://portal.lazor.sh).
62
+
63
+ ## License
64
+
65
+ MIT
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { program } from "commander";
5
+ import * as p2 from "@clack/prompts";
6
+ import pc2 from "picocolors";
7
+ import gradient from "gradient-string";
8
+ import figlet from "figlet";
9
+
10
+ // src/cli.ts
11
+ import * as p from "@clack/prompts";
12
+ import pc from "picocolors";
13
+
14
+ // src/utils/types.ts
15
+ var PRESET_INFO = {
16
+ mobile: { label: "Mobile App", hint: "React Native + Expo", platforms: ["mobile"] },
17
+ web: { label: "Web App", hint: "Next.js", platforms: ["web"] },
18
+ "fullstack-mobile": { label: "Full-Stack Mobile", hint: "React Native + Backend", platforms: ["mobile"] },
19
+ "fullstack-web": { label: "Full-Stack Web", hint: "Next.js + Backend", platforms: ["web"] },
20
+ monorepo: { label: "Monorepo", hint: "Mobile + Web + Backend", platforms: ["mobile", "web"] }
21
+ };
22
+ function getCompatibleOptions(preset) {
23
+ const info = PRESET_INFO[preset];
24
+ const hasMobile = info.platforms.includes("mobile");
25
+ const hasWeb = info.platforms.includes("web");
26
+ const hasBackend = preset.includes("fullstack") || preset === "monorepo";
27
+ return {
28
+ styling: hasMobile ? ["nativewind", "none"] : ["tailwind", "none"],
29
+ components: hasMobile ? ["nativewind-ui", "none"] : ["shadcn", "none"],
30
+ backend: hasBackend ? ["supabase", "firebase"] : ["none"],
31
+ state: ["zustand", "redux"]
32
+ };
33
+ }
34
+ function isMonorepoPreset(preset) {
35
+ return preset === "fullstack-mobile" || preset === "fullstack-web" || preset === "monorepo";
36
+ }
37
+
38
+ // src/utils/helpers.ts
39
+ import fs from "fs-extra";
40
+ import path from "path";
41
+ import { fileURLToPath } from "url";
42
+ function validateProjectName(name) {
43
+ if (!name) return "Project name is required";
44
+ if (!/^[a-z0-9-_]+$/i.test(name)) return "Only letters, numbers, hyphens, and underscores allowed";
45
+ if (fs.existsSync(name)) return `Directory "${name}" already exists`;
46
+ return void 0;
47
+ }
48
+ function getTemplatesDir() {
49
+ const __dirname2 = path.dirname(fileURLToPath(import.meta.url));
50
+ return path.join(__dirname2, "..", "templates");
51
+ }
52
+
53
+ // src/cli.ts
54
+ async function runPrompts() {
55
+ p.intro(pc.bgCyan(pc.black(" create-lightning-scaffold ")));
56
+ const name = await p.text({
57
+ message: "Project name:",
58
+ placeholder: "my-app",
59
+ validate: validateProjectName
60
+ });
61
+ if (p.isCancel(name)) return null;
62
+ const preset = await p.select({
63
+ message: "Select a preset:",
64
+ options: Object.entries(PRESET_INFO).map(([value, { label, hint }]) => ({
65
+ value,
66
+ label,
67
+ hint
68
+ }))
69
+ });
70
+ if (p.isCancel(preset)) return null;
71
+ const options = getCompatibleOptions(preset);
72
+ const customize = await p.confirm({
73
+ message: "Customize options?",
74
+ initialValue: false
75
+ });
76
+ if (p.isCancel(customize)) return null;
77
+ let styling = options.styling[0];
78
+ let components = options.components[0];
79
+ let state = "zustand";
80
+ let backend = options.backend[0];
81
+ if (customize) {
82
+ const stylingChoice = await p.select({
83
+ message: "Styling:",
84
+ options: options.styling.map((v) => ({ value: v, label: v === "none" ? "None" : v }))
85
+ });
86
+ if (p.isCancel(stylingChoice)) return null;
87
+ styling = stylingChoice;
88
+ const stateChoice = await p.select({
89
+ message: "State management:",
90
+ options: [
91
+ { value: "zustand", label: "Zustand" },
92
+ { value: "redux", label: "Redux Toolkit" }
93
+ ]
94
+ });
95
+ if (p.isCancel(stateChoice)) return null;
96
+ state = stateChoice;
97
+ const componentsChoice = await p.select({
98
+ message: "Component library:",
99
+ options: options.components.map((v) => ({ value: v, label: v === "none" ? "None" : v }))
100
+ });
101
+ if (p.isCancel(componentsChoice)) return null;
102
+ components = componentsChoice;
103
+ if (options.backend[0] !== "none") {
104
+ const backendChoice = await p.select({
105
+ message: "Backend:",
106
+ options: [
107
+ { value: "supabase", label: "Supabase" },
108
+ { value: "firebase", label: "Firebase" }
109
+ ]
110
+ });
111
+ if (p.isCancel(backendChoice)) return null;
112
+ backend = backendChoice;
113
+ }
114
+ }
115
+ const packageManager = await p.select({
116
+ message: "Package manager:",
117
+ options: [
118
+ { value: "npm", label: "npm" },
119
+ { value: "pnpm", label: "pnpm" },
120
+ { value: "yarn", label: "yarn" },
121
+ { value: "bun", label: "bun" }
122
+ ]
123
+ });
124
+ if (p.isCancel(packageManager)) return null;
125
+ const gitInit = await p.confirm({
126
+ message: "Initialize git repository?",
127
+ initialValue: true
128
+ });
129
+ if (p.isCancel(gitInit)) return null;
130
+ return {
131
+ name,
132
+ preset,
133
+ backend,
134
+ styling,
135
+ state,
136
+ components,
137
+ packageManager,
138
+ gitInit
139
+ };
140
+ }
141
+
142
+ // src/scaffolder.ts
143
+ import fs2 from "fs-extra";
144
+ import path2 from "path";
145
+ import ejs from "ejs";
146
+ async function scaffold(config) {
147
+ const targetDir = path2.resolve(process.cwd(), config.name);
148
+ const templatesDir = getTemplatesDir();
149
+ const isMonorepo = isMonorepoPreset(config.preset);
150
+ const platforms = PRESET_INFO[config.preset].platforms;
151
+ await fs2.ensureDir(targetDir);
152
+ if (isMonorepo) {
153
+ await setupMonorepo(targetDir, config);
154
+ if (platforms.includes("mobile")) {
155
+ await copyTemplate(path2.join(templatesDir, "mobile"), path2.join(targetDir, "apps/mobile"), config);
156
+ }
157
+ if (platforms.includes("web")) {
158
+ await copyTemplate(path2.join(templatesDir, "web"), path2.join(targetDir, "apps/web"), config);
159
+ }
160
+ if (config.backend !== "none") {
161
+ await fs2.ensureDir(path2.join(targetDir, "packages/backend"));
162
+ await copyTemplate(path2.join(templatesDir, "backend", config.backend), path2.join(targetDir, "packages/backend"), config);
163
+ }
164
+ } else {
165
+ const platformTemplate = platforms[0] === "mobile" ? "mobile" : "web";
166
+ await copyTemplate(path2.join(templatesDir, platformTemplate), targetDir, config);
167
+ }
168
+ const appDir = isMonorepo ? path2.join(targetDir, platforms[0] === "mobile" ? "apps/mobile" : "apps/web") : targetDir;
169
+ await addStyling(appDir, config);
170
+ await addStateManagement(appDir, config);
171
+ await addComponents(appDir, config);
172
+ await addExamples(appDir, platforms[0], config);
173
+ await copyTemplate(path2.join(templatesDir, "base"), targetDir, config);
174
+ return targetDir;
175
+ }
176
+ async function setupMonorepo(targetDir, config) {
177
+ const pkg = {
178
+ name: config.name,
179
+ private: true,
180
+ scripts: {
181
+ dev: "echo 'Run dev in apps/*'",
182
+ build: "echo 'Run build in apps/*'"
183
+ }
184
+ };
185
+ if (config.packageManager === "pnpm") {
186
+ await fs2.writeFile(path2.join(targetDir, "pnpm-workspace.yaml"), "packages:\n - 'apps/*'\n - 'packages/*'\n");
187
+ } else {
188
+ pkg.workspaces = ["apps/*", "packages/*"];
189
+ }
190
+ await fs2.writeJson(path2.join(targetDir, "package.json"), pkg, { spaces: 2 });
191
+ await fs2.ensureDir(path2.join(targetDir, "apps"));
192
+ await fs2.ensureDir(path2.join(targetDir, "packages"));
193
+ }
194
+ async function addStyling(appDir, config) {
195
+ if (config.styling === "none") return;
196
+ const pkgPath = path2.join(appDir, "package.json");
197
+ if (!await fs2.pathExists(pkgPath)) return;
198
+ const pkg = await fs2.readJson(pkgPath);
199
+ pkg.dependencies = pkg.dependencies || {};
200
+ pkg.devDependencies = pkg.devDependencies || {};
201
+ if (config.styling === "nativewind") {
202
+ pkg.dependencies["nativewind"] = "^4.0.0";
203
+ pkg.devDependencies["tailwindcss"] = "^3.4.0";
204
+ const templatesDir = getTemplatesDir();
205
+ await copyTemplate(path2.join(templatesDir, "styling", "nativewind"), appDir, config);
206
+ }
207
+ await fs2.writeJson(pkgPath, pkg, { spaces: 2 });
208
+ }
209
+ async function addStateManagement(appDir, config) {
210
+ const pkgPath = path2.join(appDir, "package.json");
211
+ if (!await fs2.pathExists(pkgPath)) return;
212
+ const pkg = await fs2.readJson(pkgPath);
213
+ pkg.dependencies = pkg.dependencies || {};
214
+ if (config.state === "zustand") {
215
+ pkg.dependencies["zustand"] = "^4.5.0";
216
+ } else {
217
+ pkg.dependencies["@reduxjs/toolkit"] = "^2.0.0";
218
+ pkg.dependencies["react-redux"] = "^9.0.0";
219
+ }
220
+ await fs2.writeJson(pkgPath, pkg, { spaces: 2 });
221
+ const templatesDir = getTemplatesDir();
222
+ await copyTemplate(path2.join(templatesDir, "state", config.state), path2.join(appDir, "lib/store"), config);
223
+ }
224
+ async function addComponents(appDir, config) {
225
+ if (config.components === "none") return;
226
+ const templatesDir = getTemplatesDir();
227
+ await copyTemplate(path2.join(templatesDir, "components", config.components), path2.join(appDir, "components/ui"), config);
228
+ }
229
+ async function addExamples(appDir, platform, config) {
230
+ const templatesDir = getTemplatesDir();
231
+ const examplesDir = path2.join(templatesDir, "examples", platform);
232
+ const targetDir = platform === "mobile" ? path2.join(appDir, "app") : path2.join(appDir, "app/examples");
233
+ await copyTemplate(examplesDir, targetDir, config);
234
+ }
235
+ async function copyTemplate(src, dest, config) {
236
+ if (!await fs2.pathExists(src)) return;
237
+ await fs2.ensureDir(dest);
238
+ const files = await fs2.readdir(src, { withFileTypes: true });
239
+ for (const file of files) {
240
+ const srcPath = path2.join(src, file.name);
241
+ const destName = file.name.replace(/\.ejs$/, "");
242
+ const destPath = path2.join(dest, destName);
243
+ if (file.isDirectory()) {
244
+ await copyTemplate(srcPath, destPath, config);
245
+ } else if (file.name.endsWith(".ejs")) {
246
+ const content = await fs2.readFile(srcPath, "utf-8");
247
+ const rendered = ejs.render(content, config);
248
+ await fs2.writeFile(destPath, rendered);
249
+ } else {
250
+ await fs2.copy(srcPath, destPath);
251
+ }
252
+ }
253
+ }
254
+
255
+ // src/installer.ts
256
+ import { spawn } from "child_process";
257
+ function installDependencies(cwd, pm) {
258
+ return new Promise((resolve, reject) => {
259
+ const cmd = pm === "npm" ? "npm" : pm;
260
+ const args = pm === "yarn" ? [] : ["install"];
261
+ const child = spawn(cmd, args, { cwd, stdio: "inherit", shell: true });
262
+ child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`Install failed with code ${code}`)));
263
+ child.on("error", reject);
264
+ });
265
+ }
266
+ function initGit(cwd) {
267
+ return new Promise((resolve, reject) => {
268
+ const child = spawn("git", ["init"], { cwd, stdio: "ignore", shell: true });
269
+ child.on("close", (code) => code === 0 ? resolve() : reject(new Error("Git init failed")));
270
+ child.on("error", reject);
271
+ });
272
+ }
273
+
274
+ // src/index.ts
275
+ var showBanner = () => {
276
+ const art = figlet.textSync("Lightning Scaffold", { font: "Slant" });
277
+ console.log(gradient.cristal.multiline(art));
278
+ console.log();
279
+ };
280
+ program.name("create-lightning-scaffold").description("Scaffold projects with LazorKit SDK integration").version("1.0.0").option("-y, --yes", "Use defaults").option("-n, --name <name>", "Project name").option("-p, --preset <preset>", "Preset: mobile, web, fullstack-mobile, fullstack-web, monorepo").option("--skip-install", "Skip dependency installation").action(async (opts) => {
281
+ showBanner();
282
+ let config;
283
+ if (opts.yes || opts.preset) {
284
+ const name = opts.name || "my-lightning-app";
285
+ const preset = opts.preset || "mobile";
286
+ const isMobile = preset === "mobile" || preset === "fullstack-mobile";
287
+ const hasBackend = preset.includes("fullstack") || preset === "monorepo";
288
+ config = {
289
+ name,
290
+ preset,
291
+ backend: hasBackend ? "supabase" : "none",
292
+ styling: isMobile ? "nativewind" : "tailwind",
293
+ state: "zustand",
294
+ components: isMobile ? "nativewind-ui" : "shadcn",
295
+ packageManager: "npm",
296
+ gitInit: true
297
+ };
298
+ p2.intro(pc2.bgCyan(pc2.black(" create-lightning-scaffold ")));
299
+ p2.log.info(`Creating: ${name} (${preset})`);
300
+ } else {
301
+ config = await runPrompts();
302
+ }
303
+ if (!config) {
304
+ p2.cancel("Operation cancelled");
305
+ process.exit(0);
306
+ }
307
+ const s = p2.spinner();
308
+ s.start("Scaffolding project...");
309
+ try {
310
+ const targetDir = await scaffold(config);
311
+ s.stop("Project scaffolded!");
312
+ if (!opts.skipInstall) {
313
+ s.start("Installing dependencies...");
314
+ await installDependencies(targetDir, config.packageManager);
315
+ s.stop("Dependencies installed!");
316
+ }
317
+ if (config.gitInit) {
318
+ s.start("Initializing git...");
319
+ await initGit(targetDir);
320
+ s.stop("Git initialized!");
321
+ }
322
+ p2.outro(pc2.green("\u2713 Project created successfully!"));
323
+ console.log(`
324
+ ${pc2.bold("Next steps:")}`);
325
+ console.log(` cd ${config.name}`);
326
+ console.log(` ${config.packageManager === "npm" ? "npm run" : config.packageManager} dev
327
+ `);
328
+ console.log(pc2.dim("LazorKit examples included: passkey-login, gasless-transfer, biometric-onboard"));
329
+ } catch (err) {
330
+ s.stop("Failed");
331
+ p2.cancel(`Error: ${err instanceof Error ? err.message : err}`);
332
+ process.exit(1);
333
+ }
334
+ });
335
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "create-lightning-scaffold",
3
+ "version": "1.0.0",
4
+ "description": "CLI to scaffold projects with LazorKit SDK integration",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-lightning-scaffold": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "templates"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup",
15
+ "dev": "tsup --watch",
16
+ "start": "node dist/index.js",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "dependencies": {
20
+ "@clack/prompts": "^0.7.0",
21
+ "commander": "^12.0.0",
22
+ "ejs": "^3.1.10",
23
+ "figlet": "^1.9.4",
24
+ "fs-extra": "^11.2.0",
25
+ "gradient-string": "^3.0.0",
26
+ "picocolors": "^1.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/ejs": "^3.1.5",
30
+ "@types/figlet": "^1.7.0",
31
+ "@types/fs-extra": "^11.0.4",
32
+ "@types/gradient-string": "^1.1.6",
33
+ "@types/node": "^20.0.0",
34
+ "tsup": "^8.0.0",
35
+ "typescript": "^5.0.0"
36
+ },
37
+ "engines": {
38
+ "node": ">=18.0.0"
39
+ },
40
+ "keywords": [
41
+ "cli",
42
+ "scaffold",
43
+ "lazorkit",
44
+ "react-native",
45
+ "nextjs",
46
+ "expo"
47
+ ],
48
+ "license": "MIT",
49
+ "author": "tobi-techy",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/tobi-techy/LIGHTING-SCAFFOLD.git"
53
+ },
54
+ "homepage": "https://github.com/tobi-techy/LIGHTING-SCAFFOLD#readme"
55
+ }
@@ -0,0 +1,25 @@
1
+ import { initializeApp } from "firebase/app";
2
+ import { getAuth } from "firebase/auth";
3
+ import { getFirestore, doc, setDoc, collection, query, where, getDocs, orderBy } from "firebase/firestore";
4
+
5
+ const firebaseConfig = {
6
+ apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY || process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
7
+ authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN || process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
8
+ projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID || process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
9
+ };
10
+
11
+ const app = initializeApp(firebaseConfig);
12
+ export const auth = getAuth(app);
13
+ export const db = getFirestore(app);
14
+
15
+ // Example: Save wallet to user profile
16
+ export async function saveWallet(userId: string, walletAddress: string) {
17
+ return setDoc(doc(db, "wallets", userId), { address: walletAddress, updatedAt: new Date() });
18
+ }
19
+
20
+ // Example: Get user's transactions
21
+ export async function getTransactions(walletAddress: string) {
22
+ const q = query(collection(db, "transactions"), where("walletAddress", "==", walletAddress), orderBy("createdAt", "desc"));
23
+ const snapshot = await getDocs(q);
24
+ return snapshot.docs.map((d) => ({ id: d.id, ...d.data() }));
25
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@<%= name %>/backend",
3
+ "version": "1.0.0",
4
+ "main": "index.ts",
5
+ "dependencies": {
6
+ "firebase": "^10.7.0"
7
+ }
8
+ }
@@ -0,0 +1,16 @@
1
+ import { createClient } from "@supabase/supabase-js";
2
+
3
+ const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL || "";
4
+ const supabaseKey = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
5
+
6
+ export const supabase = createClient(supabaseUrl, supabaseKey);
7
+
8
+ // Example: Save wallet to user profile
9
+ export async function saveWallet(userId: string, walletAddress: string) {
10
+ return supabase.from("wallets").upsert({ user_id: userId, address: walletAddress });
11
+ }
12
+
13
+ // Example: Get user's transactions
14
+ export async function getTransactions(walletAddress: string) {
15
+ return supabase.from("transactions").select("*").eq("wallet_address", walletAddress).order("created_at", { ascending: false });
16
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@<%= name %>/backend",
3
+ "version": "1.0.0",
4
+ "main": "index.ts",
5
+ "dependencies": {
6
+ "@supabase/supabase-js": "^2.39.0"
7
+ }
8
+ }
@@ -0,0 +1,18 @@
1
+ # LazorKit Configuration
2
+ LAZORKIT_API_KEY=your_api_key_here
3
+ LAZORKIT_PORTAL_URL=https://portal.lazor.sh
4
+ LAZORKIT_PAYMASTER_URL=https://kora.devnet.lazorkit.com
5
+
6
+ # Solana
7
+ SOLANA_RPC=https://api.devnet.solana.com
8
+ SOLANA_NETWORK=devnet
9
+ <% if (backend === 'supabase') { %>
10
+ # Supabase
11
+ SUPABASE_URL=your_supabase_url
12
+ SUPABASE_ANON_KEY=your_supabase_anon_key
13
+ <% } %><% if (backend === 'firebase') { %>
14
+ # Firebase
15
+ FIREBASE_API_KEY=your_firebase_api_key
16
+ FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com
17
+ FIREBASE_PROJECT_ID=your_project_id
18
+ <% } %>
@@ -0,0 +1,25 @@
1
+ import { Pressable, Text, PressableProps, TextStyle, ViewStyle } from "react-native";
2
+
3
+ interface ButtonProps extends PressableProps {
4
+ title: string;
5
+ variant?: "default" | "outline" | "ghost";
6
+ }
7
+
8
+ export function Button({ title, variant = "default", style, ...props }: ButtonProps) {
9
+ const baseStyle: ViewStyle = { paddingHorizontal: 16, paddingVertical: 12, borderRadius: 8, alignItems: "center" };
10
+ const variants: Record<string, ViewStyle> = {
11
+ default: { backgroundColor: "#6366f1" },
12
+ outline: { borderWidth: 1, borderColor: "#6366f1" },
13
+ ghost: {},
14
+ };
15
+ const textVariants: Record<string, TextStyle> = {
16
+ default: { color: "#fff" },
17
+ outline: { color: "#6366f1" },
18
+ ghost: { color: "#6366f1" },
19
+ };
20
+ return (
21
+ <Pressable style={[baseStyle, variants[variant], style as ViewStyle]} {...props}>
22
+ <Text style={[{ fontWeight: "600" }, textVariants[variant]]}>{title}</Text>
23
+ </Pressable>
24
+ );
25
+ }
@@ -0,0 +1,17 @@
1
+ import { View, Text, ViewProps, TextProps } from "react-native";
2
+
3
+ export function Card({ style, ...props }: ViewProps) {
4
+ return <View style={[{ borderRadius: 12, backgroundColor: "#fff", shadowColor: "#000", shadowOpacity: 0.1, shadowRadius: 4, padding: 16 }, style]} {...props} />;
5
+ }
6
+
7
+ export function CardHeader({ style, ...props }: ViewProps) {
8
+ return <View style={[{ marginBottom: 12 }, style]} {...props} />;
9
+ }
10
+
11
+ export function CardTitle({ style, ...props }: TextProps) {
12
+ return <Text style={[{ fontSize: 20, fontWeight: "600" }, style]} {...props} />;
13
+ }
14
+
15
+ export function CardContent(props: ViewProps) {
16
+ return <View {...props} />;
17
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./button";
2
+ export * from "./card";
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+
3
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
4
+ variant?: "default" | "outline" | "ghost";
5
+ }
6
+
7
+ export function Button({ className = "", variant = "default", ...props }: ButtonProps) {
8
+ const base = "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none px-4 py-2";
9
+ const variants = {
10
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
11
+ outline: "border border-input hover:bg-accent hover:text-accent-foreground",
12
+ ghost: "hover:bg-accent hover:text-accent-foreground",
13
+ };
14
+ return <button className={`${base} ${variants[variant]} ${className}`} {...props} />;
15
+ }
@@ -0,0 +1,19 @@
1
+ import * as React from "react";
2
+
3
+ interface CardProps extends React.HTMLAttributes<HTMLDivElement> {}
4
+
5
+ export function Card({ className = "", ...props }: CardProps) {
6
+ return <div className={`rounded-lg border bg-card text-card-foreground shadow-sm ${className}`} {...props} />;
7
+ }
8
+
9
+ export function CardHeader({ className = "", ...props }: CardProps) {
10
+ return <div className={`flex flex-col space-y-1.5 p-6 ${className}`} {...props} />;
11
+ }
12
+
13
+ export function CardTitle({ className = "", ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
14
+ return <h3 className={`text-2xl font-semibold leading-none tracking-tight ${className}`} {...props} />;
15
+ }
16
+
17
+ export function CardContent({ className = "", ...props }: CardProps) {
18
+ return <div className={`p-6 pt-0 ${className}`} {...props} />;
19
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./button";
2
+ export * from "./card";