tools-template-cli 0.1.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/LICENSE +21 -0
- package/README.md +54 -0
- package/dist/index.js +380 -0
- package/dist/templates.js +44 -0
- package/package.json +49 -0
- package/supabase-expo-app/.env.example +2 -0
- package/supabase-expo-app/App.tsx +55 -0
- package/supabase-expo-app/README.md +179 -0
- package/supabase-expo-app/app.json +34 -0
- package/supabase-expo-app/assets/adaptive-icon.png +0 -0
- package/supabase-expo-app/assets/favicon.png +0 -0
- package/supabase-expo-app/assets/icon.png +0 -0
- package/supabase-expo-app/assets/splash-icon.png +0 -0
- package/supabase-expo-app/components/Account.tsx +234 -0
- package/supabase-expo-app/components/Auth.tsx +161 -0
- package/supabase-expo-app/components/Avatar.tsx +173 -0
- package/supabase-expo-app/index.ts +8 -0
- package/supabase-expo-app/lib/supabase.ts +63 -0
- package/supabase-expo-app/package-lock.json +8983 -0
- package/supabase-expo-app/package.json +32 -0
- package/supabase-expo-app/scripts/setup-supabase.sh +73 -0
- package/supabase-expo-app/supabase/schema.sql +57 -0
- package/supabase-expo-app/tsconfig.json +6 -0
- package/supabase-swiftui-app/Package.swift +32 -0
- package/supabase-swiftui-app/README.md +215 -0
- package/supabase-swiftui-app/SupabaseSwiftUIApp/AccountView.swift +181 -0
- package/supabase-swiftui-app/SupabaseSwiftUIApp/AuthView.swift +123 -0
- package/supabase-swiftui-app/SupabaseSwiftUIApp/AvatarView.swift +113 -0
- package/supabase-swiftui-app/SupabaseSwiftUIApp/ContentView.swift +18 -0
- package/supabase-swiftui-app/SupabaseSwiftUIApp/Supabase.swift +13 -0
- package/supabase-swiftui-app/SupabaseSwiftUIApp/SupabaseSwiftUIApp.swift +22 -0
- package/supabase-swiftui-app/scripts/setup-supabase.sh +67 -0
- package/supabase-swiftui-app/supabase/schema.sql +57 -0
- package/supabase-user-management/AGENTS.md +5 -0
- package/supabase-user-management/CLAUDE.md +1 -0
- package/supabase-user-management/README.md +178 -0
- package/supabase-user-management/app/account/account-form.tsx +174 -0
- package/supabase-user-management/app/account/avatar.tsx +109 -0
- package/supabase-user-management/app/account/page.tsx +28 -0
- package/supabase-user-management/app/auth/confirm/route.ts +26 -0
- package/supabase-user-management/app/error.tsx +14 -0
- package/supabase-user-management/app/favicon.ico +0 -0
- package/supabase-user-management/app/globals.css +130 -0
- package/supabase-user-management/app/layout.tsx +22 -0
- package/supabase-user-management/app/loading.tsx +7 -0
- package/supabase-user-management/app/login/actions.ts +45 -0
- package/supabase-user-management/app/login/page.tsx +90 -0
- package/supabase-user-management/app/page.tsx +16 -0
- package/supabase-user-management/components/ui/button.tsx +58 -0
- package/supabase-user-management/components.json +25 -0
- package/supabase-user-management/eslint.config.mjs +18 -0
- package/supabase-user-management/lib/supabase/client.ts +8 -0
- package/supabase-user-management/lib/supabase/middleware.ts +52 -0
- package/supabase-user-management/lib/supabase/server.ts +29 -0
- package/supabase-user-management/lib/utils.ts +6 -0
- package/supabase-user-management/next.config.ts +7 -0
- package/supabase-user-management/package-lock.json +9910 -0
- package/supabase-user-management/package.json +36 -0
- package/supabase-user-management/postcss.config.mjs +7 -0
- package/supabase-user-management/public/file.svg +1 -0
- package/supabase-user-management/public/globe.svg +1 -0
- package/supabase-user-management/public/next.svg +1 -0
- package/supabase-user-management/public/vercel.svg +1 -0
- package/supabase-user-management/public/window.svg +1 -0
- package/supabase-user-management/scripts/setup-supabase.sh +98 -0
- package/supabase-user-management/src/proxy.ts +12 -0
- package/supabase-user-management/supabase/schema.sql +57 -0
- package/supabase-user-management/tsconfig.json +34 -0
- package/supabase_flutter_app/.metadata +45 -0
- package/supabase_flutter_app/README.md +195 -0
- package/supabase_flutter_app/analysis_options.yaml +28 -0
- package/supabase_flutter_app/android/app/build.gradle.kts +44 -0
- package/supabase_flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
- package/supabase_flutter_app/android/app/src/main/AndroidManifest.xml +54 -0
- package/supabase_flutter_app/android/app/src/main/kotlin/com/example/supabase_flutter_app/MainActivity.kt +5 -0
- package/supabase_flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
- package/supabase_flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
- package/supabase_flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/supabase_flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/supabase_flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/supabase_flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/supabase_flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/supabase_flutter_app/android/app/src/main/res/values/styles.xml +18 -0
- package/supabase_flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
- package/supabase_flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
- package/supabase_flutter_app/android/build.gradle.kts +24 -0
- package/supabase_flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
- package/supabase_flutter_app/android/gradle.properties +2 -0
- package/supabase_flutter_app/android/settings.gradle.kts +26 -0
- package/supabase_flutter_app/ios/Flutter/AppFrameworkInfo.plist +26 -0
- package/supabase_flutter_app/ios/Flutter/Debug.xcconfig +2 -0
- package/supabase_flutter_app/ios/Flutter/Release.xcconfig +2 -0
- package/supabase_flutter_app/ios/Podfile +43 -0
- package/supabase_flutter_app/ios/Runner/AppDelegate.swift +13 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +122 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +23 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
- package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +5 -0
- package/supabase_flutter_app/ios/Runner/Base.lproj/LaunchScreen.storyboard +37 -0
- package/supabase_flutter_app/ios/Runner/Base.lproj/Main.storyboard +26 -0
- package/supabase_flutter_app/ios/Runner/Info.plist +61 -0
- package/supabase_flutter_app/ios/Runner/Runner-Bridging-Header.h +1 -0
- package/supabase_flutter_app/ios/Runner.xcodeproj/project.pbxproj +619 -0
- package/supabase_flutter_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/supabase_flutter_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/supabase_flutter_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
- package/supabase_flutter_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +101 -0
- package/supabase_flutter_app/ios/Runner.xcworkspace/contents.xcworkspacedata +7 -0
- package/supabase_flutter_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/supabase_flutter_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
- package/supabase_flutter_app/ios/RunnerTests/RunnerTests.swift +12 -0
- package/supabase_flutter_app/lib/components/avatar.dart +153 -0
- package/supabase_flutter_app/lib/main.dart +70 -0
- package/supabase_flutter_app/lib/pages/account_page.dart +189 -0
- package/supabase_flutter_app/lib/pages/login_page.dart +150 -0
- package/supabase_flutter_app/linux/CMakeLists.txt +128 -0
- package/supabase_flutter_app/linux/flutter/CMakeLists.txt +88 -0
- package/supabase_flutter_app/linux/flutter/generated_plugin_registrant.cc +23 -0
- package/supabase_flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
- package/supabase_flutter_app/linux/flutter/generated_plugins.cmake +26 -0
- package/supabase_flutter_app/linux/runner/CMakeLists.txt +26 -0
- package/supabase_flutter_app/linux/runner/main.cc +6 -0
- package/supabase_flutter_app/linux/runner/my_application.cc +148 -0
- package/supabase_flutter_app/linux/runner/my_application.h +21 -0
- package/supabase_flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
- package/supabase_flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
- package/supabase_flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +18 -0
- package/supabase_flutter_app/macos/Podfile +42 -0
- package/supabase_flutter_app/macos/Runner/AppDelegate.swift +13 -0
- package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
- package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
- package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
- package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
- package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
- package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
- package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
- package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
- package/supabase_flutter_app/macos/Runner/Base.lproj/MainMenu.xib +343 -0
- package/supabase_flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
- package/supabase_flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
- package/supabase_flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
- package/supabase_flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
- package/supabase_flutter_app/macos/Runner/DebugProfile.entitlements +12 -0
- package/supabase_flutter_app/macos/Runner/Info.plist +32 -0
- package/supabase_flutter_app/macos/Runner/MainFlutterWindow.swift +15 -0
- package/supabase_flutter_app/macos/Runner/Release.entitlements +8 -0
- package/supabase_flutter_app/macos/Runner.xcodeproj/project.pbxproj +705 -0
- package/supabase_flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/supabase_flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
- package/supabase_flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +7 -0
- package/supabase_flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/supabase_flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
- package/supabase_flutter_app/pubspec.lock +818 -0
- package/supabase_flutter_app/pubspec.yaml +26 -0
- package/supabase_flutter_app/scripts/setup-supabase.sh +72 -0
- package/supabase_flutter_app/supabase/schema.sql +57 -0
- package/supabase_flutter_app/test/widget_test.dart +30 -0
- package/supabase_flutter_app/web/favicon.png +0 -0
- package/supabase_flutter_app/web/icons/Icon-192.png +0 -0
- package/supabase_flutter_app/web/icons/Icon-512.png +0 -0
- package/supabase_flutter_app/web/icons/Icon-maskable-192.png +0 -0
- package/supabase_flutter_app/web/icons/Icon-maskable-512.png +0 -0
- package/supabase_flutter_app/web/index.html +38 -0
- package/supabase_flutter_app/web/manifest.json +35 -0
- package/supabase_flutter_app/windows/CMakeLists.txt +108 -0
- package/supabase_flutter_app/windows/flutter/CMakeLists.txt +109 -0
- package/supabase_flutter_app/windows/flutter/generated_plugin_registrant.cc +20 -0
- package/supabase_flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
- package/supabase_flutter_app/windows/flutter/generated_plugins.cmake +26 -0
- package/supabase_flutter_app/windows/runner/CMakeLists.txt +40 -0
- package/supabase_flutter_app/windows/runner/Runner.rc +121 -0
- package/supabase_flutter_app/windows/runner/flutter_window.cpp +71 -0
- package/supabase_flutter_app/windows/runner/flutter_window.h +33 -0
- package/supabase_flutter_app/windows/runner/main.cpp +43 -0
- package/supabase_flutter_app/windows/runner/resource.h +16 -0
- package/supabase_flutter_app/windows/runner/resources/app_icon.ico +0 -0
- package/supabase_flutter_app/windows/runner/runner.exe.manifest +14 -0
- package/supabase_flutter_app/windows/runner/utils.cpp +65 -0
- package/supabase_flutter_app/windows/runner/utils.h +19 -0
- package/supabase_flutter_app/windows/runner/win32_window.cpp +288 -0
- package/supabase_flutter_app/windows/runner/win32_window.h +102 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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,54 @@
|
|
|
1
|
+
# tmp-cli
|
|
2
|
+
|
|
3
|
+
A small TypeScript CLI for cloning the starter templates in this repository into a fresh project directory.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
### From npm
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g tools-template-cli
|
|
11
|
+
tmp-cli list
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Locally while developing
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install
|
|
18
|
+
npm run build
|
|
19
|
+
npm link
|
|
20
|
+
tmp-cli list
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Commands
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install
|
|
27
|
+
npm run build
|
|
28
|
+
node dist/index.js list
|
|
29
|
+
node dist/index.js create supabase-expo-app my-mobile-app
|
|
30
|
+
node dist/index.js create ./apps/admin --template supabase-user-management --git
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## What it does
|
|
34
|
+
|
|
35
|
+
- Lists the templates that live in this repository
|
|
36
|
+
- Copies a template into a new target directory
|
|
37
|
+
- Skips `.git`, `node_modules`, `.DS_Store`, and common build output directories
|
|
38
|
+
- Renames basic config values for the JavaScript templates
|
|
39
|
+
- Optionally runs `npm install`, `pnpm install`, `yarn install`, or `bun install`
|
|
40
|
+
- Optionally initializes a fresh git repository in the generated project
|
|
41
|
+
|
|
42
|
+
## Global usage
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
tmp-cli list
|
|
46
|
+
tmp-cli create supabase-expo-app my-mobile-app
|
|
47
|
+
tmp-cli create ./apps/admin --template supabase-user-management --git
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Notes
|
|
51
|
+
|
|
52
|
+
- `supabase-expo-app` and `supabase-user-management` get automatic config renaming in `package.json`, `package-lock.json`, `app.json`, and `README.md`
|
|
53
|
+
- The SwiftUI and Flutter templates are copied as-is, apart from the target directory name
|
|
54
|
+
- Use `node dist/index.js help` to see all options
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { getTemplate, templates } from "./templates.js";
|
|
8
|
+
const CLI_VERSION = "0.1.0";
|
|
9
|
+
const ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
|
+
const SKIP_NAMES = new Set([
|
|
11
|
+
".git",
|
|
12
|
+
".DS_Store",
|
|
13
|
+
"node_modules",
|
|
14
|
+
".next",
|
|
15
|
+
".expo",
|
|
16
|
+
".turbo",
|
|
17
|
+
"dist",
|
|
18
|
+
"build"
|
|
19
|
+
]);
|
|
20
|
+
const VALID_PACKAGE_MANAGERS = new Set([
|
|
21
|
+
"npm",
|
|
22
|
+
"pnpm",
|
|
23
|
+
"yarn",
|
|
24
|
+
"bun"
|
|
25
|
+
]);
|
|
26
|
+
function main() {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
29
|
+
if (hasBooleanFlag(parsed.flags, "help") || parsed.command === "help") {
|
|
30
|
+
printHelp();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (hasBooleanFlag(parsed.flags, "version")) {
|
|
34
|
+
console.log(CLI_VERSION);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
switch (parsed.command) {
|
|
38
|
+
case "list":
|
|
39
|
+
listTemplates();
|
|
40
|
+
return;
|
|
41
|
+
case "create":
|
|
42
|
+
createProject(resolveCreateOptions(parsed));
|
|
43
|
+
return;
|
|
44
|
+
default:
|
|
45
|
+
printHelp(`Unknown command: ${parsed.command}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const message = error instanceof Error ? error.message : "Unknown CLI failure";
|
|
50
|
+
console.error(`Error: ${message}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function parseArgs(argv) {
|
|
55
|
+
const command = argv[0] ?? "help";
|
|
56
|
+
const positionals = [];
|
|
57
|
+
const flags = new Map();
|
|
58
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
59
|
+
const token = argv[index];
|
|
60
|
+
if (!token.startsWith("-")) {
|
|
61
|
+
positionals.push(token);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (token === "--") {
|
|
65
|
+
positionals.push(...argv.slice(index + 1));
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
if (token.startsWith("--")) {
|
|
69
|
+
const [rawName, inlineValue] = token.slice(2).split("=", 2);
|
|
70
|
+
const name = normalizeFlagName(rawName);
|
|
71
|
+
if (inlineValue !== undefined) {
|
|
72
|
+
flags.set(name, inlineValue);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (flagTakesValue(name)) {
|
|
76
|
+
const next = argv[index + 1];
|
|
77
|
+
if (!next || next.startsWith("-")) {
|
|
78
|
+
throw new Error(`Missing value for --${rawName}`);
|
|
79
|
+
}
|
|
80
|
+
flags.set(name, next);
|
|
81
|
+
index += 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
flags.set(name, true);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const shortFlags = token.slice(1).split("");
|
|
88
|
+
for (let shortIndex = 0; shortIndex < shortFlags.length; shortIndex += 1) {
|
|
89
|
+
const shortFlag = shortFlags[shortIndex];
|
|
90
|
+
const name = mapShortFlag(shortFlag);
|
|
91
|
+
if (flagTakesValue(name)) {
|
|
92
|
+
const remainder = shortFlags.slice(shortIndex + 1).join("");
|
|
93
|
+
if (remainder.length > 0) {
|
|
94
|
+
flags.set(name, remainder);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
const next = argv[index + 1];
|
|
98
|
+
if (!next || next.startsWith("-")) {
|
|
99
|
+
throw new Error(`Missing value for -${shortFlag}`);
|
|
100
|
+
}
|
|
101
|
+
flags.set(name, next);
|
|
102
|
+
index += 1;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
flags.set(name, true);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return { command, positionals, flags };
|
|
109
|
+
}
|
|
110
|
+
function resolveCreateOptions(parsed) {
|
|
111
|
+
const templateFlag = getStringFlag(parsed.flags, "template");
|
|
112
|
+
const force = hasBooleanFlag(parsed.flags, "force");
|
|
113
|
+
const install = hasBooleanFlag(parsed.flags, "install");
|
|
114
|
+
const git = hasBooleanFlag(parsed.flags, "git");
|
|
115
|
+
let templateId = templateFlag;
|
|
116
|
+
let targetDirArg;
|
|
117
|
+
if (templateId) {
|
|
118
|
+
targetDirArg = parsed.positionals[0] ?? templateId;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
templateId = parsed.positionals[0];
|
|
122
|
+
targetDirArg = parsed.positionals[1] ?? parsed.positionals[0];
|
|
123
|
+
}
|
|
124
|
+
if (!templateId) {
|
|
125
|
+
throw new Error("Missing template name. Run `tmp-cli list` to see available templates.");
|
|
126
|
+
}
|
|
127
|
+
const template = getTemplate(templateId);
|
|
128
|
+
if (!template) {
|
|
129
|
+
throw new Error(`Unknown template "${templateId}". Run \`tmp-cli list\` to see available templates.`);
|
|
130
|
+
}
|
|
131
|
+
if (!targetDirArg) {
|
|
132
|
+
throw new Error("Missing target directory for create command.");
|
|
133
|
+
}
|
|
134
|
+
const targetDir = resolve(process.cwd(), targetDirArg);
|
|
135
|
+
const directoryName = basename(targetDir);
|
|
136
|
+
const displayName = getStringFlag(parsed.flags, "name") ?? directoryName;
|
|
137
|
+
const packageName = normalizePackageName(directoryName);
|
|
138
|
+
const packageManagerValue = getStringFlag(parsed.flags, "package-manager") ??
|
|
139
|
+
template.defaultPackageManager;
|
|
140
|
+
if (packageManagerValue !== undefined &&
|
|
141
|
+
!VALID_PACKAGE_MANAGERS.has(packageManagerValue)) {
|
|
142
|
+
throw new Error(`Unsupported package manager "${packageManagerValue}". Use npm, pnpm, yarn, or bun.`);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
template,
|
|
146
|
+
targetDir,
|
|
147
|
+
displayName,
|
|
148
|
+
directoryName,
|
|
149
|
+
packageName,
|
|
150
|
+
force,
|
|
151
|
+
install,
|
|
152
|
+
git,
|
|
153
|
+
packageManager: packageManagerValue
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function createProject(options) {
|
|
157
|
+
const templateSourceDir = resolve(ROOT_DIR, options.template.path);
|
|
158
|
+
if (!existsSync(templateSourceDir)) {
|
|
159
|
+
throw new Error(`Template path does not exist: ${templateSourceDir}`);
|
|
160
|
+
}
|
|
161
|
+
prepareTargetDirectory(options.targetDir, options.force);
|
|
162
|
+
copyDirectory(templateSourceDir, options.targetDir);
|
|
163
|
+
applyTransforms(options);
|
|
164
|
+
const nextSteps = [`cd ${options.targetDir}`];
|
|
165
|
+
if (options.git) {
|
|
166
|
+
runCommand("git", ["init"], options.targetDir);
|
|
167
|
+
nextSteps.push("git add .", 'git commit -m "Initial commit"');
|
|
168
|
+
}
|
|
169
|
+
if (options.install) {
|
|
170
|
+
const packageManager = options.packageManager;
|
|
171
|
+
if (!packageManager) {
|
|
172
|
+
console.warn("Warning: --install is only supported for templates with a configured JavaScript package manager.");
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
runCommand(packageManager, ["install"], options.targetDir);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else if (options.packageManager) {
|
|
179
|
+
nextSteps.push(`${options.packageManager} install`);
|
|
180
|
+
}
|
|
181
|
+
console.log(`Created ${options.template.id} at ${options.targetDir}`);
|
|
182
|
+
console.log(`Platform: ${options.template.platform}`);
|
|
183
|
+
if (options.template.renameStrategy === "directory-only") {
|
|
184
|
+
console.log("Note: this template keeps its original framework identifiers. Only the target directory and README reference were updated.");
|
|
185
|
+
}
|
|
186
|
+
if (nextSteps.length > 0) {
|
|
187
|
+
console.log("\nNext:");
|
|
188
|
+
for (const step of nextSteps) {
|
|
189
|
+
console.log(` ${step}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function listTemplates() {
|
|
194
|
+
console.log("Available templates:\n");
|
|
195
|
+
for (const template of templates) {
|
|
196
|
+
console.log(`- ${template.id}`);
|
|
197
|
+
console.log(` ${template.description}`);
|
|
198
|
+
console.log(` Platform: ${template.platform}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function prepareTargetDirectory(targetDir, force) {
|
|
202
|
+
if (!existsSync(targetDir)) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const targetStats = statSync(targetDir);
|
|
206
|
+
if (!targetStats.isDirectory()) {
|
|
207
|
+
throw new Error(`Target path is not a directory: ${targetDir}`);
|
|
208
|
+
}
|
|
209
|
+
const entries = readdirSync(targetDir);
|
|
210
|
+
if (entries.length === 0) {
|
|
211
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (!force) {
|
|
215
|
+
throw new Error(`Target directory is not empty: ${targetDir}. Use --force to overwrite it.`);
|
|
216
|
+
}
|
|
217
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
function copyDirectory(sourceDir, targetDir) {
|
|
220
|
+
mkdirSync(targetDir, { recursive: true });
|
|
221
|
+
for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
|
|
222
|
+
if (SKIP_NAMES.has(entry.name)) {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const sourcePath = join(sourceDir, entry.name);
|
|
226
|
+
const targetPath = join(targetDir, entry.name);
|
|
227
|
+
if (entry.isDirectory()) {
|
|
228
|
+
copyDirectory(sourcePath, targetPath);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (entry.isFile()) {
|
|
232
|
+
copyFileSync(sourcePath, targetPath);
|
|
233
|
+
chmodSync(targetPath, statSync(sourcePath).mode);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function applyTransforms(options) {
|
|
238
|
+
for (const transform of options.template.transforms) {
|
|
239
|
+
applyTransform(transform, options);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function applyTransform(transform, options) {
|
|
243
|
+
switch (transform) {
|
|
244
|
+
case "package-json-name":
|
|
245
|
+
updateJsonFile(join(options.targetDir, "package.json"), (json) => {
|
|
246
|
+
json.name = options.packageName;
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
case "package-lock-name":
|
|
250
|
+
updateJsonFile(join(options.targetDir, "package-lock.json"), (json) => {
|
|
251
|
+
json.name = options.packageName;
|
|
252
|
+
if (json.packages?.[""]) {
|
|
253
|
+
json.packages[""].name = options.packageName;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
case "expo-config":
|
|
258
|
+
updateJsonFile(join(options.targetDir, "app.json"), (json) => {
|
|
259
|
+
if (!json.expo) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
json.expo.name = options.displayName;
|
|
263
|
+
json.expo.slug = slugify(options.directoryName);
|
|
264
|
+
});
|
|
265
|
+
return;
|
|
266
|
+
case "readme-name":
|
|
267
|
+
replaceInFile(join(options.targetDir, "README.md"), options.template.id, options.directoryName);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function updateJsonFile(filePath, updater) {
|
|
271
|
+
if (!existsSync(filePath)) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
275
|
+
updater(parsed);
|
|
276
|
+
writeFileSync(filePath, `${JSON.stringify(parsed, null, 2)}\n`);
|
|
277
|
+
}
|
|
278
|
+
function replaceInFile(filePath, searchValue, replacementValue) {
|
|
279
|
+
if (!existsSync(filePath)) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const original = readFileSync(filePath, "utf8");
|
|
283
|
+
const updated = original.split(searchValue).join(replacementValue);
|
|
284
|
+
if (updated !== original) {
|
|
285
|
+
writeFileSync(filePath, updated);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function runCommand(command, args, cwd) {
|
|
289
|
+
const result = spawnSync(command, args, {
|
|
290
|
+
cwd,
|
|
291
|
+
stdio: "inherit"
|
|
292
|
+
});
|
|
293
|
+
if (result.error) {
|
|
294
|
+
throw result.error;
|
|
295
|
+
}
|
|
296
|
+
if (result.status !== 0) {
|
|
297
|
+
throw new Error(`Command failed: ${command} ${args.join(" ")}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function normalizeFlagName(name) {
|
|
301
|
+
return name.trim().toLowerCase();
|
|
302
|
+
}
|
|
303
|
+
function flagTakesValue(name) {
|
|
304
|
+
return name === "template" || name === "name" || name === "package-manager";
|
|
305
|
+
}
|
|
306
|
+
function mapShortFlag(flag) {
|
|
307
|
+
switch (flag) {
|
|
308
|
+
case "t":
|
|
309
|
+
return "template";
|
|
310
|
+
case "n":
|
|
311
|
+
return "name";
|
|
312
|
+
case "p":
|
|
313
|
+
return "package-manager";
|
|
314
|
+
case "f":
|
|
315
|
+
return "force";
|
|
316
|
+
case "i":
|
|
317
|
+
return "install";
|
|
318
|
+
case "g":
|
|
319
|
+
return "git";
|
|
320
|
+
case "h":
|
|
321
|
+
return "help";
|
|
322
|
+
case "v":
|
|
323
|
+
return "version";
|
|
324
|
+
default:
|
|
325
|
+
throw new Error(`Unknown flag: -${flag}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function hasBooleanFlag(flags, name) {
|
|
329
|
+
return flags.get(name) === true;
|
|
330
|
+
}
|
|
331
|
+
function getStringFlag(flags, name) {
|
|
332
|
+
const value = flags.get(name);
|
|
333
|
+
return typeof value === "string" ? value : undefined;
|
|
334
|
+
}
|
|
335
|
+
function normalizePackageName(value) {
|
|
336
|
+
return value
|
|
337
|
+
.trim()
|
|
338
|
+
.toLowerCase()
|
|
339
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
340
|
+
.replace(/^-+|-+$/g, "")
|
|
341
|
+
.replace(/-{2,}/g, "-");
|
|
342
|
+
}
|
|
343
|
+
function slugify(value) {
|
|
344
|
+
return value
|
|
345
|
+
.trim()
|
|
346
|
+
.toLowerCase()
|
|
347
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
348
|
+
.replace(/^-+|-+$/g, "")
|
|
349
|
+
.replace(/-{2,}/g, "-");
|
|
350
|
+
}
|
|
351
|
+
function printHelp(errorMessage) {
|
|
352
|
+
if (errorMessage) {
|
|
353
|
+
console.error(errorMessage);
|
|
354
|
+
console.error("");
|
|
355
|
+
}
|
|
356
|
+
console.log(`tmp-cli ${CLI_VERSION}
|
|
357
|
+
|
|
358
|
+
Usage:
|
|
359
|
+
tmp-cli list
|
|
360
|
+
tmp-cli create <template> [target-directory] [options]
|
|
361
|
+
tmp-cli create <target-directory> --template <template> [options]
|
|
362
|
+
|
|
363
|
+
Options:
|
|
364
|
+
-t, --template <name> Template name to copy
|
|
365
|
+
-n, --name <display-name> Display name for supported templates
|
|
366
|
+
-p, --package-manager <tool> npm | pnpm | yarn | bun
|
|
367
|
+
-f, --force Overwrite a non-empty target directory
|
|
368
|
+
-i, --install Run package-manager install after copy
|
|
369
|
+
-g, --git Run git init in the generated directory
|
|
370
|
+
-h, --help Show help
|
|
371
|
+
-v, --version Show CLI version
|
|
372
|
+
|
|
373
|
+
Examples:
|
|
374
|
+
tmp-cli list
|
|
375
|
+
tmp-cli create supabase-expo-app my-mobile-app
|
|
376
|
+
tmp-cli create ./apps/admin --template supabase-user-management --git
|
|
377
|
+
tmp-cli create supabase-expo-app my-mobile-app --name "My Mobile App" --install
|
|
378
|
+
`);
|
|
379
|
+
}
|
|
380
|
+
main();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const templates = [
|
|
2
|
+
{
|
|
3
|
+
id: "supabase-expo-app",
|
|
4
|
+
path: "supabase-expo-app",
|
|
5
|
+
description: "Expo + Supabase starter with auth, account, and avatar flows",
|
|
6
|
+
platform: "React Native / Expo",
|
|
7
|
+
defaultPackageManager: "npm",
|
|
8
|
+
transforms: [
|
|
9
|
+
"package-json-name",
|
|
10
|
+
"package-lock-name",
|
|
11
|
+
"expo-config",
|
|
12
|
+
"readme-name"
|
|
13
|
+
],
|
|
14
|
+
renameStrategy: "config-only"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "supabase-user-management",
|
|
18
|
+
path: "supabase-user-management",
|
|
19
|
+
description: "Next.js + Supabase user management starter",
|
|
20
|
+
platform: "Next.js",
|
|
21
|
+
defaultPackageManager: "npm",
|
|
22
|
+
transforms: ["package-json-name", "package-lock-name", "readme-name"],
|
|
23
|
+
renameStrategy: "config-only"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "supabase-swiftui-app",
|
|
27
|
+
path: "supabase-swiftui-app",
|
|
28
|
+
description: "SwiftUI + Supabase starter",
|
|
29
|
+
platform: "SwiftUI",
|
|
30
|
+
transforms: ["readme-name"],
|
|
31
|
+
renameStrategy: "directory-only"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "supabase_flutter_app",
|
|
35
|
+
path: "supabase_flutter_app",
|
|
36
|
+
description: "Flutter + Supabase starter",
|
|
37
|
+
platform: "Flutter",
|
|
38
|
+
transforms: ["readme-name"],
|
|
39
|
+
renameStrategy: "directory-only"
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
export function getTemplate(id) {
|
|
43
|
+
return templates.find((template) => template.id === id);
|
|
44
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tools-template-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for cloning starter templates from this repository into fresh project directories",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"tmp-cli": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/vinzeny/templates.git"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/vinzeny/templates",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/vinzeny/templates/issues"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"cli",
|
|
20
|
+
"template",
|
|
21
|
+
"starter",
|
|
22
|
+
"expo",
|
|
23
|
+
"nextjs",
|
|
24
|
+
"flutter",
|
|
25
|
+
"swiftui"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.json",
|
|
29
|
+
"check": "npm run build",
|
|
30
|
+
"prepack": "npm run build",
|
|
31
|
+
"list": "node dist/index.js list",
|
|
32
|
+
"create": "node dist/index.js create"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md",
|
|
37
|
+
"supabase-expo-app",
|
|
38
|
+
"supabase-user-management",
|
|
39
|
+
"supabase-swiftui-app",
|
|
40
|
+
"supabase_flutter_app"
|
|
41
|
+
],
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^22.15.17",
|
|
47
|
+
"typescript": "^5.9.3"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
import { StyleSheet, View, ActivityIndicator } from 'react-native'
|
|
3
|
+
import { supabase } from './lib/supabase'
|
|
4
|
+
import Auth from './components/Auth'
|
|
5
|
+
import Account from './components/Account'
|
|
6
|
+
|
|
7
|
+
export default function App() {
|
|
8
|
+
const [session, setSession] = useState<any>(null)
|
|
9
|
+
const [loading, setLoading] = useState(true)
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
// Get initial session
|
|
13
|
+
supabase.auth.getSession().then(({ data: { session } }) => {
|
|
14
|
+
setSession(session)
|
|
15
|
+
setLoading(false)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
// Listen for auth changes
|
|
19
|
+
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
|
|
20
|
+
setSession(session)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
return () => subscription.unsubscribe()
|
|
24
|
+
}, [])
|
|
25
|
+
|
|
26
|
+
if (loading) {
|
|
27
|
+
return (
|
|
28
|
+
<View style={styles.loadingContainer}>
|
|
29
|
+
<ActivityIndicator size="large" color="#10b981" />
|
|
30
|
+
</View>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<View style={styles.container}>
|
|
36
|
+
{session && session.user ? (
|
|
37
|
+
<Account key={session.user.id} session={session} />
|
|
38
|
+
) : (
|
|
39
|
+
<Auth onAuthSuccess={() => {}} />
|
|
40
|
+
)}
|
|
41
|
+
</View>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const styles = StyleSheet.create({
|
|
46
|
+
container: {
|
|
47
|
+
flex: 1,
|
|
48
|
+
backgroundColor: '#fff',
|
|
49
|
+
},
|
|
50
|
+
loadingContainer: {
|
|
51
|
+
flex: 1,
|
|
52
|
+
justifyContent: 'center',
|
|
53
|
+
alignItems: 'center',
|
|
54
|
+
},
|
|
55
|
+
})
|