lupine.web 1.1.4 → 1.1.5

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 (41) hide show
  1. package/README.md +66 -3
  2. package/jsx-runtime/index.js +14 -14
  3. package/jsx-runtime/package.json +16 -16
  4. package/jsx-runtime/src/index.d.ts +2 -2
  5. package/package.json +53 -52
  6. package/src/core/bind-attributes.ts +61 -61
  7. package/src/core/bind-lang.ts +52 -52
  8. package/src/core/bind-links.ts +26 -16
  9. package/src/core/bind-meta.tsx +52 -52
  10. package/src/core/bind-ref.ts +51 -51
  11. package/src/core/bind-styles.ts +239 -239
  12. package/src/core/bind-theme.ts +53 -53
  13. package/src/core/camel-to-hyphens.ts +3 -3
  14. package/src/core/export-lupine.ts +80 -80
  15. package/src/core/index.ts +17 -17
  16. package/src/core/initialize.ts +116 -116
  17. package/src/core/mount-component.ts +72 -68
  18. package/src/core/page-loaded-events.ts +16 -16
  19. package/src/core/page-router.ts +180 -180
  20. package/src/core/render-component.ts +230 -233
  21. package/src/core/replace-innerhtml.ts +23 -23
  22. package/src/core/server-cookie.ts +24 -24
  23. package/src/global.d.ts +66 -66
  24. package/src/index.ts +14 -14
  25. package/src/jsx.ts +1044 -1043
  26. package/src/lib/cookie.ts +44 -44
  27. package/src/lib/debug-watch.ts +32 -32
  28. package/src/lib/index.ts +7 -7
  29. package/src/lib/is-frontend.ts +3 -3
  30. package/src/lib/logger.ts +55 -55
  31. package/src/lib/unique-id.ts +40 -40
  32. package/src/lib/web-config.ts +79 -79
  33. package/src/lib/web-env.ts +99 -99
  34. package/src/models/index.ts +4 -4
  35. package/src/models/json-props.ts +8 -8
  36. package/src/models/simple-storage-props.ts +9 -9
  37. package/src/models/theme-props.ts +7 -7
  38. package/src/models/to-client-delivery-props.ts +8 -8
  39. package/src/styles/css-styles.ts +814 -814
  40. package/src/styles/index.ts +4 -4
  41. package/tsconfig.json +113 -113
package/README.md CHANGED
@@ -1,3 +1,66 @@
1
- # lupine.web
2
-
3
- lupine.web is a React-like, extremely fast, small size and lightweight frontend framework.
1
+ # lupine.web
2
+
3
+ **lupine.web** is a React-like, extremely fast, small-size, and lightweight frontend framework designed for modern web development. It focuses on performance, simplicity, and a full-stack experience when paired with `lupine.api`.
4
+
5
+ ## Why lupine.web?
6
+
7
+ ### 🚀 Zero-Dependency & Lightweight
8
+
9
+ We believe in keeping things simple. `lupine.web` has **zero external dependencies**, resulting in a tiny bundle size and lightning-fast load times. It uses TSX syntax, so if you know React, you already feel at home.
10
+
11
+ ### 🎨 Built-in CSS-in-JS
12
+
13
+ Forget about setting up complex CSS loaders or external styling libraries. `lupine.web` comes with a powerful, built-in CSS-in-JS solution.
14
+
15
+ - **Scoped Styles**: Styles are automatically scoped to your components to prevent collisions.
16
+ - **Nesting Support**: Write cleaner CSS with nested selectors (e.g., `&:hover`, `& > span`).
17
+ - **Theming**: Native support for light/dark modes and custom themes.
18
+
19
+ ```tsx
20
+ const MyButton = (props) => (
21
+ <button
22
+ css={{
23
+ backgroundColor: 'blue',
24
+ color: 'white',
25
+ '&:hover': { backgroundColor: 'darkblue' },
26
+ [MediaQueryRange.Mobile]: { width: '100%' },
27
+ }}
28
+ >
29
+ {props.children}
30
+ </button>
31
+ );
32
+ ```
33
+
34
+ ### 🛣️ Powerful Router
35
+
36
+ Our functional router is designed for flexibility and control.
37
+
38
+ - **Route Guards**: Easily implement authentication checks or permissions.
39
+ - **Nested Routes**: Organize your application with sub-routers for modular architecture.
40
+ - **SSR Ready**: Routes work seamlessly on both server and client.
41
+
42
+ ```typescript
43
+ const pageRouter = new PageRouter();
44
+ // Middleware/Guard example
45
+ pageRouter.setFilter(async (props) => {
46
+ if (!checkAuth(props)) return <Redirect to='/login' />;
47
+ return null; // Pass
48
+ });
49
+ pageRouter.use('/dashboard/*', DashboardRouter);
50
+ ```
51
+
52
+ ### ⚡ Server-Side Rendering (SSR) First
53
+
54
+ Visual performance is critical. `lupine.web` is built with SSR in mind from day one.
55
+
56
+ - **No Flashing**: Content is rendered on the server, ensuring users see the page immediately.
57
+ - **SEO Friendly**: Fully customizable Metadata and Open Graph (OG) tags for social sharing.
58
+ - **Hydration**: The client takes over smoothly without re-rendering the entire tree.
59
+
60
+ ### 🌍 Internationalization (i18n)
61
+
62
+ Go global with ease. Built-in support for multi-language applications allows you to switch languages dynamically without complex configuration.
63
+
64
+ ### 🛠️ Environment Configuration
65
+
66
+ Manage your application environments efficiently. `lupine.web` supports loading environment variables (from `.env` files via `lupine.api`) and injecting strictly filtered configurations into the frontend.
@@ -1,14 +1,14 @@
1
- /**
2
- * JSX.Element factory used by Typescript's JSX transform
3
- * @param type
4
- * @param props
5
- */
6
- function jsx(type, props) {
7
- return { type, props };
8
- }
9
-
10
- function Fragment(props) {
11
- return { type: 'Fragment', props };
12
- }
13
-
14
- export { jsx as jsx, jsx as jsxs, jsx as jsxDEV, Fragment };
1
+ /**
2
+ * JSX.Element factory used by Typescript's JSX transform
3
+ * @param type
4
+ * @param props
5
+ */
6
+ function jsx(type, props) {
7
+ return { type, props };
8
+ }
9
+
10
+ function Fragment(props) {
11
+ return { type: 'Fragment', props };
12
+ }
13
+
14
+ export { jsx as jsx, jsx as jsxs, jsx as jsxDEV, Fragment };
@@ -1,16 +1,16 @@
1
- {
2
- "name": "jsx-runtime",
3
- "amdName": "jsxRuntime",
4
- "version": "1.0.0",
5
- "private": true,
6
- "description": "lupine.web JSX runtime",
7
- "main": "index.js",
8
- "module": "index.js",
9
- "umd:main": "index.js",
10
- "source": "index.js",
11
- "types": "src/index.d.ts",
12
- "license": "MIT",
13
- "peerDependencies": {
14
- "lupine.web": "^1.0.0"
15
- }
16
- }
1
+ {
2
+ "name": "jsx-runtime",
3
+ "amdName": "jsxRuntime",
4
+ "version": "1.0.0",
5
+ "private": true,
6
+ "description": "lupine.web JSX runtime",
7
+ "main": "index.js",
8
+ "module": "index.js",
9
+ "umd:main": "index.js",
10
+ "source": "index.js",
11
+ "types": "src/index.d.ts",
12
+ "license": "MIT",
13
+ "peerDependencies": {
14
+ "lupine.web": "^1.0.0"
15
+ }
16
+ }
@@ -1,2 +1,2 @@
1
- import { JSXInternal } from '../../src/jsx';
2
- export { JSXInternal as JSX };
1
+ import { JSXInternal } from '../../src/jsx';
2
+ export { JSXInternal as JSX };
package/package.json CHANGED
@@ -1,52 +1,53 @@
1
- {
2
- "name": "lupine.web",
3
- "version": "1.1.4",
4
- "license": "MIT",
5
- "author": "uuware.com",
6
- "homepage": "https://github.com/uuware/lupine.js",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/uuware/lupine.js.git",
10
- "directory": "packages/lupine.web"
11
- },
12
- "description": "lupine.web is a extremely fast, small size and lightweight frontend framework, using React TSX syntax.",
13
- "main": "src/index.ts",
14
- "source": "src/index.ts",
15
- "types": "src/index.ts",
16
- "engines": {
17
- "node": ">= 20"
18
- },
19
- "exports": {
20
- ".": {
21
- "types": "./src/index.ts",
22
- "browser": "./src/index.ts",
23
- "umd": "./src/index.ts",
24
- "import": "./src/index.ts",
25
- "require": "./src/index.ts"
26
- },
27
- "./jsx-runtime": {
28
- "types": "./jsx-runtime/src/index.d.ts",
29
- "browser": "./jsx-runtime/index.js",
30
- "umd": "./jsx-runtime/index.js",
31
- "import": "./jsx-runtime/index.js",
32
- "require": "./jsx-runtime/index.js"
33
- }
34
- },
35
- "keywords": [
36
- "frontend",
37
- "responsive",
38
- "lightweight",
39
- "grid"
40
- ],
41
- "scripts": {
42
- "note": "echo 'build is not needed as the typescript code is supposed to be referred directly from other projects'",
43
- "npm-publish": "npm publish --access public",
44
- "build": "tsc"
45
- },
46
- "peerDependencies": {
47
- "jsx-runtime": "^1.0.0"
48
- },
49
- "devDependencies": {
50
- "@types/node": "^22.10.5"
51
- }
52
- }
1
+ {
2
+ "name": "lupine.web",
3
+ "version": "1.1.5",
4
+ "license": "MIT",
5
+ "author": "uuware.com",
6
+ "homepage": "https://github.com/uuware/lupine.js",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/uuware/lupine.js.git",
10
+ "directory": "packages/lupine.web"
11
+ },
12
+ "description": "lupine.web is a extremely fast, small size and lightweight frontend framework, using React TSX syntax.",
13
+ "main": "src/index.ts",
14
+ "source": "src/index.ts",
15
+ "types": "src/index.ts",
16
+ "engines": {
17
+ "node": ">= 20"
18
+ },
19
+ "exports": {
20
+ ".": {
21
+ "types": "./src/index.ts",
22
+ "browser": "./src/index.ts",
23
+ "umd": "./src/index.ts",
24
+ "import": "./src/index.ts",
25
+ "require": "./src/index.ts"
26
+ },
27
+ "./jsx-runtime": {
28
+ "types": "./jsx-runtime/src/index.d.ts",
29
+ "browser": "./jsx-runtime/index.js",
30
+ "umd": "./jsx-runtime/index.js",
31
+ "import": "./jsx-runtime/index.js",
32
+ "require": "./jsx-runtime/index.js"
33
+ }
34
+ },
35
+ "keywords": [
36
+ "frontend",
37
+ "responsive",
38
+ "lightweight",
39
+ "grid"
40
+ ],
41
+ "scripts": {
42
+ "note": "echo 'build is not needed as the typescript code is supposed to be referred directly from other projects'",
43
+ "npm-publish": "npm publish --access public",
44
+ "build-min": "tsc && npx esbuild ./dist/build.js --minify --outfile=./dist/build.min.js",
45
+ "build": "tsc"
46
+ },
47
+ "peerDependencies": {
48
+ "jsx-runtime": "^1.0.0"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^22.10.5"
52
+ }
53
+ }
@@ -1,61 +1,61 @@
1
- import { bindRef } from './bind-ref';
2
-
3
- export const bindAttributesChildren = (topEl: Element, children: any) => {
4
- for (let i = 0; i < children.length; i++) {
5
- const item = children[i];
6
- if (item && item.type && item.props) {
7
- bindAttributes(topEl, item.type, item.props);
8
- } else if (item && Array.isArray(item)) {
9
- bindAttributesChildren(topEl, item);
10
- } else if (
11
- typeof item !== 'undefined' &&
12
- item !== null &&
13
- typeof item !== 'string' &&
14
- typeof item !== 'number' &&
15
- typeof item !== 'boolean'
16
- ) {
17
- console.warn(`Unexpected children:`, item);
18
- }
19
- }
20
- };
21
-
22
- export const bindAttributes = (topEl: Element, type: any, props: any) => {
23
- const newProps = (props._result && props._result.props) || props;
24
- if (newProps._id) {
25
- let el = topEl.querySelector(`[${newProps._id}]`);
26
- if (!el && topEl.getAttribute(newProps._id) === '') {
27
- el = topEl;
28
- }
29
- if (el) {
30
- for (let i in newProps) {
31
- if (i === 'ref') {
32
- bindRef(type, newProps, el);
33
- // } else if (i === "css") {
34
- // mountStyles(`[${newProps._id}]`, `[${newProps._id}]`, newProps[i]);
35
- } else if (i[0] === 'o' && i[1] === 'n') {
36
- let name = i;
37
- if (name.toLowerCase() in el) name = name.toLowerCase().slice(2);
38
- else name = name.slice(2);
39
- // console.log('===bind event', name, el);
40
- el.addEventListener(name, newProps[i]);
41
- }
42
- }
43
- }
44
- }
45
-
46
- if (newProps.children && Array.isArray(newProps.children)) {
47
- bindAttributesChildren(topEl, newProps.children);
48
- } else if (newProps._result && newProps._result.type !== 'Fragment' && newProps._result.props) {
49
- bindAttributes(topEl, newProps._result.type, newProps._result.props);
50
- } else if (newProps.children && newProps.children.type && newProps.children.props) {
51
- bindAttributes(topEl, newProps.children.type, newProps.children.props);
52
- } else if (
53
- !newProps.children ||
54
- typeof newProps.children === 'string' ||
55
- typeof newProps.children === 'number' ||
56
- typeof newProps.children === 'boolean'
57
- ) {
58
- } else {
59
- console.warn(`Unexpected children:`, newProps.children, type, props);
60
- }
61
- };
1
+ import { bindRef } from './bind-ref';
2
+
3
+ export const bindAttributesChildren = (topEl: Element, children: any) => {
4
+ for (let i = 0; i < children.length; i++) {
5
+ const item = children[i];
6
+ if (item && item.type && item.props) {
7
+ bindAttributes(topEl, item.type, item.props);
8
+ } else if (item && Array.isArray(item)) {
9
+ bindAttributesChildren(topEl, item);
10
+ } else if (
11
+ typeof item !== 'undefined' &&
12
+ item !== null &&
13
+ typeof item !== 'string' &&
14
+ typeof item !== 'number' &&
15
+ typeof item !== 'boolean'
16
+ ) {
17
+ console.warn(`Unexpected children:`, item);
18
+ }
19
+ }
20
+ };
21
+
22
+ export const bindAttributes = (topEl: Element, type: any, props: any) => {
23
+ const newProps = (props._result && props._result.props) || props;
24
+ if (newProps._id) {
25
+ let el = topEl.querySelector(`[${newProps._id}]`);
26
+ if (!el && topEl.getAttribute(newProps._id) === '') {
27
+ el = topEl;
28
+ }
29
+ if (el) {
30
+ for (let i in newProps) {
31
+ if (i === 'ref') {
32
+ bindRef(type, newProps, el);
33
+ // } else if (i === "css") {
34
+ // mountStyles(`[${newProps._id}]`, `[${newProps._id}]`, newProps[i]);
35
+ } else if (i[0] === 'o' && i[1] === 'n') {
36
+ let name = i;
37
+ if (name.toLowerCase() in el) name = name.toLowerCase().slice(2);
38
+ else name = name.slice(2);
39
+ // console.log('===bind event', name, el);
40
+ el.addEventListener(name, newProps[i]);
41
+ }
42
+ }
43
+ }
44
+ }
45
+
46
+ if (newProps.children && Array.isArray(newProps.children)) {
47
+ bindAttributesChildren(topEl, newProps.children);
48
+ } else if (newProps._result && newProps._result.type !== 'Fragment' && newProps._result.props) {
49
+ bindAttributes(topEl, newProps._result.type, newProps._result.props);
50
+ } else if (newProps.children && newProps.children.type && newProps.children.props) {
51
+ bindAttributes(topEl, newProps.children.type, newProps.children.props);
52
+ } else if (
53
+ !newProps.children ||
54
+ typeof newProps.children === 'string' ||
55
+ typeof newProps.children === 'number' ||
56
+ typeof newProps.children === 'boolean'
57
+ ) {
58
+ } else {
59
+ console.warn(`Unexpected children:`, newProps.children, type, props);
60
+ }
61
+ };
@@ -1,52 +1,52 @@
1
- import { setCookie } from '../lib/cookie';
2
- import { isFrontEnd } from '../lib/is-frontend';
3
- import { getEitherCookie } from './server-cookie';
4
-
5
- // The FE only loads one language for the consideration of the size
6
-
7
- export const defaultLangName = 'en';
8
- export const langCookieName = 'lang';
9
- export const updateLangEventName = 'updateLang';
10
- const _langCfg: any = { defaultLang: defaultLangName, langs: {} };
11
- export type OneLangProps = { [key: string]: string };
12
- export const bindLang = (defaultLang: string, langs: OneLangProps) => {
13
- _langCfg.defaultLang = defaultLang;
14
- _langCfg.langs = langs;
15
-
16
- // set to cookie
17
- getCurrentLang();
18
- };
19
-
20
- export const getCurrentLang = () => {
21
- let langName = getEitherCookie(langCookieName) as string;
22
- if (!langName || !_langCfg.langs[langName]) {
23
- langName = _langCfg.defaultLang;
24
- if (isFrontEnd()) {
25
- setCookie(langCookieName, _langCfg.defaultLang);
26
- }
27
- }
28
- return { langName, langs: _langCfg.langs };
29
- };
30
-
31
- // the FE needs to reload the page when the language is changed
32
- export const updateLang = (langName: string) => {
33
- // Lang is only updated in Browser
34
- _langCfg.defaultLang = langName;
35
- if (!isFrontEnd()) {
36
- return;
37
- }
38
-
39
- setCookie(langCookieName, langName);
40
- // document.documentElement.setAttribute(langAttributeName, langName);
41
-
42
- // // update lang for all iframe
43
- // const allIframe = document.querySelectorAll('iframe');
44
- // for (let i = 0; i < allIframe.length; i++) {
45
- // if (allIframe[i].contentWindow && allIframe[i].contentWindow!.top === window) {
46
- // allIframe[i].contentWindow!.document.documentElement.setAttribute(langAttributeName, langName);
47
- // }
48
- // }
49
-
50
- const event = new CustomEvent(updateLangEventName, { detail: langName });
51
- window.dispatchEvent(event);
52
- };
1
+ import { setCookie } from '../lib/cookie';
2
+ import { isFrontEnd } from '../lib/is-frontend';
3
+ import { getEitherCookie } from './server-cookie';
4
+
5
+ // The FE only loads one language for the consideration of the size
6
+
7
+ export const defaultLangName = 'en';
8
+ export const langCookieName = 'lang';
9
+ export const updateLangEventName = 'updateLang';
10
+ const _langCfg: any = { defaultLang: defaultLangName, langs: {} };
11
+ export type OneLangProps = { [key: string]: string };
12
+ export const bindLang = (defaultLang: string, langs: OneLangProps) => {
13
+ _langCfg.defaultLang = defaultLang;
14
+ _langCfg.langs = langs;
15
+
16
+ // set to cookie
17
+ getCurrentLang();
18
+ };
19
+
20
+ export const getCurrentLang = () => {
21
+ let langName = getEitherCookie(langCookieName) as string;
22
+ if (!langName || !_langCfg.langs[langName]) {
23
+ langName = _langCfg.defaultLang;
24
+ if (isFrontEnd()) {
25
+ setCookie(langCookieName, _langCfg.defaultLang);
26
+ }
27
+ }
28
+ return { langName, langs: _langCfg.langs };
29
+ };
30
+
31
+ // the FE needs to reload the page when the language is changed
32
+ export const updateLang = (langName: string) => {
33
+ // Lang is only updated in Browser
34
+ _langCfg.defaultLang = langName;
35
+ if (!isFrontEnd()) {
36
+ return;
37
+ }
38
+
39
+ setCookie(langCookieName, langName);
40
+ // document.documentElement.setAttribute(langAttributeName, langName);
41
+
42
+ // // update lang for all iframe
43
+ // const allIframe = document.querySelectorAll('iframe');
44
+ // for (let i = 0; i < allIframe.length; i++) {
45
+ // if (allIframe[i].contentWindow && allIframe[i].contentWindow!.top === window) {
46
+ // allIframe[i].contentWindow!.document.documentElement.setAttribute(langAttributeName, langName);
47
+ // }
48
+ // }
49
+
50
+ const event = new CustomEvent(updateLangEventName, { detail: langName });
51
+ window.dispatchEvent(event);
52
+ };
@@ -1,16 +1,26 @@
1
- import { _lupineJs } from './export-lupine';
2
-
3
- export function bindLinks(el: Element | Document) {
4
- const links = el.getElementsByTagName('a');
5
- for (var i = 0, l = links.length; i < l; i++) {
6
- let href = new URL(links[i].href, document.baseURI).href;
7
- if (links[i].target !== '_blank' && href.startsWith(document.location.origin)) {
8
- href = href.substring(document.location.origin.length);
9
- // console.log(`====${href}, javascript:init('${document.links[i].href}')`);
10
- links[i].onclick = () => {
11
- _lupineJs.initializePage(href);
12
- return false;
13
- };
14
- }
15
- }
16
- }
1
+ import { _lupineJs } from './export-lupine';
2
+
3
+ export function bindLinks(el: Element | Document) {
4
+ const links = el.getElementsByTagName('a');
5
+ for (var i = 0, l = links.length; i < l; i++) {
6
+ let originalHref = links[i].getAttribute('href');
7
+ if (!originalHref || originalHref.startsWith('javascript:')) continue;
8
+ if (originalHref.startsWith('#')) {
9
+ links[i].onclick = () => {
10
+ const id = decodeURIComponent(originalHref!.substring(1));
11
+ document.getElementById(id)?.scrollIntoView(true);
12
+ return false;
13
+ };
14
+ continue;
15
+ }
16
+ let href = new URL(links[i].href, document.baseURI).href;
17
+ if (links[i].target !== '_blank' && href.startsWith(document.location.origin)) {
18
+ href = href.substring(document.location.origin.length);
19
+ // console.log(`====${href}, javascript:init('${document.links[i].href}')`);
20
+ links[i].onclick = () => {
21
+ _lupineJs.initializePage(href);
22
+ return false;
23
+ };
24
+ }
25
+ }
26
+ }
@@ -1,52 +1,52 @@
1
- // import { bindPageResetEvent } from '../core/page-reset-events';
2
-
3
- let _pageTitle = { value: '', defaultValue: '' };
4
- export const setPageTitle = (title: string) => {
5
- _pageTitle.value = title;
6
- };
7
-
8
- export const getPageTitle = () => {
9
- return _pageTitle.value || _pageTitle.defaultValue;
10
- };
11
-
12
- export const setDefaultPageTitle = (title: string) => {
13
- _pageTitle.defaultValue = title;
14
- };
15
-
16
- let _description = { value: '', defaultValue: '' };
17
- export const setMetaDescription = (description: string) => {
18
- _description.value = description;
19
- };
20
-
21
- export const getMetaDescription = () => {
22
- return _description.value || _description.defaultValue;
23
- };
24
-
25
- export const setDefaultMetaDescription = (description: string) => {
26
- _description.defaultValue = description;
27
- };
28
-
29
- let _metaData: { [key: string]: string } = {};
30
- export const addMetaDataTags = (key: string, value: string) => {
31
- if (typeof value === 'undefined') {
32
- delete _metaData[key];
33
- } else {
34
- _metaData[key] = value;
35
- }
36
- };
37
-
38
- export const getMetaDataTags = () => {
39
- return Object.values(getMetaDataObject()).join('\n');
40
- };
41
-
42
- export const getMetaDataObject = () => {
43
- const metaDescription = getMetaDescription();
44
- return metaDescription
45
- ? Object.assign(
46
- {
47
- 'name:description': `<meta name="description" content="${metaDescription}">`,
48
- },
49
- _metaData
50
- )
51
- : _metaData;
52
- };
1
+ // import { bindPageResetEvent } from '../core/page-reset-events';
2
+
3
+ let _pageTitle = { value: '', defaultValue: '' };
4
+ export const setPageTitle = (title: string) => {
5
+ _pageTitle.value = title;
6
+ };
7
+
8
+ export const getPageTitle = () => {
9
+ return _pageTitle.value || _pageTitle.defaultValue;
10
+ };
11
+
12
+ export const setDefaultPageTitle = (title: string) => {
13
+ _pageTitle.defaultValue = title;
14
+ };
15
+
16
+ let _description = { value: '', defaultValue: '' };
17
+ export const setMetaDescription = (description: string) => {
18
+ _description.value = description;
19
+ };
20
+
21
+ export const getMetaDescription = () => {
22
+ return _description.value || _description.defaultValue;
23
+ };
24
+
25
+ export const setDefaultMetaDescription = (description: string) => {
26
+ _description.defaultValue = description;
27
+ };
28
+
29
+ let _metaData: { [key: string]: string } = {};
30
+ export const addMetaDataTags = (key: string, value: string) => {
31
+ if (typeof value === 'undefined') {
32
+ delete _metaData[key];
33
+ } else {
34
+ _metaData[key] = value;
35
+ }
36
+ };
37
+
38
+ export const getMetaDataTags = () => {
39
+ return Object.values(getMetaDataObject()).join('\n');
40
+ };
41
+
42
+ export const getMetaDataObject = () => {
43
+ const metaDescription = getMetaDescription();
44
+ return metaDescription
45
+ ? Object.assign(
46
+ {
47
+ 'name:description': `<meta name="description" content="${metaDescription}">`,
48
+ },
49
+ _metaData
50
+ )
51
+ : _metaData;
52
+ };