@akinon/next 1.14.0 → 1.14.1

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 (119) hide show
  1. package/.eslintrc.js +40 -40
  2. package/.prettierrc +13 -13
  3. package/CHANGELOG.md +6 -0
  4. package/api/auth.ts +231 -231
  5. package/api/cache.ts +44 -44
  6. package/api/client.ts +174 -174
  7. package/api/logout.ts +42 -42
  8. package/bin/pz-check-dependencies.js +98 -98
  9. package/bin/pz-install-plugins.js +33 -33
  10. package/bin/pz-install-theme.js +58 -58
  11. package/bin/pz-postbuild.js +1 -1
  12. package/bin/pz-postdev.js +1 -1
  13. package/bin/pz-postinstall.js +6 -6
  14. package/bin/pz-poststart.js +1 -1
  15. package/bin/pz-prebuild.js +4 -4
  16. package/bin/pz-predev.js +4 -4
  17. package/bin/pz-prestart.js +1 -1
  18. package/bin/run-script.js +44 -44
  19. package/components/accordion.tsx +52 -52
  20. package/components/button.tsx +46 -46
  21. package/components/client-root.tsx +19 -19
  22. package/components/icon.tsx +18 -18
  23. package/components/image.tsx +133 -133
  24. package/components/index.ts +17 -17
  25. package/components/input.tsx +110 -110
  26. package/components/lazy-component.tsx +33 -33
  27. package/components/loader-spinner.tsx +23 -23
  28. package/components/mobile-app-toggler.tsx +26 -26
  29. package/components/oauth-login.tsx +24 -24
  30. package/components/price.tsx +55 -55
  31. package/components/pz-providers.tsx +24 -24
  32. package/components/pz-root.tsx +21 -21
  33. package/components/radio.tsx +18 -18
  34. package/components/react-portal.tsx +45 -45
  35. package/components/redirect-three-d/content/index.tsx +74 -74
  36. package/components/redirect-three-d/index.tsx +17 -17
  37. package/components/trans.tsx +39 -39
  38. package/data/client/account.ts +208 -208
  39. package/data/client/api.ts +85 -85
  40. package/data/client/basket.ts +82 -82
  41. package/data/client/misc.ts +101 -101
  42. package/data/client/product.ts +89 -89
  43. package/data/client/user.ts +99 -99
  44. package/data/client/wishlist.ts +118 -118
  45. package/data/server/category.ts +132 -132
  46. package/data/server/flatpage.ts +21 -21
  47. package/data/server/form.ts +22 -22
  48. package/data/server/index.ts +10 -10
  49. package/data/server/landingpage.ts +24 -24
  50. package/data/server/list.ts +67 -67
  51. package/data/server/menu.ts +35 -35
  52. package/data/server/product.ts +86 -86
  53. package/data/server/seo.ts +48 -48
  54. package/data/server/special-page.ts +47 -47
  55. package/data/server/widget.ts +27 -27
  56. package/data/urls.ts +221 -221
  57. package/hocs/client/index.ts +1 -1
  58. package/hocs/client/with-segment-defaults.tsx +25 -25
  59. package/hocs/server/index.ts +1 -1
  60. package/hocs/server/with-segment-defaults.tsx +85 -85
  61. package/hooks/index.ts +10 -10
  62. package/hooks/use-captcha.tsx +76 -76
  63. package/hooks/use-common-product-attributes.ts +36 -36
  64. package/hooks/use-debounce.ts +20 -20
  65. package/hooks/use-localization.ts +78 -78
  66. package/hooks/use-media-query.ts +36 -36
  67. package/hooks/use-mobile-iframe-handler.ts +23 -23
  68. package/hooks/use-on-click-outside.tsx +28 -28
  69. package/hooks/use-router.ts +45 -45
  70. package/hooks/use-translation.ts +14 -14
  71. package/lib/cache.ts +215 -215
  72. package/localization/index.ts +5 -5
  73. package/localization/provider.tsx +58 -58
  74. package/middlewares/currency.ts +100 -100
  75. package/middlewares/default.ts +256 -256
  76. package/middlewares/index.ts +29 -29
  77. package/middlewares/locale.ts +68 -68
  78. package/middlewares/oauth-login.ts +79 -79
  79. package/middlewares/pretty-url.ts +104 -104
  80. package/middlewares/redirection-payment.ts +160 -160
  81. package/middlewares/three-d-redirection.ts +159 -159
  82. package/middlewares/url-redirection.ts +65 -65
  83. package/package.json +2 -2
  84. package/redux/hooks.ts +7 -7
  85. package/redux/middlewares/index.ts +50 -50
  86. package/redux/reducers/checkout.ts +184 -184
  87. package/redux/reducers/config.ts +28 -28
  88. package/redux/reducers/header.ts +59 -59
  89. package/redux/reducers/root.ts +61 -61
  90. package/sentry/index.ts +27 -27
  91. package/tailwind/rtl.js +137 -137
  92. package/types/commerce/account.ts +64 -64
  93. package/types/commerce/address.ts +94 -94
  94. package/types/commerce/basket.ts +43 -43
  95. package/types/commerce/category.ts +114 -114
  96. package/types/commerce/checkout.ts +143 -143
  97. package/types/commerce/flatpage.ts +7 -7
  98. package/types/commerce/form.ts +66 -66
  99. package/types/commerce/index.ts +12 -12
  100. package/types/commerce/landingpage.ts +7 -7
  101. package/types/commerce/misc.ts +127 -127
  102. package/types/commerce/order.ts +119 -119
  103. package/types/commerce/product.ts +109 -109
  104. package/types/commerce/widget.ts +28 -28
  105. package/types/gtm.ts +16 -16
  106. package/types/index.ts +274 -274
  107. package/types/metadata.ts +7 -7
  108. package/types/next-auth.d.ts +24 -24
  109. package/utils/app-fetch.ts +69 -69
  110. package/utils/deep-merge.js +24 -24
  111. package/utils/image-loader.ts +31 -31
  112. package/utils/index.ts +150 -150
  113. package/utils/localization.ts +29 -29
  114. package/utils/log.ts +138 -138
  115. package/utils/menu-generator.ts +27 -27
  116. package/utils/mobile-3d-iframe.ts +77 -77
  117. package/utils/server-translation.ts +57 -57
  118. package/utils/server-variables.ts +9 -9
  119. package/with-pz-config.js +94 -94
@@ -1,33 +1,33 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const rootDir = path.resolve(process.cwd());
6
- const spawn = require('cross-spawn');
7
- const availablePlugins = require('../plugins');
8
-
9
- let plugins;
10
-
11
- try {
12
- plugins = require(path.resolve(rootDir, './src/plugins.js'));
13
- } catch (error) {}
14
-
15
- let installCmd = [];
16
-
17
- availablePlugins
18
- .filter((p) => plugins?.includes(p))
19
- .forEach((name) => {
20
- installCmd.push(`git+https://bitbucket.org/akinonteam/${name}`);
21
- });
22
-
23
- spawn.sync('yarn', ['cache clean']);
24
- spawn.sync('yarn', ['remove', ...availablePlugins]);
25
-
26
- if (
27
- installCmd.length > 0 &&
28
- !fs.existsSync(path.resolve(rootDir, '../../turbo.json'))
29
- ) {
30
- spawn.sync('yarn', ['add', ...installCmd, '--ignore-scripts'], {
31
- stdio: 'inherit'
32
- });
33
- }
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const rootDir = path.resolve(process.cwd());
6
+ const spawn = require('cross-spawn');
7
+ const availablePlugins = require('../plugins');
8
+
9
+ let plugins;
10
+
11
+ try {
12
+ plugins = require(path.resolve(rootDir, './src/plugins.js'));
13
+ } catch (error) {}
14
+
15
+ let installCmd = [];
16
+
17
+ availablePlugins
18
+ .filter((p) => plugins?.includes(p))
19
+ .forEach((name) => {
20
+ installCmd.push(`git+https://bitbucket.org/akinonteam/${name}`);
21
+ });
22
+
23
+ spawn.sync('yarn', ['cache clean']);
24
+ spawn.sync('yarn', ['remove', ...availablePlugins]);
25
+
26
+ if (
27
+ installCmd.length > 0 &&
28
+ !fs.existsSync(path.resolve(rootDir, '../../turbo.json'))
29
+ ) {
30
+ spawn.sync('yarn', ['add', ...installCmd, '--ignore-scripts'], {
31
+ stdio: 'inherit'
32
+ });
33
+ }
@@ -1,58 +1,58 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const spawn = require('cross-spawn');
6
-
7
- function findBaseDir() {
8
- const insideNodeModules = __dirname.includes('node_modules');
9
-
10
- if (insideNodeModules) {
11
- return path.resolve(__dirname, '../../../../');
12
- } else {
13
- return path.resolve(__dirname, '../../../apps/projectzeropwa');
14
- }
15
- }
16
-
17
- const BASE_DIR = findBaseDir();
18
-
19
- const getFullPath = (relativePath) => path.join(BASE_DIR, relativePath);
20
-
21
- let theme;
22
- try {
23
- theme = require(getFullPath('src/theme.js'));
24
- } catch (error) {
25
- console.error('Error loading theme.js:', error.message);
26
- process.exit(1);
27
- }
28
-
29
- try {
30
- const tsConfigPath = getFullPath('tsconfig.json');
31
-
32
- if (!fs.existsSync(tsConfigPath)) {
33
- throw new Error(`tsconfig.json not found at ${tsConfigPath}`);
34
- }
35
-
36
- const tsConfigContent = fs.readFileSync(tsConfigPath, 'utf8');
37
- const themePaths = ['"./*"'];
38
-
39
- if (theme !== 'default') {
40
- themePaths.splice(0, 0, `"themes/${theme}/*"`);
41
- }
42
-
43
- const newContent = tsConfigContent.replace(
44
- /"@theme\/\*":\s\[.+\]/,
45
- `"@theme/*": [${themePaths.join(', ')}]`
46
- );
47
-
48
- fs.writeFileSync(tsConfigPath, newContent);
49
-
50
- if (fs.existsSync(getFullPath('turbo.json'))) {
51
- spawn.sync('turbo', ['clean'], {
52
- stdio: 'inherit'
53
- });
54
- }
55
- } catch (error) {
56
- console.error('Error modifying tsconfig.json:', error.message);
57
- process.exit(1);
58
- }
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const spawn = require('cross-spawn');
6
+
7
+ function findBaseDir() {
8
+ const insideNodeModules = __dirname.includes('node_modules');
9
+
10
+ if (insideNodeModules) {
11
+ return path.resolve(__dirname, '../../../../');
12
+ } else {
13
+ return path.resolve(__dirname, '../../../apps/projectzeropwa');
14
+ }
15
+ }
16
+
17
+ const BASE_DIR = findBaseDir();
18
+
19
+ const getFullPath = (relativePath) => path.join(BASE_DIR, relativePath);
20
+
21
+ let theme;
22
+ try {
23
+ theme = require(getFullPath('src/theme.js'));
24
+ } catch (error) {
25
+ console.error('Error loading theme.js:', error.message);
26
+ process.exit(1);
27
+ }
28
+
29
+ try {
30
+ const tsConfigPath = getFullPath('tsconfig.json');
31
+
32
+ if (!fs.existsSync(tsConfigPath)) {
33
+ throw new Error(`tsconfig.json not found at ${tsConfigPath}`);
34
+ }
35
+
36
+ const tsConfigContent = fs.readFileSync(tsConfigPath, 'utf8');
37
+ const themePaths = ['"./*"'];
38
+
39
+ if (theme !== 'default') {
40
+ themePaths.splice(0, 0, `"themes/${theme}/*"`);
41
+ }
42
+
43
+ const newContent = tsConfigContent.replace(
44
+ /"@theme\/\*":\s\[.+\]/,
45
+ `"@theme/*": [${themePaths.join(', ')}]`
46
+ );
47
+
48
+ fs.writeFileSync(tsConfigPath, newContent);
49
+
50
+ if (fs.existsSync(getFullPath('turbo.json'))) {
51
+ spawn.sync('turbo', ['clean'], {
52
+ stdio: 'inherit'
53
+ });
54
+ }
55
+ } catch (error) {
56
+ console.error('Error modifying tsconfig.json:', error.message);
57
+ process.exit(1);
58
+ }
@@ -1 +1 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
package/bin/pz-postdev.js CHANGED
@@ -1 +1 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
@@ -1,6 +1,6 @@
1
- #!/usr/bin/env node
2
-
3
- const runScript = require('./run-script');
4
-
5
- runScript('pz-check-dependencies.js');
6
- runScript('pz-install-plugins.js');
1
+ #!/usr/bin/env node
2
+
3
+ const runScript = require('./run-script');
4
+
5
+ runScript('pz-check-dependencies.js');
6
+ runScript('pz-install-plugins.js');
@@ -1 +1 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
2
-
3
- const runScript = require('./run-script');
4
- runScript('pz-install-theme.js');
1
+ #!/usr/bin/env node
2
+
3
+ const runScript = require('./run-script');
4
+ runScript('pz-install-theme.js');
package/bin/pz-predev.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
2
-
3
- const runScript = require('./run-script');
4
- runScript('pz-install-theme.js');
1
+ #!/usr/bin/env node
2
+
3
+ const runScript = require('./run-script');
4
+ runScript('pz-install-theme.js');
@@ -1 +1 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
package/bin/run-script.js CHANGED
@@ -1,44 +1,44 @@
1
- const spawn = require('cross-spawn');
2
- const path = require('path');
3
- const fs = require('fs');
4
-
5
- function runScript(scriptName) {
6
- let currentPath;
7
-
8
- const monorepoPath = path.join(
9
- process.cwd(),
10
- '../../packages/akinon-next/bin',
11
- scriptName
12
- );
13
-
14
- const standardRepoPath = path.join(
15
- process.cwd(),
16
- 'node_modules/@akinon/next/bin',
17
- scriptName
18
- );
19
-
20
- if (fs.existsSync(monorepoPath)) {
21
- currentPath = monorepoPath;
22
- } else if (fs.existsSync(standardRepoPath)) {
23
- currentPath = standardRepoPath;
24
- } else {
25
- console.error(`Unable to find the ${scriptName} file`);
26
- return;
27
- }
28
-
29
- const result = spawn.sync('node', [currentPath], { stdio: 'inherit' });
30
-
31
- if (result.error) {
32
- console.error(`Error executing ${scriptName}:`, result.error);
33
- process.exit(1);
34
- }
35
-
36
- if (result.status !== 0) {
37
- console.error(
38
- `Script ${scriptName} failed with exit code ${result.status}`
39
- );
40
- process.exit(result.status);
41
- }
42
- }
43
-
44
- module.exports = runScript;
1
+ const spawn = require('cross-spawn');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ function runScript(scriptName) {
6
+ let currentPath;
7
+
8
+ const monorepoPath = path.join(
9
+ process.cwd(),
10
+ '../../packages/akinon-next/bin',
11
+ scriptName
12
+ );
13
+
14
+ const standardRepoPath = path.join(
15
+ process.cwd(),
16
+ 'node_modules/@akinon/next/bin',
17
+ scriptName
18
+ );
19
+
20
+ if (fs.existsSync(monorepoPath)) {
21
+ currentPath = monorepoPath;
22
+ } else if (fs.existsSync(standardRepoPath)) {
23
+ currentPath = standardRepoPath;
24
+ } else {
25
+ console.error(`Unable to find the ${scriptName} file`);
26
+ return;
27
+ }
28
+
29
+ const result = spawn.sync('node', [currentPath], { stdio: 'inherit' });
30
+
31
+ if (result.error) {
32
+ console.error(`Error executing ${scriptName}:`, result.error);
33
+ process.exit(1);
34
+ }
35
+
36
+ if (result.status !== 0) {
37
+ console.error(
38
+ `Script ${scriptName} failed with exit code ${result.status}`
39
+ );
40
+ process.exit(result.status);
41
+ }
42
+ }
43
+
44
+ module.exports = runScript;
@@ -1,52 +1,52 @@
1
- 'use client';
2
-
3
- import { useState } from 'react';
4
- import { Icon } from './icon';
5
- import { twMerge } from 'tailwind-merge';
6
- import { AccordionProps } from '../types';
7
-
8
- export const Accordion = ({
9
- isCollapse = false,
10
- title,
11
- subTitle,
12
- icons = ['chevron-up', 'chevron-down'],
13
- iconSize = 16,
14
- iconColor = 'fill-[#000000]',
15
- children,
16
- className,
17
- titleClassName,
18
- dataTestId
19
- }: AccordionProps) => {
20
- const [collapse, setCollapse] = useState(isCollapse);
21
-
22
- return (
23
- <div
24
- className={twMerge(
25
- 'flex flex-col justify-center border-b pb-4 mb-4 last:border-none',
26
- className
27
- )}
28
- >
29
- <div
30
- className="flex items-center justify-between cursor-pointer"
31
- onClick={() => setCollapse(!collapse)}
32
- data-testid={dataTestId}
33
- >
34
- <div className="flex flex-col">
35
- {title && (
36
- <h3 className={twMerge('text-sm', titleClassName)}>{title}</h3>
37
- )}
38
- {subTitle && <h4 className="text-xs text-gray-700">{subTitle}</h4>}
39
- </div>
40
-
41
- {icons && (
42
- <Icon
43
- name={collapse ? icons[0] : icons[1]}
44
- size={iconSize}
45
- className={`fill-[${iconColor}]}`}
46
- />
47
- )}
48
- </div>
49
- {collapse && <div className="mt-3 text-sm">{children}</div>}
50
- </div>
51
- );
52
- };
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { Icon } from './icon';
5
+ import { twMerge } from 'tailwind-merge';
6
+ import { AccordionProps } from '../types';
7
+
8
+ export const Accordion = ({
9
+ isCollapse = false,
10
+ title,
11
+ subTitle,
12
+ icons = ['chevron-up', 'chevron-down'],
13
+ iconSize = 16,
14
+ iconColor = 'fill-[#000000]',
15
+ children,
16
+ className,
17
+ titleClassName,
18
+ dataTestId
19
+ }: AccordionProps) => {
20
+ const [collapse, setCollapse] = useState(isCollapse);
21
+
22
+ return (
23
+ <div
24
+ className={twMerge(
25
+ 'flex flex-col justify-center border-b pb-4 mb-4 last:border-none',
26
+ className
27
+ )}
28
+ >
29
+ <div
30
+ className="flex items-center justify-between cursor-pointer"
31
+ onClick={() => setCollapse(!collapse)}
32
+ data-testid={dataTestId}
33
+ >
34
+ <div className="flex flex-col">
35
+ {title && (
36
+ <h3 className={twMerge('text-sm', titleClassName)}>{title}</h3>
37
+ )}
38
+ {subTitle && <h4 className="text-xs text-gray-700">{subTitle}</h4>}
39
+ </div>
40
+
41
+ {icons && (
42
+ <Icon
43
+ name={collapse ? icons[0] : icons[1]}
44
+ size={iconSize}
45
+ className={`fill-[${iconColor}]}`}
46
+ />
47
+ )}
48
+ </div>
49
+ {collapse && <div className="mt-3 text-sm">{children}</div>}
50
+ </div>
51
+ );
52
+ };
@@ -1,46 +1,46 @@
1
- 'use client';
2
-
3
- import { ButtonProps } from '../types/index';
4
- import clsx from 'clsx';
5
- import { twMerge } from 'tailwind-merge';
6
-
7
- export const Button = (props: ButtonProps) => {
8
- return (
9
- <button
10
- {...props}
11
- className={twMerge(
12
- clsx(
13
- [
14
- 'px-4',
15
- 'h-10',
16
- 'text-xs',
17
- 'bg-primary',
18
- 'text-primary-foreground',
19
- 'border',
20
- 'border-primary',
21
- 'transition-all',
22
- 'hover:bg-white',
23
- 'hover:border-primary',
24
- 'hover:text-primary'
25
- ],
26
- props.appearance === 'outlined' && [
27
- 'bg-transparent ',
28
- 'text-primary ',
29
- 'hover:bg-primary ',
30
- 'hover:text-primary-foreground'
31
- ],
32
- props.appearance === 'ghost' && [
33
- 'bg-transparent',
34
- 'border-transparent',
35
- 'text-primary',
36
- 'hover:bg-primary',
37
- 'hover:text-primary-foreground'
38
- ]
39
- ),
40
- props.className
41
- )}
42
- >
43
- {props.children}
44
- </button>
45
- );
46
- };
1
+ 'use client';
2
+
3
+ import { ButtonProps } from '../types/index';
4
+ import clsx from 'clsx';
5
+ import { twMerge } from 'tailwind-merge';
6
+
7
+ export const Button = (props: ButtonProps) => {
8
+ return (
9
+ <button
10
+ {...props}
11
+ className={twMerge(
12
+ clsx(
13
+ [
14
+ 'px-4',
15
+ 'h-10',
16
+ 'text-xs',
17
+ 'bg-primary',
18
+ 'text-primary-foreground',
19
+ 'border',
20
+ 'border-primary',
21
+ 'transition-all',
22
+ 'hover:bg-white',
23
+ 'hover:border-primary',
24
+ 'hover:text-primary'
25
+ ],
26
+ props.appearance === 'outlined' && [
27
+ 'bg-transparent ',
28
+ 'text-primary ',
29
+ 'hover:bg-primary ',
30
+ 'hover:text-primary-foreground'
31
+ ],
32
+ props.appearance === 'ghost' && [
33
+ 'bg-transparent',
34
+ 'border-transparent',
35
+ 'text-primary',
36
+ 'hover:bg-primary',
37
+ 'hover:text-primary-foreground'
38
+ ]
39
+ ),
40
+ props.className
41
+ )}
42
+ >
43
+ {props.children}
44
+ </button>
45
+ );
46
+ };
@@ -1,19 +1,19 @@
1
- 'use client';
2
-
3
- import { useMobileIframeHandler } from '../hooks';
4
-
5
- export default function ClientRoot({
6
- children,
7
- sessionId
8
- }: {
9
- children: React.ReactNode;
10
- sessionId?: string;
11
- }) {
12
- const { preventPageRender } = useMobileIframeHandler({ sessionId });
13
-
14
- if (preventPageRender) {
15
- return null;
16
- }
17
-
18
- return <>{children}</>;
19
- }
1
+ 'use client';
2
+
3
+ import { useMobileIframeHandler } from '../hooks';
4
+
5
+ export default function ClientRoot({
6
+ children,
7
+ sessionId
8
+ }: {
9
+ children: React.ReactNode;
10
+ sessionId?: string;
11
+ }) {
12
+ const { preventPageRender } = useMobileIframeHandler({ sessionId });
13
+
14
+ if (preventPageRender) {
15
+ return null;
16
+ }
17
+
18
+ return <>{children}</>;
19
+ }
@@ -1,18 +1,18 @@
1
- import { IconProps } from '../types/index';
2
- import clsx from 'clsx';
3
-
4
- export const Icon = (props: IconProps) => {
5
- const { name, size, className, ...rest } = props;
6
-
7
- return (
8
- <i
9
- className={clsx(`flex pz-icon-${name}`, className)}
10
- {...rest}
11
- style={
12
- size && {
13
- fontSize: `${size}px`
14
- }
15
- }
16
- />
17
- );
18
- };
1
+ import { IconProps } from '../types/index';
2
+ import clsx from 'clsx';
3
+
4
+ export const Icon = (props: IconProps) => {
5
+ const { name, size, className, ...rest } = props;
6
+
7
+ return (
8
+ <i
9
+ className={clsx(`flex pz-icon-${name}`, className)}
10
+ {...rest}
11
+ style={
12
+ size && {
13
+ fontSize: `${size}px`
14
+ }
15
+ }
16
+ />
17
+ );
18
+ };