c063 1.4.3 → 1.4.5
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/README.md +3 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +16 -0
- package/dist/utils/parser.js +62 -12
- package/package.json +1 -1
- package/public/icon.png +0 -0
- package/src/index.ts +4 -6
- package/src/types/index.ts +1 -1
- package/src/utils/index.tsx +16 -0
- package/src/utils/parser.tsx +80 -14
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
import c063, { whiteSpace } from
|
|
1
|
+
export * from "./types";
|
|
2
|
+
export * from "./components";
|
|
3
|
+
import c063, { whiteSpace, extractTokenContent } from "./utils";
|
|
4
4
|
export default c063;
|
|
5
|
-
export { whiteSpace };
|
|
5
|
+
export { whiteSpace, extractTokenContent };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
import c063, { whiteSpace } from
|
|
1
|
+
export * from "./types";
|
|
2
|
+
export * from "./components";
|
|
3
|
+
import c063, { whiteSpace, extractTokenContent } from "./utils";
|
|
4
4
|
export default c063;
|
|
5
|
-
export { whiteSpace };
|
|
5
|
+
export { whiteSpace, extractTokenContent };
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import { CodeTokenBuilder, CodeTokenProps, CodeTokenType } from "../types";
|
|
2
3
|
/**
|
|
3
4
|
* 語法 token 的建構器集合,每個 key 對應一種語法類型(如 `keyword-blue`, `string`, `comment` 等),
|
|
@@ -26,4 +27,5 @@ declare const c063: Record<CodeTokenType, CodeTokenBuilder>;
|
|
|
26
27
|
* tokens.push(whiteSpace(2)); // -> { type: "default", children: " " }
|
|
27
28
|
*/
|
|
28
29
|
export declare const whiteSpace: (count?: number) => CodeTokenProps<"span">;
|
|
30
|
+
export declare const extractTokenContent: <T extends React.ElementType>(token: CodeTokenProps<T>) => string;
|
|
29
31
|
export default c063;
|
package/dist/utils/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
/**
|
|
2
3
|
* 語法 token 的建構器集合,每個 key 對應一種語法類型(如 `keyword-blue`, `string`, `comment` 等),
|
|
3
4
|
* 透過 Proxy 生成對應的 `CodeTokenBuilder`。
|
|
@@ -36,4 +37,19 @@ const c063 = new Proxy({}, {
|
|
|
36
37
|
* tokens.push(whiteSpace(2)); // -> { type: "default", children: " " }
|
|
37
38
|
*/
|
|
38
39
|
export const whiteSpace = (count = 1) => c063.default(" ".repeat(count));
|
|
40
|
+
export const extractTokenContent = (token) => {
|
|
41
|
+
const _extract = (children) => {
|
|
42
|
+
if (typeof children === "string")
|
|
43
|
+
return children;
|
|
44
|
+
if (typeof children === "number")
|
|
45
|
+
return children.toString();
|
|
46
|
+
if (Array.isArray(children))
|
|
47
|
+
return children.map(_extract).join("");
|
|
48
|
+
if (React.isValidElement(children)) {
|
|
49
|
+
return _extract(children.props.children);
|
|
50
|
+
}
|
|
51
|
+
return ""; // 如果 children 是 null 或 undefined,則返回空字串
|
|
52
|
+
};
|
|
53
|
+
return _extract(token.children);
|
|
54
|
+
};
|
|
39
55
|
export default c063;
|
package/dist/utils/parser.js
CHANGED
|
@@ -2,25 +2,75 @@ import { parsableLanguages } from "../libs";
|
|
|
2
2
|
const isParsableLanguage = (lang) => {
|
|
3
3
|
return parsableLanguages.includes(lang);
|
|
4
4
|
};
|
|
5
|
+
const regexMap = {
|
|
6
|
+
javascript: [
|
|
7
|
+
{ type: "comment", regex: /^\/\/.*/ },
|
|
8
|
+
{ type: "string", regex: /^(['"])(?:\\.|[^\\])*?\1/ },
|
|
9
|
+
{ type: "number", regex: /^\d+(\.\d+)?/ },
|
|
10
|
+
{
|
|
11
|
+
type: "keyword1",
|
|
12
|
+
regex: /^(?:const|let|var|function|class|interface|type|enum|extends|implements|new|this|super)\b/,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
type: "keyword2",
|
|
16
|
+
regex: /^(?:return|import|from|for|while|as|await|async|export|if|else|switch|case|break|continue)\b/,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
type: "type",
|
|
20
|
+
regex: /^(?:string|number|boolean|void|any|unknown|never|Record|Array)\b/,
|
|
21
|
+
},
|
|
22
|
+
{ type: "operator", regex: /^(===|!==|==|!=|<=|>=|=>|<|>|\+|-|\*|\/|=)/ },
|
|
23
|
+
{ type: "constant", regex: /^[A-Z_][A-Z0-9_]*/ },
|
|
24
|
+
{ type: "variable", regex: /^[a-zA-Z_$][a-zA-Z0-9_$]*/ },
|
|
25
|
+
{ type: "default", regex: /^[:;,.?]/ },
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
const bracketLevels = { "(": 1, "{": 1, "[": 1, ")": -1, "}": -1, "]": -1 };
|
|
5
29
|
// 解析單行 CodeTokenProps
|
|
6
30
|
const _parseTokenLine = (line, lang) => {
|
|
7
31
|
if (!isParsableLanguage(lang))
|
|
8
32
|
return [];
|
|
9
33
|
const result = [];
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
34
|
+
let remaining = line;
|
|
35
|
+
let bracketDepth = 0;
|
|
36
|
+
const bracketLevels = { "(": 1, "{": 1, "[": 1, ")": -1, "}": -1, "]": -1 };
|
|
37
|
+
const bracketRegex = /^[\[\]{}()]/;
|
|
38
|
+
const rules = regexMap[lang];
|
|
39
|
+
outer: while (remaining.length > 0) {
|
|
40
|
+
const whitespaceMatch = remaining.match(/^\s+/);
|
|
41
|
+
if (whitespaceMatch) {
|
|
42
|
+
result.push({ type: "default", children: whitespaceMatch[0] });
|
|
43
|
+
remaining = remaining.slice(whitespaceMatch[0].length);
|
|
44
|
+
continue outer;
|
|
45
|
+
}
|
|
46
|
+
// 括號處理(根據深度標記 brackets1 / 2 / 3)
|
|
47
|
+
const bracketMatch = remaining.match(bracketRegex);
|
|
48
|
+
if (bracketMatch) {
|
|
49
|
+
const bracket = bracketMatch[0];
|
|
50
|
+
bracketDepth += bracketLevels[bracket] || 0;
|
|
51
|
+
const depth = Math.max(1, Math.min(3, Math.abs(bracketDepth)));
|
|
52
|
+
result.push({
|
|
53
|
+
type: `brackets${depth}`,
|
|
54
|
+
children: bracket,
|
|
55
|
+
});
|
|
56
|
+
remaining = remaining.slice(1);
|
|
57
|
+
continue outer;
|
|
58
|
+
}
|
|
59
|
+
// 用 regexMap 中的每個語法規則嘗試匹配
|
|
60
|
+
for (const { type, regex } of rules) {
|
|
61
|
+
const match = remaining.match(regex);
|
|
62
|
+
if (match) {
|
|
63
|
+
result.push({ type, children: match[0] });
|
|
64
|
+
remaining = remaining.slice(match[0].length);
|
|
65
|
+
continue outer;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// 如果沒有任何規則匹配,就當作 default 一個字元
|
|
69
|
+
result.push({ type: "default", children: remaining[0] });
|
|
70
|
+
remaining = remaining.slice(1);
|
|
71
|
+
}
|
|
13
72
|
return result;
|
|
14
73
|
};
|
|
15
74
|
const parseTokenLines = new Proxy({}, {
|
|
16
75
|
get: (_, prop) => (content) => content.split("\n").map((line) => _parseTokenLine(line, prop)),
|
|
17
76
|
});
|
|
18
|
-
// parseTokenLines.javascript(
|
|
19
|
-
// `const FanYu = {
|
|
20
|
-
// name: '范余振富',
|
|
21
|
-
// nickname: '飯魚',
|
|
22
|
-
// age: 19, // <-點看看🤫
|
|
23
|
-
// hobbies: ['寫程式', '繪畫'],
|
|
24
|
-
// skills: ['TypeScript', 'React', 'Python'],
|
|
25
|
-
// } as const;`
|
|
26
|
-
// );
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "c063",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.5",
|
|
5
5
|
"description": "A React component for displaying code snippets with syntax highlighting.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
package/public/icon.png
ADDED
|
Binary file
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from "./types";
|
|
2
|
+
export * from "./components";
|
|
3
3
|
|
|
4
|
-
import c063, { whiteSpace }from
|
|
4
|
+
import c063, { whiteSpace, extractTokenContent } from "./utils";
|
|
5
5
|
export default c063;
|
|
6
|
-
export {
|
|
7
|
-
whiteSpace
|
|
8
|
-
}
|
|
6
|
+
export { whiteSpace, extractTokenContent };
|
package/src/types/index.ts
CHANGED
|
@@ -32,7 +32,7 @@ export type CodeTokenType =
|
|
|
32
32
|
| "type" // 類型定義:type、interface、enum
|
|
33
33
|
| "variable" // 變數名、函式名、類別名等識別符號
|
|
34
34
|
| "constant" // 常數值:例如 enum 值、靜態屬性
|
|
35
|
-
| `brackets${1 | 2 | 3}` //
|
|
35
|
+
| `brackets${1 | 2 | 3}` // 括號,多層巢狀不同樣式:{[()]}
|
|
36
36
|
| "operator" // 運算符號:=、+、*、===、<、>= 等
|
|
37
37
|
| "default"; // 其他符號:, ; . ? ! 等
|
|
38
38
|
|
package/src/utils/index.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import { CodeTokenBuilder, CodeTokenProps, CodeTokenType } from "../types";
|
|
2
3
|
/**
|
|
3
4
|
* 語法 token 的建構器集合,每個 key 對應一種語法類型(如 `keyword-blue`, `string`, `comment` 等),
|
|
@@ -46,4 +47,19 @@ const c063 = new Proxy(
|
|
|
46
47
|
export const whiteSpace = (count: number = 1): CodeTokenProps<"span"> =>
|
|
47
48
|
c063.default(" ".repeat(count));
|
|
48
49
|
|
|
50
|
+
export const extractTokenContent = <T extends React.ElementType>(
|
|
51
|
+
token: CodeTokenProps<T>
|
|
52
|
+
): string => {
|
|
53
|
+
const _extract = (children: React.ReactNode): string => {
|
|
54
|
+
if (typeof children === "string") return children;
|
|
55
|
+
if (typeof children === "number") return children.toString();
|
|
56
|
+
if (Array.isArray(children)) return children.map(_extract).join("");
|
|
57
|
+
if (React.isValidElement(children)) {
|
|
58
|
+
return _extract((children as React.JSX.Element).props.children);
|
|
59
|
+
}
|
|
60
|
+
return ""; // 如果 children 是 null 或 undefined,則返回空字串
|
|
61
|
+
};
|
|
62
|
+
return _extract(token.children);
|
|
63
|
+
};
|
|
64
|
+
|
|
49
65
|
export default c063;
|
package/src/utils/parser.tsx
CHANGED
|
@@ -1,22 +1,98 @@
|
|
|
1
1
|
import { parsableLanguages } from "../libs";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
CodeTokenProps,
|
|
4
|
+
CodeTokenType,
|
|
5
|
+
ParsableLanguage,
|
|
6
|
+
} from "../types/index";
|
|
3
7
|
|
|
4
8
|
const isParsableLanguage = (lang: string): lang is ParsableLanguage => {
|
|
5
9
|
return parsableLanguages.includes(lang as ParsableLanguage);
|
|
6
10
|
};
|
|
7
11
|
|
|
12
|
+
const regexMap: Record<
|
|
13
|
+
ParsableLanguage,
|
|
14
|
+
{ type: CodeTokenType; regex: RegExp }[]
|
|
15
|
+
> = {
|
|
16
|
+
javascript: [
|
|
17
|
+
{ type: "comment", regex: /^\/\/.*/ },
|
|
18
|
+
{ type: "string", regex: /^(['"])(?:\\.|[^\\])*?\1/ },
|
|
19
|
+
{ type: "number", regex: /^\d+(\.\d+)?/ },
|
|
20
|
+
{
|
|
21
|
+
type: "keyword1",
|
|
22
|
+
regex:
|
|
23
|
+
/^(?:const|let|var|function|class|interface|type|enum|extends|implements|new|this|super)\b/,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: "keyword2",
|
|
27
|
+
regex:
|
|
28
|
+
/^(?:return|import|from|for|while|as|await|async|export|if|else|switch|case|break|continue)\b/,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: "type",
|
|
32
|
+
regex: /^(?:string|number|boolean|void|any|unknown|never|Record|Array)\b/,
|
|
33
|
+
},
|
|
34
|
+
{ type: "operator", regex: /^(===|!==|==|!=|<=|>=|=>|<|>|\+|-|\*|\/|=)/ },
|
|
35
|
+
{ type: "constant", regex: /^[A-Z_][A-Z0-9_]*/ },
|
|
36
|
+
{ type: "variable", regex: /^[a-zA-Z_$][a-zA-Z0-9_$]*/ },
|
|
37
|
+
{ type: "default", regex: /^[:;,.?]/ },
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
const bracketLevels = { "(": 1, "{": 1, "[": 1, ")": -1, "}": -1, "]": -1 };
|
|
8
41
|
// 解析單行 CodeTokenProps
|
|
9
42
|
const _parseTokenLine = (
|
|
10
43
|
line: string,
|
|
11
44
|
lang: ParsableLanguage
|
|
12
45
|
): CodeTokenProps<"span">[] => {
|
|
13
46
|
if (!isParsableLanguage(lang)) return [];
|
|
47
|
+
|
|
14
48
|
const result: CodeTokenProps<"span">[] = [];
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
49
|
+
let remaining = line;
|
|
50
|
+
let bracketDepth = 0;
|
|
51
|
+
|
|
52
|
+
const bracketLevels = { "(": 1, "{": 1, "[": 1, ")": -1, "}": -1, "]": -1 };
|
|
53
|
+
const bracketRegex = /^[\[\]{}()]/;
|
|
54
|
+
const rules = regexMap[lang];
|
|
55
|
+
|
|
56
|
+
outer: while (remaining.length > 0) {
|
|
57
|
+
const whitespaceMatch = remaining.match(/^\s+/);
|
|
58
|
+
if (whitespaceMatch) {
|
|
59
|
+
result.push({ type: "default", children: whitespaceMatch[0] });
|
|
60
|
+
remaining = remaining.slice(whitespaceMatch[0].length);
|
|
61
|
+
continue outer;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 括號處理(根據深度標記 brackets1 / 2 / 3)
|
|
65
|
+
const bracketMatch = remaining.match(bracketRegex);
|
|
66
|
+
if (bracketMatch) {
|
|
67
|
+
const bracket = bracketMatch[0];
|
|
68
|
+
bracketDepth += bracketLevels[bracket as keyof typeof bracketLevels] || 0;
|
|
69
|
+
const depth = Math.max(1, Math.min(3, Math.abs(bracketDepth)));
|
|
70
|
+
result.push({
|
|
71
|
+
type: `brackets${depth}` as CodeTokenType,
|
|
72
|
+
children: bracket,
|
|
73
|
+
});
|
|
74
|
+
remaining = remaining.slice(1);
|
|
75
|
+
continue outer;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 用 regexMap 中的每個語法規則嘗試匹配
|
|
79
|
+
for (const { type, regex } of rules) {
|
|
80
|
+
const match = remaining.match(regex);
|
|
81
|
+
if (match) {
|
|
82
|
+
result.push({ type, children: match[0] });
|
|
83
|
+
remaining = remaining.slice(match[0].length);
|
|
84
|
+
continue outer;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 如果沒有任何規則匹配,就當作 default 一個字元
|
|
89
|
+
result.push({ type: "default", children: remaining[0] });
|
|
90
|
+
remaining = remaining.slice(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
18
93
|
return result;
|
|
19
94
|
};
|
|
95
|
+
|
|
20
96
|
const parseTokenLines = new Proxy(
|
|
21
97
|
{},
|
|
22
98
|
{
|
|
@@ -26,13 +102,3 @@ const parseTokenLines = new Proxy(
|
|
|
26
102
|
content.split("\n").map((line) => _parseTokenLine(line, prop)),
|
|
27
103
|
}
|
|
28
104
|
) as Record<ParsableLanguage, (content: string) => CodeTokenProps<"span">[][]>;
|
|
29
|
-
|
|
30
|
-
// parseTokenLines.javascript(
|
|
31
|
-
// `const FanYu = {
|
|
32
|
-
// name: '范余振富',
|
|
33
|
-
// nickname: '飯魚',
|
|
34
|
-
// age: 19, // <-點看看🤫
|
|
35
|
-
// hobbies: ['寫程式', '繪畫'],
|
|
36
|
-
// skills: ['TypeScript', 'React', 'Python'],
|
|
37
|
-
// } as const;`
|
|
38
|
-
// );
|