@mirascript/typed 0.1.62 → 0.1.63
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 +21 -2
- package/dist/json.d.ts +6 -1
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +174 -40
- package/dist/json.js.map +1 -1
- package/dist/parser.d.ts +41 -5
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +58 -1
- package/dist/parser.js.map +1 -1
- package/dist/type.js +1535 -471
- package/dist/type.js.map +1 -1
- package/package.json +7 -5
- package/src/json.ts +180 -45
- package/src/parser.ts +115 -10
- package/src/type.peggy +185 -45
package/dist/type.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["
|
|
1
|
+
{"version":3,"sources":["../src/type.peggy"],"names":["$top_level_initializer","Start","UnionType","PostfixType","PrimaryType","TypeParamList","TypeArgList","FunctionType","NamedFunction","FunctionBody","FunctionParamList","FunctionParam","RecordType","FieldList","Field","FieldName","HexDigit","EscapeSequence","String","DoubleQuotePart","SingleQuotePart","BacktickPart","DoubleQuoteChar","SingleQuoteChar","BacktickChar","Identifier","_","$initializer"],"mappings":"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","file":"type.js"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mirascript/typed",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.63",
|
|
4
4
|
"author": "CloudPSS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Parse MiraScript type definitions and generate TypeScript types / JSON schemas.",
|
|
@@ -23,17 +23,19 @@
|
|
|
23
23
|
"./package.json": "./package.json"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@types/node": "^26.0.
|
|
26
|
+
"@types/node": "^26.0.1",
|
|
27
27
|
"ava": "^8.0.1",
|
|
28
|
-
"
|
|
28
|
+
"c8": "^11.0.0",
|
|
29
|
+
"js-yaml": "^5.2.0",
|
|
29
30
|
"peggy": "^5.1.0"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"@types/json-schema": "^7.0.15"
|
|
33
|
+
"@types/json-schema": "^7.0.15",
|
|
34
|
+
"@mirascript/constants": "~0.1.63"
|
|
33
35
|
},
|
|
34
36
|
"scripts": {
|
|
35
37
|
"build": "peggy src/type.peggy --multi-output ./dist --dts --format es --source-map && tsc",
|
|
36
38
|
"clean": "rimraf dist",
|
|
37
|
-
"test": "ava --no-worker-threads"
|
|
39
|
+
"test": "c8 ava --no-worker-threads"
|
|
38
40
|
}
|
|
39
41
|
}
|
package/src/json.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { JSONSchema7 } from 'json-schema';
|
|
2
|
-
import
|
|
2
|
+
import { REG_NUMBER } from '@mirascript/constants';
|
|
3
|
+
import type { KnownType, LiteralType, NamedType, TemplateType, Type } from './parser.js';
|
|
3
4
|
|
|
4
5
|
/** Converts a KnownType or NamedType into JSON Schema */
|
|
5
6
|
function string(type: KnownType | NamedType): JSONSchema7 {
|
|
@@ -29,55 +30,77 @@ function string(type: KnownType | NamedType): JSONSchema7 {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
/**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
33
|
+
/** Escapes a literal string for use in a regular expression */
|
|
34
|
+
function escapeRegex(value: string): string {
|
|
35
|
+
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const RE_ANY = '.*?';
|
|
39
|
+
const RE_NUMBER = REG_NUMBER.source;
|
|
40
|
+
const RE_BOOLEAN = 'true|false';
|
|
41
|
+
|
|
42
|
+
/** Converts a template interpolation part into a regex pattern fragment */
|
|
43
|
+
function templatePartPattern(part: Type, grouping: boolean): string {
|
|
44
|
+
let result: string;
|
|
45
|
+
if (typeof part === 'symbol') {
|
|
46
|
+
result = RE_ANY;
|
|
47
|
+
} else if (typeof part === 'string') {
|
|
48
|
+
switch (part) {
|
|
49
|
+
case 'number':
|
|
50
|
+
result = RE_NUMBER;
|
|
51
|
+
break;
|
|
52
|
+
case 'boolean':
|
|
53
|
+
result = RE_BOOLEAN;
|
|
54
|
+
break;
|
|
55
|
+
case 'nil':
|
|
56
|
+
case 'never':
|
|
57
|
+
result = '';
|
|
58
|
+
break;
|
|
59
|
+
case 'array':
|
|
60
|
+
case 'record':
|
|
61
|
+
case 'extern':
|
|
62
|
+
case 'any':
|
|
63
|
+
case 'unknown':
|
|
64
|
+
case 'string':
|
|
65
|
+
default:
|
|
66
|
+
result = RE_ANY;
|
|
67
|
+
break;
|
|
47
68
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (type.value !== undefined) {
|
|
54
|
-
return {
|
|
55
|
-
type: 'object',
|
|
56
|
-
additionalProperties: toJSONSchema(type.value),
|
|
57
|
-
};
|
|
69
|
+
} else if (part.kind === 'literal') {
|
|
70
|
+
if (typeof part.value === 'boolean') {
|
|
71
|
+
result = String(part.value);
|
|
72
|
+
} else {
|
|
73
|
+
result = escapeRegex(part.value);
|
|
58
74
|
}
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
75
|
+
} else if (part.kind === 'union') {
|
|
76
|
+
const patterns = new Set(part.types.map((p) => templatePartPattern(p, false)));
|
|
77
|
+
if (patterns.has(RE_ANY)) {
|
|
78
|
+
result = RE_ANY;
|
|
79
|
+
} else {
|
|
80
|
+
const hasEmpty = patterns.delete('');
|
|
81
|
+
result = Array.from(patterns).join('|');
|
|
82
|
+
if (hasEmpty) {
|
|
83
|
+
result = `(${result})?`;
|
|
65
84
|
}
|
|
66
85
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
properties,
|
|
70
|
-
};
|
|
71
|
-
if (required.length > 0) {
|
|
72
|
-
schema.required = required;
|
|
73
|
-
}
|
|
74
|
-
return schema;
|
|
75
|
-
}
|
|
76
|
-
if (type.kind === 'literal') {
|
|
77
|
-
return literal(type);
|
|
86
|
+
} else {
|
|
87
|
+
result = RE_ANY;
|
|
78
88
|
}
|
|
79
|
-
(
|
|
80
|
-
return
|
|
89
|
+
if (!grouping) return result;
|
|
90
|
+
if (result.startsWith('(')) return result;
|
|
91
|
+
return `(${result})`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Converts a TemplateType into a JSON Schema pattern */
|
|
95
|
+
function templatePattern(type: TemplateType): string {
|
|
96
|
+
return `^${type.parts
|
|
97
|
+
.map((p) => {
|
|
98
|
+
if (typeof p == 'object' && p.kind === 'literal' && typeof p.value === 'string') {
|
|
99
|
+
return templatePartPattern(p, false);
|
|
100
|
+
}
|
|
101
|
+
return templatePartPattern(p, true);
|
|
102
|
+
})
|
|
103
|
+
.join('')}$`;
|
|
81
104
|
}
|
|
82
105
|
|
|
83
106
|
/** Converts a LiteralType into JSON Schema */
|
|
@@ -103,3 +126,115 @@ function literalEnum(types: LiteralType[]): JSONSchema7 {
|
|
|
103
126
|
function isLiteralType(type: Type): type is LiteralType {
|
|
104
127
|
return typeof type === 'object' && type.kind === 'literal';
|
|
105
128
|
}
|
|
129
|
+
|
|
130
|
+
/** Options for toJSONSchema */
|
|
131
|
+
export interface ToJSONSchemaOptions {
|
|
132
|
+
/** When true, object schemas allow arbitrary additional properties */
|
|
133
|
+
loose?: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Converts a Type object into JSON Schema */
|
|
137
|
+
export function toJSONSchema(type: Type, options?: ToJSONSchemaOptions): JSONSchema7 {
|
|
138
|
+
const loose = options?.loose ?? false;
|
|
139
|
+
if (typeof type === 'symbol') {
|
|
140
|
+
return {};
|
|
141
|
+
}
|
|
142
|
+
if (typeof type === 'string') {
|
|
143
|
+
return string(type);
|
|
144
|
+
}
|
|
145
|
+
if (type.kind === 'array') {
|
|
146
|
+
return {
|
|
147
|
+
type: 'array',
|
|
148
|
+
items: toJSONSchema(type.element, options),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (type.kind === 'union') {
|
|
152
|
+
const anyOf = [];
|
|
153
|
+
const literals = [];
|
|
154
|
+
for (const t of type.types) {
|
|
155
|
+
if (isLiteralType(t)) {
|
|
156
|
+
literals.push(t);
|
|
157
|
+
} else {
|
|
158
|
+
anyOf.push(toJSONSchema(t, options));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (literals.length > 0) {
|
|
162
|
+
anyOf.push(literalEnum(literals));
|
|
163
|
+
}
|
|
164
|
+
if (anyOf.length === 1) {
|
|
165
|
+
return anyOf[0]!;
|
|
166
|
+
}
|
|
167
|
+
return { anyOf };
|
|
168
|
+
}
|
|
169
|
+
if (type.kind === 'record') {
|
|
170
|
+
if ('fields' in type) {
|
|
171
|
+
const properties: Record<string, JSONSchema7> = {};
|
|
172
|
+
const required: string[] = [];
|
|
173
|
+
for (const field of type.fields) {
|
|
174
|
+
properties[field.name] = toJSONSchema(field.type, options);
|
|
175
|
+
if (!field.optional && !loose) {
|
|
176
|
+
required.push(field.name);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const schema: JSONSchema7 = {
|
|
180
|
+
type: 'object',
|
|
181
|
+
properties,
|
|
182
|
+
additionalProperties: loose,
|
|
183
|
+
};
|
|
184
|
+
if (required.length > 0) {
|
|
185
|
+
schema.required = required;
|
|
186
|
+
}
|
|
187
|
+
return schema;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const valueSchema = toJSONSchema(type.value, options);
|
|
191
|
+
if (type.key == null) {
|
|
192
|
+
return {
|
|
193
|
+
type: 'object',
|
|
194
|
+
additionalProperties: valueSchema,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (typeof type.key == 'object') {
|
|
198
|
+
if (isLiteralType(type.key)) {
|
|
199
|
+
const schema: JSONSchema7 = {
|
|
200
|
+
type: 'object',
|
|
201
|
+
properties: { [String(type.key.value)]: valueSchema },
|
|
202
|
+
additionalProperties: loose,
|
|
203
|
+
};
|
|
204
|
+
return schema;
|
|
205
|
+
} else if (type.key.kind === 'union' && type.key.types.every(isLiteralType)) {
|
|
206
|
+
const schema: JSONSchema7 = {
|
|
207
|
+
type: 'object',
|
|
208
|
+
properties: Object.fromEntries(type.key.types.map((t) => [String(t.value), valueSchema])),
|
|
209
|
+
additionalProperties: loose,
|
|
210
|
+
};
|
|
211
|
+
return schema;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const pattern = templatePartPattern(type.key, false);
|
|
215
|
+
if (pattern === RE_ANY) {
|
|
216
|
+
return {
|
|
217
|
+
type: 'object',
|
|
218
|
+
additionalProperties: valueSchema,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
const schema: JSONSchema7 = {
|
|
222
|
+
type: 'object',
|
|
223
|
+
patternProperties: { [`^${pattern}$`]: valueSchema },
|
|
224
|
+
additionalProperties: loose,
|
|
225
|
+
};
|
|
226
|
+
return schema;
|
|
227
|
+
}
|
|
228
|
+
if (type.kind === 'literal') {
|
|
229
|
+
return literal(type);
|
|
230
|
+
}
|
|
231
|
+
if (type.kind === 'function') {
|
|
232
|
+
return {};
|
|
233
|
+
}
|
|
234
|
+
if (type.kind === 'template') {
|
|
235
|
+
return { type: 'string', pattern: templatePattern(type) };
|
|
236
|
+
}
|
|
237
|
+
/* c8 ignore next 3 */
|
|
238
|
+
(type) satisfies never;
|
|
239
|
+
return {};
|
|
240
|
+
}
|
package/src/parser.ts
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { parse as _parse } from '../dist/type.js';
|
|
2
2
|
|
|
3
3
|
/** MiraScript type */
|
|
4
|
-
export type Type =
|
|
4
|
+
export type Type =
|
|
5
|
+
| KnownType
|
|
6
|
+
| NamedType
|
|
7
|
+
| GenericType
|
|
8
|
+
| LiteralType
|
|
9
|
+
| TemplateType
|
|
10
|
+
| ArrayType
|
|
11
|
+
| UnionType
|
|
12
|
+
| RecordType
|
|
13
|
+
| FunctionType;
|
|
14
|
+
|
|
15
|
+
/** Generic type parameter reference in MiraScript */
|
|
16
|
+
export type GenericType = symbol;
|
|
5
17
|
|
|
6
18
|
/** Literal type in MiraScript */
|
|
7
19
|
export interface LiteralType {
|
|
@@ -11,6 +23,14 @@ export interface LiteralType {
|
|
|
11
23
|
value: string | boolean;
|
|
12
24
|
}
|
|
13
25
|
|
|
26
|
+
/** Template literal type in MiraScript */
|
|
27
|
+
export interface TemplateType {
|
|
28
|
+
/** Tag */
|
|
29
|
+
kind: 'template';
|
|
30
|
+
/** Interpolated parts, strings are represented as {@link LiteralType} objects, other parts are nested {@link Type} objects. */
|
|
31
|
+
parts: Type[];
|
|
32
|
+
}
|
|
33
|
+
|
|
14
34
|
/** Known type names in MiraScript */
|
|
15
35
|
export type KnownType =
|
|
16
36
|
| 'string'
|
|
@@ -44,14 +64,21 @@ export interface ArrayType {
|
|
|
44
64
|
}
|
|
45
65
|
|
|
46
66
|
/** Record type in MiraScript */
|
|
47
|
-
export
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
export type RecordType =
|
|
68
|
+
| {
|
|
69
|
+
/** Tag */
|
|
70
|
+
kind: 'record';
|
|
71
|
+
/** Fields in the record */
|
|
72
|
+
fields: RecordField[];
|
|
73
|
+
}
|
|
74
|
+
| {
|
|
75
|
+
/** Tag */
|
|
76
|
+
kind: 'record';
|
|
77
|
+
/** Key type for record<K, V> */
|
|
78
|
+
key?: Type;
|
|
79
|
+
/** Value type for record<T> or record<K, V> */
|
|
80
|
+
value: Type;
|
|
81
|
+
};
|
|
55
82
|
|
|
56
83
|
/** Record field in MiraScript */
|
|
57
84
|
export interface RecordField {
|
|
@@ -63,9 +90,87 @@ export interface RecordField {
|
|
|
63
90
|
type: Type;
|
|
64
91
|
}
|
|
65
92
|
|
|
93
|
+
/** Function type in MiraScript */
|
|
94
|
+
export interface FunctionType {
|
|
95
|
+
/** Tag */
|
|
96
|
+
kind: 'function';
|
|
97
|
+
/** Function name (only allowed at the top level) */
|
|
98
|
+
name?: string;
|
|
99
|
+
/** Generic type parameters */
|
|
100
|
+
typeParams?: GenericType[];
|
|
101
|
+
/** Function parameters */
|
|
102
|
+
params: FunctionParameter[];
|
|
103
|
+
/** Return type */
|
|
104
|
+
returns?: Type;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Function parameter in MiraScript */
|
|
108
|
+
export interface FunctionParameter {
|
|
109
|
+
/** Parameter name */
|
|
110
|
+
name: string;
|
|
111
|
+
/** Parameter type */
|
|
112
|
+
type: Type;
|
|
113
|
+
/** Whether the parameter is a rest parameter */
|
|
114
|
+
spread?: boolean;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Replaces generic type parameter names in the AST with unique symbols,
|
|
119
|
+
* scoped per function declaration.
|
|
120
|
+
*/
|
|
121
|
+
function resolveGenerics(type: Type, scope = new Map<string, GenericType>()): Type {
|
|
122
|
+
/* c8 ignore next 3 */
|
|
123
|
+
if (typeof type == 'symbol') {
|
|
124
|
+
return type;
|
|
125
|
+
}
|
|
126
|
+
if (typeof type == 'string') {
|
|
127
|
+
return scope.get(type) ?? type;
|
|
128
|
+
}
|
|
129
|
+
if (type.kind === 'function') {
|
|
130
|
+
const newScope = new Map(scope);
|
|
131
|
+
for (const sym of type.typeParams ?? []) {
|
|
132
|
+
newScope.set(sym.description!, sym);
|
|
133
|
+
}
|
|
134
|
+
for (const param of type.params) {
|
|
135
|
+
param.type = resolveGenerics(param.type, newScope);
|
|
136
|
+
}
|
|
137
|
+
if (type.returns != null) type.returns = resolveGenerics(type.returns, newScope);
|
|
138
|
+
return type;
|
|
139
|
+
}
|
|
140
|
+
if (type.kind === 'array') {
|
|
141
|
+
type.element = resolveGenerics(type.element, scope);
|
|
142
|
+
return type;
|
|
143
|
+
}
|
|
144
|
+
if (type.kind === 'union') {
|
|
145
|
+
type.types = type.types.map((t) => resolveGenerics(t, scope));
|
|
146
|
+
return type;
|
|
147
|
+
}
|
|
148
|
+
if (type.kind === 'record') {
|
|
149
|
+
if ('fields' in type) {
|
|
150
|
+
for (const field of type.fields) {
|
|
151
|
+
field.type = resolveGenerics(field.type, scope);
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
if (type.key != null) type.key = resolveGenerics(type.key, scope);
|
|
155
|
+
if (type.value != null) type.value = resolveGenerics(type.value, scope);
|
|
156
|
+
}
|
|
157
|
+
return type;
|
|
158
|
+
}
|
|
159
|
+
if (type.kind === 'literal') {
|
|
160
|
+
return type;
|
|
161
|
+
}
|
|
162
|
+
if (type.kind === 'template') {
|
|
163
|
+
type.parts = type.parts.map((part) => resolveGenerics(part, scope));
|
|
164
|
+
return type;
|
|
165
|
+
}
|
|
166
|
+
/* c8 ignore next 3 */
|
|
167
|
+
(type) satisfies never;
|
|
168
|
+
return type;
|
|
169
|
+
}
|
|
170
|
+
|
|
66
171
|
/**
|
|
67
172
|
* Parses a type string into a Type object.
|
|
68
173
|
*/
|
|
69
174
|
export function parse(type: string): Type {
|
|
70
|
-
return _parse(type) as Type;
|
|
175
|
+
return resolveGenerics(_parse(type) as Type);
|
|
71
176
|
}
|