@nordcraft/ssr 1.0.21 → 1.0.23
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/dist/rendering/formulaContext.d.ts +4 -2
- package/dist/rendering/formulaContext.js.map +1 -1
- package/dist/routing/routing.d.ts +9 -8
- package/dist/routing/routing.js +45 -21
- package/dist/routing/routing.js.map +1 -1
- package/dist/ssr.types.d.ts +3 -0
- package/package.json +3 -3
- package/src/rendering/formulaContext.ts +4 -2
- package/src/routing/routing.test.ts +126 -1
- package/src/routing/routing.ts +69 -27
- package/src/ssr.types.ts +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PageComponent, PageRoute } from '@nordcraft/core/dist/component/component.types';
|
|
2
2
|
import type { FormulaContext, ToddleServerEnv } from '@nordcraft/core/dist/formula/formula';
|
|
3
|
-
import type { ProjectFiles } from '../ssr.types';
|
|
3
|
+
import type { InstalledPackage, ProjectFiles } from '../ssr.types';
|
|
4
4
|
/**
|
|
5
5
|
* Builds a FormulaContext that can be used to evaluate formulas for a page component
|
|
6
6
|
* It also initializes data->Variables with their initial values based on the FormulaContext
|
|
@@ -14,7 +14,9 @@ export declare const getPageFormulaContext: ({ branchName, component, req, logEr
|
|
|
14
14
|
}) => FormulaContext & {
|
|
15
15
|
env: ToddleServerEnv;
|
|
16
16
|
};
|
|
17
|
-
export declare const getServerToddleObject: (files: ProjectFiles
|
|
17
|
+
export declare const getServerToddleObject: (files: Pick<ProjectFiles, "formulas" | "packages"> & {
|
|
18
|
+
packages?: Record<string, Pick<InstalledPackage, "formulas">>;
|
|
19
|
+
}) => FormulaContext["toddle"];
|
|
18
20
|
export declare const getDataUrlParameters: ({ route, req, }: {
|
|
19
21
|
route: Pick<PageRoute, "path" | "query">;
|
|
20
22
|
req: Request;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formulaContext.js","sourceRoot":"","sources":["../../src/rendering/formulaContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AASrE,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAA;AAEnE,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAA;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAA;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAA;AAC3D,OAAO,KAAK,WAAW,MAAM,kCAAkC,CAAA;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AAElD;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,EACpC,UAAU,EACV,SAAS,EACT,GAAG,EACH,SAAS,EACT,KAAK,GAON,EAA6C,EAAE;IAC9C,MAAM,GAAG,GAAG,SAAS,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAA;IACrD,MAAM,EAAE,wBAAwB,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,aAAa,CAC3E,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,CAChC,CAAA;IACD,MAAM,cAAc,GAA8C;QAChE,IAAI,EAAE;YACJ,QAAQ,EAAE;gBACR,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,EAAE;gBAC1B,IAAI,EAAE,GAAG,CAAC,QAAQ;gBAClB,MAAM,EAAE,cAAc;gBACtB,KAAK,EAAE,wBAAwB;gBAC/B,IAAI;aACL;YACD,UAAU,EAAE,cAAc;YAC1B,2EAA2E;YAC3E,gFAAgF;YAChF,+BAA+B;YAC/B,gBAAgB,EAAE,oBAAoB,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;YACvE,IAAI,EAAE,EAAyB;SAChC;QACD,SAAS;QACT,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,SAAS;QAClB,GAAG;QACH,MAAM,EAAE,qBAAqB,CAAC,KAAK,CAAC;KACrC,CAAA;IACD,cAAc,CAAC,IAAI,CAAC,SAAS,GAAG,SAAS,CACvC,SAAS,CAAC,SAAS,EACnB,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE;QACnB,OAAO,YAAY,CAAC,YAAY,EAAE,cAAc,CAAC,CAAA;IACnD,CAAC,CACF,CAAA;IACD,OAAO,cAAc,CAAA;AACvB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,
|
|
1
|
+
{"version":3,"file":"formulaContext.js","sourceRoot":"","sources":["../../src/rendering/formulaContext.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PageComponent, PageRoute, RouteDeclaration } from '@nordcraft/core/dist/component/component.types';
|
|
2
|
-
import type { ToddleEnv } from '@nordcraft/core/dist/formula/formula';
|
|
2
|
+
import type { FormulaContext, ToddleEnv } from '@nordcraft/core/dist/formula/formula';
|
|
3
3
|
import type { ProjectFiles, Route } from '../ssr.types';
|
|
4
4
|
export declare const matchPageForUrl: ({ url, components, }: {
|
|
5
5
|
url: URL;
|
|
@@ -7,19 +7,20 @@ export declare const matchPageForUrl: ({ url, components, }: {
|
|
|
7
7
|
route?: RouteDeclaration | null;
|
|
8
8
|
}>>;
|
|
9
9
|
}) => PageComponent | undefined;
|
|
10
|
-
export declare const matchRouteForUrl:
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export declare const matchRouteForUrl: ({ env, req, routes, serverContext, url, }: {
|
|
11
|
+
env: ToddleEnv;
|
|
12
|
+
req: Request;
|
|
13
|
+
routes?: Record<string, Route>;
|
|
14
|
+
serverContext: FormulaContext["toddle"];
|
|
13
15
|
url: URL;
|
|
14
|
-
|
|
15
|
-
}) => T | undefined;
|
|
16
|
+
}) => Route | undefined;
|
|
16
17
|
export declare const matchRoutes: <T>({ url, entries, getRoute, }: {
|
|
17
18
|
url: URL;
|
|
18
19
|
entries: T[];
|
|
19
20
|
getRoute: (entry: T) => Pick<PageRoute, "path" | "query">;
|
|
20
21
|
}) => T | undefined;
|
|
21
|
-
export declare const getRouteDestination: ({
|
|
22
|
-
|
|
22
|
+
export declare const getRouteDestination: ({ serverContext, req, route, env, }: {
|
|
23
|
+
serverContext: FormulaContext["toddle"];
|
|
23
24
|
req: Request;
|
|
24
25
|
route: Route;
|
|
25
26
|
env: ToddleEnv;
|
package/dist/routing/routing.js
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import { getUrl } from '@nordcraft/core/dist/api/api';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { applyFormula } from '@nordcraft/core/dist/formula/formula';
|
|
3
|
+
import { isDefined, toBoolean } from '@nordcraft/core/dist/utils/util';
|
|
4
|
+
import { getParameters } from '../rendering/formulaContext';
|
|
4
5
|
export const matchPageForUrl = ({ url, components, }) => matchRoutes({
|
|
5
6
|
url,
|
|
6
7
|
entries: getPages(components),
|
|
7
8
|
getRoute: (route) => route.route,
|
|
8
9
|
});
|
|
9
|
-
export const matchRouteForUrl = ({
|
|
10
|
+
export const matchRouteForUrl = ({ env, req, routes, serverContext, url, }) => matchRoutes({
|
|
10
11
|
url,
|
|
11
|
-
entries: Object.values(routes ?? {})
|
|
12
|
+
entries: Object.values(routes ?? {}).filter((route) => {
|
|
13
|
+
if (!isDefined(route.enabled)) {
|
|
14
|
+
// If the route does not have an explicit enabled property, we assume it is enabled
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
// Only include routes that are enabled
|
|
18
|
+
const formulaContext = getRouteFormulaContext({
|
|
19
|
+
env,
|
|
20
|
+
req,
|
|
21
|
+
route,
|
|
22
|
+
serverContext,
|
|
23
|
+
});
|
|
24
|
+
return toBoolean(applyFormula(route.enabled.formula, formulaContext));
|
|
25
|
+
}),
|
|
12
26
|
getRoute: (route) => route.source,
|
|
13
27
|
});
|
|
14
28
|
export const matchRoutes = ({ url, entries, getRoute, }) => {
|
|
@@ -35,27 +49,16 @@ export const matchRoutes = ({ url, entries, getRoute, }) => {
|
|
|
35
49
|
});
|
|
36
50
|
return matches[0];
|
|
37
51
|
};
|
|
38
|
-
export const getRouteDestination = ({
|
|
52
|
+
export const getRouteDestination = ({ serverContext, req, route, env, }) => {
|
|
39
53
|
try {
|
|
40
54
|
const requestUrl = new URL(req.url);
|
|
41
|
-
const
|
|
42
|
-
|
|
55
|
+
const formulaContext = getRouteFormulaContext({
|
|
56
|
+
env,
|
|
43
57
|
req,
|
|
58
|
+
route,
|
|
59
|
+
serverContext,
|
|
44
60
|
});
|
|
45
|
-
const url = getUrl(route.destination,
|
|
46
|
-
// destination formulas should only have access to URL parameters from
|
|
47
|
-
// the route's source definition + global formulas.
|
|
48
|
-
{
|
|
49
|
-
data: {
|
|
50
|
-
Attributes: {},
|
|
51
|
-
'Route parameters': {
|
|
52
|
-
path: pathParams,
|
|
53
|
-
query: searchParamsWithDefaults,
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
toddle: getServerToddleObject(files),
|
|
57
|
-
env,
|
|
58
|
-
},
|
|
61
|
+
const url = getUrl(route.destination, formulaContext,
|
|
59
62
|
// Redirects can redirect to relative URLs - rewrites can't
|
|
60
63
|
route.type === 'redirect' ? requestUrl.origin : undefined);
|
|
61
64
|
if (route.type === 'redirect' &&
|
|
@@ -74,6 +77,27 @@ export const getRouteDestination = ({ files, req, route, env, }) => {
|
|
|
74
77
|
}
|
|
75
78
|
catch { }
|
|
76
79
|
};
|
|
80
|
+
const getRouteFormulaContext = ({ env, req, route, serverContext, }) => {
|
|
81
|
+
const { searchParamsWithDefaults, pathParams } = getParameters({
|
|
82
|
+
route: route.source,
|
|
83
|
+
req,
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
component: undefined,
|
|
87
|
+
// destination formulas should only have access to URL parameters from
|
|
88
|
+
// the route's source definition + global formulas.
|
|
89
|
+
data: {
|
|
90
|
+
Attributes: {},
|
|
91
|
+
'Route parameters': {
|
|
92
|
+
path: pathParams ?? {},
|
|
93
|
+
query: searchParamsWithDefaults,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
env,
|
|
97
|
+
package: undefined,
|
|
98
|
+
toddle: serverContext,
|
|
99
|
+
};
|
|
100
|
+
};
|
|
77
101
|
export const get404Page = (components) => getPages(components).find((page) => page.name === '404');
|
|
78
102
|
const getPages = (components) => Object.values(components).filter((c) => isDefined(c.route));
|
|
79
103
|
export const getPathSegments = (url) => url.pathname
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routing.js","sourceRoot":"","sources":["../../src/routing/routing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"routing.js","sourceRoot":"","sources":["../../src/routing/routing.ts"],"names":[],"mappings":"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"}
|
package/dist/ssr.types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"main": "dist/index.js",
|
|
14
14
|
"types": "dist/index.d.ts",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@nordcraft/core": "1.0.
|
|
17
|
-
"@nordcraft/std-lib": "1.0.
|
|
16
|
+
"@nordcraft/core": "1.0.23",
|
|
17
|
+
"@nordcraft/std-lib": "1.0.23",
|
|
18
18
|
"cookie": "1.0.2",
|
|
19
19
|
"xss": "1.0.15"
|
|
20
20
|
},
|
|
21
|
-
"version": "1.0.
|
|
21
|
+
"version": "1.0.23"
|
|
22
22
|
}
|
|
@@ -14,7 +14,7 @@ import { mapValues } from '@nordcraft/core/dist/utils/collections'
|
|
|
14
14
|
import { isDefined } from '@nordcraft/core/dist/utils/util'
|
|
15
15
|
import * as libFormulas from '@nordcraft/std-lib/dist/formulas'
|
|
16
16
|
import { getPathSegments } from '../routing/routing'
|
|
17
|
-
import type { ProjectFiles } from '../ssr.types'
|
|
17
|
+
import type { InstalledPackage, ProjectFiles } from '../ssr.types'
|
|
18
18
|
import { getRequestCookies } from './cookies'
|
|
19
19
|
import { escapeSearchParameters } from './request'
|
|
20
20
|
|
|
@@ -71,7 +71,9 @@ export const getPageFormulaContext = ({
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
export const getServerToddleObject = (
|
|
74
|
-
files: ProjectFiles,
|
|
74
|
+
files: Pick<ProjectFiles, 'formulas' | 'packages'> & {
|
|
75
|
+
packages?: Record<string, Pick<InstalledPackage, 'formulas'>>
|
|
76
|
+
},
|
|
75
77
|
): FormulaContext['toddle'] => {
|
|
76
78
|
const coreFormulas = Object.fromEntries(
|
|
77
79
|
Object.entries(libFormulas).map(([name, module]) => [
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { RouteDeclaration } from '@nordcraft/core/dist/component/component.types'
|
|
2
|
+
import { valueFormula } from '@nordcraft/core/dist/formula/formulaUtils'
|
|
2
3
|
import { describe, expect, test } from 'bun:test'
|
|
3
|
-
import {
|
|
4
|
+
import { serverEnv } from '../rendering/formulaContext'
|
|
5
|
+
import type { Route } from '../ssr.types'
|
|
6
|
+
import { matchPageForUrl, matchRouteForUrl } from './routing'
|
|
4
7
|
|
|
5
8
|
describe('matchPageForUrl', () => {
|
|
6
9
|
test('it finds the correct page for a url', () => {
|
|
@@ -151,3 +154,125 @@ describe('matchPageForUrl', () => {
|
|
|
151
154
|
expect(matchPageForUrl({ url: docsUrl, components: pages })).toBeUndefined()
|
|
152
155
|
})
|
|
153
156
|
})
|
|
157
|
+
describe('matchRouteForUrl', () => {
|
|
158
|
+
test('it finds the correct route for a url', () => {
|
|
159
|
+
const routes: Record<string, Route> = {
|
|
160
|
+
testRedirect: {
|
|
161
|
+
type: 'redirect',
|
|
162
|
+
source: {
|
|
163
|
+
path: [
|
|
164
|
+
{ type: 'static', name: 'not-docs' },
|
|
165
|
+
{ type: 'param', testValue: '', name: 'slug' },
|
|
166
|
+
],
|
|
167
|
+
query: {},
|
|
168
|
+
},
|
|
169
|
+
destination: {
|
|
170
|
+
url: { type: 'value', value: 'https://google.com' },
|
|
171
|
+
path: {},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
docsRedirect: {
|
|
175
|
+
type: 'redirect',
|
|
176
|
+
source: {
|
|
177
|
+
path: [
|
|
178
|
+
{ type: 'static', name: 'docs' },
|
|
179
|
+
{ type: 'param', testValue: '', name: 'slug' },
|
|
180
|
+
],
|
|
181
|
+
query: {},
|
|
182
|
+
},
|
|
183
|
+
destination: {
|
|
184
|
+
url: { type: 'value', value: 'https://docs.nordcraft.com' },
|
|
185
|
+
path: {
|
|
186
|
+
first: {
|
|
187
|
+
index: 0,
|
|
188
|
+
formula: valueFormula('slug'),
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const docsUrl = new URL('http://localhost:3000/docs/intro')
|
|
196
|
+
const request = new Request(docsUrl)
|
|
197
|
+
expect(
|
|
198
|
+
matchRouteForUrl({
|
|
199
|
+
url: docsUrl,
|
|
200
|
+
routes,
|
|
201
|
+
env: serverEnv({
|
|
202
|
+
branchName: 'main',
|
|
203
|
+
req: request,
|
|
204
|
+
logErrors: false,
|
|
205
|
+
}),
|
|
206
|
+
req: request,
|
|
207
|
+
serverContext: {
|
|
208
|
+
errors: [],
|
|
209
|
+
getFormula: () => undefined,
|
|
210
|
+
getCustomFormula: () => undefined,
|
|
211
|
+
},
|
|
212
|
+
}),
|
|
213
|
+
).toEqual(routes['docsRedirect'])
|
|
214
|
+
})
|
|
215
|
+
test('it ignores disabled routes', () => {
|
|
216
|
+
const routes: Record<string, Route> = {
|
|
217
|
+
disabledRedirect: {
|
|
218
|
+
type: 'redirect',
|
|
219
|
+
enabled: { formula: valueFormula(false) },
|
|
220
|
+
source: {
|
|
221
|
+
path: [
|
|
222
|
+
{ type: 'static', name: 'docs' },
|
|
223
|
+
{ type: 'param', testValue: '', name: 'slug' },
|
|
224
|
+
],
|
|
225
|
+
query: {},
|
|
226
|
+
},
|
|
227
|
+
destination: {
|
|
228
|
+
url: { type: 'value', value: 'https://docs.nordcraft.com' },
|
|
229
|
+
path: {
|
|
230
|
+
first: {
|
|
231
|
+
index: 0,
|
|
232
|
+
formula: valueFormula('slug'),
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
docsRedirect: {
|
|
238
|
+
type: 'redirect',
|
|
239
|
+
source: {
|
|
240
|
+
path: [
|
|
241
|
+
{ type: 'param', testValue: 'docs', name: 'docs' },
|
|
242
|
+
{ type: 'param', testValue: '', name: 'slug' },
|
|
243
|
+
],
|
|
244
|
+
query: {},
|
|
245
|
+
},
|
|
246
|
+
destination: {
|
|
247
|
+
url: { type: 'value', value: 'https://docs.nordcraft.com' },
|
|
248
|
+
path: {
|
|
249
|
+
first: {
|
|
250
|
+
index: 0,
|
|
251
|
+
formula: valueFormula('slug'),
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const docsUrl = new URL('http://localhost:3000/docs/intro')
|
|
259
|
+
const request = new Request(docsUrl)
|
|
260
|
+
expect(
|
|
261
|
+
matchRouteForUrl({
|
|
262
|
+
url: docsUrl,
|
|
263
|
+
routes,
|
|
264
|
+
env: serverEnv({
|
|
265
|
+
branchName: 'main',
|
|
266
|
+
req: request,
|
|
267
|
+
logErrors: false,
|
|
268
|
+
}),
|
|
269
|
+
req: request,
|
|
270
|
+
serverContext: {
|
|
271
|
+
errors: [],
|
|
272
|
+
getFormula: () => undefined,
|
|
273
|
+
getCustomFormula: () => undefined,
|
|
274
|
+
},
|
|
275
|
+
}),
|
|
276
|
+
).toEqual(routes['docsRedirect'])
|
|
277
|
+
})
|
|
278
|
+
})
|
package/src/routing/routing.ts
CHANGED
|
@@ -4,12 +4,13 @@ import type {
|
|
|
4
4
|
PageRoute,
|
|
5
5
|
RouteDeclaration,
|
|
6
6
|
} from '@nordcraft/core/dist/component/component.types'
|
|
7
|
-
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from '
|
|
7
|
+
import type {
|
|
8
|
+
FormulaContext,
|
|
9
|
+
ToddleEnv,
|
|
10
|
+
} from '@nordcraft/core/dist/formula/formula'
|
|
11
|
+
import { applyFormula } from '@nordcraft/core/dist/formula/formula'
|
|
12
|
+
import { isDefined, toBoolean } from '@nordcraft/core/dist/utils/util'
|
|
13
|
+
import { getParameters } from '../rendering/formulaContext'
|
|
13
14
|
import type { ProjectFiles, Route } from '../ssr.types'
|
|
14
15
|
|
|
15
16
|
export const matchPageForUrl = ({
|
|
@@ -25,16 +26,35 @@ export const matchPageForUrl = ({
|
|
|
25
26
|
getRoute: (route) => route.route,
|
|
26
27
|
})
|
|
27
28
|
|
|
28
|
-
export const matchRouteForUrl =
|
|
29
|
-
|
|
29
|
+
export const matchRouteForUrl = ({
|
|
30
|
+
env,
|
|
31
|
+
req,
|
|
30
32
|
routes,
|
|
33
|
+
serverContext,
|
|
34
|
+
url,
|
|
31
35
|
}: {
|
|
36
|
+
env: ToddleEnv
|
|
37
|
+
req: Request
|
|
38
|
+
routes?: Record<string, Route>
|
|
39
|
+
serverContext: FormulaContext['toddle']
|
|
32
40
|
url: URL
|
|
33
|
-
routes?: Record<string, T>
|
|
34
41
|
}) =>
|
|
35
42
|
matchRoutes({
|
|
36
43
|
url,
|
|
37
|
-
entries: Object.values(routes ?? {})
|
|
44
|
+
entries: Object.values(routes ?? {}).filter((route) => {
|
|
45
|
+
if (!isDefined(route.enabled)) {
|
|
46
|
+
// If the route does not have an explicit enabled property, we assume it is enabled
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
// Only include routes that are enabled
|
|
50
|
+
const formulaContext = getRouteFormulaContext({
|
|
51
|
+
env,
|
|
52
|
+
req,
|
|
53
|
+
route,
|
|
54
|
+
serverContext,
|
|
55
|
+
})
|
|
56
|
+
return toBoolean(applyFormula(route.enabled.formula, formulaContext))
|
|
57
|
+
}),
|
|
38
58
|
getRoute: (route) => route.source,
|
|
39
59
|
})
|
|
40
60
|
|
|
@@ -78,12 +98,12 @@ export const matchRoutes = <T>({
|
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
export const getRouteDestination = ({
|
|
81
|
-
|
|
101
|
+
serverContext,
|
|
82
102
|
req,
|
|
83
103
|
route,
|
|
84
104
|
env,
|
|
85
105
|
}: {
|
|
86
|
-
|
|
106
|
+
serverContext: FormulaContext['toddle']
|
|
87
107
|
req: Request
|
|
88
108
|
route: Route
|
|
89
109
|
env: ToddleEnv
|
|
@@ -91,26 +111,16 @@ export const getRouteDestination = ({
|
|
|
91
111
|
try {
|
|
92
112
|
const requestUrl = new URL(req.url)
|
|
93
113
|
|
|
94
|
-
const
|
|
95
|
-
|
|
114
|
+
const formulaContext = getRouteFormulaContext({
|
|
115
|
+
env,
|
|
96
116
|
req,
|
|
117
|
+
route,
|
|
118
|
+
serverContext,
|
|
97
119
|
})
|
|
98
120
|
|
|
99
121
|
const url = getUrl(
|
|
100
122
|
route.destination,
|
|
101
|
-
|
|
102
|
-
// the route's source definition + global formulas.
|
|
103
|
-
{
|
|
104
|
-
data: {
|
|
105
|
-
Attributes: {},
|
|
106
|
-
'Route parameters': {
|
|
107
|
-
path: pathParams,
|
|
108
|
-
query: searchParamsWithDefaults,
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
toddle: getServerToddleObject(files),
|
|
112
|
-
env,
|
|
113
|
-
} as any,
|
|
123
|
+
formulaContext,
|
|
114
124
|
// Redirects can redirect to relative URLs - rewrites can't
|
|
115
125
|
route.type === 'redirect' ? requestUrl.origin : undefined,
|
|
116
126
|
)
|
|
@@ -132,6 +142,38 @@ export const getRouteDestination = ({
|
|
|
132
142
|
} catch {}
|
|
133
143
|
}
|
|
134
144
|
|
|
145
|
+
const getRouteFormulaContext = ({
|
|
146
|
+
env,
|
|
147
|
+
req,
|
|
148
|
+
route,
|
|
149
|
+
serverContext,
|
|
150
|
+
}: {
|
|
151
|
+
env: ToddleEnv
|
|
152
|
+
req: Request
|
|
153
|
+
route: Route
|
|
154
|
+
serverContext: FormulaContext['toddle']
|
|
155
|
+
}): FormulaContext => {
|
|
156
|
+
const { searchParamsWithDefaults, pathParams } = getParameters({
|
|
157
|
+
route: route.source,
|
|
158
|
+
req,
|
|
159
|
+
})
|
|
160
|
+
return {
|
|
161
|
+
component: undefined,
|
|
162
|
+
// destination formulas should only have access to URL parameters from
|
|
163
|
+
// the route's source definition + global formulas.
|
|
164
|
+
data: {
|
|
165
|
+
Attributes: {},
|
|
166
|
+
'Route parameters': {
|
|
167
|
+
path: pathParams ?? {},
|
|
168
|
+
query: searchParamsWithDefaults,
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
env,
|
|
172
|
+
package: undefined,
|
|
173
|
+
toddle: serverContext,
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
135
177
|
export const get404Page = (components: ProjectFiles['components']) =>
|
|
136
178
|
getPages(components).find((page) => page.name === '404')
|
|
137
179
|
|