test
This commit is contained in:
parent
de861d0977
commit
99ad5c10dc
3
demo/lit/render.ts
Normal file
3
demo/lit/render.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import '../../src/html-preview/kevisual-base';
|
||||||
|
|
||||||
|
// console.log('KeVisualBase', KeVisualBase);
|
33
index.html
Normal file
33
index.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>KeVisual Base Example</title>
|
||||||
|
<script type="module" src="./demo/lit/render.ts"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<ev-html>
|
||||||
|
<style >
|
||||||
|
p {
|
||||||
|
color: red;
|
||||||
|
/* 添加一个id */
|
||||||
|
span {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="text/envision">
|
||||||
|
console.log('Script executed within ev');
|
||||||
|
console.log(document.body, window.shadowRoot);
|
||||||
|
</script>
|
||||||
|
<p>This paragraph should be red.
|
||||||
|
<span>This span.</span>
|
||||||
|
</p>
|
||||||
|
</ev-html>
|
||||||
|
<p>This paragraph should be blue.</p>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
14
package.json
14
package.json
@ -12,19 +12,23 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^28.0.2",
|
"@cacheable/node-cache": "^1.5.3",
|
||||||
"@rollup/plugin-node-resolve": "^16.0.0",
|
"@kevisual/tojs": "0.0.5",
|
||||||
|
"@rollup/plugin-commonjs": "^28.0.3",
|
||||||
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||||
"@rollup/plugin-typescript": "^12.1.2",
|
"@rollup/plugin-typescript": "^12.1.2",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/react": "^19.0.3",
|
"@types/react": "^19.0.10",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"rollup": "^4.30.0",
|
"rollup": "^4.36.0",
|
||||||
"rollup-plugin-copy": "^3.5.0",
|
"rollup-plugin-copy": "^3.5.0",
|
||||||
"rollup-plugin-dts": "^6.1.1",
|
"rollup-plugin-dts": "^6.1.1",
|
||||||
"typescript": "^5.7.2"
|
"typescript": "^5.8.2",
|
||||||
|
"vite": "^6.2.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"lit": "^3.2.1",
|
||||||
"lit-html": "^3.2.1"
|
"lit-html": "^3.2.1"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
|
119
src/html-preview/kevisual-base.ts
Normal file
119
src/html-preview/kevisual-base.ts
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import { unsafeCSS } from 'lit';
|
||||||
|
import { LitElement, html, css } from '../lit';
|
||||||
|
import { fnv1aHash16 } from '../utils/fnv1a-hash';
|
||||||
|
|
||||||
|
const baseStyle = `:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
function fnv1aHash(str) {
|
||||||
|
const FNV_PRIME = 16777619;
|
||||||
|
const OFFSET_BASIS = 2166136261;
|
||||||
|
|
||||||
|
let hash = OFFSET_BASIS;
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
hash ^= str.charCodeAt(i);
|
||||||
|
hash *= FNV_PRIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将结果转换为 32 位无符号整数
|
||||||
|
return hash >>> 0; // 确保结果是非负的 32 位整数
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
const hash = fnv1aHash('Hello, World!');
|
||||||
|
console.log(hash.toString(16)); // 输出哈希值的十六进制表示
|
||||||
|
// ${unsafeCSS(baseStyle)}
|
||||||
|
|
||||||
|
export class EnvisionBase extends LitElement {
|
||||||
|
static styles = css``;
|
||||||
|
unsafeCssContent = '';
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html` <slot @slotchange="${this.handleSlotChange}"></slot> `;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSlotChange() {
|
||||||
|
const slot = this.shadowRoot.querySelector('slot');
|
||||||
|
const nodes = slot.assignedNodes({ flatten: true }); // 获取所有插槽内容
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
const elementNode = node as HTMLElement;
|
||||||
|
if (elementNode.tagName.toLowerCase() === 'style') {
|
||||||
|
this.appendStyle(elementNode);
|
||||||
|
elementNode.remove();
|
||||||
|
} else if (elementNode.tagName.toLowerCase() === 'script') {
|
||||||
|
this.executeScript(elementNode as HTMLScriptElement);
|
||||||
|
elementNode.remove();
|
||||||
|
} else {
|
||||||
|
this.shadowRoot.appendChild(elementNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStyle(styleNode) {
|
||||||
|
console.log('this', this, this.classList);
|
||||||
|
console.dir(this);
|
||||||
|
this.unsafeCssContent = this.unsafeCssContent + styleNode.textContent;
|
||||||
|
const styleSheet = new CSSStyleSheet();
|
||||||
|
styleSheet.replaceSync(this.unsafeCssContent);
|
||||||
|
this.shadowRoot.adoptedStyleSheets.push(styleSheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeScript(scriptNode: HTMLScriptElement) {
|
||||||
|
const code = scriptNode.textContent;
|
||||||
|
const hash = fnv1aHash16(code);
|
||||||
|
const _code = code;
|
||||||
|
const url = `/api/s1/js/${hash}`;
|
||||||
|
|
||||||
|
const loadModule = async (url: string) => {
|
||||||
|
try {
|
||||||
|
return await import(/* @vite-ignore */ url);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('import error', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// First attempt to load the module
|
||||||
|
let module = await loadModule(url);
|
||||||
|
if (module) {
|
||||||
|
console.log('module', module);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post the code to the server
|
||||||
|
const addCache = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ code: _code }),
|
||||||
|
}).then((res) => res.json());
|
||||||
|
|
||||||
|
console.log('addCache', addCache);
|
||||||
|
|
||||||
|
// Wait for a short period before retrying
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
|
|
||||||
|
// Second attempt to load the module
|
||||||
|
module = await loadModule(url + '?test=2');
|
||||||
|
if (module) {
|
||||||
|
console.log('module2', module);
|
||||||
|
} else {
|
||||||
|
console.error('加载失败, 可能是build错误');
|
||||||
|
this.showError('加载失败');
|
||||||
|
}
|
||||||
|
module = await loadModule(url);
|
||||||
|
console.log('module3', module);
|
||||||
|
}
|
||||||
|
showError(msg?: string) {
|
||||||
|
this.shadowRoot.innerHTML = `<div style="color: red;">Error: ${msg?.toString() || 'Unknown error'}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('ev-html', EnvisionBase);
|
13
src/lit.ts
Normal file
13
src/lit.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import * as lit from 'lit';
|
||||||
|
|
||||||
|
// export { KeVisualBase } from './html-preview/kevisual-base';
|
||||||
|
|
||||||
|
export { lit };
|
||||||
|
|
||||||
|
export const html = lit.html;
|
||||||
|
export const svg = lit.svg;
|
||||||
|
export const mathml = lit.mathml;
|
||||||
|
export const render = lit.render;
|
||||||
|
export const nothing = lit.nothing;
|
||||||
|
export const css = lit.css;
|
||||||
|
export const LitElement = lit.LitElement;
|
27
src/utils/fnv1a-hash.ts
Normal file
27
src/utils/fnv1a-hash.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 将字符串转换为 32 位无符号整数
|
||||||
|
* @param str - 要转换的字符串
|
||||||
|
* @returns 32 位无符号整数
|
||||||
|
*/
|
||||||
|
export function fnv1aHash(str: string): number {
|
||||||
|
const FNV_PRIME = 16777619;
|
||||||
|
const OFFSET_BASIS = 2166136261;
|
||||||
|
|
||||||
|
let hash = OFFSET_BASIS;
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
hash ^= str.charCodeAt(i);
|
||||||
|
hash *= FNV_PRIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将结果转换为 32 位无符号整数
|
||||||
|
return hash >>> 0; // 确保结果是非负的 32 位整数
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字符串转换为 16 位无符号整数
|
||||||
|
* @param str - 要转换的字符串
|
||||||
|
* @returns 16 位无符号整数
|
||||||
|
*/
|
||||||
|
export const fnv1aHash16 = (str: string): string => {
|
||||||
|
return fnv1aHash(str).toString(16); // 取低 16 位
|
||||||
|
};
|
51
vite.config.js
Normal file
51
vite.config.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import { returnOriginalJs } from '@kevisual/tojs';
|
||||||
|
import http from 'http';
|
||||||
|
const server = http.createServer(async (req, res) => {
|
||||||
|
console.log('req', req.url, req.method);
|
||||||
|
const method = req.method;
|
||||||
|
let body2 = {};
|
||||||
|
if (method === 'POST') {
|
||||||
|
const body = await parseBody(req);
|
||||||
|
console.log('body', body);
|
||||||
|
body2 = JSON.parse(body);
|
||||||
|
}
|
||||||
|
returnOriginalJs(
|
||||||
|
{
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
query: {
|
||||||
|
...body2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prefixUrl: '/api/s1/js/',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
server.listen(4005, () => {
|
||||||
|
console.log('server listen', server.address());
|
||||||
|
});
|
||||||
|
export const parseBody = async (req) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const arr = [];
|
||||||
|
req.on('data', (chunk) => {
|
||||||
|
arr.push(chunk);
|
||||||
|
});
|
||||||
|
req.on('end', () => {
|
||||||
|
resolve(Buffer.concat(arr).toString());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
server: {
|
||||||
|
port: 4003,
|
||||||
|
host: true,
|
||||||
|
proxy: {
|
||||||
|
'/api/s1/js': {
|
||||||
|
target: 'http://localhost:4005',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user