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.
- package/.vscode/launch.json +21 -0
- package/config.ts +1 -0
- package/front.config.json +24 -0
- package/index.ts +6 -1
- package/lib/bin/bin.js +12 -0
- package/lib/config/config.d.ts +1 -0
- package/lib/config/config.js +1 -0
- package/lib/config/src/bin/config/userConfig.d.ts +14 -0
- package/lib/index.d.ts +6 -1
- package/lib/index.js +6 -7
- package/lib/src/front/server-state/DataProxy.d.ts +8 -0
- package/lib/src/front/server-state/DataProxy.js +104 -0
- package/lib/src/front/server-state/SSR.d.ts +4 -0
- package/lib/src/front/server-state/SSR.js +6 -0
- package/lib/src/front/server-state/real.d.ts +1 -0
- package/lib/src/front/server-state/real.js +36 -0
- package/lib/src/front/server-state/serverStates.d.ts +5 -0
- package/lib/src/front/server-state/serverStates.js +29 -0
- package/lib/src/front/server-state/useServerState.d.ts +10 -0
- package/lib/src/front/server-state/useServerState.js +28 -0
- package/lib/src/front/url/JinraiContext.d.ts +6 -0
- package/lib/src/front/url/JinraiContext.js +8 -0
- package/lib/src/front/url/adapter/def.d.ts +2 -0
- package/lib/src/front/url/adapter/def.js +9 -0
- package/lib/src/front/url/adapter/rrd6.d.ts +2 -0
- package/lib/src/front/url/adapter/rrd6.js +12 -0
- package/lib/src/front/url/adapter/rrd7.d.ts +2 -0
- package/lib/src/front/url/adapter/rrd7.js +11 -0
- package/lib/src/front/url/params/useParamsIndex.d.ts +1 -0
- package/lib/src/front/url/params/useParamsIndex.js +15 -0
- package/lib/src/front/url/search/useSearch.d.ts +1 -0
- package/lib/src/front/url/search/useSearch.js +15 -0
- package/lib/src/front/url/search/useSearchValue.d.ts +16 -0
- package/lib/src/front/url/search/useSearchValue.js +49 -0
- package/lib/src/front/wrapper/Custom.d.ts +8 -0
- package/lib/src/front/wrapper/Custom.js +9 -0
- package/lib/vite/plugin.js +1 -0
- package/package.json +42 -7
- package/readme.md +34 -8
- package/rollup.front.config.mjs +57 -0
- package/src/bin/bin.ts +10 -0
- package/src/bin/build/build.ts +69 -0
- package/src/{config → bin/config}/defaultIndexHtml.ts +2 -2
- package/src/{config → bin/config}/userConfig.ts +11 -3
- package/src/bin/content/normalizeContent.ts +7 -0
- package/src/bin/playwright/pageCollector.ts +11 -0
- package/src/{templates.ts → bin/playwright/templates.ts} +28 -21
- package/src/bin/routes/Parser.ts +148 -0
- package/src/{routes → bin/routes}/getRoutes.ts +9 -6
- package/src/bin/routes/replaceDevScripts.ts +16 -0
- package/src/bin/server/vitePreview.ts +13 -0
- package/src/front/server-state/DataProxy.ts +120 -0
- package/src/front/server-state/SSR.ts +4 -0
- package/src/front/server-state/real.ts +41 -0
- package/src/front/server-state/serverStates.ts +36 -0
- package/src/front/server-state/useServerState.ts +44 -0
- package/src/front/url/JinraiContext.tsx +10 -0
- package/src/front/url/adapter/def.tsx +10 -0
- package/src/front/url/adapter/rrd6.tsx +16 -0
- package/src/front/url/adapter/rrd7.tsx +15 -0
- package/src/front/url/params/useParamsIndex.ts +16 -0
- package/src/front/url/search/useSearch.ts +15 -0
- package/src/front/url/search/useSearchValue.ts +72 -0
- package/src/front/wrapper/Custom.tsx +14 -0
- package/tests/content/1.html +12 -0
- package/tests/content/1_result.json +54 -0
- package/tests/content/2.html +16 -0
- package/tests/content/2_result.json +28 -0
- package/tests/content/3.html +21 -0
- package/tests/content/3_result.json +39 -0
- package/tests/content/4.html +4 -0
- package/tests/content/4_result.json +9 -0
- package/tests/content/custom.html +5 -0
- package/tests/content/custom.json +5 -0
- package/tests/content/index.html +543 -0
- package/tests/content/index.json +49 -0
- package/tests/content/index_with_templates.json +31 -0
- package/tests/content/templates.json +13 -0
- package/tests/custom.test.ts +16 -0
- package/tests/parse.test.ts +52 -0
- package/tsconfig.json +2 -2
- package/vite/plugin.ts +83 -0
- package/vitest.config.ts +14 -0
- package/lib/bin.js +0 -3351
- package/lib/generate.js +0 -26
- package/lib/src/config/userConfig.d.ts +0 -7
- package/src/bin.ts +0 -46
- package/src/routes/parser.ts +0 -58
- package/src/routes/splitByTag.ts +0 -38
- package/test/fld.config.ts +0 -13
- package/test/jinrai.config.ts +0 -8
- /package/lib/{src → config/src/bin}/config/define.d.ts +0 -0
- /package/src/{config → bin/config}/define.ts +0 -0
- /package/src/{types → bin/types}/shims.d.ts +0 -0
- /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 {
|
|
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:
|
|
8
|
+
content: Element[]
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export const getRoutesAndTemplates = (
|
|
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
|
|
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,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 { 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
|