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": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^28.0.2",
|
||||
"@rollup/plugin-node-resolve": "^16.0.0",
|
||||
"@cacheable/node-cache": "^1.5.3",
|
||||
"@kevisual/tojs": "0.0.5",
|
||||
"@rollup/plugin-commonjs": "^28.0.3",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/react": "^19.0.3",
|
||||
"@types/react": "^19.0.10",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.30.0",
|
||||
"rollup": "^4.36.0",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"rollup-plugin-dts": "^6.1.1",
|
||||
"typescript": "^5.7.2"
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"lit": "^3.2.1",
|
||||
"lit-html": "^3.2.1"
|
||||
},
|
||||
"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