jinrai 1.0.3 → 1.0.6

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 (95) hide show
  1. package/.vscode/launch.json +21 -0
  2. package/config.ts +1 -0
  3. package/front.config.json +24 -0
  4. package/index.ts +6 -1
  5. package/lib/bin/bin.js +12 -0
  6. package/lib/config/config.d.ts +1 -0
  7. package/lib/config/config.js +1 -0
  8. package/lib/config/src/bin/config/userConfig.d.ts +14 -0
  9. package/lib/index.d.ts +6 -1
  10. package/lib/index.js +6 -7
  11. package/lib/src/front/server-state/DataProxy.d.ts +8 -0
  12. package/lib/src/front/server-state/DataProxy.js +104 -0
  13. package/lib/src/front/server-state/SSR.d.ts +4 -0
  14. package/lib/src/front/server-state/SSR.js +6 -0
  15. package/lib/src/front/server-state/real.d.ts +1 -0
  16. package/lib/src/front/server-state/real.js +36 -0
  17. package/lib/src/front/server-state/serverStates.d.ts +5 -0
  18. package/lib/src/front/server-state/serverStates.js +29 -0
  19. package/lib/src/front/server-state/useServerState.d.ts +10 -0
  20. package/lib/src/front/server-state/useServerState.js +28 -0
  21. package/lib/src/front/url/JinraiContext.d.ts +6 -0
  22. package/lib/src/front/url/JinraiContext.js +8 -0
  23. package/lib/src/front/url/adapter/def.d.ts +2 -0
  24. package/lib/src/front/url/adapter/def.js +9 -0
  25. package/lib/src/front/url/adapter/rrd6.d.ts +2 -0
  26. package/lib/src/front/url/adapter/rrd6.js +12 -0
  27. package/lib/src/front/url/adapter/rrd7.d.ts +2 -0
  28. package/lib/src/front/url/adapter/rrd7.js +11 -0
  29. package/lib/src/front/url/params/useParamsIndex.d.ts +1 -0
  30. package/lib/src/front/url/params/useParamsIndex.js +15 -0
  31. package/lib/src/front/url/search/useSearch.d.ts +1 -0
  32. package/lib/src/front/url/search/useSearch.js +15 -0
  33. package/lib/src/front/url/search/useSearchValue.d.ts +16 -0
  34. package/lib/src/front/url/search/useSearchValue.js +49 -0
  35. package/lib/src/front/wrapper/Custom.d.ts +8 -0
  36. package/lib/src/front/wrapper/Custom.js +9 -0
  37. package/lib/vite/plugin.js +1 -0
  38. package/package.json +42 -7
  39. package/readme.md +34 -8
  40. package/rollup.front.config.mjs +57 -0
  41. package/src/bin/bin.ts +10 -0
  42. package/src/bin/build/build.ts +69 -0
  43. package/src/{config → bin/config}/defaultIndexHtml.ts +2 -2
  44. package/src/{config → bin/config}/userConfig.ts +11 -3
  45. package/src/bin/content/normalizeContent.ts +7 -0
  46. package/src/bin/playwright/pageCollector.ts +11 -0
  47. package/src/{templates.ts → bin/playwright/templates.ts} +28 -21
  48. package/src/bin/routes/Parser.ts +148 -0
  49. package/src/{routes → bin/routes}/getRoutes.ts +9 -6
  50. package/src/bin/routes/replaceDevScripts.ts +16 -0
  51. package/src/bin/server/vitePreview.ts +13 -0
  52. package/src/front/server-state/DataProxy.ts +120 -0
  53. package/src/front/server-state/SSR.ts +4 -0
  54. package/src/front/server-state/real.ts +41 -0
  55. package/src/front/server-state/serverStates.ts +36 -0
  56. package/src/front/server-state/useServerState.ts +44 -0
  57. package/src/front/url/JinraiContext.tsx +10 -0
  58. package/src/front/url/adapter/def.tsx +10 -0
  59. package/src/front/url/adapter/rrd6.tsx +16 -0
  60. package/src/front/url/adapter/rrd7.tsx +15 -0
  61. package/src/front/url/params/useParamsIndex.ts +16 -0
  62. package/src/front/url/search/useSearch.ts +15 -0
  63. package/src/front/url/search/useSearchValue.ts +72 -0
  64. package/src/front/wrapper/Custom.tsx +14 -0
  65. package/tests/content/1.html +12 -0
  66. package/tests/content/1_result.json +54 -0
  67. package/tests/content/2.html +16 -0
  68. package/tests/content/2_result.json +28 -0
  69. package/tests/content/3.html +21 -0
  70. package/tests/content/3_result.json +39 -0
  71. package/tests/content/4.html +4 -0
  72. package/tests/content/4_result.json +9 -0
  73. package/tests/content/custom.html +5 -0
  74. package/tests/content/custom.json +5 -0
  75. package/tests/content/index.html +543 -0
  76. package/tests/content/index.json +49 -0
  77. package/tests/content/index_with_templates.json +31 -0
  78. package/tests/content/templates.json +13 -0
  79. package/tests/custom.test.ts +16 -0
  80. package/tests/parse.test.ts +52 -0
  81. package/tsconfig.json +2 -2
  82. package/vite/plugin.ts +83 -0
  83. package/vitest.config.ts +14 -0
  84. package/lib/bin.js +0 -3351
  85. package/lib/generate.js +0 -26
  86. package/lib/src/config/userConfig.d.ts +0 -7
  87. package/src/bin.ts +0 -46
  88. package/src/routes/parser.ts +0 -58
  89. package/src/routes/splitByTag.ts +0 -38
  90. package/test/fld.config.ts +0 -13
  91. package/test/jinrai.config.ts +0 -8
  92. /package/lib/{src → config/src/bin}/config/define.d.ts +0 -0
  93. /package/src/{config → bin/config}/define.ts +0 -0
  94. /package/src/{types → bin/types}/shims.d.ts +0 -0
  95. /package/src/{ui → bin/ui}/task.tsx +0 -0
@@ -0,0 +1,148 @@
1
+ import { normalizeHtmlWhitespace } from "../content/normalizeContent"
2
+ import { createHash } from "node:crypto"
3
+
4
+ interface ParserOptions {
5
+ templates?: boolean
6
+ normalize?: boolean
7
+ }
8
+
9
+ export type Element = ArrayElement | HtmlElement | ValueElement | CustomElement
10
+
11
+ interface ArrayElement {
12
+ type: "array"
13
+ key: string
14
+ data: Element[]
15
+ }
16
+
17
+ interface HtmlElement {
18
+ type: "html"
19
+ content: string
20
+ }
21
+
22
+ interface ValueElement {
23
+ type: "value"
24
+ key: string
25
+ }
26
+
27
+ interface CustomElement {
28
+ type: "custom"
29
+ name: string
30
+ props: string
31
+ }
32
+
33
+ export class Parser {
34
+ options?: ParserOptions
35
+
36
+ openVar = "{{"
37
+ createVar = "}}"
38
+ createArray = "</loopwrapper"
39
+ createCustom = "</custom"
40
+ deepUp = "<loopwrapper"
41
+ deepUp2 = "<custom"
42
+
43
+ templates: Record<string, string> = {}
44
+
45
+ constructor(options?: ParserOptions) {
46
+ this.options = options
47
+ }
48
+
49
+ parse(content: string) {
50
+ const tree: Element[] = []
51
+ this.handle(this.options?.normalize ? normalizeHtmlWhitespace(content) : content, tree)
52
+ return tree
53
+ }
54
+
55
+ handle(content: string, tree: Element[]) {
56
+ let match
57
+ let deep = 0
58
+ let lastIndex = 0
59
+
60
+ const tagPattern = new RegExp(
61
+ `(<loopwrapper(\\s+[^>]*)?>|</loopwrapper>|\{\{|\}\}|<custom(\\s+[^>]*)?>|</custom>)`,
62
+ "gi",
63
+ )
64
+
65
+ while ((match = tagPattern.exec(content)) !== null) {
66
+ const currentTag = match[0]
67
+ const value = content.substring(lastIndex, match.index)
68
+
69
+ if (currentTag.startsWith(this.createArray)) {
70
+ deep--
71
+ if (deep > 0) continue
72
+ this.createElement(tree, value)
73
+ } else if (currentTag.startsWith(this.deepUp)) {
74
+ deep++
75
+ if (deep > 1) continue
76
+ this.createElement(tree, value)
77
+ } else if (currentTag.startsWith(this.createCustom)) {
78
+ deep--
79
+ if (deep != 0) continue
80
+ this.createCustomElement(tree, value)
81
+ } else if (currentTag.startsWith(this.deepUp2)) {
82
+ deep++
83
+ if (deep > 1) continue
84
+ this.createElement(tree, value)
85
+ } else if (currentTag == this.createVar) {
86
+ if (deep != 0) continue
87
+ this.createElement(tree, value, true)
88
+ } else {
89
+ if (deep != 0) continue
90
+ this.createElement(tree, value)
91
+ }
92
+
93
+ lastIndex = match.index + currentTag.length
94
+ }
95
+
96
+ if (lastIndex < content.length) {
97
+ const value = content.substring(lastIndex)
98
+ this.createElement(tree, value)
99
+ }
100
+ }
101
+
102
+ createCustomElement(parent: Element[], value: string) {
103
+ const [name, ...props] = value.trimStart().split("|")
104
+ value = props.join("|")
105
+
106
+ parent.push({
107
+ type: "custom",
108
+ name,
109
+ props: value,
110
+ })
111
+ }
112
+
113
+ createElement(parent: Element[], value: string, isVarible?: boolean) {
114
+ if (isVarible)
115
+ return parent.push({
116
+ type: "value",
117
+ key: value,
118
+ })
119
+
120
+ if (value.trimStart().startsWith("ArrayDataKey=")) {
121
+ const [key, ...val] = value.trimStart().substring(13).split("|")
122
+ value = val.join("|")
123
+
124
+ const children: Element[] = []
125
+ this.handle(value, children)
126
+
127
+ return parent.push({
128
+ type: "array",
129
+ data: children,
130
+ key,
131
+ })
132
+ }
133
+
134
+ if (value)
135
+ parent.push({
136
+ type: "html",
137
+ content: this.options?.templates ? this.createTemplate(value) : value,
138
+ })
139
+ }
140
+
141
+ createTemplate(html: string): string {
142
+ if (!(html in this.templates)) {
143
+ this.templates[html] = createHash("md5").update(Object.keys(this.templates).length.toString()).digest("hex")
144
+ }
145
+
146
+ return this.templates[html]
147
+ }
148
+ }
@@ -1,21 +1,24 @@
1
- import { input, PageData } from "../templates"
2
- import { ParseItem, Parser } from "./parser"
1
+ import { input, PageData } from "../playwright/templates"
2
+ import { Element, Parser } from "./Parser"
3
3
 
4
4
  interface Route {
5
+ id: number
5
6
  mask: string
6
7
  requests: input[]
7
- content: ParseItem[]
8
+ content: Element[]
8
9
  }
9
10
 
10
- export const getRoutesAndTemplates = (templates: PageData[]) => {
11
+ export const getRoutesAndTemplates = (pages: PageData[], normalize: boolean = true, templates: boolean = true) => {
11
12
  const routes: Route[] = []
12
- const parser = new Parser()
13
+ const parser = new Parser({ normalize, templates })
13
14
 
14
- for (const template of templates) {
15
+ for (const [id, template] of pages.entries()) {
15
16
  const content = parser.parse(template.root)
17
+
16
18
  const mask = template.mask.replaceAll("/", "\\/").replace(/{(.*?)}/, ".+?")
17
19
 
18
20
  routes.push({
21
+ id,
19
22
  content,
20
23
  mask,
21
24
  requests: template.input,
@@ -0,0 +1,16 @@
1
+ export const removeDevScripts = (indexHtml: string) => {
2
+ const parts = [
3
+ '<script type="module">import { injectIntoGlobalHook } from "/@react-refresh";',
4
+ "injectIntoGlobalHook(window);",
5
+ "window.$RefreshReg$ = () => {};",
6
+ "window.$RefreshSig$ = () => (type) => type;</script>",
7
+ '<script type="module" src="/@vite/client"></script>',
8
+ '<script type="module" src="/src/main.tsx"></script>',
9
+ ]
10
+
11
+ parts.map(remove => {
12
+ indexHtml = indexHtml.replace(`${remove}\n`, "")
13
+ })
14
+
15
+ return indexHtml
16
+ }
@@ -0,0 +1,13 @@
1
+ import { preview } from "vite"
2
+
3
+ export const vitePreview = async () => {
4
+ const previewServer = await preview({
5
+ preview: {
6
+ port: 8084,
7
+ },
8
+ })
9
+
10
+ if (!previewServer.resolvedUrls?.local?.length) throw new Error("vite is not defined")
11
+
12
+ return [previewServer.resolvedUrls?.local[0].slice(0, -1), () => previewServer.close()] as [string, () => void]
13
+ }
@@ -0,0 +1,120 @@
1
+ import React from "react"
2
+ import { ssr } from "./SSR"
3
+
4
+ // IMPORT REACT
5
+
6
+ export interface DataProxy {
7
+ map: (callback: () => DataProxy) => any[]
8
+ getKey: () => string
9
+ getValue: () => any
10
+ }
11
+
12
+ export const sources = new Map<string, any>()
13
+
14
+ const getTarget = (data: any, path: string) => {
15
+ if (Array.isArray(data)) return { __array__: data }
16
+
17
+ switch (typeof data) {
18
+ case "object":
19
+ case "string":
20
+ // case "number":
21
+ case "boolean":
22
+ case "undefined":
23
+ case "symbol":
24
+ // эти типы можно просто завернуть
25
+ return { value: data }
26
+ default:
27
+ return () => `{{${path}}}`
28
+ }
29
+ }
30
+
31
+ const createDataProxy = (data: any, path: string = ""): DataProxy => {
32
+ if (path.endsWith("@")) sources.set(path.slice(0, -1), data)
33
+
34
+ return new Proxy(getTarget(data, path), {
35
+ get: (_: any, prop: string) => {
36
+ // if (typeof prop == "symbol") return data[prop]
37
+
38
+ if (!(typeof data == "object" && data !== null && prop in data))
39
+ // DEV TOOLS
40
+ switch (prop) {
41
+ // @ts-ignore
42
+ case Symbol.toPrimitive:
43
+ return (hint: string) => {
44
+ console.log("PROXYDATA", hint)
45
+ return `{{${path}}}`
46
+ }
47
+ // @ts-ignore
48
+ case Symbol.toStringTag:
49
+ return "Object"
50
+ // @ts-ignore
51
+ case Symbol.iterator:
52
+ return data[Symbol.iterator]
53
+ case "$$typeof":
54
+ case "type":
55
+ return undefined
56
+
57
+ case "_debugInfo":
58
+ return {
59
+ note: `State From Request (${path})`,
60
+ kind: typeof data,
61
+ timestamp: Date.now(),
62
+ preview: data,
63
+ }
64
+ }
65
+
66
+ // SELF
67
+ if (prop.startsWith("$")) return (key: string) => `{{${path + "/" + key}${"\\" + prop}}}`
68
+
69
+ // TYPES
70
+ switch (typeof data) {
71
+ case "string":
72
+ switch (prop) {
73
+ case "length":
74
+ case "entries":
75
+ return undefined
76
+ }
77
+ case "number":
78
+ switch (prop) {
79
+ case "@@iterator":
80
+ return undefined
81
+ }
82
+
83
+ default:
84
+ switch (prop) {
85
+ case "then":
86
+ return undefined
87
+ }
88
+ }
89
+
90
+ // OTHER
91
+ switch (prop) {
92
+ case "find":
93
+ return data[prop]
94
+ case "map":
95
+ case "forEach":
96
+ return (callback: (arg0: DataProxy) => any) =>
97
+ React.createElement("loopwrapper", null, [
98
+ `ArrayDataKey=${path}|`,
99
+ Object.entries(data)
100
+ .slice(0, 1)
101
+ .map(([key, itm]) => callback(createDataProxy(itm, `${path}/[ITEM=${key}]`))),
102
+ ])
103
+ case "getValue":
104
+ return () => data
105
+ case "toJSON":
106
+ return () => {
107
+ console.log("dataproxy toJSON", path, data)
108
+ return ssr.exportParams ? `{{${path}}}` : data
109
+ }
110
+ default:
111
+ if (data && (typeof data[prop] == "object" || Array.isArray(data[prop]))) {
112
+ return createDataProxy(data[prop], path + "/" + prop)
113
+ }
114
+ return `{{${path + "/" + prop}}}`
115
+ }
116
+ },
117
+ })
118
+ }
119
+
120
+ export default createDataProxy
@@ -0,0 +1,4 @@
1
+ export const ssr = {
2
+ current: navigator.userAgent == "____JINRAI_CLIENT____",
3
+ exportParams: false,
4
+ }
@@ -0,0 +1,41 @@
1
+ import { ssr } from "./SSR"
2
+ import { sources } from "./DataProxy"
3
+
4
+ export function real<T>(value: T): T {
5
+ if (!ssr.current) return value
6
+
7
+ switch (typeof value) {
8
+ case "number":
9
+ return value
10
+ case "string":
11
+ return value.startsWith("{{") && value.endsWith("}}") ? getArrayByPath(value.slice(2, -2)) : value
12
+
13
+ case "object":
14
+ // @ts-ignore
15
+ if (value && typeof value.getValue === "function") {
16
+ // @ts-ignore
17
+ return value.getValue()
18
+ }
19
+
20
+ return value
21
+
22
+ default:
23
+ return value
24
+ }
25
+ }
26
+
27
+ const getArrayByPath = (path: string) => {
28
+ const [sourceIndex, requestPath] = path.split("@", 2)
29
+ const keys = requestPath.split("/")
30
+ const requestId = keys.shift()
31
+
32
+ let link = sources.get(sourceIndex)
33
+
34
+ for (let key of keys) {
35
+ if (key.startsWith("[ITEM=")) key = key.slice(6, -1)
36
+
37
+ link = link[key]
38
+ }
39
+
40
+ return link
41
+ }
@@ -0,0 +1,36 @@
1
+ import createDataProxy from "./DataProxy"
2
+ import { ssr } from "./SSR"
3
+ import { ServerStateOptions } from "./useServerState"
4
+
5
+ export type ServerValue = any
6
+
7
+ export const serverStates = new Map<string, string>()
8
+ if (window != undefined) {
9
+ // @ts-ignore
10
+ window.__serverExportStates__ = serverStates
11
+ }
12
+
13
+ export const getServerValue = (key: string, def?: ServerValue) => {
14
+ if (key == "") {
15
+ return [def, false]
16
+ }
17
+
18
+ // @ts-ignore
19
+ if (window != undefined && window?.__serverInitialStates__ && key in window.__serverInitialStates__) {
20
+ // @ts-ignore
21
+ return [window.__serverInitialStates__[key], true]
22
+ }
23
+
24
+ return [serverStates.get(key) ?? def, false]
25
+ }
26
+
27
+ export const setServerValue = (key: string, value: ServerValue, options?: ServerStateOptions) => {
28
+ ssr.exportParams = true
29
+ const json = JSON.stringify({ value, options })
30
+ ssr.exportParams = false
31
+
32
+ console.log(key, json)
33
+ serverStates.set(key, json)
34
+
35
+ return createDataProxy(value, `${key}@`)
36
+ }
@@ -0,0 +1,44 @@
1
+ import { Dispatch, SetStateAction, useState } from "react"
2
+ import { ssr } from "./SSR"
3
+ import { getServerValue, ServerValue, setServerValue } from "./serverStates"
4
+
5
+ export interface ServerStateOptions {
6
+ source: {
7
+ method: string
8
+ url: string
9
+ input: any
10
+ }
11
+ }
12
+
13
+ export const useServerState = <T extends ServerValue>(
14
+ serverKey: string,
15
+ initialValue: T,
16
+ options?: ServerStateOptions,
17
+ ) => {
18
+ const [serverValue, isInitOnServer] = getServerValue(serverKey, initialValue)
19
+ const [value, setStateValue] = useState(serverValue)
20
+ const [isInit, setIsInit] = useState(isInitOnServer)
21
+
22
+ const setValue: Dispatch<SetStateAction<T>> = (value: T | ((prevState: T) => T)) => {
23
+ setStateValue((prev: T) => {
24
+ const result = value instanceof Function ? (value as (prevState: T) => T)(prev) : value
25
+
26
+ if (serverKey && ssr.current) {
27
+ return setServerValue(serverKey, result, options)
28
+ }
29
+
30
+ return result
31
+ })
32
+ }
33
+
34
+ const initOnServer = () => {
35
+ if (isInit) {
36
+ setIsInit(false)
37
+ return true
38
+ }
39
+
40
+ return false
41
+ }
42
+
43
+ return [value, setValue, initOnServer] as [T, Dispatch<SetStateAction<T>>, () => boolean]
44
+ }
@@ -0,0 +1,10 @@
1
+ import { createContext, DependencyList, ReactNode } from "react"
2
+
3
+ export interface JinraiProps {
4
+ deps?: DependencyList
5
+ children?: ReactNode
6
+ }
7
+ export const JinraiContext = createContext<JinraiProps>({
8
+ deps: [],
9
+ children: undefined,
10
+ })
@@ -0,0 +1,10 @@
1
+ import { JinraiContext, JinraiProps } from "../JinraiContext"
2
+ import { NuqsAdapter } from "nuqs/adapters/react"
3
+
4
+ export const Adapter = (props: JinraiProps) => {
5
+ return (
6
+ <NuqsAdapter>
7
+ <JinraiContext.Provider value={{ deps: props.deps ?? [] }} {...props} />
8
+ </NuqsAdapter>
9
+ )
10
+ }
@@ -0,0 +1,16 @@
1
+ import { NuqsAdapter } from "nuqs/adapters/react-router/v6"
2
+
3
+ // @ts-ignore
4
+ import { useLocation } from "react-router-dom"
5
+ import { JinraiContext, JinraiProps } from "../JinraiContext"
6
+
7
+ export const Adapter = (props: JinraiProps) => {
8
+ console.log("LOAD ADAPTER")
9
+ const { pathname } = useLocation()
10
+
11
+ return (
12
+ <NuqsAdapter>
13
+ <JinraiContext.Provider value={{ deps: [...(props.deps ?? [], [pathname])] }} {...props} />
14
+ </NuqsAdapter>
15
+ )
16
+ }
@@ -0,0 +1,15 @@
1
+ import type { FC } from "react"
2
+ import { NuqsAdapter } from "nuqs/adapters/react-router/v7"
3
+ // @ts-ignore
4
+ import { useLocation } from "react-router-dom"
5
+ import { JinraiContext, JinraiProps } from "../JinraiContext"
6
+
7
+ export const Adapter = (props: JinraiProps) => {
8
+ const { pathname } = useLocation()
9
+
10
+ return (
11
+ <NuqsAdapter>
12
+ <JinraiContext.Provider value={{ deps: [...(props.deps ?? [], [pathname])] }} {...props} />
13
+ </NuqsAdapter>
14
+ )
15
+ }
@@ -0,0 +1,16 @@
1
+ import { useContext, useMemo } from "react"
2
+
3
+ import { getJinraiValue } from "../search/useSearchValue"
4
+ import { JinraiContext } from "../JinraiContext"
5
+ import { ssr } from "../../server-state/SSR"
6
+
7
+ export const useParamsIndex = (index: number, def: string = ""): string => {
8
+ const { deps } = useContext(JinraiContext)
9
+ const value = useMemo(() => location.pathname.split("/")[index + 1] ?? def, deps ? deps : [])
10
+
11
+ const stableValue = useMemo(() => {
12
+ return ssr.current ? value.bindSource(getJinraiValue(index.toString(), "paramsIndex", "", def)) : value
13
+ }, [value])
14
+
15
+ return stableValue
16
+ }
@@ -0,0 +1,15 @@
1
+ import { useContext, useMemo } from "react"
2
+ import { JinraiContext } from "../JinraiContext"
3
+ import { ssr } from "../../server-state/SSR"
4
+ import { getJinraiValue } from "./useSearchValue"
5
+
6
+ export const useSearch = (): string => {
7
+ const { deps } = useContext(JinraiContext)
8
+ const value = useMemo(() => location.search.substring(1), deps ? deps : [])
9
+
10
+ const stableValue = useMemo(() => {
11
+ return ssr.current ? value.bindSource(getJinraiValue("", "search", "", "")) : value
12
+ }, [value])
13
+
14
+ return stableValue
15
+ }
@@ -0,0 +1,72 @@
1
+ import { parseAsArrayOf, parseAsString, useQueryState, UseQueryStateReturn } from "nuqs"
2
+ import { useMemo } from "react"
3
+ import { ssr } from "../../server-state/SSR"
4
+
5
+ declare global {
6
+ interface String {
7
+ source?: string
8
+ toJSON: () => string
9
+ bindSource: (source: string) => string
10
+ }
11
+ interface Array<T> {
12
+ source?: string
13
+ toJSON(): any
14
+ bindSource(source: string): T[]
15
+ }
16
+ }
17
+
18
+ function toJSON() {
19
+ // @ts-ignore
20
+ if (ssr.exportParams) return this.source
21
+ // @ts-ignore
22
+ return this
23
+ }
24
+
25
+ if (true) {
26
+ String.prototype.source = undefined
27
+ String.prototype.toJSON = toJSON
28
+ String.prototype.bindSource = function (source: string): string {
29
+ const result = new String(this)
30
+ result.source = source
31
+ return result as string
32
+ }
33
+
34
+ if (!Array.prototype.toJSON) {
35
+ Array.prototype.toJSON = toJSON
36
+ }
37
+
38
+ if (!Array.prototype.bindSource) {
39
+ Array.prototype.bindSource = function (source: string) {
40
+ this.source = source
41
+ return this
42
+ }
43
+ }
44
+ }
45
+
46
+ export const useSearchValue = (key: string, defaultValue: string): UseQueryStateReturn<string, string> => {
47
+ const [value, setValue] = useQueryState(key, { defaultValue })
48
+
49
+ const stableValue = useMemo(() => {
50
+ return ssr.current ? value.bindSource(getJinraiValue(key, "searchString", "", defaultValue)) : value
51
+ }, [key, value])
52
+
53
+ return [stableValue, setValue]
54
+ }
55
+
56
+ export const useSearchArray = (
57
+ key: string,
58
+ defaultValue: string[] = [],
59
+ separator = ",",
60
+ ): UseQueryStateReturn<string[], string[]> => {
61
+ const stableDefault = useMemo(() => defaultValue, [])
62
+ const [value, setValue] = useQueryState(key, parseAsArrayOf(parseAsString, separator).withDefault(stableDefault))
63
+ const stableValue = useMemo(() => {
64
+ return ssr.current ? value.bindSource(getJinraiValue(key, "searchArray", separator, defaultValue)) : value
65
+ }, [key, value])
66
+
67
+ return [stableValue, setValue]
68
+ }
69
+
70
+ export const getJinraiValue = (key: string, type: string, separator: string, def: any) => {
71
+ return `@JV[[${JSON.stringify({ key, type, separator, def })}]]`
72
+ }
@@ -0,0 +1,14 @@
1
+ import type { FC, ReactElement, ReactNode } from "react"
2
+ import { ssr } from "../server-state/SSR"
3
+
4
+ interface CustomProps {
5
+ name: string
6
+ props: object
7
+ children: ReactNode
8
+ }
9
+
10
+ export const Custom = ({ name, props, children }: CustomProps) => {
11
+ if (!ssr.current) return children as ReactElement
12
+
13
+ return `<custom>${JSON.stringify({ name, props })}<custom>`
14
+ }
@@ -0,0 +1,12 @@
1
+ header {{SUPERVALUE}}
2
+ <loopwrapper>
3
+ ArrayDataKey=FIRST_KEY|FIRST_LOOP_CONTENT
4
+ <loopwrapper
5
+ >ArrayDataKey=SECOND_KEY|
6
+ <img
7
+ src="{{0#/Api/Catalog/GetCatalog@/[ITEM=0]/preview}}"
8
+ alt="{{0#/Api/Catalog/GetCatalog@/[ITEM=0]/title}}"
9
+ />
10
+ </loopwrapper>
11
+ </loopwrapper>
12
+ footer