jinrai 1.0.3 → 1.0.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.
- package/.vscode/launch.json +21 -0
- package/lib/bin.js +11 -3350
- package/lib/index.js +1 -7
- package/lib/src/config/userConfig.d.ts +11 -2
- package/package.json +9 -6
- package/readme.md +23 -8
- package/src/bin.ts +43 -23
- package/src/config/defaultIndexHtml.ts +2 -2
- package/src/config/userConfig.ts +12 -2
- package/src/content/normolizeContent.ts +7 -0
- package/src/routes/Parser.ts +148 -0
- package/src/routes/getRoutes.ts +7 -4
- package/src/routes/replaceDevScripts.ts +16 -0
- package/src/server/vitePreview.ts +13 -0
- package/src/templates.ts +19 -3
- 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/vitest.config.ts +14 -0
- 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/index.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
export interface IndexProps {
|
|
2
|
+
html: string;
|
|
3
|
+
head: string;
|
|
4
|
+
}
|
|
1
5
|
export interface Config {
|
|
2
|
-
url
|
|
6
|
+
url?: string;
|
|
7
|
+
preview?: string;
|
|
3
8
|
pages: string[];
|
|
4
9
|
debug?: boolean;
|
|
5
|
-
|
|
10
|
+
test?: boolean;
|
|
11
|
+
dist?: string;
|
|
12
|
+
index?: (props: IndexProps) => string;
|
|
13
|
+
proxy?: Record<string, string>;
|
|
14
|
+
meta?: string;
|
|
6
15
|
}
|
|
7
16
|
export declare const getUserConfig: (configName: string) => Promise<Config>;
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jinrai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
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
|
-
"
|
|
7
|
+
"test": "vitest",
|
|
8
|
+
"dev": "nodemon --watch './src' --ext 'ts' --exec \"npm run build\"",
|
|
9
9
|
"build": "npm run build-config && npm run build-index && npm run build:types",
|
|
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 --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"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [],
|
|
15
15
|
"author": "",
|
|
@@ -18,9 +18,12 @@
|
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/node": "^24.5.2",
|
|
20
20
|
"@types/ora": "^3.1.0",
|
|
21
|
+
"@vitest/coverage-v8": "^4.0.8",
|
|
21
22
|
"dotenv": "^17.2.2",
|
|
23
|
+
"nodemon": "^3.1.10",
|
|
22
24
|
"tsx": "^4.20.6",
|
|
23
|
-
"typescript": "^5.9.2"
|
|
25
|
+
"typescript": "^5.9.2",
|
|
26
|
+
"vitest": "^3.2.4"
|
|
24
27
|
},
|
|
25
28
|
"bin": {
|
|
26
29
|
"jinrai": "lib/bin.js"
|
package/readme.md
CHANGED
|
@@ -7,18 +7,33 @@ 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
|
+
```
|
package/src/bin.ts
CHANGED
|
@@ -1,46 +1,66 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { writeFile } from "node:fs/promises"
|
|
4
|
-
import { Config, getUserConfig } from "./config/userConfig"
|
|
4
|
+
import { Config, getUserConfig, IndexProps } from "./config/userConfig"
|
|
5
5
|
import { getRoutesAndTemplates } from "./routes/getRoutes"
|
|
6
6
|
import { getRawPageData } from "./templates"
|
|
7
7
|
import path from "node:path"
|
|
8
8
|
import { mkdir } from "node:fs/promises"
|
|
9
|
-
import prettier from "prettier"
|
|
10
9
|
import Task from "./ui/task"
|
|
11
10
|
import { defaultIndexHtml } from "./config/defaultIndexHtml"
|
|
11
|
+
import { removeDevScripts } from "./routes/replaceDevScripts"
|
|
12
|
+
import { normalizeHtmlWhitespace } from "./content/normolizeContent"
|
|
13
|
+
import { vitePreview } from "./server/vitePreview"
|
|
14
|
+
|
|
15
|
+
const indexProps: IndexProps = {
|
|
16
|
+
html: "<!--app-html-->",
|
|
17
|
+
head: "<!--app-head-->",
|
|
18
|
+
}
|
|
12
19
|
|
|
13
20
|
const task = new Task()
|
|
14
21
|
|
|
15
22
|
const configName = process.argv[2] ? process.argv[2] : "jinrai.config"
|
|
16
|
-
task.do("Init
|
|
23
|
+
task.do("Init")
|
|
17
24
|
const config: Config = await getUserConfig(configName)
|
|
18
25
|
task.success()
|
|
19
26
|
|
|
20
|
-
const
|
|
27
|
+
const [serverUrl, close] = await vitePreview()
|
|
28
|
+
|
|
29
|
+
const data = await getRawPageData(serverUrl, config.pages, config.test, config.debug)
|
|
30
|
+
|
|
31
|
+
close()
|
|
32
|
+
|
|
33
|
+
const outputcashe = path.join(config.dist ?? "dist", ".cached")
|
|
21
34
|
|
|
22
35
|
task.do("Format")
|
|
23
36
|
const { routes, templates } = getRoutesAndTemplates(data.pages)
|
|
24
37
|
|
|
25
|
-
task.next(`Export:
|
|
26
|
-
await mkdir(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
task.next(`Export: (${templates.length})`)
|
|
39
|
+
await mkdir(outputcashe, { recursive: true })
|
|
40
|
+
|
|
41
|
+
console.log("dev")
|
|
42
|
+
|
|
43
|
+
const exportConfig = { routes, proxy: config.proxy, meta: config.meta }
|
|
44
|
+
|
|
45
|
+
await writeFile(path.join(outputcashe, "config.json"), JSON.stringify(exportConfig, null, 2))
|
|
46
|
+
// await writeFile(
|
|
47
|
+
// path.join(outputcashe, "index.html"),
|
|
48
|
+
// config.index ? config.index(indexProps) : removeDevScripts(data.indexHtml ?? defaultIndexHtml),
|
|
49
|
+
// )
|
|
50
|
+
|
|
51
|
+
for await (const [template, name] of Object.entries(templates)) {
|
|
52
|
+
await writeFile(path.join(outputcashe, `${name}.html`), template)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (config.test) {
|
|
56
|
+
task.next(`Tests`)
|
|
57
|
+
for await (const page of data.pages) {
|
|
58
|
+
if (!page.test) {
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
await writeFile(path.join(outputcashe, `test_${page.id}.html`), normalizeHtmlWhitespace(page.test))
|
|
63
|
+
}
|
|
44
64
|
}
|
|
45
65
|
|
|
46
66
|
task.success()
|
|
@@ -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>
|
package/src/config/userConfig.ts
CHANGED
|
@@ -2,11 +2,21 @@ 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
14
|
debug?: boolean
|
|
9
|
-
|
|
15
|
+
test?: boolean
|
|
16
|
+
dist?: string
|
|
17
|
+
index?: (props: IndexProps) => string
|
|
18
|
+
proxy?: Record<string, string>
|
|
19
|
+
meta?: string
|
|
10
20
|
}
|
|
11
21
|
|
|
12
22
|
export const getUserConfig = async (configName: string): Promise<Config> => {
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { normalizeHtmlWhitespace } from "../content/normolizeContent"
|
|
2
|
+
import { createHash } from "node:crypto"
|
|
3
|
+
|
|
4
|
+
interface ParserOptions {
|
|
5
|
+
templates?: boolean
|
|
6
|
+
normolize?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type Element = ArrayElement | HtmlElement | ValueElement | CustomElement
|
|
10
|
+
|
|
11
|
+
interface ArrayElement {
|
|
12
|
+
type: "array"
|
|
13
|
+
key: string
|
|
14
|
+
data: Element[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface HtmlElement {
|
|
18
|
+
type: "html"
|
|
19
|
+
content: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ValueElement {
|
|
23
|
+
type: "value"
|
|
24
|
+
key: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface CustomElement {
|
|
28
|
+
type: "custom"
|
|
29
|
+
name: string
|
|
30
|
+
props: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class Parser {
|
|
34
|
+
options?: ParserOptions
|
|
35
|
+
|
|
36
|
+
openVar = "{{"
|
|
37
|
+
createVar = "}}"
|
|
38
|
+
createArray = "</loopwrapper"
|
|
39
|
+
createCustom = "</custom"
|
|
40
|
+
deepUp = "<loopwrapper"
|
|
41
|
+
deepUp2 = "<custom"
|
|
42
|
+
|
|
43
|
+
templates: Record<string, string> = {}
|
|
44
|
+
|
|
45
|
+
constructor(options?: ParserOptions) {
|
|
46
|
+
this.options = options
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
parse(content: string) {
|
|
50
|
+
const tree: Element[] = []
|
|
51
|
+
this.handle(this.options?.normolize ? normalizeHtmlWhitespace(content) : content, tree)
|
|
52
|
+
return tree
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
handle(content: string, tree: Element[]) {
|
|
56
|
+
let match
|
|
57
|
+
let deep = 0
|
|
58
|
+
let lastIndex = 0
|
|
59
|
+
|
|
60
|
+
const tagPattern = new RegExp(
|
|
61
|
+
`(<loopwrapper(\\s+[^>]*)?>|</loopwrapper>|\{\{|\}\}|<custom(\\s+[^>]*)?>|</custom>)`,
|
|
62
|
+
"gi",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
while ((match = tagPattern.exec(content)) !== null) {
|
|
66
|
+
const currentTag = match[0]
|
|
67
|
+
const value = content.substring(lastIndex, match.index)
|
|
68
|
+
|
|
69
|
+
if (currentTag.startsWith(this.createArray)) {
|
|
70
|
+
deep--
|
|
71
|
+
if (deep > 0) continue
|
|
72
|
+
this.createElement(tree, value)
|
|
73
|
+
} else if (currentTag.startsWith(this.deepUp)) {
|
|
74
|
+
deep++
|
|
75
|
+
if (deep > 1) continue
|
|
76
|
+
this.createElement(tree, value)
|
|
77
|
+
} else if (currentTag.startsWith(this.createCustom)) {
|
|
78
|
+
deep--
|
|
79
|
+
if (deep != 0) continue
|
|
80
|
+
this.createCustomElement(tree, value)
|
|
81
|
+
} else if (currentTag.startsWith(this.deepUp2)) {
|
|
82
|
+
deep++
|
|
83
|
+
if (deep > 1) continue
|
|
84
|
+
this.createElement(tree, value)
|
|
85
|
+
} else if (currentTag == this.createVar) {
|
|
86
|
+
if (deep != 0) continue
|
|
87
|
+
this.createElement(tree, value, true)
|
|
88
|
+
} else {
|
|
89
|
+
if (deep != 0) continue
|
|
90
|
+
this.createElement(tree, value)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
lastIndex = match.index + currentTag.length
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (lastIndex < content.length) {
|
|
97
|
+
const value = content.substring(lastIndex)
|
|
98
|
+
this.createElement(tree, value)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
createCustomElement(parent: Element[], value: string) {
|
|
103
|
+
const [name, ...props] = value.trimStart().split("|")
|
|
104
|
+
value = props.join("|")
|
|
105
|
+
|
|
106
|
+
parent.push({
|
|
107
|
+
type: "custom",
|
|
108
|
+
name,
|
|
109
|
+
props: value,
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
createElement(parent: Element[], value: string, isVarible?: boolean) {
|
|
114
|
+
if (isVarible)
|
|
115
|
+
return parent.push({
|
|
116
|
+
type: "value",
|
|
117
|
+
key: value,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
if (value.trimStart().startsWith("ArrayDataKey=")) {
|
|
121
|
+
const [key, ...val] = value.trimStart().substring(13).split("|")
|
|
122
|
+
value = val.join("|")
|
|
123
|
+
|
|
124
|
+
const children: Element[] = []
|
|
125
|
+
this.handle(value, children)
|
|
126
|
+
|
|
127
|
+
return parent.push({
|
|
128
|
+
type: "array",
|
|
129
|
+
data: children,
|
|
130
|
+
key,
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (value)
|
|
135
|
+
parent.push({
|
|
136
|
+
type: "html",
|
|
137
|
+
content: this.options?.templates ? this.createTemplate(value) : value,
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
createTemplate(html: string): string {
|
|
142
|
+
if (!(html in this.templates)) {
|
|
143
|
+
this.templates[html] = createHash("md5").update(Object.keys(this.templates).length.toString()).digest("hex")
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return this.templates[html]
|
|
147
|
+
}
|
|
148
|
+
}
|
package/src/routes/getRoutes.ts
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { input, PageData } from "../templates"
|
|
2
|
-
import {
|
|
2
|
+
import { Element, Parser } from "./Parser"
|
|
3
3
|
|
|
4
4
|
interface Route {
|
|
5
|
+
id: number
|
|
5
6
|
mask: string
|
|
6
7
|
requests: input[]
|
|
7
|
-
content:
|
|
8
|
+
content: Element[]
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export const getRoutesAndTemplates = (templates: PageData[]) => {
|
|
11
12
|
const routes: Route[] = []
|
|
12
|
-
const parser = new Parser()
|
|
13
|
+
const parser = new Parser({ normolize: true, templates: true })
|
|
13
14
|
|
|
14
|
-
for (const template of templates) {
|
|
15
|
+
for (const [id, template] of templates.entries()) {
|
|
15
16
|
const content = parser.parse(template.root)
|
|
17
|
+
|
|
16
18
|
const mask = template.mask.replaceAll("/", "\\/").replace(/{(.*?)}/, ".+?")
|
|
17
19
|
|
|
18
20
|
routes.push({
|
|
21
|
+
id,
|
|
19
22
|
content,
|
|
20
23
|
mask,
|
|
21
24
|
requests: template.input,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const removeDevScripts = (indexHtml: string) => {
|
|
2
|
+
const parts = [
|
|
3
|
+
'<script type="module">import { injectIntoGlobalHook } from "/@react-refresh";',
|
|
4
|
+
"injectIntoGlobalHook(window);",
|
|
5
|
+
"window.$RefreshReg$ = () => {};",
|
|
6
|
+
"window.$RefreshSig$ = () => (type) => type;</script>",
|
|
7
|
+
'<script type="module" src="/@vite/client"></script>',
|
|
8
|
+
'<script type="module" src="/src/main.tsx"></script>',
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
parts.map(remove => {
|
|
12
|
+
indexHtml = indexHtml.replace(`${remove}\n`, "")
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
return indexHtml
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { preview } from "vite"
|
|
2
|
+
|
|
3
|
+
export const vitePreview = async () => {
|
|
4
|
+
const previewServer = await preview({
|
|
5
|
+
preview: {
|
|
6
|
+
port: 8084,
|
|
7
|
+
},
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
if (!previewServer.resolvedUrls?.local?.length) throw new Error("vite is not defined")
|
|
11
|
+
|
|
12
|
+
return [previewServer.resolvedUrls?.local[0].slice(0, -1), () => previewServer.close()] as [string, () => void]
|
|
13
|
+
}
|
package/src/templates.ts
CHANGED
|
@@ -9,30 +9,34 @@ export type input = {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export interface PageData {
|
|
12
|
+
id: number
|
|
12
13
|
mask: string
|
|
13
14
|
root: string
|
|
14
15
|
input: input[]
|
|
16
|
+
test?: string
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export const getRawPageData = async (
|
|
18
20
|
url: string,
|
|
19
21
|
pages: string[],
|
|
22
|
+
test: boolean = false,
|
|
20
23
|
debug: boolean = false,
|
|
21
24
|
): Promise<{ pages: PageData[]; indexHtml?: string }> => {
|
|
22
25
|
const task = new Task()
|
|
23
|
-
task.next("
|
|
26
|
+
task.next("Router analysis", "yellow", spinners.dotsCircle)
|
|
24
27
|
|
|
25
28
|
const result: PageData[] = []
|
|
26
29
|
|
|
27
30
|
const browser = await chromium.launch({ headless: !debug, devtools: true })
|
|
31
|
+
const test_browser = await chromium.launch({ headless: true })
|
|
32
|
+
|
|
28
33
|
const context = await browser.newContext({
|
|
29
34
|
userAgent: "____fast-ssr-tool___",
|
|
30
35
|
})
|
|
31
36
|
|
|
32
|
-
// let date: any[] = [];
|
|
33
37
|
let indexHtml: string | undefined = undefined
|
|
34
38
|
|
|
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()
|
|
@@ -57,6 +61,15 @@ export const getRawPageData = async (
|
|
|
57
61
|
|
|
58
62
|
if (debug) console.log({ input })
|
|
59
63
|
const root = await page.locator("#root").innerHTML()
|
|
64
|
+
let testRoot: string | undefined = undefined
|
|
65
|
+
|
|
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
|
+
}
|
|
60
73
|
|
|
61
74
|
if (debug) {
|
|
62
75
|
await task.ask("continue?")
|
|
@@ -65,13 +78,16 @@ export const getRawPageData = async (
|
|
|
65
78
|
page.close()
|
|
66
79
|
|
|
67
80
|
result.push({
|
|
81
|
+
id,
|
|
68
82
|
input,
|
|
69
83
|
mask,
|
|
70
84
|
root,
|
|
85
|
+
test: testRoot,
|
|
71
86
|
})
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
await browser.close()
|
|
90
|
+
await test_browser.close()
|
|
75
91
|
|
|
76
92
|
task.success()
|
|
77
93
|
return { pages: result, indexHtml }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
header {{SUPERVALUE}}
|
|
2
|
+
<loopwrapper>
|
|
3
|
+
ArrayDataKey=FIRST_KEY|FIRST_LOOP_CONTENT
|
|
4
|
+
<loopwrapper
|
|
5
|
+
>ArrayDataKey=SECOND_KEY|
|
|
6
|
+
<img
|
|
7
|
+
src="{{0#/Api/Catalog/GetCatalog@/[ITEM=0]/preview}}"
|
|
8
|
+
alt="{{0#/Api/Catalog/GetCatalog@/[ITEM=0]/title}}"
|
|
9
|
+
/>
|
|
10
|
+
</loopwrapper>
|
|
11
|
+
</loopwrapper>
|
|
12
|
+
footer
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"type": "html",
|
|
4
|
+
"content": "header "
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"type": "value",
|
|
8
|
+
"key": "SUPERVALUE"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"type": "html",
|
|
12
|
+
"content": " "
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"type": "array",
|
|
16
|
+
"data": [
|
|
17
|
+
{
|
|
18
|
+
"type": "html",
|
|
19
|
+
"content": "FIRST_LOOP_CONTENT "
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"type": "array",
|
|
23
|
+
"data": [
|
|
24
|
+
{
|
|
25
|
+
"type": "html",
|
|
26
|
+
"content": " <img src=\""
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "value",
|
|
30
|
+
"key": "0#/Api/Catalog/GetCatalog@/[ITEM=0]/preview"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"type": "html",
|
|
34
|
+
"content": "\" alt=\""
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"type": "value",
|
|
38
|
+
"key": "0#/Api/Catalog/GetCatalog@/[ITEM=0]/title"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "html",
|
|
42
|
+
"content": "\" />"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"key": "SECOND_KEY"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"key": "FIRST_KEY"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"type": "html",
|
|
52
|
+
"content": " footer"
|
|
53
|
+
}
|
|
54
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<header>
|
|
2
|
+
{{SITE_NAME}}
|
|
3
|
+
<nav>
|
|
4
|
+
<loopwrapper
|
|
5
|
+
>ArrayDataKey=MenuKey|
|
|
6
|
+
<a href="{{0#/Api/Menu@/[ITEM=0]/link}}">
|
|
7
|
+
{{0#/Api/Menu@/[ITEM=0]/title}}
|
|
8
|
+
<loopwrapper
|
|
9
|
+
>ArrayDataKey=0#/Api/Menu@/[ITEM=0]/submenu|
|
|
10
|
+
<span>{{0#/Api/Menu@/[ITEM=0]/submenu/[ITEM=1]/name}}</span>
|
|
11
|
+
</loopwrapper>
|
|
12
|
+
</a>
|
|
13
|
+
</loopwrapper>
|
|
14
|
+
</nav>
|
|
15
|
+
</header>
|
|
16
|
+
{{HERO_BANNER}}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "type": "html", "content": "<header> " },
|
|
3
|
+
{ "type": "value", "key": "SITE_NAME" },
|
|
4
|
+
{ "type": "html", "content": " <nav>" },
|
|
5
|
+
{
|
|
6
|
+
"type": "array",
|
|
7
|
+
"data": [
|
|
8
|
+
{ "type": "html", "content": " <a href=\"" },
|
|
9
|
+
{ "type": "value", "key": "0#/Api/Menu@/[ITEM=0]/link" },
|
|
10
|
+
{ "type": "html", "content": "\"> " },
|
|
11
|
+
{ "type": "value", "key": "0#/Api/Menu@/[ITEM=0]/title" },
|
|
12
|
+
{ "type": "html", "content": " " },
|
|
13
|
+
{
|
|
14
|
+
"type": "array",
|
|
15
|
+
"data": [
|
|
16
|
+
{ "type": "html", "content": " <span>" },
|
|
17
|
+
{ "type": "value", "key": "0#/Api/Menu@/[ITEM=0]/submenu/[ITEM=1]/name" },
|
|
18
|
+
{ "type": "html", "content": "</span>" }
|
|
19
|
+
],
|
|
20
|
+
"key": "0#/Api/Menu@/[ITEM=0]/submenu"
|
|
21
|
+
},
|
|
22
|
+
{ "type": "html", "content": "</a>" }
|
|
23
|
+
],
|
|
24
|
+
"key": "MenuKey"
|
|
25
|
+
},
|
|
26
|
+
{ "type": "html", "content": "</nav></header> " },
|
|
27
|
+
{ "type": "value", "key": "HERO_BANNER" }
|
|
28
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<section class="{{SECTION_CLASS}}">
|
|
2
|
+
<loopwrapper
|
|
3
|
+
>ArrayDataKey=categories|
|
|
4
|
+
<div data-id="{{0#/Api/Categories@/[ITEM=0]/id}}">
|
|
5
|
+
<h3>{{0#/Api/Categories@/[ITEM=0]/title}}</h3>
|
|
6
|
+
<loopwrapper
|
|
7
|
+
>ArrayDataKey=0#/Api/Categories@/[ITEM=0]/products|
|
|
8
|
+
<article>
|
|
9
|
+
<img
|
|
10
|
+
src="{{0#/Api/Categories@/[ITEM=0]/products/[ITEM=1]/image}}"
|
|
11
|
+
alt="{{0#/Api/Categories@/[ITEM=0]/products/[ITEM=1]/name}}"
|
|
12
|
+
/>
|
|
13
|
+
<loopwrapper
|
|
14
|
+
>ArrayDataKey=0#/Api/Categories@/[ITEM=0]/products/[ITEM=1]/tags|
|
|
15
|
+
<span>{{0#/Api/Categories@/[ITEM=0]/products/[ITEM=1]/tags/[ITEM=2]}}</span>
|
|
16
|
+
</loopwrapper>
|
|
17
|
+
</article>
|
|
18
|
+
</loopwrapper>
|
|
19
|
+
</div>
|
|
20
|
+
</loopwrapper>
|
|
21
|
+
</section>
|