react-lib-tools 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/dist/react-lib-tools.cjs +8 -0
- package/dist/react-lib-tools.cjs.map +1 -0
- package/dist/react-lib-tools.css +1 -0
- package/dist/react-lib-tools.d.ts +177 -0
- package/dist/react-lib-tools.js +9373 -0
- package/dist/react-lib-tools.js.map +1 -0
- package/package.json +110 -0
- package/scripts/compile-docs.ts +22 -0
- package/scripts/compile-examples.ts +71 -0
- package/scripts/utils/docs/compileComponent.ts +116 -0
- package/scripts/utils/docs/compileComponents.ts +74 -0
- package/scripts/utils/docs/compileImperativeHandle.ts +48 -0
- package/scripts/utils/docs/compileImperativeHandles.ts +45 -0
- package/scripts/utils/docs/formatDescriptionText.ts +11 -0
- package/scripts/utils/docs/getPropTypeText.ts +28 -0
- package/scripts/utils/docs/insertPropsMarkdown.ts +29 -0
- package/scripts/utils/docs/parseDescription.ts +59 -0
- package/scripts/utils/docs/propsToTable.ts +48 -0
- package/scripts/utils/examples/trimExcludedText.ts +13 -0
- package/scripts/utils/getFilesWithExtensions.ts +31 -0
- package/scripts/utils/initialize.ts +34 -0
- package/scripts/utils/rmFilesWithExtensions.ts +13 -0
- package/scripts/utils/syntax-highlight.ts +255 -0
- package/styles.css +127 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { extname, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
export async function getFilesWithExtensions(
|
|
5
|
+
directory: string,
|
|
6
|
+
extensions: string[],
|
|
7
|
+
filter?: ((path: string) => boolean) | undefined
|
|
8
|
+
) {
|
|
9
|
+
const files: string[] = [];
|
|
10
|
+
|
|
11
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
12
|
+
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
const fullPath = join(directory, entry.name);
|
|
15
|
+
|
|
16
|
+
if (entry.isDirectory()) {
|
|
17
|
+
files.push(
|
|
18
|
+
...(await getFilesWithExtensions(fullPath, extensions, filter))
|
|
19
|
+
);
|
|
20
|
+
} else if (entry.isFile()) {
|
|
21
|
+
const fileExtension = extname(entry.name);
|
|
22
|
+
if (extensions.includes(fileExtension)) {
|
|
23
|
+
if (typeof filter !== "function" || filter(fullPath)) {
|
|
24
|
+
files.push(fullPath);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return files;
|
|
31
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { cwd } from "node:process";
|
|
4
|
+
import { getFilesWithExtensions } from "./getFilesWithExtensions.ts";
|
|
5
|
+
import { rmFilesWithExtensions } from "./rmFilesWithExtensions.ts";
|
|
6
|
+
|
|
7
|
+
export async function initialize({
|
|
8
|
+
fileExtensions,
|
|
9
|
+
fileFilter,
|
|
10
|
+
inputPath,
|
|
11
|
+
outputDirName
|
|
12
|
+
}: {
|
|
13
|
+
fileExtensions: string[];
|
|
14
|
+
fileFilter?: ((path: string) => boolean) | undefined;
|
|
15
|
+
inputPath: string[];
|
|
16
|
+
outputDirName: string;
|
|
17
|
+
}) {
|
|
18
|
+
const inputDir = join(cwd(), ...inputPath);
|
|
19
|
+
const outputDir = join(cwd(), "public", "generated", outputDirName);
|
|
20
|
+
await mkdir(outputDir, { recursive: true });
|
|
21
|
+
await rmFilesWithExtensions(outputDir, [".json"]);
|
|
22
|
+
|
|
23
|
+
const files = await getFilesWithExtensions(
|
|
24
|
+
inputDir,
|
|
25
|
+
fileExtensions,
|
|
26
|
+
fileFilter
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
files,
|
|
31
|
+
inputDir,
|
|
32
|
+
outputDir
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { getFilesWithExtensions } from "./getFilesWithExtensions.ts";
|
|
3
|
+
|
|
4
|
+
export async function rmFilesWithExtensions(
|
|
5
|
+
directory: string,
|
|
6
|
+
extensions: string[]
|
|
7
|
+
) {
|
|
8
|
+
const files = await getFilesWithExtensions(directory, extensions);
|
|
9
|
+
|
|
10
|
+
for (const file of files) {
|
|
11
|
+
await rm(file);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { htmlLanguage } from "@codemirror/lang-html";
|
|
2
|
+
import {
|
|
3
|
+
jsxLanguage,
|
|
4
|
+
tsxLanguage,
|
|
5
|
+
typescriptLanguage
|
|
6
|
+
} from "@codemirror/lang-javascript";
|
|
7
|
+
import { ensureSyntaxTree, LRLanguage } from "@codemirror/language";
|
|
8
|
+
import { EditorState } from "@codemirror/state";
|
|
9
|
+
import { classHighlighter, highlightTree } from "@lezer/highlight";
|
|
10
|
+
import { type BuiltInParserName } from "prettier";
|
|
11
|
+
|
|
12
|
+
type TokenType = string;
|
|
13
|
+
type Token = {
|
|
14
|
+
columnIndex: number;
|
|
15
|
+
type: TokenType | null;
|
|
16
|
+
value: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type Language = "HTML" | "JS" | "JSX" | "TS" | "TSX";
|
|
20
|
+
|
|
21
|
+
type State = {
|
|
22
|
+
parsedTokens: Token[];
|
|
23
|
+
rawString: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const DEFAULT_MAX_CHARACTERS = 500000;
|
|
27
|
+
export const DEFAULT_MAX_TIME = 5000;
|
|
28
|
+
|
|
29
|
+
export async function syntaxHighlight(code: string, language: Language) {
|
|
30
|
+
let extension: LRLanguage;
|
|
31
|
+
let prettierParser: BuiltInParserName;
|
|
32
|
+
switch (language) {
|
|
33
|
+
case "HTML": {
|
|
34
|
+
extension = htmlLanguage.configure({ dialect: "selfClosing" });
|
|
35
|
+
prettierParser = "html";
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
case "JS":
|
|
39
|
+
case "JSX": {
|
|
40
|
+
extension = jsxLanguage;
|
|
41
|
+
prettierParser = "babel";
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case "TS": {
|
|
45
|
+
extension = typescriptLanguage;
|
|
46
|
+
prettierParser = "typescript";
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case "TSX": {
|
|
50
|
+
extension = tsxLanguage;
|
|
51
|
+
prettierParser = "typescript";
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!extension) {
|
|
56
|
+
console.error("Unsupported language %o", language);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const tokens = await parser(code, extension, prettierParser);
|
|
60
|
+
|
|
61
|
+
return tokens.map(parsedTokensToHtml).join("\n");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function parser(
|
|
65
|
+
code: string,
|
|
66
|
+
languageExtension: LRLanguage,
|
|
67
|
+
|
|
68
|
+
// @ts-expect-error TS6133
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
70
|
+
prettierParser: BuiltInParserName
|
|
71
|
+
) {
|
|
72
|
+
const parsedTokens: Token[][] = [];
|
|
73
|
+
const currentLineState: State = {
|
|
74
|
+
parsedTokens: [],
|
|
75
|
+
rawString: ""
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// The logic below to trim code sections only works with "\n"
|
|
79
|
+
code = code.replace(/\r\n?|\n|\u2028|\u2029/g, "\n");
|
|
80
|
+
if (code.length > DEFAULT_MAX_CHARACTERS) {
|
|
81
|
+
let index = DEFAULT_MAX_CHARACTERS - 1;
|
|
82
|
+
while (index > 0 && code.charAt(index) !== "\n") {
|
|
83
|
+
index--;
|
|
84
|
+
}
|
|
85
|
+
if (index === 0) {
|
|
86
|
+
while (index < code.length && code.charAt(index) !== "\n") {
|
|
87
|
+
index++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
code = code.slice(0, index + 1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// In the future I may want to (re)format code examples for different screen widths
|
|
94
|
+
// code = await format(code, {
|
|
95
|
+
// parser: prettierParser,
|
|
96
|
+
// plugins: ["prettier-plugin-tailwindcss"],
|
|
97
|
+
// printWidth: 100,
|
|
98
|
+
// proseWrap: "always",
|
|
99
|
+
// semi: true
|
|
100
|
+
// });
|
|
101
|
+
|
|
102
|
+
const state = EditorState.create({
|
|
103
|
+
doc: code,
|
|
104
|
+
extensions: [languageExtension]
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const tree = ensureSyntaxTree(
|
|
108
|
+
state,
|
|
109
|
+
DEFAULT_MAX_CHARACTERS,
|
|
110
|
+
DEFAULT_MAX_TIME
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (tree === null) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let characterIndex = 0;
|
|
118
|
+
let parsedCharacterIndex = 0;
|
|
119
|
+
highlightTree(
|
|
120
|
+
tree,
|
|
121
|
+
classHighlighter,
|
|
122
|
+
(from: number, to: number, className: string) => {
|
|
123
|
+
if (from > characterIndex) {
|
|
124
|
+
// No style applied to the token between position and from.
|
|
125
|
+
// This typically indicates white space or newline characters.
|
|
126
|
+
processSection(
|
|
127
|
+
currentLineState,
|
|
128
|
+
parsedTokens,
|
|
129
|
+
code.slice(characterIndex, from),
|
|
130
|
+
""
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
processSection(
|
|
134
|
+
currentLineState,
|
|
135
|
+
parsedTokens,
|
|
136
|
+
code.slice(from, to),
|
|
137
|
+
className
|
|
138
|
+
);
|
|
139
|
+
characterIndex = to;
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const maxPosition = code.length;
|
|
144
|
+
|
|
145
|
+
if (characterIndex < maxPosition) {
|
|
146
|
+
// No style applied on the trailing text.
|
|
147
|
+
// This typically indicates white space or newline characters.
|
|
148
|
+
processSection(
|
|
149
|
+
currentLineState,
|
|
150
|
+
parsedTokens,
|
|
151
|
+
code.slice(characterIndex, maxPosition),
|
|
152
|
+
""
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (currentLineState.parsedTokens.length) {
|
|
156
|
+
parsedTokens.push(currentLineState.parsedTokens);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
parsedCharacterIndex += characterIndex + 1;
|
|
160
|
+
|
|
161
|
+
// Anything that's left should de-opt to plain text.
|
|
162
|
+
if (parsedCharacterIndex < code.length) {
|
|
163
|
+
let nextIndex = code.indexOf("\n", parsedCharacterIndex);
|
|
164
|
+
let parsedLineTokens = [];
|
|
165
|
+
while (true) {
|
|
166
|
+
const line =
|
|
167
|
+
nextIndex >= 0
|
|
168
|
+
? code.substring(parsedCharacterIndex, nextIndex)
|
|
169
|
+
: code.substring(parsedCharacterIndex);
|
|
170
|
+
parsedLineTokens.push({
|
|
171
|
+
columnIndex: 0,
|
|
172
|
+
type: null,
|
|
173
|
+
value: line
|
|
174
|
+
});
|
|
175
|
+
if (nextIndex >= 0) {
|
|
176
|
+
parsedTokens.push(parsedLineTokens);
|
|
177
|
+
parsedLineTokens = [];
|
|
178
|
+
} else if (nextIndex === -1) {
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
parsedCharacterIndex = nextIndex + 1;
|
|
182
|
+
nextIndex = code.indexOf("\n", parsedCharacterIndex);
|
|
183
|
+
}
|
|
184
|
+
if (parsedLineTokens.length) {
|
|
185
|
+
parsedTokens.push(parsedLineTokens);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return parsedTokens;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function processSection(
|
|
193
|
+
currentLineState: State,
|
|
194
|
+
parsedTokens: Token[][],
|
|
195
|
+
section: string,
|
|
196
|
+
className: string
|
|
197
|
+
) {
|
|
198
|
+
// Remove "tok-" prefix;
|
|
199
|
+
const tokenType =
|
|
200
|
+
className === null || className === void 0
|
|
201
|
+
? null
|
|
202
|
+
: className.substring(4) || null;
|
|
203
|
+
|
|
204
|
+
let index = 0;
|
|
205
|
+
let nextIndex = section.indexOf("\n");
|
|
206
|
+
while (true) {
|
|
207
|
+
const substring =
|
|
208
|
+
nextIndex >= 0
|
|
209
|
+
? section.substring(index, nextIndex)
|
|
210
|
+
: section.substring(index);
|
|
211
|
+
const token: Token = {
|
|
212
|
+
columnIndex: currentLineState.rawString.length,
|
|
213
|
+
type: tokenType,
|
|
214
|
+
value: substring
|
|
215
|
+
};
|
|
216
|
+
currentLineState.parsedTokens.push(token);
|
|
217
|
+
currentLineState.rawString += substring;
|
|
218
|
+
if (nextIndex === -1) {
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
if (nextIndex >= 0) {
|
|
222
|
+
parsedTokens.push(currentLineState.parsedTokens);
|
|
223
|
+
currentLineState.parsedTokens = [];
|
|
224
|
+
currentLineState.rawString = "";
|
|
225
|
+
}
|
|
226
|
+
index = nextIndex + 1;
|
|
227
|
+
nextIndex = section.indexOf("\n", index);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function parsedTokensToHtml(tokens: Token[]) {
|
|
232
|
+
const htmlStrings = tokens.map((token) => {
|
|
233
|
+
const className = token.type ? `tok-${token.type}` : "";
|
|
234
|
+
const escapedValue = escapeHtmlEntities(token.value);
|
|
235
|
+
|
|
236
|
+
return `<span class="${className}">${escapedValue}</span>`;
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Edge case to avoid empty line
|
|
240
|
+
let htmlString = htmlStrings.join("");
|
|
241
|
+
if (tokens.length <= 1) {
|
|
242
|
+
if (!tokens[0].value) {
|
|
243
|
+
htmlString = " ";
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return `<div>${htmlString}</div>`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function escapeHtmlEntities(rawString: string) {
|
|
251
|
+
return rawString.replace(
|
|
252
|
+
/[\u00A0-\u9999<>&]/g,
|
|
253
|
+
(substring) => "&#" + substring.charCodeAt(0) + ";"
|
|
254
|
+
);
|
|
255
|
+
}
|
package/styles.css
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
font-size: 12px;
|
|
6
|
+
|
|
7
|
+
@media only screen and (max-width: 600px) {
|
|
8
|
+
font-size: 16px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
color-scheme: dark;
|
|
12
|
+
font-synthesis: none;
|
|
13
|
+
text-rendering: optimizeLegibility;
|
|
14
|
+
-webkit-font-smoothing: antialiased;
|
|
15
|
+
-moz-osx-font-smoothing: grayscale;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@keyframes background-gradient-animation {
|
|
19
|
+
0% {
|
|
20
|
+
background-position: 0% 50%;
|
|
21
|
+
}
|
|
22
|
+
50% {
|
|
23
|
+
background-position: 100% 50%;
|
|
24
|
+
}
|
|
25
|
+
100% {
|
|
26
|
+
background-position: 0% 50%;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#root {
|
|
31
|
+
width: 100dvw;
|
|
32
|
+
height: 100dvh;
|
|
33
|
+
overflow: auto;
|
|
34
|
+
background: linear-gradient(
|
|
35
|
+
-45deg,
|
|
36
|
+
var(--template-gradient-color-1),
|
|
37
|
+
var(--template-gradient-color-2),
|
|
38
|
+
var(--template-gradient-color-3)
|
|
39
|
+
);
|
|
40
|
+
background-size: 400% 400%;
|
|
41
|
+
animation: background-gradient-animation 20s ease infinite;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
* {
|
|
45
|
+
scrollbar-width: thin;
|
|
46
|
+
scrollbar-color: var(--color-slate-600) transparent;
|
|
47
|
+
outline: none;
|
|
48
|
+
transition:
|
|
49
|
+
color 0.25s ease,
|
|
50
|
+
background-color 0.25s ease,
|
|
51
|
+
border-color 0.25s ease,
|
|
52
|
+
opacity 0.25s ease,
|
|
53
|
+
outline-color 0.25s ease;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
main {
|
|
57
|
+
[data-link],
|
|
58
|
+
a {
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
color: var(--template-focus-color-400);
|
|
61
|
+
&:hover {
|
|
62
|
+
color: var(--template-focus-color-300);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
*[data-focus] {
|
|
68
|
+
border: 2px solid transparent;
|
|
69
|
+
&:focus {
|
|
70
|
+
border: 2px solid var(--template-focus-color-300);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
*[data-focus-within] {
|
|
75
|
+
border: 2px solid transparent;
|
|
76
|
+
&:focus-within {
|
|
77
|
+
border: 2px solid var(--template-focus-color-300);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&[data-focus-within="bold"] {
|
|
81
|
+
border-color: var(--template-focus-color-600);
|
|
82
|
+
&:focus-within {
|
|
83
|
+
border: 2px solid var(--template-focus-color-300);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
code {
|
|
89
|
+
color: rgba(255, 255, 255, 0.8);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.tok-comment {
|
|
93
|
+
color: var(--color-slate-500);
|
|
94
|
+
}
|
|
95
|
+
.tok-definition {
|
|
96
|
+
}
|
|
97
|
+
.tok-local {
|
|
98
|
+
}
|
|
99
|
+
.tok-keyword {
|
|
100
|
+
color: var(--color-pink-400);
|
|
101
|
+
}
|
|
102
|
+
.tok-meta {
|
|
103
|
+
}
|
|
104
|
+
.tok-number {
|
|
105
|
+
}
|
|
106
|
+
.tok-operator {
|
|
107
|
+
color: var(--color-slate-400);
|
|
108
|
+
}
|
|
109
|
+
.tok-propertyName {
|
|
110
|
+
color: var(--color-sky-300);
|
|
111
|
+
}
|
|
112
|
+
.tok-punctuation {
|
|
113
|
+
color: var(--color-slate-400);
|
|
114
|
+
}
|
|
115
|
+
.tok-string {
|
|
116
|
+
color: var(--color-teal-300);
|
|
117
|
+
}
|
|
118
|
+
.tok-string2 {
|
|
119
|
+
color: var(--color-sky-300);
|
|
120
|
+
}
|
|
121
|
+
.tok-typeName {
|
|
122
|
+
color: var(--color-pink-400);
|
|
123
|
+
}
|
|
124
|
+
.tok-variableName:not(.tok-definition) {
|
|
125
|
+
}
|
|
126
|
+
.tok-variableName2 {
|
|
127
|
+
}
|