@stacksjs/defaults 0.75.255 → 0.75.256

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.
@@ -171,8 +171,6 @@ if (!isRepl && !isPostinstall) {
171
171
 
172
172
  if (typeof applyRuntimeDirectoryEnv === 'function')
173
173
  applyRuntimeDirectoryEnv()
174
- else
175
- console.warn('[stacks] installed @stacksjs/path has no applyRuntimeDirectoryEnv; stx and ts-cloud will use their default state directories. Run `buddy upgrade` to refresh the framework packages.')
176
174
 
177
175
  // An app that does not vendor the framework defaults gets a gitignored
178
176
  // `storage/framework/defaults` link to the installed package. A fresh clone
@@ -181,6 +179,8 @@ if (!isRepl && !isPostinstall) {
181
179
  // published-version reason as the helper above.
182
180
  if (typeof linkPackagedDefaults === 'function')
183
181
  linkPackagedDefaults()
182
+ else
183
+ console.warn('[stacks] installed @stacksjs/path has no applyRuntimeDirectoryEnv; stx and ts-cloud will use their default state directories. Run `buddy upgrade` to refresh the framework packages.')
184
184
  }
185
185
 
186
186
  // stx template engine plugin
@@ -359,7 +359,7 @@ export async function loadAutoImports() {
359
359
  // Priority: user models (app/Models) override framework defaults
360
360
  const modelDirs = [
361
361
  path.userModelsPath(),
362
- path.storagePath('framework/defaults/app/Models'),
362
+ path.defaultsPath('app/Models'),
363
363
  ]
364
364
 
365
365
  const loadedModels = new Set<string>()
@@ -430,7 +430,7 @@ export async function loadAutoImports() {
430
430
  // Priority: user controllers > default controllers
431
431
  const controllerDirs = [
432
432
  path.userControllersPath(),
433
- path.storagePath('framework/defaults/app/Controllers'),
433
+ path.defaultsPath('app/Controllers'),
434
434
  ]
435
435
  const loadedControllers = new Set<string>()
436
436
 
@@ -0,0 +1,36 @@
1
+ # __APP_NAME__
2
+
3
+ A [Stacks](https://stacksjs.com) application.
4
+
5
+ ## Getting started
6
+
7
+ ```bash
8
+ ./buddy dev
9
+ ```
10
+
11
+ `./buddy doctor` checks your setup if anything does not start.
12
+
13
+ ## Everyday commands
14
+
15
+ | Command | What it does |
16
+ |---|---|
17
+ | `./buddy dev` | Start the development servers |
18
+ | `./buddy migrate` | Apply pending database migrations |
19
+ | `./buddy test` | Run the test suite |
20
+ | `./buddy typecheck` | Type check `app/`, `config/`, `resources/` and `routes/` |
21
+ | `./buddy lint` | Lint, and `./buddy lint:fix` to fix what can be fixed |
22
+ | `./buddy upgrade` | Move to the latest Stacks release |
23
+ | `./buddy list` | Every other command |
24
+
25
+ ## Where things live
26
+
27
+ - `app/`, `resources/`, `routes/`, `database/`: your application.
28
+ - `config/`: every setting, one typed file per subsystem. System dependencies
29
+ are declared in `config/deps.ts`, and `deps.yaml` is generated from it.
30
+ - `storage/framework/defaults`: the framework's default models, actions, views
31
+ and routes. It is a gitignored link to `node_modules/@stacksjs/defaults`, so
32
+ it upgrades with the `stacks` dependency. Override any default by creating the
33
+ same path under `app/` or `resources/`, or run `./buddy publish:defaults` to
34
+ commit a copy of the whole tree instead.
35
+
36
+ Read more at [stacksjs.com/docs](https://stacksjs.com/docs).
@@ -0,0 +1,16 @@
1
+ import type { AnalyticsConfig } from '@stacksjs/types'
2
+
3
+ /**
4
+ * AnalyticsHQ is Stacks' first-party analytics driver. Paste the site id from
5
+ * your AnalyticsHQ app to enable it. An empty site id emits no tracking tags.
6
+ */
7
+ export default {
8
+ driver: 'analyticshq',
9
+ environments: ['production', 'staging'],
10
+
11
+ drivers: {
12
+ analyticshq: {
13
+ siteId: '',
14
+ },
15
+ },
16
+ } satisfies AnalyticsConfig
@@ -0,0 +1,49 @@
1
+ import type { AppConfig } from '@stacksjs/types'
2
+ import { env } from '@stacksjs/env'
3
+
4
+ /**
5
+ * **Application Configuration**
6
+ *
7
+ * This configuration defines all of your application options. Because Stacks is fully-typed,
8
+ * you may hover any of the options below and the definitions will be provided. In case
9
+ * you have any questions, feel free to reach out via Discord or GitHub Discussions.
10
+ */
11
+ export default {
12
+ name: env.APP_NAME ?? '__APP_NAME__',
13
+ // Shown on the starter page, the coming-soon page, and any page that sets
14
+ // no description of its own.
15
+ description: '',
16
+ env: env.APP_ENV ?? 'local',
17
+ url: env.APP_URL ?? 'stacks.localhost',
18
+ redirectUrls: [],
19
+ debug: env.DEBUG ?? false,
20
+ key: env.APP_KEY,
21
+
22
+ maintenanceMode: env.APP_MAINTENANCE ?? false,
23
+ comingSoonMode: env.APP_COMING_SOON ?? false,
24
+ comingSoonSecret: env.APP_COMING_SOON_SECRET ?? '',
25
+ // docMode: true, // instead of example.com/docs, deploys example.com as main entry point for docs
26
+ docMode: false,
27
+
28
+ timezone: 'America/Los_Angeles',
29
+ locale: 'en',
30
+ fallbackLocale: 'en',
31
+ cipher: 'aes-256-cbc',
32
+
33
+ // /sitemap.xml and /robots.txt are generated from resources/views, in dev
34
+ // and production alike. A page that says `noindex` (a robots meta tag, or
35
+ // `const noindex = true` in its server script) is left out, as are dynamic
36
+ // [param] routes, error pages and anything robots.txt disallows. A file you
37
+ // put in public/ wins over the generated one.
38
+ seo: {
39
+ sitemap: {
40
+ // Concrete URLs for dynamic routes, e.g. '/products/blue-mug'.
41
+ include: [],
42
+ exclude: [],
43
+ },
44
+ robots: {
45
+ // Added to the default '/api/'.
46
+ disallow: [],
47
+ },
48
+ },
49
+ } satisfies AppConfig
@@ -0,0 +1,79 @@
1
+ import { env } from '@stacksjs/env'
2
+
3
+ export interface BlogConfig {
4
+ subdomain: string
5
+ title: string
6
+ description: string
7
+ postsPerPage: number
8
+ /**
9
+ * NOT YET HONOURED by the static blog builder. Nothing reads this: the
10
+ * generated pages carry no comment section either way. The CMS has
11
+ * commenting (the `commentable` trait and `comments` in `@stacksjs/cms`),
12
+ * but the static build does not render it (stacksjs/stacks#365-adjacent;
13
+ * found auditing config keys nothing reads).
14
+ */
15
+ enableComments: boolean
16
+ /** Honoured: generates `feed.xml` and the RSS link in the layout. */
17
+ enableRss: boolean
18
+ /** Honoured: generates `sitemap.xml`. */
19
+ enableSitemap: boolean
20
+ /**
21
+ * NOT YET HONOURED by the static blog builder. Nothing reads this and no
22
+ * search index is generated.
23
+ */
24
+ enableSearch: boolean
25
+ /** Short title used in the blog layout nav; defaults to `title`. */
26
+ siteTitle?: string
27
+ /** Fallback post author when frontmatter has none. */
28
+ author?: string
29
+ /** Canonical site origin for feed/sitemap URLs when no request origin exists. */
30
+ url?: string
31
+ nav?: { text: string, link: string }[]
32
+ /** Which modes the blog theme toggle offers. */
33
+ themes?: ('colored' | 'light' | 'dark')[]
34
+ defaultTheme?: 'colored' | 'light' | 'dark'
35
+ /** Raw HTML for the blog footer colophon line. */
36
+ colophon?: string
37
+ social: {
38
+ twitter?: string
39
+ github?: string
40
+ }
41
+ theme: {
42
+ primaryColor: string
43
+ logo?: string
44
+ }
45
+ }
46
+
47
+ // The site origin feeds and the sitemap are written against. APP_URL is often
48
+ // a bare host, which is not a URL on its own.
49
+ const appUrl = String(env.APP_URL || '').replace(/\/+$/, '')
50
+ const siteUrl = appUrl && !/^https?:\/\//.test(appUrl) ? `https://${appUrl}` : appUrl
51
+
52
+ const config: BlogConfig = {
53
+ subdomain: 'blog',
54
+ title: `${env.APP_NAME || '__APP_NAME__'} Blog`,
55
+ description: '',
56
+ postsPerPage: 10,
57
+ // `enableComments` and `enableSearch` are declared but not yet implemented
58
+ // by the static blog builder - see the interface above. Left `true` so the
59
+ // value does not have to change when they are.
60
+ enableComments: true,
61
+ enableRss: true,
62
+ enableSitemap: true,
63
+ enableSearch: true,
64
+ author: env.APP_NAME || '__APP_NAME__',
65
+ ...(siteUrl && { url: siteUrl }),
66
+ nav: [
67
+ { text: 'Home', link: '/' },
68
+ { text: 'Blog', link: '/blog' },
69
+ ],
70
+ themes: ['colored', 'light', 'dark'],
71
+ defaultTheme: 'light',
72
+ colophon: '<a href="/blog/feed.xml">RSS</a>',
73
+ social: {},
74
+ theme: {
75
+ primaryColor: '#171717',
76
+ },
77
+ }
78
+
79
+ export default config
@@ -0,0 +1,50 @@
1
+ import type { BuddyConfig } from '@buddysh/buddy'
2
+
3
+ /**
4
+ * buddy-bot is the one dependency bot a Stacks app runs.
5
+ *
6
+ * `.github/workflows/buddy-bot.yml` is what drives it: updates every two
7
+ * hours, a dashboard issue 15 minutes later, and a daily pass that closes pull
8
+ * requests whose bump already landed. Nothing else should be opening
9
+ * dependency pull requests here - two bots proposing the same bump is
10
+ * stacksjs/stacks#2574, and it costs a full CI matrix per duplicate.
11
+ */
12
+ export default {
13
+ repository: {
14
+ provider: 'github',
15
+ // In Actions this is already the account or organization the repository
16
+ // lives under, which is the only place the workflow above runs. Replace the
17
+ // fallback if you also run buddy-bot locally.
18
+ owner: process.env.GITHUB_REPOSITORY_OWNER || 'your-github-owner',
19
+ name: '__APP_SLUG__',
20
+ // token: process.env.BUDDY_BOT_TOKEN,
21
+ },
22
+
23
+ dashboard: {
24
+ enabled: true,
25
+ title: 'Dependency Dashboard',
26
+ },
27
+
28
+ workflows: {
29
+ enabled: true,
30
+ outputDir: '.github/workflows',
31
+ templates: {
32
+ daily: true,
33
+ weekly: true,
34
+ monthly: true,
35
+ },
36
+ custom: [],
37
+ },
38
+
39
+ packages: {
40
+ strategy: 'all',
41
+ ignore: [
42
+ // Packages to hold back, e.g. '@types/node'
43
+ ],
44
+ ignorePaths: [
45
+ // Glob patterns to skip, e.g. 'apps/legacy/**'
46
+ ],
47
+ },
48
+
49
+ verbose: false,
50
+ } satisfies BuddyConfig
@@ -0,0 +1,103 @@
1
+ import type { CloudConfig } from '@stacksjs/types'
2
+ import type { CloudConfig as TsCloudConfig } from '@stacksjs/ts-cloud'
3
+ import { env } from '@stacksjs/env'
4
+
5
+ const APP_SLUG = '__APP_SLUG__'
6
+ const APP_DOMAIN = env.APP_DOMAIN || undefined
7
+
8
+ /**
9
+ * Safe application cloud defaults.
10
+ *
11
+ * Set APP_DOMAIN and provider credentials before the first deploy. Use
12
+ * `cloud.attachTo` only when this app is intentionally joining a server owned
13
+ * by another ts-cloud project.
14
+ */
15
+ export const tsCloud: TsCloudConfig = {
16
+ project: {
17
+ name: APP_SLUG,
18
+ slug: APP_SLUG,
19
+ region: 'us-east-1',
20
+ },
21
+
22
+ stateDir: 'storage/cloud',
23
+
24
+ // Deploy to Hetzner Cloud, or set `provider: 'ssh'` to deploy to a Linux box
25
+ // you already own over SSH (a Raspberry Pi, a NUC, an old laptop):
26
+ //
27
+ // cloud: { provider: 'ssh' },
28
+ // ssh: { profile: 'raspberry-pi', hosts: [{ host: 'pi-stacks.local', user: 'pi' }] },
29
+ //
30
+ // See `buddy server:flash`, `server:first-boot`, `server:doctor` and
31
+ // `server:setup` for getting a fresh board to that point.
32
+ cloud: {
33
+ provider: 'hetzner',
34
+ },
35
+
36
+ mode: 'server',
37
+
38
+ environments: {
39
+ production: {
40
+ type: 'production',
41
+ deployBranch: 'main',
42
+ region: 'us-east-1',
43
+ variables: {
44
+ APP_ENV: 'production',
45
+ NODE_ENV: 'production',
46
+ LOG_LEVEL: 'info',
47
+ },
48
+ },
49
+ },
50
+
51
+ infrastructure: {
52
+ compute: {
53
+ instances: 1,
54
+ size: 'small',
55
+ disk: {
56
+ size: 20,
57
+ type: 'ssd',
58
+ encrypted: true,
59
+ },
60
+ webServer: 'rpx',
61
+ proxy: {
62
+ engine: 'rpx',
63
+ onDemandTls: true,
64
+ },
65
+ },
66
+ },
67
+
68
+ sites: {
69
+ main: {
70
+ root: '.',
71
+ path: '/',
72
+ domain: APP_DOMAIN,
73
+ start: 'bun node_modules/@stacksjs/buddy/dist/serve-entry.js',
74
+ port: 3000,
75
+ preStart: [
76
+ 'bun install --frozen-lockfile',
77
+ 'bun node_modules/@stacksjs/buddy/dist/cli.js migrate',
78
+ ],
79
+ env: {
80
+ APP_ENV: 'production',
81
+ NODE_ENV: 'production',
82
+ PORT_API: '3008',
83
+ API_URL: 'http://127.0.0.1:3008',
84
+ },
85
+ },
86
+
87
+ api: {
88
+ root: '.',
89
+ start: 'bun node_modules/@stacksjs/actions/dist/serve/api.js',
90
+ port: 3008,
91
+ preStart: ['bun install --frozen-lockfile'],
92
+ env: {
93
+ HOST: '127.0.0.1',
94
+ APP_ENV: 'production',
95
+ NODE_ENV: 'production',
96
+ },
97
+ },
98
+ },
99
+ }
100
+
101
+ const config: CloudConfig = {}
102
+
103
+ export default config
@@ -0,0 +1,16 @@
1
+ import type { DnsConfig } from '@stacksjs/types'
2
+
3
+ /**
4
+ * Additional DNS records owned by this application.
5
+ *
6
+ * Server records are derived from config/cloud.ts during deployment. Add
7
+ * provider and zone details there before asking Buddy to reconcile DNS.
8
+ */
9
+ export default {
10
+ a: [],
11
+ aaaa: [],
12
+ cname: [],
13
+ mx: [],
14
+ txt: [],
15
+ nameservers: [],
16
+ } satisfies DnsConfig
@@ -0,0 +1,57 @@
1
+ import type { BunPressOptions } from '@stacksjs/bunpress'
2
+ import { env } from '@stacksjs/env'
3
+
4
+ /**
5
+ * **Documentation Configuration**
6
+ *
7
+ * Your app's documentation site, built with BunPress from `docs/`. There is no
8
+ * `docs/` until you create one: add `docs/index.md` and `buddy dev` serves it
9
+ * at /docs, and `buddy deploy` builds it.
10
+ */
11
+
12
+ // APP_URL is often a bare host, which is not a URL on its own.
13
+ const appUrl = String(env.APP_URL || '').replace(/\/+$/, '')
14
+ const siteUrl = appUrl && !/^https?:\/\//.test(appUrl) ? `https://${appUrl}` : appUrl
15
+
16
+ const config: BunPressOptions = {
17
+ verbose: false,
18
+ docsDir: './docs',
19
+ outDir: './dist/docs',
20
+
21
+ nav: [],
22
+
23
+ markdown: {
24
+ title: `${env.APP_NAME || '__APP_NAME__'} Documentation`,
25
+ meta: {
26
+ description: '',
27
+ author: env.APP_NAME || '__APP_NAME__',
28
+ },
29
+ syntaxHighlightTheme: 'github-dark',
30
+ toc: {
31
+ enabled: true,
32
+ minDepth: 2,
33
+ maxDepth: 3,
34
+ },
35
+ sidebar: {
36
+ '/': [
37
+ {
38
+ text: 'Getting Started',
39
+ items: [
40
+ { text: 'Introduction', link: '/' },
41
+ ],
42
+ },
43
+ ],
44
+ },
45
+ },
46
+
47
+ sitemap: {
48
+ enabled: Boolean(siteUrl),
49
+ baseUrl: siteUrl ? `${siteUrl}/docs` : '',
50
+ },
51
+
52
+ robots: {
53
+ enabled: true,
54
+ },
55
+ }
56
+
57
+ export default config
@@ -0,0 +1,27 @@
1
+ import type { EmailConfig } from '@stacksjs/types'
2
+ import { env } from '@stacksjs/env'
3
+
4
+ export default {
5
+ from: {
6
+ name: env.MAIL_FROM_NAME || '__APP_NAME__',
7
+ address: env.MAIL_FROM_ADDRESS || 'hello@example.com',
8
+ },
9
+
10
+ domain: env.MAIL_DOMAIN || env.APP_DOMAIN || 'example.com',
11
+ mailboxes: [],
12
+ forwards: {},
13
+ url: env.APP_URL || '__APP_SLUG__.localhost',
14
+ charset: 'UTF-8',
15
+
16
+ server: {
17
+ // Explicit opt-in: a generated application must never reconcile the
18
+ // framework repository's shared mail server or mailboxes.
19
+ enabled: false,
20
+ scan: true,
21
+ subdomain: 'mail',
22
+ // Gmail Postmaster Tools shows how Gmail receives your mail. Add your
23
+ // domain at https://postmaster.google.com, paste the TXT value it shows,
24
+ // and every deploy keeps it published.
25
+ // postmaster: { google: 'google-site-verification=...' },
26
+ },
27
+ } satisfies EmailConfig
@@ -0,0 +1,64 @@
1
+ import type { LibraryConfig } from '@stacksjs/types'
2
+
3
+ /**
4
+ * **Library Configuration**
5
+ *
6
+ * This configuration defines all of your library options. Because Stacks is fully-typed, you
7
+ * may hover any of the options below and the definitions will be provided. In case you
8
+ * have any questions, feel free to reach out via Discord or GitHub Discussions.
9
+ */
10
+ export default {
11
+ name: '__APP_SLUG__',
12
+ // The npm scope your packages publish under, e.g. 'your-org'. Set it before
13
+ // `buddy libs:publish`; the @ prefix is added automatically.
14
+ owner: null,
15
+ // `owner/name` on GitHub; fills in each package's repository, homepage and bugs links.
16
+ repository: '',
17
+ license: 'MIT',
18
+ author: '',
19
+ contributors: [],
20
+ defaultLanguage: 'en',
21
+ releaseable: true,
22
+
23
+ /**
24
+ * One `resources/` tree, any number of npm packages.
25
+ *
26
+ * Each entry claims a slice of `resources/functions` or `resources/components`
27
+ * by glob and becomes its own package: its own name, manifest, dist and
28
+ * version. Slices may overlap, so a component can ship in a bundle package
29
+ * and in a focused one. `buddy libs` prints what each package resolved to,
30
+ * `buddy build:libs` builds them all, `buddy libs:publish` publishes them.
31
+ */
32
+ packages: [
33
+ {
34
+ name: '__APP_SLUG__-fx',
35
+ kind: 'functions',
36
+ description: 'Your function library description.',
37
+ keywords: ['functions', 'composables', 'library', 'typescript', 'javascript'],
38
+ include: ['*.ts'],
39
+ // These composables call stx's ambient globals (`state`, `useDark`),
40
+ // which no module exports. Saying so here is what lets them ship: the
41
+ // build otherwise refuses them, because a consumer importing the
42
+ // published package would hit `ReferenceError: state is not defined`.
43
+ runtime: 'stx',
44
+ },
45
+
46
+ {
47
+ name: '__APP_SLUG__-components',
48
+ kind: 'components',
49
+ description: 'Your STX component library, as tree-shakeable modules.',
50
+ keywords: ['components', 'custom-elements', 'stx', 'library', 'typescript'],
51
+ prefix: '__APP_TAG_PREFIX__',
52
+ include: ['*.stx'],
53
+ },
54
+
55
+ {
56
+ name: '__APP_SLUG__-elements',
57
+ kind: 'web-components',
58
+ description: 'Your framework agnostic web component library description.',
59
+ keywords: ['custom-elements', 'web-components', 'library', 'framework-agnostic', 'typescript', 'javascript'],
60
+ prefix: '__APP_TAG_PREFIX__',
61
+ include: ['*.stx'],
62
+ },
63
+ ],
64
+ } satisfies LibraryConfig
@@ -0,0 +1,42 @@
1
+ import type { LintConfig } from '@stacksjs/types'
2
+
3
+ /**
4
+ * Lint configuration.
5
+ *
6
+ * Code style lives in `config/code-style.ts` (pickier's own options). This file
7
+ * holds the checks Stacks runs on top of it - today, the stx conformance
8
+ * checks behind `buddy lint --stx`.
9
+ *
10
+ * The baselines are a ratchet. A number is a DEBT, not a target: going above
11
+ * one fails, and dropping below one is reported too, so a cleared violation has
12
+ * to be recorded rather than quietly banked. Every non-zero entry names what
13
+ * clears it.
14
+ */
15
+ const config: LintConfig = {
16
+ stx: {
17
+ baselines: {
18
+ 'comment-landmine': 0,
19
+ 'dist-component-error': 0,
20
+ 'dist-layout-published': 0,
21
+ 'dist-path-leak': 0,
22
+ 'script-tag-balance': 0,
23
+ 'strict-lint': 0,
24
+ 'unmanaged-timer': 0,
25
+ 'style-block': 0,
26
+ 'dom-guard': 0,
27
+ 'inline-style-attr': 0,
28
+ 'plain-internal-anchor': 0,
29
+
30
+ // config/ui.ts sets neither `strict` nor `pagesDir`. Pinning pagesDir
31
+ // changes how stx resolves topology, so it wants its own dev+build pass
32
+ // rather than being swept in with a lint change.
33
+ 'stx-config-keys': 2,
34
+
35
+ // resources/emails/order-confirmation.stx is a whole email document, so
36
+ // it owns its DOCTYPE.
37
+ 'doctype-no-nolayout': 1,
38
+ },
39
+ },
40
+ }
41
+
42
+ export default config
@@ -0,0 +1,27 @@
1
+ import type { MobileConfig } from '@stacksjs/types'
2
+ import { env } from '@stacksjs/env'
3
+
4
+ export default {
5
+ ios: {
6
+ appName: env.APP_NAME ?? '__APP_NAME__',
7
+ // A placeholder the App Store rejects on purpose: replace it with your own
8
+ // reverse-DNS id before the first build you mean to ship.
9
+ bundleId: '__APP_BUNDLE_ID__',
10
+ version: '1.0.0',
11
+ buildNumber: '1',
12
+ deploymentTarget: '16.0',
13
+ url: env.APP_URL,
14
+ darkMode: true,
15
+ backgroundColor: '#0a0a0a',
16
+ orientations: ['portrait'],
17
+ capabilities: {
18
+ haptics: true,
19
+ share: true,
20
+ secureStorage: true,
21
+ clipboard: true,
22
+ localDatabase: true,
23
+ screenCapture: true,
24
+ pdfViewer: true,
25
+ },
26
+ },
27
+ } satisfies MobileConfig
@@ -0,0 +1,6 @@
1
+ import type { Team } from '@stacksjs/types'
2
+
3
+ export default {
4
+ name: '__APP_NAME__',
5
+ members: {},
6
+ } satisfies Team
@@ -0,0 +1,65 @@
1
+ <script server>
2
+ /**
3
+ * The starter page `buddy new` gives every app, at `/`.
4
+ *
5
+ * Everything that names the site comes from the app's own config: the title
6
+ * from APP_NAME and the canonical URL from APP_URL (via config/app.ts), so a
7
+ * deploy of an untouched app describes itself rather than anyone else.
8
+ * Replace this file with your own homepage.
9
+ */
10
+ import { config } from '@stacksjs/config'
11
+
12
+ const appName = config.app.name || 'My App'
13
+
14
+ // APP_URL is often a bare host (`my-app.localhost`), which is not a valid
15
+ // canonical href on its own.
16
+ const rawUrl = String(config.app.url || '').replace(/\/+$/, '')
17
+ const siteUrl = rawUrl && !/^https?:\/\//.test(rawUrl) ? `https://${rawUrl}` : rawUrl
18
+ const canonicalUrl = siteUrl ? `${siteUrl}/` : ''
19
+
20
+ const pageDescription = `${appName} is up and running.`
21
+
22
+ const nextSteps = [
23
+ { icon: 'i-hugeicons-file-edit', title: 'Edit this page', path: 'resources/views/index.stx' },
24
+ { icon: 'i-hugeicons-database-01', title: 'Define a model', path: 'app/Models' },
25
+ { icon: 'i-hugeicons-route-01', title: 'Add an API route', path: 'routes/api.ts' },
26
+ { icon: 'i-hugeicons-settings-02', title: 'Configure the app', path: 'config/app.ts' },
27
+ ]
28
+ </script>
29
+
30
+ @nolayout
31
+ <!DOCTYPE html>
32
+ <html lang="en">
33
+ <head>
34
+ <meta charset="utf-8">
35
+ <meta name="viewport" content="width=device-width, initial-scale=1">
36
+ <title>{{ appName }}</title>
37
+ <meta name="description" content="{{ pageDescription }}">
38
+ @if(canonicalUrl)
39
+ <link rel="canonical" href="{{ canonicalUrl }}">
40
+ @endif
41
+ </head>
42
+ <body class="min-h-screen antialiased text-neutral-900 dark:text-neutral-100 bg-neutral-50 dark:bg-neutral-950">
43
+ <main class="flex flex-col justify-center mx-auto px-6 py-16 max-w-2xl min-h-screen">
44
+ <p class="font-medium text-neutral-500 text-sm dark:text-neutral-400">Your app is running</p>
45
+ <h1 class="mt-2 font-semibold text-4xl tracking-tight">{{ appName }}</h1>
46
+ <p class="mt-4 text-neutral-600 dark:text-neutral-300">
47
+ Start with <code class="px-1.5 py-0.5 text-sm bg-neutral-200 dark:bg-neutral-800 rounded">./buddy dev</code>, then make it yours.
48
+ </p>
49
+
50
+ <ul class="mt-10 border-neutral-200 border-y divide-neutral-200 divide-y dark:border-neutral-800 dark:divide-neutral-800">
51
+ @foreach(nextSteps as step)
52
+ <li class="flex gap-4 items-center py-4">
53
+ <span class="{{ step.icon }} size-5 text-neutral-500 dark:text-neutral-400" aria-hidden="true"></span>
54
+ <span class="font-medium">{{ step.title }}</span>
55
+ <code class="ml-auto text-neutral-500 text-sm dark:text-neutral-400">{{ step.path }}</code>
56
+ </li>
57
+ @endforeach
58
+ </ul>
59
+
60
+ <p class="mt-10 text-neutral-500 text-sm dark:text-neutral-400">
61
+ <a class="underline underline-offset-4 hover:text-neutral-900 dark:hover:text-neutral-100" href="https://stacksjs.com/docs">Read the Stacks docs</a>
62
+ </p>
63
+ </main>
64
+ </body>
65
+ </html>