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.
Files changed (130) hide show
  1. package/LICENSE.md +41 -0
  2. package/dist/Crelte.d.ts +55 -0
  3. package/dist/Crelte.d.ts.map +1 -0
  4. package/dist/Crelte.js +106 -0
  5. package/dist/CrelteBase.d.ts +16 -0
  6. package/dist/CrelteBase.d.ts.map +1 -0
  7. package/dist/CrelteBase.js +1 -0
  8. package/dist/CrelteRouted.d.ts +50 -0
  9. package/dist/CrelteRouted.d.ts.map +1 -0
  10. package/dist/CrelteRouted.js +88 -0
  11. package/dist/blocks/Blocks.d.ts +35 -0
  12. package/dist/blocks/Blocks.d.ts.map +1 -0
  13. package/dist/blocks/Blocks.js +100 -0
  14. package/dist/blocks/Blocks.svelte +21 -0
  15. package/dist/blocks/Blocks.svelte.d.ts +24 -0
  16. package/dist/blocks/Blocks.svelte.d.ts.map +1 -0
  17. package/dist/blocks/index.d.ts +5 -0
  18. package/dist/blocks/index.d.ts.map +1 -0
  19. package/dist/blocks/index.js +3 -0
  20. package/dist/cookies/ClientCookies.d.ts +9 -0
  21. package/dist/cookies/ClientCookies.d.ts.map +1 -0
  22. package/dist/cookies/ClientCookies.js +22 -0
  23. package/dist/cookies/ServerCookies.d.ts +13 -0
  24. package/dist/cookies/ServerCookies.d.ts.map +1 -0
  25. package/dist/cookies/ServerCookies.js +31 -0
  26. package/dist/cookies/index.d.ts +20 -0
  27. package/dist/cookies/index.d.ts.map +1 -0
  28. package/dist/cookies/index.js +1 -0
  29. package/dist/cookies/utils.d.ts +12 -0
  30. package/dist/cookies/utils.d.ts.map +1 -0
  31. package/dist/cookies/utils.js +32 -0
  32. package/dist/graphql/GraphQl.d.ts +60 -0
  33. package/dist/graphql/GraphQl.d.ts.map +1 -0
  34. package/dist/graphql/GraphQl.js +197 -0
  35. package/dist/graphql/gql.test.d.ts +2 -0
  36. package/dist/graphql/gql.test.d.ts.map +1 -0
  37. package/dist/graphql/gql.test.js +80 -0
  38. package/dist/graphql/index.d.ts +3 -0
  39. package/dist/graphql/index.d.ts.map +1 -0
  40. package/dist/graphql/index.js +2 -0
  41. package/dist/index.d.ts +67 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +83 -0
  44. package/dist/init/client.d.ts +13 -0
  45. package/dist/init/client.d.ts.map +1 -0
  46. package/dist/init/client.js +129 -0
  47. package/dist/init/server.d.ts +38 -0
  48. package/dist/init/server.d.ts.map +1 -0
  49. package/dist/init/server.js +95 -0
  50. package/dist/init/shared.d.ts +29 -0
  51. package/dist/init/shared.d.ts.map +1 -0
  52. package/dist/init/shared.js +154 -0
  53. package/dist/loadData/Globals.d.ts +33 -0
  54. package/dist/loadData/Globals.d.ts.map +1 -0
  55. package/dist/loadData/Globals.js +119 -0
  56. package/dist/loadData/index.d.ts +25 -0
  57. package/dist/loadData/index.d.ts.map +1 -0
  58. package/dist/loadData/index.js +39 -0
  59. package/dist/plugins/Events.d.ts +11 -0
  60. package/dist/plugins/Events.d.ts.map +1 -0
  61. package/dist/plugins/Events.js +29 -0
  62. package/dist/plugins/Plugins.d.ts +12 -0
  63. package/dist/plugins/Plugins.d.ts.map +1 -0
  64. package/dist/plugins/Plugins.js +12 -0
  65. package/dist/plugins/index.d.ts +5 -0
  66. package/dist/plugins/index.d.ts.map +1 -0
  67. package/dist/plugins/index.js +2 -0
  68. package/dist/routing/History.d.ts +22 -0
  69. package/dist/routing/History.d.ts.map +1 -0
  70. package/dist/routing/History.js +36 -0
  71. package/dist/routing/InnerRouter.d.ts +111 -0
  72. package/dist/routing/InnerRouter.d.ts.map +1 -0
  73. package/dist/routing/InnerRouter.js +397 -0
  74. package/dist/routing/PageLoader.d.ts +37 -0
  75. package/dist/routing/PageLoader.d.ts.map +1 -0
  76. package/dist/routing/PageLoader.js +72 -0
  77. package/dist/routing/Route.d.ts +82 -0
  78. package/dist/routing/Route.d.ts.map +1 -0
  79. package/dist/routing/Route.js +134 -0
  80. package/dist/routing/Router.d.ts +162 -0
  81. package/dist/routing/Router.d.ts.map +1 -0
  82. package/dist/routing/Router.js +333 -0
  83. package/dist/routing/Site.d.ts +47 -0
  84. package/dist/routing/Site.d.ts.map +1 -0
  85. package/dist/routing/Site.js +48 -0
  86. package/dist/routing/index.d.ts +5 -0
  87. package/dist/routing/index.d.ts.map +1 -0
  88. package/dist/routing/index.js +4 -0
  89. package/dist/ssr/SsrCache.d.ts +12 -0
  90. package/dist/ssr/SsrCache.d.ts.map +1 -0
  91. package/dist/ssr/SsrCache.js +50 -0
  92. package/dist/ssr/SsrComponents.d.ts +7 -0
  93. package/dist/ssr/SsrComponents.d.ts.map +1 -0
  94. package/dist/ssr/SsrComponents.js +30 -0
  95. package/dist/ssr/index.d.ts +4 -0
  96. package/dist/ssr/index.d.ts.map +1 -0
  97. package/dist/ssr/index.js +3 -0
  98. package/package.json +79 -0
  99. package/src/Crelte.ts +135 -0
  100. package/src/CrelteBase.ts +24 -0
  101. package/src/CrelteRouted.ts +128 -0
  102. package/src/blocks/Blocks.svelte +68 -0
  103. package/src/blocks/Blocks.ts +155 -0
  104. package/src/blocks/index.ts +14 -0
  105. package/src/cookies/ClientCookies.ts +30 -0
  106. package/src/cookies/ServerCookies.ts +42 -0
  107. package/src/cookies/index.ts +24 -0
  108. package/src/cookies/utils.ts +53 -0
  109. package/src/graphql/GraphQl.ts +281 -0
  110. package/src/graphql/gql.test.ts +123 -0
  111. package/src/graphql/index.ts +8 -0
  112. package/src/index.ts +109 -0
  113. package/src/init/client.ts +190 -0
  114. package/src/init/server.ts +177 -0
  115. package/src/init/shared.ts +221 -0
  116. package/src/loadData/Globals.ts +150 -0
  117. package/src/loadData/index.ts +67 -0
  118. package/src/plugins/Events.ts +50 -0
  119. package/src/plugins/Plugins.ts +23 -0
  120. package/src/plugins/index.ts +5 -0
  121. package/src/routing/History.ts +52 -0
  122. package/src/routing/InnerRouter.ts +469 -0
  123. package/src/routing/PageLoader.ts +112 -0
  124. package/src/routing/Route.ts +184 -0
  125. package/src/routing/Router.ts +476 -0
  126. package/src/routing/Site.ts +65 -0
  127. package/src/routing/index.ts +5 -0
  128. package/src/ssr/SsrCache.ts +61 -0
  129. package/src/ssr/SsrComponents.ts +34 -0
  130. package/src/ssr/index.ts +4 -0
@@ -0,0 +1,129 @@
1
+ import { CrelteBuilder } from '../Crelte.js';
2
+ import { loadFn, pluginsBeforeRender, setupPlugins } from './shared.js';
3
+ import { tick } from 'svelte';
4
+ const mainDataDefault = {
5
+ preloadOnMouseOver: false,
6
+ viewTransition: false,
7
+ playIntro: false,
8
+ // will be passed down to ClientRenderer
9
+ graphQlDebug: false,
10
+ debugTiming: false,
11
+ };
12
+ export function main(data) {
13
+ data = { ...mainDataDefault, ...data };
14
+ // rendering steps
15
+ // loadSites (first time)
16
+ // determine route (if site is empty on server redirect to correct language)
17
+ // loadData, entry (make globally available), pluginsData
18
+ // loadTemplate
19
+ // render
20
+ // on route change
21
+ // determine route
22
+ // entry, pluginsData
23
+ // loadTemplate
24
+ // update
25
+ // construct Crelte
26
+ const builder = new CrelteBuilder();
27
+ const endpoint = builder.ssrCache.get('ENDPOINT_URL');
28
+ builder.setupGraphQl(endpoint, {
29
+ debug: data.graphQlDebug,
30
+ debugTiming: data.debugTiming,
31
+ });
32
+ // on the client the cookies are always comming from document.cookie
33
+ builder.setupCookies('');
34
+ const csites = builder.ssrCache.get('crelteSites');
35
+ builder.setupRouter(csites, {
36
+ preloadOnMouseOver: data.preloadOnMouseOver,
37
+ });
38
+ const crelte = builder.build();
39
+ const serverError = crelte.ssrCache.get('ERROR');
40
+ if (serverError) {
41
+ // should this be called??
42
+ crelte.router._internal.initClient();
43
+ new data.errorPage.default({
44
+ target: document.body,
45
+ props: { ...serverError },
46
+ hydrate: true,
47
+ context: crelte._getContext(),
48
+ });
49
+ return;
50
+ }
51
+ // setup plugins
52
+ setupPlugins(crelte, data.app.plugins ?? []);
53
+ // setup load Data
54
+ crelte.router._internal.onLoad = (route, site, opts) => {
55
+ const cr = crelte.toRouted(route, site);
56
+ return loadFn(cr, data.app, data.entryQuery, data.globalQuery, opts);
57
+ };
58
+ // render Space
59
+ let appInstance;
60
+ const updateAppProps = (props) => {
61
+ if (!appInstance) {
62
+ appInstance = new data.app.default({
63
+ target: document.body,
64
+ props,
65
+ hydrate: true,
66
+ context: crelte._getContext(),
67
+ intro: data.playIntro,
68
+ });
69
+ }
70
+ else {
71
+ appInstance.$set(props);
72
+ }
73
+ };
74
+ let firstLoad = true;
75
+ crelte.router._internal.onLoaded = async (success, route, site, readyForProps) => {
76
+ const isFirstLoad = firstLoad;
77
+ firstLoad = false;
78
+ if (!success) {
79
+ // if this is not the first load we should reload the page
80
+ // we don't reload everytime because this might cause a reload loop
81
+ // and since the first load will probably just contain ssrCache data in almost all cases the first load will never faill
82
+ if (!isFirstLoad) {
83
+ // the load might contain a js error and we prefer the error
84
+ // page
85
+ window.location.reload();
86
+ return;
87
+ }
88
+ return handleLoadError(readyForProps());
89
+ }
90
+ const cr = crelte.toRouted(route, site);
91
+ const startTime = data.debugTiming ? Date.now() : null;
92
+ let render = async () => {
93
+ // we should trigger the route update here
94
+ pluginsBeforeRender(cr);
95
+ crelte.globals._updateSiteId(site.id);
96
+ updateAppProps(readyForProps());
97
+ await tick();
98
+ if (startTime) {
99
+ console.log('dom update took ' + (Date.now() - startTime) + 'ms');
100
+ }
101
+ crelte.router._internal.domReady(route);
102
+ };
103
+ // render with view Transition if enabled and not in hydration
104
+ if (data.viewTransition &&
105
+ appInstance &&
106
+ document.startViewTransition) {
107
+ render = () => document.startViewTransition(render);
108
+ }
109
+ await render();
110
+ };
111
+ crelte.router._internal.initClient();
112
+ }
113
+ function handleLoadError(e) {
114
+ console.log('loading or rendering the page failed with the error:');
115
+ console.error(e);
116
+ // Detect the browser language
117
+ // @ts-ignore
118
+ const userLang = navigator.language || navigator.userLanguage;
119
+ // Messages in different languages
120
+ const messages = {
121
+ en: 'An error has occurred. Please reload the page or try again later.',
122
+ de: 'Leider ist ein Fehler aufgetreten. Laden Sie die Seite neu, oder versuchen Sie es später noch mal.',
123
+ fr: 'Une erreur s’est produite. Veuillez recharger la page ou réessayer plus tard.',
124
+ it: 'Si è verificato un errore. Ricarica la pagina o riprova più tardi.',
125
+ nl: 'Er is een fout opgetreden. Herlaad de pagina of probeer het later opnieuw.',
126
+ };
127
+ const message = messages[userLang.split('-')[0]] ?? messages.en;
128
+ alert(message);
129
+ }
@@ -0,0 +1,38 @@
1
+ export type ServerData = {
2
+ url: string;
3
+ htmlTemplate: string;
4
+ ssrManifest: Record<string, string[]>;
5
+ acceptLang?: string;
6
+ endpoint: string;
7
+ craftWeb: string;
8
+ viteEnv: Map<string, string>;
9
+ cookies?: string;
10
+ };
11
+ export type MainData = {
12
+ app: any;
13
+ entryQuery: any;
14
+ globalQuery?: any;
15
+ serverData: ServerData;
16
+ graphQlDebug?: boolean;
17
+ debugTiming?: boolean;
18
+ };
19
+ export declare function main(data: MainData): Promise<{
20
+ status: number;
21
+ location?: string;
22
+ html?: string;
23
+ setCookies?: string[];
24
+ }>;
25
+ export type Error = {
26
+ status: number;
27
+ message: any;
28
+ };
29
+ export type MainErrorData = {
30
+ error: Error;
31
+ errorPage: any;
32
+ serverData: ServerData;
33
+ };
34
+ export declare function mainError(data: MainErrorData): Promise<{
35
+ status: number;
36
+ html?: string;
37
+ }>;
38
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../src/init/server.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,UAAU,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IAEtB,GAAG,EAAE,GAAG,CAAC;IACT,UAAU,EAAE,GAAG,CAAC;IAChB,WAAW,CAAC,EAAE,GAAG,CAAC;IAElB,UAAU,EAAE,UAAU,CAAC;IAGvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,wBAAsB,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC,CA4ED;AAED,MAAM,MAAM,KAAK,GAAG;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,GAAG,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAE3B,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,GAAG,CAAC;IAEf,UAAU,EAAE,UAAU,CAAC;CACvB,CAAC;AAEF,wBAAsB,SAAS,CAC9B,IAAI,EAAE,aAAa,GACjB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA0B5C"}
@@ -0,0 +1,95 @@
1
+ import { CrelteBuilder } from '../Crelte.js';
2
+ import { loadFn, pluginsBeforeRender, setupPlugins } from './shared.js';
3
+ import SsrComponents from '../ssr/SsrComponents.js';
4
+ import SsrCache from '../ssr/SsrCache.js';
5
+ export async function main(data) {
6
+ const builder = new CrelteBuilder();
7
+ // setup viteEnv
8
+ data.serverData.viteEnv.forEach((v, k) => {
9
+ builder.ssrCache.set(k, v);
10
+ });
11
+ const endpoint = data.serverData.endpoint;
12
+ builder.ssrCache.set('ENDPOINT_URL', endpoint);
13
+ builder.ssrCache.set('CRAFT_WEB_URL', data.serverData.craftWeb);
14
+ builder.setupGraphQl(endpoint, {
15
+ debug: data.graphQlDebug,
16
+ debugTiming: data.debugTiming,
17
+ });
18
+ const cookies = data.serverData.cookies ?? '';
19
+ builder.setupCookies(cookies);
20
+ const csites = await loadSites(builder);
21
+ builder.ssrCache.set('crelteSites', csites);
22
+ builder.setupRouter(csites);
23
+ const crelte = builder.build();
24
+ // setup plugins
25
+ setupPlugins(crelte, data.app.plugins ?? []);
26
+ // setup load Data
27
+ crelte.router._internal.onLoad = (route, site) => {
28
+ const cr = crelte.toRouted(route, site);
29
+ return loadFn(cr, data.app, data.entryQuery, data.globalQuery);
30
+ };
31
+ const { success, redirect, route, site, props } = await crelte.router._internal.initServer(data.serverData.url, data.serverData.acceptLang);
32
+ if (!success)
33
+ throw props;
34
+ if (redirect) {
35
+ return {
36
+ status: 302,
37
+ location: route.url.toString(),
38
+ };
39
+ }
40
+ const context = crelte._getContext();
41
+ const ssrComponents = new SsrComponents();
42
+ ssrComponents.addToContext(context);
43
+ pluginsBeforeRender(crelte.toRouted(route, site));
44
+ crelte.globals._updateSiteId(site.id);
45
+ // eslint-disable-next-line prefer-const
46
+ let { html, head } = data.app.default.render(props, { context });
47
+ head += ssrComponents.toHead(data.serverData.ssrManifest);
48
+ head += crelte.ssrCache._exportToHead();
49
+ let htmlTemplate = data.serverData.htmlTemplate;
50
+ htmlTemplate = htmlTemplate.replace('<!--page-lang-->', site.language);
51
+ const finalHtml = htmlTemplate
52
+ .replace('</head>', head + '\n\t</head>')
53
+ .replace('<!--ssr-body-->', html);
54
+ const entry = props.entry;
55
+ return {
56
+ status: entry.sectionHandle === 'error' ? parseInt(entry.typeHandle) : 200,
57
+ html: finalHtml,
58
+ setCookies: crelte.cookies._getSetCookiesHeaders(),
59
+ };
60
+ }
61
+ export async function mainError(data) {
62
+ const ssrCache = new SsrCache();
63
+ const context = new Map();
64
+ const ssrComponents = new SsrComponents();
65
+ ssrComponents.addToContext(context);
66
+ ssrCache.set('ERROR', data.error);
67
+ // eslint-disable-next-line prefer-const
68
+ let { html, head } = data.errorPage.default.render(data.error, { context });
69
+ head += ssrComponents.toHead(data.serverData.ssrManifest);
70
+ head += ssrCache._exportToHead();
71
+ let htmlTemplate = data.serverData.htmlTemplate;
72
+ htmlTemplate = htmlTemplate.replace('<!--page-lang-->', 'de');
73
+ const finalHtml = htmlTemplate
74
+ .replace('<!--ssr-head-->', head)
75
+ .replace('<!--ssr-body-->', html);
76
+ return {
77
+ status: data.error.status,
78
+ html: finalHtml,
79
+ };
80
+ }
81
+ // requires, GraphQl, SsrCache
82
+ async function loadSites(builder) {
83
+ if (!builder.graphQl)
84
+ throw new Error();
85
+ if ('CRAFT_SITES_CACHED' in globalThis) {
86
+ // @ts-ignore
87
+ return globalThis['CRAFT_SITES_CACHED'];
88
+ }
89
+ const resp = (await builder.graphQl.request('query { crelteSites { id baseUrl language name handle primary } }', {},
90
+ // don't cache since we cache ourself
91
+ { caching: false }));
92
+ // @ts-ignore
93
+ globalThis['CRAFT_SITES_CACHED'] = resp.crelteSites;
94
+ return resp.crelteSites;
95
+ }
@@ -0,0 +1,29 @@
1
+ import CrelteRouted, { GraphQlQuery } from '../CrelteRouted.js';
2
+ import Crelte from '../Crelte.js';
3
+ import { LoadData } from '../loadData/index.js';
4
+ import { PluginCreator } from '../plugins/Plugins.js';
5
+ import { LoadOptions } from '../routing/PageLoader.js';
6
+ interface App<D, E, T> {
7
+ loadGlobalData?: LoadData<D>;
8
+ loadEntryData?: LoadData<any>;
9
+ templates?: Record<string, LazyTemplateModule<E, T>>;
10
+ }
11
+ interface TemplateModule<E, T> {
12
+ default: any;
13
+ loadData?(cr: CrelteRouted, entry: E): Promise<T>;
14
+ }
15
+ type LazyTemplateModule<E, T> = (() => Promise<TemplateModule<E, T>>) | TemplateModule<E, T>;
16
+ export declare function setupPlugins(crelte: Crelte, plugins: PluginCreator[]): void;
17
+ export declare function pluginsBeforeRender(cr: CrelteRouted): void;
18
+ /**
19
+ * Get the entry from the page
20
+ *
21
+ * entries should export sectionHandle and typeHandle
22
+ *
23
+ * products should alias productTypeHandle with typeHandle,
24
+ * sectionHandle will be automatically set to product
25
+ */
26
+ export declare function getEntry(page: any): any;
27
+ export declare function loadFn<D, E, T>(cr: CrelteRouted, app: App<D, E, T>, entryQuery: GraphQlQuery, globalQuery?: GraphQlQuery, loadOpts?: LoadOptions): Promise<any>;
28
+ export {};
29
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/init/shared.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,EAAE,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,MAAM,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAgB,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,UAAU,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACpB,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAG7B,aAAa,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;IAE9B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;CACrD;AAED,UAAU,cAAc,CAAC,CAAC,EAAE,CAAC;IAE5B,OAAO,EAAE,GAAG,CAAC;IAEb,QAAQ,CAAC,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClD;AAED,KAAK,kBAAkB,CAAC,CAAC,EAAE,CAAC,IACzB,CAAC,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GACrC,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAExB,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,QAKpE;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAE1D;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAYvC;AAED,wBAAsB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACnC,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EACjB,UAAU,EAAE,YAAY,EACxB,WAAW,CAAC,EAAE,YAAY,EAC1B,QAAQ,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,GAAG,CAAC,CAsGd"}
@@ -0,0 +1,154 @@
1
+ import { callLoadData } from '../loadData/index.js';
2
+ export function setupPlugins(crelte, plugins) {
3
+ for (const plugin of plugins) {
4
+ const p = plugin(crelte);
5
+ crelte.plugins.add(p);
6
+ }
7
+ }
8
+ export function pluginsBeforeRender(cr) {
9
+ cr.events.trigger('beforeRender', cr);
10
+ }
11
+ /**
12
+ * Get the entry from the page
13
+ *
14
+ * entries should export sectionHandle and typeHandle
15
+ *
16
+ * products should alias productTypeHandle with typeHandle,
17
+ * sectionHandle will be automatically set to product
18
+ */
19
+ export function getEntry(page) {
20
+ if (page?.entry)
21
+ return { ...page.entry };
22
+ if (page?.product)
23
+ return {
24
+ sectionHandle: 'product',
25
+ ...page.product,
26
+ };
27
+ return {
28
+ sectionHandle: 'error',
29
+ typeHandle: '404',
30
+ };
31
+ }
32
+ export async function loadFn(cr, app, entryQuery, globalQuery, loadOpts) {
33
+ let dataProm = null;
34
+ // @ts-ignore
35
+ if (app.loadData) {
36
+ throw new Error('loadData is ambigous, choose loadGlobalData or ' +
37
+ 'loadEntryData depending on if you need access to entry or not?');
38
+ }
39
+ if (app.loadGlobalData) {
40
+ dataProm = callLoadData(app.loadGlobalData, cr);
41
+ }
42
+ let globalProm = null;
43
+ if (globalQuery && !cr.globals._wasLoaded()) {
44
+ globalProm = (async () => {
45
+ const res = await cr.query(globalQuery);
46
+ // we need to do this sorcery here and can't wait until all
47
+ // globals functions are done, because some global function
48
+ // might want to use globals, and for that the function
49
+ // getOrWait exists on Globals
50
+ cr.globals._setData(cr.site.id, res);
51
+ return res;
52
+ })();
53
+ }
54
+ let pageProm = null;
55
+ if (cr.route.site) {
56
+ let uri = decodeURI(cr.route.uri);
57
+ if (uri.startsWith('/'))
58
+ uri = uri.substring(1);
59
+ if (uri === '' || uri === '/')
60
+ uri = '__home__';
61
+ pageProm = cr.query(entryQuery, {
62
+ uri,
63
+ siteId: cr.route.site.id,
64
+ });
65
+ }
66
+ const pluginsLoadGlobalData = cr.events.trigger('loadGlobalData', cr);
67
+ // loading progress is at 20%
68
+ loadOpts?.setProgress(0.2);
69
+ const [data, global, page] = await Promise.all([
70
+ dataProm,
71
+ globalProm,
72
+ pageProm,
73
+ ...pluginsLoadGlobalData,
74
+ ]);
75
+ if (global) {
76
+ cr.globals._setData(cr.site.id, global);
77
+ }
78
+ else if (!cr.globals._wasLoaded()) {
79
+ // we need to set the global data to an empty object
80
+ // so any waiters get's triggered
81
+ cr.globals._setData(cr.site.id, {});
82
+ }
83
+ // allow cr to get the global data
84
+ cr._globalDataLoaded();
85
+ const entry = getEntry(page);
86
+ let template;
87
+ if (app.templates) {
88
+ template = await loadTemplate(app.templates, entry);
89
+ }
90
+ else {
91
+ throw new Error('App must have templates or loadTemplate method');
92
+ }
93
+ // loading progress is at 60%
94
+ loadOpts?.setProgress(0.6);
95
+ const pluginsLoadData = cr.events.trigger('loadData', cr, entry);
96
+ let loadDataProm = null;
97
+ if (template.loadData) {
98
+ loadDataProm = callLoadData(template.loadData, cr, entry);
99
+ }
100
+ let entryDataProm = null;
101
+ if (app.loadEntryData) {
102
+ entryDataProm = callLoadData(app.loadEntryData, cr, entry);
103
+ }
104
+ const [templateData, entryData] = await Promise.all([
105
+ loadDataProm,
106
+ entryDataProm,
107
+ ...pluginsLoadData,
108
+ ]);
109
+ // loading progress is at 100%
110
+ loadOpts?.setProgress(1);
111
+ return {
112
+ ...data,
113
+ ...entryData,
114
+ entry,
115
+ template: template.default,
116
+ templateData: templateData,
117
+ };
118
+ }
119
+ function parseFilename(path) {
120
+ // get filename with extension
121
+ const slash = path.lastIndexOf('/');
122
+ const filename = path.substring(slash + 1);
123
+ const extPos = filename.lastIndexOf('.');
124
+ const name = filename.substring(0, extPos);
125
+ const ext = filename.substring(extPos + 1);
126
+ return [name, ext];
127
+ }
128
+ async function loadTemplate(rawModules, entry) {
129
+ // parse modules
130
+ const modules = new Map(Object.entries(rawModules)
131
+ .map(([path, mod]) => {
132
+ const [name, _ext] = parseFilename(path);
133
+ return [name, mod];
134
+ })
135
+ .filter(([name, _mod]) => !!name));
136
+ const entr = entry;
137
+ const handle = `${entr.sectionHandle}-${entr.typeHandle}`;
138
+ if (
139
+ // @ts-ignore
140
+ import.meta.env.DEV &&
141
+ !modules.has(handle) &&
142
+ !modules.has(entr.sectionHandle)) {
143
+ console.error(`Template not found: <${handle}>, expecting file: ${handle}.svelte or ${entr.sectionHandle}.svelte`);
144
+ }
145
+ const loadMod = modules.get(handle) ??
146
+ modules.get(entr.sectionHandle) ??
147
+ modules.get('error-404');
148
+ if (!loadMod)
149
+ throw new Error('could not find error-404 template');
150
+ if (typeof loadMod === 'function') {
151
+ return await loadMod();
152
+ }
153
+ return loadMod;
154
+ }
@@ -0,0 +1,33 @@
1
+ export type GlobalWaiters = [(g: Global<any> | null) => void];
2
+ export default class Globals {
3
+ private waiters;
4
+ private entries;
5
+ private loaded;
6
+ private prevSiteId;
7
+ constructor();
8
+ get(name: string): Global<any> | null;
9
+ getAsync(name: string): Promise<Global<any> | null> | Global<any> | null;
10
+ _wasLoaded(): boolean;
11
+ _setData(siteId: number, data: any): void;
12
+ _globalsBySite(siteId: number): Map<string, any>;
13
+ _updateSiteId(siteId: number): void;
14
+ }
15
+ export interface GlobalData {
16
+ siteId?: number;
17
+ }
18
+ export declare class Global<T extends GlobalData> {
19
+ private inner;
20
+ private languages;
21
+ constructor(name: string, data: T[] | T, siteId: number);
22
+ /**
23
+ * The function get's called once with the current value and then when the
24
+ * values changes
25
+ *
26
+ * @return a function which should be called to unsubscribe
27
+ */
28
+ subscribe(fn: (val: T) => void): () => void;
29
+ get(): T;
30
+ bySiteId(siteId: number): T | null;
31
+ _updateSiteId(siteId: number): void;
32
+ }
33
+ //# sourceMappingURL=Globals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Globals.d.ts","sourceRoot":"","sources":["../../../../src/loadData/Globals.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC;AAE9D,MAAM,CAAC,OAAO,OAAO,OAAO;IAG3B,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,UAAU,CAAgB;;IASlC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI;IAMrC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI;IAexE,UAAU,IAAI,OAAO;IAQrB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG;IAgBlC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;IAUhD,aAAa,CAAC,MAAM,EAAE,MAAM;CAM5B;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,MAAM,CAAC,CAAC,SAAS,UAAU;IACvC,OAAO,CAAC,KAAK,CAAc;IAE3B,OAAO,CAAC,SAAS,CAAa;gBAElB,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM;IAsBvD;;;;;OAKG;IACH,SAAS,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI;IAI3C,GAAG,IAAI,CAAC;IAKR,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAOlC,aAAa,CAAC,MAAM,EAAE,MAAM;CAM5B"}
@@ -0,0 +1,119 @@
1
+ /*
2
+ // returns a store which get's updated as soon as the site changes
3
+ const emergency = getGlobal('emergency');
4
+
5
+ // returns the data based on the current site (no store)
6
+ cr.getGlobal('emergency')
7
+ */
8
+ import { Writable } from 'crelte-std/stores';
9
+ export default class Globals {
10
+ // while the globals are not loaded if somebody calls
11
+ // getOrWait then we need to store the waiters
12
+ waiters;
13
+ entries;
14
+ loaded;
15
+ prevSiteId;
16
+ constructor() {
17
+ this.waiters = new Map();
18
+ this.entries = new Map();
19
+ this.loaded = false;
20
+ this.prevSiteId = null;
21
+ }
22
+ get(name) {
23
+ return this.entries.get(name) ?? null;
24
+ }
25
+ // call this only in loadGlobalData when by default the global
26
+ // is not available
27
+ getAsync(name) {
28
+ if (this.loaded)
29
+ return this.get(name);
30
+ let waiter = this.waiters.get(name);
31
+ if (!waiter) {
32
+ waiter = [];
33
+ this.waiters.set(name, waiter);
34
+ }
35
+ return new Promise(resolve => {
36
+ waiter.push(resolve);
37
+ });
38
+ }
39
+ // hidden
40
+ _wasLoaded() {
41
+ return this.loaded;
42
+ }
43
+ // hidden
44
+ // data is the data from the global graphql
45
+ // so it contains some keys and data which should be parsed
46
+ // and created a store for each key
47
+ _setData(siteId, data) {
48
+ const wasLoaded = this.loaded;
49
+ this.loaded = true;
50
+ for (const [key, value] of Object.entries(data)) {
51
+ this.entries.set(key, new Global(key, value, siteId));
52
+ }
53
+ if (!wasLoaded) {
54
+ this.waiters.forEach((waiters, key) => {
55
+ waiters.forEach(waiter => waiter(this.get(key)));
56
+ });
57
+ this.waiters.clear();
58
+ }
59
+ }
60
+ _globalsBySite(siteId) {
61
+ const map = new Map();
62
+ for (const [key, global] of this.entries) {
63
+ map.set(key, global.bySiteId(siteId));
64
+ }
65
+ return map;
66
+ }
67
+ _updateSiteId(siteId) {
68
+ // todo we should only trigger
69
+ if (this.prevSiteId === siteId)
70
+ return;
71
+ this.entries.forEach(global => global._updateSiteId(siteId));
72
+ }
73
+ }
74
+ export class Global {
75
+ inner;
76
+ /// if languages is null this means we always have the same data
77
+ languages;
78
+ constructor(name, data, siteId) {
79
+ this.languages = null;
80
+ let inner;
81
+ if (Array.isArray(data)) {
82
+ // make sure the data contains an object with the property
83
+ // siteId
84
+ this.languages = data;
85
+ inner = data.find(d => d.siteId === siteId);
86
+ if (!inner?.siteId) {
87
+ throw new Error(`The global query ${name} does not contain the required siteId property`);
88
+ }
89
+ }
90
+ else {
91
+ inner = data;
92
+ }
93
+ this.inner = new Writable(inner);
94
+ }
95
+ /**
96
+ * The function get's called once with the current value and then when the
97
+ * values changes
98
+ *
99
+ * @return a function which should be called to unsubscribe
100
+ */
101
+ subscribe(fn) {
102
+ return this.inner.subscribe(fn);
103
+ }
104
+ get() {
105
+ return this.inner.get();
106
+ }
107
+ /// if you pass a siteId which comes from craft then you will never receive null
108
+ bySiteId(siteId) {
109
+ if (this.languages)
110
+ return this.languages.find(d => d.siteId === siteId) ?? null;
111
+ return this.inner.get();
112
+ }
113
+ _updateSiteId(siteId) {
114
+ if (!this.languages)
115
+ return;
116
+ const inner = this.languages.find(d => d.siteId === siteId);
117
+ this.inner.set(inner);
118
+ }
119
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ *
3
+ */
4
+ import type CrelteRouted from '../CrelteRouted.js';
5
+ import { type GraphQlQuery } from '../graphql/GraphQl.js';
6
+ import type Globals from './Globals.js';
7
+ import type { Global } from './Globals.js';
8
+ export type { Globals, Global };
9
+ export type LoadData<T> = ((cr: CrelteRouted, ...args: any[]) => Promise<T>) | GraphQlQuery | T;
10
+ export declare function callLoadData(ld: LoadData<unknown>, cr: CrelteRouted, ...args: any[]): Promise<unknown>;
11
+ /**
12
+ * Spread the data of two loadData functions.
13
+ *
14
+ * ## Example
15
+ * ```
16
+ * export const loadData = mergeLoadData(
17
+ * {
18
+ * filter: (cr) => cr.route.search.get('filter'),
19
+ * },
20
+ * (cr) => cr.query(myQuery, { siteId: cr.site.id })
21
+ * )
22
+ * ```
23
+ */
24
+ export declare function mergeLoadData(...lds: LoadData<object>[]): LoadData<object>;
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/loadData/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAEhC,MAAM,MAAM,QAAQ,CAAC,CAAC,IACnB,CAAC,CAAC,EAAE,EAAE,YAAY,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,GAClD,YAAY,GACZ,CAAC,CAAC;AAEL,wBAAsB,YAAY,CACjC,EAAE,EAAE,QAAQ,CAAC,OAAO,CAAC,EACrB,EAAE,EAAE,YAAY,EAChB,GAAG,IAAI,EAAE,GAAG,EAAE,GACZ,OAAO,CAAC,OAAO,CAAC,CAuBlB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,GAAG,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAQ1E"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ *
3
+ */
4
+ import { isGraphQlQuery } from '../graphql/GraphQl.js';
5
+ export async function callLoadData(ld, cr, ...args) {
6
+ // either we have a function
7
+ if (typeof ld === 'function') {
8
+ return await ld(cr, ...args);
9
+ }
10
+ // or a graphql query
11
+ if (isGraphQlQuery(ld)) {
12
+ return await cr.query(ld);
13
+ }
14
+ // or an object
15
+ if (typeof ld === 'object' && ld !== null) {
16
+ const data = await Promise.all(Object.values(ld).map(nld => callLoadData(nld, cr, ...args)));
17
+ return Object.fromEntries(Object.keys(ld).map((key, i) => [key, data[i]]));
18
+ }
19
+ return ld;
20
+ }
21
+ /**
22
+ * Spread the data of two loadData functions.
23
+ *
24
+ * ## Example
25
+ * ```
26
+ * export const loadData = mergeLoadData(
27
+ * {
28
+ * filter: (cr) => cr.route.search.get('filter'),
29
+ * },
30
+ * (cr) => cr.query(myQuery, { siteId: cr.site.id })
31
+ * )
32
+ * ```
33
+ */
34
+ export function mergeLoadData(...lds) {
35
+ return async (cr, ...args) => {
36
+ const datas = await Promise.all(lds.map(ld => callLoadData(ld, cr, ...args)));
37
+ return datas.reduce((acc, data) => ({ ...acc, ...data }), {});
38
+ };
39
+ }