@speclynx/apidom-parser-adapter-json 4.0.3 → 4.0.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/CHANGELOG.md +10 -0
- package/package.json +7 -6
- package/src/adapter.ts +0 -80
- package/src/media-types.ts +0 -17
- package/src/native/index.ts +0 -36
- package/src/tree-sitter/index.ts +0 -57
- package/src/tree-sitter/lexical-analysis/index.ts +0 -48
- package/src/tree-sitter/syntactic-analysis/index.ts +0 -350
- package/src/wasm.d.ts +0 -18
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [4.0.5](https://github.com/speclynx/apidom/compare/v4.0.4...v4.0.5) (2026-03-13)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @speclynx/apidom-parser-adapter-json
|
|
9
|
+
|
|
10
|
+
## [4.0.4](https://github.com/speclynx/apidom/compare/v4.0.3...v4.0.4) (2026-03-12)
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- **release:** override minimatch 10.2.3 to fix glob pattern regression in lerna publish ([#157](https://github.com/speclynx/apidom/issues/157)) ([c2d65a0](https://github.com/speclynx/apidom/commit/c2d65a06a2187e8563a9dc9db74ba27255450e0b)), closes [lerna/lerna#4305](https://github.com/lerna/lerna/issues/4305) [isaacs/minimatch#284](https://github.com/isaacs/minimatch/issues/284)
|
|
15
|
+
|
|
6
16
|
## [4.0.3](https://github.com/speclynx/apidom/compare/v4.0.2...v4.0.3) (2026-03-11)
|
|
7
17
|
|
|
8
18
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speclynx/apidom-parser-adapter-json",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.5",
|
|
4
4
|
"description": "Parser adapter for parsing JSON documents into base namespace.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"apidom",
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
"license": "Apache-2.0",
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@babel/runtime-corejs3": "^7.28.4",
|
|
61
|
-
"@speclynx/apidom-core": "4.0.
|
|
62
|
-
"@speclynx/apidom-datamodel": "4.0.
|
|
63
|
-
"@speclynx/apidom-error": "4.0.
|
|
61
|
+
"@speclynx/apidom-core": "4.0.5",
|
|
62
|
+
"@speclynx/apidom-datamodel": "4.0.5",
|
|
63
|
+
"@speclynx/apidom-error": "4.0.5",
|
|
64
64
|
"web-tree-sitter": "=0.26.6"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
@@ -68,7 +68,8 @@
|
|
|
68
68
|
"tree-sitter-json": "=0.24.8"
|
|
69
69
|
},
|
|
70
70
|
"files": [
|
|
71
|
-
"src
|
|
71
|
+
"src/**/*.mjs",
|
|
72
|
+
"src/**/*.cjs",
|
|
72
73
|
"dist/",
|
|
73
74
|
"types/apidom-parser-adapter-json.d.ts",
|
|
74
75
|
"LICENSES",
|
|
@@ -76,5 +77,5 @@
|
|
|
76
77
|
"README.md",
|
|
77
78
|
"CHANGELOG.md"
|
|
78
79
|
],
|
|
79
|
-
"gitHead": "
|
|
80
|
+
"gitHead": "5a85d2a832eeefb07d03760faa391b457447e966"
|
|
80
81
|
}
|
package/src/adapter.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { ParseResultElement, Namespace } from '@speclynx/apidom-datamodel';
|
|
2
|
-
import { UnsupportedOperationError } from '@speclynx/apidom-error';
|
|
3
|
-
|
|
4
|
-
import * as native from './native/index.ts';
|
|
5
|
-
import * as treeSitter from './tree-sitter/index.ts';
|
|
6
|
-
|
|
7
|
-
export type { JSONMediaTypes } from './media-types.ts';
|
|
8
|
-
export type { Tree } from './tree-sitter/index.ts';
|
|
9
|
-
|
|
10
|
-
export { lexicalAnalysis, syntacticAnalysis } from './tree-sitter/index.ts';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @public
|
|
14
|
-
*/
|
|
15
|
-
export { default as mediaTypes } from './media-types.ts';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @public
|
|
19
|
-
*/
|
|
20
|
-
export const namespace = new Namespace();
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @public
|
|
24
|
-
*/
|
|
25
|
-
export const detectionRegExp = treeSitter.detectionRegExp;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @public
|
|
29
|
-
*/
|
|
30
|
-
export interface ParseDetectOptions {
|
|
31
|
-
strict?: boolean;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @public
|
|
36
|
-
*/
|
|
37
|
-
export const detect = async (
|
|
38
|
-
source: string,
|
|
39
|
-
{ strict = false }: ParseDetectOptions = {},
|
|
40
|
-
): Promise<boolean> => {
|
|
41
|
-
if (strict) {
|
|
42
|
-
return native.detect(source);
|
|
43
|
-
}
|
|
44
|
-
return treeSitter.detect(source);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @public
|
|
49
|
-
*/
|
|
50
|
-
export interface ParseOptions {
|
|
51
|
-
sourceMap?: boolean;
|
|
52
|
-
style?: boolean;
|
|
53
|
-
strict?: boolean;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @public
|
|
58
|
-
*/
|
|
59
|
-
export const parse = async (
|
|
60
|
-
source: string,
|
|
61
|
-
{ sourceMap = false, style = false, strict = false }: ParseOptions = {},
|
|
62
|
-
): Promise<ParseResultElement> => {
|
|
63
|
-
if (strict && sourceMap) {
|
|
64
|
-
throw new UnsupportedOperationError(
|
|
65
|
-
'Cannot use sourceMap with strict parsing. Strict parsing does not support source maps.',
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (strict && style) {
|
|
70
|
-
throw new UnsupportedOperationError(
|
|
71
|
-
'Cannot use style with strict parsing. Strict parsing does not support style preservation.',
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (strict) {
|
|
76
|
-
return native.parse(source);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return treeSitter.parse(source, { sourceMap, style });
|
|
80
|
-
};
|
package/src/media-types.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { MediaTypes } from '@speclynx/apidom-core';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @public
|
|
5
|
-
*/
|
|
6
|
-
export class JSONMediaTypes extends MediaTypes<string> {
|
|
7
|
-
latest() {
|
|
8
|
-
return this[0];
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @public
|
|
14
|
-
*/
|
|
15
|
-
const mediaTypes = new JSONMediaTypes('application/json');
|
|
16
|
-
|
|
17
|
-
export default mediaTypes;
|
package/src/native/index.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { ParseResultElement, refract } from '@speclynx/apidom-datamodel';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @public
|
|
5
|
-
*/
|
|
6
|
-
export const detect = async (source: string): Promise<boolean> => {
|
|
7
|
-
if (source.trim().length === 0) {
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
JSON.parse(source);
|
|
13
|
-
return true;
|
|
14
|
-
} catch {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @public
|
|
21
|
-
*/
|
|
22
|
-
export const parse = async (source: string): Promise<ParseResultElement> => {
|
|
23
|
-
const parseResult = new ParseResultElement();
|
|
24
|
-
|
|
25
|
-
if (source.trim().length === 0) {
|
|
26
|
-
return parseResult;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let pojo: unknown = JSON.parse(source);
|
|
30
|
-
const element = refract(pojo);
|
|
31
|
-
pojo = null; // allow GC to reclaim POJO
|
|
32
|
-
element.classes.push('result');
|
|
33
|
-
parseResult.push(element);
|
|
34
|
-
|
|
35
|
-
return parseResult;
|
|
36
|
-
};
|
package/src/tree-sitter/index.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { ParseResultElement } from '@speclynx/apidom-datamodel';
|
|
2
|
-
import type { Tree } from 'web-tree-sitter';
|
|
3
|
-
|
|
4
|
-
import lexicalAnalysis from './lexical-analysis/index.ts';
|
|
5
|
-
import syntacticAnalysis from './syntactic-analysis/index.ts';
|
|
6
|
-
|
|
7
|
-
export type { Tree };
|
|
8
|
-
export { lexicalAnalysis, syntacticAnalysis };
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @public
|
|
12
|
-
*/
|
|
13
|
-
export const detectionRegExp =
|
|
14
|
-
// eslint-disable-next-line no-control-regex
|
|
15
|
-
/(?<true>^\s*true\s*$)|(?<false>^\s*false\s*$)|(?<null>^\s*null\s*$)|(?<number>^\s*\d+\s*$)|(?<object>^\s*{\s*)|(?<array>^\s*\[\s*)|(?<string>^\s*"(((?=\\)\\(["\\/bfnrt]|u[0-9a-fA-F]{4}))|[^"\\\x00-\x1F\x7F])*"\s*$)/;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @public
|
|
19
|
-
*/
|
|
20
|
-
export const detect = async (source: string): Promise<boolean> => {
|
|
21
|
-
if (!detectionRegExp.test(source)) {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
let cst: Tree | null = null;
|
|
26
|
-
try {
|
|
27
|
-
cst = await lexicalAnalysis(source);
|
|
28
|
-
return cst.rootNode.type !== 'ERROR';
|
|
29
|
-
} catch {
|
|
30
|
-
return false;
|
|
31
|
-
} finally {
|
|
32
|
-
cst?.delete();
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @public
|
|
38
|
-
*/
|
|
39
|
-
export interface ParseOptions {
|
|
40
|
-
sourceMap?: boolean;
|
|
41
|
-
style?: boolean;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @public
|
|
46
|
-
*/
|
|
47
|
-
export const parse = async (
|
|
48
|
-
source: string,
|
|
49
|
-
{ sourceMap = false, style = false }: ParseOptions = {},
|
|
50
|
-
): Promise<ParseResultElement> => {
|
|
51
|
-
const cst = await lexicalAnalysis(source);
|
|
52
|
-
try {
|
|
53
|
-
return syntacticAnalysis(cst, { sourceMap, style });
|
|
54
|
-
} finally {
|
|
55
|
-
cst.delete();
|
|
56
|
-
}
|
|
57
|
-
};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { Tree, Parser, Language } from 'web-tree-sitter';
|
|
2
|
-
import treeSitter from 'web-tree-sitter/web-tree-sitter.wasm';
|
|
3
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
4
|
-
import treeSitterJson from 'tree-sitter-json/tree-sitter-json.wasm';
|
|
5
|
-
import { ApiDOMError } from '@speclynx/apidom-error';
|
|
6
|
-
|
|
7
|
-
let parser: Parser | null = null;
|
|
8
|
-
let parserInitLock: Promise<Parser> | null = null;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Lexical Analysis of source string using WebTreeSitter.
|
|
12
|
-
* This is WebAssembly version of TreeSitters Lexical Analysis.
|
|
13
|
-
*
|
|
14
|
-
* Given JavaScript doesn't support true parallelism, this
|
|
15
|
-
* code should be as lazy as possible and temporal safety should be fine.
|
|
16
|
-
* @public
|
|
17
|
-
*/
|
|
18
|
-
const analyze = async (source: string): Promise<Tree> => {
|
|
19
|
-
if (parser === null && parserInitLock === null) {
|
|
20
|
-
// acquire lock
|
|
21
|
-
parserInitLock = Parser.init({
|
|
22
|
-
wasmBinary: treeSitter,
|
|
23
|
-
locateFile: (scriptName: string) => scriptName,
|
|
24
|
-
} as unknown as EmscriptenModule)
|
|
25
|
-
.then(() => Language.load(treeSitterJson))
|
|
26
|
-
.then((jsonLanguage) => {
|
|
27
|
-
const parserInstance = new Parser();
|
|
28
|
-
parserInstance.setLanguage(jsonLanguage);
|
|
29
|
-
return parserInstance;
|
|
30
|
-
})
|
|
31
|
-
.finally(() => {
|
|
32
|
-
// release lock
|
|
33
|
-
parserInitLock = null;
|
|
34
|
-
});
|
|
35
|
-
parser = await parserInitLock;
|
|
36
|
-
} else if (parser === null && parserInitLock !== null) {
|
|
37
|
-
// await for lock to be released if there is one
|
|
38
|
-
parser = await parserInitLock;
|
|
39
|
-
} else if (parser === null) {
|
|
40
|
-
throw new ApiDOMError(
|
|
41
|
-
'Error while initializing web-tree-sitter and loading tree-sitter-json grammar.',
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return parser.parse(source) as Tree;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export default analyze;
|
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
import { TreeCursor, Tree, Point } from 'web-tree-sitter';
|
|
2
|
-
import {
|
|
3
|
-
BooleanElement,
|
|
4
|
-
NullElement,
|
|
5
|
-
NumberElement,
|
|
6
|
-
ParseResultElement,
|
|
7
|
-
Element,
|
|
8
|
-
MemberElement,
|
|
9
|
-
ObjectElement,
|
|
10
|
-
ArrayElement,
|
|
11
|
-
StringElement,
|
|
12
|
-
AnnotationElement,
|
|
13
|
-
isPrimitiveElement,
|
|
14
|
-
} from '@speclynx/apidom-datamodel';
|
|
15
|
-
|
|
16
|
-
interface TransformContext {
|
|
17
|
-
sourceMap: boolean;
|
|
18
|
-
style: boolean;
|
|
19
|
-
indent: number;
|
|
20
|
-
annotations: AnnotationElement[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface CursorInfo {
|
|
24
|
-
type: string;
|
|
25
|
-
startPosition: Point;
|
|
26
|
-
endPosition: Point;
|
|
27
|
-
startIndex: number;
|
|
28
|
-
endIndex: number;
|
|
29
|
-
text: string;
|
|
30
|
-
isNamed: boolean;
|
|
31
|
-
isMissing: boolean;
|
|
32
|
-
hasError: boolean;
|
|
33
|
-
fieldName: string | null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const getCursorInfo = (cursor: TreeCursor): CursorInfo => ({
|
|
37
|
-
type: cursor.nodeType,
|
|
38
|
-
startPosition: cursor.startPosition,
|
|
39
|
-
endPosition: cursor.endPosition,
|
|
40
|
-
startIndex: cursor.startIndex,
|
|
41
|
-
endIndex: cursor.endIndex,
|
|
42
|
-
text: cursor.nodeText,
|
|
43
|
-
isNamed: cursor.nodeIsNamed,
|
|
44
|
-
isMissing: cursor.nodeIsMissing,
|
|
45
|
-
hasError: cursor.currentNode.hasError,
|
|
46
|
-
fieldName: cursor.currentFieldName,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const maybeAddSourceMap = (info: CursorInfo, element: Element, ctx: TransformContext): void => {
|
|
50
|
-
if (!ctx.sourceMap) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
element.startLine = info.startPosition.row;
|
|
55
|
-
element.startCharacter = info.startPosition.column;
|
|
56
|
-
element.startOffset = info.startIndex;
|
|
57
|
-
element.endLine = info.endPosition.row;
|
|
58
|
-
element.endCharacter = info.endPosition.column;
|
|
59
|
-
element.endOffset = info.endIndex;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// build json style object for an element
|
|
63
|
-
const buildJsonStyle = (
|
|
64
|
-
ctx: TransformContext,
|
|
65
|
-
extras?: Record<string, unknown>,
|
|
66
|
-
): Record<string, unknown> => {
|
|
67
|
-
const jsonStyle: Record<string, unknown> = { indent: ctx.indent };
|
|
68
|
-
if (extras) Object.assign(jsonStyle, extras);
|
|
69
|
-
return { json: jsonStyle };
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// detect indent from an object's first pair child position
|
|
73
|
-
// called during transformChildren when we encounter the first pair
|
|
74
|
-
const detectIndent = (objectColumn: number, firstPairColumn: number): number => {
|
|
75
|
-
const diff = firstPairColumn - objectColumn;
|
|
76
|
-
return diff > 0 ? diff : 2;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
type Transformer = (cursor: TreeCursor, ctx: TransformContext) => Element | null;
|
|
80
|
-
type TransformerMap = Record<string, Transformer>;
|
|
81
|
-
|
|
82
|
-
const transform = (
|
|
83
|
-
cursor: TreeCursor,
|
|
84
|
-
transformerMap: TransformerMap,
|
|
85
|
-
ctx: TransformContext,
|
|
86
|
-
): Element | null => {
|
|
87
|
-
const info = getCursorInfo(cursor);
|
|
88
|
-
|
|
89
|
-
// Handle missing anonymous literals
|
|
90
|
-
if (!info.isNamed && info.isMissing) {
|
|
91
|
-
const value = info.type || info.text;
|
|
92
|
-
const message = `(Missing ${value})`;
|
|
93
|
-
const element = new AnnotationElement(message);
|
|
94
|
-
element.classes.push('warning');
|
|
95
|
-
maybeAddSourceMap(info, element, ctx);
|
|
96
|
-
ctx.annotations.push(element);
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const transformer = transformerMap[info.type];
|
|
101
|
-
if (!transformer) {
|
|
102
|
-
return null; // remove unrecognized nodes
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return transformer(cursor, ctx);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const transformChildren = (
|
|
109
|
-
cursor: TreeCursor,
|
|
110
|
-
transformerMap: TransformerMap,
|
|
111
|
-
ctx: TransformContext,
|
|
112
|
-
): Element[] => {
|
|
113
|
-
const results: Element[] = [];
|
|
114
|
-
|
|
115
|
-
if (cursor.gotoFirstChild()) {
|
|
116
|
-
do {
|
|
117
|
-
const transformed = transform(cursor, transformerMap, ctx);
|
|
118
|
-
if (transformed !== null) {
|
|
119
|
-
results.push(transformed);
|
|
120
|
-
}
|
|
121
|
-
} while (cursor.gotoNextSibling());
|
|
122
|
-
cursor.gotoParent();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return results;
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const createTransformers = (transformerMap: TransformerMap): TransformerMap => ({
|
|
129
|
-
document(cursor: TreeCursor, ctx: TransformContext): ParseResultElement {
|
|
130
|
-
const info = getCursorInfo(cursor);
|
|
131
|
-
const element = new ParseResultElement();
|
|
132
|
-
maybeAddSourceMap(info, element, ctx);
|
|
133
|
-
|
|
134
|
-
// Transform children
|
|
135
|
-
const children = transformChildren(cursor, transformerMap, ctx);
|
|
136
|
-
for (const child of children) {
|
|
137
|
-
element.push(child);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Mark first non-Annotation element as result
|
|
141
|
-
// @ts-ignore
|
|
142
|
-
const elements = element.findElements(isPrimitiveElement);
|
|
143
|
-
if (elements.length > 0) {
|
|
144
|
-
elements[0].classes.push('result');
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Add collected annotations
|
|
148
|
-
for (const annotation of ctx.annotations) {
|
|
149
|
-
element.push(annotation);
|
|
150
|
-
}
|
|
151
|
-
ctx.annotations = [];
|
|
152
|
-
|
|
153
|
-
return element;
|
|
154
|
-
},
|
|
155
|
-
|
|
156
|
-
object(cursor: TreeCursor, ctx: TransformContext): ObjectElement {
|
|
157
|
-
const info = getCursorInfo(cursor);
|
|
158
|
-
const element = new ObjectElement();
|
|
159
|
-
maybeAddSourceMap(info, element, ctx);
|
|
160
|
-
|
|
161
|
-
// Detect indent from first pair if style is enabled and not yet detected
|
|
162
|
-
if (ctx.style && ctx.indent === 0) {
|
|
163
|
-
if (cursor.gotoFirstChild()) {
|
|
164
|
-
do {
|
|
165
|
-
if (cursor.nodeType === 'pair') {
|
|
166
|
-
ctx.indent = detectIndent(info.startPosition.column, cursor.startPosition.column);
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
} while (cursor.gotoNextSibling());
|
|
170
|
-
cursor.gotoParent();
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Transform children (pairs)
|
|
175
|
-
const children = transformChildren(cursor, transformerMap, ctx);
|
|
176
|
-
for (const child of children) {
|
|
177
|
-
element.push(child);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (ctx.style) {
|
|
181
|
-
element.style = buildJsonStyle(ctx);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return element;
|
|
185
|
-
},
|
|
186
|
-
|
|
187
|
-
array(cursor: TreeCursor, ctx: TransformContext): ArrayElement {
|
|
188
|
-
const info = getCursorInfo(cursor);
|
|
189
|
-
const element = new ArrayElement();
|
|
190
|
-
maybeAddSourceMap(info, element, ctx);
|
|
191
|
-
|
|
192
|
-
// Transform children
|
|
193
|
-
const children = transformChildren(cursor, transformerMap, ctx);
|
|
194
|
-
for (const child of children) {
|
|
195
|
-
element.push(child);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (ctx.style) {
|
|
199
|
-
element.style = buildJsonStyle(ctx);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return element;
|
|
203
|
-
},
|
|
204
|
-
|
|
205
|
-
pair(cursor: TreeCursor, ctx: TransformContext): MemberElement {
|
|
206
|
-
const info = getCursorInfo(cursor);
|
|
207
|
-
|
|
208
|
-
let key: Element | null = null;
|
|
209
|
-
let value: Element | null = null;
|
|
210
|
-
|
|
211
|
-
// Find key and value by field name
|
|
212
|
-
if (cursor.gotoFirstChild()) {
|
|
213
|
-
do {
|
|
214
|
-
const fieldName = cursor.currentFieldName;
|
|
215
|
-
if (fieldName === 'key') {
|
|
216
|
-
key = transform(cursor, transformerMap, ctx);
|
|
217
|
-
} else if (fieldName === 'value') {
|
|
218
|
-
value = transform(cursor, transformerMap, ctx);
|
|
219
|
-
} else if (cursor.nodeType === 'ERROR') {
|
|
220
|
-
// Process error nodes
|
|
221
|
-
transform(cursor, transformerMap, ctx);
|
|
222
|
-
}
|
|
223
|
-
} while (cursor.gotoNextSibling());
|
|
224
|
-
cursor.gotoParent();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const element = new MemberElement(key ?? new StringElement(''), value ?? new StringElement(''));
|
|
228
|
-
maybeAddSourceMap(info, element, ctx);
|
|
229
|
-
|
|
230
|
-
return element;
|
|
231
|
-
},
|
|
232
|
-
|
|
233
|
-
string(cursor: TreeCursor, ctx: TransformContext): StringElement {
|
|
234
|
-
const info = getCursorInfo(cursor);
|
|
235
|
-
let element: StringElement;
|
|
236
|
-
|
|
237
|
-
try {
|
|
238
|
-
element = new StringElement(JSON.parse(info.text));
|
|
239
|
-
} catch (error: unknown) {
|
|
240
|
-
element = new StringElement(info.text);
|
|
241
|
-
if (error instanceof Error) {
|
|
242
|
-
element.meta.set('jsonParse', {
|
|
243
|
-
isError: true,
|
|
244
|
-
errorType: error.name,
|
|
245
|
-
errorMessage: error.message,
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (ctx.style) {
|
|
251
|
-
element.style = buildJsonStyle(ctx);
|
|
252
|
-
}
|
|
253
|
-
maybeAddSourceMap(info, element, ctx);
|
|
254
|
-
return element;
|
|
255
|
-
},
|
|
256
|
-
|
|
257
|
-
number(cursor: TreeCursor, ctx: TransformContext): NumberElement {
|
|
258
|
-
const info = getCursorInfo(cursor);
|
|
259
|
-
const element = new NumberElement(Number(info.text));
|
|
260
|
-
if (ctx.style) {
|
|
261
|
-
element.style = buildJsonStyle(ctx, { rawContent: info.text });
|
|
262
|
-
}
|
|
263
|
-
maybeAddSourceMap(info, element, ctx);
|
|
264
|
-
return element;
|
|
265
|
-
},
|
|
266
|
-
|
|
267
|
-
null(cursor: TreeCursor, ctx: TransformContext): NullElement {
|
|
268
|
-
const info = getCursorInfo(cursor);
|
|
269
|
-
const element = new NullElement();
|
|
270
|
-
if (ctx.style) {
|
|
271
|
-
element.style = buildJsonStyle(ctx);
|
|
272
|
-
}
|
|
273
|
-
maybeAddSourceMap(info, element, ctx);
|
|
274
|
-
return element;
|
|
275
|
-
},
|
|
276
|
-
|
|
277
|
-
true(cursor: TreeCursor, ctx: TransformContext): BooleanElement {
|
|
278
|
-
const info = getCursorInfo(cursor);
|
|
279
|
-
const element = new BooleanElement(true);
|
|
280
|
-
if (ctx.style) {
|
|
281
|
-
element.style = buildJsonStyle(ctx);
|
|
282
|
-
}
|
|
283
|
-
maybeAddSourceMap(info, element, ctx);
|
|
284
|
-
return element;
|
|
285
|
-
},
|
|
286
|
-
|
|
287
|
-
false(cursor: TreeCursor, ctx: TransformContext): BooleanElement {
|
|
288
|
-
const info = getCursorInfo(cursor);
|
|
289
|
-
const element = new BooleanElement(false);
|
|
290
|
-
if (ctx.style) {
|
|
291
|
-
element.style = buildJsonStyle(ctx);
|
|
292
|
-
}
|
|
293
|
-
maybeAddSourceMap(info, element, ctx);
|
|
294
|
-
return element;
|
|
295
|
-
},
|
|
296
|
-
|
|
297
|
-
ERROR(cursor: TreeCursor, ctx: TransformContext): ParseResultElement | null {
|
|
298
|
-
const info = getCursorInfo(cursor);
|
|
299
|
-
const isUnexpected = !info.hasError;
|
|
300
|
-
const message = isUnexpected ? `(Unexpected ${info.text})` : `(Error ${info.text})`;
|
|
301
|
-
const element = new AnnotationElement(message);
|
|
302
|
-
|
|
303
|
-
element.classes.push('error');
|
|
304
|
-
maybeAddSourceMap(info, element, ctx);
|
|
305
|
-
|
|
306
|
-
ctx.annotations.push(element);
|
|
307
|
-
|
|
308
|
-
return null;
|
|
309
|
-
},
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
// Create the transformers map with self-reference for recursion
|
|
313
|
-
const transformers: TransformerMap = {};
|
|
314
|
-
Object.assign(transformers, createTransformers(transformers));
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Syntactic analysis translates TreeSitter CST directly into ApiDOM.
|
|
318
|
-
*
|
|
319
|
-
* Direct transformation from TreeSitter CST using TreeSitter cursor.
|
|
320
|
-
* TreeSitter cursor is a stateful object that allows us to walk syntax tree
|
|
321
|
-
* containing large number of nodes with maximum efficiency.
|
|
322
|
-
*
|
|
323
|
-
* Single pass transformation from CST to ApiDOM.
|
|
324
|
-
* @public
|
|
325
|
-
*/
|
|
326
|
-
const analyze = (cst: Tree, { sourceMap = false, style = false } = {}): ParseResultElement => {
|
|
327
|
-
const cursor = cst.walk();
|
|
328
|
-
|
|
329
|
-
const ctx: TransformContext = {
|
|
330
|
-
sourceMap,
|
|
331
|
-
style,
|
|
332
|
-
indent: 0,
|
|
333
|
-
annotations: [],
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
const result = transform(cursor, transformers, ctx);
|
|
337
|
-
|
|
338
|
-
// Handle case where there's no document, only errors
|
|
339
|
-
if (result === null) {
|
|
340
|
-
const parseResult = new ParseResultElement();
|
|
341
|
-
for (const annotation of ctx.annotations) {
|
|
342
|
-
parseResult.push(annotation);
|
|
343
|
-
}
|
|
344
|
-
return parseResult;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
return result as ParseResultElement;
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
export default analyze;
|
package/src/wasm.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TypeScript module declarations for WebAssembly (.wasm) files.
|
|
3
|
-
* This allows importing .wasm files as modules without @ts-ignore.
|
|
4
|
-
*/
|
|
5
|
-
declare module '*.wasm' {
|
|
6
|
-
const content: string;
|
|
7
|
-
export default content;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
declare module 'web-tree-sitter/tree-sitter.wasm' {
|
|
11
|
-
const content: string;
|
|
12
|
-
export default content;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
declare module 'tree-sitter-json/tree-sitter-json.wasm' {
|
|
16
|
-
const content: string;
|
|
17
|
-
export default content;
|
|
18
|
-
}
|