jinrai 1.1.2 → 1.1.4

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 (85) hide show
  1. package/front.config.json +2 -1
  2. package/index.ts +4 -1
  3. package/lib/bin/bin.js +123 -59
  4. package/lib/index.d.ts +3 -1
  5. package/lib/index.js +3 -1
  6. package/lib/src/bin/agent/agent.d.ts +2 -0
  7. package/lib/src/bin/agent/agent.js +4 -0
  8. package/lib/src/bin/playwright/pageCollector.d.ts +2 -0
  9. package/lib/src/bin/playwright/pageTestCollector.d.ts +6 -0
  10. package/lib/src/bin/playwright/templates.d.ts +6 -1
  11. package/lib/src/bin/routes/Parser.d.ts +25 -2
  12. package/lib/src/bin/routes/Parser.js +5 -0
  13. package/lib/src/bin/routes/getRoutes.d.ts +1 -0
  14. package/lib/src/front/server/useIsServer.d.ts +1 -0
  15. package/lib/src/front/server/useIsServer.js +7 -0
  16. package/lib/src/front/server-state/DataProxy.d.ts +2 -1
  17. package/lib/src/front/server-state/DataProxy.js +122 -60
  18. package/lib/src/front/server-state/SSR.d.ts +3 -0
  19. package/lib/src/front/server-state/SSR.js +18 -3
  20. package/lib/src/front/server-state/orig.d.ts +2 -0
  21. package/lib/src/front/server-state/{real.js → orig.js} +18 -3
  22. package/lib/src/front/server-state/serverStates.d.ts +3 -1
  23. package/lib/src/front/server-state/serverStates.js +24 -17
  24. package/lib/src/front/server-state/testState.d.ts +3 -0
  25. package/lib/src/front/server-state/testState.js +14 -0
  26. package/lib/src/front/server-state/useServerState.d.ts +1 -1
  27. package/lib/src/front/server-state/useServerState.js +5 -9
  28. package/lib/src/front/translate/TranslateConfig.d.ts +21 -0
  29. package/lib/src/front/translate/TranslateConfig.js +108 -0
  30. package/lib/src/front/url/JinraiContext.d.ts +1 -0
  31. package/lib/src/front/url/JinraiContext.js +1 -0
  32. package/lib/src/front/url/adapter/def.js +1 -1
  33. package/lib/src/front/url/adapter/rrd6.js +2 -2
  34. package/lib/src/front/url/adapter/rrd7.js +2 -2
  35. package/lib/src/front/url/params/useParamsIndex.js +2 -1
  36. package/lib/src/front/url/search/useSearch.js +4 -4
  37. package/lib/src/front/url/search/useSearchValue.d.ts +11 -5
  38. package/lib/src/front/url/search/useSearchValue.js +13 -8
  39. package/lib/src/front/wrapper/Custom.d.ts +3 -3
  40. package/lib/src/front/wrapper/Custom.js +18 -1
  41. package/lib/vite/plugin.js +26 -154
  42. package/package.json +9 -1
  43. package/rollup.config.mjs +2 -1
  44. package/src/bin/agent/agent.ts +2 -0
  45. package/src/bin/build/build.ts +23 -10
  46. package/src/bin/playwright/pageCollector.ts +8 -6
  47. package/src/bin/playwright/pageTestCollector.ts +15 -0
  48. package/src/bin/playwright/templates.ts +16 -5
  49. package/src/bin/routes/Parser.ts +100 -32
  50. package/src/bin/routes/getRoutes.ts +5 -1
  51. package/src/front/server/useIsServer.ts +5 -0
  52. package/src/front/server-state/DataProxy.ts +140 -61
  53. package/src/front/server-state/SSR.ts +22 -2
  54. package/src/front/server-state/{real.ts → orig.ts} +19 -2
  55. package/src/front/server-state/serverStates.ts +33 -18
  56. package/src/front/server-state/testState.ts +15 -0
  57. package/src/front/server-state/useServerState.ts +6 -11
  58. package/src/front/translate/TranslateConfig.tsx +153 -0
  59. package/src/front/url/JinraiContext.tsx +2 -0
  60. package/src/front/url/adapter/def.tsx +1 -1
  61. package/src/front/url/adapter/rrd6.tsx +2 -3
  62. package/src/front/url/adapter/rrd7.tsx +2 -2
  63. package/src/front/url/search/useSearch.ts +3 -4
  64. package/src/front/url/search/useSearchValue.ts +25 -13
  65. package/src/front/wrapper/Custom.tsx +28 -4
  66. package/tests/data-proxy/create-dataproxy.test.ts +116 -0
  67. package/tests/{custom.test.ts → parse/custom.test.ts} +2 -2
  68. package/tests/{parse.test.ts → parse/parse.test.ts} +7 -7
  69. package/tsconfig.types.json +1 -0
  70. package/vite/plugin.ts +40 -22
  71. package/lib/src/front/server-state/real.d.ts +0 -1
  72. /package/tests/{content → parse/content}/1.html +0 -0
  73. /package/tests/{content → parse/content}/1_result.json +0 -0
  74. /package/tests/{content → parse/content}/2.html +0 -0
  75. /package/tests/{content → parse/content}/2_result.json +0 -0
  76. /package/tests/{content → parse/content}/3.html +0 -0
  77. /package/tests/{content → parse/content}/3_result.json +0 -0
  78. /package/tests/{content → parse/content}/4.html +0 -0
  79. /package/tests/{content → parse/content}/4_result.json +0 -0
  80. /package/tests/{content → parse/content}/custom.html +0 -0
  81. /package/tests/{content → parse/content}/custom.json +0 -0
  82. /package/tests/{content → parse/content}/index.html +0 -0
  83. /package/tests/{content → parse/content}/index.json +0 -0
  84. /package/tests/{content → parse/content}/index_with_templates.json +0 -0
  85. /package/tests/{content → parse/content}/templates.json +0 -0
@@ -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 { deps } = useContext(JinraiContext)
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 ? value.bindSource(getJinraiValue("", "search", "", "")) : value
12
- }, [value])
10
+ return ssr.current ? search.bindSource(getJinraiValue("", "searchFull", "", "")) : search
11
+ }, [search])
13
12
 
14
13
  return stableValue
15
14
  }
@@ -2,36 +2,43 @@ import { parseAsArrayOf, parseAsString, useQueryState, UseQueryStateReturn } fro
2
2
  import { useMemo } from "react"
3
3
  import { ssr } from "../../server-state/SSR"
4
4
 
5
+ export interface JinraiValue {
6
+ key: string
7
+ type: "searchArray" | "searchString" | "proxy" | "searchFull" | "paramsIndex"
8
+ separator: string
9
+ def: any
10
+ }
11
+
5
12
  declare global {
6
13
  interface String {
7
- source?: string
14
+ $JV?: JinraiValue
8
15
  toJSON: () => string
9
- bindSource: (source: string) => string
16
+ bindSource: (source: JinraiValue) => string
10
17
  }
11
18
  interface Array<T> {
12
- source?: string
19
+ $JV?: JinraiValue
13
20
  toJSON(): any
14
- bindSource(source: string): T[]
21
+ bindSource(source: JinraiValue): T[]
15
22
  }
16
23
  }
17
24
 
18
25
  function toJSON() {
19
26
  if (ssr.exportParams) {
20
27
  // @ts-ignore
21
- const source = this.source
28
+ const $JV = this.$JV
22
29
  // @ts-ignore
23
- return source ? source : this
30
+ return $JV ? { $JV } : this
24
31
  }
25
32
  // @ts-ignore
26
33
  return this
27
34
  }
28
35
 
29
36
  if (true) {
30
- String.prototype.source = undefined
37
+ String.prototype.$JV = undefined
31
38
  String.prototype.toJSON = toJSON
32
- String.prototype.bindSource = function (source: string): string {
39
+ String.prototype.bindSource = function (source: JinraiValue): string {
33
40
  const result = new String(this)
34
- result.source = source
41
+ result.$JV = source
35
42
  return result as string
36
43
  }
37
44
 
@@ -40,8 +47,8 @@ if (true) {
40
47
  }
41
48
 
42
49
  if (!Array.prototype.bindSource) {
43
- Array.prototype.bindSource = function (source: string) {
44
- this.source = source
50
+ Array.prototype.bindSource = function (source: JinraiValue) {
51
+ this.$JV = source
45
52
  return this
46
53
  }
47
54
  }
@@ -71,6 +78,11 @@ export const useSearchArray = (
71
78
  return [stableValue, setValue]
72
79
  }
73
80
 
74
- export const getJinraiValue = (key: string, type: string, separator: string, def: any) => {
75
- return `@JV[[${JSON.stringify({ key, type, separator, def })}]]`
81
+ export const getJinraiValue = (key: string, type: JinraiValue["type"], separator: string, def: any): JinraiValue => {
82
+ return {
83
+ key,
84
+ type,
85
+ separator,
86
+ def,
87
+ }
76
88
  }
@@ -1,14 +1,38 @@
1
- import type { FC, ReactElement, ReactNode } from "react"
1
+ import { Fragment, type ReactElement, type ReactNode } from "react"
2
2
  import { ssr } from "../server-state/SSR"
3
+ import { SPLIT } from "../../bin/routes/Parser"
4
+ import { orig } from "../server-state/orig"
5
+
3
6
 
4
7
  interface CustomProps {
5
8
  name: string
6
- props: object
9
+ props: () => object
7
10
  children: ReactNode
8
11
  }
9
12
 
10
13
  export const Custom = ({ name, props, children }: CustomProps) => {
11
- if (!ssr.current) return children as ReactElement
14
+ if (!ssr.current) return <Fragment>{children as ReactElement}</Fragment>
15
+
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
+
23
+ ssr.exportToJV = true
24
+ const customProps = JSON.stringify({ name, props: props() })
25
+ ssr.exportToJV = false
12
26
 
13
- return `<custom>${JSON.stringify({ name, props })}<custom>`
27
+ return (
28
+ //@ts-ignore
29
+ <custom>
30
+ {customProps}
31
+ {SPLIT}
32
+ {exampleProps}
33
+ {SPLIT}
34
+ {children}
35
+ {/* @ts-ignore */}
36
+ </custom>
37
+ )
14
38
  }
@@ -0,0 +1,116 @@
1
+ import { describe, it, expect } from "vitest"
2
+ import createDataProxy, { DataProxy } from "../../src/front/server-state/DataProxy"
3
+ import { ssr, stringifyInput } from "../../src/front/server-state/SSR"
4
+
5
+ const getValue = <T>(p: T & DataProxy) => p.getValue()
6
+
7
+ describe("createDataProxy", () => {
8
+ it("should return getValue() = original data", () => {
9
+ const proxy = createDataProxy({ a: 1 })
10
+ expect(proxy.getValue()).toEqual({ a: 1 })
11
+ })
12
+
13
+ it("should generate template for primitive children", () => {
14
+ const proxy = createDataProxy({ name: "John" })
15
+
16
+ // name → {{/name}}
17
+ expect((proxy as any).name).toBe("{{/name}}")
18
+ })
19
+
20
+ it("should create nested proxies for objects", () => {
21
+ const proxy = createDataProxy({ user: { age: 20 } })
22
+
23
+ // user is proxy
24
+ const sub = (proxy as any).user
25
+ expect(typeof sub.getValue).toBe("function")
26
+
27
+ // nested fallback
28
+ expect(sub.age).toBe("{{/user/age}}")
29
+ })
30
+
31
+ it("should work with arrays in map()", () => {
32
+ const data = { items: [1, 2, 3] }
33
+ const proxy = createDataProxy(data)
34
+
35
+ const res = (proxy as any).items.map((itm: any) => itm)
36
+ // вернул React.createElement(...) → проверим массив аргументов
37
+ const [, arr] = res.props.children
38
+
39
+ expect(arr.length).toBe(1) // slice(0,1)
40
+ const mapped = arr[0]
41
+ expect(getValue(mapped)).toBe(1)
42
+ })
43
+
44
+ it("should handle Symbol.toPrimitive", () => {
45
+ const proxy = createDataProxy(123 as any)
46
+
47
+ const str = `${proxy}` // вызывает toPrimitive
48
+
49
+ expect(str).toBe("{{}}") // путь "" → {{}}
50
+ })
51
+
52
+ it("should return correct path in nested map()", () => {
53
+ const proxy = createDataProxy({
54
+ list: [{ value: 10 }],
55
+ })
56
+
57
+ const result = (proxy as any).list.map((itm: any) => itm)
58
+
59
+ const [, arr] = result.props.children
60
+ const mappedProxy = arr[0]
61
+
62
+ expect(mappedProxy.value).toBe("{{/list/[ITEM=0]/value}}")
63
+ })
64
+
65
+ it("should allow calling $ accessors", () => {
66
+ const proxy = createDataProxy({})
67
+
68
+ const expr = (proxy as any).$some("id")
69
+
70
+ expect(expr).toBe("{{/id\\$some}}")
71
+ })
72
+
73
+ it("toJSON should return template when ssr.exportParams = true", () => {
74
+ const proxy = createDataProxy({ a: 1 }, "/root")
75
+
76
+ expect(stringifyInput(proxy)).toBe('"{{/root}}"')
77
+ })
78
+
79
+ it("should ignore then / Promise-like traps", () => {
80
+ const proxy = createDataProxy({ then: "abc" } as any)
81
+
82
+ // then должен быть undefined → чтобы Proxy не выглядел как Promise
83
+ expect((proxy as any).then).toBe(undefined)
84
+ })
85
+
86
+ it("should preserve Symbol.iterator for iterable data", () => {
87
+ const original = [1, 2, 3]
88
+ const proxy = createDataProxy(original)
89
+
90
+ const iter = (proxy as any)[Symbol.iterator]
91
+ expect(typeof iter).toBe("function")
92
+
93
+ const collected = [...proxy]
94
+ expect(collected).toEqual(["{{/0}}", "{{/1}}", "{{/2}}"])
95
+ })
96
+
97
+ it("should not define Symbol.iterator on non-iterables", () => {
98
+ const proxy = createDataProxy({})
99
+
100
+ const iter = (proxy as any)[Symbol.iterator]
101
+ expect(iter).toBe(undefined)
102
+ })
103
+
104
+ it("should produce fallback template for unknown prop", () => {
105
+ const proxy = createDataProxy({})
106
+
107
+ expect((proxy as any).abc).toBe("{{/abc}}")
108
+ })
109
+
110
+ it("should produce path-based fallback for nested unknown prop", () => {
111
+ const proxy = createDataProxy({ user: {} })
112
+
113
+ const sub = (proxy as any).user
114
+ expect(sub.unknown).toBe("{{/user/unknown}}")
115
+ })
116
+ })
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest"
2
- import { Parser } from "../src/bin/routes/Parser"
2
+ import { Parser } from "../../src/bin/routes/Parser"
3
3
  import { readFile } from "fs/promises"
4
4
 
5
5
  import custom from "./content/custom.json"
@@ -8,7 +8,7 @@ describe("test custom component", async () => {
8
8
  const parsr = new Parser({ normalize: true })
9
9
 
10
10
  it("parse custom.html", async () => {
11
- const html = await readFile("./tests/content/custom.html", "utf-8")
11
+ const html = await readFile("./tests/parse/content/custom.html", "utf-8")
12
12
  // expect(JSON.stringify(parsr.parse(html))).toEqual("")
13
13
 
14
14
  expect(parsr.parse(html)).toEqual(custom)
@@ -1,6 +1,6 @@
1
1
  import { readFile } from "node:fs/promises"
2
2
  import { describe, expect, it } from "vitest"
3
- import { Parser } from "../src/bin/routes/Parser"
3
+ import { Parser } from "../../src/bin/routes/Parser"
4
4
 
5
5
  import result_1 from "./content/1_result.json"
6
6
  import result_2 from "./content/2_result.json"
@@ -14,27 +14,27 @@ describe("test Parser class", async () => {
14
14
  const parsr = new Parser({ normalize: true })
15
15
 
16
16
  it("parse 1.html", async () => {
17
- const html = await readFile("./tests/content/1.html", "utf-8")
17
+ const html = await readFile("./tests/parse/content/1.html", "utf-8")
18
18
  expect(parsr.parse(html)).toEqual(result_1)
19
19
  })
20
20
 
21
21
  it("parse 2.html", async () => {
22
- const html = await readFile("./tests/content/2.html", "utf-8")
22
+ const html = await readFile("./tests/parse/content/2.html", "utf-8")
23
23
  expect(parsr.parse(html)).toEqual(result_2)
24
24
  })
25
25
 
26
26
  it("parse 3.html", async () => {
27
- const html = await readFile("./tests/content/3.html", "utf-8")
27
+ const html = await readFile("./tests/parse/content/3.html", "utf-8")
28
28
  expect(parsr.parse(html)).toEqual(result_3)
29
29
  })
30
30
 
31
31
  it("parse 4.html", async () => {
32
- const html = await readFile("./tests/content/4.html", "utf-8")
32
+ const html = await readFile("./tests/parse/content/4.html", "utf-8")
33
33
  expect(parsr.parse(html)).toEqual(result_4)
34
34
  })
35
35
 
36
36
  it("parse index.html", async () => {
37
- const html = await readFile("./tests/content/index.html", "utf-8")
37
+ const html = await readFile("./tests/parse/content/index.html", "utf-8")
38
38
  expect(parsr.parse(html)).toEqual(index)
39
39
  })
40
40
  })
@@ -43,7 +43,7 @@ describe("test Parser templates", async () => {
43
43
  const parsr = new Parser({ normalize: true, templates: true })
44
44
 
45
45
  it("parse index.html", async () => {
46
- const html = await readFile("./tests/content/index.html", "utf-8")
46
+ const html = await readFile("./tests/parse/content/index.html", "utf-8")
47
47
  // expect(JSON.stringify(parsr.templates)).toEqual("")
48
48
 
49
49
  expect(parsr.parse(html)).toEqual(index_tempates)
@@ -30,3 +30,4 @@
30
30
  ]
31
31
  }
32
32
 
33
+
package/vite/plugin.ts CHANGED
@@ -1,23 +1,30 @@
1
1
  import { createServer, type Plugin, type ViteDevServer } from "vite"
2
2
 
3
3
  import { AsyncLocalStorage } from "async_hooks"
4
- import { type BrowserContext, chromium } from "playwright"
5
- import { pageCollector } from "../src/bin/playwright/pageCollector"
6
- import { getRoutesAndTemplates } from "../src/bin/routes/getRoutes"
7
-
8
- import { writeFile } from "fs/promises"
4
+ import { type BrowserContext, chromium, Page } from "playwright"
5
+ import { ViteAgent } from "../src/bin/agent/agent"
6
+ import { pageTestCollector } from "../src/bin/playwright/pageTestCollector"
9
7
 
10
8
  const urlStorage = new AsyncLocalStorage<string>()
11
9
 
10
+ interface CasheItem {
11
+ state: any,
12
+ root: string,
13
+ }
14
+
15
+ const cashe = new Map<string, CasheItem>()
16
+
17
+
18
+
12
19
  export function hydration(): Plugin {
13
20
  if (process.env.CHILD_JINRAI_DEV_SERVER) {
14
21
  return { name: "vite-jinrai-dummy" }
15
22
  }
16
23
  process.env.CHILD_JINRAI_DEV_SERVER = "true"
17
24
 
18
- console.log("create mirror")
19
25
  let mirrorServer: ViteDevServer | undefined = undefined
20
26
  let context: BrowserContext | undefined = undefined
27
+ let page: Page | undefined = undefined
21
28
 
22
29
  let debugUrl: string | undefined = undefined
23
30
 
@@ -31,18 +38,20 @@ export function hydration(): Plugin {
31
38
 
32
39
  debugUrl = mirrorServer.resolvedUrls?.local[0].slice(0, -1)
33
40
 
34
- chromium.launch({ headless: true, devtools: false }).then(async browser => {
35
- console.log("create context")
41
+ chromium.launch({ headless: true, devtools: false, channel: "chrome" }).then(async browser => {
42
+
36
43
  context = await browser.newContext({
37
- userAgent: "____fast-ssr-tool___",
44
+ userAgent: ViteAgent,
38
45
  locale: "ru-RU",
39
46
  })
47
+
48
+ page = await context.newPage()
40
49
  })
41
50
  })
42
51
 
43
52
  return {
44
53
  name: "vite-jinrai",
45
-
54
+
46
55
  configureServer(server: ViteDevServer) {
47
56
  server.middlewares.use((req, _res, next) => {
48
57
  if (req.url?.startsWith("/@")) {
@@ -56,28 +65,37 @@ export function hydration(): Plugin {
56
65
 
57
66
  async transformIndexHtml(html: string) {
58
67
  const currentUrl = urlStorage.getStore()
59
- if (currentUrl && context) {
60
- const page = await context.newPage()
68
+ if (currentUrl && page) {
69
+ const data = cashe.get(currentUrl)
70
+ if (data) {
71
+ return print(html, data.root, data.state)
72
+ }
73
+
61
74
  await page.goto(debugUrl + currentUrl)
62
75
  await page.waitForLoadState("networkidle")
63
76
 
64
- const { root } = await pageCollector(page)
65
- const { routes } = getRoutesAndTemplates([{ id: 1, state: {}, mask: currentUrl, root }], true, false)
66
-
67
- console.log({ routes })
68
- writeFile("./routs.json", JSON.stringify(routes, null, 2))
77
+ await page.waitForTimeout(2000)
69
78
 
70
- const result = html.replace("<!--app-html-->", root)
79
+ const { root, state } = await pageTestCollector(page)
80
+ cashe.set(currentUrl, { root, state })
71
81
 
72
- page.close()
73
- return result
82
+ return print(html, root, state)
74
83
  }
75
84
  return html
76
85
  },
77
86
 
78
87
  closeWatcher() {
79
- console.log("Stop server")
80
- // mirrorServer?.close()
88
+ page?.close()
89
+ mirrorServer?.close()
90
+ context?.close()
81
91
  },
82
92
  }
83
93
  }
94
+
95
+
96
+ const print = (html: string, root: string, state: any) => {
97
+ html = html.replace("<!--app-html-->", root)
98
+ html = html.replace("<!--app-head-->", `<script>window.__appc__ = ${JSON.stringify({state})}</script>`)
99
+
100
+ return html
101
+ }
@@ -1 +0,0 @@
1
- export declare function real<T>(value: T): T;
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes