Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
"build:docs": "TARGET=docs vite build",
"build:lib": "TARGET=lib vite build",
"compile": "pnpm run compile:code-snippets && pnpm run compile:docs",
"compile:code-snippets": "node ./scripts/code-snippets/run.js",
"compile:docs": "node ./scripts/docs/run.js",
"compile:code-snippets": "node --loader ts-node/esm ./scripts/code-snippets/run.ts",
"compile:docs": "node --loader ts-node/esm ./scripts/docs/run.ts",
"lint": "eslint .",
"prerelease": "rm -rf dist && rm -rf docs && pnpm run build",
"prettier": "prettier --write \"**/*.{css,html,js,json,jsx,ts,tsx}\"",
Expand Down Expand Up @@ -87,6 +87,7 @@
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.11",
"ts-blank-space": "^0.6.2",
"ts-node": "^10.9.2",
"typescript": "~5.8.3",
"typescript-eslint": "^8.35.1",
"typescript-json-schema": "^0.65.1",
Expand Down
23 changes: 23 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions scripts/code-snippets/run.js → scripts/code-snippets/run.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { basename, join } from "node:path";
import { cwd } from "node:process";
import { getFilesWithExtensions, rmFilesWithExtensions } from "../utils.js";
import { syntaxHighlight } from "./syntax-highlight.js";
import { getFilesWithExtensions, rmFilesWithExtensions } from "../utils.ts";
import { syntaxHighlight } from "./syntax-highlight.ts";

async function run() {
const inputDir = join(cwd(), "src", "routes");
Expand All @@ -19,13 +19,12 @@ async function run() {
]);
const exampleFiles = tsFiles.filter((file) => file.includes(".example."));

for (let file of exampleFiles) {
for (const file of exampleFiles) {
console.debug("Extracting", file);

const buffer = await readFile(file);

let rawText = buffer.toString();
let json;

{
// Remove special comments and directives before syntax highlighting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,35 @@ import {
typescriptLanguage
} from "@codemirror/lang-javascript";
import { htmlLanguage } from "@codemirror/lang-html";
import { ensureSyntaxTree } from "@codemirror/language";
import { ensureSyntaxTree, LRLanguage } from "@codemirror/language";
import { EditorState } from "@codemirror/state";
import { classHighlighter, highlightTree } from "@lezer/highlight";

type TokenType = string;
type Token = {
columnIndex: number;
type: TokenType | null;
value: string;
};

type Language = "HTML" | "JS" | "JSX" | "TS" | "TSX";

type State = {
parsedTokens: Token[];
rawString: string;
};

export const DEFAULT_MAX_CHARACTERS = 500000;
export const DEFAULT_MAX_TIME = 5000;

export async function syntaxHighlight(code, language) {
let extension;
export async function syntaxHighlight(code: string, language: Language) {
let extension: LRLanguage;
switch (language) {
case "HTML": {
extension = htmlLanguage.configure({ dialect: "selfClosing" });
break;
}
case "JS":
case "JSX": {
extension = jsxLanguage;
break;
Expand All @@ -40,9 +55,9 @@ export async function syntaxHighlight(code, language) {
return tokens.map(parsedTokensToHtml).join("\n");
}

async function parser(code, languageExtension = jsxLanguage) {
const parsedTokens = [];
const currentLineState = {
async function parser(code: string, languageExtension: LRLanguage) {
const parsedTokens: Token[][] = [];
const currentLineState: State = {
parsedTokens: [],
rawString: ""
};
Expand Down Expand Up @@ -79,25 +94,29 @@ async function parser(code, languageExtension = jsxLanguage) {

let characterIndex = 0;
let parsedCharacterIndex = 0;
highlightTree(tree, classHighlighter, (from, to, className) => {
if (from > characterIndex) {
// No style applied to the token between position and from.
// This typically indicates white space or newline characters.
highlightTree(
tree,
classHighlighter,
(from: number, to: number, className: string) => {
if (from > characterIndex) {
// No style applied to the token between position and from.
// This typically indicates white space or newline characters.
processSection(
currentLineState,
parsedTokens,
code.slice(characterIndex, from),
""
);
}
processSection(
currentLineState,
parsedTokens,
code.slice(characterIndex, from),
""
code.slice(from, to),
className
);
characterIndex = to;
}
processSection(
currentLineState,
parsedTokens,
code.slice(from, to),
className
);
characterIndex = to;
});
);

const maxPosition = code.length - 1;

Expand Down Expand Up @@ -148,23 +167,26 @@ async function parser(code, languageExtension = jsxLanguage) {
return parsedTokens;
}

function processSection(currentLineState, parsedTokens, section, className) {
var _a;
function processSection(
currentLineState: State,
parsedTokens: Token[][],
section: string,
className: string
) {
// Remove "tok-" prefix;
const tokenType =
(_a =
className === null || className === void 0
? void 0
: className.substring(4)) !== null && _a !== void 0
? _a
: null; // Remove "tok-" prefix;
className === null || className === void 0
? null
: className.substring(4) || null;

let index = 0;
let nextIndex = section.indexOf("\n");
while (true) {
const substring =
nextIndex >= 0
? section.substring(index, nextIndex)
: section.substring(index);
const token = {
const token: Token = {
columnIndex: currentLineState.rawString.length,
type: tokenType,
value: substring
Expand All @@ -184,10 +206,10 @@ function processSection(currentLineState, parsedTokens, section, className) {
}
}

function parsedTokensToHtml(tokens) {
function parsedTokensToHtml(tokens: Token[]) {
let indent = 0;

tokens = tokens.map((token, index) => {
const htmlStrings = tokens.map((token, index) => {
const className = token.type ? `tok-${token.type}` : "";

// Trim leading space and use CSS to indent instead;
Expand All @@ -207,10 +229,12 @@ function parsedTokensToHtml(tokens) {
return `<span class="${className}">${escapedValue}</span>`;
});

return `<div style="min-height: 1rem; padding-left: ${indent + 2}ch; text-indent: -2ch;">${tokens.join("")}</div>`;
return `<div style="min-height: 1rem; padding-left: ${
indent + 2
}ch; text-indent: -2ch;">${htmlStrings.join("")}</div>`;
}

function escapeHtmlEntities(rawString) {
function escapeHtmlEntities(rawString: string) {
return rawString.replace(
/[\u00A0-\u9999<>&]/g,
(substring) => "&#" + substring.charCodeAt(0) + ";"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import prettier from "prettier";
import tsBlankSpace from "ts-blank-space";

export async function tsToJs(source) {
export async function tsToJs(source: string) {
source = tsBlankSpace(source);
source = source.replace(/<Grid<[^>]+>/g, "<Grid");
source = source.replace(/<List<[^>]+>/g, "<List");
Expand Down
12 changes: 6 additions & 6 deletions scripts/docs/run.js → scripts/docs/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mkdir, writeFile } from "node:fs/promises";
import { join, relative } from "node:path";
import { cwd } from "node:process";
import { withCustomConfig } from "react-docgen-typescript";
import { getFilesWithExtensions, rmFilesWithExtensions } from "../utils.js";
import { getFilesWithExtensions, rmFilesWithExtensions } from "../utils.ts";

const parser = withCustomConfig("./tsconfig.json", {
savePropValueAsString: true,
Expand All @@ -19,25 +19,25 @@ async function run() {

await rmFilesWithExtensions(outputDir, [".json"]);

let files = await getFilesWithExtensions(
const files = await getFilesWithExtensions(
inputDir,
[".ts", ".tsx"],
(file) => file.endsWith("/List.tsx") || file.endsWith("/Grid.tsx")
);

for (let file of files) {
for (const file of files) {
console.debug("Parsing", file);

const components = parser.parse(file);
for (let component of components) {
for (const component of components) {
// Convert to local paths
component.filePath = relative(cwd(), file);

// Filter inherited HTML attributes
for (let key in component.props) {
for (const key in component.props) {
const prop = component.props[key];
if (
prop.declarations.filter(
prop.declarations?.filter(
(declaration) => !declaration.fileName.includes("node_modules")
).length === 0
) {
Expand Down
15 changes: 11 additions & 4 deletions scripts/utils.js → scripts/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { readdir, rm } from "fs/promises";
import { extname, join } from "node:path";

export async function getFilesWithExtensions(directory, extensions, filter) {
const files = [];
export async function getFilesWithExtensions(
directory: string,
extensions: string[],
filter?: (path: string) => boolean
) {
const files: string[] = [];

const entries = await readdir(directory, { withFileTypes: true });

Expand All @@ -26,10 +30,13 @@ export async function getFilesWithExtensions(directory, extensions, filter) {
return files;
}

export async function rmFilesWithExtensions(directory, extensions) {
export async function rmFilesWithExtensions(
directory: string,
extensions: string[]
) {
const files = await getFilesWithExtensions(directory, extensions);

for (let file of files) {
for (const file of files) {
await rm(file);
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@
"@testing-library/jest-dom/vitest"
]
},
"include": ["lib", "scripts/**/*", "src", "index.tsx"]
"include": ["lib", "scripts", "src", "index.tsx"]
}