5htp-core 0.4.9-6 → 0.4.9-8

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.9-6",
4
+ "version": "0.4.9-8",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -13,7 +13,6 @@
13
13
  "framework"
14
14
  ],
15
15
  "dependencies": {
16
- "@excalidraw/excalidraw": "^0.17.6",
17
16
  "@lexical/file": "^0.19.0",
18
17
  "@lexical/headless": "^0.18.0",
19
18
  "@lexical/html": "^0.18.0",
@@ -99,9 +98,10 @@
99
98
  "@types/universal-analytics": "^0.4.5",
100
99
  "@types/webpack-env": "^1.16.2",
101
100
  "@types/ws": "^7.4.7",
102
- "@types/yargs-parser": "^21.0.0"
101
+ "@types/yargs-parser": "^21.0.0",
102
+ "schema-dts": "^1.1.2"
103
103
  },
104
104
  "peerDependencies": {
105
- "5htp": "0.3.9"
105
+ "5htp": "0.4.5"
106
106
  }
107
107
  }
@@ -4,11 +4,11 @@
4
4
 
5
5
  // Npm
6
6
  import type { VNode } from 'preact';
7
+ import type { Thing } from 'schema-dts';
7
8
 
8
9
  // Core libs
9
10
  import { ClientOrServerRouter, TClientOrServerContext, TRoute, TErrorRoute } from '@common/router';
10
11
  import { TFetcherList, TDataReturnedByFetchers } from '@common/router/request/api';
11
- import { history } from '@client/services/router/request/history';
12
12
 
13
13
  /*----------------------------------
14
14
  - TYPES
@@ -50,7 +50,9 @@ export type TPageResource = {
50
50
  } | {
51
51
  url: string,
52
52
  preload?: boolean
53
- })
53
+ })
54
+
55
+ type TMetasList = ({ $: string } & { [key: string]: string })[]
54
56
 
55
57
  const debug = false;
56
58
 
@@ -65,8 +67,12 @@ export default abstract class PageResponse<TRouter extends ClientOrServerRouter
65
67
  public description?: string;
66
68
  public bodyClass: Set<string> = new Set<string>();
67
69
  public bodyId?: string;
70
+ public url: string;
68
71
 
69
72
  // Resources
73
+ public head: TMetasList = [];
74
+ public metas: { [name: string]: string } = {};
75
+ public jsonld: Thing[] = [];
70
76
  public scripts: TPageResource[] = [];
71
77
  public style: TPageResource[] = [];
72
78
 
@@ -82,6 +88,8 @@ export default abstract class PageResponse<TRouter extends ClientOrServerRouter
82
88
 
83
89
  this.chunkId = context.route.options["id"];
84
90
 
91
+ this.url = context.request.url;
92
+
85
93
  this.fetchers = this.createFetchers(route.options.data);
86
94
 
87
95
  }
@@ -48,6 +48,7 @@ type AppIdentityConfig = {
48
48
 
49
49
  },
50
50
 
51
+ locale: string
51
52
  language: string
52
53
  maincolor: string,
53
54
  iconsPack?: string,
@@ -72,22 +72,46 @@ export default class DocumentRenderer<TRouter extends Router> {
72
72
  <meta content="IE=edge" httpEquiv="X-UA-Compatible" />
73
73
  <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
74
74
 
75
- {/* Basique*/}
76
- <meta content={this.app.identity.web.title} name="apple-mobile-web-app-title" />
75
+ {/* Mobile */}
76
+ <meta name="application-name" content={this.app.identity.web.title} />
77
+ <meta name="apple-mobile-web-app-title" content={this.app.identity.web.title} />
78
+ <meta name="apple-mobile-web-app-title" content={this.app.identity.web.title} />
79
+ <meta content={this.app.identity.author.name} name="author" />
80
+ <meta name="theme-color" content={this.app.identity.maincolor} />
81
+ <meta name="msapplication-TileColor" content={this.app.identity.maincolor} />
82
+ <meta name="apple-mobile-web-app-capable" content="yes" />
83
+ <meta name="mobile-web-app-capable" content="yes" />
84
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
85
+
86
+ {/* https://stackoverflow.com/questions/48956465/favicon-standard-2019-svg-ico-png-and-dimensions */}
87
+ {/*<link rel="manifest" href={RES['manifest.json']} />*/}
88
+ <link rel="shortcut icon" href="/public/app/favicon.ico" />
89
+ <link rel="icon" type="image/png" sizes="16x16" href="/public/app/favicon-16x16.png" />
90
+ <link rel="icon" type="image/png" sizes="32x32" href="/public/app/favicon-32x32.png" />
91
+ <link rel="apple-touch-icon" sizes="180x180" href="/public/app/apple-touch-icon-180x180.png" />
92
+ <meta name="msapplication-config" content="/public/app/browserconfig.xml" />
93
+
94
+ {/* Page */}
77
95
  <title>{page.title}</title>
78
96
  <meta content={page.description} name="description" />
79
97
  <link rel="canonical" href={canonicalUrl} />
80
98
 
81
- {this.metas( page )}
99
+ {/* SEO, social medias, OG tags, ... */}
100
+ {page.head.map(({ $, ...attrs }) => (
101
+ React.createElement($, attrs)
102
+ ))}
82
103
 
83
104
  {this.styles( page )}
84
105
 
85
106
  {await this.scripts( response, page )}
86
107
 
87
108
  {/* Rich Snippets: https://schema.org/docs/full.html + https://jsonld.com/ */}
88
- {/* <script type="application/ld+json" dangerouslySetInnerHTML={{
89
- __html: JSON.stringify( schemaGenerator(page, route) )
90
- }}/> */}
109
+ <script type="application/ld+json" dangerouslySetInnerHTML={{
110
+ __html: JSON.stringify({
111
+ '@context': 'http://schema.org',
112
+ '@graph': page.jsonld
113
+ })
114
+ }}/>
91
115
 
92
116
  </head>
93
117
  <body {...attrsBody} dangerouslySetInnerHTML={{ __html: html }}></body>
@@ -95,41 +119,6 @@ export default class DocumentRenderer<TRouter extends Router> {
95
119
  )
96
120
  }
97
121
 
98
- private metas( page: Page ) {
99
- return <>
100
- {/* Réseaux sociaux */}
101
- {/*page.metas.metasAdditionnelles && Object.entries(page.metas.metasAdditionnelles).map(
102
- ([ cle, val ]: [ string, string ]) => (
103
- <meta name={cle} content={val} />
104
- )
105
- )*/}
106
-
107
- {/* Mobile */}
108
- <meta name="theme-color" content={this.app.identity.maincolor} />
109
- <meta name="msapplication-TileColor" content={this.app.identity.maincolor} />
110
- <meta name="apple-mobile-web-app-capable" content="yes" />
111
- <meta name="apple-mobile-web-app-title" content={this.app.identity.web.title} />
112
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
113
-
114
- {/* Identité */}
115
- <meta name="mobile-web-app-capable" content="yes" />
116
- <meta name="application-name" content={this.app.identity.web.title} />
117
- <meta name="type" content="website" />
118
-
119
- {/* https://stackoverflow.com/questions/48956465/favicon-standard-2019-svg-ico-png-and-dimensions */}
120
- {/*<link rel="manifest" href={RES['manifest.json']} />*/}
121
- <link rel="shortcut icon" href="/public/app/favicon.ico" />
122
- <link rel="icon" type="image/png" sizes="16x16" href="/public/app/favicon-16x16.png" />
123
- <link rel="icon" type="image/png" sizes="32x32" href="/public/app/favicon-32x32.png" />
124
- <link rel="apple-touch-icon" sizes="180x180" href="/public/app/apple-touch-icon-180x180.png" />
125
- <meta name="msapplication-config" content="/public/app/browserconfig.xml" />
126
-
127
- {page.metas?.map(({ $, ...attrs }) => (
128
- React.createElement($, attrs)
129
- ))}
130
- </>
131
- }
132
-
133
122
  private styles( page: Page ) {
134
123
  return <>
135
124
  <link rel="stylesheet" type="text/css" href={"/public/icons.css?" + BUILD_ID} />
@@ -4,8 +4,7 @@
4
4
 
5
5
  // Npm
6
6
  import React from 'react';
7
- import renderToString from "preact-render-to-string";
8
- const safeStringify = require('fast-safe-stringify'); // remplace les références circulairs par un [Circular]
7
+ import renderToString from "preact-render-to-string";
9
8
 
10
9
  // Core
11
10
  import { default as Router, TRouterContext } from "@server/services/router";
@@ -27,16 +26,12 @@ const seoLimits = {
27
26
  description: 255
28
27
  }
29
28
 
30
- type TMetasList = ({ $: string } & { [key: string]: string })[]
31
-
32
29
  /*----------------------------------
33
30
  - FONCTION
34
31
  ----------------------------------*/
35
32
 
36
33
  export default class Page<TRouter extends Router = Router> extends PageResponse<TRouter> {
37
34
 
38
- public metas: TMetasList = [];
39
-
40
35
  public constructor(
41
36
  public route: TRoute | TErrorRoute,
42
37
  public renderer: TFrontRenderer,
@@ -75,6 +70,10 @@ export default class Page<TRouter extends Router = Router> extends PageResponse<
75
70
  if (html === undefined)
76
71
  throw new Error(`Page HTML is empty (undefined)`);
77
72
 
73
+ // Metas
74
+ this.buildMetas();
75
+ this.buildJsonLd();
76
+
78
77
  // Un chunk peut regrouper plusieurs fihciers css / js
79
78
  // L'id du chunk est injecté depuis le plugin babel
80
79
  this.addChunks();
@@ -119,52 +118,72 @@ export default class Page<TRouter extends Router = Router> extends PageResponse<
119
118
  }
120
119
  }
121
120
 
122
- private getSocialMetas() {
123
- /*if (page.metas.imageUrl)
124
- page.metas.imageUrl = app.web.url + page.metas.imageUrl;
125
-
126
- if (!page.metas.metasAdditionnelles)
127
- page.metas.metasAdditionnelles = {};
128
-
129
- page.metas.metasAdditionnelles = {
130
- 'og:locale': 'fr_FR',
131
- 'og:site_name': app.identity.web.title,
132
- 'og:title': page.title,
133
- 'og:description': page.description,
134
- 'og:url': fullUrl,
135
-
136
- ...(page.metas.imageUrl ? {
137
- 'og:image': page.metas.imageUrl,
138
- ...(page.metas.imageX ? {
139
- 'og:image:width': page.metas.imageX.toString()
140
- } : {}),
141
- ...(page.metas.imageY ? {
142
- 'og:image:height': page.metas.imageY.toString()
143
- } : {})
144
- } : {}),
121
+ private buildMetas() {
122
+
123
+ const metas = {
124
+
125
+ 'og:type': 'website',
126
+ 'og:locale': this.app.identity.locale,
127
+ 'og:site_name': this.app.identity.web.title,
128
+ 'og:url': this.context.request.req.url,
129
+
130
+ 'og:title': this.title,
131
+ 'og:description': this.description,
145
132
 
133
+ 'twitter:url': this.context.request.req.url,
146
134
  'twitter:card': 'summary_large_image',
147
- 'twitter:title': page.title,
148
- 'twitter:description': page.description,
149
- 'twitter:url': fullUrl,
150
- 'twitter:text:title': page.title,
151
-
152
- ...(app.identity.social?.twitter? ? {
153
- 'twitter:site': `@${app.identity.social?.twitter?}`,
154
- 'twitter:creator': `@${app.identity.social?.twitter?}`
155
- } : {}),
156
-
157
- ...(page.metas.imageUrl ? {
158
- 'twitter:image': page.metas.imageUrl,
159
- ...(page.metas.imageX ? {
160
- 'twitter:image:width': page.metas.imageX.toString()
161
- } : {}),
162
- ...(page.metas.imageY ? {
163
- 'twitter:image:height': page.metas.imageY.toString()
164
- } : {})
165
- } : {}),
166
-
167
- ...page.metas.metasAdditionnelles
168
- };*/
135
+ 'twitter:title': this.title,
136
+ 'twitter:text:title': this.title,
137
+ 'twitter:description': this.description,
138
+
139
+ ...this.metas
140
+ };
141
+
142
+ for (const key in metas) {
143
+ const value = metas[key];
144
+ if (value === "") continue;
145
+ this.head.push({ $: 'meta', property: key, content: value });
146
+ }
147
+ }
148
+
149
+ private buildJsonLd() {
150
+ this.jsonld.push({
151
+ '@type': 'Organization',
152
+ '@id': this.router.url('/#organization'),
153
+ name: this.app.identity.author.name,
154
+ url: this.app.identity.author.url,
155
+ logo: {
156
+ '@type': 'ImageObject',
157
+ '@id': this.router.url('/#logo'),
158
+ url: this.router.url('/public/app/android-chrome-512x512.png'),
159
+ width: "512px",
160
+ height: "512px",
161
+ caption: this.app.identity.author.name
162
+ },
163
+ sameAs: []
164
+ }, {
165
+ '@type': 'WebSite',
166
+ '@id': this.router.url('/#website'),
167
+ url: this.router.url('/'),
168
+ name: this.app.identity.name,
169
+ description: this.app.identity.description,
170
+ "publisher": {
171
+ "@id": this.router.url('/#organization'),
172
+ },
173
+ inLanguage: this.app.identity.locale,
174
+ potentialAction: [],
175
+ }, {
176
+ '@type': "WebPage",
177
+ '@id': this.url,
178
+ url: this.url,
179
+
180
+ "isPartOf": {
181
+ "@id": this.router.url('/#website'),
182
+ },
183
+
184
+ name: this.title,
185
+ description: this.description,
186
+ inLanguage: this.app.identity.locale,
187
+ });
169
188
  }
170
189
  }
@@ -1,70 +0,0 @@
1
- // Types
2
-
3
-
4
- export default (page: TInfosPage, route: TInfosRoute) => {
5
- //const isSubPage = pageTitle && url.pathname !== '/';
6
-
7
- let schemaPage = {
8
- '@type': "WebPage",
9
- '@id': route.requete.url,
10
- name: page.title,
11
- url: route.requete.url,
12
- description: page.description,
13
- potentialAction: [{
14
- "@type": "SearchAction"
15
- }],
16
-
17
- ...(page.metas.richSnippetsPage === undefined ? {} : page.metas.richSnippetsPage)
18
- }
19
-
20
- /*if (isSubPage)
21
- schemaPage.breadcrumb = {
22
- '@context': 'http://schema.org',
23
- '@type': 'BreadcrumbList',
24
- itemListElement: [
25
- {
26
- '@type': 'ListItem',
27
- position: 1,
28
- item: {
29
- '@id': siteUrl,
30
- name: siteTitle,
31
- },
32
- },
33
- {
34
- '@type': 'ListItem',
35
- position: 2,
36
- item: {
37
- '@id': canonical,
38
- name: pageTitle,
39
- },
40
- },
41
- ],
42
- }*/
43
-
44
- let schemaFinal = page.metas.richSnippets === undefined ? schemaPage : {
45
- ...page.metas.richSnippets,
46
- mainEntityOfPage: schemaPage
47
- };
48
-
49
- return {
50
- '@context': 'http://schema.org',
51
- ...schemaFinal
52
- }
53
-
54
- /*
55
- '@type': 'WebSite',
56
- url: SEO.url,
57
- name: SEO.titre.site,
58
- inLanguage: 'French',
59
- about: [{
60
- "@type": "Thing",
61
- name: "Gagner de l'argent sur internet"
62
- }, {
63
- "@type": "Thing",
64
- name: "Développer son business en ligne"
65
- }, {
66
- "@type": "Thing",
67
- name: "Marketing"
68
- }]
69
- */
70
- };