create-stx 0.2.3

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 (2) hide show
  1. package/index.ts +703 -0
  2. package/package.json +31 -0
package/index.ts ADDED
@@ -0,0 +1,703 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * create-stx
4
+ *
5
+ * Scaffold a new STX application.
6
+ *
7
+ * Usage:
8
+ * bun create stx my-app
9
+ * bunx create-stx my-app
10
+ * npx create-stx my-app
11
+ */
12
+
13
+ import fs from 'node:fs'
14
+ import path from 'node:path'
15
+ import { spawn } from 'node:child_process'
16
+
17
+ // Colors for terminal output
18
+ const colors = {
19
+ reset: '\x1b[0m',
20
+ bright: '\x1b[1m',
21
+ dim: '\x1b[2m',
22
+ green: '\x1b[32m',
23
+ yellow: '\x1b[33m',
24
+ blue: '\x1b[34m',
25
+ cyan: '\x1b[36m',
26
+ red: '\x1b[31m',
27
+ }
28
+
29
+ function log(message: string) {
30
+ console.log(message)
31
+ }
32
+
33
+ function success(message: string) {
34
+ console.log(`${colors.green}✓${colors.reset} ${message}`)
35
+ }
36
+
37
+ function error(message: string) {
38
+ console.error(`${colors.red}✗${colors.reset} ${message}`)
39
+ }
40
+
41
+ function info(message: string) {
42
+ console.log(`${colors.blue}ℹ${colors.reset} ${message}`)
43
+ }
44
+
45
+ // Template files
46
+ const templates = {
47
+ 'pages/index.stx': `<script>
48
+ export const title = 'Welcome to STX'
49
+ export const description = 'Build modern web apps with intuitive templating'
50
+ </script>
51
+
52
+ <!DOCTYPE html>
53
+ <html lang="en">
54
+ <head>
55
+ <meta charset="UTF-8">
56
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
57
+ <title>{{ title }}</title>
58
+ <link rel="stylesheet" href="/dist/app.css">
59
+ <style>
60
+ * { margin: 0; padding: 0; box-sizing: border-box; }
61
+ body {
62
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
63
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
64
+ min-height: 100vh;
65
+ display: flex;
66
+ align-items: center;
67
+ justify-content: center;
68
+ color: white;
69
+ }
70
+ .container { text-align: center; padding: 2rem; }
71
+ h1 { font-size: 3rem; margin-bottom: 1rem; }
72
+ p { font-size: 1.25rem; opacity: 0.9; margin-bottom: 2rem; }
73
+ .counter { margin: 2rem 0; }
74
+ .counter button {
75
+ padding: 0.5rem 1.5rem;
76
+ margin: 0 0.5rem;
77
+ font-size: 1.5rem;
78
+ border: 2px solid white;
79
+ background: transparent;
80
+ color: white;
81
+ border-radius: 8px;
82
+ cursor: pointer;
83
+ }
84
+ .counter button:hover { background: white; color: #764ba2; }
85
+ .counter span { font-size: 2rem; margin: 0 1rem; }
86
+ .links { display: flex; gap: 1rem; justify-content: center; }
87
+ a {
88
+ color: white;
89
+ text-decoration: none;
90
+ padding: 0.75rem 1.5rem;
91
+ border: 2px solid white;
92
+ border-radius: 8px;
93
+ transition: all 0.2s;
94
+ }
95
+ a:hover { background: white; color: #764ba2; }
96
+ </style>
97
+ <!-- Load stores synchronously -->
98
+ <script>
99
+ var xhr = new XMLHttpRequest();
100
+ xhr.open('GET', '/js/stores.js', false);
101
+ xhr.send();
102
+ if (xhr.status === 200) eval(xhr.responseText);
103
+ </script>
104
+ </head>
105
+ <body>
106
+ <div class="container">
107
+ <h1>{{ title }}</h1>
108
+ <p>{{ description }}</p>
109
+
110
+ <!-- Counter demo using stores -->
111
+ <div class="counter">
112
+ <button id="decrement">-</button>
113
+ <span id="count">0</span>
114
+ <button id="increment">+</button>
115
+ </div>
116
+
117
+ <div class="links">
118
+ <a href="https://stx.sh" target="_blank">Documentation</a>
119
+ <a href="https://github.com/stacksjs/stx" target="_blank">GitHub</a>
120
+ </div>
121
+ </div>
122
+
123
+ <script>
124
+ (function() {
125
+ var stores = window.AppStores;
126
+ var appStore = stores.appStore;
127
+ var appActions = stores.appActions;
128
+
129
+ // Subscribe to store changes
130
+ appStore.subscribe(function(state) {
131
+ document.getElementById('count').textContent = state.count;
132
+ });
133
+
134
+ // Button handlers
135
+ document.getElementById('increment').onclick = appActions.increment;
136
+ document.getElementById('decrement').onclick = appActions.decrement;
137
+ })();
138
+ </script>
139
+ </body>
140
+ </html>
141
+ `,
142
+
143
+ 'components/Button.stx': `<!--
144
+ Button Component
145
+
146
+ Props:
147
+ - text: Button text
148
+ - type: Button type (primary, secondary, danger)
149
+ - disabled: Whether button is disabled
150
+ -->
151
+ <button
152
+ class="btn btn-{{ type || 'primary' }}"
153
+ {{ disabled ? 'disabled' : '' }}
154
+ >
155
+ {{ text || slot }}
156
+ </button>
157
+
158
+ <style scoped>
159
+ .btn {
160
+ padding: 0.5rem 1rem;
161
+ border: none;
162
+ border-radius: 6px;
163
+ font-size: 1rem;
164
+ cursor: pointer;
165
+ transition: all 0.2s;
166
+ }
167
+ .btn-primary {
168
+ background: #667eea;
169
+ color: white;
170
+ }
171
+ .btn-primary:hover {
172
+ background: #5a67d8;
173
+ }
174
+ .btn-secondary {
175
+ background: #e2e8f0;
176
+ color: #4a5568;
177
+ }
178
+ .btn-danger {
179
+ background: #fc8181;
180
+ color: white;
181
+ }
182
+ .btn:disabled {
183
+ opacity: 0.5;
184
+ cursor: not-allowed;
185
+ }
186
+ </style>
187
+ `,
188
+
189
+ 'public/.gitkeep': '',
190
+
191
+ 'public/js/.gitkeep': '',
192
+
193
+ 'lib/store.ts': `/**
194
+ * Lightweight reactive store implementation
195
+ * Based on STX state-management patterns
196
+ */
197
+
198
+ export type Subscriber<T> = (value: T, previousValue: T | undefined) => void
199
+ export type Unsubscribe = () => void
200
+
201
+ export interface StoreOptions<T> {
202
+ name?: string
203
+ persist?: {
204
+ key?: string
205
+ storage?: 'local' | 'session'
206
+ debounce?: number
207
+ }
208
+ onChange?: (value: T, prev: T | undefined) => void
209
+ }
210
+
211
+ export interface Store<T> {
212
+ get: () => T
213
+ set: (value: T | ((prev: T) => T)) => void
214
+ update: (partial: Partial<T> | ((prev: T) => Partial<T>)) => void
215
+ subscribe: (subscriber: Subscriber<T>) => Unsubscribe
216
+ reset: () => void
217
+ name?: string
218
+ }
219
+
220
+ export function createStore<T extends object>(
221
+ initialValue: T,
222
+ options: StoreOptions<T> = {}
223
+ ): Store<T> {
224
+ const { name, persist, onChange } = options
225
+
226
+ let currentValue = { ...initialValue }
227
+ const subscribers = new Set<Subscriber<T>>()
228
+ let persistTimeout: ReturnType<typeof setTimeout> | null = null
229
+
230
+ // Load persisted value
231
+ if (persist && typeof window !== 'undefined') {
232
+ const storage = persist.storage === 'session' ? sessionStorage : localStorage
233
+ const key = persist.key || \`app:\${name || 'store'}\`
234
+ try {
235
+ const stored = storage.getItem(key)
236
+ if (stored) {
237
+ currentValue = { ...initialValue, ...JSON.parse(stored) }
238
+ }
239
+ } catch { /* Invalid stored value */ }
240
+ }
241
+
242
+ const persistValue = (value: T) => {
243
+ if (!persist || typeof window === 'undefined') return
244
+ const storage = persist.storage === 'session' ? sessionStorage : localStorage
245
+ const key = persist.key || \`app:\${name || 'store'}\`
246
+ if (persist.debounce) {
247
+ if (persistTimeout) clearTimeout(persistTimeout)
248
+ persistTimeout = setTimeout(() => storage.setItem(key, JSON.stringify(value)), persist.debounce)
249
+ } else {
250
+ storage.setItem(key, JSON.stringify(value))
251
+ }
252
+ }
253
+
254
+ const notify = (newValue: T, prevValue: T | undefined) => {
255
+ for (const subscriber of subscribers) {
256
+ try { subscriber(newValue, prevValue) } catch (e) { console.error('[store]', e) }
257
+ }
258
+ if (onChange) onChange(newValue, prevValue)
259
+ }
260
+
261
+ return {
262
+ get: () => currentValue,
263
+ set: (value) => {
264
+ const prevValue = currentValue
265
+ const newValue = typeof value === 'function' ? (value as (prev: T) => T)(currentValue) : value
266
+ if (newValue !== prevValue) {
267
+ currentValue = newValue
268
+ persistValue(newValue)
269
+ notify(newValue, prevValue)
270
+ }
271
+ },
272
+ update: (partial) => {
273
+ const prevValue = currentValue
274
+ const updates = typeof partial === 'function' ? (partial as (prev: T) => Partial<T>)(currentValue) : partial
275
+ currentValue = { ...currentValue, ...updates }
276
+ persistValue(currentValue)
277
+ notify(currentValue, prevValue)
278
+ },
279
+ subscribe: (subscriber) => {
280
+ subscribers.add(subscriber)
281
+ subscriber(currentValue, undefined)
282
+ return () => subscribers.delete(subscriber)
283
+ },
284
+ reset: () => {
285
+ const prevValue = currentValue
286
+ currentValue = { ...initialValue }
287
+ persistValue(currentValue)
288
+ notify(currentValue, prevValue)
289
+ },
290
+ name
291
+ }
292
+ }
293
+ `,
294
+
295
+ 'lib/composables/index.ts': `/**
296
+ * Composables - Browser API utilities (Nuxt-style)
297
+ */
298
+ export * from './use-storage'
299
+ export * from './use-cookie'
300
+ `,
301
+
302
+ 'lib/composables/use-storage.ts': `/**
303
+ * useStorage - Reactive localStorage/sessionStorage wrapper
304
+ */
305
+ export interface StorageOptions<T> {
306
+ defaultValue?: T
307
+ serializer?: {
308
+ read: (raw: string) => T
309
+ write: (value: T) => string
310
+ }
311
+ }
312
+
313
+ export function useStorage<T>(
314
+ key: string,
315
+ storage: Storage = localStorage,
316
+ options: StorageOptions<T> = {}
317
+ ) {
318
+ const { defaultValue, serializer = { read: JSON.parse, write: JSON.stringify } } = options
319
+ let listeners: Array<(value: T | undefined) => void> = []
320
+
321
+ const get = (): T | undefined => {
322
+ try {
323
+ const raw = storage.getItem(key)
324
+ return raw !== null ? serializer.read(raw) : defaultValue
325
+ } catch {
326
+ return defaultValue
327
+ }
328
+ }
329
+
330
+ const set = (value: T): void => {
331
+ try {
332
+ storage.setItem(key, serializer.write(value))
333
+ listeners.forEach(fn => fn(value))
334
+ } catch (e) {
335
+ console.error('[useStorage] Failed to set:', e)
336
+ }
337
+ }
338
+
339
+ const remove = (): void => {
340
+ storage.removeItem(key)
341
+ listeners.forEach(fn => fn(undefined))
342
+ }
343
+
344
+ const subscribe = (fn: (value: T | undefined) => void) => {
345
+ listeners.push(fn)
346
+ fn(get())
347
+ return () => { listeners = listeners.filter(l => l !== fn) }
348
+ }
349
+
350
+ return { get, set, remove, subscribe }
351
+ }
352
+
353
+ export const useLocalStorage = <T>(key: string, options?: StorageOptions<T>) =>
354
+ useStorage<T>(key, localStorage, options)
355
+
356
+ export const useSessionStorage = <T>(key: string, options?: StorageOptions<T>) =>
357
+ useStorage<T>(key, sessionStorage, options)
358
+ `,
359
+
360
+ 'lib/composables/use-cookie.ts': `/**
361
+ * useCookie - Cookie management utility
362
+ */
363
+ export interface CookieOptions {
364
+ expires?: number | Date
365
+ path?: string
366
+ domain?: string
367
+ secure?: boolean
368
+ sameSite?: 'Strict' | 'Lax' | 'None'
369
+ }
370
+
371
+ export function getCookie(name: string): string | undefined {
372
+ const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'))
373
+ return match ? decodeURIComponent(match[2]) : undefined
374
+ }
375
+
376
+ export function setCookie(name: string, value: string, options: CookieOptions = {}): void {
377
+ let cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value)
378
+ if (options.expires) {
379
+ const date = options.expires instanceof Date
380
+ ? options.expires
381
+ : new Date(Date.now() + options.expires * 864e5)
382
+ cookie += '; expires=' + date.toUTCString()
383
+ }
384
+ if (options.path) cookie += '; path=' + options.path
385
+ if (options.domain) cookie += '; domain=' + options.domain
386
+ if (options.secure) cookie += '; secure'
387
+ if (options.sameSite) cookie += '; samesite=' + options.sameSite
388
+ document.cookie = cookie
389
+ }
390
+
391
+ export function removeCookie(name: string, options: CookieOptions = {}): void {
392
+ setCookie(name, '', { ...options, expires: -1 })
393
+ }
394
+
395
+ export function useCookie<T = string>(name: string, options: CookieOptions = {}) {
396
+ const serialize = (val: T): string =>
397
+ typeof val === 'string' ? val : JSON.stringify(val)
398
+ const deserialize = (raw: string): T => {
399
+ try { return JSON.parse(raw) as T } catch { return raw as T }
400
+ }
401
+
402
+ return {
403
+ get: (): T | undefined => {
404
+ const raw = getCookie(name)
405
+ return raw !== undefined ? deserialize(raw) : undefined
406
+ },
407
+ set: (value: T) => setCookie(name, serialize(value), options),
408
+ remove: () => removeCookie(name, options)
409
+ }
410
+ }
411
+ `,
412
+
413
+ 'lib/stores/index.ts': `/**
414
+ * Stores - Central export
415
+ */
416
+ export * from './app'
417
+ export { createStore } from '../store'
418
+ export * from '../composables'
419
+ `,
420
+
421
+ 'lib/stores/app.ts': `/**
422
+ * App Store - Example application state
423
+ */
424
+ import { createStore } from '../store'
425
+
426
+ export interface AppState {
427
+ count: number
428
+ theme: 'light' | 'dark'
429
+ }
430
+
431
+ export const appStore = createStore<AppState>({
432
+ count: 0,
433
+ theme: 'light'
434
+ }, {
435
+ name: 'app',
436
+ persist: { key: 'app:state', storage: 'local' }
437
+ })
438
+
439
+ export const appActions = {
440
+ increment: () => appStore.update(s => ({ count: s.count + 1 })),
441
+ decrement: () => appStore.update(s => ({ count: s.count - 1 })),
442
+ setTheme: (theme: AppState['theme']) => appStore.update({ theme })
443
+ }
444
+ `,
445
+
446
+ 'lib/build.ts': `/**
447
+ * Build script to bundle stores for browser use
448
+ */
449
+ import { build } from 'bun'
450
+ import { writeFileSync, readFileSync } from 'fs'
451
+
452
+ const result = await build({
453
+ entrypoints: ['./lib/stores/index.ts'],
454
+ outdir: './public/js',
455
+ target: 'browser',
456
+ format: 'iife',
457
+ naming: 'stores.js',
458
+ minify: false,
459
+ define: { 'process.env.NODE_ENV': '"production"' }
460
+ })
461
+
462
+ if (!result.success) {
463
+ console.error('Build failed:', result.logs)
464
+ process.exit(1)
465
+ }
466
+
467
+ // Wrap to export globally
468
+ const bundlePath = './public/js/stores.js'
469
+ let bundle = readFileSync(bundlePath, 'utf-8')
470
+ bundle = bundle.replace(/^\\(\\(\\) => \\{/, 'window.AppStores = (function() {')
471
+ bundle = bundle.replace(/\\}\\)\\(\\);?\\s*$/, ' return exports_stores;\\n})();')
472
+
473
+ writeFileSync(bundlePath, \`/**
474
+ * App Stores - Browser Bundle
475
+ * Auto-generated, do not edit
476
+ */
477
+ \${bundle}
478
+ \`)
479
+ console.log('Stores built to public/js/stores.js')
480
+ `,
481
+
482
+ 'stx.config.ts': `import type { StxConfig } from '@stacksjs/stx'
483
+
484
+ const config: StxConfig = {
485
+ // Pages directory for file-based routing
486
+ pagesDir: 'pages',
487
+
488
+ // Components directory
489
+ componentsDir: 'components',
490
+
491
+ // Public assets directory
492
+ publicDir: 'public',
493
+
494
+ // Enable caching in production
495
+ cache: process.env.NODE_ENV === 'production',
496
+
497
+ // Debug mode in development
498
+ debug: process.env.NODE_ENV !== 'production',
499
+ }
500
+
501
+ export default config
502
+ `,
503
+
504
+ 'package.json': (name: string) => JSON.stringify({
505
+ name,
506
+ version: '0.0.1',
507
+ type: 'module',
508
+ scripts: {
509
+ dev: 'bun run build:stores && bun run --bun stx dev',
510
+ build: 'bun run build:stores && bun run --bun stx build',
511
+ 'build:stores': 'bun run lib/build.ts',
512
+ preview: 'bun run --bun stx preview',
513
+ },
514
+ dependencies: {},
515
+ devDependencies: {
516
+ '@stacksjs/stx': 'latest',
517
+ 'bun-plugin-stx': 'latest',
518
+ typescript: '^5.0.0',
519
+ },
520
+ }, null, 2),
521
+
522
+ 'bunfig.toml': `preload = ["bun-plugin-stx"]
523
+ `,
524
+
525
+ 'tsconfig.json': JSON.stringify({
526
+ compilerOptions: {
527
+ target: 'ESNext',
528
+ module: 'ESNext',
529
+ moduleResolution: 'bundler',
530
+ strict: true,
531
+ esModuleInterop: true,
532
+ skipLibCheck: true,
533
+ resolveJsonModule: true,
534
+ declaration: true,
535
+ outDir: 'dist',
536
+ rootDir: '.',
537
+ },
538
+ include: ['**/*.ts', '**/*.stx'],
539
+ exclude: ['node_modules', 'dist'],
540
+ }, null, 2),
541
+
542
+ '.gitignore': `# Dependencies
543
+ node_modules/
544
+
545
+ # Build output
546
+ dist/
547
+ .stx/
548
+
549
+ # Environment
550
+ .env
551
+ .env.local
552
+ .env.*.local
553
+
554
+ # IDE
555
+ .vscode/
556
+ .idea/
557
+ *.swp
558
+ *.swo
559
+
560
+ # OS
561
+ .DS_Store
562
+ Thumbs.db
563
+
564
+ # Logs
565
+ *.log
566
+ npm-debug.log*
567
+ `,
568
+
569
+ 'README.md': (name: string) => `# ${name}
570
+
571
+ A modern web application built with [STX](https://stx.sh).
572
+
573
+ ## Getting Started
574
+
575
+ \`\`\`bash
576
+ # Start development server
577
+ bun dev
578
+
579
+ # Build for production
580
+ bun run build
581
+
582
+ # Preview production build
583
+ bun run preview
584
+ \`\`\`
585
+
586
+ ## Project Structure
587
+
588
+ \`\`\`
589
+ ${name}/
590
+ pages/ # File-based routing
591
+ index.stx # Homepage (/)
592
+ components/ # Reusable components
593
+ public/ # Static assets
594
+ stx.config.ts # STX configuration
595
+ \`\`\`
596
+
597
+ ## Learn More
598
+
599
+ - [STX Documentation](https://stx.sh)
600
+ - [GitHub Repository](https://github.com/stacksjs/stx)
601
+ `,
602
+ }
603
+
604
+ async function createProject(projectName: string, targetDir: string) {
605
+ log('')
606
+ log(`${colors.bright}${colors.blue} Creating STX app in ${colors.cyan}${targetDir}${colors.reset}`)
607
+ log('')
608
+
609
+ // Create directories
610
+ const dirs = ['pages', 'components', 'public', 'public/js', 'lib', 'lib/stores', 'lib/composables']
611
+ for (const dir of dirs) {
612
+ const dirPath = path.join(targetDir, dir)
613
+ fs.mkdirSync(dirPath, { recursive: true })
614
+ }
615
+ success('Created project structure')
616
+
617
+ // Write template files
618
+ for (const [filePath, content] of Object.entries(templates)) {
619
+ const fullPath = path.join(targetDir, filePath)
620
+ const fileContent = typeof content === 'function' ? content(projectName) : content
621
+ fs.writeFileSync(fullPath, fileContent)
622
+ }
623
+ success('Created template files')
624
+
625
+ // Install dependencies
626
+ info('Installing dependencies...')
627
+
628
+ return new Promise<void>((resolve) => {
629
+ const child = spawn('bun', ['install'], {
630
+ cwd: targetDir,
631
+ stdio: 'inherit',
632
+ })
633
+
634
+ child.on('close', (code) => {
635
+ if (code === 0) {
636
+ success('Installed dependencies')
637
+ } else {
638
+ log('')
639
+ info('Could not install STX packages from npm (not yet published)')
640
+ info('For local development, link the packages manually:')
641
+ log(` ${colors.dim}cd ${targetDir}${colors.reset}`)
642
+ log(` ${colors.dim}bun link @stacksjs/stx${colors.reset}`)
643
+ log(` ${colors.dim}bun link bun-plugin-stx${colors.reset}`)
644
+ }
645
+ resolve()
646
+ })
647
+
648
+ child.on('error', () => {
649
+ info('Could not run bun install')
650
+ resolve()
651
+ })
652
+ })
653
+ }
654
+
655
+ async function main() {
656
+ const args = process.argv.slice(2)
657
+
658
+ // Get project name from args or prompt
659
+ let projectName = args[0]
660
+
661
+ if (!projectName) {
662
+ // Default to 'my-stx-app' if no name provided
663
+ projectName = 'my-stx-app'
664
+ info(`No project name provided, using "${projectName}"`)
665
+ }
666
+
667
+ // Validate project name
668
+ if (!/^[a-zA-Z0-9-_]+$/.test(projectName)) {
669
+ error('Project name can only contain letters, numbers, dashes, and underscores')
670
+ process.exit(1)
671
+ }
672
+
673
+ const targetDir = path.resolve(process.cwd(), projectName)
674
+
675
+ // Check if directory exists
676
+ if (fs.existsSync(targetDir)) {
677
+ const files = fs.readdirSync(targetDir)
678
+ if (files.length > 0) {
679
+ error(`Directory "${projectName}" already exists and is not empty`)
680
+ process.exit(1)
681
+ }
682
+ } else {
683
+ fs.mkdirSync(targetDir, { recursive: true })
684
+ }
685
+
686
+ try {
687
+ await createProject(projectName, targetDir)
688
+
689
+ log('')
690
+ log(`${colors.green}${colors.bright} Done!${colors.reset} Created ${projectName}`)
691
+ log('')
692
+ log(' To get started:')
693
+ log('')
694
+ log(` ${colors.cyan}cd ${projectName}${colors.reset}`)
695
+ log(` ${colors.cyan}bun dev${colors.reset}`)
696
+ log('')
697
+ } catch (err) {
698
+ error(`Failed to create project: ${err}`)
699
+ process.exit(1)
700
+ }
701
+ }
702
+
703
+ main()
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "create-stx",
3
+ "version": "0.2.3",
4
+ "description": "Create a new STX application",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-stx": "./index.ts"
8
+ },
9
+ "files": [
10
+ "index.ts",
11
+ "templates"
12
+ ],
13
+ "keywords": [
14
+ "stx",
15
+ "create",
16
+ "scaffold",
17
+ "template",
18
+ "bun"
19
+ ],
20
+ "author": "Chris Breuer <chris@stacksjs.org>",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/stacksjs/stx.git",
25
+ "directory": "packages/create-stx"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/stacksjs/stx/issues"
29
+ },
30
+ "homepage": "https://github.com/stacksjs/stx#readme"
31
+ }