jinrai 1.0.3 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/launch.json +21 -0
- package/config.ts +1 -0
- package/front.config.json +24 -0
- package/index.ts +6 -1
- package/lib/bin/bin.js +12 -0
- package/lib/config/config.d.ts +1 -0
- package/lib/config/config.js +1 -0
- package/lib/config/src/bin/config/userConfig.d.ts +14 -0
- package/lib/index.d.ts +6 -1
- package/lib/index.js +6 -7
- package/lib/src/front/server-state/DataProxy.d.ts +8 -0
- package/lib/src/front/server-state/DataProxy.js +104 -0
- package/lib/src/front/server-state/SSR.d.ts +4 -0
- package/lib/src/front/server-state/SSR.js +6 -0
- package/lib/src/front/server-state/real.d.ts +1 -0
- package/lib/src/front/server-state/real.js +36 -0
- package/lib/src/front/server-state/serverStates.d.ts +5 -0
- package/lib/src/front/server-state/serverStates.js +29 -0
- package/lib/src/front/server-state/useServerState.d.ts +10 -0
- package/lib/src/front/server-state/useServerState.js +28 -0
- package/lib/src/front/url/JinraiContext.d.ts +6 -0
- package/lib/src/front/url/JinraiContext.js +8 -0
- package/lib/src/front/url/adapter/def.d.ts +2 -0
- package/lib/src/front/url/adapter/def.js +9 -0
- package/lib/src/front/url/adapter/rrd6.d.ts +2 -0
- package/lib/src/front/url/adapter/rrd6.js +12 -0
- package/lib/src/front/url/adapter/rrd7.d.ts +2 -0
- package/lib/src/front/url/adapter/rrd7.js +11 -0
- package/lib/src/front/url/params/useParamsIndex.d.ts +1 -0
- package/lib/src/front/url/params/useParamsIndex.js +15 -0
- package/lib/src/front/url/search/useSearch.d.ts +1 -0
- package/lib/src/front/url/search/useSearch.js +15 -0
- package/lib/src/front/url/search/useSearchValue.d.ts +16 -0
- package/lib/src/front/url/search/useSearchValue.js +49 -0
- package/lib/src/front/wrapper/Custom.d.ts +8 -0
- package/lib/src/front/wrapper/Custom.js +9 -0
- package/lib/vite/plugin.js +1 -0
- package/package.json +42 -7
- package/readme.md +34 -8
- package/rollup.front.config.mjs +57 -0
- package/src/bin/bin.ts +10 -0
- package/src/bin/build/build.ts +69 -0
- package/src/{config → bin/config}/defaultIndexHtml.ts +2 -2
- package/src/{config → bin/config}/userConfig.ts +11 -3
- package/src/bin/content/normalizeContent.ts +7 -0
- package/src/bin/playwright/pageCollector.ts +11 -0
- package/src/{templates.ts → bin/playwright/templates.ts} +28 -21
- package/src/bin/routes/Parser.ts +148 -0
- package/src/{routes → bin/routes}/getRoutes.ts +9 -6
- package/src/bin/routes/replaceDevScripts.ts +16 -0
- package/src/bin/server/vitePreview.ts +13 -0
- package/src/front/server-state/DataProxy.ts +120 -0
- package/src/front/server-state/SSR.ts +4 -0
- package/src/front/server-state/real.ts +41 -0
- package/src/front/server-state/serverStates.ts +36 -0
- package/src/front/server-state/useServerState.ts +44 -0
- package/src/front/url/JinraiContext.tsx +10 -0
- package/src/front/url/adapter/def.tsx +10 -0
- package/src/front/url/adapter/rrd6.tsx +16 -0
- package/src/front/url/adapter/rrd7.tsx +15 -0
- package/src/front/url/params/useParamsIndex.ts +16 -0
- package/src/front/url/search/useSearch.ts +15 -0
- package/src/front/url/search/useSearchValue.ts +72 -0
- package/src/front/wrapper/Custom.tsx +14 -0
- package/tests/content/1.html +12 -0
- package/tests/content/1_result.json +54 -0
- package/tests/content/2.html +16 -0
- package/tests/content/2_result.json +28 -0
- package/tests/content/3.html +21 -0
- package/tests/content/3_result.json +39 -0
- package/tests/content/4.html +4 -0
- package/tests/content/4_result.json +9 -0
- package/tests/content/custom.html +5 -0
- package/tests/content/custom.json +5 -0
- package/tests/content/index.html +543 -0
- package/tests/content/index.json +49 -0
- package/tests/content/index_with_templates.json +31 -0
- package/tests/content/templates.json +13 -0
- package/tests/custom.test.ts +16 -0
- package/tests/parse.test.ts +52 -0
- package/tsconfig.json +2 -2
- package/vite/plugin.ts +83 -0
- package/vitest.config.ts +14 -0
- package/lib/bin.js +0 -3351
- package/lib/generate.js +0 -26
- package/lib/src/config/userConfig.d.ts +0 -7
- package/src/bin.ts +0 -46
- package/src/routes/parser.ts +0 -58
- package/src/routes/splitByTag.ts +0 -38
- package/test/fld.config.ts +0 -13
- package/test/jinrai.config.ts +0 -8
- /package/lib/{src → config/src/bin}/config/define.d.ts +0 -0
- /package/src/{config → bin/config}/define.ts +0 -0
- /package/src/{types → bin/types}/shims.d.ts +0 -0
- /package/src/{ui → bin/ui}/task.tsx +0 -0
|
@@ -0,0 +1 @@
|
|
|
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 @@
|
|
|
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
|
+
"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": "
|
|
8
|
-
"
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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="
|
|
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>
|
|
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
|
|
11
|
+
url?: string
|
|
12
|
+
preview?: string
|
|
7
13
|
pages: string[]
|
|
8
|
-
|
|
9
|
-
|
|
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,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 "
|
|
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<
|
|
25
|
+
): Promise<PageData[]> => {
|
|
22
26
|
const task = new Task()
|
|
23
|
-
task.next("
|
|
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
|
-
|
|
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
|
|
55
|
-
|
|
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 (
|
|
59
|
-
const
|
|
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
|
-
|
|
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
|
|
84
|
+
return result
|
|
78
85
|
}
|