This commit is contained in:
Seth Trowbridge 2024-10-11 13:04:47 -04:00
parent 469c0c4edd
commit a0d6f89290
13 changed files with 332 additions and 12 deletions

13
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"request": "attach",
"name": "Attach",
"type": "node"
}
]
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"deno.enable": true,
}

View File

@ -1 +1,5 @@
{} {
"tasks": {
"dbg": "deno run -A --inspect-wait junk/module_inspector.test.ts"
}
}

13
deno.lock Normal file
View File

@ -0,0 +1,13 @@
{
"version": "3",
"remote": {
"https://esm.sh/preact@10.23.1": "5c90e3946c882878ada8f82671a1e2cfc3d307eb1253e83cee74f04194cecf41",
"https://esm.sh/preact@10.23.1/compat": "e6e124f2c59692ee4dda7d1b9271a6169c57eaa43077495497a44313195fc859",
"https://esm.sh/preact@10.23.1/debug": "8d66a656164fe5d5f40078740a444af0cbf38f4eefe415e7bbf76ceefdde4c32",
"https://esm.sh/stable/preact@10.23.1/denonext/compat.js": "49c7e42b0555fab75f189e8317d39a252bbb38b49224badf4a110c6a708954f7",
"https://esm.sh/stable/preact@10.23.1/denonext/debug.js": "baec17edc90c1c0ba53a2f64c66b3890779169e71289450634543a5a2aab839a",
"https://esm.sh/stable/preact@10.23.1/denonext/devtools.js": "bb198cea043a5878c0367867e1be710e9a11581f8ab10ca0a51091e3d2add74d",
"https://esm.sh/stable/preact@10.23.1/denonext/hooks.js": "6962ffaaded3eee1cff070f660b6304007754ef1993d63241d2f3807a10ee21e",
"https://esm.sh/stable/preact@10.23.1/denonext/preact.mjs": "c30c9bd2ab4b41104f97c0679413cdfe37ec2fa0131a7c5a1a2d3b6f6fae9670"
}
}

View File

@ -1,5 +1,6 @@
<script type="module" src="test_receiver.mjs"></script> <script type="module" src="test_receiver.mjs"></script>
<!--
<script type="module"> <script type="module">
import r1 from "./test_rand.mjs?1"; import r1 from "./test_rand.mjs?1";
import r2 from "./test_rand.mjs?2"; import r2 from "./test_rand.mjs?2";
@ -8,4 +9,42 @@
console.log(r1, r2, r3, r4); console.log(r1, r2, r3, r4);
</script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/4.5.4/typescript.min.js"></script>
<script type="module">
// TypeScript source code
const source = `
export const num: number = 42;
export function greet() {
console.log('Hello, world!');
}
`;
// Transpile the TypeScript code to JavaScript
const result = ts.transpileModule(source, {
compilerOptions: {
module: ts.ModuleKind.ESNext,
target: ts.ScriptTarget.ESNext
}
});
// Parse the AST of the TypeScript source code
const sourceFile = ts.createSourceFile('module.ts', source, ts.ScriptTarget.ESNext, true);
// Function to inspect the AST and find export declarations
function findExports(node) {
if (ts.isExportDeclaration(node) || ts.isExportAssignment(node)) {
console.log('Export found:', node);
}
ts.forEachChild(node, findExports);
}
// Traverse the AST
//findExports(sourceFile);
console.log(sourceFile);
// Output: export const num = 42; export function greet() { console.log('Hello, world!'); }
</script> </script>

131
junk/module_inspector.js Normal file
View File

@ -0,0 +1,131 @@
/**
* @typedef {function(string): boolean} GlyphCheck
*/
/**
* @type {GlyphCheck}
*/
const isAlphaLike = (inGlyph) => {
const inCode = inGlyph.charCodeAt(0);
if (inCode >= 97 && inCode <= 122) {
return true;
}
if (inCode >= 65 && inCode <= 90) {
return true;
}
return `$_.`.includes(inGlyph);
};
/**
* @type {GlyphCheck}
*/
const isWhiteSpace = (inGlyph) => `\n\r\t `.includes(inGlyph);
/**
* @type {GlyphCheck}
*/
const isQuote = (inGlyph) => `"'\``.includes(inGlyph);
/**
* @param {GlyphCheck} inCheck
* @returns {GlyphCheck}
*/
const isNot = (inCheck) => (inGlyph) => !inCheck(inGlyph);
/**
* @param {string} inText
* @param {number} inStart
* @param {GlyphCheck} inTest
* @returns {number}
*/
const contiguous = (inText, inStart, inTest) => {
let ok = true;
let index = inStart;
let count = 0;
while (ok && count < inText.length) {
count++;
ok = inTest(inText.charAt(index++));
}
return index - 1;
};
/**
* @param {string} inFile
* @param {number} [inIndex=0]
* @param {Array<{internal: string, external: string}>} inLocal
* @param {Array<string>} inForeign
* @returns {number|boolean}
*/
const findNextExport = (inFile, inIndex = 0, inLocal, inForeign) => {
const pos = inFile.indexOf("export", inIndex);
if (pos !== -1) {
if (!isAlphaLike(inFile.charAt(pos - 1)) || !isAlphaLike(inFile.charAt(pos + 6))) {
const nextCharInd = contiguous(inFile, pos + 6, isWhiteSpace);
const nextChar = inFile[nextCharInd];
if (nextChar === "*") {
const firstQuoteInd = contiguous(inFile, nextCharInd + 1, isNot(isQuote));
const secondQuoteInd = contiguous(inFile, firstQuoteInd + 1, isNot(isQuote));
inForeign.push(inFile.substring(nextCharInd, secondQuoteInd + 1));
} else if (nextChar == "{") {
const endBracketInd = contiguous(inFile, nextCharInd, (inGlyph) => inGlyph !== "}");
const nextLetterInd = contiguous(inFile, endBracketInd + 1, isWhiteSpace);
if (inFile.substring(nextLetterInd, nextLetterInd + 4) == "from") {
const firstQuoteInd = contiguous(inFile, nextLetterInd + 4, isNot(isQuote));
const secondQuoteInd = contiguous(inFile, firstQuoteInd + 1, isNot(isQuote));
inForeign.push(inFile.substring(nextCharInd, secondQuoteInd + 1));
} else {
const members = inFile.substring(nextCharInd + 1, endBracketInd).replaceAll(" as ", "|||").replace(/\s/g, '');
members.split(",").forEach(part => {
const renamed = part.split("|||");
inLocal.push({ internal: renamed, external: renamed || renamed });
});
}
} else if (isAlphaLike(nextChar)) {
const keywordEndInd = contiguous(inFile, nextCharInd, isAlphaLike);
const keyword = inFile.substring(nextCharInd, keywordEndInd);
if (keyword === "default") {
inLocal.push({ internal: keyword, external: keyword });
} else if (["const", "let", "var", "function", "class"].includes(keyword)) {
const varStartInd = contiguous(inFile, keywordEndInd + 1, isWhiteSpace);
const varEndInd = contiguous(inFile, varStartInd + 1, isAlphaLike);
const keyword = inFile.substring(varStartInd, varEndInd);
inLocal.push({ internal: keyword, external: keyword });
}
}
}
return pos + 7;
} else {
return false;
}
};
/**
* @param {string} inFile
* @returns {[local: string[], foreign: string[]]}
*/
export const Exports = (inFile) => {
let match = /** @type {number|boolean} */ (0);
let count = 0;
const local = /** @type {string[]} */ ([]);
const foreign = /** @type {string[]} */ ([]);
while (match !== false && count < 200) {
count++;
match = findNextExport(inFile, match, local, foreign);
}
return [local, foreign];
};
/**
* @param {string|URL} inURL
* @returns {Promise<[local: string[], foreign: string[]]>}
*/
export const FileExports = async (inURL) => {
const resp = await fetch(inURL);
const text = await resp.text();
return Exports(text);
};

View File

@ -1,12 +1,14 @@
import * as Inspector from "./module_inspector.ts"; import * as Inspector from "./module_inspector.ts";
console.log();
Deno.test("check string parsing", ()=>{ Deno.test("check string parsing", ()=>{
const [local, global] = Inspector.Exports(` const [local, global] = Inspector.Exports(`
// export in comment // export in comment
/** /**
* *
* export const TESTtt * export const
* / * /
const fakeexport =()=>{}; const fakeexport =()=>{};
const exportfake =()=>{}; const exportfake =()=>{};

View File

@ -32,7 +32,7 @@ const contiguous =(inText:string, inStart:number, inTest:GlyphCheck):number=>
return index-1; return index-1;
} }
const findNextExport =(inFile:string, inIndex=0, inLocal:Array<string>, inForeign:Array<string>)=> const findNextExport =(inFile:string, inIndex=0, inLocal:Array<{internal:string, external:string }>, inForeign:Array<string>)=>
{ {
const pos = inFile.indexOf("export", inIndex); const pos = inFile.indexOf("export", inIndex);
if(pos !== -1) if(pos !== -1)
@ -65,11 +65,12 @@ const findNextExport =(inFile:string, inIndex=0, inLocal:Array<string>, inForeig
} }
else else
{ {
const members = inFile.substring(nextCharInd+1, endBracketInd).replace(/\s/g, ''); const members = inFile.substring(nextCharInd+1, endBracketInd).replaceAll(" as ", "|||").replace(/\s/g, '');
members.split(",").forEach(part=> members.split(",").forEach(part=>
{ {
const renamed = part.split(" as "); const renamed = part.split("|||");
inLocal.push(renamed[1] || renamed[0]); //inLocal.push(renamed[1] || renamed[0]);
inLocal.push({internal:renamed[0], external:renamed[1]||renamed[0]})
}); });
} }
@ -80,7 +81,7 @@ const findNextExport =(inFile:string, inIndex=0, inLocal:Array<string>, inForeig
const keyword = inFile.substring(nextCharInd, keywordEndInd); const keyword = inFile.substring(nextCharInd, keywordEndInd);
if(keyword === "default") if(keyword === "default")
{ {
inLocal.push(keyword); inLocal.push({internal:keyword, external:keyword});
//console.log(`MEMBER: >>${keyword})}<<`); //console.log(`MEMBER: >>${keyword})}<<`);
} }
else if(["const", "let", "var", "function", "class"].includes(keyword)) else if(["const", "let", "var", "function", "class"].includes(keyword))
@ -88,7 +89,8 @@ const findNextExport =(inFile:string, inIndex=0, inLocal:Array<string>, inForeig
const varStartInd = contiguous(inFile, keywordEndInd+1, isWhiteSpace); const varStartInd = contiguous(inFile, keywordEndInd+1, isWhiteSpace);
const varEndInd = contiguous(inFile, varStartInd+1, isAlphaLike); const varEndInd = contiguous(inFile, varStartInd+1, isAlphaLike);
//console.log(`MEMBER: >>${inFile.substring(varStartInd, varEndInd)}<<`); //console.log(`MEMBER: >>${inFile.substring(varStartInd, varEndInd)}<<`);
inLocal.push(inFile.substring(varStartInd, varEndInd)) const keyword = inFile.substring(varStartInd, varEndInd);
inLocal.push({internal:keyword, external:keyword});
} }
} }
} }

43
preactthing.tsx Normal file
View File

@ -0,0 +1,43 @@
import { useState as preactUseState, useEffect as preactUseEffect } from 'preact/hooks';
import { useState as reactUseState, useEffect as reactUseEffect } from 'react';
import { options as preactOptions } from 'preact';
const isPreact = typeof preactUseState === 'function';
const useState = isPreact ? preactUseState : reactUseState;
const useEffect = isPreact ? preactUseEffect : reactUseEffect;
function useLogger(componentName) {
useEffect(() => {
console.log(`${isPreact ? 'Preact' : 'React'}: Component ${componentName} mounted`);
return () => {
console.log(`${isPreact ? 'Preact' : 'React'}: Component ${componentName} unmounted`);
};
}, [componentName]);
useEffect(() => {
console.log(`${isPreact ? 'Preact' : 'React'}: Component ${componentName} rendered`);
});
}
if (isPreact) {
preactOptions.__r = (vnode) => {
console.log('Preact: Rendering component:', vnode);
};
preactOptions.diffed = (vnode) => {
console.log('Preact: Component diffed:', vnode);
};
preactOptions.unmount = (vnode) => {
console.log('Preact: Unmounting component:', vnode);
};
}
// Example component using the custom hook
function MyComponent() {
useLogger('MyComponent');
return <div>My Component</div>;
}
export default MyComponent;

View File

@ -0,0 +1,7 @@
<html>
<head></head>
<body>
<div id="app"></div>
<script type="module" src="./preact.js"></script>
</body>
</html>

30
state-preserve/preact.js Normal file
View File

@ -0,0 +1,30 @@
import "https://esm.sh/preact@10.23.1/debug";
import * as Preact from "https://esm.sh/preact@10.23.1";
import * as React from "https://esm.sh/preact@10.23.1/compat";
const H = Preact.h;
console.log(React);
/** @typedef {(vnode:Preact.VNode)=>unknown} DeepHook*/
/** @type {DeepHook|undefined} */
const renderOld =Preact.options.__r;
/** @type {DeepHook} */
const renderNew =(vnode)=>
{
console.log("render!")
console.log(vnode);
// how to examine state context here?
};
Preact.options.__r = renderOld ? /**@type{DeepHook}*/((vnode)=>{renderOld(vnode); renderNew(vnode);}) : renderNew;
const Component =()=>
{
const [countGet, countSet] = React.useState(3);
return H("h1", {onClick(){countSet(countGet+1)}}, `count: ${countGet}`);
}
const root = document.querySelector("#app")||document.body;
Preact.render(H(Component, {HEY:"HEY"}), root);

View File

@ -1,14 +1,20 @@
export let changing = 2; export let changing = 42;
////////////////////////////////////////////
let ReImported = {};
const thisURL = new URL(import.meta.url) const thisURL = new URL(import.meta.url)
const thisFile = thisURL.pathname; const thisFile = thisURL.pathname;
if(!thisURL.search) if(!thisURL.search)
{ {
setInterval(()=>{ setInterval(()=>{
import(thisFile+"?"+Math.random()).then(module=>{ import(thisFile+"?"+Math.random()).then(module=>{
ReImported = module;
changing = module.changing; for(let key in module)
console.log(changing); {
const statement = `${key}=ReImported.${key}`
eval(statement);
console.log(statement);
}
}) })
}, 3000); }, 3000);
} }

27
transpiler/index.html Normal file
View File

@ -0,0 +1,27 @@
<html>
<head></head>
<body style="display: flex; gap: 30px;">
<textarea id="input"></textarea>
<button style="align-self: center;" id="transpile"></button>
<textarea id="output"></textarea>
<script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/4.5.4/typescript.min.js"></script>
<script type="module">
const domIn = document.getElementById("input");
const domOut = document.getElementById("output");
const domDo = document.getElementById("transpile");
domDo.addEventListener("click", ()=>{
const result = ts.transpileModule(domIn.value, {
compilerOptions: {
module: ts.ModuleKind.ESNext,
target: ts.ScriptTarget.ESNext
}
});
domOut.value = result.outputText
console.log(result);
})
</script>
</body>
</html>