5htp-core 0.1.1-2 → 0.1.1-4

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": "5-HTP, scientifically called 5-Hydroxytryptophan, is the precursor of happiness neurotransmitter.",
4
- "version": "0.1.1-2",
4
+ "version": "0.1.1-4",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -0,0 +1,36 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import React from 'react';
7
+ import type { ComponentChild } from 'preact';
8
+ import { history } from './request/history';
9
+
10
+ /*----------------------------------
11
+ - COMPONENT
12
+ ----------------------------------*/
13
+ // Simple link
14
+ export const Link = ({ to, ...props }: {
15
+ to: string,
16
+ children?: ComponentChild,
17
+ class?: string,
18
+ className?: string
19
+ } & React.HTMLProps<HTMLAnchorElement>) => {
20
+
21
+ // External = open in new tab by default
22
+ if (to[0] !== '/' || to.startsWith('//'))
23
+ props.target = '_blank';
24
+ // Otherwise, propagate to the router
25
+ else
26
+ props.onClick = (e) => {
27
+ history?.push(to);
28
+ e.preventDefault();
29
+ return false
30
+ }
31
+
32
+ return (
33
+ <a {...props} href={to} />
34
+ )
35
+
36
+ }
@@ -4,119 +4,97 @@
4
4
 
5
5
  // Npm
6
6
  import React from 'react';
7
- import type { ComponentChild } from 'preact';
8
- import type { Location } from 'history';
9
7
 
10
8
  // Core: libs
11
9
  import ClientRequest from './request';
12
10
  import ClientResponse from './response';
13
- import { history } from './request/history';
14
11
 
15
12
  // Core: types
16
- import BaseRouter, { TBaseRoute, TRouteOptions, defaultOptions, } from '@common/router'
13
+ import BaseRouter, { defaultOptions, } from '@common/router'
17
14
  import { PageResponse } from '@common/router/response';
18
- import { TFetcher, TFetcherList } from '@common/router/request';
19
- import type { TSsrData, default as ServerResponse } from '@server/services/router/response';
15
+ import type { TSsrData } from '@server/services/router/response';
20
16
  import type { ClientContext } from '@client/context';
21
17
 
22
18
  // Type xports
23
19
  export type { default as ClientResponse } from "./response";
20
+ export { Link } from './Link';
21
+ import type {
22
+ TClientRoute,
23
+ TUnresolvedRoute,
24
+ TSsrUnresolvedRoute,
25
+ TRoutesLoaders,
26
+ TRouteCallback,
27
+ TFetchedRoute,
28
+ TRegisterPageArgs
29
+ } from './route';
30
+
31
+ // Temporary
32
+ // TODO: Import these types directly from router/routes
33
+ export type {
34
+ TClientRoute,
35
+ TUnresolvedRoute,
36
+ TSsrUnresolvedRoute,
37
+ TRoutesLoaders,
38
+ TRouteCallback,
39
+ TFetchedRoute,
40
+ TRegisterPageArgs
41
+ } from './route';
24
42
 
25
43
  /*----------------------------------
26
- - TYPES: PARTIAL ROUTES
44
+ - CONFIG
27
45
  ----------------------------------*/
28
46
 
29
- export type TSsrUnresolvedRoute = Pick<TClientRoute, 'type' | 'keys'> & {
30
- regex: string,
31
- chunk: string,
32
- }
33
-
34
- export type TUnresolvedRoute = Pick<TClientRoute, 'type' | 'keys'> & {
35
- regex: RegExp,
36
- chunk: string,
37
- load: TRouteLoader,
38
- }
39
-
40
- type TFetchedRoute = Pick<TClientRoute, 'path' | 'options' | 'controller' | 'renderer' | 'method'>
47
+ const debug = true;
48
+ const LogPrefix = '[router]'
41
49
 
42
50
  /*----------------------------------
43
- - TYPES: REGISTER
51
+ - TYPES
44
52
  ----------------------------------*/
45
53
 
46
- type TRouteLoader = () => Promise<{ default: TFetchedRoute }>;
47
-
48
- export type TRoutesLoaders = {
49
- [chunkId: string]: /* Preloaded via require() */TFetchedRoute | /* Loader via import() */TRouteLoader/* | undefined*/
50
- }
51
-
52
- type TRouteCallback = (route?: TClientRoute) => void;
54
+ export type THookCallback = (request: ClientRequest) => void;
53
55
 
54
- export type TRegisterPageArgs<TControllerData extends TFetcherList = {}> = [
55
- path: string,
56
- options: Partial<TRouteOptions>,
57
- controller: TFrontController<TControllerData> | null,
58
- renderer: TFrontRenderer<TControllerData>
59
- ];
56
+ type THookName = 'locationChange'
60
57
 
61
58
  /*----------------------------------
62
- - TYPES: COMPLETE ROUTES
59
+ - ROUTER
63
60
  ----------------------------------*/
61
+ class Router extends BaseRouter {
64
62
 
65
- export type TClientRoute = TBaseRoute & {
66
- type: 'PAGE',
67
- method: 'GET',
68
- controller: TFrontController | null,
69
- renderer: TFrontRenderer
70
- }
63
+ /*----------------------------------
64
+ - HOOKS
65
+ ----------------------------------*/
66
+ private hooks: {[hookname in THookName]?: THookCallback[]} = {}
67
+ public on( hookName: THookName, callback: THookCallback ) {
71
68
 
72
- // https://stackoverflow.com/questions/44851268/typescript-how-to-extract-the-generic-parameter-from-a-type
73
- type TypeWithGeneric<T> = TFetcher<T>
74
- type extractGeneric<Type> = Type extends TypeWithGeneric<infer X> ? X : never
75
-
76
- export type TFrontController<TControllerData extends TFetcherList = {}> =
77
- (urlParams: TObjetDonnees, context: ClientContext) => TControllerData
78
-
79
- export type TFrontRenderer<TControllerData extends TFetcherList = {}> = (
80
- data: {
81
- [Property in keyof TControllerData]: undefined | (extractGeneric<TControllerData[Property]> extends ((...args: any[]) => any)
82
- ? ThenArg<ReturnType< extractGeneric<TControllerData[Property]> >>
83
- : extractGeneric<TControllerData[Property]>
84
- )
85
- },
86
- context: ClientContext
87
- ) => ComponentChild
88
-
89
- // Simple link
90
- export const Link = ({ to, ...props }: {
91
- to: string,
92
- children?: ComponentChild,
93
- class?: string,
94
- className?: string
95
- }) => {
96
-
97
- // External = open in new tab by default
98
- if (to[0] !== '/' || to.startsWith('//'))
99
- props.target = '_blank';
100
- // Otherwise, propagate to the router
101
- else
102
- props.onClick = (e) => {
103
- history?.push(to);
104
- e.preventDefault();
105
- return false
106
- }
69
+ debug && console.info(LogPrefix, `Register hook ${hookName}`);
107
70
 
108
- return (
109
- <a {...props} href={to} />
110
- )
71
+ let cbIndex: number;
72
+ let callbacks = this.hooks[ hookName ];
73
+ if (!callbacks) {
74
+ cbIndex = 0;
75
+ callbacks = this.hooks[ hookName ] = [callback]
76
+ } else {
77
+ cbIndex = callbacks.length;
78
+ callbacks.push(callback);
79
+ }
111
80
 
112
- }
81
+ // Listener remover
82
+ return () => {
83
+ debug && console.info(LogPrefix, `De-register hook ${hookName} (index ${cbIndex})`);
84
+ delete (callbacks as THookCallback[])[ cbIndex ];
85
+ }
113
86
 
114
- const debug = true;
87
+ }
88
+ private runHook( hookName: THookName, request: ClientRequest ) {
89
+ const callbacks = this.hooks[hookName];
90
+ if (callbacks)
91
+ for (const callback of callbacks)
92
+ callback(request);
93
+ }
115
94
 
116
- /*----------------------------------
117
- - ROUTER
118
- ----------------------------------*/
119
- class Router extends BaseRouter {
95
+ /*----------------------------------
96
+ - ROUTES MANAGEMENT
97
+ ----------------------------------*/
120
98
 
121
99
  public disableResolver = false;
122
100
 
@@ -171,6 +149,8 @@ class Router extends BaseRouter {
171
149
  public async resolve( request: ClientRequest, context: ClientContext ): Promise<PageResponse | undefined | null> {
172
150
  debug && console.log('Resolving request', request.path, Object.keys(request.data));
173
151
 
152
+ this.runHook('locationChange', request);
153
+
174
154
  for (let iRoute = 0; iRoute < this.routes.length; iRoute++) {
175
155
 
176
156
  let route = this.routes[iRoute];
@@ -0,0 +1,75 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import type { ComponentChild } from 'preact';
7
+
8
+ // Core
9
+ import type { ClientContext } from '@client/context';
10
+ import type { TBaseRoute, TRouteOptions, } from '@common/router'
11
+ import type { TFetcher, TFetcherList } from '@common/router/request';
12
+
13
+ /*----------------------------------
14
+ - TYPES: PARTIAL ROUTES
15
+ ----------------------------------*/
16
+
17
+ export type TSsrUnresolvedRoute = Pick<TClientRoute, 'type' | 'keys'> & {
18
+ regex: string,
19
+ chunk: string,
20
+ }
21
+
22
+ export type TUnresolvedRoute = Pick<TClientRoute, 'type' | 'keys'> & {
23
+ regex: RegExp | null,
24
+ chunk: string,
25
+ load: TRouteLoader,
26
+ }
27
+
28
+ export type TFetchedRoute = Pick<TClientRoute, 'path' | 'options' | 'controller' | 'renderer' | 'method'>
29
+
30
+ /*----------------------------------
31
+ - TYPES: REGISTER
32
+ ----------------------------------*/
33
+
34
+ type TRouteLoader = () => Promise<{ default: TFetchedRoute }>;
35
+
36
+ export type TRoutesLoaders = {
37
+ [chunkId: string]: /* Preloaded via require() */TFetchedRoute | /* Loader via import() */TRouteLoader/* | undefined*/
38
+ }
39
+
40
+ export type TRouteCallback = (route?: TClientRoute) => void;
41
+
42
+ export type TRegisterPageArgs<TControllerData extends TFetcherList = {}> = [
43
+ path: string,
44
+ options: Partial<TRouteOptions>,
45
+ controller: TFrontController<TControllerData> | null,
46
+ renderer: TFrontRenderer<TControllerData>
47
+ ];
48
+
49
+ /*----------------------------------
50
+ - TYPES: COMPLETE ROUTES
51
+ ----------------------------------*/
52
+
53
+ export type TClientRoute = TBaseRoute & {
54
+ type: 'PAGE',
55
+ method: 'GET',
56
+ controller: TFrontController | null,
57
+ renderer: TFrontRenderer
58
+ }
59
+
60
+ // https://stackoverflow.com/questions/44851268/typescript-how-to-extract-the-generic-parameter-from-a-type
61
+ type TypeWithGeneric<T> = TFetcher<T>
62
+ type extractGeneric<Type> = Type extends TypeWithGeneric<infer X> ? X : never
63
+
64
+ export type TFrontController<TControllerData extends TFetcherList = {}> =
65
+ (urlParams: TObjetDonnees, context: ClientContext) => TControllerData
66
+
67
+ export type TFrontRenderer<TControllerData extends TFetcherList = {}> = (
68
+ data: {
69
+ [Property in keyof TControllerData]: undefined | (extractGeneric<TControllerData[Property]> extends ((...args: any[]) => any)
70
+ ? ThenArg<ReturnType< extractGeneric<TControllerData[Property]> >>
71
+ : extractGeneric<TControllerData[Property]>
72
+ )
73
+ },
74
+ context: ClientContext
75
+ ) => ComponentChild