jinrai 1.0.4 → 1.0.7

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 (74) hide show
  1. package/config.ts +1 -0
  2. package/front.config.json +24 -0
  3. package/index.ts +6 -1
  4. package/lib/bin/bin.js +12 -0
  5. package/lib/config/config.d.ts +1 -0
  6. package/lib/config/config.js +1 -0
  7. package/lib/{src → config/src/bin}/config/userConfig.d.ts +0 -2
  8. package/lib/index.d.ts +6 -1
  9. package/lib/index.js +2 -1
  10. package/lib/src/front/server-state/DataProxy.d.ts +8 -0
  11. package/lib/src/front/server-state/DataProxy.js +104 -0
  12. package/lib/src/front/server-state/SSR.d.ts +4 -0
  13. package/lib/src/front/server-state/SSR.js +6 -0
  14. package/lib/src/front/server-state/real.d.ts +1 -0
  15. package/lib/src/front/server-state/real.js +36 -0
  16. package/lib/src/front/server-state/serverStates.d.ts +5 -0
  17. package/lib/src/front/server-state/serverStates.js +29 -0
  18. package/lib/src/front/server-state/useServerState.d.ts +14 -0
  19. package/lib/src/front/server-state/useServerState.js +28 -0
  20. package/lib/src/front/url/JinraiContext.d.ts +6 -0
  21. package/lib/src/front/url/JinraiContext.js +8 -0
  22. package/lib/src/front/url/adapter/def.d.ts +2 -0
  23. package/lib/src/front/url/adapter/def.js +9 -0
  24. package/lib/src/front/url/adapter/rrd6.d.ts +2 -0
  25. package/lib/src/front/url/adapter/rrd6.js +12 -0
  26. package/lib/src/front/url/adapter/rrd7.d.ts +2 -0
  27. package/lib/src/front/url/adapter/rrd7.js +11 -0
  28. package/lib/src/front/url/params/useParamsIndex.d.ts +1 -0
  29. package/lib/src/front/url/params/useParamsIndex.js +15 -0
  30. package/lib/src/front/url/search/useSearch.d.ts +1 -0
  31. package/lib/src/front/url/search/useSearch.js +15 -0
  32. package/lib/src/front/url/search/useSearchValue.d.ts +16 -0
  33. package/lib/src/front/url/search/useSearchValue.js +49 -0
  34. package/lib/src/front/wrapper/Custom.d.ts +8 -0
  35. package/lib/src/front/wrapper/Custom.js +9 -0
  36. package/lib/vite/plugin.js +1 -0
  37. package/package.json +39 -7
  38. package/readme.md +11 -0
  39. package/rollup.front.config.mjs +57 -0
  40. package/src/bin/bin.ts +10 -0
  41. package/src/bin/build/build.ts +69 -0
  42. package/src/{config → bin/config}/userConfig.ts +0 -2
  43. package/src/bin/playwright/pageCollector.ts +11 -0
  44. package/src/{templates.ts → bin/playwright/templates.ts} +20 -29
  45. package/src/{routes → bin/routes}/Parser.ts +3 -3
  46. package/src/{routes → bin/routes}/getRoutes.ts +4 -4
  47. package/src/front/server-state/DataProxy.ts +120 -0
  48. package/src/front/server-state/SSR.ts +4 -0
  49. package/src/front/server-state/real.ts +41 -0
  50. package/src/front/server-state/serverStates.ts +36 -0
  51. package/src/front/server-state/useServerState.ts +48 -0
  52. package/src/front/url/JinraiContext.tsx +10 -0
  53. package/src/front/url/adapter/def.tsx +10 -0
  54. package/src/front/url/adapter/rrd6.tsx +16 -0
  55. package/src/front/url/adapter/rrd7.tsx +15 -0
  56. package/src/front/url/params/useParamsIndex.ts +16 -0
  57. package/src/front/url/search/useSearch.ts +15 -0
  58. package/src/front/url/search/useSearchValue.ts +72 -0
  59. package/src/front/wrapper/Custom.tsx +14 -0
  60. package/tests/custom.test.ts +2 -2
  61. package/tests/parse.test.ts +3 -3
  62. package/tsconfig.json +2 -2
  63. package/vite/plugin.ts +83 -0
  64. package/lib/bin.js +0 -12
  65. package/lib/generate.js +0 -26
  66. package/src/bin.ts +0 -66
  67. /package/lib/{src → config/src/bin}/config/define.d.ts +0 -0
  68. /package/src/{config → bin/config}/defaultIndexHtml.ts +0 -0
  69. /package/src/{config → bin/config}/define.ts +0 -0
  70. /package/src/{content/normolizeContent.ts → bin/content/normalizeContent.ts} +0 -0
  71. /package/src/{routes → bin/routes}/replaceDevScripts.ts +0 -0
  72. /package/src/{server → bin/server}/vitePreview.ts +0 -0
  73. /package/src/{types → bin/types}/shims.d.ts +0 -0
  74. /package/src/{ui → bin/ui}/task.tsx +0 -0
@@ -0,0 +1,15 @@
1
+ import { useContext, useMemo } from 'react';
2
+ import { getJinraiValue } from '../search/useSearchValue.js';
3
+ import { JinraiContext } from '../JinraiContext.js';
4
+ import { ssr } from '../../server-state/SSR.js';
5
+
6
+ const useParamsIndex = (index, def = "") => {
7
+ const { deps } = useContext(JinraiContext);
8
+ const value = useMemo(() => location.pathname.split("/")[index + 1] ?? def, deps ? deps : []);
9
+ const stableValue = useMemo(() => {
10
+ return ssr.current ? value.bindSource(getJinraiValue(index.toString(), "paramsIndex", "", def)) : value;
11
+ }, [value]);
12
+ return stableValue;
13
+ };
14
+
15
+ export { useParamsIndex };
@@ -0,0 +1 @@
1
+ export declare const useSearch: () => string;
@@ -0,0 +1,15 @@
1
+ import { useContext, useMemo } from 'react';
2
+ import { JinraiContext } from '../JinraiContext.js';
3
+ import { ssr } from '../../server-state/SSR.js';
4
+ import { getJinraiValue } from './useSearchValue.js';
5
+
6
+ const useSearch = () => {
7
+ const { deps } = useContext(JinraiContext);
8
+ const value = useMemo(() => location.search.substring(1), deps ? deps : []);
9
+ const stableValue = useMemo(() => {
10
+ return ssr.current ? value.bindSource(getJinraiValue("", "search", "", "")) : value;
11
+ }, [value]);
12
+ return stableValue;
13
+ };
14
+
15
+ export { useSearch };
@@ -0,0 +1,16 @@
1
+ import { UseQueryStateReturn } from "nuqs";
2
+ declare global {
3
+ interface String {
4
+ source?: string;
5
+ toJSON: () => string;
6
+ bindSource: (source: string) => string;
7
+ }
8
+ interface Array<T> {
9
+ source?: string;
10
+ toJSON(): any;
11
+ bindSource(source: string): T[];
12
+ }
13
+ }
14
+ export declare const useSearchValue: (key: string, defaultValue: string) => UseQueryStateReturn<string, string>;
15
+ export declare const useSearchArray: (key: string, defaultValue?: string[], separator?: string) => UseQueryStateReturn<string[], string[]>;
16
+ export declare const getJinraiValue: (key: string, type: string, separator: string, def: any) => string;
@@ -0,0 +1,49 @@
1
+ import { useQueryState, parseAsArrayOf, parseAsString } from 'nuqs';
2
+ import { useMemo } from 'react';
3
+ import { ssr } from '../../server-state/SSR.js';
4
+
5
+ function toJSON() {
6
+ // @ts-ignore
7
+ if (ssr.exportParams)
8
+ return this.source;
9
+ // @ts-ignore
10
+ return this;
11
+ }
12
+ {
13
+ String.prototype.source = undefined;
14
+ String.prototype.toJSON = toJSON;
15
+ String.prototype.bindSource = function (source) {
16
+ const result = new String(this);
17
+ result.source = source;
18
+ return result;
19
+ };
20
+ if (!Array.prototype.toJSON) {
21
+ Array.prototype.toJSON = toJSON;
22
+ }
23
+ if (!Array.prototype.bindSource) {
24
+ Array.prototype.bindSource = function (source) {
25
+ this.source = source;
26
+ return this;
27
+ };
28
+ }
29
+ }
30
+ const useSearchValue = (key, defaultValue) => {
31
+ const [value, setValue] = useQueryState(key, { defaultValue });
32
+ const stableValue = useMemo(() => {
33
+ return ssr.current ? value.bindSource(getJinraiValue(key, "searchString", "", defaultValue)) : value;
34
+ }, [key, value]);
35
+ return [stableValue, setValue];
36
+ };
37
+ const useSearchArray = (key, defaultValue = [], separator = ",") => {
38
+ const stableDefault = useMemo(() => defaultValue, []);
39
+ const [value, setValue] = useQueryState(key, parseAsArrayOf(parseAsString, separator).withDefault(stableDefault));
40
+ const stableValue = useMemo(() => {
41
+ return ssr.current ? value.bindSource(getJinraiValue(key, "searchArray", separator, defaultValue)) : value;
42
+ }, [key, value]);
43
+ return [stableValue, setValue];
44
+ };
45
+ const getJinraiValue = (key, type, separator, def) => {
46
+ return `@JV[[${JSON.stringify({ key, type, separator, def })}]]`;
47
+ };
48
+
49
+ export { getJinraiValue, useSearchArray, useSearchValue };
@@ -0,0 +1,8 @@
1
+ import type { ReactElement, ReactNode } from "react";
2
+ interface CustomProps {
3
+ name: string;
4
+ props: object;
5
+ children: ReactNode;
6
+ }
7
+ export declare const Custom: ({ name, props, children }: CustomProps) => string | ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
8
+ export {};
@@ -0,0 +1,9 @@
1
+ import { ssr } from '../server-state/SSR.js';
2
+
3
+ const Custom = ({ name, props, children }) => {
4
+ if (!ssr.current)
5
+ return children;
6
+ return `<custom>${JSON.stringify({ name, props })}<custom>`;
7
+ };
8
+
9
+ export { Custom };
@@ -0,0 +1 @@
1
+ import{createServer as f}from"vite";import{AsyncLocalStorage as d}from"async_hooks";import{chromium as y}from"playwright";var c=async a=>{let t=await a.evaluate(()=>window.__page_requests),e=await a.locator("#root").innerHTML();return{state:t,root:e}};var m=a=>a.replace(/\r?\n|\r/g," ").replace(/\s+/g," ").replace(/>\s+</g,"><").trim();import{createHash as h}from"node:crypto";var p=class{options;openVar="{{";createVar="}}";createArray="</loopwrapper";createCustom="</custom";deepUp="<loopwrapper";deepUp2="<custom";templates={};constructor(t){this.options=t}parse(t){let e=[];return this.handle(this.options?.normalize?m(t):t,e),e}handle(t,e){let n,r=0,s=0,o=new RegExp("(<loopwrapper(\\s+[^>]*)?>|</loopwrapper>|{{|}}|<custom(\\s+[^>]*)?>|</custom>)","gi");for(;(n=o.exec(t))!==null;){let i=n[0],l=t.substring(s,n.index);if(i.startsWith(this.createArray)){if(r--,r>0)continue;this.createElement(e,l)}else if(i.startsWith(this.deepUp)){if(r++,r>1)continue;this.createElement(e,l)}else if(i.startsWith(this.createCustom)){if(r--,r!=0)continue;this.createCustomElement(e,l)}else if(i.startsWith(this.deepUp2)){if(r++,r>1)continue;this.createElement(e,l)}else if(i==this.createVar){if(r!=0)continue;this.createElement(e,l,!0)}else{if(r!=0)continue;this.createElement(e,l)}s=n.index+i.length}if(s<t.length){let i=t.substring(s);this.createElement(e,i)}}createCustomElement(t,e){let[n,...r]=e.trimStart().split("|");e=r.join("|"),t.push({type:"custom",name:n,props:e})}createElement(t,e,n){if(n)return t.push({type:"value",key:e});if(e.trimStart().startsWith("ArrayDataKey=")){let[r,...s]=e.trimStart().substring(13).split("|");e=s.join("|");let o=[];return this.handle(e,o),t.push({type:"array",data:o,key:r})}e&&t.push({type:"html",content:this.options?.templates?this.createTemplate(e):e})}createTemplate(t){return t in this.templates||(this.templates[t]=h("md5").update(Object.keys(this.templates).length.toString()).digest("hex")),this.templates[t]}};var u=(a,t=!0,e=!0)=>{let n=[],r=new p({normalize:t,templates:e});for(let[s,o]of a.entries()){let i=r.parse(o.root),l=o.mask.replaceAll("/","\\/").replace(/{(.*?)}/,".+?");n.push({id:s,content:i,mask:l,requests:o.input})}return{routes:n,templates:r.templates}};import{writeFile as E}from"fs/promises";var g=new d;function W(){if(process.env.CHILD_JINRAI_DEV_SERVER)return{name:"vite-jinrai-dummy"};process.env.CHILD_JINRAI_DEV_SERVER="true",console.log("create mirror");let a,t,e;return f({server:{port:3012}}).then(async n=>{a=n,await a.listen(),e=a.resolvedUrls?.local[0].slice(0,-1),y.launch({headless:!0,devtools:!1}).then(async r=>{console.log("create context"),t=await r.newContext({userAgent:"____fast-ssr-tool___",locale:"ru-RU"})})}),{name:"vite-jinrai",configureServer(n){n.middlewares.use((r,s,o)=>{if(r.url?.startsWith("/@"))return o();g.run(r.url,()=>{o()})})},async transformIndexHtml(n){let r=g.getStore();if(r&&t){let s=await t.newPage();await s.goto(e+r),await s.waitForLoadState("networkidle");let{root:o}=await c(s),{routes:i}=u([{id:1,input:[],mask:r,root:o}],!0,!1);console.log({routes:i}),E("./routs.json",JSON.stringify(i,null,2));let l=n.replace("<!--app-html-->",o);return s.close(),l}return n},closeWatcher(){console.log("Stop server")}}}export{W as hydration};
package/package.json CHANGED
@@ -1,38 +1,70 @@
1
1
  {
2
2
  "name": "jinrai",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "A powerful library that analyzes your modern web application and automatically generates a perfectly rendered, static snapshot of its pages. Experience unparalleled loading speed and SEO clarity without the complexity of traditional SSR setups. Simply point Jinrai at your SPA and witness divine speed.",
5
5
  "main": "lib/index.ts",
6
6
  "scripts": {
7
7
  "test": "vitest",
8
- "dev": "nodemon --watch './src' --ext 'ts' --exec \"npm run build\"",
9
- "build": "npm run build-config && npm run build-index && npm run build:types",
8
+ "dev": "nodemon --watch './src' --watch './vite' --ext 'ts' --exec \"npm run build\"",
9
+ "build": "npm run build-front && npm run build-config && npm run build-index && npm run build-plugin && npm run build:types && npm run build-plugin-config",
10
10
  "build:types": "tsc",
11
- "build-index": "npx esbuild index.ts --bundle --platform=node --format=esm --outfile=lib/index.js --external:jiti --external:node:* --external:playwright --minify",
12
- "build-config": "npx esbuild src/bin.ts --bundle --platform=node --format=esm --outfile=lib/bin.js --external:jiti --external:node:* --external:playwright --external:prettier --external:vite --minify"
11
+ "build-index": "npx esbuild index.ts --bundle --platform=node --format=esm --outfile=lib/index.js --external:jiti --external:node:* --external:playwright --external:react --minify",
12
+ "build-plugin-config": "npx esbuild config.ts --bundle --platform=node --format=esm --outfile=lib/config/config.js --external:jiti --external:node:* --external:playwright --external:react --minify",
13
+ "build-config": "npx esbuild src/bin/bin.ts --bundle --platform=node --format=esm --outfile=lib/bin/bin.js --external:jiti --external:node:* --external:playwright --external:prettier --external:vite --external:react --minify",
14
+ "build-plugin": "npx esbuild vite/plugin.ts --bundle --platform=node --format=esm --outfile=lib/vite/plugin.js --external:jiti --external:node:* --external:playwright --external:prettier --external:vite --external:react --minify",
15
+ "build-front": "rollup -c rollup.front.config.mjs && tsc -p front.config.json --emitDeclarationOnly"
13
16
  },
14
17
  "keywords": [],
15
18
  "author": "",
16
19
  "license": "ISC",
17
20
  "type": "module",
18
21
  "devDependencies": {
22
+ "@rollup/plugin-typescript": "^11.1.6",
19
23
  "@types/node": "^24.5.2",
20
24
  "@types/ora": "^3.1.0",
21
25
  "@vitest/coverage-v8": "^4.0.8",
22
26
  "dotenv": "^17.2.2",
23
27
  "nodemon": "^3.1.10",
28
+ "rollup": "^4.24.0",
29
+ "tslib": "^2.8.1",
24
30
  "tsx": "^4.20.6",
25
31
  "typescript": "^5.9.2",
26
- "vitest": "^3.2.4"
32
+ "vitest": "^4.0.8"
27
33
  },
28
34
  "bin": {
29
- "jinrai": "lib/bin.js"
35
+ "jinrai": "lib/bin/bin.js"
30
36
  },
31
37
  "dependencies": {
32
38
  "@types/prettier": "^2.7.3",
33
39
  "jiti": "^2.6.0",
40
+ "nuqs": "^2.0.0",
34
41
  "ora": "^9.0.0",
35
42
  "playwright": "^1.55.1",
36
43
  "prettier": "^3.6.2"
44
+ },
45
+ "peerDependencies": {
46
+ "@types/react": "^18.0.0 || ^19.0.0",
47
+ "nuqs": "^2.0.0",
48
+ "react": "^18.0.0 || ^19.0.0",
49
+ "react-dom": "^18.0.0 || ^19.0.0",
50
+ "react-router-dom": "^6.0.0"
51
+ },
52
+ "exports": {
53
+ ".": {
54
+ "types": "./lib/index.d.ts",
55
+ "import": "./lib/index.js"
56
+ },
57
+ "./rrd6": {
58
+ "types": "./lib/src/front/url/adapter/rrd6.d.ts",
59
+ "import": "./lib/src/front/url/adapter/rrd6.js"
60
+ },
61
+ "./rrd7": {
62
+ "types": "./lib/src/front/url/adapter/rrd7.d.ts",
63
+ "import": "./lib/src/front/url/adapter/rrd7.js"
64
+ },
65
+ "./config": {
66
+ "types": "./lib/config/config.d.ts",
67
+ "import": "./lib/config/config.js"
68
+ }
37
69
  }
38
70
  }
package/readme.md CHANGED
@@ -37,3 +37,14 @@ package.json
37
37
  "preview": "vite preview"
38
38
  },
39
39
  ```
40
+
41
+ ## beta
42
+
43
+ hydrationPlugin for vite.config.ts
44
+
45
+ ```ts
46
+ import { hydration } from "jinrai/vite/plugin"
47
+
48
+
49
+ plugins: [react(), hydration() as Plugin],
50
+ ```
@@ -0,0 +1,57 @@
1
+ import path from "node:path"
2
+ import { fileURLToPath } from "node:url"
3
+ import { createRequire } from "node:module"
4
+
5
+ import typescript from "@rollup/plugin-typescript"
6
+ import { defineConfig } from "rollup"
7
+
8
+ const __filename = fileURLToPath(import.meta.url)
9
+ const __dirname = path.dirname(__filename)
10
+
11
+ const require = createRequire(import.meta.url)
12
+ const pkg = require("./package.json")
13
+
14
+ const externalDeps = new Set([
15
+ ...Object.keys(pkg.dependencies ?? {}),
16
+ ...Object.keys(pkg.peerDependencies ?? {})
17
+ ])
18
+
19
+ const entryPoints = [
20
+ path.resolve(__dirname, "index.ts"),
21
+ path.resolve(__dirname, "src/front/url/adapter/rrd6.tsx"),
22
+ path.resolve(__dirname, "src/front/url/adapter/rrd7.tsx"),
23
+ path.resolve(__dirname, "src/front/url/adapter/def.tsx")
24
+ ]
25
+
26
+ const isExternal = (id) => {
27
+ if (id.startsWith(".") || path.isAbsolute(id)) {
28
+ return false
29
+ }
30
+
31
+ for (const dep of externalDeps) {
32
+ if (id === dep || id.startsWith(`${dep}/`)) {
33
+ return true
34
+ }
35
+ }
36
+
37
+ return false
38
+ }
39
+
40
+ export default defineConfig({
41
+ input: entryPoints,
42
+ output: {
43
+ dir: path.resolve(__dirname, "lib"),
44
+ format: "esm",
45
+ preserveModules: true,
46
+ preserveModulesRoot: "."
47
+ },
48
+ external: isExternal,
49
+ plugins: [
50
+ typescript({
51
+ tsconfig: path.resolve(__dirname, "front.config.json"),
52
+ declaration: false,
53
+ sourceMap: false
54
+ })
55
+ ]
56
+ })
57
+
package/src/bin/bin.ts ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runBuild } from "./build/build"
4
+
5
+ const args = process.argv.slice(2)
6
+
7
+ const debug = args.includes("--debug")
8
+ const customConfig = args.find(itm => itm.startsWith("--config="))?.substring(9)
9
+
10
+ runBuild({ debug, customConfig })
@@ -0,0 +1,69 @@
1
+ import { writeFile } from "node:fs/promises"
2
+ import { Config, getUserConfig, IndexProps } from "../config/userConfig"
3
+ import { getRoutesAndTemplates } from "../routes/getRoutes"
4
+ import { getRawPageData } from "../playwright/templates"
5
+ import path from "node:path"
6
+ import { mkdir } from "node:fs/promises"
7
+ import Task from "../ui/task"
8
+ import { normalizeHtmlWhitespace } from "../content/normalizeContent"
9
+ import { vitePreview } from "../server/vitePreview"
10
+
11
+ const indexProps: IndexProps = {
12
+ html: "<!--app-html-->",
13
+ head: "<!--app-head-->",
14
+ }
15
+
16
+ interface buildArgs {
17
+ customConfig?: string
18
+ debug?: boolean
19
+ }
20
+
21
+ export const runBuild = async (options: buildArgs) => {
22
+ const task = new Task()
23
+
24
+ const configName = options.customConfig ?? "jinrai.config"
25
+ task.do("Init")
26
+ const config: Config = await getUserConfig(configName)
27
+ task.success()
28
+
29
+ const [serverUrl, close] = await vitePreview()
30
+
31
+ const pages = await getRawPageData(serverUrl, config.pages, config.test, options.debug)
32
+
33
+ close()
34
+
35
+ const outputcashe = path.join(config.dist ?? "dist", ".cached")
36
+
37
+ task.do("Format")
38
+ const { routes, templates } = getRoutesAndTemplates(pages)
39
+
40
+ task.next(`Export: (${templates.length})`)
41
+ await mkdir(outputcashe, { recursive: true })
42
+
43
+ console.log("dev")
44
+
45
+ const exportConfig = { routes, proxy: config.proxy, meta: config.meta }
46
+
47
+ await writeFile(path.join(outputcashe, "config.json"), JSON.stringify(exportConfig, null, 2))
48
+ // await writeFile(
49
+ // path.join(outputcashe, "index.html"),
50
+ // config.index ? config.index(indexProps) : removeDevScripts(data.indexHtml ?? defaultIndexHtml),
51
+ // )
52
+
53
+ for await (const [template, name] of Object.entries(templates)) {
54
+ await writeFile(path.join(outputcashe, `${name}.html`), template)
55
+ }
56
+
57
+ if (config.test) {
58
+ task.next(`Tests`)
59
+ for await (const page of pages) {
60
+ if (!page.test) {
61
+ continue
62
+ }
63
+
64
+ await writeFile(path.join(outputcashe, `test_${page.id}.html`), normalizeHtmlWhitespace(page.test))
65
+ }
66
+ }
67
+
68
+ task.success()
69
+ }
@@ -11,10 +11,8 @@ export interface Config {
11
11
  url?: string
12
12
  preview?: string
13
13
  pages: string[]
14
- debug?: boolean
15
14
  test?: boolean
16
15
  dist?: string
17
- index?: (props: IndexProps) => string
18
16
  proxy?: Record<string, string>
19
17
  meta?: string
20
18
  }
@@ -0,0 +1,11 @@
1
+ import { Page } from "playwright"
2
+
3
+ export const pageCollector = async (page: Page) => {
4
+ const state = await page.evaluate(() => {
5
+ return (window as any).__page_requests
6
+ })
7
+
8
+ const root = await page.locator("#root").innerHTML()
9
+
10
+ return { state, root }
11
+ }
@@ -1,6 +1,7 @@
1
1
  import { chromium } from "playwright"
2
- import Task from "./ui/task"
2
+ import Task from "../ui/task"
3
3
  import { spinners } from "ora"
4
+ import { pageCollector } from "./pageCollector"
4
5
 
5
6
  export type input = {
6
7
  method: string
@@ -21,33 +22,25 @@ export const getRawPageData = async (
21
22
  pages: string[],
22
23
  test: boolean = false,
23
24
  debug: boolean = false,
24
- ): Promise<{ pages: PageData[]; indexHtml?: string }> => {
25
+ ): Promise<PageData[]> => {
25
26
  const task = new Task()
26
27
  task.next("Router analysis", "yellow", spinners.dotsCircle)
27
28
 
28
29
  const result: PageData[] = []
29
30
 
30
- const browser = await chromium.launch({ headless: !debug, devtools: true })
31
- const test_browser = await chromium.launch({ headless: true })
31
+ const browser = await chromium.launch({ headless: !debug, devtools: true, channel: "chrome" })
32
+ // const test_browser = await chromium.launch({ headless: true, channel: "chrome" })
32
33
 
33
34
  const context = await browser.newContext({
34
- userAgent: "____fast-ssr-tool___",
35
+ userAgent: "____JINRAI_CLIENT____",
36
+ locale: "ru-RU",
35
37
  })
36
38
 
37
- let indexHtml: string | undefined = undefined
38
-
39
39
  for await (const [id, mask] of pages.entries()) {
40
40
  task.next(mask, "yellow", spinners.dotsCircle, 1)
41
41
 
42
42
  const page = await context.newPage()
43
43
  const path = mask.replaceAll("{", "").replaceAll("}", "")
44
- if (!indexHtml) {
45
- page.on("response", async responce => {
46
- if (responce.status() == 200 && responce.url() == url + path) {
47
- indexHtml = await responce.text()
48
- }
49
- })
50
- }
51
44
 
52
45
  await page.goto(url + path)
53
46
 
@@ -55,21 +48,19 @@ export const getRawPageData = async (
55
48
 
56
49
  await page.waitForTimeout(1000)
57
50
 
58
- const input = await page.evaluate(() => {
59
- return (window as any).__page_requests
60
- })
51
+ const { state, root } = await pageCollector(page)
52
+
53
+ // if (debug) console.log({ input })
61
54
 
62
- if (debug) console.log({ input })
63
- const root = await page.locator("#root").innerHTML()
64
55
  let testRoot: string | undefined = undefined
65
56
 
66
- if (test) {
67
- const testPage = await test_browser.newPage()
68
- await testPage.goto(url + path)
69
- await testPage.waitForLoadState("networkidle")
70
- await testPage.waitForTimeout(1000)
71
- testRoot = await page.locator("#root").innerHTML()
72
- }
57
+ // if (test) {
58
+ // const testPage = await test_browser.newPage()
59
+ // await testPage.goto(url + path)
60
+ // await testPage.waitForLoadState("networkidle")
61
+ // await testPage.waitForTimeout(1000)
62
+ // testRoot = await page.locator("#root").innerHTML()
63
+ // }
73
64
 
74
65
  if (debug) {
75
66
  await task.ask("continue?")
@@ -79,7 +70,7 @@ export const getRawPageData = async (
79
70
 
80
71
  result.push({
81
72
  id,
82
- input,
73
+ input: state,
83
74
  mask,
84
75
  root,
85
76
  test: testRoot,
@@ -87,8 +78,8 @@ export const getRawPageData = async (
87
78
  }
88
79
 
89
80
  await browser.close()
90
- await test_browser.close()
81
+ // await test_browser.close()
91
82
 
92
83
  task.success()
93
- return { pages: result, indexHtml }
84
+ return result
94
85
  }
@@ -1,9 +1,9 @@
1
- import { normalizeHtmlWhitespace } from "../content/normolizeContent"
1
+ import { normalizeHtmlWhitespace } from "../content/normalizeContent"
2
2
  import { createHash } from "node:crypto"
3
3
 
4
4
  interface ParserOptions {
5
5
  templates?: boolean
6
- normolize?: boolean
6
+ normalize?: boolean
7
7
  }
8
8
 
9
9
  export type Element = ArrayElement | HtmlElement | ValueElement | CustomElement
@@ -48,7 +48,7 @@ export class Parser {
48
48
 
49
49
  parse(content: string) {
50
50
  const tree: Element[] = []
51
- this.handle(this.options?.normolize ? normalizeHtmlWhitespace(content) : content, tree)
51
+ this.handle(this.options?.normalize ? normalizeHtmlWhitespace(content) : content, tree)
52
52
  return tree
53
53
  }
54
54
 
@@ -1,4 +1,4 @@
1
- import { input, PageData } from "../templates"
1
+ import { input, PageData } from "../playwright/templates"
2
2
  import { Element, Parser } from "./Parser"
3
3
 
4
4
  interface Route {
@@ -8,11 +8,11 @@ interface Route {
8
8
  content: Element[]
9
9
  }
10
10
 
11
- export const getRoutesAndTemplates = (templates: PageData[]) => {
11
+ export const getRoutesAndTemplates = (pages: PageData[], normalize: boolean = true, templates: boolean = true) => {
12
12
  const routes: Route[] = []
13
- const parser = new Parser({ normolize: true, templates: true })
13
+ const parser = new Parser({ normalize, templates })
14
14
 
15
- for (const [id, template] of templates.entries()) {
15
+ for (const [id, template] of pages.entries()) {
16
16
  const content = parser.parse(template.root)
17
17
 
18
18
  const mask = template.mask.replaceAll("/", "\\/").replace(/{(.*?)}/, ".+?")
@@ -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
+ }