crelte 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.md +41 -0
- package/dist/Crelte.d.ts +55 -0
- package/dist/Crelte.d.ts.map +1 -0
- package/dist/Crelte.js +106 -0
- package/dist/CrelteBase.d.ts +16 -0
- package/dist/CrelteBase.d.ts.map +1 -0
- package/dist/CrelteBase.js +1 -0
- package/dist/CrelteRouted.d.ts +50 -0
- package/dist/CrelteRouted.d.ts.map +1 -0
- package/dist/CrelteRouted.js +88 -0
- package/dist/blocks/Blocks.d.ts +35 -0
- package/dist/blocks/Blocks.d.ts.map +1 -0
- package/dist/blocks/Blocks.js +100 -0
- package/dist/blocks/Blocks.svelte +21 -0
- package/dist/blocks/Blocks.svelte.d.ts +24 -0
- package/dist/blocks/Blocks.svelte.d.ts.map +1 -0
- package/dist/blocks/index.d.ts +5 -0
- package/dist/blocks/index.d.ts.map +1 -0
- package/dist/blocks/index.js +3 -0
- package/dist/cookies/ClientCookies.d.ts +9 -0
- package/dist/cookies/ClientCookies.d.ts.map +1 -0
- package/dist/cookies/ClientCookies.js +22 -0
- package/dist/cookies/ServerCookies.d.ts +13 -0
- package/dist/cookies/ServerCookies.d.ts.map +1 -0
- package/dist/cookies/ServerCookies.js +31 -0
- package/dist/cookies/index.d.ts +20 -0
- package/dist/cookies/index.d.ts.map +1 -0
- package/dist/cookies/index.js +1 -0
- package/dist/cookies/utils.d.ts +12 -0
- package/dist/cookies/utils.d.ts.map +1 -0
- package/dist/cookies/utils.js +32 -0
- package/dist/graphql/GraphQl.d.ts +60 -0
- package/dist/graphql/GraphQl.d.ts.map +1 -0
- package/dist/graphql/GraphQl.js +197 -0
- package/dist/graphql/gql.test.d.ts +2 -0
- package/dist/graphql/gql.test.d.ts.map +1 -0
- package/dist/graphql/gql.test.js +80 -0
- package/dist/graphql/index.d.ts +3 -0
- package/dist/graphql/index.d.ts.map +1 -0
- package/dist/graphql/index.js +2 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/init/client.d.ts +13 -0
- package/dist/init/client.d.ts.map +1 -0
- package/dist/init/client.js +129 -0
- package/dist/init/server.d.ts +38 -0
- package/dist/init/server.d.ts.map +1 -0
- package/dist/init/server.js +95 -0
- package/dist/init/shared.d.ts +29 -0
- package/dist/init/shared.d.ts.map +1 -0
- package/dist/init/shared.js +154 -0
- package/dist/loadData/Globals.d.ts +33 -0
- package/dist/loadData/Globals.d.ts.map +1 -0
- package/dist/loadData/Globals.js +119 -0
- package/dist/loadData/index.d.ts +25 -0
- package/dist/loadData/index.d.ts.map +1 -0
- package/dist/loadData/index.js +39 -0
- package/dist/plugins/Events.d.ts +11 -0
- package/dist/plugins/Events.d.ts.map +1 -0
- package/dist/plugins/Events.js +29 -0
- package/dist/plugins/Plugins.d.ts +12 -0
- package/dist/plugins/Plugins.d.ts.map +1 -0
- package/dist/plugins/Plugins.js +12 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +2 -0
- package/dist/routing/History.d.ts +22 -0
- package/dist/routing/History.d.ts.map +1 -0
- package/dist/routing/History.js +36 -0
- package/dist/routing/InnerRouter.d.ts +111 -0
- package/dist/routing/InnerRouter.d.ts.map +1 -0
- package/dist/routing/InnerRouter.js +397 -0
- package/dist/routing/PageLoader.d.ts +37 -0
- package/dist/routing/PageLoader.d.ts.map +1 -0
- package/dist/routing/PageLoader.js +72 -0
- package/dist/routing/Route.d.ts +82 -0
- package/dist/routing/Route.d.ts.map +1 -0
- package/dist/routing/Route.js +134 -0
- package/dist/routing/Router.d.ts +162 -0
- package/dist/routing/Router.d.ts.map +1 -0
- package/dist/routing/Router.js +333 -0
- package/dist/routing/Site.d.ts +47 -0
- package/dist/routing/Site.d.ts.map +1 -0
- package/dist/routing/Site.js +48 -0
- package/dist/routing/index.d.ts +5 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +4 -0
- package/dist/ssr/SsrCache.d.ts +12 -0
- package/dist/ssr/SsrCache.d.ts.map +1 -0
- package/dist/ssr/SsrCache.js +50 -0
- package/dist/ssr/SsrComponents.d.ts +7 -0
- package/dist/ssr/SsrComponents.d.ts.map +1 -0
- package/dist/ssr/SsrComponents.js +30 -0
- package/dist/ssr/index.d.ts +4 -0
- package/dist/ssr/index.d.ts.map +1 -0
- package/dist/ssr/index.js +3 -0
- package/package.json +79 -0
- package/src/Crelte.ts +135 -0
- package/src/CrelteBase.ts +24 -0
- package/src/CrelteRouted.ts +128 -0
- package/src/blocks/Blocks.svelte +68 -0
- package/src/blocks/Blocks.ts +155 -0
- package/src/blocks/index.ts +14 -0
- package/src/cookies/ClientCookies.ts +30 -0
- package/src/cookies/ServerCookies.ts +42 -0
- package/src/cookies/index.ts +24 -0
- package/src/cookies/utils.ts +53 -0
- package/src/graphql/GraphQl.ts +281 -0
- package/src/graphql/gql.test.ts +123 -0
- package/src/graphql/index.ts +8 -0
- package/src/index.ts +109 -0
- package/src/init/client.ts +190 -0
- package/src/init/server.ts +177 -0
- package/src/init/shared.ts +221 -0
- package/src/loadData/Globals.ts +150 -0
- package/src/loadData/index.ts +67 -0
- package/src/plugins/Events.ts +50 -0
- package/src/plugins/Plugins.ts +23 -0
- package/src/plugins/index.ts +5 -0
- package/src/routing/History.ts +52 -0
- package/src/routing/InnerRouter.ts +469 -0
- package/src/routing/PageLoader.ts +112 -0
- package/src/routing/Route.ts +184 -0
- package/src/routing/Router.ts +476 -0
- package/src/routing/Site.ts +65 -0
- package/src/routing/index.ts +5 -0
- package/src/ssr/SsrCache.ts +61 -0
- package/src/ssr/SsrComponents.ts +34 -0
- package/src/ssr/index.ts +4 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import SsrCache, { calcKey as ssrCacheCalcKey } from '../ssr/SsrCache.js';
|
|
2
|
+
|
|
3
|
+
export type GraphQlErrorResponse = {
|
|
4
|
+
status?: number;
|
|
5
|
+
body?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// todo improve this
|
|
9
|
+
export class GraphQlError extends Error {
|
|
10
|
+
resp: GraphQlErrorResponse;
|
|
11
|
+
ctx: any;
|
|
12
|
+
|
|
13
|
+
// ctx might be anything
|
|
14
|
+
constructor(resp: GraphQlErrorResponse, ctx: any = null) {
|
|
15
|
+
super();
|
|
16
|
+
|
|
17
|
+
this.resp = resp;
|
|
18
|
+
this.ctx = ctx;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
status(): number {
|
|
22
|
+
return this.resp?.status ?? 500;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
__isGraphQlError__() {}
|
|
26
|
+
|
|
27
|
+
get message(): string {
|
|
28
|
+
return 'GraphqlError: ' + JSON.stringify(this.resp);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type GraphQlOptions = {
|
|
33
|
+
debug?: boolean;
|
|
34
|
+
debugTiming?: boolean;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type GraphQlRequestOptions = {
|
|
38
|
+
path?: string;
|
|
39
|
+
ignoreStatusCode?: boolean;
|
|
40
|
+
previewToken?: string;
|
|
41
|
+
siteToken?: string;
|
|
42
|
+
caching?: boolean;
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
// will be set by the request function
|
|
45
|
+
status?: number;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default class GraphQl {
|
|
49
|
+
private endpoint: string;
|
|
50
|
+
private ssrCache: SsrCache;
|
|
51
|
+
private listeners: Map<
|
|
52
|
+
string,
|
|
53
|
+
[(resp: unknown) => void, (error: unknown) => void][]
|
|
54
|
+
>;
|
|
55
|
+
|
|
56
|
+
private loggingRequests: boolean;
|
|
57
|
+
private loggingTiming: boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a new GraphQL
|
|
61
|
+
*
|
|
62
|
+
* @param {Object} [opts={}] opts `{ debug: false, debugTiming: false }`
|
|
63
|
+
*/
|
|
64
|
+
constructor(
|
|
65
|
+
endpoint: string,
|
|
66
|
+
ssrCache: SsrCache,
|
|
67
|
+
opts: GraphQlOptions = {},
|
|
68
|
+
) {
|
|
69
|
+
this.endpoint = endpoint;
|
|
70
|
+
this.ssrCache = ssrCache;
|
|
71
|
+
this.listeners = new Map();
|
|
72
|
+
|
|
73
|
+
this.loggingRequests = opts?.debug ?? false;
|
|
74
|
+
this.loggingTiming = opts?.debugTiming ?? false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// returns {} or throws
|
|
78
|
+
// options: {ignoreStatusCode, previewToken, caching, headers}
|
|
79
|
+
async request(
|
|
80
|
+
query: string,
|
|
81
|
+
variables: Record<string, unknown> = {},
|
|
82
|
+
options: GraphQlRequestOptions = {},
|
|
83
|
+
): Promise<unknown> {
|
|
84
|
+
let key;
|
|
85
|
+
if (options?.caching ?? true) {
|
|
86
|
+
key = await ssrCacheCalcKey({ query, variables });
|
|
87
|
+
|
|
88
|
+
const inCache = this.ssrCache.get(key);
|
|
89
|
+
if (inCache) return inCache;
|
|
90
|
+
|
|
91
|
+
// check if a listeners exists meaning the same request is already
|
|
92
|
+
// in progress
|
|
93
|
+
const listeners = this.listeners.get(key);
|
|
94
|
+
if (listeners) {
|
|
95
|
+
return new Promise((resolve, error) => {
|
|
96
|
+
listeners.push([resolve, error]);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// else setup the listener
|
|
101
|
+
this.listeners.set(key, []);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const resp = await this.rawRequest(query, variables, options);
|
|
106
|
+
if (key) {
|
|
107
|
+
this.ssrCache.set(key, resp);
|
|
108
|
+
|
|
109
|
+
// ! because the listeners get's in the previous if statement and will
|
|
110
|
+
// always be set when the key is set
|
|
111
|
+
const listeners = this.listeners.get(key)!;
|
|
112
|
+
listeners.forEach(([resolve, _error]) => {
|
|
113
|
+
resolve(resp);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
this.listeners.delete(key);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return resp;
|
|
120
|
+
} catch (e: unknown) {
|
|
121
|
+
if (key) {
|
|
122
|
+
// ! because the listeners get's in the previous if statement and will
|
|
123
|
+
// always be set when the key is set
|
|
124
|
+
const listeners = this.listeners.get(key)!;
|
|
125
|
+
listeners.forEach(([_resolve, error]) => {
|
|
126
|
+
error(e);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
this.listeners.delete(key);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
throw e;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// status will be set in opts
|
|
137
|
+
private async rawRequest(
|
|
138
|
+
query: string,
|
|
139
|
+
variables: Record<string, unknown> = {},
|
|
140
|
+
opts: GraphQlRequestOptions = {},
|
|
141
|
+
) {
|
|
142
|
+
opts.ignoreStatusCode = opts.ignoreStatusCode ?? false;
|
|
143
|
+
|
|
144
|
+
let url = this.endpoint;
|
|
145
|
+
|
|
146
|
+
if (opts?.previewToken) url += '?token=' + opts.previewToken;
|
|
147
|
+
else if (opts?.siteToken) url += '?siteToken=' + opts.siteToken;
|
|
148
|
+
|
|
149
|
+
const headers = opts?.headers ?? {};
|
|
150
|
+
headers['Content-Type'] = 'application/json';
|
|
151
|
+
|
|
152
|
+
if (this.loggingRequests) {
|
|
153
|
+
console.log('query to ', url, variables, opts);
|
|
154
|
+
headers['X-Debug'] = 'enable';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let timing;
|
|
158
|
+
if (this.loggingTiming) timing = Date.now();
|
|
159
|
+
|
|
160
|
+
const resp = await fetch(url, {
|
|
161
|
+
method: 'POST',
|
|
162
|
+
headers,
|
|
163
|
+
body: JSON.stringify({
|
|
164
|
+
query,
|
|
165
|
+
variables,
|
|
166
|
+
}),
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (opts.ignoreStatusCode) {
|
|
170
|
+
opts.status = resp.status;
|
|
171
|
+
} else if (!resp.ok) {
|
|
172
|
+
throw new GraphQlError(
|
|
173
|
+
{
|
|
174
|
+
status: resp.status,
|
|
175
|
+
body: await resp.text(),
|
|
176
|
+
},
|
|
177
|
+
'resp not ok',
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (resp.headers.get('x-debug-link'))
|
|
182
|
+
console.log('Debug link', resp.headers.get('x-debug-link'));
|
|
183
|
+
|
|
184
|
+
if (timing) {
|
|
185
|
+
console.log(
|
|
186
|
+
'request ' +
|
|
187
|
+
opts.path +
|
|
188
|
+
' vars: ' +
|
|
189
|
+
JSON.stringify(variables) +
|
|
190
|
+
' took ' +
|
|
191
|
+
(Date.now() - timing) +
|
|
192
|
+
'ms',
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
let jsonResp;
|
|
197
|
+
try {
|
|
198
|
+
jsonResp = await resp.json();
|
|
199
|
+
} catch (e: unknown) {
|
|
200
|
+
throw new GraphQlError(
|
|
201
|
+
{
|
|
202
|
+
status: resp.status,
|
|
203
|
+
},
|
|
204
|
+
e,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if ('errors' in jsonResp) {
|
|
209
|
+
console.log('graphql errors', jsonResp.errors);
|
|
210
|
+
throw new GraphQlError(
|
|
211
|
+
{
|
|
212
|
+
status: resp.status,
|
|
213
|
+
},
|
|
214
|
+
jsonResp.errors,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return jsonResp.data ?? null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export class GraphQlQuery {
|
|
223
|
+
query: string;
|
|
224
|
+
path: string;
|
|
225
|
+
|
|
226
|
+
constructor(query: string, path: string) {
|
|
227
|
+
this.query = query;
|
|
228
|
+
this.path = path;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/// doc hidden
|
|
232
|
+
__GraphQlQuery__() {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Returns true if the passed object is a GraphQlQuery */
|
|
238
|
+
export function isGraphQlQuery(obj: any): obj is GraphQlQuery {
|
|
239
|
+
return (
|
|
240
|
+
typeof obj === 'object' &&
|
|
241
|
+
obj !== null &&
|
|
242
|
+
typeof obj.__GraphQlQuery__ === 'function'
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* @description Create a GraphQL query string with variables.
|
|
248
|
+
* @param strings
|
|
249
|
+
* @param keys
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
*
|
|
253
|
+
* const query = gql`query ($id: ID!) { page(id: $id) { id } }`
|
|
254
|
+
*/
|
|
255
|
+
export function gql(
|
|
256
|
+
strings: TemplateStringsArray | string[] | string,
|
|
257
|
+
...keys: unknown[]
|
|
258
|
+
): GraphQlQuery {
|
|
259
|
+
if (typeof strings === 'string') strings = [strings];
|
|
260
|
+
|
|
261
|
+
let query = '';
|
|
262
|
+
strings.forEach((string, i) => {
|
|
263
|
+
query += string;
|
|
264
|
+
|
|
265
|
+
if (typeof keys[i] !== 'undefined') {
|
|
266
|
+
const variable = keys[i];
|
|
267
|
+
|
|
268
|
+
// nesting support
|
|
269
|
+
if (isGraphQlQuery(variable)) {
|
|
270
|
+
query += variable.query;
|
|
271
|
+
} else if (typeof variable === 'string') {
|
|
272
|
+
query += variable;
|
|
273
|
+
} else {
|
|
274
|
+
console.error('invalid key', variable);
|
|
275
|
+
throw new Error('Invalid key: ' + typeof variable);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return new GraphQlQuery(query, import.meta.url);
|
|
281
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { test, expect, describe } from 'vitest';
|
|
2
|
+
import { gql } from './GraphQl.js';
|
|
3
|
+
|
|
4
|
+
// tests for the gql function
|
|
5
|
+
// testing the query property
|
|
6
|
+
describe('gql: query property', () => {
|
|
7
|
+
test('gql: without variables', () => {
|
|
8
|
+
const query = gql`a bc`;
|
|
9
|
+
|
|
10
|
+
expect(query).toHaveProperty('query', 'a bc');
|
|
11
|
+
expect(query).toHaveProperty('path');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('gql: with one variable', () => {
|
|
15
|
+
const variable = 'var';
|
|
16
|
+
const query = gql`a ${variable} bc`;
|
|
17
|
+
|
|
18
|
+
expect(query).toHaveProperty('query', `a ${variable} bc`);
|
|
19
|
+
expect(query).toHaveProperty('path');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('gql: with multiple variables', () => {
|
|
23
|
+
const variable1 = 'var1';
|
|
24
|
+
const variable2 = 'var2';
|
|
25
|
+
const variable3 = 'var3';
|
|
26
|
+
|
|
27
|
+
const query = gql`a ${variable1} bc ${variable2} def ${variable3}`;
|
|
28
|
+
|
|
29
|
+
expect(query).toHaveProperty(
|
|
30
|
+
'query',
|
|
31
|
+
`a ${variable1} bc ${variable2} def ${variable3}`,
|
|
32
|
+
);
|
|
33
|
+
expect(query).toHaveProperty('path');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('gql: beginning with variable', () => {
|
|
37
|
+
const variable1 = 'var1';
|
|
38
|
+
const variable2 = 'var2';
|
|
39
|
+
const variable3 = 'var3';
|
|
40
|
+
|
|
41
|
+
const query = gql`${variable1} abc ${variable2} def ${variable3}`;
|
|
42
|
+
|
|
43
|
+
expect(query).toHaveProperty(
|
|
44
|
+
'query',
|
|
45
|
+
`${variable1} abc ${variable2} def ${variable3}`,
|
|
46
|
+
);
|
|
47
|
+
expect(query).toHaveProperty('path');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('gql: ending with variable', () => {
|
|
51
|
+
const variable1 = 'var1';
|
|
52
|
+
|
|
53
|
+
const query = gql`abcdef ${variable1}`;
|
|
54
|
+
|
|
55
|
+
expect(query.query.trim()).toBe(`abcdef ${variable1}`);
|
|
56
|
+
expect(query).toHaveProperty('path');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('gql: exclusive variable', () => {
|
|
60
|
+
const variable1 = 'var1';
|
|
61
|
+
|
|
62
|
+
const query = gql`
|
|
63
|
+
${variable1}
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
expect(query.query.trim()).toBe(`${variable1}`);
|
|
67
|
+
expect(query).toHaveProperty('path');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('gql: empty string', () => {
|
|
71
|
+
const query = gql``;
|
|
72
|
+
|
|
73
|
+
expect(query).toHaveProperty('query', ``);
|
|
74
|
+
expect(query).toHaveProperty('path');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('gql: only variables', () => {
|
|
78
|
+
const variable1 = 'var1';
|
|
79
|
+
const variable2 = 'var2';
|
|
80
|
+
const variable3 = 'var3';
|
|
81
|
+
|
|
82
|
+
// prettier-ignore
|
|
83
|
+
const query = gql`${variable1}${variable2}${variable3}`;
|
|
84
|
+
|
|
85
|
+
expect(query).toHaveProperty(
|
|
86
|
+
'query',
|
|
87
|
+
`${variable1}${variable2}${variable3}`,
|
|
88
|
+
);
|
|
89
|
+
expect(query).toHaveProperty('path');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('gql: path property', () => {
|
|
94
|
+
const root = process.cwd();
|
|
95
|
+
|
|
96
|
+
test('gql: path is graphql.js file', () => {
|
|
97
|
+
const query = gql``;
|
|
98
|
+
|
|
99
|
+
expect(query).toHaveProperty('query');
|
|
100
|
+
expect(query).toHaveProperty(
|
|
101
|
+
'path',
|
|
102
|
+
'file://' + root + '/src/graphql/GraphQl.ts',
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('gql: nesting', () => {
|
|
108
|
+
test('gql: nesting', () => {
|
|
109
|
+
const variable1 = 'var1';
|
|
110
|
+
const variable2 = 'var2';
|
|
111
|
+
const variable3 = 'var3';
|
|
112
|
+
|
|
113
|
+
const query = gql`a ${variable1} bc ${gql`a ${variable2} bc ${variable3}`}`;
|
|
114
|
+
|
|
115
|
+
expect(query).toHaveProperty(
|
|
116
|
+
'query',
|
|
117
|
+
`a ${variable1} bc a ${variable2} bc ${variable3}`,
|
|
118
|
+
);
|
|
119
|
+
expect(query).toHaveProperty('path');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe.todo('gql: variable types. Handling of objects and arrays');
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { getContext } from 'svelte';
|
|
2
|
+
import type Route from './routing/Route.js';
|
|
3
|
+
import type Router from './routing/Router.js';
|
|
4
|
+
import type SsrCache from './ssr/SsrCache.js';
|
|
5
|
+
import type Site from './routing/Site.js';
|
|
6
|
+
import type GraphQl from './graphql/GraphQl.js';
|
|
7
|
+
import Crelte from './Crelte.js';
|
|
8
|
+
import CrelteRouted from './CrelteRouted.js';
|
|
9
|
+
import CrelteBase from './CrelteBase.js';
|
|
10
|
+
import { Global, GlobalData } from './loadData/Globals.js';
|
|
11
|
+
import { Cookies } from './cookies/index.js';
|
|
12
|
+
import { Readable } from 'crelte-std/stores';
|
|
13
|
+
|
|
14
|
+
export { Crelte, CrelteRouted };
|
|
15
|
+
export type { CrelteBase };
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get the Crelte from the current context
|
|
19
|
+
*/
|
|
20
|
+
export function getCrelte(): Crelte {
|
|
21
|
+
return getContext('crelte');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the router from the current context
|
|
26
|
+
*/
|
|
27
|
+
export function getRouter(): Router {
|
|
28
|
+
return getCrelte().router;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the SSR cache from the current context
|
|
33
|
+
*/
|
|
34
|
+
export function getSsrCache(): SsrCache {
|
|
35
|
+
return getCrelte().ssrCache;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the GraphQl from the current context
|
|
40
|
+
*/
|
|
41
|
+
export function getGraphQl(): GraphQl {
|
|
42
|
+
return getCrelte().graphQl;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the current route
|
|
47
|
+
*/
|
|
48
|
+
export function getRoute(): Readable<Route> {
|
|
49
|
+
return getRouter().route;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get the current site
|
|
54
|
+
*/
|
|
55
|
+
export function getSite(): Readable<Site> {
|
|
56
|
+
return getRouter().site;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get the next route
|
|
61
|
+
*/
|
|
62
|
+
export function getNextRoute(): Readable<Route> {
|
|
63
|
+
return getRouter().nextRoute;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get the next site
|
|
68
|
+
*/
|
|
69
|
+
export function getNextSite(): Readable<Site> {
|
|
70
|
+
return getRouter().nextSite;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* returns an env Variables, always needs to be prefixed VITE_
|
|
75
|
+
* Except ENDPOINT_URL and CRAFT_WEB_URL
|
|
76
|
+
*/
|
|
77
|
+
export function getEnv(name: string): string | null {
|
|
78
|
+
return getCrelte().getEnv(name);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* returns a store which indicates if the a page is loading
|
|
83
|
+
*/
|
|
84
|
+
export function getLoading(): Readable<boolean> {
|
|
85
|
+
return getRouter().loading;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* returns a store which indicates the loading progress
|
|
90
|
+
*/
|
|
91
|
+
export function getLoadingProgress(): Readable<number> {
|
|
92
|
+
return getRouter().loadingProgress;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* returns a store which contains a globalSet
|
|
97
|
+
*/
|
|
98
|
+
export function getGlobal<T extends GlobalData>(
|
|
99
|
+
name: string,
|
|
100
|
+
): Global<T> | null {
|
|
101
|
+
return getCrelte().globals.get(name) ?? null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* returns the cookies instance
|
|
106
|
+
*/
|
|
107
|
+
export function getCookies(): Cookies {
|
|
108
|
+
return getCrelte().cookies;
|
|
109
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { CrelteBuilder } from '../Crelte.js';
|
|
2
|
+
import { SiteFromGraphQl } from '../routing/Site.js';
|
|
3
|
+
import { loadFn, pluginsBeforeRender, setupPlugins } from './shared.js';
|
|
4
|
+
import { tick } from 'svelte';
|
|
5
|
+
|
|
6
|
+
export type MainData = {
|
|
7
|
+
/// svelte app component
|
|
8
|
+
app: any;
|
|
9
|
+
/// error page
|
|
10
|
+
errorPage: any;
|
|
11
|
+
entryQuery: any;
|
|
12
|
+
globalQuery?: any;
|
|
13
|
+
|
|
14
|
+
// options
|
|
15
|
+
preloadOnMouseOver?: boolean;
|
|
16
|
+
viewTransition?: boolean;
|
|
17
|
+
playIntro?: boolean;
|
|
18
|
+
|
|
19
|
+
// debug
|
|
20
|
+
graphQlDebug?: boolean;
|
|
21
|
+
debugTiming?: boolean;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const mainDataDefault = {
|
|
25
|
+
preloadOnMouseOver: false,
|
|
26
|
+
viewTransition: false,
|
|
27
|
+
playIntro: false,
|
|
28
|
+
|
|
29
|
+
// will be passed down to ClientRenderer
|
|
30
|
+
graphQlDebug: false,
|
|
31
|
+
debugTiming: false,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export function main(data: MainData) {
|
|
35
|
+
data = { ...mainDataDefault, ...data };
|
|
36
|
+
|
|
37
|
+
// rendering steps
|
|
38
|
+
|
|
39
|
+
// loadSites (first time)
|
|
40
|
+
// determine route (if site is empty on server redirect to correct language)
|
|
41
|
+
// loadData, entry (make globally available), pluginsData
|
|
42
|
+
// loadTemplate
|
|
43
|
+
// render
|
|
44
|
+
|
|
45
|
+
// on route change
|
|
46
|
+
// determine route
|
|
47
|
+
// entry, pluginsData
|
|
48
|
+
// loadTemplate
|
|
49
|
+
// update
|
|
50
|
+
|
|
51
|
+
// construct Crelte
|
|
52
|
+
|
|
53
|
+
const builder = new CrelteBuilder();
|
|
54
|
+
const endpoint = builder.ssrCache.get('ENDPOINT_URL') as string;
|
|
55
|
+
builder.setupGraphQl(endpoint, {
|
|
56
|
+
debug: data.graphQlDebug,
|
|
57
|
+
debugTiming: data.debugTiming,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// on the client the cookies are always comming from document.cookie
|
|
61
|
+
builder.setupCookies('');
|
|
62
|
+
|
|
63
|
+
const csites = builder.ssrCache.get('crelteSites') as SiteFromGraphQl[];
|
|
64
|
+
builder.setupRouter(csites, {
|
|
65
|
+
preloadOnMouseOver: data.preloadOnMouseOver,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const crelte = builder.build();
|
|
69
|
+
|
|
70
|
+
const serverError = crelte.ssrCache.get('ERROR');
|
|
71
|
+
if (serverError) {
|
|
72
|
+
// should this be called??
|
|
73
|
+
crelte.router._internal.initClient();
|
|
74
|
+
|
|
75
|
+
new data.errorPage.default({
|
|
76
|
+
target: document.body,
|
|
77
|
+
props: { ...serverError },
|
|
78
|
+
hydrate: true,
|
|
79
|
+
context: crelte._getContext(),
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// setup plugins
|
|
85
|
+
setupPlugins(crelte, data.app.plugins ?? []);
|
|
86
|
+
|
|
87
|
+
// setup load Data
|
|
88
|
+
|
|
89
|
+
crelte.router._internal.onLoad = (route, site, opts) => {
|
|
90
|
+
const cr = crelte.toRouted(route, site);
|
|
91
|
+
return loadFn(cr, data.app, data.entryQuery, data.globalQuery, opts);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// render Space
|
|
95
|
+
|
|
96
|
+
let appInstance: any;
|
|
97
|
+
const updateAppProps = (props: any) => {
|
|
98
|
+
if (!appInstance) {
|
|
99
|
+
appInstance = new data.app.default({
|
|
100
|
+
target: document.body,
|
|
101
|
+
props,
|
|
102
|
+
hydrate: true,
|
|
103
|
+
context: crelte._getContext(),
|
|
104
|
+
intro: data.playIntro,
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
appInstance.$set(props);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
let firstLoad = true;
|
|
112
|
+
crelte.router._internal.onLoaded = async (
|
|
113
|
+
success,
|
|
114
|
+
route,
|
|
115
|
+
site,
|
|
116
|
+
readyForProps,
|
|
117
|
+
) => {
|
|
118
|
+
const isFirstLoad = firstLoad;
|
|
119
|
+
firstLoad = false;
|
|
120
|
+
|
|
121
|
+
if (!success) {
|
|
122
|
+
// if this is not the first load we should reload the page
|
|
123
|
+
// we don't reload everytime because this might cause a reload loop
|
|
124
|
+
// and since the first load will probably just contain ssrCache data in almost all cases the first load will never faill
|
|
125
|
+
if (!isFirstLoad) {
|
|
126
|
+
// the load might contain a js error and we prefer the error
|
|
127
|
+
// page
|
|
128
|
+
window.location.reload();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return handleLoadError(readyForProps());
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const cr = crelte.toRouted(route, site);
|
|
136
|
+
|
|
137
|
+
const startTime = data.debugTiming ? Date.now() : null;
|
|
138
|
+
let render = async () => {
|
|
139
|
+
// we should trigger the route update here
|
|
140
|
+
pluginsBeforeRender(cr);
|
|
141
|
+
crelte.globals._updateSiteId(site.id);
|
|
142
|
+
updateAppProps(readyForProps());
|
|
143
|
+
|
|
144
|
+
await tick();
|
|
145
|
+
|
|
146
|
+
if (startTime) {
|
|
147
|
+
console.log(
|
|
148
|
+
'dom update took ' + (Date.now() - startTime) + 'ms',
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
crelte.router._internal.domReady(route);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// render with view Transition if enabled and not in hydration
|
|
156
|
+
if (
|
|
157
|
+
data.viewTransition &&
|
|
158
|
+
appInstance &&
|
|
159
|
+
(document as any).startViewTransition
|
|
160
|
+
) {
|
|
161
|
+
render = () => (document as any).startViewTransition(render);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
await render();
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
crelte.router._internal.initClient();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function handleLoadError(e: any) {
|
|
171
|
+
console.log('loading or rendering the page failed with the error:');
|
|
172
|
+
console.error(e);
|
|
173
|
+
|
|
174
|
+
// Detect the browser language
|
|
175
|
+
// @ts-ignore
|
|
176
|
+
const userLang: string = navigator.language || navigator.userLanguage;
|
|
177
|
+
|
|
178
|
+
// Messages in different languages
|
|
179
|
+
const messages: Record<string, string> = {
|
|
180
|
+
en: 'An error has occurred. Please reload the page or try again later.',
|
|
181
|
+
de: 'Leider ist ein Fehler aufgetreten. Laden Sie die Seite neu, oder versuchen Sie es später noch mal.',
|
|
182
|
+
fr: 'Une erreur s’est produite. Veuillez recharger la page ou réessayer plus tard.',
|
|
183
|
+
it: 'Si è verificato un errore. Ricarica la pagina o riprova più tardi.',
|
|
184
|
+
nl: 'Er is een fout opgetreden. Herlaad de pagina of probeer het later opnieuw.',
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const message = messages[userLang.split('-')[0]] ?? messages.en;
|
|
188
|
+
|
|
189
|
+
alert(message);
|
|
190
|
+
}
|