jinrai 1.0.3 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.vscode/launch.json +21 -0
  2. package/config.ts +1 -0
  3. package/front.config.json +24 -0
  4. package/index.ts +6 -1
  5. package/lib/bin/bin.js +12 -0
  6. package/lib/config/config.d.ts +1 -0
  7. package/lib/config/config.js +1 -0
  8. package/lib/config/src/bin/config/userConfig.d.ts +14 -0
  9. package/lib/index.d.ts +6 -1
  10. package/lib/index.js +6 -7
  11. package/lib/src/front/server-state/DataProxy.d.ts +8 -0
  12. package/lib/src/front/server-state/DataProxy.js +104 -0
  13. package/lib/src/front/server-state/SSR.d.ts +4 -0
  14. package/lib/src/front/server-state/SSR.js +6 -0
  15. package/lib/src/front/server-state/real.d.ts +1 -0
  16. package/lib/src/front/server-state/real.js +36 -0
  17. package/lib/src/front/server-state/serverStates.d.ts +5 -0
  18. package/lib/src/front/server-state/serverStates.js +29 -0
  19. package/lib/src/front/server-state/useServerState.d.ts +10 -0
  20. package/lib/src/front/server-state/useServerState.js +28 -0
  21. package/lib/src/front/url/JinraiContext.d.ts +6 -0
  22. package/lib/src/front/url/JinraiContext.js +8 -0
  23. package/lib/src/front/url/adapter/def.d.ts +2 -0
  24. package/lib/src/front/url/adapter/def.js +9 -0
  25. package/lib/src/front/url/adapter/rrd6.d.ts +2 -0
  26. package/lib/src/front/url/adapter/rrd6.js +12 -0
  27. package/lib/src/front/url/adapter/rrd7.d.ts +2 -0
  28. package/lib/src/front/url/adapter/rrd7.js +11 -0
  29. package/lib/src/front/url/params/useParamsIndex.d.ts +1 -0
  30. package/lib/src/front/url/params/useParamsIndex.js +15 -0
  31. package/lib/src/front/url/search/useSearch.d.ts +1 -0
  32. package/lib/src/front/url/search/useSearch.js +15 -0
  33. package/lib/src/front/url/search/useSearchValue.d.ts +16 -0
  34. package/lib/src/front/url/search/useSearchValue.js +49 -0
  35. package/lib/src/front/wrapper/Custom.d.ts +8 -0
  36. package/lib/src/front/wrapper/Custom.js +9 -0
  37. package/lib/vite/plugin.js +1 -0
  38. package/package.json +42 -7
  39. package/readme.md +34 -8
  40. package/rollup.front.config.mjs +57 -0
  41. package/src/bin/bin.ts +10 -0
  42. package/src/bin/build/build.ts +69 -0
  43. package/src/{config → bin/config}/defaultIndexHtml.ts +2 -2
  44. package/src/{config → bin/config}/userConfig.ts +11 -3
  45. package/src/bin/content/normalizeContent.ts +7 -0
  46. package/src/bin/playwright/pageCollector.ts +11 -0
  47. package/src/{templates.ts → bin/playwright/templates.ts} +28 -21
  48. package/src/bin/routes/Parser.ts +148 -0
  49. package/src/{routes → bin/routes}/getRoutes.ts +9 -6
  50. package/src/bin/routes/replaceDevScripts.ts +16 -0
  51. package/src/bin/server/vitePreview.ts +13 -0
  52. package/src/front/server-state/DataProxy.ts +120 -0
  53. package/src/front/server-state/SSR.ts +4 -0
  54. package/src/front/server-state/real.ts +41 -0
  55. package/src/front/server-state/serverStates.ts +36 -0
  56. package/src/front/server-state/useServerState.ts +44 -0
  57. package/src/front/url/JinraiContext.tsx +10 -0
  58. package/src/front/url/adapter/def.tsx +10 -0
  59. package/src/front/url/adapter/rrd6.tsx +16 -0
  60. package/src/front/url/adapter/rrd7.tsx +15 -0
  61. package/src/front/url/params/useParamsIndex.ts +16 -0
  62. package/src/front/url/search/useSearch.ts +15 -0
  63. package/src/front/url/search/useSearchValue.ts +72 -0
  64. package/src/front/wrapper/Custom.tsx +14 -0
  65. package/tests/content/1.html +12 -0
  66. package/tests/content/1_result.json +54 -0
  67. package/tests/content/2.html +16 -0
  68. package/tests/content/2_result.json +28 -0
  69. package/tests/content/3.html +21 -0
  70. package/tests/content/3_result.json +39 -0
  71. package/tests/content/4.html +4 -0
  72. package/tests/content/4_result.json +9 -0
  73. package/tests/content/custom.html +5 -0
  74. package/tests/content/custom.json +5 -0
  75. package/tests/content/index.html +543 -0
  76. package/tests/content/index.json +49 -0
  77. package/tests/content/index_with_templates.json +31 -0
  78. package/tests/content/templates.json +13 -0
  79. package/tests/custom.test.ts +16 -0
  80. package/tests/parse.test.ts +52 -0
  81. package/tsconfig.json +2 -2
  82. package/vite/plugin.ts +83 -0
  83. package/vitest.config.ts +14 -0
  84. package/lib/bin.js +0 -3351
  85. package/lib/generate.js +0 -26
  86. package/lib/src/config/userConfig.d.ts +0 -7
  87. package/src/bin.ts +0 -46
  88. package/src/routes/parser.ts +0 -58
  89. package/src/routes/splitByTag.ts +0 -38
  90. package/test/fld.config.ts +0 -13
  91. package/test/jinrai.config.ts +0 -8
  92. /package/lib/{src → config/src/bin}/config/define.d.ts +0 -0
  93. /package/src/{config → bin/config}/define.ts +0 -0
  94. /package/src/{types → bin/types}/shims.d.ts +0 -0
  95. /package/src/{ui → bin/ui}/task.tsx +0 -0
@@ -0,0 +1 @@
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,26 +1,35 @@
1
1
  {
2
2
  "name": "jinrai",
3
- "version": "1.0.3",
3
+ "version": "1.0.6",
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
- "test": "node ./lib/bin",
8
- "test2": "node --import tsx src/bin.ts",
9
- "build": "npm run build-config && npm run build-index && npm run build:types",
7
+ "test": "vitest",
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",
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"
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",
25
+ "@vitest/coverage-v8": "^4.0.8",
21
26
  "dotenv": "^17.2.2",
27
+ "nodemon": "^3.1.10",
28
+ "rollup": "^4.24.0",
29
+ "tslib": "^2.8.1",
22
30
  "tsx": "^4.20.6",
23
- "typescript": "^5.9.2"
31
+ "typescript": "^5.9.2",
32
+ "vitest": "^4.0.8"
24
33
  },
25
34
  "bin": {
26
35
  "jinrai": "lib/bin.js"
@@ -28,8 +37,34 @@
28
37
  "dependencies": {
29
38
  "@types/prettier": "^2.7.3",
30
39
  "jiti": "^2.6.0",
40
+ "nuqs": "^2.0.0",
31
41
  "ora": "^9.0.0",
32
42
  "playwright": "^1.55.1",
33
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
+ }
34
69
  }
35
70
  }
package/readme.md CHANGED
@@ -7,18 +7,44 @@ Simply point Jinrai at your SPA and witness divine speed.
7
7
 
8
8
  ---
9
9
 
10
- .ssr.config.ts
10
+ install
11
+
12
+ ```
13
+ npm i -D jinrai
14
+ ```
15
+
16
+ ---
17
+
18
+ .jinrai.config.ts
11
19
 
12
20
  ```ts
13
21
  import { defineConfig } from "jinrai"
14
22
 
15
23
  export default defineConfig({
16
- url: "<spa-url>",
17
- dev: true,
18
- pages: ["/", "/products", "/products/{phones}", "/product/{iphone17pro}", "/{iphone17pro_42}"],
19
- export: {
20
- outDir: "export",
21
- index: "index.html",
22
- },
24
+ pages: ["", "products/pro", "products/teams", "docs"],
23
25
  })
24
26
  ```
27
+
28
+ add to build
29
+
30
+ package.json
31
+
32
+ ```
33
+ "scripts": {
34
+ "dev": "vite",
35
+ "build": "tsc -b && vite build && jinrai",
36
+ "lint": "eslint .",
37
+ "preview": "vite preview"
38
+ },
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
+ }
@@ -1,11 +1,11 @@
1
1
  export const defaultIndexHtml = `
2
2
  <!doctype html>
3
- <html lang="eng">
3
+ <html lang="en">
4
4
  <head>
5
5
  <meta charset="UTF-8" />
6
6
  <link rel="icon" type="image/svg+xml" href="/images/favicon.svg" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
- <title>Jinrai app</title>
8
+ <title>App</title>
9
9
  <!--app-head-->
10
10
  </head>
11
11
  <body>
@@ -2,11 +2,19 @@ import { createJiti } from "jiti"
2
2
  import { pathToFileURL } from "url"
3
3
  import { resolve } from "path"
4
4
 
5
+ export interface IndexProps {
6
+ html: string
7
+ head: string
8
+ }
9
+
5
10
  export interface Config {
6
- url: string
11
+ url?: string
12
+ preview?: string
7
13
  pages: string[]
8
- debug?: boolean
9
- outDir: string
14
+ test?: boolean
15
+ dist?: string
16
+ proxy?: Record<string, string>
17
+ meta?: string
10
18
  }
11
19
 
12
20
  export const getUserConfig = async (configName: string): Promise<Config> => {
@@ -0,0 +1,7 @@
1
+ export const normalizeHtmlWhitespace = (html: string): string => {
2
+ return html
3
+ .replace(/\r?\n|\r/g, " ")
4
+ .replace(/\s+/g, " ")
5
+ .replace(/>\s+</g, "><")
6
+ .trim()
7
+ }
@@ -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
@@ -9,41 +10,37 @@ export type input = {
9
10
  }
10
11
 
11
12
  export interface PageData {
13
+ id: number
12
14
  mask: string
13
15
  root: string
14
16
  input: input[]
17
+ test?: string
15
18
  }
16
19
 
17
20
  export const getRawPageData = async (
18
21
  url: string,
19
22
  pages: string[],
23
+ test: boolean = false,
20
24
  debug: boolean = false,
21
- ): Promise<{ pages: PageData[]; indexHtml?: string }> => {
25
+ ): Promise<PageData[]> => {
22
26
  const task = new Task()
23
- task.next("Parsing pages", "yellow", spinners.dotsCircle)
27
+ task.next("Router analysis", "yellow", spinners.dotsCircle)
24
28
 
25
29
  const result: PageData[] = []
26
30
 
27
31
  const browser = await chromium.launch({ headless: !debug, devtools: true })
32
+ const test_browser = await chromium.launch({ headless: true })
33
+
28
34
  const context = await browser.newContext({
29
35
  userAgent: "____fast-ssr-tool___",
36
+ locale: "ru-RU",
30
37
  })
31
38
 
32
- // let date: any[] = [];
33
- let indexHtml: string | undefined = undefined
34
-
35
- for await (const mask of pages) {
39
+ for await (const [id, mask] of pages.entries()) {
36
40
  task.next(mask, "yellow", spinners.dotsCircle, 1)
37
41
 
38
42
  const page = await context.newPage()
39
43
  const path = mask.replaceAll("{", "").replaceAll("}", "")
40
- if (!indexHtml) {
41
- page.on("response", async responce => {
42
- if (responce.status() == 200 && responce.url() == url + path) {
43
- indexHtml = await responce.text()
44
- }
45
- })
46
- }
47
44
 
48
45
  await page.goto(url + path)
49
46
 
@@ -51,12 +48,19 @@ export const getRawPageData = async (
51
48
 
52
49
  await page.waitForTimeout(1000)
53
50
 
54
- const input = await page.evaluate(() => {
55
- return (window as any).__page_requests
56
- })
51
+ const { state, root } = await pageCollector(page)
52
+
53
+ // if (debug) console.log({ input })
54
+
55
+ let testRoot: string | undefined = undefined
57
56
 
58
- if (debug) console.log({ input })
59
- const root = await page.locator("#root").innerHTML()
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
+ // }
60
64
 
61
65
  if (debug) {
62
66
  await task.ask("continue?")
@@ -65,14 +69,17 @@ export const getRawPageData = async (
65
69
  page.close()
66
70
 
67
71
  result.push({
68
- input,
72
+ id,
73
+ input: state,
69
74
  mask,
70
75
  root,
76
+ test: testRoot,
71
77
  })
72
78
  }
73
79
 
74
80
  await browser.close()
81
+ await test_browser.close()
75
82
 
76
83
  task.success()
77
- return { pages: result, indexHtml }
84
+ return result
78
85
  }