jinrai 1.1.3 → 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.
- package/front.config.json +2 -1
- package/index.ts +2 -1
- package/lib/bin/bin.js +115 -52
- package/lib/index.d.ts +2 -1
- package/lib/index.js +2 -1
- package/lib/src/bin/agent/agent.d.ts +1 -0
- package/lib/src/bin/agent/agent.js +2 -1
- package/lib/src/bin/config/userConfig.d.ts +1 -0
- package/lib/src/bin/playwright/pageCollector.d.ts +2 -0
- package/lib/src/bin/playwright/pageTestCollector.d.ts +6 -0
- package/lib/src/bin/playwright/templates.d.ts +6 -1
- package/lib/src/bin/routes/Parser.d.ts +19 -1
- package/lib/src/bin/routes/getRoutes.d.ts +1 -0
- package/lib/src/front/server/useIsServer.d.ts +1 -0
- package/lib/src/front/server/useIsServer.js +7 -0
- package/lib/src/front/server-state/DataProxy.js +3 -0
- package/lib/src/front/server-state/SSR.d.ts +1 -0
- package/lib/src/front/server-state/SSR.js +3 -1
- package/lib/src/front/server-state/orig.d.ts +2 -0
- package/lib/src/front/server-state/{real.js → orig.js} +3 -2
- package/lib/src/front/server-state/serverStates.d.ts +1 -0
- package/lib/src/front/server-state/serverStates.js +6 -2
- package/lib/src/front/server-state/testState.d.ts +3 -0
- package/lib/src/front/server-state/testState.js +14 -0
- package/lib/src/front/server-state/useServerState.js +5 -9
- package/lib/src/front/translate/TranslateConfig.d.ts +21 -0
- package/lib/src/front/translate/TranslateConfig.js +108 -0
- package/lib/src/front/url/JinraiContext.d.ts +1 -0
- package/lib/src/front/url/JinraiContext.js +1 -0
- package/lib/src/front/url/adapter/def.js +1 -1
- package/lib/src/front/url/adapter/rrd6.js +2 -2
- package/lib/src/front/url/adapter/rrd7.js +2 -2
- package/lib/src/front/url/search/useSearch.js +3 -4
- package/lib/src/front/wrapper/Custom.js +5 -1
- package/lib/vite/plugin.js +20 -14
- package/package.json +6 -2
- package/rollup.config.mjs +2 -1
- package/src/bin/agent/agent.ts +1 -0
- package/src/bin/build/build.ts +23 -10
- package/src/bin/config/define.ts +1 -3
- package/src/bin/config/userConfig.ts +1 -0
- package/src/bin/playwright/pageCollector.ts +8 -4
- package/src/bin/playwright/pageTestCollector.ts +15 -0
- package/src/bin/playwright/templates.ts +14 -4
- package/src/bin/routes/Parser.ts +88 -27
- package/src/bin/routes/getRoutes.ts +5 -1
- package/src/front/server/useIsServer.ts +5 -0
- package/src/front/server-state/DataProxy.ts +4 -0
- package/src/front/server-state/SSR.ts +5 -1
- package/src/front/server-state/{real.ts → orig.ts} +3 -1
- package/src/front/server-state/serverStates.ts +8 -2
- package/src/front/server-state/testState.ts +15 -0
- package/src/front/server-state/useServerState.ts +6 -11
- package/src/front/translate/TranslateConfig.tsx +153 -0
- package/src/front/url/JinraiContext.tsx +2 -0
- package/src/front/url/adapter/def.tsx +1 -1
- package/src/front/url/adapter/rrd6.tsx +2 -3
- package/src/front/url/adapter/rrd7.tsx +2 -2
- package/src/front/url/search/useSearch.ts +3 -4
- package/src/front/wrapper/Custom.tsx +9 -1
- package/tests/parse/custom.test.ts +2 -1
- package/tsconfig.types.json +1 -0
- package/vite/plugin.ts +32 -20
- package/lib/src/front/server-state/real.d.ts +0 -1
package/src/bin/routes/Parser.ts
CHANGED
|
@@ -13,7 +13,7 @@ interface ParserOptions {
|
|
|
13
13
|
normalize?: boolean
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export type Element = ArrayElement | HtmlElement | ValueElement | CustomElement
|
|
16
|
+
export type Element = ArrayElement | HtmlElement | ValueElement | CustomElement | TranslateText | TValueElement
|
|
17
17
|
|
|
18
18
|
interface ArrayElement {
|
|
19
19
|
type: "array"
|
|
@@ -30,6 +30,15 @@ interface ValueElement {
|
|
|
30
30
|
type: "value"
|
|
31
31
|
key: string
|
|
32
32
|
}
|
|
33
|
+
interface TValueElement {
|
|
34
|
+
type: "tvalue"
|
|
35
|
+
value: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface TranslateText {
|
|
39
|
+
type: "t"
|
|
40
|
+
text: string
|
|
41
|
+
}
|
|
33
42
|
|
|
34
43
|
interface CustomElement {
|
|
35
44
|
type: "custom"
|
|
@@ -37,17 +46,27 @@ interface CustomElement {
|
|
|
37
46
|
props: object
|
|
38
47
|
}
|
|
39
48
|
|
|
49
|
+
export interface CustomExample {
|
|
50
|
+
name: string
|
|
51
|
+
html: string
|
|
52
|
+
props: any
|
|
53
|
+
}
|
|
54
|
+
|
|
40
55
|
export class Parser {
|
|
41
56
|
options?: ParserOptions
|
|
42
57
|
|
|
43
58
|
openVar = "{{"
|
|
44
59
|
createVar = "}}"
|
|
60
|
+
openTVar = "{!"
|
|
61
|
+
createTVar = "!}"
|
|
62
|
+
|
|
45
63
|
createArray = "</loopwrapper"
|
|
46
64
|
createCustom = "</custom"
|
|
47
65
|
deepUp = "<loopwrapper"
|
|
48
66
|
deepUp2 = "<custom"
|
|
49
67
|
|
|
50
68
|
templates: Record<string, string> = {}
|
|
69
|
+
custom: CustomExample[] = []
|
|
51
70
|
|
|
52
71
|
constructor(options?: ParserOptions) {
|
|
53
72
|
this.options = options
|
|
@@ -63,9 +82,10 @@ export class Parser {
|
|
|
63
82
|
let match
|
|
64
83
|
let deep = 0
|
|
65
84
|
let lastIndex = 0
|
|
85
|
+
let translate = false
|
|
66
86
|
|
|
67
87
|
const tagPattern = new RegExp(
|
|
68
|
-
`(<loopwrapper(\\s+[^>]*)?>|</loopwrapper>|\{\{|\}\}|<custom(\\s+[^>]*)?>|</custom>)`,
|
|
88
|
+
`(<loopwrapper(\\s+[^>]*)?>|</loopwrapper>|\{\{|\}\}|\{\!|\!\}|<custom(\\s+[^>]*)?>|</custom>)`,
|
|
69
89
|
"gi",
|
|
70
90
|
)
|
|
71
91
|
|
|
@@ -73,28 +93,42 @@ export class Parser {
|
|
|
73
93
|
const currentTag = match[0]
|
|
74
94
|
const value = content.substring(lastIndex, match.index)
|
|
75
95
|
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this.createElement(tree, value)
|
|
84
|
-
} else if (currentTag.startsWith(this.createCustom)) {
|
|
85
|
-
deep--
|
|
86
|
-
if (deep != 0) continue
|
|
87
|
-
this.createCustomElement(tree, value)
|
|
88
|
-
} else if (currentTag.startsWith(this.deepUp2)) {
|
|
89
|
-
deep++
|
|
90
|
-
if (deep > 1) continue
|
|
91
|
-
this.createElement(tree, value)
|
|
92
|
-
} else if (currentTag == this.createVar) {
|
|
93
|
-
if (deep != 0) continue
|
|
94
|
-
this.createElement(tree, value, true)
|
|
96
|
+
if (translate) {
|
|
97
|
+
if (currentTag.startsWith(this.createTVar)) {
|
|
98
|
+
translate = false
|
|
99
|
+
this.createTranslate(tree, value)
|
|
100
|
+
} else {
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
95
103
|
} else {
|
|
96
|
-
if (
|
|
97
|
-
|
|
104
|
+
if (currentTag.startsWith(this.createArray)) {
|
|
105
|
+
deep--
|
|
106
|
+
if (deep > 0) continue
|
|
107
|
+
this.createElement(tree, value)
|
|
108
|
+
} else if (currentTag.startsWith(this.deepUp)) {
|
|
109
|
+
deep++
|
|
110
|
+
if (deep > 1) continue
|
|
111
|
+
this.createElement(tree, value)
|
|
112
|
+
} else if (currentTag.startsWith(this.createCustom)) {
|
|
113
|
+
deep--
|
|
114
|
+
if (deep != 0) continue
|
|
115
|
+
this.createCustomElement(tree, value)
|
|
116
|
+
} else if (currentTag.startsWith(this.deepUp2)) {
|
|
117
|
+
deep++
|
|
118
|
+
if (deep > 1) continue
|
|
119
|
+
this.createElement(tree, value)
|
|
120
|
+
} else if (currentTag.startsWith(this.openTVar)) {
|
|
121
|
+
////////////// >>>>
|
|
122
|
+
if (deep != 0) continue
|
|
123
|
+
translate = true
|
|
124
|
+
this.createElement(tree, value)
|
|
125
|
+
} else if (currentTag == this.createVar) {
|
|
126
|
+
if (deep != 0) continue
|
|
127
|
+
this.createElement(tree, value, true)
|
|
128
|
+
} else {
|
|
129
|
+
if (deep != 0) continue
|
|
130
|
+
this.createElement(tree, value)
|
|
131
|
+
}
|
|
98
132
|
}
|
|
99
133
|
|
|
100
134
|
lastIndex = match.index + currentTag.length
|
|
@@ -110,6 +144,14 @@ export class Parser {
|
|
|
110
144
|
const [customProps, exampleProps, children] = value.split(SPLIT)
|
|
111
145
|
const custom = JSON.parse(customProps) as CustomElement
|
|
112
146
|
|
|
147
|
+
if (this.custom.find(itm => itm.name == custom.name) == undefined) {
|
|
148
|
+
this.custom.push({
|
|
149
|
+
name: custom.name,
|
|
150
|
+
html: children,
|
|
151
|
+
props: exampleProps,
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
113
155
|
parent.push({
|
|
114
156
|
type: "custom",
|
|
115
157
|
name: custom.name,
|
|
@@ -117,12 +159,31 @@ export class Parser {
|
|
|
117
159
|
})
|
|
118
160
|
}
|
|
119
161
|
|
|
120
|
-
|
|
121
|
-
|
|
162
|
+
createVarible(parent: Element[], value: string) {
|
|
163
|
+
return parent.push({
|
|
164
|
+
type: "value",
|
|
165
|
+
key: value,
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
createTranslate(parent: Element[], value: string) {
|
|
170
|
+
if (value.startsWith("{{") && value.endsWith("}}")) {
|
|
122
171
|
return parent.push({
|
|
123
|
-
type: "
|
|
124
|
-
|
|
172
|
+
type: "tvalue",
|
|
173
|
+
value: value.slice(2, -2),
|
|
125
174
|
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return parent.push({
|
|
178
|
+
type: "t",
|
|
179
|
+
text: value,
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
createElement(parent: Element[], value: string, isVarible?: boolean) {
|
|
184
|
+
if (isVarible) {
|
|
185
|
+
return this.createVarible(parent, value)
|
|
186
|
+
}
|
|
126
187
|
|
|
127
188
|
if (value.trimStart().startsWith("ArrayDataKey=")) {
|
|
128
189
|
const [key, ...val] = value.trimStart().substring(13).split("|")
|
|
@@ -16,7 +16,10 @@ export const getRoutesAndTemplates = (pages: PageData[], normalize: boolean = tr
|
|
|
16
16
|
for (const [id, template] of pages.entries()) {
|
|
17
17
|
const content = parser.parse(template.root)
|
|
18
18
|
|
|
19
|
-
const mask = template.mask
|
|
19
|
+
const mask = template.mask
|
|
20
|
+
.replaceAll("/", "\\/")
|
|
21
|
+
.replace(/{(.*?)}/, ".+?")
|
|
22
|
+
.replace("\/*", "\/?.*")
|
|
20
23
|
|
|
21
24
|
routes.push({
|
|
22
25
|
id,
|
|
@@ -29,5 +32,6 @@ export const getRoutesAndTemplates = (pages: PageData[], normalize: boolean = tr
|
|
|
29
32
|
return {
|
|
30
33
|
routes,
|
|
31
34
|
templates: parser.templates,
|
|
35
|
+
customComponents: parser.custom,
|
|
32
36
|
}
|
|
33
37
|
}
|
|
@@ -161,6 +161,10 @@ function createDataProxy<T>(data: T, path: string = ""): WithDataProxy<T> {
|
|
|
161
161
|
// ---------------------------
|
|
162
162
|
// 8. Nested object
|
|
163
163
|
// ---------------------------
|
|
164
|
+
if (data === null) {
|
|
165
|
+
return undefined
|
|
166
|
+
}
|
|
167
|
+
|
|
164
168
|
const value = (data as any)[prop]
|
|
165
169
|
if (!ssr.exportParams) {
|
|
166
170
|
return value
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { JinraiAgent } from "../../bin/agent/agent"
|
|
1
|
+
import { JinraiAgent, ViteAgent } from "../../bin/agent/agent"
|
|
2
2
|
|
|
3
3
|
export const ssr = {
|
|
4
4
|
current: navigator.userAgent == JinraiAgent,
|
|
5
|
+
// current: true,
|
|
6
|
+
|
|
7
|
+
test: navigator.userAgent == ViteAgent,
|
|
5
8
|
exportParams: true,
|
|
6
9
|
exportToJV: false,
|
|
7
10
|
}
|
|
@@ -13,6 +16,7 @@ if (window != undefined) {
|
|
|
13
16
|
|
|
14
17
|
export const stringifyInput = (input: any) => {
|
|
15
18
|
ssr.exportParams = false
|
|
19
|
+
|
|
16
20
|
const result = JSON.stringify(input)
|
|
17
21
|
ssr.exportParams = true
|
|
18
22
|
|
|
@@ -2,7 +2,7 @@ import { ssr } from "./SSR"
|
|
|
2
2
|
import { sources } from "./DataProxy"
|
|
3
3
|
import { getJinraiValue, JinraiValue } from "../url/search/useSearchValue"
|
|
4
4
|
|
|
5
|
-
export function
|
|
5
|
+
export function orig<T>(value: T): T {
|
|
6
6
|
if (!ssr.current) return value
|
|
7
7
|
|
|
8
8
|
switch (typeof value) {
|
|
@@ -31,6 +31,8 @@ export function real<T>(value: T): T {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
export const original = orig
|
|
35
|
+
|
|
34
36
|
const wrapSource = <T>(value: T, source: JinraiValue): T => {
|
|
35
37
|
if (typeof value == "string") {
|
|
36
38
|
return value.bindSource(source) as T
|
|
@@ -40,7 +40,7 @@ if (window != undefined) {
|
|
|
40
40
|
window.$exportServerStates = serverStates
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const getIdent = (key: ServerKey): string => {
|
|
43
|
+
export const getIdent = (key: ServerKey): string => {
|
|
44
44
|
return Array.isArray(key) ? key.join("-") : key
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -51,13 +51,19 @@ export const getServerValue = (key?: ServerKey, def?: ServerValue["value"], opti
|
|
|
51
51
|
const ident = getIdent(key)
|
|
52
52
|
serverStates.set(ident, { options, value: !options?.source ? def : undefined, key })
|
|
53
53
|
|
|
54
|
+
console.log("CHECK", ident)
|
|
55
|
+
|
|
54
56
|
if (ident in initialState) {
|
|
55
57
|
const result = initialState[ident]
|
|
56
58
|
// delete initialState[ident]
|
|
57
59
|
|
|
58
60
|
console.log("HAS", ident, result)
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
if (result != null && typeof result == 'object' && "data" in result) {
|
|
63
|
+
return [result.data, true]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return [result, true]
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
return [def, false]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getIdent, ServerValue } from "./serverStates";
|
|
2
|
+
import { ssr } from "./SSR";
|
|
3
|
+
import { ServerKey } from "./useServerState";
|
|
4
|
+
|
|
5
|
+
const test_states = new Map<string, any>()
|
|
6
|
+
|
|
7
|
+
if (window != undefined && ssr.test) {
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
window.$testStates = test_states
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const setTestState = (key: ServerKey, value: ServerValue["value"], options?: ServerValue["options"]) => {
|
|
13
|
+
const ident = getIdent(key)
|
|
14
|
+
test_states.set(ident, value)
|
|
15
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Dispatch, SetStateAction,
|
|
1
|
+
import { Dispatch, SetStateAction, useState } from "react"
|
|
2
2
|
import { ssr } from "./SSR"
|
|
3
3
|
import { getServerValue, ServerValue, setServerValue } from "./serverStates"
|
|
4
|
+
import { setTestState } from "./testState"
|
|
4
5
|
|
|
5
6
|
export type ServerStateMap = Record<string, ServerValue>
|
|
6
7
|
export type ServerKey = string | string[]
|
|
@@ -12,7 +13,6 @@ export const useServerState = <T extends ServerValue["value"]>(
|
|
|
12
13
|
) => {
|
|
13
14
|
const [serverValue, isInitOnServer] = getServerValue(serverKey, initialValue, options)
|
|
14
15
|
const [value, setStateValue] = useState(serverValue)
|
|
15
|
-
const isInit = useRef(isInitOnServer)
|
|
16
16
|
|
|
17
17
|
const setValue: Dispatch<SetStateAction<T>> = (value: T | ((prevState: T) => T)) => {
|
|
18
18
|
setStateValue((prev: T) => {
|
|
@@ -22,18 +22,13 @@ export const useServerState = <T extends ServerValue["value"]>(
|
|
|
22
22
|
return setServerValue(serverKey, result, options)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
if (serverKey != undefined && ssr.test) {
|
|
26
|
+
setTestState(serverKey, result, options)
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
return result
|
|
26
30
|
})
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
// const initOnServer = () => {
|
|
30
|
-
// if (isInit.current) {
|
|
31
|
-
// isInit.current = false
|
|
32
|
-
// return true
|
|
33
|
-
// }
|
|
34
|
-
|
|
35
|
-
// return false
|
|
36
|
-
// }
|
|
37
|
-
|
|
38
33
|
return [value, setValue, isInitOnServer] as [T, Dispatch<SetStateAction<T>>, boolean]
|
|
39
34
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { createContext, useContext, useEffect, useState, type ReactNode } from "react"
|
|
2
|
+
import { ssr } from "../server-state/SSR"
|
|
3
|
+
|
|
4
|
+
export type DefaultLangType = {
|
|
5
|
+
defaultLang: string
|
|
6
|
+
langBaseUrl: string
|
|
7
|
+
source: TranslateSource
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// let DefaultConfig: DefaultLangType | undefined = undefined
|
|
11
|
+
|
|
12
|
+
// if (window != undefined) {
|
|
13
|
+
// // @ts-ignore
|
|
14
|
+
// window.$langDefaultConfig = DefaultConfig
|
|
15
|
+
// }
|
|
16
|
+
|
|
17
|
+
interface TranslateContextProps {
|
|
18
|
+
translate: (text: string, context?: string) => string
|
|
19
|
+
changeLang: (lang: string) => void
|
|
20
|
+
lang: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const TranslateContext = createContext<TranslateContextProps | null>(null)
|
|
24
|
+
|
|
25
|
+
export const useTranslate = () => {
|
|
26
|
+
const context = useContext(TranslateContext)
|
|
27
|
+
if (!context) throw new Error("not in context (<TranslateConfig>)")
|
|
28
|
+
return context
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface TranslateSource {
|
|
32
|
+
from: "cookie" | "url"
|
|
33
|
+
key: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type TranslateConfigProps = DefaultLangType & {
|
|
37
|
+
children: ReactNode
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type LangMap = Record<string, string>
|
|
41
|
+
|
|
42
|
+
const ONE_DAY = 1000 * 60 * 60 * 24
|
|
43
|
+
|
|
44
|
+
const getCookie = (key: string): string | null => {
|
|
45
|
+
const match = document.cookie.match(new RegExp(`(?:^|; )${key.replace(/([$?*|{}()[\]\\/+^])/g, "\\$1")}=([^;]*)`))
|
|
46
|
+
return match ? decodeURIComponent(match[1]) : null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const setCookie = (key: string, value: string, days = 365) => {
|
|
50
|
+
const expires = new Date(Date.now() + days * 864e5).toUTCString()
|
|
51
|
+
document.cookie = `${key}=${encodeURIComponent(value)}; expires=${expires}; path=/`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const getStorageKey = (lang: string) => `lang:${lang}`
|
|
55
|
+
|
|
56
|
+
const loadFromStorage = (lang: string): LangMap | null => {
|
|
57
|
+
try {
|
|
58
|
+
const raw = localStorage.getItem(getStorageKey(lang))
|
|
59
|
+
if (!raw) return null
|
|
60
|
+
|
|
61
|
+
const parsed = JSON.parse(raw) as { data: LangMap; expires: number }
|
|
62
|
+
if (Date.now() > parsed.expires) {
|
|
63
|
+
localStorage.removeItem(getStorageKey(lang))
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return parsed.data
|
|
68
|
+
} catch {
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const saveToStorage = (lang: string, data: LangMap) => {
|
|
74
|
+
localStorage.setItem(
|
|
75
|
+
getStorageKey(lang),
|
|
76
|
+
JSON.stringify({
|
|
77
|
+
data,
|
|
78
|
+
expires: Date.now() + ONE_DAY,
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const TranslateConfig = ({ children, defaultLang, langBaseUrl, source }: TranslateConfigProps) => {
|
|
84
|
+
const [lang, setLang] = useState(defaultLang)
|
|
85
|
+
const [langMap, setLangMap] = useState<LangMap | null>(null)
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
window.$langDefaultConfig = {
|
|
90
|
+
defaultLang,
|
|
91
|
+
langBaseUrl,
|
|
92
|
+
source,
|
|
93
|
+
}
|
|
94
|
+
}, [])
|
|
95
|
+
|
|
96
|
+
// initial lang from cookie
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (source.from === "cookie") {
|
|
99
|
+
const fromCookie = getCookie(source.key)
|
|
100
|
+
if (fromCookie) setLang(fromCookie)
|
|
101
|
+
}
|
|
102
|
+
}, [source.from, source.key])
|
|
103
|
+
|
|
104
|
+
// load translations on lang change
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (lang === defaultLang) {
|
|
107
|
+
setLangMap(null)
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const cached = loadFromStorage(lang)
|
|
112
|
+
if (cached) {
|
|
113
|
+
setLangMap(cached)
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fetch(langBaseUrl.replace("*", lang))
|
|
118
|
+
.then(res => {
|
|
119
|
+
if (!res.ok) throw new Error("Failed to load lang map")
|
|
120
|
+
return res.json()
|
|
121
|
+
})
|
|
122
|
+
.then((data: LangMap) => {
|
|
123
|
+
saveToStorage(lang, data)
|
|
124
|
+
setLangMap(data)
|
|
125
|
+
})
|
|
126
|
+
.catch(() => {
|
|
127
|
+
setLangMap(null)
|
|
128
|
+
})
|
|
129
|
+
}, [lang, defaultLang, langBaseUrl])
|
|
130
|
+
|
|
131
|
+
const translate = (text: string, context?: string): string => {
|
|
132
|
+
const key = context ? `${text}(context:${context})` : text
|
|
133
|
+
|
|
134
|
+
if (ssr.current) {
|
|
135
|
+
return `{!${key}!}`
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!langMap) return text
|
|
139
|
+
return langMap[key] ?? langMap[text] ?? text
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const changeLang = (nextLang: string) => {
|
|
143
|
+
if (nextLang === lang) return
|
|
144
|
+
|
|
145
|
+
if (source.from === "cookie") {
|
|
146
|
+
setCookie(source.key, nextLang)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
setLang(nextLang)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return <TranslateContext.Provider value={{ translate, changeLang, lang }}>{children}</TranslateContext.Provider>
|
|
153
|
+
}
|
|
@@ -3,8 +3,10 @@ import { createContext, DependencyList, ReactNode } from "react"
|
|
|
3
3
|
export interface JinraiProps {
|
|
4
4
|
deps?: DependencyList
|
|
5
5
|
children?: ReactNode
|
|
6
|
+
search: string
|
|
6
7
|
}
|
|
7
8
|
export const JinraiContext = createContext<JinraiProps>({
|
|
8
9
|
deps: [],
|
|
9
10
|
children: undefined,
|
|
11
|
+
search: ""
|
|
10
12
|
})
|
|
@@ -4,7 +4,7 @@ import { NuqsAdapter } from "nuqs/adapters/react"
|
|
|
4
4
|
export const Adapter = (props: JinraiProps) => {
|
|
5
5
|
return (
|
|
6
6
|
<NuqsAdapter>
|
|
7
|
-
<JinraiContext.Provider value={{ deps: props.deps ?? [] }} {...props} />
|
|
7
|
+
<JinraiContext.Provider value={{ deps: props.deps ?? [], search: "" }} {...props} />
|
|
8
8
|
</NuqsAdapter>
|
|
9
9
|
)
|
|
10
10
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import React from "react"
|
|
2
1
|
import { NuqsAdapter } from "nuqs/adapters/react-router/v6"
|
|
3
2
|
|
|
4
3
|
// @ts-ignore
|
|
@@ -9,11 +8,11 @@ export const Adapter = (props: JinraiProps) => {
|
|
|
9
8
|
// useLocation требует, чтобы компонент был внутри RouterProvider
|
|
10
9
|
// NuqsAdapter должен быть установлен на верхнем уровне, но может работать внутри роутера
|
|
11
10
|
// для React Router v6 адаптера
|
|
12
|
-
const { pathname } = useLocation()
|
|
11
|
+
const { pathname, search } = useLocation()
|
|
13
12
|
|
|
14
13
|
return (
|
|
15
14
|
<NuqsAdapter>
|
|
16
|
-
<JinraiContext.Provider value={{ deps: [...(props.deps ?? []), pathname] }} {...props} />
|
|
15
|
+
<JinraiContext.Provider value={{ deps: [...(props.deps ?? []), pathname], search: search.substring(1) }} {...props} />
|
|
17
16
|
</NuqsAdapter>
|
|
18
17
|
)
|
|
19
18
|
}
|
|
@@ -8,11 +8,11 @@ export const Adapter = (props: JinraiProps) => {
|
|
|
8
8
|
// useLocation требует, чтобы компонент был внутри RouterProvider
|
|
9
9
|
// NuqsAdapter должен быть установлен на верхнем уровне, но может работать внутри роутера
|
|
10
10
|
// для React Router v7 адаптера
|
|
11
|
-
const { pathname } = useLocation()
|
|
11
|
+
const { pathname, search } = useLocation()
|
|
12
12
|
|
|
13
13
|
return (
|
|
14
14
|
<NuqsAdapter>
|
|
15
|
-
<JinraiContext.Provider value={{ deps: [...(props.deps ?? []), pathname] }} {...props} />
|
|
15
|
+
<JinraiContext.Provider value={{ deps: [...(props.deps ?? []), pathname], search}} {...props} />
|
|
16
16
|
</NuqsAdapter>
|
|
17
17
|
)
|
|
18
18
|
}
|
|
@@ -4,12 +4,11 @@ import { ssr } from "../../server-state/SSR"
|
|
|
4
4
|
import { getJinraiValue } from "./useSearchValue"
|
|
5
5
|
|
|
6
6
|
export const useSearch = (): string => {
|
|
7
|
-
const {
|
|
8
|
-
const value = useMemo(() => location.search.substring(1), deps ? deps : [])
|
|
7
|
+
const { search } = useContext(JinraiContext)
|
|
9
8
|
|
|
10
9
|
const stableValue = useMemo(() => {
|
|
11
|
-
return ssr.current ?
|
|
12
|
-
}, [
|
|
10
|
+
return ssr.current ? search.bindSource(getJinraiValue("", "searchFull", "", "")) : search
|
|
11
|
+
}, [search])
|
|
13
12
|
|
|
14
13
|
return stableValue
|
|
15
14
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Fragment, type ReactElement, type ReactNode } from "react"
|
|
2
2
|
import { ssr } from "../server-state/SSR"
|
|
3
3
|
import { SPLIT } from "../../bin/routes/Parser"
|
|
4
|
+
import { orig } from "../server-state/orig"
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
interface CustomProps {
|
|
6
8
|
name: string
|
|
@@ -11,7 +13,13 @@ interface CustomProps {
|
|
|
11
13
|
export const Custom = ({ name, props, children }: CustomProps) => {
|
|
12
14
|
if (!ssr.current) return <Fragment>{children as ReactElement}</Fragment>
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
if (typeof props != "function") {
|
|
17
|
+
throw new Error(`Custom props is not function (${name})`);
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const exampleProps = JSON.stringify({ name, props: orig(props()) })
|
|
22
|
+
|
|
15
23
|
ssr.exportToJV = true
|
|
16
24
|
const customProps = JSON.stringify({ name, props: props() })
|
|
17
25
|
ssr.exportToJV = false
|
|
@@ -9,7 +9,8 @@ describe("test custom component", async () => {
|
|
|
9
9
|
|
|
10
10
|
it("parse custom.html", async () => {
|
|
11
11
|
const html = await readFile("./tests/parse/content/custom.html", "utf-8")
|
|
12
|
-
|
|
12
|
+
const result = parsr.parse(html)
|
|
13
|
+
// expect(JSON.stringify(result)).toEqual("")
|
|
13
14
|
|
|
14
15
|
expect(parsr.parse(html)).toEqual(custom)
|
|
15
16
|
})
|
package/tsconfig.types.json
CHANGED