5htp-core 0.4.4-1 → 0.4.4-3

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "5htp-core",
3
3
  "description": "Convenient TypeScript framework designed for Performance and Productivity.",
4
- "version": "0.4.4-1",
4
+ "version": "0.4.4-3",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -66,11 +66,8 @@ export default ({ service: clientRouter }: { service?: ClientRouter }) => {
66
66
  if (clientRouter !== undefined)
67
67
  clientRouter.context = context;
68
68
 
69
- const [pages, setPages] = React.useState<{
70
- current: undefined | Page
71
- }>({
72
- current: context.page
73
- });
69
+ const [currentPage, setCurrentPage] = React.useState<undefined | Page>(context.page);
70
+
74
71
 
75
72
  /*----------------------------------
76
73
  - ACTIONS
@@ -85,12 +82,19 @@ export default ({ service: clientRouter }: { service?: ClientRouter }) => {
85
82
  // WARNING: Don"t try to play with pages here, since the object will not be updated
86
83
  // If needed to play with pages, do it in the setPages callback below
87
84
  // Unchanged path
88
- if (request.path === currentRequest.path && request.hash !== currentRequest.hash && request.hash !== undefined) {
85
+ if (
86
+ request.path === currentRequest.path
87
+ &&
88
+ request.hash !== currentRequest.hash
89
+ &&
90
+ request.hash !== undefined
91
+ ) {
89
92
  scrollToElement(request.hash);
90
93
  return;
91
94
  }
92
95
 
93
96
  // Set loading state
97
+ clientRouter.runHook('page.change', request);
94
98
  clientRouter.setLoading(true);
95
99
  const newpage = context.page = await clientRouter.resolve(request);
96
100
 
@@ -114,13 +118,13 @@ export default ({ service: clientRouter }: { service?: ClientRouter }) => {
114
118
  }
115
119
 
116
120
  // Add page container
117
- setPages( pages => {
121
+ setCurrentPage( page => {
118
122
 
119
123
  // WARN: Don't cancel navigation if same page as before, as we already instanciated the new page and bound the context with it
120
124
  // Otherwise it would cause reference issues (ex: page.setAllData makes ref to the new context)
121
125
 
122
126
  // If if the layout changed
123
- const curLayout = pages.current?.layout;
127
+ const curLayout = currentPage?.layout;
124
128
  const newLayout = newpage?.layout;
125
129
  if (newLayout && curLayout && newLayout.path !== curLayout.path) {
126
130
 
@@ -130,12 +134,12 @@ export default ({ service: clientRouter }: { service?: ClientRouter }) => {
130
134
  // Find a way to unload the previous layout / page resources before to load the new one
131
135
  console.log(LogPrefix, `Changing layout. Before:`, curLayout, 'New layout:', newLayout);
132
136
  window.location.replace(request.url);
133
- return { ...pages }
137
+ return { ...page }
134
138
 
135
139
  context.app.setLayout(newLayout);
136
140
  }
137
141
 
138
- return { current: newpage }
142
+ return newpage;
139
143
  });
140
144
  }
141
145
 
@@ -169,25 +173,25 @@ export default ({ service: clientRouter }: { service?: ClientRouter }) => {
169
173
  // Reset scroll
170
174
  window.scrollTo(0, 0);
171
175
  // Should be called AFTER rendering the page (so after the state change)
172
- pages.current?.updateClient();
176
+ currentPage?.updateClient();
173
177
  // Scroll to the selected content via url hash
174
- restoreScroll(pages.current);
178
+ restoreScroll(currentPage);
175
179
 
176
180
  // Hooks
177
- clientRouter.runHook('page.changed', pages.current)
181
+ clientRouter.runHook('page.changed', currentPage)
178
182
 
179
- }, [pages.current]);
183
+ }, [currentPage]);
180
184
 
181
185
  /*----------------------------------
182
186
  - RENDER
183
187
  ----------------------------------*/
184
188
  // Render the page component
185
189
  return <>
186
- {pages.current && (
187
- <PageComponent page={pages.current}
190
+ {currentPage && (
191
+ <PageComponent page={currentPage}
188
192
  /* Create a new instance of the Page component every time the page change
189
193
  Otherwise the page will memorise the data of the previous page */
190
- key={pages.current.chunkId === undefined ? undefined : 'page_' + pages.current.chunkId}
194
+ key={currentPage.chunkId === undefined ? undefined : 'page_' + currentPage.chunkId}
191
195
  />
192
196
  )}
193
197
 
@@ -17,7 +17,7 @@ import type { TBasicSSrData } from '@server/services/router/response';
17
17
  import BaseRouter, {
18
18
  defaultOptions, TRoute, TErrorRoute,
19
19
  TClientOrServerContext, TRouteModule,
20
- buildUrl, TDomainsList
20
+ matchRoute, buildUrl, TDomainsList
21
21
  } from '@common/router'
22
22
  import { getLayout } from '@common/router/layouts';
23
23
  import { getRegisterPageArgs, buildRegex } from '@common/router/register';
@@ -123,7 +123,7 @@ export type TRoutesLoaders = {
123
123
 
124
124
  export type THookCallback<TRouter extends ClientRouter> = (request: ClientRequest<TRouter>) => void;
125
125
 
126
- type THookName = 'location.change' | 'page.changed'
126
+ type THookName = 'page.change' | 'page.changed'
127
127
 
128
128
  type Config<TAdditionnalContext extends {} = {}> = {
129
129
  preload: string[], // List of globs
@@ -307,7 +307,6 @@ export default class ClientRouter<
307
307
  public async resolve(request: ClientRequest<this>): Promise<ClientPage | undefined | null> {
308
308
 
309
309
  debug && console.log(LogPrefix, 'Resolving request', request.path, Object.keys(request.data));
310
- this.runHook('location.change', request);
311
310
 
312
311
  for (let iRoute = 0; iRoute < this.routes.length; iRoute++) {
313
312
 
@@ -315,17 +314,10 @@ export default class ClientRouter<
315
314
  if (!('regex' in route))
316
315
  continue;
317
316
 
318
- const match = route.regex.exec(request.path);
319
- if (!match)
317
+ const isMatching = matchRoute(route, request);
318
+ if (!isMatching)
320
319
  continue;
321
320
 
322
- // URL data
323
- for (let iKey = 0; iKey < route.keys.length; iKey++) {
324
- const nomParam = route.keys[iKey];
325
- if (typeof nomParam === 'string') // number = sans nom
326
- request.data[nomParam] = match[iKey + 1]
327
- }
328
-
329
321
  // Create response
330
322
  debug && console.log(LogPrefix, 'Resolved request', request.path, '| Route:', route);
331
323
  const page = await this.createResponse(route, request);
@@ -15,6 +15,8 @@ import type {
15
15
  TRouteHttpMethod
16
16
  } from '@server/services/router';
17
17
 
18
+ import type RouterRequest from './request';
19
+
18
20
  import type { TUserRole } from '@server/services/auth';
19
21
 
20
22
  import type { TAppArrowFunction } from '@common/app';
@@ -173,6 +175,24 @@ export const buildUrl = (
173
175
  return prefix + path + (searchParams.toString() ? '?' + searchParams.toString() : '');
174
176
  }
175
177
 
178
+ export const matchRoute = (route: TRoute, request: RouterRequest) => {
179
+
180
+ // Match Path
181
+ const match = route.regex.exec(request.path);
182
+ if (!match)
183
+ return false;
184
+
185
+ // Extract URL params
186
+ for (let iKey = 0; iKey < route.keys.length; iKey++) {
187
+ const key = route.keys[iKey];
188
+ const value = match[iKey + 1];
189
+ if (typeof key === 'string' && value) // number = sans nom
190
+ request.data[key] = decodeURIComponent( value.replaceAll('+', '%20') );
191
+ }
192
+
193
+ return true;
194
+ }
195
+
176
196
  /*----------------------------------
177
197
  - BASE ROUTER
178
198
  ----------------------------------*/
@@ -67,7 +67,6 @@ export default abstract class PageResponse<TRouter extends ClientOrServerRouter
67
67
  public bodyId?: string;
68
68
 
69
69
  // Resources
70
- public amp?: boolean;
71
70
  public scripts: TPageResource[] = [];
72
71
  public style: TPageResource[] = [];
73
72
 
@@ -25,7 +25,7 @@ import { CoreError, NotFound } from '@common/errors';
25
25
  import BaseRouter, {
26
26
  TRoute, TErrorRoute, TRouteModule,
27
27
  TRouteOptions, defaultOptions,
28
- buildUrl, TDomainsList
28
+ matchRoute, buildUrl, TDomainsList
29
29
  } from '@common/router';
30
30
  import { buildRegex, getRegisterPageArgs } from '@common/router/register';
31
31
  import { layoutsList, getLayout } from '@common/router/layouts';
@@ -523,19 +523,10 @@ declare type Routes = {
523
523
  if (!request.accepts(route.options.accept))
524
524
  continue;
525
525
 
526
- // Match Path
527
- const match = route.regex.exec(request.path);
528
- if (!match)
526
+ const isMatching = matchRoute(route, request);
527
+ if (!isMatching)
529
528
  continue;
530
529
 
531
- // Extract URL params
532
- for (let iKey = 0; iKey < route.keys.length; iKey++) {
533
- const key = route.keys[iKey];
534
- const value = match[iKey + 1];
535
- if (typeof key === 'string' && value) // number = sans nom
536
- request.data[key] = decodeURIComponent(value);
537
- }
538
-
539
530
  // Run on resolution hooks. Ex: authentication check
540
531
  await this.runHook('resolved', route);
541
532
 
@@ -57,31 +57,26 @@ export default class DocumentRenderer<TRouter extends Router> {
57
57
 
58
58
  public async page( html: string, page: Page, response: ServerResponse<TRouter> ) {
59
59
 
60
- const fullUrl = this.router.http.publicUrl + response.request.path;
60
+ // TODO: can be customized via page / route config
61
+ const canonicalUrl = response.request.req.url;
61
62
 
62
63
  let attrsBody = {
63
64
  className: [...page.bodyClass].join(' '),
64
65
  };
65
66
 
66
67
  return '<!doctype html>' + renderToString(
67
- <html lang="en" {...(page.amp ? { amp: "true" } : {})}>
68
+ <html lang="en">
68
69
  <head>
69
70
  {/* Format */}
70
71
  <meta charSet="utf-8" />
71
- {page.amp && ( // As a best practice, you should include the script as early as possible in the <head>.
72
- <script async={true} src="https://cdn.ampproject.org/v0.js"></script>
73
- )}
74
72
  <meta content="IE=edge" httpEquiv="X-UA-Compatible" />
75
73
  <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
76
- {!page.amp && page.amp && (
77
- <link rel="amphtml" href={fullUrl + '/amp'} />
78
- )}
79
74
 
80
75
  {/* Basique*/}
81
76
  <meta content={this.app.identity.web.title} name="apple-mobile-web-app-title" />
82
77
  <title>{page.title}</title>
83
78
  <meta content={page.description} name="description" />
84
- <link rel="canonical" href={fullUrl} />
79
+ <link rel="canonical" href={canonicalUrl} />
85
80
 
86
81
  {this.metas( page )}
87
82
 
@@ -147,9 +142,6 @@ export default class DocumentRenderer<TRouter extends Router> {
147
142
  </> : <>
148
143
  <style id={style.id} dangerouslySetInnerHTML={{ __html: style.inline }} />
149
144
  </>)}
150
-
151
- {/* Sera remplacé par la chaine exacte après renderToStaticMarkup */}
152
- {page.amp && (<style amp-boilerplate=""></style>)}
153
145
  </>
154
146
  }
155
147
 
@@ -161,13 +153,11 @@ export default class DocumentRenderer<TRouter extends Router> {
161
153
 
162
154
  return <>
163
155
  {/* JS */}
164
- {!page.amp && (
165
- <script type="text/javascript" dangerouslySetInnerHTML={{
166
- __html: `window.ssr=${context}; window.routes=${routesForClient};` + (
167
- this.app.env.profile === 'dev' ? 'window.dev = true;' : ''
168
- )
169
- }} />
170
- )}
156
+ <script type="text/javascript" dangerouslySetInnerHTML={{
157
+ __html: `window.ssr=${context}; window.routes=${routesForClient};` + (
158
+ this.app.env.profile === 'dev' ? 'window.dev = true;' : ''
159
+ )
160
+ }} />
171
161
 
172
162
  <link rel="preload" href={"/public/client.js?v=" + BUILD_ID} as="script" />
173
163
  <script defer type="text/javascript" src={"/public/client.js?v=" + BUILD_ID} />
@@ -83,11 +83,7 @@ export default class Page<TRouter extends Router = Router> extends PageResponse<
83
83
  attrsBody.className += ' ' + page.classeBody.join(' ');
84
84
 
85
85
  if (page.theme)
86
- attrsBody.className += ' ' + page.theme;
87
-
88
- // L'url canonique doit pointer vers la version html
89
- if (page.amp && fullUrl.endsWith('/amp'))
90
- fullUrl = fullUrl.substring(0, fullUrl.length - 4);*/
86
+ attrsBody.className += ' ' + page.theme;*/
91
87
 
92
88
  return this.router.render.page(html, this, this.context.response);
93
89
  }
@@ -113,9 +109,7 @@ export default class Page<TRouter extends Router = Router> extends PageResponse<
113
109
  id: chunk,
114
110
  url: '/public/' + asset
115
111
  })
116
- // Si mode amp, on ne charge pas le JS react (rendu serveur uniquement)
117
- // Sauf si mode dev, car le hot reload est quand même bien pratique ...
118
- else if (!this.amp)
112
+ else
119
113
  this.scripts.push({
120
114
  id: chunk,
121
115
  url: '/public/' + asset