mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-28 13:09:51 +01:00
feat: typescript types
This commit is contained in:
1 parent
4e586253c0
commit
80cc74a945
2 files changed
+112
-40
No files matched your search
@@ -65,9 +65,6 @@ for (const line of bodyOriginal.split("\n")) {
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<MarkdownCustom {body} />
|
<MarkdownCustom {body} />
|
||||||
|
|
||||||
<!-- {"see" in macros && <ApiMacroSee {locale} value={macros.see} />}
|
|
||||||
{"type" in macros && <ApiMacroType {locale} value={macros.type} />} -->
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Object.entries(macroComponents).map(([macro, Component]) => {
|
Object.entries(macroComponents).map(([macro, Component]) => {
|
||||||
if (!(macro in macros)) return null;
|
if (!(macro in macros)) return null;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
import { resolve } from "path";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
import { readFile } from "fs/promises";
|
import { readFile } from "fs/promises";
|
||||||
import { Link } from "./Link";
|
import { Link } from "./Link";
|
||||||
@@ -7,6 +8,22 @@ import HighlightedCodeBlock from "./HighlightedCodeBlock.astro";
|
|||||||
import { Text } from "./Text";
|
import { Text } from "./Text";
|
||||||
import { ArrowBottomRightIcon } from "@radix-ui/react-icons";
|
import { ArrowBottomRightIcon } from "@radix-ui/react-icons";
|
||||||
import { Code } from "./Code";
|
import { Code } from "./Code";
|
||||||
|
import {
|
||||||
|
createProgram,
|
||||||
|
createSourceFile,
|
||||||
|
forEachChild,
|
||||||
|
isEnumDeclaration,
|
||||||
|
isInterfaceDeclaration,
|
||||||
|
isMethodSignature,
|
||||||
|
isPropertySignature,
|
||||||
|
isTypeAliasDeclaration,
|
||||||
|
isTypeReferenceNode,
|
||||||
|
ScriptTarget,
|
||||||
|
TypeFormatFlags,
|
||||||
|
type InterfaceDeclaration,
|
||||||
|
type Node,
|
||||||
|
type TypeNode,
|
||||||
|
} from "typescript";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value: string;
|
value: string;
|
||||||
@@ -18,60 +35,118 @@ const strings = await api.strings(locale);
|
|||||||
|
|
||||||
const roots = ["src/protos", "../closed/protos"];
|
const roots = ["src/protos", "../closed/protos"];
|
||||||
|
|
||||||
const lang = "proto";
|
const lang = "ts";
|
||||||
const importPattern = /import "(.+)";/gm;
|
|
||||||
const messagePattern = /message .+ {\s*\n(.+\s*\n)+}/gm;
|
|
||||||
|
|
||||||
const explored = new Set<string>();
|
const name = "Avatar";
|
||||||
let messages: string[] = [];
|
const path = resolve(`${roots[0]}/${value}.ts`);
|
||||||
|
const buffer = await readFile(path);
|
||||||
|
|
||||||
await pullMessages(`${roots[0]}/${value}.proto`);
|
const program = createProgram([path], {});
|
||||||
|
const source = program.getSourceFile(path);
|
||||||
|
|
||||||
async function pullMessages(path: string) {
|
if (!source) {
|
||||||
if (explored.has(path)) return;
|
throw new Error(`Could not find ${path}`);
|
||||||
|
}
|
||||||
|
|
||||||
explored.add(path);
|
const checker = program.getTypeChecker();
|
||||||
|
|
||||||
const file = await readFile(path);
|
const collected = new Map<string, string>();
|
||||||
const content = file.toString();
|
|
||||||
const imports = content.matchAll(importPattern);
|
|
||||||
|
|
||||||
for (const _import of imports) {
|
let declaration: InterfaceDeclaration | undefined;
|
||||||
for (const root of roots) {
|
|
||||||
const path = `${root}/${_import[1]}`;
|
|
||||||
|
|
||||||
if (existsSync(path)) {
|
forEachChild(source, (node) => {
|
||||||
await pullMessages(path);
|
if (!isInterfaceDeclaration(node) || node.name.text !== name) return;
|
||||||
break;
|
declaration = node;
|
||||||
}
|
});
|
||||||
|
|
||||||
|
if (declaration === undefined) {
|
||||||
|
throw new Error(`Could not find ${name} in ${path}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function printNode(node: Node) {
|
||||||
|
if (isInterfaceDeclaration(node)) {
|
||||||
|
const symbol = checker.getSymbolAtLocation(node.name);
|
||||||
|
|
||||||
|
if (symbol) {
|
||||||
|
const text = node.getText();
|
||||||
|
|
||||||
|
collected.set(node.name.text, text);
|
||||||
|
|
||||||
|
node.members.forEach((member) => {
|
||||||
|
if (isPropertySignature(member) && member.type) {
|
||||||
|
collectTypeDependencies(member.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMethodSignature(member)) {
|
||||||
|
member.parameters.forEach((param) => {
|
||||||
|
if (param.type) {
|
||||||
|
collectTypeDependencies(param.type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (member.type) {
|
||||||
|
collectTypeDependencies(member.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const matchedMessages = content.matchAll(messagePattern);
|
forEachChild(node, printNode);
|
||||||
|
}
|
||||||
|
|
||||||
for (const message of matchedMessages) {
|
function isRelevantTypeDecl(node: Node) {
|
||||||
messages = [message[0], ...messages];
|
return (
|
||||||
|
isInterfaceDeclaration(node) ||
|
||||||
|
isTypeAliasDeclaration(node) ||
|
||||||
|
isEnumDeclaration(node)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectTypeDependencies(node: TypeNode) {
|
||||||
|
const type = checker.getTypeAtLocation(node);
|
||||||
|
const symbol = type.getSymbol();
|
||||||
|
|
||||||
|
if (!symbol) return;
|
||||||
|
|
||||||
|
const name = symbol.getName();
|
||||||
|
|
||||||
|
if (!collected.has(name)) {
|
||||||
|
const declarations = symbol.getDeclarations();
|
||||||
|
|
||||||
|
if (declarations) {
|
||||||
|
declarations.forEach((decl) => {
|
||||||
|
if (!isRelevantTypeDecl(decl)) return;
|
||||||
|
|
||||||
|
const name = (decl as any).name?.text;
|
||||||
|
if (!name || collected.has(name)) return;
|
||||||
|
|
||||||
|
collected.set(name, decl.getText());
|
||||||
|
|
||||||
|
forEachChild(decl, collectTypeDependenciesFromNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = messages.join("\n\n");
|
function collectTypeDependenciesFromNode(node: Node) {
|
||||||
|
if (isTypeReferenceNode(node)) {
|
||||||
const indexPattern = /^( +).+\.(.+ = \d+;)$/m;
|
collectTypeDependencies(node);
|
||||||
|
|
||||||
let stripped = "";
|
|
||||||
|
|
||||||
for (const line of content.split("\n")) {
|
|
||||||
const matches = line.match(indexPattern);
|
|
||||||
|
|
||||||
if (matches === null) {
|
|
||||||
stripped += `${line}\n`;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stripped += `${matches[1]}${matches[2]}\n`;
|
forEachChild(node, collectTypeDependenciesFromNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
stripped = stripped.replaceAll(" ", " ");
|
printNode(declaration);
|
||||||
|
|
||||||
|
let content = "";
|
||||||
|
|
||||||
|
for (const [, definition] of collected) {
|
||||||
|
content += `${definition}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
content = content.replaceAll(/^export\s*/gm, "");
|
||||||
|
content = content.trim();
|
||||||
---
|
---
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -136,7 +211,7 @@ stripped = stripped.replaceAll(" ", " ");
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="types">
|
<div class="types">
|
||||||
<HighlightedCodeBlock {lang} content={stripped} />
|
<HighlightedCodeBlock {lang} content={content} />
|
||||||
|
|
||||||
<div class="overlay">
|
<div class="overlay">
|
||||||
<Link underline="always" href="#" className="view-full">
|
<Link underline="always" href="#" className="view-full">
|
||||||
|
|||||||
Reference in new issue
Block a user