@stacksjs/defaults 0.75.9 → 0.75.10
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/app/Actions/SitemapAction.ts +43 -11
- package/bootstrap.ts +14 -16
- package/ide/vscode/package.json +1 -1
- package/package.json +2 -2
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
|
-
import {
|
|
2
|
+
import { resolveDefaultsResources } from '@stacksjs/actions/dev/defaults-resources'
|
|
3
|
+
import { config, feature, overridesReady, resolveViewPatterns } from '@stacksjs/config'
|
|
3
4
|
import { db } from '@stacksjs/database/runtime'
|
|
4
|
-
import { response } from '@stacksjs/router'
|
|
5
|
+
import { mountedDefaultRouteBundles, response } from '@stacksjs/router'
|
|
5
6
|
import { existsSync, readdirSync, statSync } from 'node:fs'
|
|
6
|
-
import { join } from 'node:path'
|
|
7
|
+
import { join, resolve, sep } from 'node:path'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Generate the top-level sitemap.xml.
|
|
10
11
|
*
|
|
11
12
|
* Strategy:
|
|
12
|
-
* 1. Walk
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* 1. Walk the view roots the views server serves - the app's views, and
|
|
14
|
+
* whichever framework default views `ui.defaultViews` and the mounted
|
|
15
|
+
* route bundles leave in (`resolveViewPatterns`) - collecting static
|
|
16
|
+
* pages: file paths that don't contain `[` (dynamic) or `[...]`
|
|
17
|
+
* (catch-all). The static set covers the home page, /about, /faq,
|
|
18
|
+
* /products listing, etc. This walked the whole defaults tree whatever
|
|
19
|
+
* the app had turned off, so a sitemap could advertise a page the
|
|
20
|
+
* server answers 404 for.
|
|
16
21
|
* 2. For dynamic patterns (e.g. resources/views/products/[slug].stx)
|
|
17
22
|
* fan out using the database — currently the storefront's
|
|
18
23
|
* `products` table by `slug`. Apps with other dynamic models
|
|
@@ -30,7 +35,6 @@ import { join } from 'node:path'
|
|
|
30
35
|
*/
|
|
31
36
|
|
|
32
37
|
const PROJECT_VIEWS = 'resources/views'
|
|
33
|
-
const FRAMEWORK_VIEWS = 'storage/framework/defaults/resources/views'
|
|
34
38
|
|
|
35
39
|
// Path prefixes that should never be indexed. Matched as a literal
|
|
36
40
|
// prefix on the URL path (so '/cart' matches '/cart' and '/cart/x').
|
|
@@ -151,12 +155,40 @@ async function discoverProducts(): Promise<SitemapEntry[]> {
|
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
157
|
|
|
154
|
-
|
|
158
|
+
/**
|
|
159
|
+
* The view roots the views server serves, and the files under them it does
|
|
160
|
+
* not, resolved exactly as `dev/views.ts` and `production-server.ts` resolve
|
|
161
|
+
* them. Package view roots are left out: this sitemap never listed them.
|
|
162
|
+
*/
|
|
163
|
+
async function servedViewRoots(): Promise<{ roots: string[], isExcluded: (file: string) => boolean }> {
|
|
164
|
+
await overridesReady
|
|
165
|
+
const resolution = resolveViewPatterns(
|
|
166
|
+
PROJECT_VIEWS,
|
|
167
|
+
join(resolveDefaultsResources(), 'views'),
|
|
168
|
+
config.ui?.defaultViews,
|
|
169
|
+
existsSync,
|
|
170
|
+
[],
|
|
171
|
+
mountedDefaultRouteBundles(feature),
|
|
172
|
+
)
|
|
173
|
+
const excluded = resolution.exclude.map(entry => resolve(entry))
|
|
174
|
+
return {
|
|
175
|
+
roots: resolution.patterns,
|
|
176
|
+
isExcluded: (file) => {
|
|
177
|
+
const full = resolve(file)
|
|
178
|
+
return excluded.some(entry => full === entry || full.startsWith(`${entry}${sep}`))
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function staticEntries(): Promise<SitemapEntry[]> {
|
|
155
184
|
const seen = new Set<string>()
|
|
156
185
|
const entries: SitemapEntry[] = []
|
|
186
|
+
const { roots, isExcluded: isWithheld } = await servedViewRoots()
|
|
157
187
|
|
|
158
|
-
for (const root of
|
|
188
|
+
for (const root of roots) {
|
|
159
189
|
for (const file of walkViews(root)) {
|
|
190
|
+
if (isWithheld(file))
|
|
191
|
+
continue
|
|
160
192
|
const urlPath = fileToUrl(root, file)
|
|
161
193
|
if (!urlPath)
|
|
162
194
|
continue
|
|
@@ -223,7 +255,7 @@ export default new Action({
|
|
|
223
255
|
async handle() {
|
|
224
256
|
const siteUrl = (config as any).app?.url || 'http://localhost:3000'
|
|
225
257
|
const all: SitemapEntry[] = [
|
|
226
|
-
...staticEntries(),
|
|
258
|
+
...(await staticEntries()),
|
|
227
259
|
...(await discoverProducts()),
|
|
228
260
|
]
|
|
229
261
|
|
package/bootstrap.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
import { feature } from '@stacksjs/config'
|
|
25
25
|
import { frameworkPath } from '@stacksjs/path'
|
|
26
|
-
import { route } from '@stacksjs/router'
|
|
26
|
+
import { bundleMounts, DEFAULT_ROUTE_BUNDLE_FEATURES, route } from '@stacksjs/router'
|
|
27
27
|
import MaintenanceMiddleware from './app/Middleware/Maintenance'
|
|
28
28
|
|
|
29
29
|
// Global maintenance / coming-soon gate. Registered first so the
|
|
@@ -59,18 +59,16 @@ const selection = (globalThis as Record<string, unknown>).__stacksDefaultRouteBu
|
|
|
59
59
|
{ bundles: Set<string>, explicit: boolean } | undefined
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Whether a bundle mounts
|
|
62
|
+
* Whether a bundle mounts: named by the app, or, when the app named none, its
|
|
63
|
+
* feature flag is on.
|
|
63
64
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* precisely as they did before.
|
|
65
|
+
* The rule and the flag each bundle rides on live in the router
|
|
66
|
+
* (`bundleMounts`, `DEFAULT_ROUTE_BUNDLE_FEATURES`), because the views server
|
|
67
|
+
* asks the same question from another process - it serves `/login` only when
|
|
68
|
+
* this file mounts `POST /login` - and two copies of one rule drift.
|
|
69
69
|
*/
|
|
70
|
-
function mounts(bundle:
|
|
71
|
-
|
|
72
|
-
return false
|
|
73
|
-
return selection?.explicit ? true : featureEnabled
|
|
70
|
+
function mounts(bundle: keyof typeof DEFAULT_ROUTE_BUNDLE_FEATURES): boolean {
|
|
71
|
+
return bundleMounts(selection, bundle, feature(DEFAULT_ROUTE_BUNDLE_FEATURES[bundle]))
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
// Auth: login, registration, logout, refresh/revoke, passkeys, TOTP 2FA and
|
|
@@ -88,7 +86,7 @@ function mounts(bundle: string, featureEnabled: boolean): boolean {
|
|
|
88
86
|
// `/generate-two-factor-secret`, `/logout-all` and `/auth/tokens` — in apps
|
|
89
87
|
// currently running with `dashboard` off. Widening an app's public surface on
|
|
90
88
|
// upgrade is not something a refactor gets to do silently.
|
|
91
|
-
if (mounts('auth'
|
|
89
|
+
if (mounts('auth'))
|
|
92
90
|
await route.register(frameworkPath('defaults/routes/auth.ts'))
|
|
93
91
|
|
|
94
92
|
// The rest of dashboard.ts: email subscribe, storefront cart/checkout,
|
|
@@ -100,7 +98,7 @@ if (mounts('auth', feature('dashboard')))
|
|
|
100
98
|
// Apps that need only a slice can also define the routes they want directly
|
|
101
99
|
// in `routes/api.ts` — first-registration-wins means the user version takes
|
|
102
100
|
// priority.
|
|
103
|
-
if (mounts('dashboard'
|
|
101
|
+
if (mounts('dashboard')) {
|
|
104
102
|
await route.register(frameworkPath('defaults/routes/dashboard.ts'))
|
|
105
103
|
// JSON endpoints for the dev dashboard UI. Kept separate from the view
|
|
106
104
|
// routes above so the data layer is one obvious file to grep.
|
|
@@ -137,21 +135,21 @@ if (mounts('dashboard', feature('dashboard'))) {
|
|
|
137
135
|
// non-default mount path register their own routes in `routes/api.ts`
|
|
138
136
|
// and the framework's mount silently no-ops since user routes
|
|
139
137
|
// register first.
|
|
140
|
-
if (mounts('email'
|
|
138
|
+
if (mounts('email')) {
|
|
141
139
|
await route.register(frameworkPath('defaults/routes/email.ts'))
|
|
142
140
|
}
|
|
143
141
|
|
|
144
142
|
// Form-builder public endpoints (`@stacksjs/forms`). Gated on the `forms`
|
|
145
143
|
// feature, which defaults OFF - an app opts in with config/forms.ts
|
|
146
144
|
// (`buddy forms:install`) and only then do the public submit routes exist.
|
|
147
|
-
if (mounts('forms'
|
|
145
|
+
if (mounts('forms')) {
|
|
148
146
|
await route.register(frameworkPath('defaults/routes/forms.ts'))
|
|
149
147
|
}
|
|
150
148
|
|
|
151
149
|
// Courier delivery endpoints: position ingest and the stop/route lifecycle a
|
|
152
150
|
// courier's device drives. Gated with the rest of the commerce surface, since
|
|
153
151
|
// they are meaningless without the delivery models behind them.
|
|
154
|
-
if (mounts('delivery'
|
|
152
|
+
if (mounts('delivery')) {
|
|
155
153
|
await route.register(frameworkPath('defaults/routes/delivery.ts'))
|
|
156
154
|
}
|
|
157
155
|
|
package/ide/vscode/package.json
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.10",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/stacksjs/stacks.git",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@iconify-json/f7": "^1.2.2",
|
|
57
57
|
"@iconify-json/hugeicons": "^1.2.27",
|
|
58
|
-
"@stacksjs/mobile": "^0.75.
|
|
58
|
+
"@stacksjs/mobile": "^0.75.10",
|
|
59
59
|
"@stacksjs/sanitizer": "^0.2.113",
|
|
60
60
|
"ts-qr-codes": "^0.1.8"
|
|
61
61
|
}
|